import React from 'react'; import { NavLink } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { LayoutDashboard, CalendarDays, Camera, Settings } from 'lucide-react'; import toast from 'react-hot-toast'; import { cn } from '@/lib/utils'; import { ADMIN_HOME_PATH, ADMIN_EVENTS_PATH, ADMIN_EVENT_VIEW_PATH, ADMIN_EVENT_PHOTOS_PATH, ADMIN_SETTINGS_PATH, ADMIN_BILLING_PATH, } from '../constants'; import { registerApiErrorListener } from '../lib/apiError'; import { getDashboardSummary, getEvents, getTenantPackagesOverview } from '../api'; import { NotificationCenter } from './NotificationCenter'; import { UserMenu } from './UserMenu'; import { useEventContext } from '../context/EventContext'; import { EventSwitcher, EventMenuBar } from './EventNav'; type NavItem = { key: string; to: string; label: string; icon: React.ComponentType>; end?: boolean; highlight?: boolean; prefetchKey?: string; }; interface AdminLayoutProps { title: string; subtitle?: string; actions?: React.ReactNode; children: React.ReactNode; } export function AdminLayout({ title, subtitle, actions, children }: AdminLayoutProps) { const { t } = useTranslation('common'); const prefetchedPathsRef = React.useRef>(new Set()); const { events } = useEventContext(); const singleEvent = events.length === 1 ? events[0] : null; const eventsPath = singleEvent?.slug ? ADMIN_EVENT_VIEW_PATH(singleEvent.slug) : ADMIN_EVENTS_PATH; const eventsLabel = events.length === 1 ? t('navigation.event', { defaultValue: 'Event' }) : t('navigation.events'); const photosPath = singleEvent?.slug ? ADMIN_EVENT_PHOTOS_PATH(singleEvent.slug) : ADMIN_EVENTS_PATH; const photosLabel = t('navigation.photos', { defaultValue: 'Fotos' }); const settingsLabel = t('navigation.settings'); const navItems = React.useMemo(() => [ { key: 'dashboard', to: ADMIN_HOME_PATH, label: t('navigation.dashboard'), icon: LayoutDashboard, end: true, prefetchKey: ADMIN_HOME_PATH, }, { key: 'events', to: eventsPath, label: eventsLabel, icon: CalendarDays, end: Boolean(singleEvent?.slug), highlight: events.length === 1, prefetchKey: ADMIN_EVENTS_PATH, }, { key: 'photos', to: photosPath, label: photosLabel, icon: Camera, end: Boolean(singleEvent?.slug), prefetchKey: singleEvent?.slug ? undefined : ADMIN_EVENTS_PATH, }, { key: 'settings', to: ADMIN_SETTINGS_PATH, label: settingsLabel, icon: Settings, prefetchKey: ADMIN_SETTINGS_PATH, }, ], [eventsLabel, eventsPath, photosPath, photosLabel, settingsLabel, singleEvent, events.length, t]); const prefetchers = React.useMemo(() => ({ [ADMIN_HOME_PATH]: () => Promise.all([ getDashboardSummary(), getEvents(), getTenantPackagesOverview(), ]).then(() => undefined), [ADMIN_EVENTS_PATH]: () => getEvents().then(() => undefined), [ADMIN_BILLING_PATH]: () => getTenantPackagesOverview().then(() => undefined), [ADMIN_SETTINGS_PATH]: () => Promise.resolve(), }), []); const triggerPrefetch = React.useCallback( (path: string) => { if (prefetchedPathsRef.current.has(path)) { return; } const runner = prefetchers[path as keyof typeof prefetchers]; if (!runner) { return; } prefetchedPathsRef.current.add(path); Promise.resolve(runner()).catch(() => { prefetchedPathsRef.current.delete(path); }); }, [prefetchers], ); React.useEffect(() => { document.body.classList.add('tenant-admin-theme'); return () => { document.body.classList.remove('tenant-admin-theme'); }; }, []); React.useEffect(() => { const unsubscribe = registerApiErrorListener((detail) => { const fallback = t('errors.generic'); const message = detail?.message?.trim() ? detail.message : fallback; toast.error(message, { id: detail?.code ? `api-error-${detail.code}` : undefined, }); }); return unsubscribe; }, [t]); return (

{t('app.brand')}

{title}

{subtitle ?

{subtitle}

: null}
{actions}
{children}
); } function TenantMobileNav({ items, onPrefetch, }: { items: NavItem[]; onPrefetch: (path: string) => void; }) { const { t } = useTranslation('common'); return ( ); }