ui design for schedule

This commit is contained in:
Marshal
2026-06-10 00:12:52 +00:00
parent 257428be18
commit 2b5a0f4e96
25 changed files with 1687 additions and 611 deletions

View File

@@ -0,0 +1,126 @@
/* ============================================================
EDR Freight — Header styles
============================================================ */
.fdh-root {
display: flex;
height: 80px;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 0 22px;
}
/* eyebrow above the page title */
.fdh-eyebrow {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.6px;
text-transform: uppercase;
color: #15803d;
margin-bottom: 3px;
}
.fdh-eyebrow-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.16);
}
/* action icon buttons */
.fdh-icon-btn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 12px;
background: #f7f9fb;
border: 1px solid #eef1f4;
color: #475569;
cursor: pointer;
transition: all 160ms ease;
}
.fdh-icon-btn:hover {
background: #ffffff;
border-color: rgba(21, 128, 61, 0.28);
color: #15803d;
box-shadow: 0 4px 12px -4px rgba(21, 128, 61, 0.28);
transform: translateY(-1px);
}
.fdh-icon-btn:active {
transform: translateY(0);
}
/* notification badge */
.fdh-badge {
position: absolute;
top: -5px;
right: -5px;
min-width: 17px;
height: 17px;
padding: 0 4px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 9px;
background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
color: #ffffff;
font-size: 10px;
font-weight: 700;
line-height: 1;
border: 2px solid #ffffff;
box-shadow: 0 2px 6px -1px rgba(239, 68, 68, 0.45);
}
.fdh-divider {
width: 1px;
height: 30px;
background: #e9eef3;
margin: 0 2px;
}
/* user button */
.fdh-user {
display: flex;
align-items: center;
gap: 10px;
padding: 5px 12px 5px 5px;
border-radius: 13px;
cursor: pointer;
border: 1px solid transparent;
transition: all 160ms ease;
}
.fdh-user:hover {
background: #f7f9fb;
border-color: #eef1f4;
}
.fdh-avatar-ring {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
padding: 2px;
background: linear-gradient(135deg, #22c55e 0%, #15803d 100%);
box-shadow: 0 4px 10px -3px rgba(21, 128, 61, 0.4);
}
.fdh-avatar {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border-radius: 50%;
background: linear-gradient(135deg, #16a34a 0%, #166534 100%);
color: #ffffff;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.3px;
border: 2px solid #ffffff;
}

View File

@@ -9,10 +9,10 @@ import {
Sun,
User,
} from "lucide-react";
import { Group, Stack, Text, Avatar, Menu, ActionIcon, Badge, Box } from "@mantine/core";
import { Group, Stack, Text, Menu, Tooltip, Box } from "@mantine/core";
import type { PageMeta } from "./types";
import { freightBrand } from "@/theme/freight-brand";
import "./FreightDashboardHeader.css";
export interface FreightDashboardHeaderProps {
pageMeta: PageMeta;
@@ -39,12 +39,13 @@ const FreightDashboardHeader = ({
}: FreightDashboardHeaderProps) => {
const initials =
userInitials ??
userName
(userName
.split(" ")
.filter(Boolean)
.slice(0, 2)
.map((n) => n[0].toUpperCase())
.join("");
.join("") ||
"U");
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
const userMenuRef = useRef<HTMLDivElement | null>(null);
@@ -74,133 +75,137 @@ const FreightDashboardHeader = ({
}, [isUserMenuOpen]);
return (
<header
style={{
display: "flex",
height: "80px",
alignItems: "center",
justifyContent: "space-between",
gap: "16px",
padding: "0 24px",
// borderBottom: `3px solid ${freightBrand.primary}`,
}}
>
<Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
<Text size="lg" fw={700} truncate style={{ color: freightBrand.primaryDark }}>
<header className="fdh-root">
<Stack gap={0} style={{ flex: 1, minWidth: 0 }}>
<span className="fdh-eyebrow">
<span className="fdh-eyebrow-dot" />
Freight Backoffice
</span>
<Text
fw={700}
truncate
style={{ fontSize: "20px", lineHeight: 1.2, color: "#0f172a", letterSpacing: "-0.4px" }}
>
{pageMeta.title}
</Text>
<Text size="sm" c="dimmed" truncate>
<Text size="sm" truncate style={{ color: "#94a3b8", lineHeight: 1.35 }}>
{pageMeta.subtitle}
</Text>
</Stack>
<Group gap="sm" wrap="nowrap">
<Group gap={10} wrap="nowrap">
{enableThemeToggle && (
<ActionIcon
variant="default"
size={40}
radius="lg"
onClick={onToggleTheme}
style={{
background: "var(--mantine-color-gray-1)",
border: "1px solid var(--mantine-color-gray-2)",
color: "var(--mantine-color-gray-7)",
}}
<Tooltip
label={theme === "dark" ? "Light mode" : "Dark mode"}
withArrow
openDelay={300}
>
{theme === "dark" ? <Sun size={18} /> : <Moon size={18} />}
</ActionIcon>
<button
type="button"
className="fdh-icon-btn"
onClick={onToggleTheme}
aria-label="Toggle theme"
>
{theme === "dark" ? <Sun size={18} /> : <Moon size={18} />}
</button>
</Tooltip>
)}
<ActionIcon
variant="default"
size={40}
radius="lg"
style={{
background: "var(--mantine-color-gray-1)",
border: "1px solid var(--mantine-color-gray-2)",
color: "var(--mantine-color-gray-7)",
}}
>
<Languages size={18} />
</ActionIcon>
<Tooltip label="Language" withArrow openDelay={300}>
<button type="button" className="fdh-icon-btn" aria-label="Language">
<Languages size={18} />
</button>
</Tooltip>
<ActionIcon
variant="default"
size={40}
radius="lg"
style={{
background: "var(--mantine-color-gray-1)",
border: "1px solid var(--mantine-color-gray-2)",
color: "var(--mantine-color-gray-7)",
position: "relative",
}}
>
<MessageSquare size={18} />
<Badge
size="xs"
color="red"
circle
style={{
position: "absolute",
top: "-3px",
right: "-3px",
}}
/>
</ActionIcon>
<Tooltip label="Messages" withArrow openDelay={300}>
<button type="button" className="fdh-icon-btn" aria-label="Messages">
<MessageSquare size={18} />
<span className="fdh-badge">3</span>
</button>
</Tooltip>
<ActionIcon
variant="default"
size={40}
radius="lg"
style={{
background: "var(--mantine-color-gray-1)",
border: "1px solid var(--mantine-color-gray-2)",
color: "var(--mantine-color-gray-7)",
position: "relative",
}}
>
<Bell size={18} />
<Badge
size="xs"
color="red"
circle
style={{
position: "absolute",
top: "-3px",
right: "-3px",
}}
/>
</ActionIcon>
<Tooltip label="Notifications" withArrow openDelay={300}>
<button
type="button"
className="fdh-icon-btn"
aria-label="Notifications"
>
<Bell size={18} />
<span className="fdh-badge">5</span>
</button>
</Tooltip>
<Menu position="bottom-end" shadow="md" opened={isUserMenuOpen} onOpen={() => setIsUserMenuOpen(true)} onClose={() => setIsUserMenuOpen(false)}>
<div className="fdh-divider" />
<Menu
position="bottom-end"
shadow="lg"
radius="md"
width={240}
opened={isUserMenuOpen}
onOpen={() => setIsUserMenuOpen(true)}
onClose={() => setIsUserMenuOpen(false)}
>
<Menu.Target>
<Group gap="sm" p="xs" style={{ cursor: "pointer", borderRadius: "12px" }}>
<Avatar name={initials} color="green" size="md" styles={{ root: { background: freightBrand.primary } }} />
<ChevronDown size={16} style={{ transition: "transform 0.2s", transform: isUserMenuOpen ? "rotate(180deg)" : "rotate(0deg)" }} />
</Group>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item disabled>
<Stack gap={0}>
<Text size="sm" fw={600}>
<div className="fdh-user">
<div className="fdh-avatar-ring">
<div className="fdh-avatar">{initials}</div>
</div>
<Stack gap={0} style={{ minWidth: 0 }} visibleFrom="sm">
<Text
size="sm"
fw={600}
truncate
style={{ color: "#0f172a", lineHeight: 1.25, maxWidth: 140 }}
>
{userName}
</Text>
{userEmail && (
<Text size="xs" c="dimmed">
{userEmail}
</Text>
)}
<Text
size="xs"
truncate
style={{ color: "#94a3b8", lineHeight: 1.25, maxWidth: 140 }}
>
{userEmail ?? "Administrator"}
</Text>
</Stack>
</Menu.Item>
<ChevronDown
size={16}
style={{
color: "#94a3b8",
flexShrink: 0,
transition: "transform 0.2s",
transform: isUserMenuOpen ? "rotate(180deg)" : "rotate(0deg)",
}}
/>
</div>
</Menu.Target>
<Menu.Dropdown>
<Box px="sm" py="xs">
<Group gap={10} wrap="nowrap">
<div className="fdh-avatar-ring">
<div className="fdh-avatar">{initials}</div>
</div>
<Stack gap={0} style={{ minWidth: 0 }}>
<Text size="sm" fw={600} truncate style={{ color: "#0f172a" }}>
{userName}
</Text>
{userEmail && (
<Text size="xs" truncate style={{ color: "#94a3b8" }}>
{userEmail}
</Text>
)}
</Stack>
</Group>
</Box>
<Menu.Divider />
<Menu.Item
leftSection={<User size={14} />}
leftSection={<User size={15} />}
onClick={() => setIsUserMenuOpen(false)}
>
Profile
</Menu.Item>
<Menu.Item
leftSection={<LogOut size={14} />}
leftSection={<LogOut size={15} />}
color="red"
onClick={() => {
setIsUserMenuOpen(false);

View File

@@ -0,0 +1,304 @@
/* ============================================================
EDR Freight — Sidebar styles
Polished, professional navigation surface.
============================================================ */
.fsb-aside {
height: 100%;
max-height: 100%;
width: 280px;
flex-shrink: 0;
border-radius: 16px;
border: 1px solid #eef1f4;
background: #ffffff;
box-shadow:
0 1px 2px rgba(15, 23, 42, 0.04),
0 8px 24px -16px rgba(15, 23, 42, 0.12);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ---- Brand header ---- */
.fsb-brand {
position: relative;
display: flex;
align-items: center;
gap: 12px;
height: 80px;
padding: 0 20px;
flex-shrink: 0;
border-bottom: 1px solid #f1f5f9;
overflow: hidden;
}
.fsb-brand::after {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(120px 80px at 24px 18px, rgba(34, 197, 94, 0.08), transparent 70%);
pointer-events: none;
}
.fsb-logo {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 13px;
background: linear-gradient(135deg, #22c55e 0%, #15803d 60%, #166534 100%);
box-shadow:
0 6px 16px -4px rgba(21, 128, 61, 0.45),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
flex-shrink: 0;
}
/* ---- Nav scroll region ---- */
.fsb-nav {
flex: 1;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
padding: 14px 12px 12px;
display: flex;
flex-direction: column;
gap: 20px;
}
.fsb-nav::-webkit-scrollbar {
width: 6px;
}
.fsb-nav::-webkit-scrollbar-thumb {
background: #e2e8f0;
border-radius: 3px;
}
.fsb-nav::-webkit-scrollbar-thumb:hover {
background: #cbd5e1;
}
.fsb-nav::-webkit-scrollbar-track {
background: transparent;
}
.fsb-section-label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.7px;
text-transform: uppercase;
color: #94a3b8;
padding: 0 12px;
margin-bottom: 6px;
}
/* ---- Top-level item ---- */
.fsb-item {
position: relative;
display: flex;
align-items: center;
gap: 11px;
width: 100%;
padding: 9px 12px;
border-radius: 11px;
cursor: pointer;
color: #475569;
font-size: 14px;
font-weight: 500;
line-height: 1.2;
text-align: left;
text-decoration: none;
transition:
background-color 160ms ease,
color 160ms ease,
box-shadow 160ms ease;
}
.fsb-item:hover {
background-color: #f5f7fa;
color: #0f172a;
}
.fsb-item[data-active="true"] {
background: linear-gradient(
135deg,
rgba(34, 197, 94, 0.12) 0%,
rgba(21, 128, 61, 0.06) 100%
);
color: #15803d;
font-weight: 600;
}
.fsb-item[data-active="true"]::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 22px;
border-radius: 0 4px 4px 0;
background: linear-gradient(180deg, #22c55e 0%, #15803d 100%);
}
.fsb-item-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ---- Icon well ---- */
.fsb-icon {
display: flex;
align-items: center;
justify-content: center;
width: 31px;
height: 31px;
border-radius: 9px;
flex-shrink: 0;
background: #f1f5f9;
color: #64748b;
transition: all 160ms ease;
}
.fsb-item:hover .fsb-icon {
background: #e6ebf1;
color: #334155;
}
.fsb-item[data-active="true"] .fsb-icon {
background: linear-gradient(135deg, #22c55e 0%, #15803d 100%);
color: #ffffff;
box-shadow: 0 5px 12px -2px rgba(21, 128, 61, 0.45);
}
.fsb-chevron {
flex-shrink: 0;
color: #94a3b8;
transition: transform 220ms ease;
}
/* ---- Nested branch ---- */
.fsb-branch {
margin: 2px 0 2px 22px;
padding-left: 12px;
border-left: 1.5px solid #eef2f6;
display: flex;
flex-direction: column;
gap: 2px;
}
/* group header (non-navigable) */
.fsb-group {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 7px 10px;
border-radius: 8px;
cursor: pointer;
background: transparent;
transition: background-color 150ms ease;
}
.fsb-group:hover {
background-color: #f5f7fa;
}
.fsb-group-label {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.4px;
text-transform: uppercase;
color: #94a3b8;
}
.fsb-group[data-active="true"] .fsb-group-label {
color: #15803d;
}
/* child leaf */
.fsb-child {
position: relative;
display: flex;
align-items: center;
gap: 9px;
width: 100%;
padding: 7px 10px;
border-radius: 8px;
cursor: pointer;
color: #64748b;
font-size: 13px;
font-weight: 500;
text-decoration: none;
transition:
background-color 150ms ease,
color 150ms ease;
}
.fsb-child:hover {
background-color: #f5f7fa;
color: #0f172a;
}
.fsb-child[data-active="true"] {
color: #15803d;
font-weight: 600;
background-color: rgba(21, 128, 61, 0.08);
}
.fsb-dot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
background: #cbd5e1;
transition: all 150ms ease;
}
.fsb-child:hover .fsb-dot {
background: #94a3b8;
}
.fsb-child[data-active="true"] .fsb-dot {
background: #15803d;
box-shadow: 0 0 0 3px rgba(21, 128, 61, 0.16);
}
/* ---- Footer status card ---- */
.fsb-footer {
flex-shrink: 0;
padding: 12px;
border-top: 1px solid #f1f5f9;
}
.fsb-status {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 11px;
background: linear-gradient(135deg, #f0fdf4 0%, #f8fafc 100%);
border: 1px solid #e7f3ec;
}
.fsb-pulse {
position: relative;
width: 9px;
height: 9px;
border-radius: 50%;
background: #22c55e;
flex-shrink: 0;
}
.fsb-pulse::after {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
background: #22c55e;
animation: fsb-pulse 2s ease-out infinite;
}
@keyframes fsb-pulse {
0% {
transform: scale(1);
opacity: 0.6;
}
100% {
transform: scale(2.6);
opacity: 0;
}
}

View File

@@ -1,15 +1,16 @@
import {
type MouseEvent,
type ReactNode,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { ChevronDown, ChevronRight, Train } from "lucide-react";
import { Stack, Group, Text, Box, UnstyledButton, NavLink } from "@mantine/core";
import { ChevronDown, Train } from "lucide-react";
import { Box, Stack, Text } from "@mantine/core";
import type { SidebarItem, SidebarSection } from "./types";
import { freightBrand } from "@/theme/freight-brand";
import "./FreightSidebar.css";
export interface FreightSidebarProps {
sections: SidebarSection[];
@@ -105,7 +106,7 @@ const FreightSidebar = ({
children: SidebarItem[],
depth: number,
parentKey: string,
) =>
): ReactNode =>
children.map((child) => {
const key = sidebarItemKey(child, parentKey);
const isGroup = Boolean(child.children?.length) && !child.href;
@@ -115,87 +116,50 @@ const FreightSidebar = ({
const groupActive = branchContainsActive(child.children!);
return (
<Stack key={key} gap={4}>
<UnstyledButton
<div key={key}>
<button
type="button"
className="fsb-group"
data-active={groupActive}
onClick={() => toggleExpanded(key)}
style={{
background: groupActive ? freightBrand.mutedBg : "transparent",
padding: "8px 12px",
borderRadius: "8px",
width: "100%",
cursor: "pointer",
}}
>
<Group justify="space-between">
<Text size="xs" fw={600} style={{ color: groupActive ? freightBrand.primary : undefined }} c={groupActive ? undefined : "dimmed"} tt="uppercase">
{child.label}
</Text>
<ChevronDown
size={14}
style={{
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
transition: "transform 0.2s",
color: groupActive ? freightBrand.primary : "var(--mantine-color-gray-5)",
}}
/>
</Group>
</UnstyledButton>
<span className="fsb-group-label">{child.label}</span>
<ChevronDown
size={13}
className="fsb-chevron"
style={{
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
color: groupActive ? "#15803d" : undefined,
}}
/>
</button>
{isOpen && (
<Stack gap={2} style={{ paddingLeft: "12px", borderLeft: `2px solid ${freightBrand.mutedBorder}` }}>
<div className="fsb-branch">
{renderNavBranch(child.children!, depth + 1, key)}
</Stack>
</div>
)}
</Stack>
</div>
);
}
if (!child.href) return null;
const childHref = child.href.toLowerCase();
const childActiveHref = isHrefActive(childHref);
const childActive = isHrefActive(child.href);
return (
<NavLink
<a
key={key}
component="a"
href={child.href}
onClick={(e) => navigateTo(e as any, child.href!)}
label={child.label}
active={childActiveHref}
color="green"
style={{
borderRadius: "8px",
cursor: "pointer",
fontSize: "14px",
}}
rightSection={<ChevronRight size={16} />}
/>
className="fsb-child"
data-active={childActive}
onClick={(e) => navigateTo(e, child.href!)}
>
<span className="fsb-dot" />
<span className="fsb-item-label">{child.label}</span>
</a>
);
});
const renderIconWell = (icon: React.ReactNode, active: boolean) => {
if (!icon) return null;
return (
<Box
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "30px",
height: "30px",
borderRadius: "8px",
flexShrink: 0,
background: active ? freightBrand.gradient : "var(--mantine-color-gray-1)",
color: active ? "white" : "var(--mantine-color-gray-6)",
boxShadow: active ? freightBrand.shadowSm : "none",
transition: "all 0.2s ease",
}}
>
{icon}
</Box>
);
};
const renderTopLevelItem = (item: SidebarItem) => {
if (!item.href) return null;
@@ -207,132 +171,92 @@ const FreightSidebar = ({
const isCurrentItem = hasChildren
? activePath === itemHref
: isHrefActive(itemHref);
const isSectionActive = childActive && !isCurrentItem;
const isActive = isCurrentItem || isSectionActive;
const isActive = isCurrentItem || childActive;
const isOpen = expanded[item.href] ?? false;
const leafActive = isCurrentItem && !hasChildren;
return (
<Stack key={item.href} gap={0}>
<NavLink
component="a"
<Box key={item.href}>
<a
href={item.href}
onClick={(e) => navigateTo(e as any, item.href!)}
label={item.label}
leftSection={renderIconWell(item.icon, isActive)}
active={leafActive}
color="green"
variant="light"
style={{
borderRadius: "10px",
cursor: "pointer",
fontSize: "14px",
fontWeight: 500,
padding: "8px 10px",
}}
rightSection={
hasChildren ? (
<ChevronDown
size={16}
style={{
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
transition: "transform 0.2s",
}}
onClick={(e) => {
e.preventDefault();
toggleExpanded(item.href!);
}}
/>
) : (
<ChevronRight size={16} />
)
}
/>
className="fsb-item"
data-active={isActive}
onClick={(e) => navigateTo(e, item.href!)}
>
{item.icon && <span className="fsb-icon">{item.icon}</span>}
<span className="fsb-item-label">{item.label}</span>
{hasChildren && (
<ChevronDown
size={16}
className="fsb-chevron"
style={{
transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)",
}}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpanded(item.href!);
}}
/>
)}
</a>
{hasChildren && isOpen && (
<Stack gap={4} style={{ paddingLeft: "16px", borderLeft: `2px solid ${freightBrand.mutedBorder}` }}>
<div className="fsb-branch">
{renderNavBranch(item.children!, 0, item.href)}
</Stack>
</div>
)}
</Stack>
</Box>
);
};
return (
<Box
component="aside"
style={{
height: "100%",
maxHeight: "100%",
width: "280px",
flexShrink: 0,
borderRadius: "12px",
border: "1px solid var(--mantine-color-gray-2)",
background: "white",
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.05)",
display: "flex",
flexDirection: "column",
overflow: "hidden",
}}
>
<Group
gap={12}
px="lg"
py="md"
style={{
borderBottom: "1px solid var(--mantine-color-gray-2)",
flexShrink: 0,
height: "80px",
}}
wrap="nowrap"
>
<Box
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "44px",
height: "44px",
borderRadius: "12px",
background: freightBrand.gradient,
boxShadow: freightBrand.shadow,
flexShrink: 0,
}}
>
<Train size={24} color="white" strokeWidth={2} />
</Box>
<Stack gap={0} style={{ minWidth: 0 }}>
<Text size="md" fw={700} style={{ letterSpacing: "-0.3px", lineHeight: 1.2 }}>
<Box component="aside" className="fsb-aside">
<div className="fsb-brand">
<div className="fsb-logo">
<Train size={23} color="white" strokeWidth={2.1} />
</div>
<Stack gap={1} style={{ minWidth: 0, position: "relative", zIndex: 1 }}>
<Text
size="md"
fw={700}
style={{ letterSpacing: "-0.3px", lineHeight: 1.2, color: "#0f172a" }}
>
EDR Freight
</Text>
<Text size="xs" c="dimmed" fw={500} style={{ letterSpacing: "0.3px" }}>
Backoffice
<Text
size="xs"
fw={600}
style={{ letterSpacing: "0.4px", color: "#94a3b8" }}
>
Backoffice Console
</Text>
</Stack>
</Group>
</div>
<Stack
component="nav"
gap="lg"
p="md"
style={{
flex: 1,
minHeight: 0,
overflowY: "auto",
overscrollBehavior: "contain",
}}
>
<nav className="fsb-nav">
{sections.map((section) => (
<Stack key={section.title} gap={8}>
<Text size="xs" fw={600} c="dimmed" tt="uppercase" style={{ letterSpacing: "0.5px", paddingLeft: "8px" }}>
{section.title}
</Text>
<Stack gap={2}>
<div key={section.title}>
<div className="fsb-section-label">{section.title}</div>
<Stack gap={3}>
{section.items.map((item) => renderTopLevelItem(item))}
</Stack>
</Stack>
</div>
))}
</Stack>
</nav>
<div className="fsb-footer">
<div className="fsb-status">
<span className="fsb-pulse" />
<Stack gap={0} style={{ minWidth: 0 }}>
<Text size="xs" fw={600} style={{ color: "#166534", lineHeight: 1.3 }}>
All systems operational
</Text>
<Text size="10px" style={{ color: "#94a3b8", lineHeight: 1.3 }}>
EDR Platform · v1.0
</Text>
</Stack>
</div>
</div>
</Box>
);
};