Update html layout

- add messages fragment
  - extract error and success message list from edit.html
  - extract error message list from create.html
  - add messages.html

- update edit account
  - update AccountController
    - update postEditAccount for validation
    - update getEditAccount for roleGroups
  - update validation for AccountForm for edit
  - add EditGroup
  - update Account.Response.toAccountForm()
  - update edit.html

- update create account
  - update AccountController
    - update postCreateAccount for validation
    - update getCreateAccount for role group
  - add validation to AccountForm
  - add PasswordMatchValidator
  - add annotation PasswordMatch
  - add CreateGroup
  - add temporary getRoles() in AccountRegistryService
  - update AccountForm.toAccountRequest() in Mapping
  - change management.css
    - add ::selection
    - add Selected Option Styling
  - add passwordMatchCheck to management.js
  - update create.html

- update user
  - add makeLocalTime in management.js
  - update users.html
  - update Pagination
    - add size of items
    - rename size to show

- update goodbye.html

- update logout.html

- update login.html

- update welcome.html

- update index.html

- update layout
  - update management.css
  - update management.js
  - update layout.html
This commit is contained in:
2025-03-01 16:04:56 +01:00
parent 4c6c7dd9d8
commit 4c4baa95dd
22 changed files with 807 additions and 344 deletions

View File

@@ -1,90 +1,62 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Pages</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Test create user</h1>
<hr>
<form th:action="@{/account/create}" th:method="post">
<!-- Display error message if present -->
<div th:if="${errorMessage != null}" style="color: red; margin-bottom: 10px;">
<span th:text="${errorMessage}">Error Message</span>
</div>
<!-- Username -->
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" th:field="*{accountForm.username}" required/>
</div>
<!-- Password -->
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required/>
</div>
<!-- Re-enter Password -->
<div>
<label for="passwordConfirm">Re-enter Password:</label>
<input type="password" id="passwordConfirm" name="passwordConfirm" required/>
<span id="passwordMatchMessage"></span>
</div>
<!-- Role -->
<div>
<label for="role">Role:</label>
<select id="role" name="role" th:field="*{accountForm.role}" required>
<option value="user">User</option>
<option value="registered">Registered</option>
<option value="admin">Admin</option>
</select>
</div>
<!-- Enabled -->
<div>
<label for="enabled">Enabled:</label>
<input type="checkbox" id="enabled" name="enabled" th:field="*{accountForm.enabled}"/>
</div>
<!-- Submit Button -->
<button type="submit">Create User</button>
</form>
<br>
<a href="/account">Account</a>
<a href="/logout">Logout</a><br>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<main class="container mx-auto p-4 flex-grow flex items-center justify-center">
<div class="w-full max-w-md">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">New Account Registration</h2>
<hr class="border-green-900 mb-4">
<th:block th:replace="~{messages :: messageDisplay(messageList=${validationErrors}, error=true, styleClass='text-red-600')}"/>
<form th:action="@{/account/create}" th:method="post">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<div class="mb-4">
<label for="username" class="block text-sm mb-2">Username</label>
<input th:field="*{accountForm.username}" id="username" type="text" placeholder="Enter username..." class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
</div>
<div class="mb-4">
<label for="roles" class="block text-sm mb-2">Roles</label>
<select th:field="*{accountForm.roles}" id="roles" multiple="multiple" size="6" class="w-full bg-gray-900 border border-green-900 rounded scrollable-no-radius-right px-3 py-2 text-green-400 focus:outline-none focus:border-green-600 overflow-y-auto">
<optgroup th:each="group : ${roleGroups}" th:label="${group.key}" class="text-green-600">
<option th:each="role : ${group.value}" th:value="${role}" th:text="${role}" class="text-green-400"/>
</optgroup>
</select>
<p class="text-xs text-green-600 mt-1">[Hold Ctrl/Cmd to select multiple]</p>
</div>
<div class="mb-4">
<label for="enabled" class="block text-sm mb-2">Enabled</label>
<div class="switch-wrapper">
<input th:checked="${accountForm.enabled}" value="true" id="enabled" name="enabled" type="checkbox">
<span class="switch-slider border-green-900"></span>
</div>
</div>
<div class="mb-4">
<label for="password" class="block text-sm mb-2">Password</label>
<input th:field="*{accountForm.password}" id="password" type="password" placeholder="Enter password..." class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
</div>
<div class="mb-4">
<label class="block text-sm mb-2" for="passwordConfirm">Confirm password</label>
<input th:field="*{accountForm.passwordConfirm}" id="passwordConfirm" type="password" placeholder="Confirm password..." class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
<span id="passwordMatchMessage" class="text-xs mt-1"></span>
</div>
</div>
<div class="mt-4 flex justify-end space-x-4">
<a href="/account" class="bg-gray-800 hover:bg-gray-700 text-green-400 px-4 py-2 rounded border border-green-900 transition-colors inline-block">Cancel</a>
<button type="submit" class="bg-green-900 hover:bg-green-800 text-green-400 px-4 py-2 rounded border border-green-600 transition-colors">Create</button>
</div>
</form>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<script>
// Get password fields
const password = document.getElementById('password');
const passwordConfirm = document.getElementById('passwordConfirm');
const passwordMatchMessage = document.getElementById('passwordMatchMessage');
// Function to check if passwords match
function checkPasswordMatch() {
if (password.value === passwordConfirm.value) {
passwordMatchMessage.textContent = 'Passwords match!';
passwordMatchMessage.style.color = 'green';
} else {
passwordMatchMessage.textContent = 'Passwords do not match!';
passwordMatchMessage.style.color = 'red';
}
}
// Add event listeners to both password fields
password.addEventListener('input', checkPasswordMatch);
passwordConfirm.addEventListener('input', checkPasswordMatch);
// Form submit validation
document.querySelector('form').addEventListener('submit', function(e) {
if (password.value !== passwordConfirm.value) {
alert('Passwords do not match!');
e.preventDefault();
}
document.addEventListener('DOMContentLoaded', () => {
passwordMatchCheck(
document.getElementById('password'),
document.getElementById('passwordConfirm'),
document.getElementById('passwordMatchMessage')
);
});
</script>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,96 +1,71 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Pages</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Test edit user <strong th:text="${accountForm.username}">username</strong></h1>
<hr>
<form th:action="@{/account/edit-{account}(account = ${account})}" th:method="post">
<!-- Display error message if present -->
<div th:if="${errorMessage != null}" style="color: red; margin-bottom: 10px;">
<span th:text="${errorMessage}">Error Message</span>
</div>
<div th:if="${successMessage != null}" style="color: blue; margin-bottom: 10px;">
<span th:text="${successMessage}">success Message</span>
</div>
<!-- Username -->
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" th:field="*{accountForm.username}" required/>
</div>
<!-- Role -->
<div>
<label for="role">Role:</label>
<select id="role" name="role" th:field="*{accountForm.role}" required>
<option value="user">User</option>
<option value="registered">Registered</option>
<option value="admin">Admin</option>
</select>
</div>
<!-- Enabled -->
<div>
<label for="enabled">Enabled:</label>
<input type="checkbox" id="enabled" name="enabled" th:field="*{accountForm.enabled}"/>
</div>
<hr>
<!-- Password -->
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password"/>
</div>
<!-- Re-enter Password -->
<div>
<label for="passwordConfirm">Re-enter Password:</label>
<input type="password" id="passwordConfirm" name="passwordConfirm"/>
<span id="passwordMatchMessage"></span>
</div>
<hr>
<!-- Submit Button -->
<button type="submit">Update User</button>
</form>
<br>
<a href="/account">Account</a>
<a href="/logout">Logout</a><br>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<main class="container mx-auto p-4 flex-grow flex items-center justify-center">
<div class="w-full max-w-4xl">
<form th:action="@{/account/edit-{account}(account = ${account})}" th:method="post">
<div class="mb-4">
<h1 class="text-lg sm:text-xl mb-4 terminal-glow">Edit User <strong th:text="${accountForm.username}"/></h1>
<hr class="border-green-900 mb-4">
<th:block th:replace="~{messages :: messageDisplay(messageList=${validationErrors}, error=true, styleClass='text-red-600')}"/>
<th:block th:replace="~{messages :: messageDisplay(messageList=${successMessage}, error=false, styleClass='text-green-600')}"/>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">Account Details</h2>
<div class="mb-4">
<label class="block text-sm mb-2" for="username">Username:</label>
<input th:field="*{accountForm.username}" id="username" name="username" type="text" class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
</div>
<div class="mb-4">
<label class="block text-sm mb-2" for="roles">Role:</label>
<select th:field="*{accountForm.roles}" id="roles" multiple="multiple" size="6" class="w-full bg-gray-900 border border-green-900 rounded scrollable-no-radius-right px-3 py-2 text-green-400 focus:outline-none focus:border-green-600 overflow-y-auto">
<optgroup th:each="group : ${roleGroups}" th:label="${group.key}" class="text-green-600">
<option th:each="role : ${group.value}" th:value="${role}" th:text="${role}" class="text-green-400"/>
</optgroup>
</select>
<p class="text-xs text-green-600 mt-1">[Hold Ctrl/Cmd to select multiple]</p>
</div>
<div class="mb-4">
<label class="block text-sm mb-2" for="enabled">Enabled:</label>
<div class="switch-wrapper">
<input th:checked="${accountForm.enabled}" value="true" id="enabled" name="enabled" type="checkbox">
<span class="switch-slider border-green-900"></span>
</div>
</div>
</div>
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">Password Update</h2>
<div class="mb-4">
<label class="block text-sm mb-2" for="password">Password:</label>
<input th:field="*{accountForm.password}" id="password" name="password" type="password" placeholder="Leave blank to keep current" class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
</div>
<div class="mb-4">
<label class="block text-sm mb-2" for="passwordConfirm">Re-enter Password:</label>
<input th:field="*{accountForm.passwordConfirm}" id="passwordConfirm" name="passwordConfirm" type="password" placeholder="Confirm new password" class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600">
<span id="passwordMatchMessage" class="text-sm text-green-600"></span>
</div>
</div>
</div>
<div class="mt-4 flex justify-end space-x-4">
<a href="/account" class="bg-gray-800 hover:bg-gray-700 text-green-400 px-4 py-2 rounded border border-green-900 transition-colors inline-block">Cancel</a>
<button type="submit" class="bg-green-900 hover:bg-green-800 text-green-400 px-4 py-2 rounded border border-green-600 transition-colors">Update User</button>
</div>
</div>
</form>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<script>
// Get password fields
const password = document.getElementById('password');
const passwordConfirm = document.getElementById('passwordConfirm');
const passwordMatchMessage = document.getElementById('passwordMatchMessage');
// Function to check if passwords match
function checkPasswordMatch() {
if (password.value === passwordConfirm.value) {
passwordMatchMessage.textContent = 'Passwords match!';
passwordMatchMessage.style.color = 'green';
} else {
passwordMatchMessage.textContent = 'Passwords do not match!';
passwordMatchMessage.style.color = 'red';
}
}
// Add event listeners to both password fields
password.addEventListener('input', checkPasswordMatch);
passwordConfirm.addEventListener('input', checkPasswordMatch);
// Form submit validation
document.querySelector('form').addEventListener('submit', function(e) {
if (password.value !== passwordConfirm.value) {
alert('Passwords do not match!');
e.preventDefault();
}
document.addEventListener('DOMContentLoaded', () => {
passwordMatchCheck(
document.getElementById('password'),
document.getElementById('passwordConfirm'),
document.getElementById('passwordMatchMessage')
);
});
</script>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,44 +1,67 @@
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Pages</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Test</h1>
<hr>
<div>Show page <span th:text="${pagination.page}"/> items <span th:text="${pagination.start}"/> - <span th:text="${pagination.end}"/></div>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Description</th>
<th>Actions</th>
</tr>
<tr th:each="item : ${items}">
<td th:text="${item.id}">ID</td>
<td th:text="${item.timestamp}">timestamp</td>
<td th:text="${item.username}">username</td>
<td><a th:href="@{/account/edit-{id}(id = ${item.id})}">Edit</a></td>
</tr>
</table>
<div th:if="${pagination.showSize}">
<span th:if="${pagination.first}">Previous</span>
<a th:unless="${pagination.first}" th:href="@{'/account/page-' + ${pagination.previous} + '/show-' + ${size}}">Previous</a>
<a th:if="${pagination.hasMore}" th:href="@{'/account/page-' + ${pagination.next} + '/show-' + ${size}}">Next</a>
<span th:unless="${pagination.hasMore}">Next</span>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<main class="container mx-auto p-4 flex-grow">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h1 class="text-lg sm:text-xl mb-4 terminal-glow">Account Registry</h1>
<hr class="border-green-900 mb-4">
<div class="flex justify-between items-center mb-4">
<div th:if="${pagination.start > pagination.size}" class="text-sm">
Show page <span th:text="${pagination.page}"/> items 0 - 0
</div>
<div th:unless="${pagination.start > pagination.size}" class="text-sm">
Show page <span th:text="${pagination.page}"/>
items <span th:text="${pagination.start}"/> -
<span th:text="${pagination.size}"/>
</div>
<div class="mt-[-2px]">
<a th:href="@{/account/create}" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded border border-green-900 transition-colors text-sm">$ Create New Account</a>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<tr class="border-b border-green-900">
<th class="py-2 px-4 text-left">Username</th>
<th class="py-2 px-4 text-left">Timestamp</th>
<th class="py-2 px-4 text-left">Id</th>
<th class="py-2 px-4 text-left">Actions</th>
</tr>
<tr th:if="${items.isEmpty()}">
<td colspan="4" class="py-2 px-4 text-center text-green-600">No accounts found</td>
</tr>
<tr th:each="item : ${items}" class="border-b border-gray-700 hover:bg-gray-700">
<td class="py-2 px-4" th:text="${item.username}">username</td>
<td class="py-2 px-4 utcTimestamp" th:data-timestamp="${item.timestamp}">Loading...</td>
<td class="py-2 px-4" th:text="${item.id}">ID</td>
<td class="py-2 px-4"><a th:href="@{/account/edit-{id}(id = ${item.id})}">Edit</a></td>
</tr>
</table>
</div>
<div class="mt-6 text-sm" th:classappend="${pagination.showSize} ? 'space-y-2' : ''">
<div th:if="${pagination.showSize}" class="mt-6 flex justify-between items-center text-sm">
<span th:if="${pagination.first}" class="px-3 py-1 bg-gray-800 text-gray-500 rounded border border-green-900">Previous</span>
<a th:unless="${pagination.first}" th:href="@{'/account/page-' + ${pagination.previous} + '/show-' + ${pagination.show}}" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded border border-green-900 transition-colors">Previous</a>
<a th:if="${pagination.hasMore}" th:href="@{'/account/page-' + ${pagination.next} + '/show-' + ${pagination.show}}" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded border border-green-900 transition-colors">Next</a>
<span th:unless="${pagination.hasMore}" class="px-3 py-1 bg-gray-800 text-gray-500 rounded border border-green-900">Next</span>
</div>
<div th:unless="${pagination.showSize}" class="mt-6 flex justify-between items-center text-sm">
<span th:if="${pagination.first}" class="px-3 py-1 bg-gray-800 text-gray-500 rounded border border-green-900">Previous</span>
<a th:unless="${pagination.first}" th:href="@{'/account/page-' + ${pagination.previous}}" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded border border-green-900 transition-colors">Previous</a>
<a th:if="${pagination.hasMore}" th:href="@{'/account/page-' + ${pagination.next}}" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded border border-green-900 transition-colors">Next</a>
<span th:unless="${pagination.hasMore}" class="px-3 py-1 bg-gray-800 text-gray-500 rounded border border-green-900">Next</span>
</div>
</div>
</div>
<div th:unless="${pagination.showSize}">
<span th:if="${pagination.first}">Previous</span>
<a th:unless="${pagination.first}" th:href="@{'/account/page-' + ${pagination.previous}}">Previous</a>
<a th:if="${pagination.hasMore}" th:href="@{'/account/page-' + ${pagination.next}}">Next</a>
<span th:unless="${pagination.hasMore}">Next</span>
</div>
<br>
<a href="/account/create">Create Account</a><br>
<a href="/logout">Logout</a><br>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
<script>
// Assuming makeLocalTime is defined elsewhere or needs to be added
document.addEventListener('DOMContentLoaded', () => {
makeLocalTime(document.querySelectorAll('.utcTimestamp'));
});
</script>
</body>
</html>

View File

@@ -1,16 +1,28 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Goodbye</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>You are logged out</h1>
<hr>
<p>We hope to see you again soon!</p>
<a th:href="@{/login}">Login Again</a>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<main class="container mx-auto p-4 flex-grow flex items-center justify-center">
<div class="w-full max-w-md">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">Session Terminated</h2>
<pre class="text-sm mb-6 text-green-600">
> logout initiated...
> Saving session data... [OK]
> Closing connections... [OK]
> System cleanup complete... [OK]
Thank you for using Hlaeja Systems.
Goodbye.
</pre>
<div class="flex justify-center">
<a href="/login" class="bg-green-900 hover:bg-green-800 text-green-400 px-6 py-2 rounded border border-green-600 transition-colors">Reconnect</a>
</div>
</div>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,23 +1,42 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Login</h1>
<hr>
<form id="loginForm" th:action="@{/login}" th:method="post">
<label for="username" >Username</label>
<input type="text" id="username" name="username" placeholder="Enter your username" required>
<br>
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Enter your password" required>
<br>
<button type="submit">Login</button>
</form>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<!-- Main Content -->
<main class="container mx-auto p-4 flex-grow flex items-center justify-center">
<div class="w-full max-w-md">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">Authentication Required</h2>
<div id="login-error" class="mb-4 text-red-600 text-sm hidden">
<span>Error: </span>Bad username or password!
</div>
<form th:action="@{/login}" th:method="post">
<div class="mb-4">
<label class="block text-sm mb-2" for="username">username</label>
<input type="text" id="username" name="username" class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600" placeholder="Enter user ID...">
</div>
<div class="mb-6">
<label class="block text-sm mb-2" for="password">password</label>
<input type="password" id="password" name="password" class="w-full bg-gray-900 border border-green-900 rounded px-3 py-2 text-green-400 focus:outline-none focus:border-green-600" placeholder="Enter access code...">
</div>
<div class="flex justify-end">
<button type="submit" class="bg-green-900 hover:bg-green-800 text-green-400 px-4 py-2 rounded border border-green-600 transition-colors">Login</button>
</div>
</form>
</div>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
<script>
document.addEventListener('DOMContentLoaded', () => {
const errorDiv = document.getElementById('login-error');
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('error')) {
errorDiv.classList.remove('hidden');
}
});
</script>
</body>
</html>

View File

@@ -1,17 +1,24 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Logout</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Logout</h1>
<hr>
<p>Are you sure you want to logout?</p>
<form id="logoutForm" th:action="@{/logout}" th:method="post"></form>
<button type="submit" onclick="document.getElementById('logoutForm').submit(); return false;">Logout</button>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<main class="container mx-auto p-4 flex-grow flex items-center justify-center">
<div class="w-full max-w-md">
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
<h2 class="text-lg sm:text-xl mb-4 terminal-glow">Confirm System Logout</h2>
<p class="text-sm mb-6 text-green-600">Are you sure you want to terminate your session?</p>
<div class="flex justify-end space-x-4">
<a href="/account/page-1/show-10" class="bg-gray-800 hover:bg-gray-700 text-green-400 px-4 py-2 rounded border border-green-900 transition-colors inline-block">Cancel</a>
<!-- <a href="/account" class=" text-center bg-gray-700 hover:bg-gray-600 text-green-400 px-4 py-2 rounded border border-green-900 transition-colors">Cansel</a>-->
<form th:action="@{/logout}" th:method="post" class="flex-1">
<button type="submit" class="w-full bg-red-900 hover:bg-red-800 text-green-400 px-4 py-2 rounded border border-red-600 transition-colors">Yes, Logout</button>
</form>
</div>
</div>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,16 +1,72 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Pages</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Test</h1>
<hr>
<p>This is a index page!</p>
<a href="/login">login</a>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<!-- Main Content -->
<main class="container mx-auto p-4 flex-grow">
<!-- Main Grid -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
<!-- Sidebar -->
<div class="md:col-span-3">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">System Commands</h2>
<ul class="space-y-2 text-sm sm:text-base">
<li><span class="text-green-600">$</span> status_check</li>
<li><span class="text-green-600">$</span> sys_reboot</li>
<li><span class="text-green-600">$</span> mem_info</li>
<li><span class="text-green-600">$</span> net_stat</li>
</ul>
</div>
</div>
<!-- Main Terminal -->
<div class="md:col-span-6">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900 h-64 sm:h-96 overflow-y-auto">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">Terminal Output</h2>
<pre class="text-xs sm:text-sm">
SYSTEM BOOT [OK]
Initializing kernel... [OK]
Mounting drives... [OK]
Network interfaces up... [OK]
Loading console interface... [OK]
> Running diagnostics...
CPU: 2.4GHz [85%]
Memory: 16GB [72% used]
Storage: 1TB [45% free]
Network: 1Gbps [Stable]
</pre>
</div>
</div>
<!-- Status Panel -->
<div class="md:col-span-3">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">Status</h2>
<div class="space-y-2 text-sm sm:text-base">
<p>CPU: <span class="text-green-600">Online</span></p>
<p>Memory: <span class="text-green-600">Stable</span></p>
<p>Network: <span class="text-green-600">Connected</span></p>
<p>Uptime: 23h 45m</p>
</div>
</div>
</div>
</div>
<!-- Terminal Input -->
<div class="mt-4">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<div class="flex items-center">
<span class="text-green-600 mr-2">$</span>
<input type="text" class="bg-transparent w-full focus:outline-none text-green-400 text-sm sm:text-base" placeholder="Enter command...">
</div>
</div>
</div>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,24 +1,72 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Pages</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Welcome</h1>
<hr>
<!--/*@thymesVar id="remoteUser" type="ltd.hlaeja.security.RemoteAuthentication"*/-->
<div th:if="${remoteUser.hasRole('admin')}">
You are an admin!
<a href="/account">Account</a>
<!--/*/<th:block th:replace="~{layout.html :: documentHead ('Hlaeja Management')}"/>/*/-->
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<!--/*/<th:block th:replace="~{layout.html :: header}"/>/*/-->
<!-- Main Content -->
<main class="container mx-auto p-4 flex-grow">
<!-- Main Grid -->
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
<!-- Sidebar -->
<div class="md:col-span-3">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">System Commands</h2>
<ul class="space-y-2 text-sm sm:text-base">
<li><span class="text-green-600">$</span> status_check</li>
<li><span class="text-green-600">$</span> sys_reboot</li>
<li><span class="text-green-600">$</span> mem_info</li>
<li><span class="text-green-600">$</span> net_stat</li>
</ul>
</div>
</div>
<!-- Main Terminal -->
<div class="md:col-span-6">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900 h-64 sm:h-96 overflow-y-auto">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">Terminal Output</h2>
<pre class="text-xs sm:text-sm">
SYSTEM BOOT [OK]
Initializing kernel... [OK]
Mounting drives... [OK]
Network interfaces up... [OK]
Loading console interface... [OK]
> Running diagnostics...
CPU: 2.4GHz [85%]
Memory: 16GB [72% used]
Storage: 1TB [45% free]
Network: 1Gbps [Stable]
</pre>
</div>
</div>
<!-- Status Panel -->
<div class="md:col-span-3">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<h2 class="text-base sm:text-lg mb-2 terminal-glow">Status</h2>
<div class="space-y-2 text-sm sm:text-base">
<p>CPU: <span class="text-green-600">Online</span></p>
<p>Memory: <span class="text-green-600">Stable</span></p>
<p>Network: <span class="text-green-600">Connected</span></p>
<p>Uptime: 23h 45m</p>
</div>
</div>
</div>
</div>
<div th:if="${remoteUser.hasRole('user')}">
You are a user!
<!-- Terminal Input -->
<div class="mt-4">
<div class="bg-gray-800 p-4 rounded-lg border border-green-900">
<div class="flex items-center">
<span class="text-green-600 mr-2">$</span>
<input type="text" class="bg-transparent w-full focus:outline-none text-green-400 text-sm sm:text-base" placeholder="Enter command...">
</div>
</div>
</div>
<p>This is welcome pages and you're a user!</p>
<a href="/logout">Logout</a>
</main>
<!--/*/<th:block th:replace="~{layout.html :: footer}"/>/*/-->
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -1,10 +1,53 @@
<!DOCTYPE HTML>
<html lang="" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="documentHead">
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="documentHead (title)">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:text="${title}"/>
<link th:href="@{/css/management.css}" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
<header th:fragment="header" class="bg-gray-800 border-b border-green-900">
<div class="container mx-auto px-4 py-2 flex items-center justify-between">
<div>
<h1 class="text-xl sm:text-2xl terminal-glow">Hlaeja Management</h1>
</div>
<div class="relative">
<button id="menu-toggle" class="focus:outline-none">
<svg class="w-6 h-6 text-green-400 hover:text-green-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
<div id="dropdown-menu" class="hidden absolute right-0 mt-2 w-48 bg-gray-800 border border-green-900 shadow-lg z-10">
<th:block th:if="${remoteUser.hasRole('admin')}">
<a href="/account" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Account</a>
<hr class="dropdown-divider">
</th:block>
<th:block th:if="${remoteUser.authenticated}">
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ diagnostics</a>
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ logs</a>
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ shutdown</a>
</th:block>
<th:block th:if="${remoteUser.authenticated}">
<hr class="dropdown-divider">
<a href="/logout" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Logout</a>
</th:block>
<th:block th:unless="${remoteUser.authenticated}">
<a href="/login" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Login</a>
</th:block>
</div>
</div>
</div>
</header>
<footer th:fragment="footer" class="bg-gray-800 border-t border-green-900 mt-4">
<div class="container mx-auto p-4">
<div class="flex flex-col sm:flex-row justify-between items-center text-sm">
<div class="text-green-600 mb-2 sm:mb-0">Hlaeja © 2025 Lulz Ltd</div>
<div class="flex space-x-4"></div>
</div>
</div>
</footer>
<div th:fragment="script">
<script th:src="@{/js/management.js}"></script>
</div>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<th:block th:fragment="messageDisplay(messageList, error, styleClass)">
<div th:if="${messageList != null and #lists.size(messageList) > 0}" th:class="'mb-4 text-sm ' + ${styleClass}">
<th:block th:switch="${#lists.size(messageList)}">
<!-- Single Message Case -->
<th:block th:case="1">
<span th:if="${error}">Validation Error:</span>
<span th:unless="${error}">Validation:</span>
<span th:text="${messageList[0]}"></span>
</th:block>
<!-- Multiple Messages Case -->
<th:block th:case="*">
<span th:if="${error}">Validation Errors:</span>
<span th:unless="${error}">Validations:</span>
<ul class="list-none error-list-dot">
<li th:each="message : ${messageList}" th:text="'→ ' + ${message}"/>
</ul>
</th:block>
</th:block>
</div>
</th:block>
</body>
</html>