完成报表中心
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
<a href="${pageContext.request.contextPath}/books">Books</a>
|
||||
<a href="${pageContext.request.contextPath}/readers">Readers</a>
|
||||
<a href="${pageContext.request.contextPath}/borrowing">Borrowing</a>
|
||||
<a href="${pageContext.request.contextPath}/reports">Reports</a>
|
||||
</c:if>
|
||||
<a href="${pageContext.request.contextPath}/reader/home">Reader</a>
|
||||
<c:if test="${sessionScope.userRole == 'reader'}">
|
||||
|
||||
@@ -52,6 +52,12 @@
|
||||
<p>Create loans, process returns, renew active records, and review overdue items.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/borrowing">Open</a>
|
||||
</article>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>Report Center</h2>
|
||||
<p>Review inventory health, borrowing counts, overdue records, and popular books.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/reports">Open</a>
|
||||
</article>
|
||||
</c:if>
|
||||
|
||||
<article class="workspace-card">
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Reports - 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="reports-title">
|
||||
<div>
|
||||
<p class="eyebrow">Reports</p>
|
||||
<h1 id="reports-title">Report center</h1>
|
||||
<p>Review collection inventory, borrowing health, overdue loans, and popular books.</p>
|
||||
</div>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/borrowing">Borrowing records</a>
|
||||
</section>
|
||||
|
||||
<c:if test="${not empty errorMessage}">
|
||||
<div class="message message-error" role="alert">
|
||||
<c:out value="${errorMessage}" />
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<c:if test="${not empty reportCenter}">
|
||||
<section class="report-grid" aria-label="Report summary">
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Inventory</p>
|
||||
<h2>Total titles</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.inventorySummary.totalTitles}" /></p>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Inventory</p>
|
||||
<h2>Total copies</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.inventorySummary.totalCopies}" /></p>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Inventory</p>
|
||||
<h2>Available copies</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.inventorySummary.availableCopies}" /></p>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Attention</p>
|
||||
<h2>Unavailable or empty</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.inventorySummary.unavailableOrEmptyTitles}" /></p>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Borrowing</p>
|
||||
<h2>Currently borrowed</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.borrowingSummary.activeLoans}" /></p>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<p class="eyebrow">Borrowing</p>
|
||||
<h2>Returned records</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.borrowingSummary.returnedLoans}" /></p>
|
||||
</article>
|
||||
<article class="report-card report-card-alert">
|
||||
<p class="eyebrow">Borrowing</p>
|
||||
<h2>Overdue loans</h2>
|
||||
<p class="report-metric"><c:out value="${reportCenter.borrowingSummary.overdueLoans}" /></p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="table-panel" aria-labelledby="overdue-report-title">
|
||||
<h2 id="overdue-report-title">Overdue list</h2>
|
||||
<c:choose>
|
||||
<c:when test="${empty reportCenter.overdueRows}">
|
||||
<p class="empty-state">No active overdue borrowing records.</p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="table-scroll">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Reader</th>
|
||||
<th scope="col">Book</th>
|
||||
<th scope="col">Due date</th>
|
||||
<th scope="col">Overdue days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<c:forEach var="row" items="${reportCenter.overdueRows}">
|
||||
<tr>
|
||||
<td>
|
||||
<strong><c:out value="${row.readerIdentifier}" /></strong>
|
||||
<div class="muted-text"><c:out value="${row.readerName}" /></div>
|
||||
</td>
|
||||
<td>
|
||||
<strong><c:out value="${row.bookIdentifier}" /></strong>
|
||||
<div class="muted-text"><c:out value="${row.bookTitle}" /></div>
|
||||
</td>
|
||||
<td><c:out value="${row.dueAtText}" /></td>
|
||||
<td>
|
||||
<span class="status-pill status-overdue">
|
||||
<c:out value="${row.overdueDays}" /> days
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</section>
|
||||
|
||||
<section class="table-panel" aria-labelledby="popular-report-title">
|
||||
<h2 id="popular-report-title">Popular borrowing ranking</h2>
|
||||
<c:choose>
|
||||
<c:when test="${empty reportCenter.popularBooks}">
|
||||
<p class="empty-state">No borrowing records are available for ranking yet.</p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="table-scroll">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Book</th>
|
||||
<th scope="col">Author</th>
|
||||
<th scope="col">Borrow records</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<c:forEach var="row" items="${reportCenter.popularBooks}">
|
||||
<tr>
|
||||
<td>
|
||||
<strong><c:out value="${row.bookIdentifier}" /></strong>
|
||||
<div class="muted-text"><c:out value="${row.title}" /></div>
|
||||
</td>
|
||||
<td><c:out value="${row.author}" /></td>
|
||||
<td><c:out value="${row.borrowCount}" /></td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</section>
|
||||
</c:if>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -45,6 +45,12 @@
|
||||
<p>Create loans, process returns, renew records, and review overdue items.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/borrowing">Manage borrowing</a>
|
||||
</article>
|
||||
|
||||
<article class="workspace-card">
|
||||
<h2>Report Center</h2>
|
||||
<p>Review inventory summaries, borrowing health, overdue lists, and popular books.</p>
|
||||
<a class="button button-secondary" href="${pageContext.request.contextPath}/reports">View reports</a>
|
||||
</article>
|
||||
</c:if>
|
||||
|
||||
<c:if test="${sessionScope.userRole == 'reader'}">
|
||||
|
||||
@@ -132,6 +132,15 @@
|
||||
<url-pattern>/reader/loans</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>ReportServlet</servlet-name>
|
||||
<servlet-class>com.mzh.library.controller.ReportServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>ReportServlet</servlet-name>
|
||||
<url-pattern>/reports</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>UnauthorizedServlet</servlet-name>
|
||||
<servlet-class>com.mzh.library.controller.UnauthorizedServlet</servlet-class>
|
||||
|
||||
@@ -238,6 +238,13 @@ h2 {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.report-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.workspace-card {
|
||||
min-height: 190px;
|
||||
display: flex;
|
||||
@@ -254,6 +261,32 @@ h2 {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.report-card {
|
||||
min-height: 150px;
|
||||
padding: 20px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
background: var(--color-panel);
|
||||
box-shadow: var(--shadow-panel);
|
||||
}
|
||||
|
||||
.report-card h2 {
|
||||
color: var(--color-muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.report-card-alert {
|
||||
border-color: rgba(181, 66, 56, 0.28);
|
||||
}
|
||||
|
||||
.report-metric {
|
||||
margin-bottom: 0;
|
||||
color: var(--color-primary-strong);
|
||||
font-size: 34px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.notice-panel {
|
||||
max-width: 680px;
|
||||
padding: 28px;
|
||||
@@ -491,6 +524,7 @@ h2 {
|
||||
.notice-panel,
|
||||
.dashboard-hero,
|
||||
.workspace-card,
|
||||
.report-card,
|
||||
.toolbar-panel,
|
||||
.table-panel,
|
||||
.form-panel {
|
||||
|
||||
Reference in New Issue
Block a user