+
+
+
-
-
+
+
+
+
+
+
+
+
+ 📦
+
+
+
+ 📅
+
+
+
+ 📅
+
+
+
+ 📦
+
+
+
+
+
+
+ Cartons Moved to Warehouse by Day
+
+
+
+
+
+
+
+
+ Day-wise Movement Details
+
+
+
+
+ | DATE |
+ MOVED CARTONS |
+ SUCCESSFUL SCANS |
+ DUPLICATE REJECTIONS |
+ MANUAL ENTRY REJECTIONS |
+ LAST MOVED AT |
+
+
+
+
+
+
+
+
@@ -181,5 +276,7 @@
@section Scripts {
+
+
}
diff --git a/wwwroot/css/site.css b/wwwroot/css/site.css
index 04c87fe..96114ee 100644
--- a/wwwroot/css/site.css
+++ b/wwwroot/css/site.css
@@ -62,10 +62,45 @@ body {
gap: 10px;
}
-.left-column,
-.right-column {
+.kpi-grid {
display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
+ align-items: stretch;
+}
+
+.section-title-center {
+ justify-content: center;
+}
+
+.panel-available-cartons {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ min-height: 100%;
+}
+
+.available-cartons-value {
+ font-size: 3.25rem;
+ font-weight: 800;
+ color: #0d3b7a;
+ line-height: 1.05;
+ letter-spacing: -0.5px;
+ margin: 4px 0 2px;
+}
+
+.available-cartons-subtitle {
+ color: #637898;
+ font-size: 1.05rem;
+ font-weight: 600;
+}
+
+.panel-status,
+.panel-verification-result,
+.panel-scanned-details {
+ min-height: 100%;
}
.panel {
@@ -259,6 +294,10 @@ body {
color: #223a5f;
}
+.scan-history-dialog--wide {
+ max-width: min(1180px, 98vw);
+}
+
.scan-history-dialog::backdrop {
background: rgba(11, 47, 107, 0.5);
}
@@ -328,6 +367,40 @@ body {
font-size: 0.88rem;
}
+.scan-history-mode-toolbar {
+ align-items: center;
+ gap: 10px;
+}
+
+.btn-history-mode {
+ appearance: none;
+ border: 1px solid #1b5caa;
+ background: #fff;
+ color: #0d4a9c;
+ font-weight: 700;
+ font-size: 0.85rem;
+ padding: 7px 14px;
+ border-radius: 6px;
+ cursor: pointer;
+ white-space: nowrap;
+ margin-left: 4px;
+}
+
+.btn-history-mode:hover {
+ background: #ebf4ff;
+}
+
+.btn-history-mode.is-active {
+ background: #0d4a9c;
+ color: #fff;
+ border-color: #0d4a9c;
+}
+
+.btn-history-mode:focus-visible {
+ outline: 2px solid #5a9fd4;
+ outline-offset: 2px;
+}
+
.scan-history-dialog-body {
flex: 1;
overflow: auto;
@@ -346,6 +419,233 @@ body {
font-size: 0.95rem;
}
+.is-hidden {
+ display: none !important;
+}
+
+.scan-history-analytics-view {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+}
+
+.analytics-toolbar {
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 12px;
+}
+
+.analytics-toolbar-filters {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 8px 10px;
+}
+
+.analytics-date-input {
+ padding: 6px 8px;
+ border-radius: 6px;
+ border: 1px solid #c5d4e8;
+ background: #fff;
+ color: #223a5f;
+ font-size: 0.88rem;
+}
+
+.analytics-date-sep {
+ color: #637898;
+ font-weight: 600;
+}
+
+.btn-analytics-export {
+ appearance: none;
+ border: 1px solid #1b5caa;
+ background: #fff;
+ color: #0d4a9c;
+ font-weight: 700;
+ font-size: 0.85rem;
+ padding: 7px 14px;
+ border-radius: 6px;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.btn-analytics-export:hover {
+ background: #ebf4ff;
+}
+
+.analytics-summary-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 10px;
+ padding: 12px 14px 0;
+}
+
+.analytics-summary-card {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ background: #fff;
+ border: 1px solid #e2e8f2;
+ border-radius: 8px;
+ padding: 12px;
+ box-shadow: 0 1px 2px rgba(16, 36, 79, 0.05);
+}
+
+.analytics-summary-icon {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ display: grid;
+ place-items: center;
+ font-size: 1.1rem;
+ flex-shrink: 0;
+}
+
+.analytics-icon-box {
+ background: #e8f0fa;
+ color: #0d4a9c;
+}
+
+.analytics-icon-week {
+ background: #e4f5e8;
+ color: #2f9950;
+}
+
+.analytics-icon-month {
+ background: #e8f0fa;
+ color: #0d4a9c;
+}
+
+.analytics-icon-total {
+ background: #e8f0fa;
+ color: #0d4a9c;
+}
+
+.analytics-summary-value {
+ font-size: 1.35rem;
+ font-weight: 800;
+ color: #0d3b7a;
+ line-height: 1.1;
+}
+
+.analytics-summary-label {
+ font-size: 0.82rem;
+ color: #637898;
+ font-weight: 600;
+ margin-top: 2px;
+}
+
+.analytics-charts-row {
+ display: grid;
+ grid-template-columns: 1.4fr 1fr;
+ gap: 12px;
+ padding: 12px 14px 0;
+}
+
+.analytics-chart-panel {
+ background: #fff;
+ border: 1px solid #e2e8f2;
+ border-radius: 8px;
+ padding: 12px;
+ min-height: 280px;
+}
+
+.analytics-donut-panel {
+ display: flex;
+ flex-direction: column;
+}
+
+.analytics-panel-title {
+ margin: 0 0 10px;
+ font-size: 0.9rem;
+ font-weight: 800;
+ color: #163b72;
+}
+
+.analytics-bar-chart-wrap {
+ height: 220px;
+ position: relative;
+}
+
+.analytics-donut-wrap {
+ height: 180px;
+ position: relative;
+ flex: 1;
+}
+
+.analytics-legend {
+ list-style: none;
+ margin: 8px 0 0;
+ padding: 0;
+ font-size: 0.88rem;
+ color: #2a436a;
+}
+
+.analytics-legend li {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 6px;
+}
+
+.analytics-legend-swatch {
+ width: 12px;
+ height: 12px;
+ border-radius: 2px;
+ flex-shrink: 0;
+}
+
+.analytics-legend-moved {
+ background: #3cae58;
+}
+
+.analytics-legend-pending {
+ background: #d5dde8;
+}
+
+.analytics-table-section {
+ padding: 12px 14px;
+}
+
+.analytics-table-wrap {
+ border: 1px solid #d7e1f0;
+ border-radius: 6px;
+ overflow: hidden;
+ max-height: 220px;
+}
+
+.analytics-table-wrap .table thead th {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+}
+
+.scan-history-dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ padding: 10px 14px 14px;
+ border-top: 1px solid #e2e8f2;
+ margin-top: auto;
+}
+
+.btn-scan-history-dialog-close {
+ appearance: none;
+ border: 1px solid #c5d4e8;
+ background: #fff;
+ color: #2b456f;
+ font-weight: 700;
+ font-size: 0.88rem;
+ padding: 8px 18px;
+ border-radius: 6px;
+ cursor: pointer;
+}
+
+.btn-scan-history-dialog-close:hover {
+ background: #f3f6fb;
+}
+
.history-split {
display: grid;
grid-template-columns: 1fr 1fr;
@@ -460,6 +760,10 @@ body {
grid-template-columns: 1fr;
}
+ .kpi-grid {
+ grid-template-columns: 1fr;
+ }
+
.info-grid {
grid-template-columns: 1fr;
}
@@ -467,4 +771,16 @@ body {
.history-split {
grid-template-columns: 1fr;
}
+
+ .available-cartons-value {
+ font-size: 2.75rem;
+ }
+
+ .analytics-summary-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .analytics-charts-row {
+ grid-template-columns: 1fr;
+ }
}
diff --git a/wwwroot/js/movement-analytics.js b/wwwroot/js/movement-analytics.js
new file mode 100644
index 0000000..5224428
--- /dev/null
+++ b/wwwroot/js/movement-analytics.js
@@ -0,0 +1,398 @@
+(() => {
+ const pollIntervalMs = 5000;
+
+ let barChart = null;
+ let donutChart = null;
+ let pollTimer = null;
+ let lastAnalytics = null;
+
+ const els = {
+ dialog: () => document.getElementById("scan-history-dialog"),
+ recordsView: () => document.getElementById("scan-history-records-view"),
+ analyticsView: () => document.getElementById("scan-history-analytics-view"),
+ filterRecords: () => document.getElementById("scan-history-filter"),
+ btnAnalytics: () => document.getElementById("btn-movement-analytics"),
+ dateFrom: () => document.getElementById("analytics-date-from"),
+ dateTo: () => document.getElementById("analytics-date-to"),
+ exportBtn: () => document.getElementById("btn-analytics-export"),
+ footerClose: () => document.getElementById("btn-scan-history-dialog-close"),
+ summaryToday: () => document.getElementById("summary-moved-today"),
+ summaryWeek: () => document.getElementById("summary-moved-week"),
+ summaryMonth: () => document.getElementById("summary-moved-month"),
+ summaryTotal: () => document.getElementById("summary-total-moved"),
+ dayTbody: () => document.getElementById("analytics-day-tbody"),
+ legend: () => document.getElementById("analytics-status-legend")
+ };
+
+ function formatCount(value) {
+ if (value === null || value === undefined || Number.isNaN(Number(value))) {
+ return "0";
+ }
+
+ return Number(value).toLocaleString("en-US");
+ }
+
+ function toInputDate(date) {
+ const y = date.getFullYear();
+ const m = String(date.getMonth() + 1).padStart(2, "0");
+ const d = String(date.getDate()).padStart(2, "0");
+ return `${y}-${m}-${d}`;
+ }
+
+ function setDefaultDateRange() {
+ const dateFrom = els.dateFrom();
+ const dateTo = els.dateTo();
+ if (!dateFrom || !dateTo) {
+ return;
+ }
+
+ const to = new Date();
+ const from = new Date();
+ from.setDate(from.getDate() - 6);
+ dateTo.value = toInputDate(to);
+ dateFrom.value = toInputDate(from);
+ }
+
+ function setModeButtonActive(active) {
+ const btn = els.btnAnalytics();
+ if (!btn) {
+ return;
+ }
+
+ btn.classList.toggle("is-active", active);
+ btn.setAttribute("aria-pressed", active ? "true" : "false");
+ }
+
+ function setAnalyticsVisible(visible) {
+ const recordsView = els.recordsView();
+ const analyticsView = els.analyticsView();
+ const dialog = els.dialog();
+ if (!recordsView || !analyticsView) {
+ return;
+ }
+
+ if (visible) {
+ recordsView.classList.add("is-hidden");
+ recordsView.hidden = true;
+ analyticsView.classList.remove("is-hidden");
+ analyticsView.hidden = false;
+ dialog?.classList.add("scan-history-dialog--wide");
+ setModeButtonActive(true);
+ setDefaultDateRange();
+ void refreshMovementAnalytics();
+ startPolling();
+ return;
+ }
+
+ analyticsView.classList.add("is-hidden");
+ analyticsView.hidden = true;
+ recordsView.classList.remove("is-hidden");
+ recordsView.hidden = false;
+ dialog?.classList.remove("scan-history-dialog--wide");
+ setModeButtonActive(false);
+ stopPolling();
+ }
+
+ function startPolling() {
+ stopPolling();
+ pollTimer = window.setInterval(() => {
+ if (els.analyticsView()?.hidden) {
+ return;
+ }
+
+ void refreshMovementAnalytics();
+ }, pollIntervalMs);
+ }
+
+ function stopPolling() {
+ if (pollTimer !== null) {
+ window.clearInterval(pollTimer);
+ pollTimer = null;
+ }
+ }
+
+ async function refreshMovementAnalytics() {
+ const dateFrom = els.dateFrom();
+ const dateTo = els.dateTo();
+ if (!dateFrom?.value || !dateTo?.value) {
+ return;
+ }
+
+ const url = `/api/verification/movement-analytics?from=${encodeURIComponent(dateFrom.value)}&to=${encodeURIComponent(dateTo.value)}`;
+
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ return;
+ }
+
+ const data = await response.json();
+ lastAnalytics = data;
+ renderMovementAnalytics(data);
+ } catch {
+ // Keep last rendered values on failure.
+ }
+ }
+
+ function renderMovementAnalytics(data) {
+ const summary = data.summary ?? {};
+ const statusShare = data.statusShare ?? {};
+
+ if (els.summaryToday()) {
+ els.summaryToday().textContent = formatCount(summary.movedToday);
+ }
+
+ if (els.summaryWeek()) {
+ els.summaryWeek().textContent = formatCount(summary.movedThisWeek);
+ }
+
+ if (els.summaryMonth()) {
+ els.summaryMonth().textContent = formatCount(summary.movedThisMonth);
+ }
+
+ if (els.summaryTotal()) {
+ els.summaryTotal().textContent = formatCount(summary.totalMoved);
+ }
+
+ renderBarChart(data.chartByDay ?? []);
+ renderDonutChart(statusShare);
+ renderDayTable(data.dayDetails ?? []);
+ }
+
+ function renderBarChart(chartByDay) {
+ const canvas = document.getElementById("analytics-bar-chart");
+ if (!canvas || typeof Chart === "undefined") {
+ return;
+ }
+
+ const labels = chartByDay.map(x => x.dayLabel);
+ const values = chartByDay.map(x => x.movedCartons);
+
+ if (barChart) {
+ barChart.destroy();
+ }
+
+ barChart = new Chart(canvas, {
+ type: "bar",
+ data: {
+ labels,
+ datasets: [{
+ label: "Moved cartons",
+ data: values,
+ backgroundColor: "#1a5fb4",
+ borderRadius: 4,
+ maxBarThickness: 48
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: true,
+ position: "bottom",
+ labels: { boxWidth: 12, color: "#4f6687" }
+ }
+ },
+ scales: {
+ x: {
+ grid: { display: false },
+ ticks: { color: "#4f6687" }
+ },
+ y: {
+ beginAtZero: true,
+ title: { display: true, text: "Cartons", color: "#4f6687" },
+ ticks: { color: "#4f6687" }
+ }
+ }
+ }
+ });
+ }
+
+ function renderDonutChart(statusShare) {
+ const canvas = document.getElementById("analytics-donut-chart");
+ const legend = els.legend();
+ if (!canvas || typeof Chart === "undefined") {
+ return;
+ }
+
+ const moved = statusShare.moved ?? 0;
+ const pending = statusShare.pending ?? 0;
+ const total = statusShare.total ?? moved + pending;
+ const movedPct = statusShare.movedPercent ?? (total > 0 ? Math.round(moved * 1000 / total) / 10 : 0);
+ const pendingPct = statusShare.pendingPercent ?? (total > 0 ? Math.round(pending * 1000 / total) / 10 : 0);
+
+ if (donutChart) {
+ donutChart.destroy();
+ }
+
+ donutChart = new Chart(canvas, {
+ type: "doughnut",
+ data: {
+ labels: ["Moved", "Pending"],
+ datasets: [{
+ data: [moved, pending],
+ backgroundColor: ["#3cae58", "#d5dde8"],
+ borderWidth: 0
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ cutout: "62%",
+ plugins: {
+ legend: { display: false },
+ tooltip: { enabled: true }
+ }
+ },
+ plugins: [{
+ id: "centerText",
+ beforeDraw(chart) {
+ const { ctx, chartArea } = chart;
+ if (!chartArea) {
+ return;
+ }
+
+ ctx.save();
+ ctx.font = "bold 14px Segoe UI, sans-serif";
+ ctx.fillStyle = "#0d3b7a";
+ ctx.textAlign = "center";
+ ctx.textBaseline = "middle";
+ ctx.fillText(`Total ${formatCount(total)}`, (chartArea.left + chartArea.right) / 2, (chartArea.top + chartArea.bottom) / 2);
+ ctx.restore();
+ }
+ }]
+ });
+
+ if (legend) {
+ legend.innerHTML = `
+
Moved ${formatCount(moved)} (${movedPct}%)
+
Pending ${formatCount(pending)} (${pendingPct}%)`;
+ }
+ }
+
+ function formatLastMovedTime(utcValue) {
+ if (!utcValue) {
+ return "—";
+ }
+
+ const date = new Date(utcValue);
+ if (Number.isNaN(date.getTime())) {
+ return "—";
+ }
+
+ return date.toLocaleTimeString("en-US", {
+ hour: "numeric",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: true
+ });
+ }
+
+ function renderDayTable(dayDetails) {
+ const tbody = els.dayTbody();
+ if (!tbody) {
+ return;
+ }
+
+ if (!dayDetails.length) {
+ tbody.innerHTML = `
| No movement data for this date range. |
`;
+ return;
+ }
+
+ tbody.innerHTML = "";
+ dayDetails.forEach(row => {
+ const tr = document.createElement("tr");
+ tr.innerHTML = `
+
${escapeHtml(row.dateDisplay)} |
+
${formatCount(row.movedCartons)} |
+
${formatCount(row.successfulScans)} |
+
${formatCount(row.duplicateRejections)} |
+
${formatCount(row.manualEntryRejections)} |
+
${formatLastMovedTime(row.lastMovedAtUtc)} | `;
+ tbody.appendChild(tr);
+ });
+ }
+
+ function escapeHtml(value) {
+ return (value ?? "").replace(/[&<>\"']/g, char => ({
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ "\"": """,
+ "'": "'"
+ }[char]));
+ }
+
+ function exportDayDetailsCsv() {
+ if (!lastAnalytics?.dayDetails?.length) {
+ return;
+ }
+
+ const header = ["Date", "Moved Cartons", "Successful Scans", "Duplicate Rejections", "Manual Entry Rejections", "Last Moved At"];
+ const rows = lastAnalytics.dayDetails.map(row => [
+ row.dateDisplay,
+ row.movedCartons,
+ row.successfulScans,
+ row.duplicateRejections,
+ row.manualEntryRejections,
+ row.lastMovedAtUtc ? new Date(row.lastMovedAtUtc).toLocaleString() : ""
+ ]);
+
+ const csv = [header, ...rows]
+ .map(cols => cols.map(col => `"${String(col).replace(/"/g, '""')}"`).join(","))
+ .join("\r\n");
+
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
+ const link = document.createElement("a");
+ link.href = URL.createObjectURL(blob);
+ link.download = `movement-analytics-${els.dateFrom()?.value ?? "export"}.csv`;
+ link.click();
+ URL.revokeObjectURL(link.href);
+ }
+
+ function wire() {
+ const btnAnalytics = els.btnAnalytics();
+ const dateFrom = els.dateFrom();
+ const dateTo = els.dateTo();
+ const exportBtn = els.exportBtn();
+ const footerClose = document.getElementById("scan-history-dialog-close");
+ const footerCloseAlt = els.footerClose();
+
+ const onDateChange = () => void refreshMovementAnalytics();
+ dateFrom?.addEventListener("change", onDateChange);
+ dateTo?.addEventListener("change", onDateChange);
+ exportBtn?.addEventListener("click", exportDayDetailsCsv);
+
+ const closeDialog = () => {
+ const dialog = els.dialog();
+ if (dialog && typeof dialog.close === "function") {
+ dialog.close();
+ }
+ };
+
+ footerClose?.addEventListener("click", closeDialog);
+ footerCloseAlt?.addEventListener("click", closeDialog);
+
+ btnAnalytics?.addEventListener("click", () => {
+ const analyticsView = els.analyticsView();
+ const isOpen = analyticsView && !analyticsView.hidden;
+ if (isOpen) {
+ setAnalyticsVisible(false);
+ window.dispatchEvent(new CustomEvent("avs-history-show-records"));
+ return;
+ }
+
+ setAnalyticsVisible(true);
+ });
+ }
+
+ window.avsMovementAnalytics = {
+ wire,
+ setAnalyticsVisible,
+ refreshMovementAnalytics,
+ stopPolling
+ };
+
+ window.avsMovementAnalytics.wire();
+})();
diff --git a/wwwroot/js/verification-dashboard.js b/wwwroot/js/verification-dashboard.js
index 4918377..3cfe203 100644
--- a/wwwroot/js/verification-dashboard.js
+++ b/wwwroot/js/verification-dashboard.js
@@ -16,6 +16,7 @@
const scanHistoryFilter = document.getElementById("scan-history-filter");
const scanHistoryModalThead = document.getElementById("scan-history-modal-thead");
const scanHistoryModalTbody = document.getElementById("scan-history-modal-tbody");
+ const availableCartonsCountEl = document.getElementById("available-cartons-count");
const details = {
model: document.getElementById("detail-model"),
@@ -42,6 +43,7 @@
const incompleteNoSemicolonIdleMs = 420;
const manualEntryMessage = "Manual entry is not allowed. Please scan or paste.";
const completeQrPattern = /^[^;\r\n]+;[^;\r\n]+$/;
+ const dashboardSummaryPollMs = 3000;
function clearAutoSubmitTimer() {
if (autoSubmitTimer !== null) {
@@ -133,6 +135,7 @@
const payload = await response.json();
renderSuccessfulHistory(payload.recentSuccessfulScans ?? []);
renderRejectedHistory(payload.recentRejectedScans ?? []);
+ updateAvailableCartonsCount(resolveAvailableCartons(payload));
} catch {
// Ignore — UI already shows manual rejection.
}
@@ -282,6 +285,7 @@
renderStatus(result);
renderSuccessfulHistory(payload.recentSuccessfulScans ?? []);
renderRejectedHistory(payload.recentRejectedScans ?? []);
+ updateAvailableCartonsCount(resolveAvailableCartons(payload));
} catch {
setRejectedState("Unexpected communication error.", null);
triggerFailureAlarm();
@@ -393,6 +397,61 @@
}[char]));
}
+ function formatCartonCount(value) {
+ if (value === null || value === undefined || Number.isNaN(Number(value))) {
+ return "—";
+ }
+
+ return Number(value).toLocaleString("en-US");
+ }
+
+ function resolveAvailableCartons(payload) {
+ if (!payload) {
+ return undefined;
+ }
+
+ if (payload.availableCartons !== undefined && payload.availableCartons !== null) {
+ return payload.availableCartons;
+ }
+
+ if (payload.availableCartonsCount !== undefined && payload.availableCartonsCount !== null) {
+ return payload.availableCartonsCount;
+ }
+
+ return undefined;
+ }
+
+ function updateAvailableCartonsCount(count) {
+ if (!availableCartonsCountEl || count === undefined || count === null) {
+ return;
+ }
+
+ availableCartonsCountEl.textContent = formatCartonCount(count);
+ }
+
+ async function refreshDashboardSummary() {
+ if (!availableCartonsCountEl) {
+ return;
+ }
+
+ try {
+ const response = await fetch("/api/verification/dashboard-summary");
+ if (!response.ok) {
+ return;
+ }
+
+ const payload = await response.json();
+ updateAvailableCartonsCount(resolveAvailableCartons(payload));
+ } catch {
+ // Keep last visible count; retry on next interval.
+ }
+ }
+
+ function startDashboardSummaryPolling() {
+ void refreshDashboardSummary();
+ window.setInterval(refreshDashboardSummary, dashboardSummaryPollMs);
+ }
+
function formatDisplayTime(utcValue) {
if (!utcValue) {
return "-";
@@ -429,6 +488,7 @@
btnOpenScanHistory.addEventListener("click", async () => {
scanHistoryFilter.value = "successful";
+ window.avsMovementAnalytics?.setAnalyticsVisible(false);
await loadFullScanHistory("successful");
if (typeof scanHistoryDialog.showModal === "function") {
scanHistoryDialog.showModal();
@@ -437,14 +497,22 @@
});
btnCloseScanHistory.addEventListener("click", () => {
+ window.avsMovementAnalytics?.stopPolling();
scanHistoryDialog.close();
});
scanHistoryFilter.addEventListener("change", async () => {
+ window.avsMovementAnalytics?.setAnalyticsVisible(false);
+ await loadFullScanHistory(scanHistoryFilter.value);
+ });
+
+ window.addEventListener("avs-history-show-records", async () => {
await loadFullScanHistory(scanHistoryFilter.value);
});
scanHistoryDialog.addEventListener("close", () => {
+ window.avsMovementAnalytics?.stopPolling();
+ window.avsMovementAnalytics?.setAnalyticsVisible(false);
qrInput.focus();
});
}
@@ -537,6 +605,7 @@
}
wireScanHistoryModal();
+ startDashboardSummaryPolling();
qrInput.focus();
})();