From af7e0d35600f5a6060ba4affeefccb3c7c90a458 Mon Sep 17 00:00:00 2001 From: "sas.fajri" Date: Thu, 30 Apr 2026 15:48:16 +0700 Subject: [PATCH] Refine KPI card highlights and demo checkout flow --- cpone-dashboard/scripts/demo_live.sh | 63 ++++++++++++++++--- .../templates/dashboard/index.html | 32 ++++++++++ .../templates/dashboard/partials/kpi.html | 4 +- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/cpone-dashboard/scripts/demo_live.sh b/cpone-dashboard/scripts/demo_live.sh index 9c545a3..665408e 100644 --- a/cpone-dashboard/scripts/demo_live.sh +++ b/cpone-dashboard/scripts/demo_live.sh @@ -6,11 +6,12 @@ # ./demo_live.sh 3 # 1 action every 3 seconds (faster) # ./demo_live.sh 15 # 1 action every 15 seconds (slower) # -# What it simulates (cycles through 4 action types): +# What it simulates (cycles through 5 action types): # A — New patient check-in for today (April 30, patients 811–825) # B — Station progress update for today's in-progress patients -# C — Doctor validates a yesterday resume (April 29) -# D — Publish result PDF (April 29 patient gets a file URL) +# C — Checkout patient today when all required stations are done +# D — Doctor validates a yesterday resume (April 29) +# E — Publish result PDF (April 29 patient gets a file URL) # # Press Ctrl+C to stop. @@ -181,6 +182,49 @@ action_station() { } # ── ACTION C: validate resume ───────────────────────────────────────────────── +action_checkout() { + local ALL_STATIONS="1,31,17,4,5,2,7,33" + local patient + patient=$(DB " + SELECT c.Mcu_CheckinoutPreregisterID + FROM mcu_checkinout c + WHERE c.Mcu_CheckinoutMcuID = $MCU_ID + AND c.Mcu_CheckinoutDate = '$TODAY' + AND c.Mcu_CheckinoutOutTime IS NULL + AND ( + SELECT COUNT(DISTINCT sp.Mcu_StationProgressStationID) + FROM mcu_station_progress sp + WHERE sp.Mcu_StationProgressPreregisterID = c.Mcu_CheckinoutPreregisterID + AND sp.Mcu_StationProgressMcuID = $MCU_ID + AND sp.Mcu_StationProgressStationID IN ($ALL_STATIONS) + ) >= 8 + ORDER BY c.Mcu_CheckinoutPreregisterID + LIMIT 1; + ") + + if [ -z "$patient" ]; then + LOG "⏩ Belum ada pasien eligible untuk CHECK-OUT" + return + fi + + local now_time + now_time=$(date '+%H:%M:%S') + local name + name=$(DB "SELECT Mcu_PatientName FROM mcu_patient WHERE Mcu_PatientPreregisterID = $patient;") + + DB_EXEC " + UPDATE mcu_checkinout + SET Mcu_CheckinoutOutTime = '$now_time', + Mcu_CheckinoutSyncedAt = NOW() + WHERE Mcu_CheckinoutMcuID = $MCU_ID + AND Mcu_CheckinoutDate = '$TODAY' + AND Mcu_CheckinoutPreregisterID = $patient + AND Mcu_CheckinoutOutTime IS NULL; + " + LOG "🏁 CHECK-OUT │ $name │ keluar pukul $now_time" +} + +# ── ACTION D: validate resume ───────────────────────────────────────────────── action_validate() { local patient patient=$(DB " @@ -213,7 +257,7 @@ action_validate() { LOG "✔ VALIDASI │ $name │ resume divalidasi dokter" } -# ── ACTION D: publish PDF ───────────────────────────────────────────────────── +# ── ACTION E: publish PDF ───────────────────────────────────────────────────── action_publish() { local order_id order_id=$(DB " @@ -273,20 +317,21 @@ echo "║ CpOne — DEMO LIVE SIMULATION ║" echo "║ MCU PROJECT DEMO 2026 │ ID: $MCU_ID │ Hari ini: $TODAY ║" echo "╚══════════════════════════════════════════════════════════════╝" echo "" -echo " Aksi: A=Check-in B=Station C=Validasi D=Publish PDF" +echo " Aksi: A=Check-in B=Station C=Checkout D=Validasi E=Publish PDF" echo " Interval: ${SPEED}s │ Ctrl+C untuk berhenti" echo "" -actions=(A B C D) +actions=(A B C D E) idx=0 while true; do - act="${actions[$((idx % 4))]}" + act="${actions[$((idx % 5))]}" case "$act" in A) action_checkin ;; B) action_station ;; - C) action_validate ;; - D) action_publish ;; + C) action_checkout ;; + D) action_validate ;; + E) action_publish ;; esac idx=$(( idx + 1 )) sleep "$SPEED" diff --git a/cpone-dashboard/templates/dashboard/index.html b/cpone-dashboard/templates/dashboard/index.html index ee8444e..0e7e4d2 100644 --- a/cpone-dashboard/templates/dashboard/index.html +++ b/cpone-dashboard/templates/dashboard/index.html @@ -154,6 +154,11 @@ #sse-arrivals .arrival-row-updated { animation: arrivalRowPulse 2.6s ease-out; } + +#sse-kpi .kpi-card-updated { + animation: sseFlash 2.6s ease-out, ssePop 0.9s cubic-bezier(.22,.61,.36,1); + box-shadow: 0 0 0 2px rgba(59, 80, 160, 0.35), 0 10px 22px -14px rgba(59, 80, 160, 0.55); +} @@ -383,6 +388,7 @@ function openPatientsModal() { stations: 'sse-stations', arrivals: 'sse-arrivals' }; + const kpiSnapshot = new Map(); const stationSnapshot = new Map(); const arrivalSnapshot = new Set(); let audioCtx = null; @@ -504,6 +510,30 @@ function openPatientsModal() { return highlighted > 0; } + function triggerKpiCards(target) { + const cards = target.querySelectorAll('.kpi-card'); + if (!cards || !cards.length) return false; + + let highlighted = 0; + cards.forEach(function (card) { + const kind = card.dataset.kpiKind || ''; + if (kind !== 'inprogress' && kind !== 'checkedout') return; + const value = parseInt(card.dataset.kpiValue || '0', 10); + const prev = kpiSnapshot.has(kind) ? kpiSnapshot.get(kind) : null; + kpiSnapshot.set(kind, value); + if (prev === null || value === prev) return; + + highlighted++; + card.classList.remove('kpi-card-updated'); + void card.offsetWidth; + card.classList.add('kpi-card-updated'); + setTimeout(function () { + card.classList.remove('kpi-card-updated'); + }, 2600); + }); + return highlighted > 0; + } + function triggerHighlight(target) { if (!target) return; const isStations = target.id === 'sse-stations'; @@ -511,6 +541,8 @@ function openPatientsModal() { if (!isStations) { if (target.id === 'sse-arrivals') { didHighlightRows = !!triggerArrivalRows(target); + } else if (target.id === 'sse-kpi') { + didHighlightRows = !!triggerKpiCards(target); } else { target.classList.remove('sse-updated'); void target.offsetWidth; diff --git a/cpone-dashboard/templates/dashboard/partials/kpi.html b/cpone-dashboard/templates/dashboard/partials/kpi.html index b3ab2bb..36b4b16 100644 --- a/cpone-dashboard/templates/dashboard/partials/kpi.html +++ b/cpone-dashboard/templates/dashboard/partials/kpi.html @@ -16,7 +16,7 @@ -
+

In Progress

@@ -28,7 +28,7 @@
-
+

Checked Out