// 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 (
);
}
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 });