@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,85 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import type { NavItem } from '../../../types/navigation';
5
+ import { cn } from '../../../lib/utils';
6
+ import { ChevronDown } from 'lucide-react';
7
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../../../components/ui/dropdown-menu';
8
+
9
+ interface NavbarProps {
10
+ navItems?: NavItem[];
11
+ currentUrl?: string;
12
+ rightSlot?: ReactNode;
13
+ className?: string;
14
+ }
15
+
16
+ function isActive(item: NavItem, currentUrl: string): boolean {
17
+ if (!item.href || item.href === '#') return false;
18
+ return currentUrl.startsWith(item.href);
19
+ }
20
+
21
+ function hasActiveChild(items: NavItem[], currentUrl: string): boolean {
22
+ return items.some((item) => isActive(item, currentUrl) || (item.items && hasActiveChild(item.items, currentUrl)));
23
+ }
24
+
25
+ export function Navbar({ navItems = [], currentUrl = '', rightSlot, className }: NavbarProps) {
26
+ return (
27
+ <div className={cn('border-b border-border pb-5 lg:pb-0 mb-5 lg:mb-10', className)}>
28
+ <div className="container mx-auto px-4 flex flex-wrap justify-between items-center gap-2">
29
+ <nav className="flex items-stretch overflow-x-auto">
30
+ {navItems.map((item, index) => {
31
+ const active = isActive(item, currentUrl);
32
+ const childActive = item.items ? hasActiveChild(item.items, currentUrl) : false;
33
+ const highlighted = active || childActive;
34
+
35
+ if (item.items && item.items.length > 0) {
36
+ return (
37
+ <DropdownMenu key={index}>
38
+ <DropdownMenuTrigger asChild>
39
+ <button
40
+ className={cn(
41
+ 'flex items-center gap-1.5 px-3 py-3.5 text-sm text-nowrap border-b-2 transition-colors',
42
+ 'hover:text-mono bg-transparent border-transparent',
43
+ highlighted ? 'text-mono border-mono' : 'text-secondary-foreground',
44
+ )}
45
+ >
46
+ {item.title}
47
+ <ChevronDown className="size-3.5" />
48
+ </button>
49
+ </DropdownMenuTrigger>
50
+ <DropdownMenuContent sideOffset={0} className="min-w-[175px]">
51
+ {item.items.map((child, ci) => (
52
+ <DropdownMenuItem key={ci} asChild>
53
+ <a
54
+ href={child.href}
55
+ className={cn(isActive(child, currentUrl) && 'bg-accent')}
56
+ >
57
+ {child.title}
58
+ </a>
59
+ </DropdownMenuItem>
60
+ ))}
61
+ </DropdownMenuContent>
62
+ </DropdownMenu>
63
+ );
64
+ }
65
+
66
+ return (
67
+ <a
68
+ key={index}
69
+ href={item.href}
70
+ className={cn(
71
+ 'flex items-center px-3 py-3.5 text-sm text-nowrap border-b-2 transition-colors',
72
+ 'hover:text-mono border-transparent',
73
+ highlighted ? 'text-mono border-mono' : 'text-secondary-foreground',
74
+ )}
75
+ >
76
+ {item.title}
77
+ </a>
78
+ );
79
+ })}
80
+ </nav>
81
+ {rightSlot && <div className="flex items-center gap-4">{rightSlot}</div>}
82
+ </div>
83
+ </div>
84
+ );
85
+ }
@@ -0,0 +1,47 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { BreadcrumbItem } from '../../../types/navigation';
3
+ import { cn } from '../../../lib/utils';
4
+ import { ChevronRight } from 'lucide-react';
5
+
6
+ interface ToolbarProps {
7
+ title?: string;
8
+ breadcrumbs?: BreadcrumbItem[];
9
+ actions?: ReactNode;
10
+ className?: string;
11
+ currentUrl?: string;
12
+ }
13
+
14
+ export function Toolbar({ title, breadcrumbs = [], actions, className, currentUrl }: ToolbarProps) {
15
+ return (
16
+ <div className={cn('mb-5 lg:mb-10', className)}>
17
+ <div className="container mx-auto px-4 flex items-center justify-between flex-wrap gap-5">
18
+ <div className="flex items-center flex-wrap gap-1 lg:gap-5">
19
+ {title && <h1 className="font-medium text-lg text-mono">{title}</h1>}
20
+ {breadcrumbs.length > 0 && (
21
+ <nav className="flex items-center gap-1 text-xs lg:text-sm font-medium">
22
+ {breadcrumbs.map((item, index) => {
23
+ const isLast = index === breadcrumbs.length - 1;
24
+ const isActive = currentUrl ? currentUrl === item.href : isLast;
25
+ return (
26
+ <span key={index} className="flex items-center gap-1">
27
+ {item.href && !isLast ? (
28
+ <a href={item.href} className={cn('hover:text-primary transition-colors', isActive ? 'text-mono' : 'text-muted-foreground')}>
29
+ {item.title}
30
+ </a>
31
+ ) : (
32
+ <span className={isLast ? 'text-mono' : 'text-muted-foreground'}>
33
+ {item.title}
34
+ </span>
35
+ )}
36
+ {!isLast && <ChevronRight className="size-3.5 text-muted-foreground" />}
37
+ </span>
38
+ );
39
+ })}
40
+ </nav>
41
+ )}
42
+ </div>
43
+ {actions && <div className="flex items-center gap-2.5">{actions}</div>}
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * AppLayout — tenant operational portal layout (sidebar variant).
3
+ *
4
+ * This is the thin dispatcher used by Inertia page layouts in the core app.
5
+ * It delegates to AppSidebarLayout which wraps AppTopNav.
6
+ *
7
+ * NOTE: AppTopNav is an app-level component — it uses wayfinder routes and
8
+ * app-specific navigation that cannot be defined in this package. The consuming
9
+ * app (core/) provides the AppTopNav via app-sidebar-layout.tsx; this file
10
+ * exists here so the package exports a consistent AppLayout type.
11
+ *
12
+ * Use in app layouts:
13
+ * import AppLayoutTemplate from '@trackany-device/components/layouts/app/app-sidebar-layout';
14
+ */
15
+ export type AppLayoutProps = {
16
+ breadcrumbs?: Array<{ title: string; href: string }>;
17
+ children: React.ReactNode;
18
+ };
19
+
20
+ /**
21
+ * Placeholder re-exported for type compatibility. The real implementation is
22
+ * in core/resources/js/layouts/app-layout.tsx which uses the app's own
23
+ * app-sidebar-layout.tsx (which knows about AppTopNav).
24
+ */
25
+ export default function AppLayout({
26
+ children,
27
+ }: AppLayoutProps) {
28
+ return <>{children}</>;
29
+ }
@@ -0,0 +1,58 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Card, CardContent } from '../../components/ui/card';
3
+
4
+ interface AuthBrandedLayoutProps {
5
+ children: ReactNode;
6
+ logo?: ReactNode;
7
+ brandTitle?: string;
8
+ brandSubtitle?: string;
9
+ brandImage?: string;
10
+ brandImageDark?: string;
11
+ logoHref?: string;
12
+ }
13
+
14
+ export default function AuthBrandedLayout({
15
+ children,
16
+ logo,
17
+ brandTitle = 'Secure Dashboard Access',
18
+ brandSubtitle = 'A robust authentication gateway ensuring secure, efficient access.',
19
+ brandImage,
20
+ brandImageDark,
21
+ logoHref = '/',
22
+ }: AuthBrandedLayoutProps) {
23
+ return (
24
+ <>
25
+ {(brandImage || brandImageDark) && (
26
+ <style>{`
27
+ .auth-branded-bg { ${brandImage ? `background-image: url('${brandImage}');` : ''} }
28
+ .dark .auth-branded-bg { ${brandImageDark ? `background-image: url('${brandImageDark}');` : ''} }
29
+ `}</style>
30
+ )}
31
+ <div className="grid lg:grid-cols-2 grow min-h-screen">
32
+ {/* Form panel */}
33
+ <div className="flex justify-center items-center p-8 lg:p-10 order-2 lg:order-1">
34
+ <Card className="w-full max-w-[400px]">
35
+ <CardContent className="p-6">
36
+ {children}
37
+ </CardContent>
38
+ </Card>
39
+ </div>
40
+
41
+ {/* Brand panel */}
42
+ <div className={`lg:rounded-xl lg:border lg:border-border lg:m-5 order-1 lg:order-2 bg-top xxl:bg-center xl:bg-cover bg-no-repeat auth-branded-bg bg-muted`}>
43
+ <div className="flex flex-col p-8 lg:p-16 gap-4">
44
+ {logo && (
45
+ <a href={logoHref} className="shrink-0">
46
+ {logo}
47
+ </a>
48
+ )}
49
+ <div className="flex flex-col gap-3">
50
+ <h3 className="text-2xl font-semibold text-mono">{brandTitle}</h3>
51
+ <p className="text-base font-medium text-secondary-foreground">{brandSubtitle}</p>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </>
57
+ );
58
+ }
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { AuthLayoutProps } from '../../types';
3
+ import { Card } from '../../components/Card';
4
+
5
+ interface Props extends AuthLayoutProps {
6
+ logo?: ReactNode;
7
+ homeUrl?: string;
8
+ }
9
+
10
+ export default function AuthCardLayout({ children, title, description, logo, homeUrl = '/' }: Props) {
11
+ return (
12
+ <div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
13
+ <div className="flex w-full max-w-md flex-col gap-6">
14
+ {logo && (
15
+ <a href={homeUrl} className="self-center">
16
+ {logo}
17
+ </a>
18
+ )}
19
+ <Card>
20
+ <div className="px-10 pt-8 pb-0 text-center">
21
+ <h2 className="text-xl font-semibold">{title}</h2>
22
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
23
+ </div>
24
+ <div className="px-10 py-8">
25
+ {children}
26
+ </div>
27
+ </Card>
28
+ </div>
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { AuthLayoutProps } from '../../types';
3
+
4
+ interface Props extends AuthLayoutProps {
5
+ logo?: ReactNode;
6
+ appName?: string;
7
+ homeUrl?: string;
8
+ bannerUrl?: string;
9
+ }
10
+
11
+ export default function AuthCenteredLayout({ children, title, description, logo, appName, homeUrl = '/', bannerUrl = '/banner.png' }: Props) {
12
+ return (
13
+ <div className="relative flex min-h-svh flex-col items-center justify-center bg-background bg-cover bg-center bg-no-repeat p-6"
14
+ style={{ backgroundImage: `url(${bannerUrl})` }}>
15
+ <div aria-hidden className="absolute inset-0 bg-gradient-to-br from-background/95 via-background/85 to-primary-subtle/60" />
16
+
17
+ <div className="relative z-10 w-full max-w-md">
18
+ <a href={homeUrl} className="mb-6 flex flex-col items-center gap-2 text-foreground">
19
+ {logo}
20
+ {appName && <span className="font-display text-sm font-semibold">{appName}</span>}
21
+ </a>
22
+
23
+ <div className="rounded-2xl border bg-card/95 p-8 shadow-lg backdrop-blur">
24
+ <div className="mb-6 text-center">
25
+ <h1 className="font-display text-xl font-semibold text-foreground">{title}</h1>
26
+ {description && <p className="mt-1 text-sm text-muted-foreground">{description}</p>}
27
+ </div>
28
+ {children}
29
+ </div>
30
+
31
+ <p className="mt-4 text-center text-xs text-muted-foreground">
32
+ <a href="/terms" className="hover:text-foreground hover:underline">Terms</a>
33
+ {' · '}
34
+ <a href="/privacy" className="hover:text-foreground hover:underline">Privacy</a>
35
+ {' · '}
36
+ <a href="/cookies" className="hover:text-foreground hover:underline">Cookies</a>
37
+ </p>
38
+ </div>
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Card, CardContent } from '../../components/ui/card';
3
+
4
+ interface AuthClassicLayoutProps {
5
+ children: ReactNode;
6
+ logo?: ReactNode;
7
+ logoHref?: string;
8
+ backgroundImage?: string;
9
+ backgroundImageDark?: string;
10
+ }
11
+
12
+ export default function AuthClassicLayout({
13
+ children,
14
+ logo,
15
+ logoHref = '/',
16
+ backgroundImage,
17
+ backgroundImageDark,
18
+ }: AuthClassicLayoutProps) {
19
+ return (
20
+ <>
21
+ {(backgroundImage || backgroundImageDark) && (
22
+ <style>{`
23
+ .auth-classic-bg { ${backgroundImage ? `background-image: url('${backgroundImage}');` : ''} }
24
+ .dark .auth-classic-bg { ${backgroundImageDark ? `background-image: url('${backgroundImageDark}');` : ''} }
25
+ `}</style>
26
+ )}
27
+ <div className="flex flex-col items-center justify-center grow min-h-screen bg-center bg-no-repeat auth-classic-bg">
28
+ {logo && (
29
+ <div className="m-5">
30
+ <a href={logoHref}>{logo}</a>
31
+ </div>
32
+ )}
33
+ <Card className="w-full max-w-[400px]">
34
+ <CardContent className="p-6">
35
+ {children}
36
+ </CardContent>
37
+ </Card>
38
+ </div>
39
+ </>
40
+ );
41
+ }
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { AuthLayoutProps } from '../../types';
3
+
4
+ interface Props extends AuthLayoutProps {
5
+ logo?: ReactNode;
6
+ homeUrl?: string;
7
+ }
8
+
9
+ export default function AuthSimpleLayout({ children, title, description, logo, homeUrl = '/' }: Props) {
10
+ return (
11
+ <div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-background p-6 md:p-10">
12
+ <div className="w-full max-w-sm">
13
+ <div className="flex flex-col gap-8">
14
+ <div className="flex flex-col items-center gap-4">
15
+ {logo && (
16
+ <a href={homeUrl} className="flex flex-col items-center gap-2 font-medium">
17
+ <div className="mb-1 flex h-9 w-9 items-center justify-center rounded-md">
18
+ {logo}
19
+ </div>
20
+ <span className="sr-only">{title}</span>
21
+ </a>
22
+ )}
23
+ <div className="space-y-2 text-center">
24
+ <h1 className="text-xl font-medium">{title}</h1>
25
+ <p className="text-center text-sm text-muted-foreground">{description}</p>
26
+ </div>
27
+ </div>
28
+ {children}
29
+ </div>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,89 @@
1
+ import { Activity, MapPin, Radio, Shield } from 'lucide-react';
2
+ import type { ReactNode } from 'react';
3
+ import type { AuthLayoutProps } from '../../types';
4
+
5
+ const FEATURES = [
6
+ { icon: MapPin, label: 'Real-time GPS tracking', sub: 'Live positions on interactive maps' },
7
+ { icon: Shield, label: 'Beat zone enforcement', sub: 'Instant geo-fence breach alerts' },
8
+ { icon: Radio, label: 'Multi-device support', sub: 'P901 · GF-07 · JT808 and more' },
9
+ { icon: Activity, label: 'Incident management', sub: 'SOS, offline, and violation tracking' },
10
+ ];
11
+
12
+ interface Props extends AuthLayoutProps {
13
+ logo?: ReactNode;
14
+ appName?: string;
15
+ }
16
+
17
+ export default function AuthSplitLayout({ children, title, description, logo, appName = 'Track Any Device' }: Props) {
18
+ return (
19
+ <div className="flex h-dvh overflow-hidden bg-background">
20
+ {/* LEFT PANEL */}
21
+ <aside className="relative hidden overflow-hidden border-r border-border bg-[#0b1220] lg:flex lg:w-[48%] lg:flex-col">
22
+ <div aria-hidden className="pointer-events-none absolute -top-32 -right-32 h-[480px] w-[480px] rounded-full opacity-25 blur-3xl"
23
+ style={{ background: 'radial-gradient(circle, var(--primary) 0%, transparent 70%)' }} />
24
+ <div aria-hidden className="pointer-events-none absolute -bottom-32 -left-24 h-[420px] w-[420px] rounded-full opacity-20 blur-3xl"
25
+ style={{ background: 'radial-gradient(circle, #38bdf8 0%, transparent 70%)' }} />
26
+ <div aria-hidden className="pointer-events-none absolute inset-0 opacity-[0.05]"
27
+ style={{ backgroundImage: 'radial-gradient(circle, white 1px, transparent 1px)', backgroundSize: '24px 24px' }} />
28
+
29
+ <div className="relative z-10 flex h-full flex-col justify-between p-10">
30
+ <div className="flex items-center gap-3">
31
+ {logo && <div className="flex h-10 items-center rounded-lg bg-white/95 px-3 shadow-sm">{logo}</div>}
32
+ <span className="text-base font-semibold text-white">{appName}</span>
33
+ </div>
34
+
35
+ <div className="max-w-md">
36
+ <span className="mb-4 inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/[0.06] px-3 py-1 text-[11px] font-medium tracking-wider text-white/70 uppercase">
37
+ <span className="relative flex h-1.5 w-1.5">
38
+ <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-80" />
39
+ <span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-emerald-400" />
40
+ </span>
41
+ Live operations platform
42
+ </span>
43
+
44
+ <h2 className="text-4xl leading-tight font-bold tracking-tight text-white">
45
+ Field intelligence,<br />in one view.
46
+ </h2>
47
+ <p className="mt-5 text-base leading-relaxed text-white/65">
48
+ Real-time GPS tracking, beat zone enforcement, and automated incident management — built for fleet, government, and IoT operators.
49
+ </p>
50
+
51
+ <ul className="mt-9 space-y-2">
52
+ {FEATURES.map((f) => (
53
+ <li key={f.label} className="flex items-start gap-3 rounded-xl border border-white/[0.08] bg-white/[0.04] p-3 backdrop-blur-sm transition-colors hover:border-white/15 hover:bg-white/[0.06]">
54
+ <span className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-primary"
55
+ style={{ background: 'color-mix(in oklab, var(--primary) 18%, transparent)' }}>
56
+ <f.icon className="h-4 w-4" />
57
+ </span>
58
+ <span className="leading-tight">
59
+ <span className="block text-sm font-semibold text-white">{f.label}</span>
60
+ <span className="block text-xs text-white/55">{f.sub}</span>
61
+ </span>
62
+ </li>
63
+ ))}
64
+ </ul>
65
+ </div>
66
+
67
+ <div className="text-[11px] tracking-wide text-white/35">
68
+ © {new Date().getFullYear()} · Secure · Encrypted · Enterprise-grade
69
+ </div>
70
+ </div>
71
+ </aside>
72
+
73
+ {/* RIGHT PANEL */}
74
+ <div className="flex flex-1 items-center justify-center overflow-y-auto bg-background p-6 lg:p-12">
75
+ <div className="w-full max-w-sm">
76
+ {logo && <div className="mb-8 flex items-center justify-center gap-2 lg:hidden">{logo}</div>}
77
+ <div className="rounded-2xl border border-border bg-card p-8 shadow-sm">
78
+ <div className="mb-6">
79
+ <h1 className="text-xl font-semibold tracking-tight text-foreground">{title}</h1>
80
+ {description && <p className="mt-1.5 text-sm text-muted-foreground">{description}</p>}
81
+ </div>
82
+ {children}
83
+ </div>
84
+ <p className="mt-5 text-center text-xs text-muted-foreground">Secure · Encrypted · Enterprise-grade</p>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,162 @@
1
+ 'use client';
2
+ import { PlatformLink, usePlatformUrl } from '../platform/context';
3
+ import {
4
+ AlertTriangle,
5
+ LayoutDashboard,
6
+ MapPin,
7
+ Package,
8
+ Shield,
9
+ Smartphone,
10
+ User,
11
+ } from 'lucide-react';
12
+
13
+ import SiteHeader from '../components/web/SiteHeader';
14
+
15
+ type SidebarItem = {
16
+ label: string;
17
+ href: string;
18
+ icon: React.ComponentType<{ className?: string }>;
19
+ matchPrefixes: string[];
20
+ };
21
+
22
+ const SIDEBAR: SidebarItem[] = [
23
+ {
24
+ label: 'Dashboard',
25
+ href: '/dashboard',
26
+ icon: LayoutDashboard,
27
+ matchPrefixes: ['/dashboard'],
28
+ },
29
+ {
30
+ label: 'Live Tracking',
31
+ href: '/live-tracking',
32
+ icon: MapPin,
33
+ matchPrefixes: ['/live-tracking'],
34
+ },
35
+ {
36
+ label: 'My Devices',
37
+ href: '/devices',
38
+ icon: Smartphone,
39
+ matchPrefixes: ['/devices', '/my-devices'],
40
+ },
41
+ {
42
+ label: 'My Beats',
43
+ href: '/beats',
44
+ icon: Shield,
45
+ matchPrefixes: ['/beats', '/my-beats'],
46
+ },
47
+ {
48
+ label: 'My Incidents',
49
+ href: '/incidents',
50
+ icon: AlertTriangle,
51
+ matchPrefixes: ['/incidents', '/my-incidents'],
52
+ },
53
+ {
54
+ label: 'My Orders',
55
+ href: '/orders',
56
+ icon: Package,
57
+ matchPrefixes: ['/orders', '/my-orders'],
58
+ },
59
+ {
60
+ label: 'Profile',
61
+ href: '/settings/profile',
62
+ icon: User,
63
+ matchPrefixes: ['/settings'],
64
+ },
65
+ ];
66
+
67
+ /**
68
+ * WebAppLayout — used for authenticated web-host user-area pages.
69
+ *
70
+ * Composition:
71
+ * ┌───────────────────────────────────┐
72
+ * │ SiteHeader │ (public web header, with auth menu)
73
+ * ├──────────┬────────────────────────┤
74
+ * │ Sidebar │ Main page content │
75
+ * │ (Dash, │ │
76
+ * │ My Dev, │ │
77
+ * │ Orders, │ │
78
+ * │ Profile)│ │
79
+ * ├──────────┴────────────────────────┤
80
+ * │ SiteFooter │
81
+ * └───────────────────────────────────┘
82
+ *
83
+ * Distinct from the tenant-portal AppLayout, which uses an app-style sidebar
84
+ * + breadcrumbs and no public footer.
85
+ *
86
+ */
87
+ export default function WebAppLayout({
88
+ children,
89
+ }: {
90
+ children: React.ReactNode;
91
+ }) {
92
+ const url = usePlatformUrl();
93
+ const appName =
94
+ (typeof window !== 'undefined' && window.AppConfig?.appName) ||
95
+ 'Fleet Tracking';
96
+
97
+ return (
98
+ <div className="flex min-h-screen flex-col bg-background text-foreground">
99
+ <SiteHeader />
100
+
101
+ <div className="mt-16 flex-1 px-4 sm:px-6 lg:px-8">
102
+ <div className="mx-auto flex w-full max-w-7xl flex-col lg:flex-row">
103
+ <aside className="border-b border-border bg-muted/30 lg:w-56 lg:shrink-0 lg:rounded-l-xl lg:border-r lg:border-b-0">
104
+ <nav className="sticky top-16 flex gap-1 overflow-x-auto p-3 lg:flex-col lg:gap-0.5 lg:overflow-visible lg:p-4">
105
+ {SIDEBAR.map((item) => {
106
+ const isActive = item.matchPrefixes.some(
107
+ (prefix) =>
108
+ url === prefix ||
109
+ url.startsWith(prefix + '/') ||
110
+ url.startsWith(prefix + '?'),
111
+ );
112
+ const Icon = item.icon;
113
+
114
+ return (
115
+ <PlatformLink
116
+ key={item.href}
117
+ href={item.href}
118
+ className={
119
+ 'flex shrink-0 items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition-colors lg:shrink ' +
120
+ (isActive
121
+ ? 'bg-primary text-primary-foreground'
122
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground')
123
+ }
124
+ >
125
+ <Icon className="size-4" />
126
+ <span>{item.label}</span>
127
+ </PlatformLink>
128
+ );
129
+ })}
130
+ </nav>
131
+ </aside>
132
+
133
+ <main className="min-w-0 flex-1 py-6 lg:px-6 lg:py-8">
134
+ {children}
135
+ </main>
136
+ </div>
137
+ </div>
138
+
139
+ <footer className="mt-8 border-t border-border bg-card">
140
+ <div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-2 px-4 py-4 text-xs text-muted-foreground sm:flex-row sm:px-6 lg:px-8">
141
+ <span>
142
+ © {new Date().getFullYear()} {appName}
143
+ </span>
144
+ <nav className="flex flex-wrap items-center gap-3">
145
+ <PlatformLink href="/terms" className="hover:text-foreground">
146
+ Terms
147
+ </PlatformLink>
148
+ <PlatformLink href="/privacy" className="hover:text-foreground">
149
+ Privacy
150
+ </PlatformLink>
151
+ <PlatformLink href="/cookies" className="hover:text-foreground">
152
+ Cookies
153
+ </PlatformLink>
154
+ <PlatformLink href="/contact" className="hover:text-foreground">
155
+ Contact
156
+ </PlatformLink>
157
+ </nav>
158
+ </div>
159
+ </footer>
160
+ </div>
161
+ );
162
+ }
@@ -0,0 +1,23 @@
1
+ import { PlatformLink } from '../platform/context';
2
+ import SiteFooter from '../components/web/SiteFooter';
3
+ import SiteHeader from '../components/web/SiteHeader';
4
+
5
+ /**
6
+ * WebLayout — wraps central-host public pages with the shared shadcn
7
+ * header + footer. Token-driven, so it respects the active tenant
8
+ * data-theme + light/dark mode.
9
+ *
10
+ * Pages with a full-bleed hero (home/) render SiteHeader / SiteFooter
11
+ * directly so they can sit above and below the hero without the
12
+ * fixed-header offset.
13
+ *
14
+ */
15
+ export default function WebLayout({ children }: { children: React.ReactNode }) {
16
+ return (
17
+ <div className="flex min-h-screen flex-col bg-background text-foreground">
18
+ <SiteHeader />
19
+ <main className="mt-16 flex flex-1 flex-col">{children}</main>
20
+ <SiteFooter />
21
+ </div>
22
+ );
23
+ }