// screens-admin-shell.jsx — Admin shell (tab bar, top strip) + Now screen const { fmtIDR, findClass, findCoach, findMember, DAYS_SHORT, DAYS_LONG, TODAY_DAY, TODAY_HOUR, TODAY_MIN, DATES_THIS_WEEK } = window.NADI_HELPERS; // ── Admin tab bar (4 tabs, distinct from member bar) ───────────────── const ADMIN_TAB_DEFS = [ { id: "now", label: "Now" }, { id: "schedule", label: "Schedule" }, { id: "people", label: "People" }, { id: "more", label: "More" }, ]; function AdminTabIcon({ id, active }) { const stroke = active ? "var(--bg)" : "rgba(248,241,225,0.55)"; const sw = 1.4; if (id === "now") return ( ); if (id === "schedule") return ( ); if (id === "people") return ( ); if (id === "more") return ( ); return null; } function AdminTabBar({ tab, setTab }) { return ( ); } // ── Admin top strip (always visible at top) ──────────────────────── function AdminTopStrip({ admin, onExitAdmin }) { return (
staff mode · {admin.role.toLowerCase()}
); } // ── NOW SCREEN ───────────────────────────────────────────────────── function AdminNowScreen({ admin, openMember, openSession }) { const today = window.NADI_ADMIN.todaysSessions(); const liveOrUpcoming = today.filter((s) => true); const rev = window.NADI_ADMIN.TODAY_REVENUE; const yest = window.NADI_ADMIN.YESTERDAY_REVENUE; const diff = rev - yest; const diffPct = Math.round((diff / yest) * 100); return (
{/* Greeting */}
{DAYS_LONG[TODAY_DAY].toLowerCase()} · {String(DATES_THIS_WEEK[TODAY_DAY]).padStart(2, "0")} {new Date().toLocaleDateString("en-GB", { month: "short" }).toLowerCase()} {String(TODAY_HOUR).padStart(2, "0")}:{String(TODAY_MIN).padStart(2, "0")} · live

The floor, now.

{/* Revenue pulse */}
today · gross revenue
{fmtIDR(rev)}
0 ? "var(--primary)" : "rgba(248,241,225,0.55)"} style={{ marginTop: 6 }}> {diff > 0 ? "↑" : "↓"} {Math.abs(diffPct)}% vs yesterday · {fmtIDR(yest)} {/* Hourly pulse */}
{/* Live class strip */}
today's classes {today.length} sessions
{today.map((s) => openSession && openSession(s)} />)}
{/* Churn alerts */} {/* Quick actions */}
quick actions
); } function HourlyPulse({ rows }) { const max = Math.max(1, ...rows.map((r) => r.v)); const nowH = new Date().getHours(); return (
{rows.map((r) => { const isPast = parseInt(r.h, 10) <= nowH; const h = r.v > 0 ? Math.max(4, Math.round((r.v / max) * 50)) : 2; return (
0 ? "var(--primary)" : (isPast ? "rgba(248,241,225,0.16)" : "rgba(248,241,225,0.08)"), opacity: r.v > 0 || isPast ? 1 : 0.6, }} /> ); })}
{rows.map((r, i) => ( {(i % 2 === 0) ? r.h : ""} ))}
); } function LiveSessionRow({ s, onClick }) { const c = findClass(s.classId), co = findCoach(s.coachId); const checkin = (window.NADI_ADMIN.CHECKINS && window.NADI_ADMIN.CHECKINS[s.id]) || { checked: 0, ready: s.booked }; const nowTime = `${String(TODAY_HOUR).padStart(2, "0")}:${String(TODAY_MIN).padStart(2, "0")}`; const isPast = s.time < nowTime; const isNow = !isPast && s.time <= `${String(TODAY_HOUR + 1).padStart(2, "0")}:${String(TODAY_MIN + 30).padStart(2, "0")}`; const pct = Math.round((checkin.checked / s.booked) * 100) || 0; return ( ); } function ChurnAlerts({ openMember }) { const list = window.NADI_ADMIN.CHURN_LIST; return (
churn watch · 3 lapsed members
{list.slice(0, 3).map((m, i) => ( ))}
); } function QuickAction({ icon, label, sub, onClick }) { return ( ); } function QAIcon({ id }) { const props = { width: 18, height: 18, viewBox: "0 0 20 20", fill: "none", stroke: "var(--primary)", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round" }; if (id === "walkin") return ; if (id === "book") return ; if (id === "cancel") return ; if (id === "qr") return ; return null; } Object.assign(window, { ADMIN_TAB_DEFS, AdminTabBar, AdminTopStrip, AdminNowScreen, LiveSessionRow });