Refine KPI card highlights and demo checkout flow
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- SSE wrapper — satu koneksi, semua section dapat update otomatis -->
|
||||
@@ -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;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</article>
|
||||
|
||||
<!-- Checked In -->
|
||||
<article class="card border-l-4 border-l-brand-500 p-4">
|
||||
<article class="kpi-card kpi-card-inprogress card border-l-4 border-l-brand-500 p-4" data-kpi-kind="inprogress" data-kpi-value="{{.CheckedIn}}">
|
||||
<div class="flex items-start justify-between">
|
||||
<p class="text-xs font-semibold uppercase tracking-widest text-slate-400">In Progress</p>
|
||||
<svg class="h-4 w-4 text-brand-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
@@ -28,7 +28,7 @@
|
||||
</article>
|
||||
|
||||
<!-- Checked Out -->
|
||||
<article class="card border-l-4 border-l-emerald-500 p-4">
|
||||
<article class="kpi-card kpi-card-checkedout card border-l-4 border-l-emerald-500 p-4" data-kpi-kind="checkedout" data-kpi-value="{{.CheckedOut}}">
|
||||
<div class="flex items-start justify-between">
|
||||
<p class="text-xs font-semibold uppercase tracking-widest text-slate-400">Checked Out</p>
|
||||
<svg class="h-4 w-4 text-emerald-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
|
||||
Reference in New Issue
Block a user