mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-30 12:18:12 +00:00
Merge branch 'WorkflowChange' of github.com:Tria-plc/emaui into WorkflowChange
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { AppShell } from '@mantine/core';
|
||||
import { AppShell, Drawer } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BrandMark, logout } from '@ema-platform/auth';
|
||||
import { BrandMark, logout, useIdleTimer } from '@ema-platform/auth';
|
||||
import { AppHeader, AppSidebar } from '@ema-platform/ui';
|
||||
import type { NavItem, NavSection } from '@ema-platform/ui';
|
||||
import { notify } from '@ema-platform/ui';
|
||||
@@ -26,26 +26,24 @@ const BADGE_POLL_MS = 60_000;
|
||||
|
||||
const HEADER_HEIGHT = 116;
|
||||
|
||||
/**
|
||||
* A desk left unlocked with a license-review or medical-record screen open is
|
||||
* the actual threat model here, not a slow token. 15 minutes of no mouse,
|
||||
* key, scroll, or touch activity signs the officer out automatically.
|
||||
*/
|
||||
const IDLE_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
|
||||
export function BackofficeLayout() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useAppDispatch();
|
||||
const [opened, { toggle: toggleNav }] = useDisclosure();
|
||||
const [opened, { toggle: toggleNav, close: closeNav }] = useDisclosure();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const user = useAppSelector((state) => state.auth.user);
|
||||
const layoutMode = useAppSelector((state) => state.preferences.layoutMode);
|
||||
const { permissions: granted, known } = usePermissions();
|
||||
|
||||
// TEMPORARY diagnostic — remove once the sidebar is confirmed working.
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'[NAV] known=', known,
|
||||
'granted=', granted.length,
|
||||
'| cookies:', document.cookie.split('; ').map((c) => c.split('=')[0]).filter((n) => n.includes('token')),
|
||||
'| token tail:', (document.cookie.match(/ema-backoffice-auth-token=([^;]+)/)?.[1] ?? 'NONE').slice(-12),
|
||||
);
|
||||
|
||||
// Badges reflect real pending work. One grouped request on a timer, shared
|
||||
// by the sidebar and the top bar via the RTK cache.
|
||||
const { data: counts } = useGetQueueCountsQuery(undefined, {
|
||||
@@ -89,9 +87,20 @@ export function BackofficeLayout() {
|
||||
const handleLogout = useCallback(() => {
|
||||
dispatch(logout());
|
||||
dispatch(baseApi.util.resetApiState());
|
||||
navigate("/login");
|
||||
navigate("/");
|
||||
}, [dispatch, navigate]);
|
||||
|
||||
useIdleTimer(IDLE_TIMEOUT_MS, () => {
|
||||
notify.info(
|
||||
t(
|
||||
'common.idleLogoutMessage',
|
||||
'You were signed out after 15 minutes of inactivity.',
|
||||
),
|
||||
t('common.idleLogoutTitle', 'Session ended'),
|
||||
);
|
||||
handleLogout();
|
||||
});
|
||||
|
||||
const segments = location.pathname.split('/').filter(Boolean);
|
||||
// Label each crumb from the nav item it corresponds to, falling back to a
|
||||
// readable form of the path segment. Every crumb was previously labelled
|
||||
@@ -141,7 +150,10 @@ export function BackofficeLayout() {
|
||||
? {
|
||||
width: collapsed ? 72 : 264,
|
||||
breakpoint: "sm",
|
||||
collapsed: { mobile: !opened },
|
||||
// Mobile has its own Drawer below — AppShell's built-in mobile
|
||||
// navbar takes over the full viewport width, which felt like
|
||||
// it swallowed the page. Always collapsed here on mobile.
|
||||
collapsed: { mobile: true },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
@@ -222,6 +234,34 @@ export function BackofficeLayout() {
|
||||
|
||||
{/* Registered once for the whole backoffice; opens on ⌘K anywhere. */}
|
||||
<CommandPalette sections={sections} />
|
||||
|
||||
{/* Mobile nav: a proper Drawer (sized, backdrop, closes on outside
|
||||
click) instead of AppShell's full-width mobile navbar. Mirrors the
|
||||
landing page's mobile menu. */}
|
||||
{isSidebar && (
|
||||
<Drawer
|
||||
opened={opened}
|
||||
onClose={closeNav}
|
||||
hiddenFrom="sm"
|
||||
size="75%"
|
||||
padding={0}
|
||||
withCloseButton={false}
|
||||
>
|
||||
<AppSidebar
|
||||
navItems={sections}
|
||||
collapsed={false}
|
||||
activePath={location.pathname}
|
||||
onToggleCollapse={handleToggleCollapse}
|
||||
onNavigate={(item) => {
|
||||
go(item);
|
||||
closeNav();
|
||||
}}
|
||||
brandName={t('app.name')}
|
||||
brandSubtitle={t('app.authority')}
|
||||
brandLogo={<BrandMark size={32} />}
|
||||
/>
|
||||
</Drawer>
|
||||
)}
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -108,12 +108,11 @@ export const NAV_SECTIONS: NavSection[] = [
|
||||
{
|
||||
label: 'nav.groupVessels',
|
||||
items: [
|
||||
{ to: '/licence-review/type/VESSEL_REGISTRATION', label: 'nav.vesselRegistrationApplicationQueue', icon: IconAnchor, permissions: APPLICATION_QUEUE },
|
||||
{ to: '/vessel-registration-report', label: 'nav.vesselRegistrationReport', icon: IconChartBar, permissions: [P.VIEW_VESSEL_REGISTRY] },
|
||||
{ to: '/vessel-registration-queue', label: 'nav.vesselRegistrationQueue', icon: IconAnchor, permissions: [P.VIEW_VESSEL_REGISTRY] },
|
||||
{ to: '/licence-review/type/VESSEL_REGISTRATION', label: 'nav.vesselRegistrationApplicationQueue', icon: IconAnchor, permissions: APPLICATION_QUEUE },
|
||||
{ to: '/licence-review/type/VESSEL_OWNERSHIP_TRANSFER', label: 'nav.ownershipTransferQueue', icon: IconArrowsExchange, permissions: APPLICATION_QUEUE },
|
||||
{ to: '/vessel-registration-queue/new', label: 'nav.vesselFormBuilder', icon: IconFilePlus, soon: true, permissions: [P.VIEW_VESSEL_REGISTRY] },
|
||||
{ to: '/vessel-registration-report', label: 'nav.vesselRegistrationReport', icon: IconChartBar, soon: true, permissions: [P.VIEW_VESSEL_REGISTRY] },
|
||||
{ to: '/vessel-registration-head-dashboard', label: 'nav.vesselRegistrationHeadDashboard', icon: IconGauge, soon: true, permissions: [P.VIEW_VESSEL_REGISTRY] },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user