void;
+}) {
+ const Icon = item.icon;
+ return (
+
+
+
{label}
+
+ );
+}
+
+export function MobileNav() {
+ const t = useTranslations("nav");
+ const tGroups = useTranslations("nav.groups");
+ const tBrand = useTranslations("brand");
+ const locale = useLocale();
+ const pathname = usePathname();
+ const user = useAuthStore((s) => s.user);
+ const role = user?.role;
+ const branchId = user?.branchId ?? null;
+ const permissions = useMemo(() => permissionsOf(user), [user]);
+ const [open, setOpen] = useState(false);
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => setMounted(true), []);
+
+ // Close on Escape; lock body scroll while open.
+ useEffect(() => {
+ if (!open) return;
+ const onKey = (e: KeyboardEvent) => {
+ if (e.key === "Escape") setOpen(false);
+ };
+ document.addEventListener("keydown", onKey);
+ const prevOverflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ return () => {
+ document.removeEventListener("keydown", onKey);
+ document.body.style.overflow = prevOverflow;
+ };
+ }, [open]);
+
+ const close = () => setOpen(false);
+ const isRtl = locale !== "en";
+
+ const visible = (items: NavItemDef[]) =>
+ items.filter((item) => canSeeNavItem(item.key, role, branchId, permissions));
+
+ const isActive = (item: NavItemDef) =>
+ item.href === "/"
+ ? pathname === "/"
+ : pathname === item.href || pathname.startsWith(`${item.href}/`);
+
+ return (
+ <>
+
+
+ {mounted &&
+ open &&
+ createPortal(
+
+ {/* Backdrop */}
+
+ {/* Panel */}
+
+
+ {tBrand("name")}
+
+
+
+
+
+ {user && (
+
+
+
+ {(user.actor ?? user.role).charAt(0).toUpperCase()}
+
+
+
+
{user.actor ?? user.userId}
+
{user.role}
+
+
+ )}
+
+
,
+ document.body
+ )}
+ >
+ );
+}
diff --git a/web/dashboard/src/components/layout/sidebar.tsx b/web/dashboard/src/components/layout/sidebar.tsx
index ea08798..e2652e3 100644
--- a/web/dashboard/src/components/layout/sidebar.tsx
+++ b/web/dashboard/src/components/layout/sidebar.tsx
@@ -283,7 +283,8 @@ export function Sidebar({ side }: { side: "left" | "right" }) {
return (