@trackany-device/components 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/package.json +185 -0
  2. package/src/assets/logo.png +0 -0
  3. package/src/assets/map/arrows/map-arrow-blue.png +0 -0
  4. package/src/assets/map/arrows/map-arrow-green.png +0 -0
  5. package/src/assets/map/arrows/map-arrow-purple.png +0 -0
  6. package/src/assets/map/arrows/map-arrow-red.png +0 -0
  7. package/src/assets/map/flags/flag-blue.png +0 -0
  8. package/src/assets/map/flags/flag-green.png +0 -0
  9. package/src/assets/map/flags/flag-red.png +0 -0
  10. package/src/assets/map/flags/flag-yellow.png +0 -0
  11. package/src/assets/map/pins/map-pin-blue.png +0 -0
  12. package/src/assets/map/pins/map-pin-green.png +0 -0
  13. package/src/assets/map/pins/map-pin-purple.png +0 -0
  14. package/src/assets/map/pins/map-pin-red.png +0 -0
  15. package/src/components/Card.tsx +9 -0
  16. package/src/components/alert-error.tsx +24 -0
  17. package/src/components/app-content.tsx +22 -0
  18. package/src/components/app-header.tsx +153 -0
  19. package/src/components/app-logo-icon.tsx +13 -0
  20. package/src/components/app-logo.tsx +21 -0
  21. package/src/components/app-shell.tsx +19 -0
  22. package/src/components/app-sidebar-header.tsx +68 -0
  23. package/src/components/app-sidebar.tsx +106 -0
  24. package/src/components/appearance-tabs.tsx +46 -0
  25. package/src/components/breadcrumbs.tsx +50 -0
  26. package/src/components/cms/blurred-image.tsx +111 -0
  27. package/src/components/cms/section-bg.tsx +473 -0
  28. package/src/components/cms/section-button.tsx +127 -0
  29. package/src/components/cms/sections/banner-5050-section.tsx +135 -0
  30. package/src/components/cms/sections/blogs-listing-section.tsx +270 -0
  31. package/src/components/cms/sections/cards-grid-section.tsx +185 -0
  32. package/src/components/cms/sections/contact-form-section.tsx +157 -0
  33. package/src/components/cms/sections/cta-section.tsx +101 -0
  34. package/src/components/cms/sections/featured-blog-slider-section.tsx +256 -0
  35. package/src/components/cms/sections/featured-products-grid-section.tsx +173 -0
  36. package/src/components/cms/sections/featured-solutions-grid-section.tsx +183 -0
  37. package/src/components/cms/sections/hero-section.tsx +180 -0
  38. package/src/components/cms/sections/solutions-with-filter-section.tsx +234 -0
  39. package/src/components/cms/sections/text-section.tsx +77 -0
  40. package/src/components/cutout-image.tsx +228 -0
  41. package/src/components/devices/devices-mini-map.tsx +275 -0
  42. package/src/components/docs/docs-shell.tsx +280 -0
  43. package/src/components/fleet-hero-animated.tsx +383 -0
  44. package/src/components/input-error.tsx +17 -0
  45. package/src/components/keenicons/assets/duotone/Read Me.txt +7 -0
  46. package/src/components/keenicons/assets/duotone/demo-files/demo.css +160 -0
  47. package/src/components/keenicons/assets/duotone/demo-files/demo.js +32 -0
  48. package/src/components/keenicons/assets/duotone/demo.html +12424 -0
  49. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.svg +1109 -0
  50. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.ttf +0 -0
  51. package/src/components/keenicons/assets/duotone/fonts/keenicons-duotone.woff +0 -0
  52. package/src/components/keenicons/assets/duotone/selection.json +17313 -0
  53. package/src/components/keenicons/assets/duotone/style.css +4931 -0
  54. package/src/components/keenicons/assets/filled/Read Me.txt +7 -0
  55. package/src/components/keenicons/assets/filled/demo-files/demo.css +160 -0
  56. package/src/components/keenicons/assets/filled/demo-files/demo.js +32 -0
  57. package/src/components/keenicons/assets/filled/demo.html +12370 -0
  58. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.svg +1082 -0
  59. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.ttf +0 -0
  60. package/src/components/keenicons/assets/filled/fonts/keenicons-filled.woff +0 -0
  61. package/src/components/keenicons/assets/filled/selection.json +17096 -0
  62. package/src/components/keenicons/assets/filled/style.css +4769 -0
  63. package/src/components/keenicons/assets/outline/Read Me.txt +7 -0
  64. package/src/components/keenicons/assets/outline/demo-files/demo.css +160 -0
  65. package/src/components/keenicons/assets/outline/demo-files/demo.js +32 -0
  66. package/src/components/keenicons/assets/outline/demo.html +11356 -0
  67. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.svg +575 -0
  68. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.ttf +0 -0
  69. package/src/components/keenicons/assets/outline/fonts/keenicons-outline.woff +0 -0
  70. package/src/components/keenicons/assets/outline/selection.json +13054 -0
  71. package/src/components/keenicons/assets/outline/style.css +1721 -0
  72. package/src/components/keenicons/assets/solid/Read Me.txt +7 -0
  73. package/src/components/keenicons/assets/solid/demo-files/demo.css +160 -0
  74. package/src/components/keenicons/assets/solid/demo-files/demo.js +32 -0
  75. package/src/components/keenicons/assets/solid/demo.html +11356 -0
  76. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.svg +575 -0
  77. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.ttf +0 -0
  78. package/src/components/keenicons/assets/solid/fonts/keenicons-solid.woff +0 -0
  79. package/src/components/keenicons/assets/solid/selection.json +13048 -0
  80. package/src/components/keenicons/assets/solid/style.css +1721 -0
  81. package/src/components/keenicons/assets/styles.css +4 -0
  82. package/src/components/keenicons/index.ts +2 -0
  83. package/src/components/keenicons/keenicons.tsx +16 -0
  84. package/src/components/keenicons/types.ts +7 -0
  85. package/src/components/nav-footer.tsx +49 -0
  86. package/src/components/nav-main.tsx +53 -0
  87. package/src/components/nav-user.tsx +59 -0
  88. package/src/components/notification-bell.tsx +190 -0
  89. package/src/components/products/product-card.tsx +159 -0
  90. package/src/components/text-link.tsx +23 -0
  91. package/src/components/ui/accordion-menu.tsx +322 -0
  92. package/src/components/ui/accordion.tsx +133 -0
  93. package/src/components/ui/alert-dialog.tsx +82 -0
  94. package/src/components/ui/alert.tsx +63 -0
  95. package/src/components/ui/avatar-group.tsx +129 -0
  96. package/src/components/ui/avatar.tsx +67 -0
  97. package/src/components/ui/badge.tsx +230 -0
  98. package/src/components/ui/breadcrumb.tsx +88 -0
  99. package/src/components/ui/button.tsx +412 -0
  100. package/src/components/ui/calendar.tsx +56 -0
  101. package/src/components/ui/card.tsx +147 -0
  102. package/src/components/ui/chart.tsx +290 -0
  103. package/src/components/ui/checkbox.tsx +47 -0
  104. package/src/components/ui/code.tsx +45 -0
  105. package/src/components/ui/collapsible.tsx +31 -0
  106. package/src/components/ui/command-palette.tsx +189 -0
  107. package/src/components/ui/command.tsx +138 -0
  108. package/src/components/ui/cookie-banner.tsx +220 -0
  109. package/src/components/ui/copy-button.tsx +60 -0
  110. package/src/components/ui/data-grid-column-filter.tsx +124 -0
  111. package/src/components/ui/data-grid-column-header.tsx +284 -0
  112. package/src/components/ui/data-grid-column-visibility.tsx +38 -0
  113. package/src/components/ui/data-grid-pagination.tsx +206 -0
  114. package/src/components/ui/data-grid-table-dnd-rows.tsx +147 -0
  115. package/src/components/ui/data-grid-table-dnd.tsx +175 -0
  116. package/src/components/ui/data-grid-table.tsx +500 -0
  117. package/src/components/ui/data-grid.tsx +193 -0
  118. package/src/components/ui/data-list.tsx +76 -0
  119. package/src/components/ui/datefield.tsx +91 -0
  120. package/src/components/ui/dialog.tsx +139 -0
  121. package/src/components/ui/divider.tsx +41 -0
  122. package/src/components/ui/drawer.tsx +59 -0
  123. package/src/components/ui/dropdown-menu.tsx +224 -0
  124. package/src/components/ui/empty-state.tsx +54 -0
  125. package/src/components/ui/file-upload.tsx +152 -0
  126. package/src/components/ui/form.tsx +88 -0
  127. package/src/components/ui/icon.tsx +14 -0
  128. package/src/components/ui/input-otp.tsx +71 -0
  129. package/src/components/ui/input.tsx +155 -0
  130. package/src/components/ui/kbd.tsx +26 -0
  131. package/src/components/ui/label.tsx +31 -0
  132. package/src/components/ui/navigation-menu.tsx +168 -0
  133. package/src/components/ui/pagination.tsx +37 -0
  134. package/src/components/ui/placeholder-pattern.tsx +21 -0
  135. package/src/components/ui/popover.tsx +50 -0
  136. package/src/components/ui/progress.tsx +65 -0
  137. package/src/components/ui/radio-group.tsx +73 -0
  138. package/src/components/ui/resizable.tsx +39 -0
  139. package/src/components/ui/scroll-area.tsx +50 -0
  140. package/src/components/ui/select.tsx +234 -0
  141. package/src/components/ui/separator.tsx +24 -0
  142. package/src/components/ui/sheet.tsx +147 -0
  143. package/src/components/ui/sidebar.tsx +721 -0
  144. package/src/components/ui/skeleton.tsx +15 -0
  145. package/src/components/ui/slider.tsx +35 -0
  146. package/src/components/ui/sonner.tsx +28 -0
  147. package/src/components/ui/sortable.tsx +724 -0
  148. package/src/components/ui/spinner.tsx +17 -0
  149. package/src/components/ui/stat-card.tsx +82 -0
  150. package/src/components/ui/stepper.tsx +410 -0
  151. package/src/components/ui/switch.tsx +68 -0
  152. package/src/components/ui/table.tsx +42 -0
  153. package/src/components/ui/tabs.tsx +196 -0
  154. package/src/components/ui/timeline.tsx +90 -0
  155. package/src/components/ui/toggle-group.tsx +73 -0
  156. package/src/components/ui/toggle.tsx +45 -0
  157. package/src/components/ui/tooltip.tsx +55 -0
  158. package/src/components/user-info.tsx +33 -0
  159. package/src/components/user-menu-content.tsx +53 -0
  160. package/src/components/web/SiteFooter.tsx +154 -0
  161. package/src/components/web/SiteHeader.tsx +159 -0
  162. package/src/components/workflows/workflow-canvas.tsx +321 -0
  163. package/src/controls/Blockquote.tsx +25 -0
  164. package/src/controls/Button.tsx +101 -0
  165. package/src/controls/Checkbox.tsx +29 -0
  166. package/src/controls/DateField.tsx +37 -0
  167. package/src/controls/FormField.tsx +20 -0
  168. package/src/controls/Heading.tsx +28 -0
  169. package/src/controls/Input.tsx +21 -0
  170. package/src/controls/Label.tsx +18 -0
  171. package/src/controls/Paragraph.tsx +39 -0
  172. package/src/controls/PasswordInput.tsx +40 -0
  173. package/src/controls/RadioGroup.tsx +70 -0
  174. package/src/controls/Select.tsx +24 -0
  175. package/src/controls/Slider.tsx +33 -0
  176. package/src/controls/Switch.tsx +31 -0
  177. package/src/controls/Textarea.tsx +22 -0
  178. package/src/elements/ConfirmPasswordForm.tsx +43 -0
  179. package/src/elements/DeviceStatusBadge.tsx +38 -0
  180. package/src/elements/DriverCard.tsx +67 -0
  181. package/src/elements/ForgotPasswordForm.tsx +64 -0
  182. package/src/elements/IncidentCard.tsx +67 -0
  183. package/src/elements/LoginForm.tsx +100 -0
  184. package/src/elements/OtpForm.tsx +71 -0
  185. package/src/elements/RegisterForm.tsx +150 -0
  186. package/src/elements/ResetPasswordForm.tsx +72 -0
  187. package/src/elements/SmsChallengeForm.tsx +104 -0
  188. package/src/elements/VehicleCard.tsx +73 -0
  189. package/src/elements/VerifyEmailForm.tsx +39 -0
  190. package/src/hooks/use-appearance.tsx +117 -0
  191. package/src/hooks/use-applied-theme.ts +98 -0
  192. package/src/hooks/use-clipboard.ts +34 -0
  193. package/src/hooks/use-current-url.ts +83 -0
  194. package/src/hooks/use-dark-mode.ts +48 -0
  195. package/src/hooks/use-flash-toast.ts +29 -0
  196. package/src/hooks/use-initials.tsx +24 -0
  197. package/src/hooks/use-mobile-navigation.ts +12 -0
  198. package/src/hooks/use-mobile.tsx +38 -0
  199. package/src/index.ts +408 -0
  200. package/src/layouts/AppLayout.tsx +60 -0
  201. package/src/layouts/AuthLayout.tsx +32 -0
  202. package/src/layouts/SettingsLayout.tsx +21 -0
  203. package/src/layouts/app/AIChatLayout.tsx +73 -0
  204. package/src/layouts/app/AsideSidebarLayout.tsx +3 -0
  205. package/src/layouts/app/CalendarSidebarLayout.tsx +69 -0
  206. package/src/layouts/app/CommunitiesNavbarLayout.tsx +3 -0
  207. package/src/layouts/app/DualNavbarSidebarLayout.tsx +3 -0
  208. package/src/layouts/app/FocusSidebarLayout.tsx +75 -0
  209. package/src/layouts/app/MailLayout.tsx +69 -0
  210. package/src/layouts/app/MegaMenuHeaderLayout.tsx +3 -0
  211. package/src/layouts/app/MegaMenuLayout.tsx +81 -0
  212. package/src/layouts/app/MegaMenuNavbarLayout.tsx +88 -0
  213. package/src/layouts/app/MegaMenuSearchNavbarLayout.tsx +3 -0
  214. package/src/layouts/app/NavbarCollapsibleLayout.tsx +88 -0
  215. package/src/layouts/app/NavbarCollapsibleLinksLayout.tsx +3 -0
  216. package/src/layouts/app/NavbarMinimalLayout.tsx +3 -0
  217. package/src/layouts/app/NavbarMinimalSidebarLayout.tsx +3 -0
  218. package/src/layouts/app/NavbarSidebarDashboardLayout.tsx +3 -0
  219. package/src/layouts/app/NavbarSidebarLayout.tsx +92 -0
  220. package/src/layouts/app/NavbarSimpleSidebarLayout.tsx +3 -0
  221. package/src/layouts/app/NavbarTitledSidebarLayout.tsx +3 -0
  222. package/src/layouts/app/PanelSidebarLayout.tsx +3 -0
  223. package/src/layouts/app/SearchNavbarSidebarLayout.tsx +3 -0
  224. package/src/layouts/app/SidebarBreadcrumbLayout.tsx +3 -0
  225. package/src/layouts/app/SidebarCleanLayout.tsx +3 -0
  226. package/src/layouts/app/SidebarCommunitiesLayout.tsx +3 -0
  227. package/src/layouts/app/SidebarContentLayout.tsx +3 -0
  228. package/src/layouts/app/SidebarDualMenuLayout.tsx +104 -0
  229. package/src/layouts/app/SidebarFixedLayout.tsx +166 -0
  230. package/src/layouts/app/SidebarFooterNavbarLayout.tsx +3 -0
  231. package/src/layouts/app/SidebarHeaderMenuLayout.tsx +3 -0
  232. package/src/layouts/app/SidebarMegaMenuLayout.tsx +4 -0
  233. package/src/layouts/app/SidebarMinimalLayout.tsx +70 -0
  234. package/src/layouts/app/SidebarMobileSearchLayout.tsx +3 -0
  235. package/src/layouts/app/SidebarMultiPanelLayout.tsx +3 -0
  236. package/src/layouts/app/SidebarPrimarySecondaryLayout.tsx +3 -0
  237. package/src/layouts/app/SidebarSearchHeaderLayout.tsx +103 -0
  238. package/src/layouts/app/SidebarSearchToolbarLayout.tsx +3 -0
  239. package/src/layouts/app/SidebarTabsDualLayout.tsx +3 -0
  240. package/src/layouts/app/SidebarTabsLayout.tsx +98 -0
  241. package/src/layouts/app/SidebarTreeLayout.tsx +3 -0
  242. package/src/layouts/app/SplitNavbarLayout.tsx +3 -0
  243. package/src/layouts/app/SplitSidebarDashboardLayout.tsx +3 -0
  244. package/src/layouts/app/SplitSidebarLayout.tsx +99 -0
  245. package/src/layouts/app/TopNavLayout.tsx +105 -0
  246. package/src/layouts/app/TopNavLinksLayout.tsx +3 -0
  247. package/src/layouts/app/WorkspaceBreadcrumbLayout.tsx +3 -0
  248. package/src/layouts/app/WorkspaceCommunitiesLayout.tsx +3 -0
  249. package/src/layouts/app/WorkspaceNavbarLayout.tsx +3 -0
  250. package/src/layouts/app/WorkspaceSidebarLayout.tsx +98 -0
  251. package/src/layouts/app/WorkspaceSidebarTitleLayout.tsx +3 -0
  252. package/src/layouts/app/app-header-layout.tsx +45 -0
  253. package/src/layouts/app/app-sidebar-layout.tsx +56 -0
  254. package/src/layouts/app/layout-context.tsx +44 -0
  255. package/src/layouts/app/layout-types.ts +47 -0
  256. package/src/layouts/app/partials/Footer.tsx +35 -0
  257. package/src/layouts/app/partials/HeaderTopbar.tsx +96 -0
  258. package/src/layouts/app/partials/Navbar.tsx +85 -0
  259. package/src/layouts/app/partials/Toolbar.tsx +47 -0
  260. package/src/layouts/app-layout.tsx +29 -0
  261. package/src/layouts/auth/AuthBrandedLayout.tsx +58 -0
  262. package/src/layouts/auth/AuthCardLayout.tsx +31 -0
  263. package/src/layouts/auth/AuthCenteredLayout.tsx +41 -0
  264. package/src/layouts/auth/AuthClassicLayout.tsx +41 -0
  265. package/src/layouts/auth/AuthSimpleLayout.tsx +33 -0
  266. package/src/layouts/auth/AuthSplitLayout.tsx +89 -0
  267. package/src/layouts/web-app-layout.tsx +162 -0
  268. package/src/layouts/web-layout.tsx +23 -0
  269. package/src/lib/datetime.ts +188 -0
  270. package/src/lib/google-maps-loader.ts +99 -0
  271. package/src/lib/location.ts +127 -0
  272. package/src/lib/lucide-icon-map.ts +132 -0
  273. package/src/lib/map-markers.ts +124 -0
  274. package/src/lib/map-styles.ts +351 -0
  275. package/src/lib/utils.ts +11 -0
  276. package/src/platform/adapters/default.tsx +156 -0
  277. package/src/platform/adapters/inertia.tsx +88 -0
  278. package/src/platform/adapters/nextjs.ts +86 -0
  279. package/src/platform/context.tsx +106 -0
  280. package/src/platform/index.ts +27 -0
  281. package/src/platform/types.ts +105 -0
  282. package/src/styles/layouts/sidebar-fixed.css +161 -0
  283. package/src/styles/themes.css +583 -0
  284. package/src/types/assets.d.ts +5 -0
  285. package/src/types/auth.ts +25 -0
  286. package/src/types/global.d.ts +13 -0
  287. package/src/types/index.ts +9 -0
  288. package/src/types/navigation.ts +15 -0
  289. package/src/types/ui.ts +32 -0
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import { HeaderTopbar } from './partials/HeaderTopbar';
7
+ import { ScrollArea } from '../../components/ui/scroll-area';
8
+ import { Toolbar } from './partials/Toolbar';
9
+
10
+ /**
11
+ * CalendarSidebarLayout (layout-36)
12
+ * Sidebar with mini calendar + category list + main calendar area.
13
+ */
14
+ interface CalendarSidebarLayoutProps extends BaseAppLayoutProps {
15
+ miniCalendar?: ReactNode;
16
+ categories?: Array<{ name: string; color: string; visible?: boolean }>;
17
+ showToolbar?: boolean;
18
+ sidebarFooter?: ReactNode;
19
+ }
20
+
21
+ export function CalendarSidebarLayout({
22
+ children, currentUrl = '',
23
+ logo, logoHref = '/', appName, user,
24
+ title, breadcrumbs = [], toolbarActions,
25
+ settingsUrl, logoutUrl, onLogout, unreadCount = 0,
26
+ miniCalendar, categories = [], sidebarFooter,
27
+ showToolbar = true,
28
+ }: CalendarSidebarLayoutProps) {
29
+ return (
30
+ <div className="flex min-h-screen">
31
+ <aside className="w-64 shrink-0 flex flex-col border-e border-sidebar-border bg-sidebar">
32
+ <div className="flex items-center gap-2 px-4 h-[70px] border-b border-sidebar-border shrink-0">
33
+ {logo && <a href={logoHref}>{logo}</a>}
34
+ {appName && <span className="text-sm font-semibold text-sidebar-foreground">{appName}</span>}
35
+ </div>
36
+ <ScrollArea className="flex-1">
37
+ {miniCalendar && <div className="p-3 border-b border-sidebar-border">{miniCalendar}</div>}
38
+ {categories.length > 0 && (
39
+ <div className="p-3">
40
+ <p className="text-xs font-medium text-muted-foreground mb-2 px-1">Calendars</p>
41
+ <div className="space-y-1">
42
+ {categories.map((cat, i) => (
43
+ <label key={i} className="flex items-center gap-2 px-1 py-1 rounded text-sm cursor-pointer hover:bg-sidebar-accent">
44
+ <span className="size-3 rounded-full shrink-0" style={{ backgroundColor: cat.color }} />
45
+ <span className="text-sidebar-foreground">{cat.name}</span>
46
+ </label>
47
+ ))}
48
+ </div>
49
+ </div>
50
+ )}
51
+ </ScrollArea>
52
+ {sidebarFooter && <div className="p-3 border-t border-sidebar-border">{sidebarFooter}</div>}
53
+ </aside>
54
+
55
+ <div className="flex flex-col flex-1 min-w-0">
56
+ <header className="flex items-center h-[70px] border-b border-border bg-background px-4 shrink-0">
57
+ <div className="flex-1" />
58
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
59
+ </header>
60
+ <main className="flex-1 overflow-hidden flex flex-col" role="content">
61
+ {showToolbar && (title || breadcrumbs.length > 0 || toolbarActions) && (
62
+ <Toolbar title={title} breadcrumbs={breadcrumbs} actions={toolbarActions} currentUrl={currentUrl} />
63
+ )}
64
+ {children}
65
+ </main>
66
+ </div>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Workspace variant — shares same structure as WorkspaceSidebarLayout
3
+ export { WorkspaceSidebarLayout as CommunitiesNavbarLayout } from './WorkspaceSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Workspace variant — shares same structure as WorkspaceSidebarLayout
3
+ export { WorkspaceSidebarLayout as DualNavbarSidebarLayout } from './WorkspaceSidebarLayout';
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import { HeaderTopbar } from './partials/HeaderTopbar';
7
+ import { ScrollArea } from '../../components/ui/scroll-area';
8
+ import { AccordionMenu, AccordionMenuGroup, AccordionMenuItem } from '../../components/ui/accordion-menu';
9
+ import { Button } from '../../controls/Button';
10
+ import { Search } from 'lucide-react';
11
+
12
+ /**
13
+ * FocusSidebarLayout (layout-39)
14
+ * Todo/task-focused sidebar: focus card + tag list + task groups + toolbar search.
15
+ */
16
+ interface FocusSidebarLayoutProps extends BaseAppLayoutProps {
17
+ focusCard?: ReactNode;
18
+ tags?: Array<{ name: string; count?: number; href: string }>;
19
+ headerSearchPlaceholder?: string;
20
+ sidebarFooter?: ReactNode;
21
+ }
22
+
23
+ export function FocusSidebarLayout({
24
+ children, navItems = [], currentUrl = '',
25
+ logo, logoHref = '/', user,
26
+ settingsUrl, logoutUrl, onLogout, unreadCount = 0,
27
+ focusCard, tags = [], sidebarFooter,
28
+ }: FocusSidebarLayoutProps) {
29
+ return (
30
+ <div className="flex min-h-screen">
31
+ <aside className="w-64 shrink-0 flex flex-col border-e border-sidebar-border bg-sidebar">
32
+ <div className="flex items-center justify-between px-4 h-[70px] border-b border-sidebar-border shrink-0">
33
+ {logo && <a href={logoHref}>{logo}</a>}
34
+ <Button variant="ghost" size="sm" className="size-8 p-0 ms-auto">
35
+ <Search className="size-4" />
36
+ </Button>
37
+ </div>
38
+ <ScrollArea className="flex-1 py-2 px-2">
39
+ {focusCard && <div className="mb-3 p-2 rounded-lg bg-primary/10 border border-primary/20">{focusCard}</div>}
40
+ <AccordionMenu type="single" collapsible matchPath={(href) => !!href && currentUrl.startsWith(href)} selectedValue={currentUrl}>
41
+ <AccordionMenuGroup>
42
+ {navItems.map((item, i) => (
43
+ <AccordionMenuItem key={i} value={item.href ?? item.title} asChild>
44
+ <a href={item.href}>{item.title}</a>
45
+ </AccordionMenuItem>
46
+ ))}
47
+ </AccordionMenuGroup>
48
+ {tags.length > 0 && (
49
+ <AccordionMenuGroup>
50
+ <div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">Tags</div>
51
+ {tags.map((tag, i) => (
52
+ <AccordionMenuItem key={i} value={tag.href} asChild>
53
+ <a href={tag.href} className="flex justify-between">
54
+ <span>{tag.name}</span>
55
+ {tag.count !== undefined && <span className="text-muted-foreground text-xs">{tag.count}</span>}
56
+ </a>
57
+ </AccordionMenuItem>
58
+ ))}
59
+ </AccordionMenuGroup>
60
+ )}
61
+ </AccordionMenu>
62
+ </ScrollArea>
63
+ {sidebarFooter && <div className="p-3 border-t border-sidebar-border">{sidebarFooter}</div>}
64
+ </aside>
65
+
66
+ <div className="flex flex-col flex-1 min-w-0">
67
+ <header className="flex items-center h-[70px] border-b border-border bg-background px-4 shrink-0">
68
+ <div className="flex-1" />
69
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
70
+ </header>
71
+ <main className="flex-1" role="content">{children}</main>
72
+ </div>
73
+ </div>
74
+ );
75
+ }
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import { HeaderTopbar } from './partials/HeaderTopbar';
7
+ import { ScrollArea } from '../../components/ui/scroll-area';
8
+ import { AccordionMenu, AccordionMenuGroup, AccordionMenuItem } from '../../components/ui/accordion-menu';
9
+
10
+ /**
11
+ * MailLayout (layout-37)
12
+ * Three-column layout: sidebar nav + mail list panel + mail view panel.
13
+ * Used for email clients, CRM inbox, notification centre.
14
+ */
15
+ interface MailLayoutProps extends BaseAppLayoutProps {
16
+ listPanel?: ReactNode;
17
+ viewPanel?: ReactNode;
18
+ sidebarFooter?: ReactNode;
19
+ listPanelWidth?: string;
20
+ }
21
+
22
+ export function MailLayout({
23
+ children, navItems = [], currentUrl = '',
24
+ logo, logoHref = '/', user,
25
+ settingsUrl, logoutUrl, onLogout, unreadCount = 0,
26
+ listPanel, viewPanel, sidebarFooter,
27
+ listPanelWidth = 'w-80',
28
+ }: MailLayoutProps) {
29
+ return (
30
+ <div className="flex min-h-screen">
31
+ {/* Sidebar */}
32
+ <aside className="w-60 shrink-0 flex flex-col border-e border-sidebar-border bg-sidebar">
33
+ <div className="flex items-center gap-2 px-4 h-[70px] border-b border-sidebar-border shrink-0">
34
+ {logo && <a href={logoHref}>{logo}</a>}
35
+ </div>
36
+ <ScrollArea className="flex-1 py-2 px-2">
37
+ <AccordionMenu type="single" collapsible matchPath={(href) => !!href && currentUrl.startsWith(href)} selectedValue={currentUrl}>
38
+ <AccordionMenuGroup>
39
+ {navItems.map((item, i) => (
40
+ <AccordionMenuItem key={i} value={item.href ?? item.title} asChild>
41
+ <a href={item.href}>{item.title}</a>
42
+ </AccordionMenuItem>
43
+ ))}
44
+ </AccordionMenuGroup>
45
+ </AccordionMenu>
46
+ </ScrollArea>
47
+ {sidebarFooter && <div className="p-3 border-t border-sidebar-border">{sidebarFooter}</div>}
48
+ </aside>
49
+
50
+ {/* List panel */}
51
+ {listPanel && (
52
+ <aside className={cn('shrink-0 border-e border-border flex flex-col', listPanelWidth)}>
53
+ {listPanel}
54
+ </aside>
55
+ )}
56
+
57
+ {/* View panel / main */}
58
+ <div className="flex flex-col flex-1 min-w-0">
59
+ <header className="flex items-center h-[70px] border-b border-border bg-background px-4 shrink-0">
60
+ <div className="flex-1" />
61
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
62
+ </header>
63
+ <main className="flex-1 overflow-auto" role="content">
64
+ {viewPanel ?? children}
65
+ </main>
66
+ </div>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // layout-7: header with mega-menu, no sidebar (same as MegaMenuLayout)
3
+ export { MegaMenuLayout as MegaMenuHeaderLayout } from './MegaMenuLayout';
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import type { NavItem } from '../../types/navigation';
7
+ import { Toolbar } from './partials/Toolbar';
8
+ import { Footer } from './partials/Footer';
9
+ import { HeaderTopbar } from './partials/HeaderTopbar';
10
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../components/ui/dropdown-menu';
11
+ import { ChevronDown } from 'lucide-react';
12
+
13
+ /**
14
+ * MegaMenuLayout (demo7)
15
+ * Fixed header with integrated horizontal mega menu. No sidebar.
16
+ */
17
+ interface MegaMenuLayoutProps extends BaseAppLayoutProps {
18
+ showToolbar?: boolean;
19
+ }
20
+
21
+ export function MegaMenuLayout({
22
+ children, navItems = [], currentUrl = '',
23
+ logo, logoHref = '/', appName, user,
24
+ title, breadcrumbs = [], toolbarActions,
25
+ onLogout, settingsUrl, logoutUrl, unreadCount = 0,
26
+ footerLinks = [], copyright, showToolbar = true,
27
+ }: MegaMenuLayoutProps) {
28
+ function isActive(url: string) { return !!url && url !== '#' && currentUrl.startsWith(url); }
29
+ function hasActiveChild(items: NavItem[]): boolean { return items.some((i) => isActive(i.href ?? '') || (i.items ? hasActiveChild(i.items) : false)); }
30
+
31
+ return (
32
+ <div className="flex flex-col min-h-screen">
33
+ <header className="sticky top-0 z-20 flex items-center h-[70px] border-b border-border bg-background/95 backdrop-blur-sm px-4 shrink-0">
34
+ <div className="container mx-auto flex items-center gap-6">
35
+ {logo && <a href={logoHref} className="shrink-0">{logo}</a>}
36
+ {appName && <span className="text-sm font-medium hidden md:inline">{appName}</span>}
37
+
38
+ {/* Mega menu inline */}
39
+ <nav className="flex items-stretch flex-1 overflow-x-auto">
40
+ {navItems.map((item, i) => {
41
+ const active = isActive(item.href ?? '') || (item.items ? hasActiveChild(item.items) : false);
42
+ if (item.items && item.items.length > 0) {
43
+ return (
44
+ <DropdownMenu key={i}>
45
+ <DropdownMenuTrigger asChild>
46
+ <button className={cn('flex items-center gap-1.5 px-3 py-3.5 text-sm text-nowrap border-b-2 transition-colors hover:text-mono bg-transparent', active ? 'text-mono border-mono' : 'text-secondary-foreground border-transparent')}>
47
+ {item.title} <ChevronDown className="size-3.5" />
48
+ </button>
49
+ </DropdownMenuTrigger>
50
+ <DropdownMenuContent className="min-w-[200px]">
51
+ {item.items.map((child, ci) => (
52
+ <DropdownMenuItem key={ci} asChild>
53
+ <a href={child.href} className={cn(isActive(child.href ?? '') && 'bg-accent')}>{child.title}</a>
54
+ </DropdownMenuItem>
55
+ ))}
56
+ </DropdownMenuContent>
57
+ </DropdownMenu>
58
+ );
59
+ }
60
+ return (
61
+ <a key={i} href={item.href} className={cn('flex items-center px-3 py-3.5 text-sm text-nowrap border-b-2 transition-colors hover:text-mono', active ? 'text-mono border-mono' : 'text-secondary-foreground border-transparent')}>
62
+ {item.title}
63
+ </a>
64
+ );
65
+ })}
66
+ </nav>
67
+
68
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
69
+ </div>
70
+ </header>
71
+
72
+ <main className="flex-1" role="content">
73
+ {showToolbar && (title || breadcrumbs.length > 0 || toolbarActions) && (
74
+ <Toolbar title={title} breadcrumbs={breadcrumbs} actions={toolbarActions} currentUrl={currentUrl} />
75
+ )}
76
+ {children}
77
+ </main>
78
+ <Footer links={footerLinks} copyright={copyright} />
79
+ </div>
80
+ );
81
+ }
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import type { BaseAppLayoutProps } from './layout-types';
4
+ import type { NavItem } from '../../types/navigation';
5
+ import { Navbar } from './partials/Navbar';
6
+ import { Toolbar } from './partials/Toolbar';
7
+ import { Footer } from './partials/Footer';
8
+ import { HeaderTopbar } from './partials/HeaderTopbar';
9
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../components/ui/dropdown-menu';
10
+ import { ChevronDown, Search } from 'lucide-react';
11
+ import { cn } from '../../lib/utils';
12
+ import { Button } from '../../controls/Button';
13
+
14
+ /**
15
+ * MegaMenuNavbarLayout (demo9)
16
+ * Header with logo, search, and mega-menu dropdown nav + horizontal navbar below.
17
+ */
18
+ interface MegaMenuNavbarLayoutProps extends BaseAppLayoutProps {
19
+ showToolbar?: boolean;
20
+ onSearchOpen?: () => void;
21
+ }
22
+
23
+ export function MegaMenuNavbarLayout({
24
+ children, navItems = [], currentUrl = '',
25
+ logo, logoHref = '/', appName, user,
26
+ title, breadcrumbs = [], toolbarActions,
27
+ onLogout, settingsUrl, logoutUrl, unreadCount = 0,
28
+ footerLinks = [], copyright, showToolbar = true, onSearchOpen,
29
+ }: MegaMenuNavbarLayoutProps) {
30
+ function isActive(url: string) { return !!url && url !== '#' && currentUrl.startsWith(url); }
31
+ function hasActiveChild(items: NavItem[]): boolean { return items.some((i) => isActive(i.href ?? '') || (i.items ? hasActiveChild(i.items) : false)); }
32
+
33
+ // Split navItems: top-level as mega menu triggers, sub-items go in dropdown
34
+ const megaItems = navItems;
35
+
36
+ return (
37
+ <div className="flex flex-col min-h-screen">
38
+ {/* Header with mega menu */}
39
+ <header className="sticky top-0 z-20 flex items-center h-[70px] border-b border-border bg-background/95 backdrop-blur-sm px-4 shrink-0">
40
+ <div className="container mx-auto flex items-center gap-4">
41
+ {logo && <a href={logoHref} className="shrink-0">{logo}</a>}
42
+ {appName && <span className="text-sm font-medium hidden md:inline">{appName}</span>}
43
+
44
+ {/* Mega menu */}
45
+ <nav className="flex items-stretch flex-1 overflow-x-auto">
46
+ {megaItems.map((item, i) => {
47
+ const active = isActive(item.href ?? '') || (item.items ? hasActiveChild(item.items) : false);
48
+ if (item.items && item.items.length > 0) {
49
+ return (
50
+ <DropdownMenu key={i}>
51
+ <DropdownMenuTrigger asChild>
52
+ <button className={cn('flex items-center gap-1 px-3 py-3.5 text-sm border-b-2 transition-colors hover:text-mono bg-transparent text-nowrap', active ? 'text-mono border-mono' : 'text-secondary-foreground border-transparent')}>
53
+ {item.title} <ChevronDown className="size-3" />
54
+ </button>
55
+ </DropdownMenuTrigger>
56
+ <DropdownMenuContent className="min-w-[180px]">
57
+ {item.items.map((c, ci) => <DropdownMenuItem key={ci} asChild><a href={c.href}>{c.title}</a></DropdownMenuItem>)}
58
+ </DropdownMenuContent>
59
+ </DropdownMenu>
60
+ );
61
+ }
62
+ return (
63
+ <a key={i} href={item.href} className={cn('flex items-center px-3 py-3.5 text-sm border-b-2 transition-colors hover:text-mono text-nowrap', active ? 'text-mono border-mono' : 'text-secondary-foreground border-transparent')}>
64
+ {item.title}
65
+ </a>
66
+ );
67
+ })}
68
+ </nav>
69
+
70
+ {onSearchOpen && (
71
+ <Button variant="ghost" size="sm" className="size-9 p-0 rounded-full" onClick={onSearchOpen}>
72
+ <Search className="size-4" />
73
+ </Button>
74
+ )}
75
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
76
+ </div>
77
+ </header>
78
+
79
+ <main className="flex-1" role="content">
80
+ {showToolbar && (title || breadcrumbs.length > 0 || toolbarActions) && (
81
+ <Toolbar title={title} breadcrumbs={breadcrumbs} actions={toolbarActions} currentUrl={currentUrl} />
82
+ )}
83
+ {children}
84
+ </main>
85
+ <Footer links={footerLinks} copyright={copyright} />
86
+ </div>
87
+ );
88
+ }
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // layout-9: mega-menu header with search + navbar (same as MegaMenuNavbarLayout)
3
+ export { MegaMenuNavbarLayout as MegaMenuSearchNavbarLayout } from './MegaMenuNavbarLayout';
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import { Navbar } from './partials/Navbar';
7
+ import { Toolbar } from './partials/Toolbar';
8
+ import { Footer } from './partials/Footer';
9
+ import { HeaderTopbar } from './partials/HeaderTopbar';
10
+ import { AccordionMenu, AccordionMenuGroup, AccordionMenuItem, AccordionMenuSub, AccordionMenuSubContent, AccordionMenuSubTrigger } from '../../components/ui/accordion-menu';
11
+ import { ScrollArea } from '../../components/ui/scroll-area';
12
+ import { Button } from '../../controls/Button';
13
+ import { Menu, X } from 'lucide-react';
14
+ import type { NavItem } from '../../types/navigation';
15
+
16
+ /**
17
+ * NavbarCollapsibleLayout (demo3)
18
+ * Header (logo + topbar) + horizontal navbar + optional collapsible sidebar.
19
+ * Sidebar contains section-specific sub-navigation driven by the active navbar item.
20
+ */
21
+ interface NavbarCollapsibleLayoutProps extends BaseAppLayoutProps {
22
+ sidebarItems?: NavItem[];
23
+ showToolbar?: boolean;
24
+ }
25
+
26
+ export function NavbarCollapsibleLayout({
27
+ children, navItems = [], sidebarItems = [], currentUrl = '',
28
+ logo, logoHref = '/', appName, user,
29
+ title, breadcrumbs = [], toolbarActions,
30
+ onLogout, settingsUrl, logoutUrl, unreadCount = 0,
31
+ footerLinks = [], copyright, showToolbar = true,
32
+ defaultSidebarCollapsed = false,
33
+ }: NavbarCollapsibleLayoutProps) {
34
+ const [sidebarOpen, setSidebarOpen] = useState(!defaultSidebarCollapsed);
35
+
36
+ return (
37
+ <div className="flex flex-col min-h-screen">
38
+ {/* Header */}
39
+ <header className="flex items-center h-[70px] border-b border-border bg-background px-4 shrink-0">
40
+ <div className="container mx-auto flex justify-between items-center gap-4">
41
+ <div className="flex items-center gap-3">
42
+ {logo && <a href={logoHref}>{logo}</a>}
43
+ {appName && <span className="text-sm font-medium hidden md:inline">{appName}</span>}
44
+ </div>
45
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
46
+ </div>
47
+ </header>
48
+
49
+ {/* Navbar */}
50
+ <Navbar navItems={navItems} currentUrl={currentUrl} />
51
+
52
+ {/* Body: optional sidebar + content */}
53
+ <div className="flex flex-1">
54
+ {sidebarItems.length > 0 && (
55
+ <>
56
+ <aside className={cn('w-64 shrink-0 border-e border-border bg-sidebar hidden lg:block', !sidebarOpen && 'hidden')}>
57
+ <ScrollArea className="h-full py-3 px-2">
58
+ <AccordionMenu type="single" collapsible matchPath={(href) => !!href && currentUrl.startsWith(href)} selectedValue={currentUrl}>
59
+ <AccordionMenuGroup>
60
+ {sidebarItems.map((item, i) => (
61
+ item.items ? (
62
+ <AccordionMenuSub key={i} value={item.title}>
63
+ <AccordionMenuSubTrigger>{item.title}</AccordionMenuSubTrigger>
64
+ <AccordionMenuSubContent type="single" collapsible parentValue={item.title}>
65
+ {item.items.map((c, ci) => <AccordionMenuItem key={ci} value={c.href ?? c.title} asChild><a href={c.href}>{c.title}</a></AccordionMenuItem>)}
66
+ </AccordionMenuSubContent>
67
+ </AccordionMenuSub>
68
+ ) : (
69
+ <AccordionMenuItem key={i} value={item.href ?? item.title} asChild><a href={item.href}>{item.title}</a></AccordionMenuItem>
70
+ )
71
+ ))}
72
+ </AccordionMenuGroup>
73
+ </AccordionMenu>
74
+ </ScrollArea>
75
+ </aside>
76
+ </>
77
+ )}
78
+ <main className="flex-1 min-w-0" role="content">
79
+ {showToolbar && (title || breadcrumbs.length > 0 || toolbarActions) && (
80
+ <Toolbar title={title} breadcrumbs={breadcrumbs} actions={toolbarActions} currentUrl={currentUrl} />
81
+ )}
82
+ {children}
83
+ </main>
84
+ </div>
85
+ <Footer links={footerLinks} copyright={copyright} />
86
+ </div>
87
+ );
88
+ }
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // layout-3: same as NavbarCollapsibleLayout with navbar links
3
+ export { NavbarCollapsibleLayout as NavbarCollapsibleLinksLayout } from './NavbarCollapsibleLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Navbar + sidebar variant
3
+ export { NavbarSidebarLayout as NavbarMinimalLayout } from './NavbarSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Workspace variant — shares same structure as WorkspaceSidebarLayout
3
+ export { WorkspaceSidebarLayout as NavbarMinimalSidebarLayout } from './WorkspaceSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // layout-5: navbar + sidebar with dashboard/default tabs (same structure as NavbarSidebarLayout)
3
+ export { NavbarSidebarLayout as NavbarSidebarDashboardLayout } from './NavbarSidebarLayout';
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import type { BaseAppLayoutProps } from './layout-types';
6
+ import type { NavItem } from '../../types/navigation';
7
+ import { Navbar } from './partials/Navbar';
8
+ import { Toolbar } from './partials/Toolbar';
9
+ import { Footer } from './partials/Footer';
10
+ import { HeaderTopbar } from './partials/HeaderTopbar';
11
+ import { AccordionMenu, AccordionMenuGroup, AccordionMenuItem, AccordionMenuSub, AccordionMenuSubContent, AccordionMenuSubTrigger } from '../../components/ui/accordion-menu';
12
+ import { ScrollArea } from '../../components/ui/scroll-area';
13
+ import { Button } from '../../controls/Button';
14
+ import { Menu } from 'lucide-react';
15
+
16
+ /**
17
+ * NavbarSidebarLayout (demo5)
18
+ * Header (logo + topbar) + horizontal navbar + collapsible sidebar.
19
+ * Sidebar nav changes based on the active navbar section.
20
+ */
21
+ interface NavbarSidebarLayoutProps extends BaseAppLayoutProps {
22
+ sidebarItems?: NavItem[];
23
+ showToolbar?: boolean;
24
+ }
25
+
26
+ export function NavbarSidebarLayout({
27
+ children, navItems = [], sidebarItems = [], currentUrl = '',
28
+ logo, logoHref = '/', appName, user,
29
+ title, breadcrumbs = [], toolbarActions,
30
+ onLogout, settingsUrl, logoutUrl, unreadCount = 0,
31
+ footerLinks = [], copyright, showToolbar = true,
32
+ defaultSidebarCollapsed = false,
33
+ }: NavbarSidebarLayoutProps) {
34
+ const [collapsed, setCollapsed] = useState(defaultSidebarCollapsed);
35
+ const effectiveSidebarItems = sidebarItems.length > 0 ? sidebarItems : [];
36
+
37
+ return (
38
+ <div className="flex flex-col min-h-screen">
39
+ {/* Header */}
40
+ <header className="flex items-center h-[70px] border-b border-border bg-background px-4 shrink-0">
41
+ <div className="container mx-auto flex justify-between items-center gap-4">
42
+ <div className="flex items-center gap-3">
43
+ {logo && <a href={logoHref}>{logo}</a>}
44
+ {appName && <span className="text-sm font-medium hidden md:inline">{appName}</span>}
45
+ </div>
46
+ <HeaderTopbar user={user} unreadCount={unreadCount} settingsUrl={settingsUrl} logoutUrl={logoutUrl} onLogout={onLogout} />
47
+ </div>
48
+ </header>
49
+
50
+ {/* Navbar */}
51
+ <Navbar navItems={navItems} currentUrl={currentUrl} />
52
+
53
+ {/* Body */}
54
+ <div className="flex flex-1">
55
+ {effectiveSidebarItems.length > 0 && (
56
+ <aside className={cn('shrink-0 border-e border-sidebar-border bg-sidebar transition-all', collapsed ? 'w-0 overflow-hidden' : 'w-60')}>
57
+ <div className="flex items-center justify-between px-3 py-2 border-b border-sidebar-border">
58
+ <Button variant="ghost" size="sm" className="size-8 p-0" onClick={() => setCollapsed((c) => !c)}>
59
+ <Menu className="size-4" />
60
+ </Button>
61
+ </div>
62
+ <ScrollArea className="py-2 px-2">
63
+ <AccordionMenu type="single" collapsible matchPath={(href) => !!href && currentUrl.startsWith(href)} selectedValue={currentUrl}>
64
+ <AccordionMenuGroup>
65
+ {effectiveSidebarItems.map((item, i) => (
66
+ item.items ? (
67
+ <AccordionMenuSub key={i} value={item.title}>
68
+ <AccordionMenuSubTrigger>{item.title}</AccordionMenuSubTrigger>
69
+ <AccordionMenuSubContent type="single" collapsible parentValue={item.title}>
70
+ {item.items.map((c, ci) => <AccordionMenuItem key={ci} value={c.href ?? c.title} asChild><a href={c.href}>{c.title}</a></AccordionMenuItem>)}
71
+ </AccordionMenuSubContent>
72
+ </AccordionMenuSub>
73
+ ) : (
74
+ <AccordionMenuItem key={i} value={item.href ?? item.title} asChild><a href={item.href}>{item.title}</a></AccordionMenuItem>
75
+ )
76
+ ))}
77
+ </AccordionMenuGroup>
78
+ </AccordionMenu>
79
+ </ScrollArea>
80
+ </aside>
81
+ )}
82
+ <main className="flex-1 min-w-0" role="content">
83
+ {showToolbar && (title || breadcrumbs.length > 0 || toolbarActions) && (
84
+ <Toolbar title={title} breadcrumbs={breadcrumbs} actions={toolbarActions} currentUrl={currentUrl} />
85
+ )}
86
+ {children}
87
+ </main>
88
+ </div>
89
+ <Footer links={footerLinks} copyright={copyright} />
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Navbar + sidebar variant
3
+ export { NavbarSidebarLayout as NavbarSimpleSidebarLayout } from './NavbarSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Navbar + sidebar variant
3
+ export { NavbarSidebarLayout as NavbarTitledSidebarLayout } from './NavbarSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Panel sidebar variant
3
+ export { SidebarMinimalLayout as PanelSidebarLayout } from './SidebarMinimalLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Workspace variant — shares same structure as WorkspaceSidebarLayout
3
+ export { WorkspaceSidebarLayout as SearchNavbarSidebarLayout } from './WorkspaceSidebarLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Panel sidebar variant
3
+ export { SidebarMinimalLayout as SidebarBreadcrumbLayout } from './SidebarMinimalLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // layout-8: clean single sidebar with header/footer (same as SidebarMinimalLayout)
3
+ export { SidebarMinimalLayout as SidebarCleanLayout } from './SidebarMinimalLayout';
@@ -0,0 +1,3 @@
1
+ 'use client';
2
+ // Workspace-community sidebar variant — wraps WorkspaceSidebarLayout
3
+ export { WorkspaceSidebarLayout as SidebarCommunitiesLayout } from './WorkspaceSidebarLayout';