import { useCallback, useState } from 'react'; import { AppShell, rem } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { logout } from '@ema-platform/auth'; import { AppHeader, AppSidebar } from '@ema-platform/ui'; import type { NavItem } from '@ema-platform/ui'; import { IconAnchor, IconBook2, IconChartBar, IconCreditCard, IconFileDescription, IconHeart, IconLayoutDashboard, IconShieldCheck, IconRubberStamp, IconSettings, IconShip, IconUser, IconUsers, IconUserShield, IconQuestionMark, IconClipboardList, IconReport, IconReportAnalytics, } from '@tabler/icons-react'; import { notify } from '@ema-platform/ui'; import { SUPPORTED_LANGUAGES } from '../i18n/config'; import { useAppDispatch, useAppSelector } from '../store/hooks'; const NAV_ITEMS: NavItem[] = [ { to: '/dashboard', label: 'nav.dashboard', icon: IconLayoutDashboard }, { to: '/um/user-management/dashboard', label: 'nav.userManagement', icon: IconUserShield }, { to: '/seaman-book-queue', label: 'nav.seamanBookQueue', icon: IconBook2 }, { to: '/coc-queue', label: 'nav.cocQueue', icon: IconShieldCheck }, { to: '/endorsement-queue', label: 'nav.endorsementQueue', icon: IconRubberStamp }, { to: '/vessel-registrations', label: 'nav.vesselRegistrations', icon: IconAnchor }, { to: '/vessel-registration-report', label: 'nav.vesselRegistrationReport', icon: IconReportAnalytics }, { to: '/vessel-transfers', label: 'nav.vesselTransfers', icon: IconShip }, { to: '/seafarer-registry', label: 'nav.seafarerRegistry', icon: IconUsers }, // { to: '/applications', label: 'nav.applications', icon: IconFileDescription }, { to: '/payment-config', label: 'nav.paymentConfig', icon: IconCreditCard }, { to: '/analytics', label: 'nav.analytics', icon: IconChartBar }, { to: '/medical-verification', label: 'nav.medicalVerification', icon: IconHeart }, { to: '/questions', label: 'nav.questions', icon: IconQuestionMark }, { to: '/exams', label: 'nav.exams', icon: IconClipboardList }, { to: '/exam-results', label: 'nav.examResults', icon: IconReport }, { to: '/configuration', label: 'nav.configuration', icon: IconSettings }, { to: '/profile', label: 'nav.profile', icon: IconUser }, ]; const HEADER_HEIGHT = 116; export function BackofficeLayout() { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); const dispatch = useAppDispatch(); const [opened, { toggle: toggleNav }] = useDisclosure(); const [collapsed, setCollapsed] = useState(false); const user = useAppSelector((state) => state.auth.user); const layoutMode = useAppSelector((state) => state.preferences.layoutMode); const displayName = user?.name?.en || user?.username || ''; const initials = displayName ? displayName.split(/\s+/).map((s) => s[0]).join('').toUpperCase().slice(0, 2) : '?'; const handleLogout = useCallback(() => { dispatch(logout()); navigate('/login'); }, [dispatch, navigate]); const segments = location.pathname.split('/').filter(Boolean); const crumbs = [ { label: t('nav.dashboard'), path: '/dashboard' }, ...segments .map((_, i) => '/' + segments.slice(0, i + 1).join('/')) .filter((path) => path !== '/dashboard') .filter((path) => !path.startsWith('/um')) .map((path) => ({ label: t('nav.dashboard'), path })), ]; const go = (item: NavItem) => { if (item.soon) { notify.info(`${t(item.label)} — coming soon.`); return; } if (item.to) { navigate(item.to); } }; const handleToggleCollapse = useCallback(() => { setCollapsed((prev) => !prev); }, []); const isSidebar = layoutMode === 'sidebar'; return (
{!isSidebar && (
{NAV_ITEMS.map((item) => { const active = !!item.to && (location.pathname === item.to || location.pathname.startsWith(`${item.to}/`)); const ItemIcon = item.icon; return ( ); })}
)}
{isSidebar && ( )}
); }