Merge branch 'WorkflowChange' into logestic_chnage

This commit is contained in:
Nati Nigussie
2026-08-27 11:23:14 +03:00
committed by GitHub
92 changed files with 5309 additions and 1041 deletions

View File

@@ -1,5 +1,5 @@
import { useCallback, useMemo, useState } from 'react';
import { AppShell, Drawer } from '@mantine/core';
import { AppShell, Box, Drawer, Group, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
@@ -27,6 +27,14 @@ const BADGE_POLL_MS = 60_000;
const HEADER_HEIGHT = 116;
/**
* Horizontal inset of the header chrome. `AppHeader` adds its own `px="lg"`
* inside this, so the nav strip below needs the sum to line up with the
* controls above it — it used to start 20px to their left.
*/
const CHROME_PAD_X = 32;
const NAV_STRIP_PAD_X = CHROME_PAD_X + 20;
/**
* 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,
@@ -149,7 +157,9 @@ export function BackofficeLayout() {
the 20-plus nav items instead of tabbing through them every time. */}
<SkipLink />
<AppShell
header={{ height: isSidebar ? 74 : HEADER_HEIGHT }}
// The top layout drops its nav strip on small screens — the drawer is
// the nav there — so the header shrinks back to a single row with it.
header={{ height: isSidebar ? 74 : { base: 74, sm: HEADER_HEIGHT } }}
navbar={
isSidebar
? {
@@ -167,13 +177,28 @@ export function BackofficeLayout() {
<AppShell.Header
style={{
background: "var(--mantine-color-body)",
borderBottom: "1px solid var(--mantine-color-gray-2)",
borderBottom: "1px solid var(--mantine-color-default-border)",
display: "flex",
flexDirection: "column",
}}
>
<div style={{ height: 74, flexShrink: 0, padding: "0 32px" }}>
<div
style={{ height: 74, flexShrink: 0, padding: `0 ${CHROME_PAD_X}px` }}
>
<AppHeader
brand={
isSidebar ? undefined : (
<Group gap="xs" wrap="nowrap">
<BrandMark size={28} />
<Text fw={700} size="sm" lh={1.1} visibleFrom="xs">
{t('app.name')}
</Text>
</Group>
)
}
// Nothing to toggle on a desktop top bar; on mobile it opens the
// drawer below.
burgerHiddenFrom={isSidebar ? undefined : 'sm'}
onToggleNav={toggleNav}
onToggleSidebar={isSidebar ? handleToggleCollapse : toggleNav}
navOpened={opened}
@@ -187,13 +212,14 @@ export function BackofficeLayout() {
</div>
{!isSidebar && (
<div
<Box
visibleFrom="sm"
style={{
display: 'flex',
alignItems: 'center',
padding: '0 32px',
padding: `0 ${NAV_STRIP_PAD_X}px`,
height: 42,
borderTop: '1px solid var(--mantine-color-gray-1)',
borderTop: '1px solid var(--mantine-color-default-border)',
flexShrink: 0,
}}
>
@@ -204,7 +230,7 @@ export function BackofficeLayout() {
activePath={location.pathname}
onNavigate={go}
/>
</div>
</Box>
)}
</AppShell.Header>
@@ -215,7 +241,7 @@ export function BackofficeLayout() {
overflow: "hidden",
transition: "width 200ms ease",
background: "var(--mantine-color-body)",
borderRight: "1px solid var(--mantine-color-gray-2)",
borderRight: "1px solid var(--mantine-color-default-border)",
}}
>
<AppSidebar
@@ -243,30 +269,28 @@ export function BackofficeLayout() {
{/* 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>
)}
<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>
</>
);

View File

@@ -103,6 +103,12 @@ export const NAV_SECTIONS: NavSection[] = [
{ to: '/btc-queue', label: 'nav.btcQueue', icon: IconShieldCheck, permissions: [P.VIEW_SEAFARER_REGISTRY] },
{ to: '/licence-review/type/ENDORSEMENT_COC', label: 'nav.endorsementCocQueue', icon: IconRubberStamp, permissions: [P.VIEW_SEAFARER_REGISTRY] },
{ to: '/licence-review/type/ENDORSEMENT_GOC', label: 'nav.endorsementGocQueue', icon: IconRubberStamp, permissions: [P.VIEW_SEAFARER_REGISTRY] },
{ to: '/seafarer-registrations', label: 'nav.seafarerRegistrationQueue', icon: IconId, permissions: APPLICATION_QUEUE },
{ to: '/licence-review/type/CERTIFICATE_OF_COMPETENCY', label: 'nav.cocQueue', icon: IconShieldCheck, permissions: APPLICATION_QUEUE },
{ to: '/licence-review/type/CERTIFICATE_OF_PROFICIENCY', label: 'nav.copQueue', icon: IconShieldCheck, permissions: APPLICATION_QUEUE },
{ to: '/seaman-book-queue', label: 'nav.seamanBookQueue', icon: IconBook2, permissions: APPLICATION_QUEUE },
{ to: '/btc-queue', label: 'nav.btcQueue', icon: IconShieldCheck, permissions: APPLICATION_QUEUE },
{ to: '/licence-review/type/ENDORSEMENT_SEAFARER', label: 'nav.endorsementQueue', icon: IconRubberStamp, permissions: APPLICATION_QUEUE },
{ to: '/sea-service-verification', label: 'nav.seaServiceVerification', icon: IconAnchor, permissions: [P.VERIFY_SEAFARER_RECORDS] },
{ to: '/medical-verification', label: 'nav.medicalVerification', icon: IconHeart, permissions: [P.VERIFY_SEAFARER_RECORDS] },
],