// shell.jsx — sidebar, topbar, command bar (wired to live store) const { useState, useEffect, useRef, useMemo } = React; function Sidebar({ active, onNav, collapsed, onSignOut, onClose }) { const [userMenuOpen, setUserMenuOpen] = useState(false); const store = window.MSStore.useStore(); const t = window.MSStore.useT(); const profile = store && store.profile; const account = store && store.activeAccount; const role = store && store.activeRole; const rawName = (profile && profile.name) || ""; const displayName = rawName ? (/^Dr\.?\s/i.test(rawName) ? rawName : "Dr. " + rawName) : "MedSync user"; const subline = ((profile && profile.specialty) || "").trim() || (role === "owner" ? t("set.role.adminDoctor") : role === "admin_doctor" ? t("set.role.adminDoctor") : role === "doctor" ? t("set.role.doctor") : role === "staff" ? t("set.role.staff") : t("set.role.doctor")); const clinicName = (account && account.name) || (profile && profile.clinic_name) || "MedSync"; const userInitials = (rawName || "MS").replace(/^Dr\.?\s+/i, "") .split(/\s+/).filter(Boolean).map(s => s[0]).slice(0, 2).join("").toUpperCase() || "MS"; const avatarBg = (profile && profile.avatar_color) || "var(--primary-soft)"; const avatarBorder = (profile && profile.avatar_color) || "var(--primary-line)"; const avatarFg = profile && profile.avatar_color ? "white" : "var(--primary-ink)"; const primary = [ { id: "dashboard", label: t("nav.today"), icon: Icon.Dashboard }, { id: "schedule", label: t("nav.schedule"), icon: Icon.Calendar }, { id: "patients", label: t("nav.patients"), icon: Icon.Patients }, { id: "analytics", label: t("nav.analytics"), icon: Icon.Pulse }, { id: "assistant", label: t("nav.assistant"), icon: Icon.Sparkle }, { id: "whatsapp", label: t("nav.whatsapp"), icon: Icon.Phone }, { id: "dictionary", label: t("nav.dictionary"), icon: Icon.Clipboard }, { id: "transcribe",label: t("nav.transcribe"),icon: Icon.Mic }, ]; const handleNav = (page) => { onNav(page); if (onClose) onClose(); }; return ( ); } function Topbar({ onCmd, onNav, onMenu, title, subtitle, actions }) { const [lang, setLang] = window.MSStore.useLang(); const store = window.MSStore.useStore(); const unread = (store && store.unreadNotifications) || 0; return (
MedSync
{actions}
{window.MSi18n && window.MSi18n.SUPPORTED.map(code => ( ))}
); } function CommandBar({ open, onClose, onNav }) { const [q, setQ] = useState(""); const inputRef = useRef(null); const store = window.MSStore.useStore(); useEffect(() => { if (open) setTimeout(() => inputRef.current?.focus(), 30); }, [open]); useEffect(() => { function onKey(e) { if (e.key === "Escape") onClose(); } if (open) window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); const items = useMemo(() => { const patientItems = (store && store.patients || []).slice(0, 50).map(p => ({ kind: "Patient", label: `${p.first} ${p.last}`.trim() || "Unnamed patient", sub: [p.age && `${p.age}y`, p.phone, p.email].filter(Boolean).join(" · "), action: () => { onNav("patients", p.id); onClose(); }, avatar: p.avatar, })); const navItems = [ { kind: "Go to", label: "Today's dashboard", icon: Icon.Dashboard, action: () => { onNav("dashboard"); onClose(); } }, { kind: "Go to", label: "Schedule", icon: Icon.Calendar, action: () => { onNav("schedule"); onClose(); } }, { kind: "Go to", label: "Patients", icon: Icon.Patients, action: () => { onNav("patients"); onClose(); } }, { kind: "Go to", label: "Analytics", icon: Icon.Pulse, action: () => { onNav("analytics"); onClose(); } }, { kind: "Go to", label: "MedSync Assist", icon: Icon.Sparkle, action: () => { onNav("assistant"); onClose(); } }, { kind: "Go to", label: "Dictionary", icon: Icon.Clipboard, action: () => { onNav("dictionary"); onClose(); } }, { kind: "Action", label: "Start a transcription session", icon: Icon.Mic, action: () => { onNav("transcribe"); onClose(); } }, { kind: "Go to", label: "Settings", icon: Icon.Settings, action: () => { onNav("settings"); onClose(); } }, ]; const all = [...navItems, ...patientItems]; if (!q) return all.slice(0, 8); const ql = q.toLowerCase(); return all.filter(i => i.label.toLowerCase().includes(ql) || (i.sub || "").toLowerCase().includes(ql)).slice(0, 12); }, [q, store && store.patients]); if (!open) return null; return (
e.stopPropagation()}>
setQ(e.target.value)} placeholder="Patient, command, or page…" /> esc
{items.length === 0 &&
No results
} {items.map((it, i) => ( ))}
↑↓ navigate select esc close
); } Object.assign(window, { Sidebar, Topbar, CommandBar });