'use client'; import * as React from 'react'; import { usePathname } from 'next/navigation'; import { cn } from '@/lib/utils'; import { Sidebar } from '@/components/layout/sidebar'; import { Topbar } from '@/components/layout/topbar'; import { Drawer } from '@/components/layout/drawer'; import { BottomTabBar } from '@/components/layout/bottom-tab-bar'; import { Breadcrumb, type BreadcrumbEntry } from '@/components/layout/breadcrumb'; import { persistSidebarCollapsed } from '@/lib/layout/sidebar-cookie'; import type { NavItem } from '@/lib/layout/nav-items'; interface AppShellProps { /** Logo/marca completa (sidebar desktop expandida e header do drawer). */ brand: React.ReactNode; /** Só o símbolo — sidebar desktop recolhida e topbar mobile. */ brandCollapsed: React.ReactNode; items: NavItem[]; bottomHrefs: string[]; footer: React.ReactNode; /** Rodapé enxuto (só avatar + logout) para a sidebar recolhida. */ footerCollapsed: React.ReactNode; initialCollapsed: boolean; children: React.ReactNode; } function findBreadcrumb(items: NavItem[], pathname: string): BreadcrumbEntry[] { for (const item of items) { if (item.children) { const child = item.children.find( (c) => pathname === c.href || pathname.startsWith(`${c.href}/`), ); if (child) return [{ label: item.label }, { label: child.label }]; } if (pathname === item.href || pathname.startsWith(`${item.href}/`)) { return [{ label: item.label }]; } } return []; } /** * Casco de navegação das áreas autenticadas (Etapa 14): compõe * Sidebar/Topbar/Drawer/BottomTabBar num único lugar para não duplicar essa * orquestração entre o shell do master e o do tenant — cada um só passa * marca, itens de menu e rodapé de usuário diferentes. */ export function AppShell({ brand, brandCollapsed, items, bottomHrefs, footer, footerCollapsed, initialCollapsed, children, }: AppShellProps) { const pathname = usePathname(); const [collapsed, setCollapsed] = React.useState(initialCollapsed); const [drawerOpen, setDrawerOpen] = React.useState(false); const toggleCollapsed = React.useCallback(() => { setCollapsed((prev) => { const next = !prev; persistSidebarCollapsed(next); return next; }); }, []); const bottomItems = React.useMemo( () => bottomHrefs .map((href) => items.find((item) => item.href === href)) .filter((item): item is NavItem => Boolean(item)), [items, bottomHrefs], ); const moreItems = React.useMemo( () => items.filter((item) => !bottomHrefs.includes(item.href)), [items, bottomHrefs], ); const breadcrumbItems = React.useMemo(() => findBreadcrumb(items, pathname), [items, pathname]); return (
} mobileBrand={brandCollapsed} onMenuClick={() => setDrawerOpen(true)} />
{children}
); }