用户/账号管理,系统日志
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><c:out value="${formTitle}" /> - MZH Library</title>
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<%@ include file="/WEB-INF/jsp/common/header.jspf" %>
|
||||
<main class="page-shell">
|
||||
<section class="form-panel" aria-labelledby="user-form-title">
|
||||
<p class="eyebrow">Administration</p>
|
||||
<h1 id="user-form-title"><c:out value="${formTitle}" /></h1>
|
||||
|
||||
<c:if test="${not empty errorMessage}">
|
||||
<div class="message message-error" role="alert">
|
||||
<c:out value="${errorMessage}" />
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<c:set var="hasFormValues" value="${not empty formValues}" />
|
||||
<c:set var="usernameValue" value="${hasFormValues ? formValues.username : user.username}" />
|
||||
<c:set var="displayNameValue" value="${hasFormValues ? formValues.displayName : user.displayName}" />
|
||||
<c:set var="roleValue" value="${hasFormValues ? formValues.role : user.role.code}" />
|
||||
<c:set var="activeValue" value="${hasFormValues ? formValues.active : user.active}" />
|
||||
|
||||
<form class="user-form" action="${pageContext.request.contextPath}${formAction}" method="post" novalidate>
|
||||
<c:if test="${user.id > 0}">
|
||||
<input type="hidden" name="id" value="${user.id}">
|
||||
<input type="hidden" name="username" value="${fn:escapeXml(usernameValue)}">
|
||||
</c:if>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-field">
|
||||
<label for="username">Username</label>
|
||||
<c:choose>
|
||||
<c:when test="${user.id > 0}">
|
||||
<input id="username" type="text" value="${fn:escapeXml(usernameValue)}" disabled>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<input id="username" name="username" type="text" value="${fn:escapeXml(usernameValue)}" required>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
<c:if test="${not empty errors.username}">
|
||||
<span class="field-error"><c:out value="${errors.username}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="displayName">Display name</label>
|
||||
<input id="displayName" name="displayName" type="text"
|
||||
value="${fn:escapeXml(displayNameValue)}" required>
|
||||
<c:if test="${not empty errors.displayName}">
|
||||
<span class="field-error"><c:out value="${errors.displayName}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="role">Role</label>
|
||||
<select id="role" name="role" required>
|
||||
<option value="">Select role</option>
|
||||
<c:forEach var="role" items="${roles}">
|
||||
<option value="${role.code}" <c:if test="${roleValue == role.code}">selected</c:if>>
|
||||
<c:out value="${role.displayName}" />
|
||||
</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
<c:if test="${not empty errors.role}">
|
||||
<span class="field-error"><c:out value="${errors.role}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="active">Active state</label>
|
||||
<select id="active" name="active" required>
|
||||
<option value="true" <c:if test="${activeValue == true or activeValue == 'true'}">selected</c:if>>
|
||||
Active
|
||||
</option>
|
||||
<option value="false" <c:if test="${activeValue == false or activeValue == 'false'}">selected</c:if>>
|
||||
Inactive
|
||||
</option>
|
||||
</select>
|
||||
<c:if test="${not empty errors.active}">
|
||||
<span class="field-error"><c:out value="${errors.active}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label for="password">
|
||||
<c:choose>
|
||||
<c:when test="${user.id > 0}">New password</c:when>
|
||||
<c:otherwise>Password</c:otherwise>
|
||||
</c:choose>
|
||||
</label>
|
||||
<c:choose>
|
||||
<c:when test="${user.id > 0}">
|
||||
<input id="password" name="password" type="password" autocomplete="new-password">
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<input id="password" name="password" type="password" autocomplete="new-password" required>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
<c:if test="${not empty errors.password}">
|
||||
<span class="field-error"><c:out value="${errors.password}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="button button-primary" type="submit">Save</button>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/users">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,139 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Manage Users - MZH Library</title>
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<%@ include file="/WEB-INF/jsp/common/header.jspf" %>
|
||||
<main class="page-shell">
|
||||
<section class="dashboard-hero catalog-hero" aria-labelledby="manage-users-title">
|
||||
<div>
|
||||
<p class="eyebrow">Administration</p>
|
||||
<h1 id="manage-users-title">Manage users</h1>
|
||||
<p>Create, update, deactivate, and review administrator, librarian, and reader accounts.</p>
|
||||
</div>
|
||||
<a class="button button-primary" href="${pageContext.request.contextPath}/admin/users/new">New user</a>
|
||||
</section>
|
||||
|
||||
<c:if test="${not empty successMessage}">
|
||||
<div class="message message-success" role="status">
|
||||
<c:out value="${successMessage}" />
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${not empty errorMessage}">
|
||||
<div class="message message-error" role="alert">
|
||||
<c:out value="${errorMessage}" />
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<section class="toolbar-panel" aria-label="User management search">
|
||||
<form class="search-form" action="${pageContext.request.contextPath}/admin/users" method="get">
|
||||
<div class="search-field">
|
||||
<label for="keyword">Keyword</label>
|
||||
<input id="keyword" name="keyword" type="text" value="${fn:escapeXml(criteria.keyword)}">
|
||||
<c:if test="${not empty errors.keyword}">
|
||||
<span class="field-error"><c:out value="${errors.keyword}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="search-field">
|
||||
<label for="role">Role</label>
|
||||
<select id="role" name="role">
|
||||
<option value="">All roles</option>
|
||||
<c:forEach var="role" items="${roles}">
|
||||
<option value="${role.code}" <c:if test="${criteria.roleCode == role.code}">selected</c:if>>
|
||||
<c:out value="${role.displayName}" />
|
||||
</option>
|
||||
</c:forEach>
|
||||
</select>
|
||||
<c:if test="${not empty errors.role}">
|
||||
<span class="field-error"><c:out value="${errors.role}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="search-field">
|
||||
<label for="active">Active state</label>
|
||||
<select id="active" name="active">
|
||||
<option value="">All states</option>
|
||||
<option value="active" <c:if test="${criteria.activeStatus == 'active'}">selected</c:if>>Active</option>
|
||||
<option value="inactive" <c:if test="${criteria.activeStatus == 'inactive'}">selected</c:if>>Inactive</option>
|
||||
</select>
|
||||
<c:if test="${not empty errors.active}">
|
||||
<span class="field-error"><c:out value="${errors.active}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<button class="button button-primary" type="submit">Search</button>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/users">Clear</a>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="table-panel" aria-labelledby="user-results-title">
|
||||
<h2 id="user-results-title">User accounts</h2>
|
||||
<c:choose>
|
||||
<c:when test="${empty users}">
|
||||
<p class="empty-state">No user accounts match the current filters.</p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="table-scroll">
|
||||
<table class="data-table user-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Display name</th>
|
||||
<th scope="col">Role</th>
|
||||
<th scope="col">State</th>
|
||||
<th scope="col">Created</th>
|
||||
<th scope="col">Updated</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<c:forEach var="account" items="${users}">
|
||||
<tr>
|
||||
<td><c:out value="${account.username}" /></td>
|
||||
<td><c:out value="${account.displayName}" /></td>
|
||||
<td><c:out value="${account.role.displayName}" /></td>
|
||||
<td>
|
||||
<span class="status-pill status-${account.activeStatusCode}">
|
||||
<c:out value="${account.activeStatusName}" />
|
||||
</span>
|
||||
</td>
|
||||
<td><c:out value="${account.createdAtText}" /></td>
|
||||
<td><c:out value="${account.updatedAtText}" /></td>
|
||||
<td>
|
||||
<div class="table-actions">
|
||||
<a class="button button-secondary"
|
||||
href="${pageContext.request.contextPath}/admin/users/edit?id=${account.id}">Edit</a>
|
||||
<c:choose>
|
||||
<c:when test="${account.id == sessionScope.authenticatedUser.id or not account.active}">
|
||||
<button class="button button-secondary" type="button" disabled>Deactivate</button>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<form action="${pageContext.request.contextPath}/admin/users/deactivate"
|
||||
method="post"
|
||||
onsubmit="return confirm('Deactivate this user account?');">
|
||||
<input type="hidden" name="id" value="${account.id}">
|
||||
<button class="button button-danger" type="submit">Deactivate</button>
|
||||
</form>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,6 +7,8 @@
|
||||
<a href="${pageContext.request.contextPath}/catalog">Catalog</a>
|
||||
<c:if test="${sessionScope.userRole == 'administrator'}">
|
||||
<a href="${pageContext.request.contextPath}/admin/home">Admin</a>
|
||||
<a href="${pageContext.request.contextPath}/admin/users">Users</a>
|
||||
<a href="${pageContext.request.contextPath}/admin/system-logs">Logs</a>
|
||||
</c:if>
|
||||
<c:if test="${sessionScope.userRole == 'administrator' or sessionScope.userRole == 'librarian'}">
|
||||
<a href="${pageContext.request.contextPath}/librarian/home">Librarian</a>
|
||||
|
||||
@@ -26,6 +26,18 @@
|
||||
<p>Account, role, permission, and system-maintenance entry point.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/home">Open</a>
|
||||
</article>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>User Management</h2>
|
||||
<p>Create, update, deactivate, and review login accounts.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/users">Open</a>
|
||||
</article>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>System Logs</h2>
|
||||
<p>Review read-only audit entries for account and maintenance actions.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/system-logs">Open</a>
|
||||
</article>
|
||||
</c:if>
|
||||
|
||||
<c:if test="${sessionScope.userRole == 'administrator' or sessionScope.userRole == 'librarian'}">
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>System Logs - MZH Library</title>
|
||||
<link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<%@ include file="/WEB-INF/jsp/common/header.jspf" %>
|
||||
<main class="page-shell">
|
||||
<section class="dashboard-hero catalog-hero" aria-labelledby="system-logs-title">
|
||||
<div>
|
||||
<p class="eyebrow">System Maintenance</p>
|
||||
<h1 id="system-logs-title">System logs</h1>
|
||||
<p>Review administrative account changes and maintenance audit records.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<c:if test="${not empty errorMessage}">
|
||||
<div class="message message-error" role="alert">
|
||||
<c:out value="${errorMessage}" />
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<section class="toolbar-panel" aria-label="System log search">
|
||||
<form class="search-form system-log-search-form"
|
||||
action="${pageContext.request.contextPath}/admin/system-logs" method="get">
|
||||
<div class="search-field">
|
||||
<label for="operationType">Operation</label>
|
||||
<select id="operationType" name="operationType">
|
||||
<option value="">All operations</option>
|
||||
<c:forEach var="operationType" items="${operationTypes}">
|
||||
<option value="${fn:escapeXml(operationType)}"
|
||||
<c:if test="${criteria.operationType == operationType}">selected</c:if>>
|
||||
<c:out value="${operationType}" />
|
||||
</option>
|
||||
</c:forEach>
|
||||
<c:if test="${not empty criteria.operationType and empty operationTypes}">
|
||||
<option value="${fn:escapeXml(criteria.operationType)}" selected>
|
||||
<c:out value="${criteria.operationType}" />
|
||||
</option>
|
||||
</c:if>
|
||||
</select>
|
||||
<c:if test="${not empty errors.operationType}">
|
||||
<span class="field-error"><c:out value="${errors.operationType}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="search-field">
|
||||
<label for="keyword">Keyword</label>
|
||||
<input id="keyword" name="keyword" type="text" value="${fn:escapeXml(criteria.keyword)}">
|
||||
<c:if test="${not empty errors.keyword}">
|
||||
<span class="field-error"><c:out value="${errors.keyword}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="search-field">
|
||||
<label for="createdFrom">From</label>
|
||||
<input id="createdFrom" name="createdFrom" type="date"
|
||||
value="${fn:escapeXml(criteria.createdFromText)}">
|
||||
<c:if test="${not empty errors.createdFrom}">
|
||||
<span class="field-error"><c:out value="${errors.createdFrom}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="search-field">
|
||||
<label for="createdTo">To</label>
|
||||
<input id="createdTo" name="createdTo" type="date"
|
||||
value="${fn:escapeXml(criteria.createdToText)}">
|
||||
<c:if test="${not empty errors.createdTo}">
|
||||
<span class="field-error"><c:out value="${errors.createdTo}" /></span>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<button class="button button-primary" type="submit">Search</button>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/system-logs">Clear</a>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="table-panel" aria-labelledby="system-log-results-title">
|
||||
<h2 id="system-log-results-title">Log entries</h2>
|
||||
<c:choose>
|
||||
<c:when test="${empty logs}">
|
||||
<p class="empty-state">No system logs match the current filters.</p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="table-scroll">
|
||||
<table class="data-table system-log-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Time</th>
|
||||
<th scope="col">Operator</th>
|
||||
<th scope="col">Operation</th>
|
||||
<th scope="col">Target</th>
|
||||
<th scope="col">Result</th>
|
||||
<th scope="col">IP address</th>
|
||||
<th scope="col">Detail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<c:forEach var="log" items="${logs}">
|
||||
<tr>
|
||||
<td><c:out value="${log.createdAtText}" /></td>
|
||||
<td>
|
||||
<div><c:out value="${log.operatorLabel}" /></div>
|
||||
<c:if test="${not empty log.operatorMetaText}">
|
||||
<div class="muted-text"><c:out value="${log.operatorMetaText}" /></div>
|
||||
</c:if>
|
||||
</td>
|
||||
<td><c:out value="${log.operationType}" /></td>
|
||||
<td>
|
||||
<c:out value="${log.targetTable}" />
|
||||
<c:if test="${not empty log.targetId}">
|
||||
#<c:out value="${log.targetId}" />
|
||||
</c:if>
|
||||
</td>
|
||||
<td>
|
||||
<span class="status-pill status-${log.resultStatusCode}">
|
||||
<c:out value="${log.resultStatusName}" />
|
||||
</span>
|
||||
</td>
|
||||
<td><c:out value="${log.requestIp}" /></td>
|
||||
<td><c:out value="${log.message}" /></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -28,6 +28,20 @@
|
||||
</article>
|
||||
|
||||
<c:if test="${sessionScope.userRole == 'administrator' or sessionScope.userRole == 'librarian'}">
|
||||
<c:if test="${sessionScope.userRole == 'administrator'}">
|
||||
<article class="workspace-card">
|
||||
<h2>User Management</h2>
|
||||
<p>Create, update, deactivate, and review login accounts.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/users">Manage users</a>
|
||||
</article>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>System Logs</h2>
|
||||
<p>Review read-only audit entries for account and maintenance actions.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/admin/system-logs">View logs</a>
|
||||
</article>
|
||||
</c:if>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>Book Management</h2>
|
||||
<p>Create, update, delete, and review inventory fields for book records.</p>
|
||||
|
||||
@@ -75,6 +75,28 @@
|
||||
<url-pattern>/reader/home</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>UserManagementServlet</servlet-name>
|
||||
<servlet-class>com.mzh.library.controller.UserManagementServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>UserManagementServlet</servlet-name>
|
||||
<url-pattern>/admin/users</url-pattern>
|
||||
<url-pattern>/admin/users/new</url-pattern>
|
||||
<url-pattern>/admin/users/edit</url-pattern>
|
||||
<url-pattern>/admin/users/update</url-pattern>
|
||||
<url-pattern>/admin/users/deactivate</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>SystemLogServlet</servlet-name>
|
||||
<servlet-class>com.mzh.library.controller.SystemLogServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SystemLogServlet</servlet-name>
|
||||
<url-pattern>/admin/system-logs</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>BookCatalogServlet</servlet-name>
|
||||
<servlet-class>com.mzh.library.controller.BookCatalogServlet</servlet-class>
|
||||
|
||||
@@ -198,6 +198,11 @@ h2 {
|
||||
background: #8f3028;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 16px;
|
||||
padding: 10px 12px;
|
||||
@@ -326,6 +331,10 @@ h2 {
|
||||
grid-template-columns: repeat(3, minmax(120px, 1fr)) auto auto;
|
||||
}
|
||||
|
||||
.system-log-search-form {
|
||||
grid-template-columns: repeat(4, minmax(120px, 1fr)) auto auto;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
@@ -343,6 +352,8 @@ h2 {
|
||||
.book-form select,
|
||||
.reader-form input,
|
||||
.reader-form select,
|
||||
.user-form input,
|
||||
.user-form select,
|
||||
.borrow-form input {
|
||||
width: 100%;
|
||||
min-height: 42px;
|
||||
@@ -359,6 +370,8 @@ h2 {
|
||||
.book-form select:focus,
|
||||
.reader-form input:focus,
|
||||
.reader-form select:focus,
|
||||
.user-form input:focus,
|
||||
.user-form select:focus,
|
||||
.borrow-form input:focus {
|
||||
outline: 3px solid rgba(37, 111, 108, 0.18);
|
||||
border-color: var(--color-primary);
|
||||
@@ -380,6 +393,11 @@ h2 {
|
||||
min-width: 980px;
|
||||
}
|
||||
|
||||
.user-table,
|
||||
.system-log-table {
|
||||
min-width: 980px;
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 12px 10px;
|
||||
@@ -445,6 +463,21 @@ h2 {
|
||||
background: #eef1f5;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
color: var(--color-success);
|
||||
background: #edf8ef;
|
||||
}
|
||||
|
||||
.status-failure {
|
||||
color: #7a211a;
|
||||
background: #fff0ee;
|
||||
}
|
||||
|
||||
.status-unknown {
|
||||
color: var(--color-muted);
|
||||
background: #eef1f5;
|
||||
}
|
||||
|
||||
.status-overdue {
|
||||
color: #7a211a;
|
||||
background: #fff0ee;
|
||||
@@ -472,6 +505,7 @@ h2 {
|
||||
|
||||
.book-form,
|
||||
.reader-form,
|
||||
.user-form,
|
||||
.borrow-form {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
|
||||
Reference in New Issue
Block a user