feat(ui): integrate account pages with shared shell

This commit is contained in:
sechmachine
2026-08-15 00:53:45 +07:00
parent 69b81ddc71
commit 7dd61b9dd4
9 changed files with 236 additions and 28 deletions
File diff suppressed because one or more lines are too long
@@ -1,15 +1,18 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>Activate account</title></head>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/auth-layout :: shell(
pageTitle='Choose your password',
eyebrow='Account activation',
content=~{::main})}">
<body>
<main>
<h1>Choose your password</h1>
<p th:if="${error}" th:text="${error}" role="alert"></p>
<form method="post" th:action="@{/activate}">
<p class="page-description">Use at least 12 characters. This activation link can be used once.</p>
<p class="alert alert-error" th:if="${error}" th:text="${error}" role="alert"></p>
<form class="form-grid auth-form" method="post" th:action="@{/activate}">
<input name="token" type="hidden" th:value="${token}">
<label>Password <input name="password" type="password" minlength="12" maxlength="128" required autocomplete="new-password"></label>
<label>Confirm password <input name="confirmPassword" type="password" minlength="12" maxlength="128" required autocomplete="new-password"></label>
<button type="submit">Activate account</button>
<div class="field"><label class="field-label" for="password">Password</label><input class="control" id="password" name="password" type="password" minlength="12" maxlength="128" required autocomplete="new-password"></div>
<div class="field"><label class="field-label" for="confirmPassword">Confirm password</label><input class="control" id="confirmPassword" name="confirmPassword" type="password" minlength="12" maxlength="128" required autocomplete="new-password"></div>
<button class="button button-primary" type="submit">Activate account</button>
</form>
</main>
</body>
+26 -17
View File
@@ -1,29 +1,38 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>Create account</title></head>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: shell(
pageTitle='Create account',
section='Admin',
activeNav='accounts',
primaryAction=~{::#primary-action},
content=~{::main})}">
<body>
<a id="primary-action" class="button" th:href="@{/dashboard}">Back to overview</a>
<main>
<h1>Create account</h1>
<p th:if="${param.created}" role="status">Account created and activation email sent.</p>
<p th:if="${param.deliveryFailed}" role="alert">Account created, but activation delivery failed.</p>
<p th:if="${error}" th:text="${error}" role="alert"></p>
<form method="post" th:action="@{/admin/accounts}">
<label>Email <input name="email" type="email" required autocomplete="off"></label>
<label>Display name <input name="displayName" required autocomplete="off"></label>
<label>Role
<select name="role" required>
<p class="page-description">Create a role-specific account and send its one-time activation link.</p>
<p class="alert" th:if="${param.created}" role="status">Account created and activation email sent.</p>
<p class="alert alert-error" th:if="${param.deliveryFailed}" role="alert">Account created, but activation delivery failed.</p>
<p class="alert alert-error" th:if="${error}" th:text="${error}" role="alert"></p>
<form class="panel form-panel form-grid" method="post" th:action="@{/admin/accounts}">
<div class="field"><label class="field-label" for="email">Email</label><input class="control" id="email" name="email" type="email" required autocomplete="off"></div>
<div class="field"><label class="field-label" for="displayName">Display name</label><input class="control" id="displayName" name="displayName" required autocomplete="off"></div>
<div class="field"><label class="field-label" for="role">Role</label>
<select class="control" id="role" name="role" required>
<option value="ADMIN">Admin</option>
<option value="MENTOR">Mentor</option>
<option value="INTERN">Intern</option>
</select>
</label>
<fieldset>
</div>
<fieldset class="form-section">
<legend>Intern details</legend>
<label>Student code <input name="studentCode" autocomplete="off"></label>
<label>Internship start <input name="internshipStart" type="date"></label>
<label>Internship end <input name="internshipEnd" type="date"></label>
<p class="field-help">Required only when the selected role is Intern.</p>
<div class="form-grid form-grid-three">
<div class="field"><label class="field-label" for="studentCode">Student code</label><input class="control" id="studentCode" name="studentCode" autocomplete="off"></div>
<div class="field"><label class="field-label" for="internshipStart">Internship start</label><input class="control" id="internshipStart" name="internshipStart" type="date"></div>
<div class="field"><label class="field-label" for="internshipEnd">Internship end</label><input class="control" id="internshipEnd" name="internshipEnd" type="date"></div>
</div>
</fieldset>
<button type="submit">Create account</button>
<div class="form-actions"><a class="button" th:href="@{/dashboard}">Cancel</a><button class="button button-primary" type="submit">Create account</button></div>
</form>
</main>
</body>
@@ -7,7 +7,7 @@
primaryAction=~{::#primary-action},
content=~{::main})}">
<body>
<a id="primary-action" class="button button-primary" th:href="@{/admin/users/new}">Create account</a>
<a id="primary-action" class="button button-primary" th:href="@{/admin/accounts/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">
@@ -0,0 +1,37 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:fragment="shell(pageTitle, eyebrow, content)">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title th:text="${pageTitle + ' · Lab Timesheet'}">Lab Timesheet</title>
<script th:src="@{/assets/theme.js}"></script>
<link rel="stylesheet" th:href="@{/assets/app.css}">
<script defer th:src="@{/assets/app.js}"></script>
</head>
<body>
<div class="auth-shell">
<header class="auth-header">
<a class="brand" th:href="@{/}">
<span class="brand-mark" aria-hidden="true">LT</span>
<strong>Lab Timesheet</strong>
</a>
<div class="theme-field auth-theme">
<label class="sr-only" for="theme-preference">Theme</label>
<select id="theme-preference" data-theme-select aria-label="Theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">System</option>
</select>
</div>
</header>
<main class="auth-main">
<section class="auth-card">
<p class="auth-eyebrow" th:text="${eyebrow}">Account access</p>
<h1 class="page-title" th:text="${pageTitle}">Page</h1>
<th:block th:replace="${content}"></th:block>
</section>
</main>
</div>
</body>
</html>
@@ -21,7 +21,7 @@
<ul class="nav-list">
<li><a class="nav-link" th:href="@{/dashboard}" th:attr="aria-current=${activeNav == 'dashboard'} ? 'page' : null">
<svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#layout-dashboard}"></use></svg><span class="sidebar-label">Overview</span></a></li>
<li sec:authorize="hasRole('ADMIN')"><a class="nav-link" th:href="@{/admin/users}"><svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#users}"></use></svg><span class="sidebar-label">Accounts</span></a></li>
<li sec:authorize="hasRole('ADMIN')"><a class="nav-link" th:href="@{/admin/accounts/new}" th:attr="aria-current=${activeNav == 'accounts'} ? 'page' : null"><svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#users}"></use></svg><span class="sidebar-label">Accounts</span></a></li>
<li sec:authorize="hasRole('ADMIN')"><a class="nav-link" th:href="@{/admin/calendar}"><svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#calendar-days}"></use></svg><span class="sidebar-label">Global calendar</span></a></li>
<li sec:authorize="hasRole('MENTOR')"><a class="nav-link" th:href="@{/projects}"><svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#folder-kanban}"></use></svg><span class="sidebar-label">Owned Projects</span></a></li>
<li sec:authorize="hasRole('MENTOR')"><a class="nav-link" th:href="@{/attendance}"><svg class="nav-icon" aria-hidden="true"><use th:href="@{/assets/icons.svg#clock}"></use></svg><span class="sidebar-label">Intern attendance</span></a></li>