feat(reporting): add role dashboard template contracts
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package com.lab.labtimesheet.feature.reporting.model.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public sealed interface DashboardView {
|
||||
|
||||
record Admin(long activeAccounts, long pendingActivations,
|
||||
long activeInternships, long activeProjects) implements DashboardView {
|
||||
}
|
||||
|
||||
record Mentor(String displayName, long activeProjects, long activeMembers,
|
||||
long blockedTasks, long pendingDecisions) implements DashboardView {
|
||||
}
|
||||
|
||||
record Intern(String displayName, AttendanceState attendanceState, long activeProjects,
|
||||
long assignedTasks, long unreadNotifications,
|
||||
List<AssignedTask> priorityTasks) implements DashboardView {
|
||||
}
|
||||
|
||||
record AssignedTask(String title, String projectName, String status, LocalDate dueDate) {
|
||||
}
|
||||
|
||||
enum AttendanceState {
|
||||
NOT_CHECKED_IN("Not checked in"),
|
||||
CHECKED_IN("Checked in"),
|
||||
CHECKED_OUT("Checked out");
|
||||
|
||||
private final String label;
|
||||
|
||||
AttendanceState(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.lab.labtimesheet.reporting;
|
||||
|
||||
/** Reporting module boundary. */
|
||||
public final class ModuleBoundary {
|
||||
private ModuleBoundary() {
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{fragments/layout :: shell(
|
||||
pageTitle='System overview',
|
||||
section='Admin',
|
||||
activeNav='dashboard',
|
||||
primaryAction=~{::#primary-action},
|
||||
content=~{::main})}">
|
||||
<body>
|
||||
<a id="primary-action" class="button button-primary" th:href="@{/admin/users/new}">Create account</a>
|
||||
<main>
|
||||
<p class="page-description">Account readiness, internship activity, and active Project work.</p>
|
||||
<section class="panel metric-strip" aria-label="System metrics">
|
||||
<div class="metric"><div class="metric-label">Active accounts</div><div class="metric-value" th:text="${dashboard.activeAccounts}">0</div><div class="metric-detail">Can authenticate when otherwise eligible</div></div>
|
||||
<div class="metric"><div class="metric-label">Pending activation</div><div class="metric-value" th:text="${dashboard.pendingActivations}">0</div><div class="metric-detail">Awaiting first password</div></div>
|
||||
<div class="metric"><div class="metric-label">Active internships</div><div class="metric-value" th:text="${dashboard.activeInternships}">0</div><div class="metric-detail">Current Intern lifecycle</div></div>
|
||||
<div class="metric"><div class="metric-label">Active Projects</div><div class="metric-value" th:text="${dashboard.activeProjects}">0</div><div class="metric-detail">Read-only Admin scope</div></div>
|
||||
</section>
|
||||
<section class="panel" th:if="${dashboard.pendingActivations == 0}"><th:block th:replace="~{fragments/components :: empty('No pending activations', 'New accounts awaiting activation will appear here.')} "></th:block></section>
|
||||
<section class="panel" th:unless="${dashboard.pendingActivations == 0}">
|
||||
<header class="panel-header"><h2 class="panel-title">Attention required</h2></header>
|
||||
<div class="alert alert-warning" role="status"><strong th:text="${dashboard.pendingActivations}">0</strong> account(s) are waiting for activation.</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{fragments/layout :: shell(
|
||||
pageTitle='Today',
|
||||
section='Intern',
|
||||
activeNav='dashboard',
|
||||
primaryAction=~{::#primary-action},
|
||||
content=~{::main})}">
|
||||
<body>
|
||||
<form id="primary-action" th:if="${dashboard.attendanceState.name() != 'CHECKED_OUT'}"
|
||||
th:action="${dashboard.attendanceState.name() == 'CHECKED_IN' ? '/attendance/check-out' : '/attendance/check-in'}" method="post">
|
||||
<button class="button button-primary" type="submit" th:text="${dashboard.attendanceState.name() == 'CHECKED_IN' ? 'Check out' : 'Check in'}">Check in</button>
|
||||
</form>
|
||||
<main>
|
||||
<p class="page-description" th:text="${'Attendance, assigned work, and Project activity for ' + dashboard.displayName + '.'}">Today.</p>
|
||||
<section class="panel metric-strip" aria-label="Intern metrics">
|
||||
<div class="metric"><div class="metric-label">Attendance</div><div class="metric-value" th:text="${dashboard.attendanceState.label}">Not checked in</div><div class="metric-detail">Server-authoritative state</div></div>
|
||||
<div class="metric"><div class="metric-label">Active Projects</div><div class="metric-value" th:text="${dashboard.activeProjects}">0</div><div class="metric-detail">Current memberships</div></div>
|
||||
<div class="metric"><div class="metric-label">Assigned Tasks</div><div class="metric-value" th:text="${dashboard.assignedTasks}">0</div><div class="metric-detail">Current assignment scope</div></div>
|
||||
<div class="metric"><div class="metric-label">Unread notifications</div><div class="metric-value" th:text="${dashboard.unreadNotifications}">0</div><div class="metric-detail">Own notifications only</div></div>
|
||||
</section>
|
||||
<section class="panel">
|
||||
<header class="panel-header"><h2 class="panel-title">Priority Tasks</h2></header>
|
||||
<th:block th:if="${#lists.isEmpty(dashboard.priorityTasks)}"><th:block th:replace="~{fragments/components :: empty('No assigned Tasks', 'Assigned active-Project Tasks will appear here.')} "></th:block></th:block>
|
||||
<div class="table-scroll" th:unless="${#lists.isEmpty(dashboard.priorityTasks)}">
|
||||
<table class="data-table">
|
||||
<caption class="sr-only">Current assigned Tasks</caption>
|
||||
<thead><tr><th scope="col">Task</th><th scope="col">Project</th><th scope="col">Status</th><th scope="col">Due</th></tr></thead>
|
||||
<tbody><tr th:each="task : ${dashboard.priorityTasks}"><td th:text="${task.title}">Task</td><td th:text="${task.projectName}">Project</td><td><span class="badge" th:classappend="${task.status == 'BLOCKED' ? ' badge-warning' : task.status == 'DONE' ? ' badge-success' : ''}" th:attr="aria-label=${'Status: ' + task.status}" th:text="${task.status}">TODO</span></td><td th:text="${task.dueDate == null ? 'No due date' : #temporals.format(task.dueDate, 'dd/MM/yyyy')}">No due date</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{fragments/layout :: shell(
|
||||
pageTitle=${'Good morning, ' + dashboard.displayName},
|
||||
section='Mentor',
|
||||
activeNav='dashboard',
|
||||
primaryAction=~{::#primary-action},
|
||||
content=~{::main})}">
|
||||
<body>
|
||||
<a id="primary-action" class="button button-primary" th:href="@{/projects/new}">Create Project</a>
|
||||
<main>
|
||||
<p class="page-description">Review pending decisions, then scan attendance and owned Project health.</p>
|
||||
<section class="panel metric-strip" aria-label="Mentor metrics">
|
||||
<div class="metric"><div class="metric-label">Active owned Projects</div><div class="metric-value" th:text="${dashboard.activeProjects}">0</div><div class="metric-detail">Only your Project scope</div></div>
|
||||
<div class="metric"><div class="metric-label">Active members</div><div class="metric-value" th:text="${dashboard.activeMembers}">0</div><div class="metric-detail">Across owned active Projects</div></div>
|
||||
<div class="metric"><div class="metric-label">Blocked Tasks</div><div class="metric-value" th:text="${dashboard.blockedTasks}">0</div><div class="metric-detail">View and comment</div></div>
|
||||
<div class="metric"><div class="metric-label">Pending decisions</div><div class="metric-value" th:text="${dashboard.pendingDecisions}">0</div><div class="metric-detail">Global leave and corrections</div></div>
|
||||
</section>
|
||||
<section class="panel" th:if="${dashboard.pendingDecisions == 0}"><th:block th:replace="~{fragments/components :: empty('No pending decisions', 'New leave and correction requests will appear in their queues.')} "></th:block></section>
|
||||
<section class="panel" th:unless="${dashboard.pendingDecisions == 0}"><header class="panel-header"><h2 class="panel-title">Decision queue</h2></header><div class="alert" role="status"><strong th:text="${dashboard.pendingDecisions}">0</strong> request(s) need Mentor review.</div></section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user