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
parent
ae9a42fa70
commit
b2b6d740e3
|
|
@ -19,7 +19,7 @@
|
|||
<section class="left-column">
|
||||
<section class="panel panel-scan">
|
||||
<h2 class="section-title"><span class="section-icon">🖶</span> SCAN QR CODE</h2>
|
||||
<form id="scan-form" autocomplete="off" novalidate>
|
||||
<form id="scan-form" autocomplete="off" novalidate onsubmit="return false;">
|
||||
@Html.AntiForgeryToken()
|
||||
<div class="scan-input-wrap">
|
||||
<span class="scan-icon">⚙</span>
|
||||
|
|
@ -27,11 +27,11 @@
|
|||
name="qrValue"
|
||||
class="scan-input"
|
||||
type="text"
|
||||
placeholder="Scan QR code or enter manually..."
|
||||
placeholder="Use scanner or paste (typing is not allowed)…"
|
||||
autofocus
|
||||
required />
|
||||
spellcheck="false" />
|
||||
</div>
|
||||
<div class="form-hint">Format: modelnumber;uniquenumber</div>
|
||||
<div class="form-hint">Format: modelnumber;uniquenumber — scans submit automatically.</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
|
@ -69,52 +69,113 @@
|
|||
</section>
|
||||
|
||||
<section class="panel panel-history">
|
||||
<h2 class="section-title"><span class="section-icon">🗎</span> LAST 5 SCANNED RECORDS</h2>
|
||||
<div class="table-responsive history-wrap">
|
||||
<table class="table table-sm align-middle" id="history-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>MODEL NUMBER</th>
|
||||
<th>UNIQUE NUMBER</th>
|
||||
<th>STATUS</th>
|
||||
<th>MARKED AT</th>
|
||||
<th>MARKED BY</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
var rowNumber = 1;
|
||||
}
|
||||
@foreach (var item in Model.RecentScans)
|
||||
{
|
||||
<tr>
|
||||
<td>@rowNumber</td>
|
||||
<td>@item.ModelNumber</td>
|
||||
<td>@item.UniqueNumber</td>
|
||||
<td>
|
||||
<span class="status-pill @(item.ShipmentMarked ? "status-true" : "status-false")">
|
||||
@(item.ShipmentMarked ? "TRUE" : "FALSE")
|
||||
</span>
|
||||
</td>
|
||||
<td>@DateTime.SpecifyKind(item.ProcessedAtUtc, DateTimeKind.Utc).ToLocalTime().ToString("dd MMM yyyy h:mm tt")</td>
|
||||
<td>System</td>
|
||||
</tr>
|
||||
rowNumber++;
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="history-empty @(Model.RecentScans.Count > 0 ? "is-hidden" : string.Empty)" id="history-empty">
|
||||
<div class="empty-icon">📦</div>
|
||||
<div class="empty-title">No records found</div>
|
||||
<div class="empty-text">Scan a QR code to see history here.</div>
|
||||
<div class="panel-history-head">
|
||||
<h2 class="section-title section-title-tight"><span class="section-icon">🗎</span> RECENT SCANS</h2>
|
||||
<button type="button" class="btn-scan-history" id="btn-open-scan-history">History</button>
|
||||
</div>
|
||||
<div class="history-split">
|
||||
<div class="history-column">
|
||||
<h3 class="history-subtitle">Successful Scans</h3>
|
||||
<div class="table-responsive history-wrap">
|
||||
<table class="table table-sm align-middle" id="history-success-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>MODEL NUMBER</th>
|
||||
<th>UNIQUE NUMBER</th>
|
||||
<th>STATUS</th>
|
||||
<th>MARKED AT</th>
|
||||
<th>MARKED BY</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="history-success-body">
|
||||
@{
|
||||
var rowNumber = 1;
|
||||
}
|
||||
@foreach (var item in Model.RecentSuccessfulScans)
|
||||
{
|
||||
<tr>
|
||||
<td>@rowNumber</td>
|
||||
<td>@item.ModelNumber</td>
|
||||
<td>@item.UniqueNumber</td>
|
||||
<td>
|
||||
<span class="status-pill status-true">TRUE</span>
|
||||
</td>
|
||||
<td>@DateTime.SpecifyKind(item.ProcessedAtUtc, DateTimeKind.Utc).ToLocalTime().ToString("dd MMM yyyy h:mm tt")</td>
|
||||
<td>System</td>
|
||||
</tr>
|
||||
rowNumber++;
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="history-empty @(Model.RecentSuccessfulScans.Count > 0 ? "is-hidden" : string.Empty)" id="history-success-empty">
|
||||
<div class="empty-icon">📦</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">⚠</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>
|
||||
</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">×</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>
|
||||
|
||||
<footer class="page-footer">
|
||||
<span>AVS C# .NET Application</span>
|
||||
<span>AVS BY TECHNOLOGY</span>
|
||||
<span>|</span>
|
||||
<span>SHIPPING MARK</span>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -213,6 +213,157 @@ body {
|
|||
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 {
|
||||
border: 1px solid #d7e1f0;
|
||||
border-radius: 6px;
|
||||
|
|
@ -312,4 +463,8 @@ body {
|
|||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.history-split {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue