// App shell — sidebar, topbar, tweaks panel const NAV_BY_ROLE = { reception: [ { section: 'Asosiy', items: [ { id: 'dashboard', label: "Boshqaruv paneli", icon: 'house' }, { id: 'new-visit', label: "Yangi rentgen", icon: 'plus-circle', highlight: true }, { id: 'patients', label: 'Bemorlar', icon: 'users-three' }, ]}, { section: 'Kassa', items: [ { id: 'kassa', label: 'Yozuvlar', icon: 'list-bullets' }, { id: 'debts', label: 'Qarzlar', icon: 'warning' }, ]}, ], doctor: [ { section: 'Asosiy', items: [ { id: 'doctor-dashboard', label: "Boshqaruv paneli", icon: 'house' }, { id: 'doctor-patients', label: 'Bemorlarim', icon: 'users-three' }, ]}, ], admin: [ { section: 'Asosiy', items: [ { id: 'dashboard', label: "Boshqaruv paneli", icon: 'house' }, { id: 'new-visit', label: "Yangi rentgen", icon: 'plus-circle' }, { id: 'patients', label: 'Bemorlar', icon: 'users-three' }, ]}, { section: 'Kassa', items: [ { id: 'kassa', label: 'Yozuvlar', icon: 'list-bullets' }, { id: 'debts', label: 'Qarzlar', icon: 'warning' }, { id: 'reports', label: 'Hisobotlar', icon: 'chart-line-up' }, ]}, { section: 'Boshqaruv', items: [ { id: 'services', label: 'Xizmatlar', icon: 'package' }, { id: 'doctors', label: 'Shifokorlar', icon: 'stethoscope' }, ]}, ], super_admin: [ { section: 'Asosiy', items: [ { id: 'dashboard', label: "Boshqaruv paneli", icon: 'house' }, { id: 'new-visit', label: "Yangi rentgen", icon: 'plus-circle' }, { id: 'patients', label: 'Bemorlar', icon: 'users-three' }, ]}, { section: 'Kassa', items: [ { id: 'kassa', label: 'Yozuvlar', icon: 'list-bullets' }, { id: 'debts', label: 'Qarzlar', icon: 'warning' }, { id: 'reports', label: 'Hisobotlar', icon: 'chart-line-up' }, ]}, { section: 'Boshqaruv', items: [ { id: 'services', label: 'Xizmatlar', icon: 'package' }, { id: 'doctors', label: 'Shifokorlar', icon: 'stethoscope' }, { id: 'users', label: 'Foydalanuvchilar', icon: 'user-gear' }, { id: 'audit', label: 'Audit log', icon: 'shield-check' }, { id: 'settings', label: 'Sozlamalar', icon: 'gear' }, ]}, ], }; function Sidebar({ role, page, onNav, collapsed, activeUser }) { const sections = NAV_BY_ROLE[role] || NAV_BY_ROLE.reception; return ( ); } function Topbar({ title, breadcrumbs, role, me, onNav, onToggleSidebar, theme, onToggleTheme, onShowNotif, onShowTweaks }) { const [showMenu, setShowMenu] = useState(false); const wrapRef = useRef(null); useEffect(() => { const h = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setShowMenu(false); }; document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h); }, []); const fullName = (me && (me.full_name || me.name)) || '—'; return (
{breadcrumbs && (
{breadcrumbs.map((b, i) => ( {i > 0 && } {b} ))}
)}
{title}
{ROLE_LABELS[role]}
setShowMenu(v => !v)}> {fullName}
{showMenu && (
{fullName}
{ setShowMenu(false); onShowTweaks && onShowTweaks(); }}>Ko'rinish sozlamalari
{ setShowMenu(false); onNav && onNav('login'); }}>Chiqish
)}
); } /* ---------- Notifications panel (slide-in from right) — REAL API ---------- */ const NOTIF_ICONS = { new_xray: 'tooth', link_attached: 'link', link_requested: 'bell-ringing', payment_received: 'money', default: 'bell', }; function NotifPanel({ open, onClose }) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const load = useCallback(() => { if (!window.API) return; setLoading(true); window.API.listNotifications(false) .then(rows => setItems(Array.isArray(rows) ? rows : [])) .catch(() => setItems([])) .finally(() => setLoading(false)); }, []); useEffect(() => { if (open) load(); }, [open, load]); const unread = items.filter(n => !n.is_read).length; const markAll = () => { if (!window.API) return; window.API.markAllRead().then(() => load()).catch(() => {}); }; if (!open) return null; return (
e.stopPropagation()} style={{ width: 380, maxWidth: '100%', height: '100vh', maxHeight: '100vh', borderRadius: 0, animation: 'slide-down 280ms var(--ease-out)' }}>

Bildirishnomalar

{unread > 0 ? `${unread} yangi` : "Yangi yo'q"}
{loading &&
Yuklanmoqda…
} {!loading && items.length === 0 && (

Hozircha bildirishnoma yo'q

)} {!loading && items.map(n => (

{n.title}

{n.message}

{window.uzDateTime ? window.uzDateTime(n.created_at) : n.created_at}
))}
); } /* ---------- Ko'rinish sozlamalari paneli (rol-almashtirgich OLIB TASHLANDI — production) ---------- */ function TweaksPanel({ open, onClose, state, setState }) { if (!open) return null; return (
e.stopPropagation()} style={{ width: 360, maxWidth: '100%', height: '100vh', maxHeight: '100vh', borderRadius: 0 }}>

Ko'rinish sozlamalari

Mavzu
{['light', 'dark'].map(t => ( ))}
Sidebar
{[{id:'expanded',l:"Kengaytirilgan"},{id:'collapsed',l:"Yig'ilgan"}].map(t => ( ))}
Zichlik
{[{id:'comfortable',l:'Bemalol'},{id:'compact',l:'Zich'}].map(t => ( ))}
Brend rang
{[ { id: 'slate', name: 'Slate', c: '#0F172A' }, { id: 'sky', name: 'Sky', c: '#0EA5E9' }, { id: 'indigo', name: 'Indigo', c: '#4F46E5' }, { id: 'teal', name: 'Teal', c: '#0D9488' }, ].map(c => (
Bu sozlamalar faqat ko'rinishni o'zgartiradi.
); } /* ---------- Page header ---------- */ function PageHeader({ title, sub, breadcrumbs, actions }) { return (
{breadcrumbs && (
{breadcrumbs.map((b, i) => ( {i > 0 && } {b} ))}
)}

{title}

{sub &&

{sub}

}
{actions &&
{actions}
}
); } Object.assign(window, { Sidebar, Topbar, NotifPanel, TweaksPanel, PageHeader, NAV_BY_ROLE, NOTIF_ICONS });