Refresh dashboard UI for scan results and history

Updates the Razor dashboard layout, scan/result panels, rejected/successful history display, modal history view, and related styling.
main
SYED MUSTUFA AHMED NAQVI 2026-05-15 11:46:35 +05:00
parent ae9a42fa70
commit b2b6d740e3
2 changed files with 260 additions and 44 deletions

View File

@ -19,7 +19,7 @@
<section class="left-column"> <section class="left-column">
<section class="panel panel-scan"> <section class="panel panel-scan">
<h2 class="section-title"><span class="section-icon">&#128438;</span> SCAN QR CODE</h2> <h2 class="section-title"><span class="section-icon">&#128438;</span> SCAN QR CODE</h2>
<form id="scan-form" autocomplete="off" novalidate> <form id="scan-form" autocomplete="off" novalidate onsubmit="return false;">
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
<div class="scan-input-wrap"> <div class="scan-input-wrap">
<span class="scan-icon">&#9881;</span> <span class="scan-icon">&#9881;</span>
@ -27,11 +27,11 @@
name="qrValue" name="qrValue"
class="scan-input" class="scan-input"
type="text" type="text"
placeholder="Scan QR code or enter manually..." placeholder="Use scanner or paste (typing is not allowed)…"
autofocus autofocus
required /> spellcheck="false" />
</div> </div>
<div class="form-hint">Format: modelnumber;uniquenumber</div> <div class="form-hint">Format: modelnumber;uniquenumber — scans submit automatically.</div>
</form> </form>
</section> </section>
@ -69,52 +69,113 @@
</section> </section>
<section class="panel panel-history"> <section class="panel panel-history">
<h2 class="section-title"><span class="section-icon">&#128462;</span> LAST 5 SCANNED RECORDS</h2> <div class="panel-history-head">
<div class="table-responsive history-wrap"> <h2 class="section-title section-title-tight"><span class="section-icon">&#128462;</span> RECENT SCANS</h2>
<table class="table table-sm align-middle" id="history-table"> <button type="button" class="btn-scan-history" id="btn-open-scan-history">History</button>
<thead> </div>
<tr> <div class="history-split">
<th>#</th> <div class="history-column">
<th>MODEL NUMBER</th> <h3 class="history-subtitle">Successful Scans</h3>
<th>UNIQUE NUMBER</th> <div class="table-responsive history-wrap">
<th>STATUS</th> <table class="table table-sm align-middle" id="history-success-table">
<th>MARKED AT</th> <thead>
<th>MARKED BY</th> <tr>
</tr> <th>#</th>
</thead> <th>MODEL NUMBER</th>
<tbody> <th>UNIQUE NUMBER</th>
@{ <th>STATUS</th>
var rowNumber = 1; <th>MARKED AT</th>
} <th>MARKED BY</th>
@foreach (var item in Model.RecentScans) </tr>
{ </thead>
<tr> <tbody id="history-success-body">
<td>@rowNumber</td> @{
<td>@item.ModelNumber</td> var rowNumber = 1;
<td>@item.UniqueNumber</td> }
<td> @foreach (var item in Model.RecentSuccessfulScans)
<span class="status-pill @(item.ShipmentMarked ? "status-true" : "status-false")"> {
@(item.ShipmentMarked ? "TRUE" : "FALSE") <tr>
</span> <td>@rowNumber</td>
</td> <td>@item.ModelNumber</td>
<td>@DateTime.SpecifyKind(item.ProcessedAtUtc, DateTimeKind.Utc).ToLocalTime().ToString("dd MMM yyyy h:mm tt")</td> <td>@item.UniqueNumber</td>
<td>System</td> <td>
</tr> <span class="status-pill status-true">TRUE</span>
rowNumber++; </td>
} <td>@DateTime.SpecifyKind(item.ProcessedAtUtc, DateTimeKind.Utc).ToLocalTime().ToString("dd MMM yyyy h:mm tt")</td>
</tbody> <td>System</td>
</table> </tr>
<div class="history-empty @(Model.RecentScans.Count > 0 ? "is-hidden" : string.Empty)" id="history-empty"> rowNumber++;
<div class="empty-icon">&#128230;</div> }
<div class="empty-title">No records found</div> </tbody>
<div class="empty-text">Scan a QR code to see history here.</div> </table>
<div class="history-empty @(Model.RecentSuccessfulScans.Count > 0 ? "is-hidden" : string.Empty)" id="history-success-empty">
<div class="empty-icon">&#128230;</div>
<div class="empty-title">No successful scans yet</div>
<div class="empty-text">Verified cartons will appear here.</div>
</div>
</div>
</div>
<div class="history-column">
<h3 class="history-subtitle">Rejected Scans</h3>
<div class="table-responsive history-wrap">
<table class="table table-sm align-middle" id="history-rejected-table">
<thead>
<tr>
<th>TIME</th>
<th>SCANNED / INPUT</th>
<th>MODEL</th>
<th>UNIQUE</th>
<th>REASON</th>
</tr>
</thead>
<tbody id="history-rejected-body">
@foreach (var item in Model.RecentRejectedScans)
{
<tr>
<td>@DateTime.SpecifyKind(item.ProcessedAtUtc, DateTimeKind.Utc).ToLocalTime().ToString("dd MMM yyyy h:mm tt")</td>
<td>@item.RawInputValue</td>
<td>@(string.IsNullOrEmpty(item.ModelNumber) ? "—" : item.ModelNumber)</td>
<td>@(string.IsNullOrEmpty(item.UniqueNumber) ? "—" : item.UniqueNumber)</td>
<td>@item.RejectionReason</td>
</tr>
}
</tbody>
</table>
<div class="history-empty @(Model.RecentRejectedScans.Count > 0 ? "is-hidden" : string.Empty)" id="history-rejected-empty">
<div class="empty-icon">&#9888;</div>
<div class="empty-title">No rejected scans</div>
<div class="empty-text">Rejections will appear here with a reason.</div>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
<dialog id="scan-history-dialog" class="scan-history-dialog" aria-labelledby="scan-history-dialog-title">
<div class="scan-history-dialog-content">
<header class="scan-history-dialog-header">
<h2 id="scan-history-dialog-title" class="scan-history-dialog-title">Scan history</h2>
<button type="button" class="scan-history-dialog-close" id="scan-history-dialog-close" aria-label="Close">&times;</button>
</header>
<div class="scan-history-dialog-toolbar">
<label class="scan-history-filter-label" for="scan-history-filter">Show</label>
<select id="scan-history-filter" class="scan-history-filter" aria-label="Record type">
<option value="successful">Successful Records</option>
<option value="rejected">Rejected Records</option>
</select>
</div>
<div class="scan-history-dialog-body table-responsive">
<table class="table table-sm align-middle" id="scan-history-modal-table">
<thead id="scan-history-modal-thead"></thead>
<tbody id="scan-history-modal-tbody"></tbody>
</table>
</div>
</div>
</dialog>
</main> </main>
<footer class="page-footer"> <footer class="page-footer">
<span>AVS C# .NET Application</span> <span>AVS BY TECHNOLOGY</span>
<span>|</span> <span>|</span>
<span>SHIPPING MARK</span> <span>SHIPPING MARK</span>
</footer> </footer>

View File

@ -213,6 +213,157 @@ body {
padding-bottom: 0; padding-bottom: 0;
} }
.panel-history-head {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 10px;
}
.section-title-tight {
margin-bottom: 0;
}
.btn-scan-history {
appearance: none;
border: 1px solid #1b5caa;
background: #0d4a9c;
color: #fff;
font-weight: 700;
font-size: 0.85rem;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
letter-spacing: 0.2px;
}
.btn-scan-history:hover {
background: #0a3d82;
}
.btn-scan-history:focus-visible {
outline: 2px solid #5a9fd4;
outline-offset: 2px;
}
.scan-history-dialog {
border: none;
border-radius: 10px;
padding: 0;
max-width: min(980px, 96vw);
width: 100%;
max-height: 88vh;
box-shadow: 0 16px 48px rgba(11, 36, 79, 0.35);
color: #223a5f;
}
.scan-history-dialog::backdrop {
background: rgba(11, 47, 107, 0.5);
}
.scan-history-dialog-content {
display: flex;
flex-direction: column;
max-height: 88vh;
}
.scan-history-dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 12px 14px;
border-bottom: 1px solid #e2e8f2;
background: #f6f9fd;
}
.scan-history-dialog-title {
margin: 0;
font-size: 1.05rem;
font-weight: 800;
color: #0d3b7a;
}
.scan-history-dialog-close {
appearance: none;
border: 0;
background: transparent;
color: #4f6687;
font-size: 1.75rem;
line-height: 1;
cursor: pointer;
padding: 4px 8px;
border-radius: 6px;
}
.scan-history-dialog-close:hover {
background: #e8eef8;
color: #223a5f;
}
.scan-history-dialog-toolbar {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-bottom: 1px solid #e2e8f2;
flex-wrap: wrap;
}
.scan-history-filter-label {
font-weight: 700;
font-size: 0.88rem;
color: #2b456f;
}
.scan-history-filter {
min-width: 200px;
padding: 6px 10px;
border-radius: 6px;
border: 1px solid #c5d4e8;
background: #fff;
color: #223a5f;
font-size: 0.88rem;
}
.scan-history-dialog-body {
flex: 1;
overflow: auto;
padding: 0 14px 14px;
min-height: 120px;
}
.scan-history-dialog-body .table {
margin-top: 10px;
}
.scan-history-modal-empty {
text-align: center;
padding: 24px 12px;
color: #657a98;
font-size: 0.95rem;
}
.history-split {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.history-column {
min-width: 0;
}
.history-subtitle {
margin: 0 0 8px;
font-size: 0.82rem;
font-weight: 800;
color: #1a4a8c;
letter-spacing: 0.15px;
}
.history-wrap { .history-wrap {
border: 1px solid #d7e1f0; border: 1px solid #d7e1f0;
border-radius: 6px; border-radius: 6px;
@ -312,4 +463,8 @@ body {
.info-grid { .info-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.history-split {
grid-template-columns: 1fr;
}
} }