From d4a5ba767341a3b694edaa456537d07c575d6c5a Mon Sep 17 00:00:00 2001 From: estifanos Date: Fri, 21 Aug 2026 07:38:01 +0000 Subject: [PATCH] UI fixes --- .../src/app/layouts/BackofficeLayout.tsx | 90 +++++++++++------- libs/ui/src/lib/layout/AppHeader.tsx | 16 ++++ libs/ui/src/lib/layout/AppTopNav.tsx | 92 +++++++++++-------- 3 files changed, 127 insertions(+), 71 deletions(-) diff --git a/apps/backoffice/src/app/layouts/BackofficeLayout.tsx b/apps/backoffice/src/app/layouts/BackofficeLayout.tsx index de599a69a..d65aaac45 100644 --- a/apps/backoffice/src/app/layouts/BackofficeLayout.tsx +++ b/apps/backoffice/src/app/layouts/BackofficeLayout.tsx @@ -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'; @@ -26,6 +26,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, @@ -144,7 +152,9 @@ export function BackofficeLayout() { return ( -
+
+ + + {t('app.name')} + + + ) + } + // 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} @@ -182,13 +207,14 @@ export function BackofficeLayout() {
{!isSidebar && ( -
@@ -199,7 +225,7 @@ export function BackofficeLayout() { activePath={location.pathname} onNavigate={go} /> -
+ )} @@ -210,7 +236,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)", }} > - { - go(item); - closeNav(); - }} - brandName={t('app.name')} - brandSubtitle={t('app.authority')} - brandLogo={} - /> - - )} + + { + go(item); + closeNav(); + }} + brandName={t('app.name')} + brandSubtitle={t('app.authority')} + brandLogo={} + /> + ); } diff --git a/libs/ui/src/lib/layout/AppHeader.tsx b/libs/ui/src/lib/layout/AppHeader.tsx index a7e14a148..0e5755955 100644 --- a/libs/ui/src/lib/layout/AppHeader.tsx +++ b/libs/ui/src/lib/layout/AppHeader.tsx @@ -14,6 +14,7 @@ import { IconLogout, IconUserCircle, } from '@tabler/icons-react'; +import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { LanguageSwitcher } from './LanguageSwitcher'; import { ColorSchemeToggle } from './ColorSchemeToggle'; @@ -36,6 +37,17 @@ interface AppHeaderProps { supportedLanguages: readonly string[]; onNotificationsClick?: () => void; notificationCount?: number; + /** + * Rendered at the far left. The sidebar layout carries the brand in the + * sidebar itself; the top-bar layout has no sidebar, so it passes the brand + * here rather than leaving the chrome unbranded. + */ + brand?: ReactNode; + /** + * Breakpoint from which the burger is hidden. The top-bar layout only needs + * it on small screens, where the drawer replaces the nav strip. + */ + burgerHiddenFrom?: string; } export function AppHeader({ @@ -50,17 +62,21 @@ export function AppHeader({ supportedLanguages, onNotificationsClick, notificationCount, + brand, + burgerHiddenFrom, }: AppHeaderProps) { const isMobile = typeof window !== 'undefined' && window.innerWidth < 768; return ( + {brand} {/* Hamburger — styled like user-management Top.tsx */} {/* The Burger itself owns the click so the control is a real, keyboard reachable