import { useEffect, useState } from 'react'; import { NavLink, Outlet, useLocation } from 'react-router-dom'; import { Icon } from './Icon'; import { OmniSearch } from './OmniSearch'; import { NotificationBell } from './NotificationBell'; import { nav } from '../nav'; import { useNotifications } from '../lib/notifications'; import { useGateway } from '../lib/gateway'; import { time } from '../lib/format'; import { Confirm } from './ui'; /* The shell: a top bar spanning the width, a rail down the left, and the page. * * Everything in the bar is true of the console rather than of any one screen — which * gateway this is, whether it is answering, the page search, the activity bell and the way * out. Everything in the rail is a destination. Nothing else lives in either. */ function Rail({ open, onNavigate }: { open: boolean; onNavigate: () => void }) { const { unread } = useNotifications(); const location = useLocation(); // A group holding the current page is open regardless of what was collapsed last time: // a rail that hides the page you are on is a rail that has lost its place. const [collapsed, setCollapsed] = useState>(() => { try { return JSON.parse(localStorage.getItem('memby-admin-nav') ?? '{}') as Record; } catch { return {}; } }); const toggle = (id: string) => { setCollapsed((current) => { const next = { ...current, [id]: !current[id] }; try { localStorage.setItem('memby-admin-nav', JSON.stringify(next)); } catch { // Private-mode storage refusals cost the memory of which groups were open, and // nothing else. } return next; }); }; return ( ); } export function Layout() { const { version, online, checkedAt, status, setMaintenance } = useGateway(); const [railOpen, setRailOpen] = useState(false); const [changingAvailability, setChangingAvailability] = useState(false); const [confirmingOffline, setConfirmingOffline] = useState(false); const location = useLocation(); const offline = Boolean(status?.maintenance?.enabled); const toggleAvailability = async () => { if (changingAvailability || !status) return; setChangingAvailability(true); try { await setMaintenance(!offline); } finally { setChangingAvailability(false); } }; // The rail is an overlay on a narrow screen, so a navigation has to close it — otherwise // it sits over the page that was just opened. useEffect(() => setRailOpen(false), [location.pathname]); return ( <> Skip to content
Memby Gateway
gateway {version || 'unknown'} {/* This is both a live gateway verdict and its operator-controlled availability. The switch is intentionally in the shared header: taking the household offline should not require leaving the page where the operator noticed it. */}
setRailOpen(false)} />
{confirmingOffline ? ( { setConfirmingOffline(false); void toggleAvailability(); }} onCancel={() => setConfirmingOffline(false)} /> ) : null} ); }