@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,196 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
7
+
8
+ // Variants for TabsList
9
+ const tabsListVariants = cva('flex items-center shrink-0', {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-accent p-1',
13
+ button: '',
14
+ line: 'border-b border-border',
15
+ },
16
+ shape: {
17
+ default: '',
18
+ pill: '',
19
+ },
20
+ size: {
21
+ lg: 'gap-2.5',
22
+ md: 'gap-2',
23
+ sm: 'gap-1.5',
24
+ xs: 'gap-1',
25
+ },
26
+ },
27
+ compoundVariants: [
28
+ { variant: 'default', size: 'lg', className: 'p-1.5 gap-2.5' },
29
+ { variant: 'default', size: 'md', className: 'p-1 gap-2' },
30
+ { variant: 'default', size: 'sm', className: 'p-1 gap-1.5' },
31
+ { variant: 'default', size: 'xs', className: 'p-1 gap-1' },
32
+
33
+ {
34
+ variant: 'default',
35
+ shape: 'default',
36
+ size: 'lg',
37
+ className: 'rounded-lg',
38
+ },
39
+ {
40
+ variant: 'default',
41
+ shape: 'default',
42
+ size: 'md',
43
+ className: 'rounded-lg',
44
+ },
45
+ {
46
+ variant: 'default',
47
+ shape: 'default',
48
+ size: 'sm',
49
+ className: 'rounded-md',
50
+ },
51
+ {
52
+ variant: 'default',
53
+ shape: 'default',
54
+ size: 'xs',
55
+ className: 'rounded-md',
56
+ },
57
+
58
+ { variant: 'line', size: 'lg', className: 'gap-9' },
59
+ { variant: 'line', size: 'md', className: 'gap-8' },
60
+ { variant: 'line', size: 'sm', className: 'gap-4' },
61
+ { variant: 'line', size: 'xs', className: 'gap-4' },
62
+
63
+ {
64
+ variant: 'default',
65
+ shape: 'pill',
66
+ className: 'rounded-full [&_[role=tab]]:rounded-full',
67
+ },
68
+ {
69
+ variant: 'button',
70
+ shape: 'pill',
71
+ className: 'rounded-full [&_[role=tab]]:rounded-full',
72
+ },
73
+ ],
74
+ defaultVariants: {
75
+ variant: 'default',
76
+ size: 'md',
77
+ },
78
+ });
79
+
80
+ // Variants for TabsTrigger
81
+ const tabsTriggerVariants = cva(
82
+ 'shrink-0 cursor-pointer whitespace-nowrap inline-flex justify-center items-center font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&:hover_svg]:text-primary [&[data-state=active]_svg]:text-primary',
83
+ {
84
+ variants: {
85
+ variant: {
86
+ default:
87
+ 'text-muted-foreground data-[state=active]:bg-background hover:text-foreground data-[state=active]:text-foreground data-[state=active]:shadow-xs data-[state=active]:shadow-black/5',
88
+ button:
89
+ 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-lg text-accent-foreground hover:text-foreground data-[state=active]:bg-accent data-[state=active]:text-foreground',
90
+ line: 'border-b-2 text-muted-foreground border-transparent data-[state=active]:border-primary hover:text-primary data-[state=active]:text-primary data-[state=active]:border-primary data-[state=active]:text-primary',
91
+ },
92
+ size: {
93
+ lg: 'gap-2.5 [&_svg]:size-5 text-sm',
94
+ md: 'gap-2 [&_svg]:size-4 text-sm',
95
+ sm: 'gap-1.5 [&_svg]:size-3.5 text-xs',
96
+ xs: 'gap-1 [&_svg]:size-3.5 text-xs',
97
+ },
98
+ },
99
+ compoundVariants: [
100
+ { variant: 'default', size: 'lg', className: 'py-2.5 px-4 rounded-md' },
101
+ { variant: 'default', size: 'md', className: 'py-1.5 px-3 rounded-md' },
102
+ { variant: 'default', size: 'sm', className: 'py-1.5 px-2.5 rounded-sm' },
103
+ { variant: 'default', size: 'xs', className: 'py-1 px-2 rounded-sm' },
104
+
105
+ { variant: 'button', size: 'lg', className: 'py-3 px-4 rounded-lg' },
106
+ { variant: 'button', size: 'md', className: 'py-2.5 px-3 rounded-lg' },
107
+ { variant: 'button', size: 'sm', className: 'py-2 px-2.5 rounded-md' },
108
+ { variant: 'button', size: 'xs', className: 'py-1.5 px-2 rounded-md' },
109
+
110
+ { variant: 'line', size: 'lg', className: 'py-3' },
111
+ { variant: 'line', size: 'md', className: 'py-2.5' },
112
+ { variant: 'line', size: 'sm', className: 'py-2' },
113
+ { variant: 'line', size: 'xs', className: 'py-1.5' },
114
+ ],
115
+ defaultVariants: {
116
+ variant: 'default',
117
+ size: 'md',
118
+ },
119
+ },
120
+ );
121
+
122
+ // Variants for TabsContent
123
+ const tabsContentVariants = cva(
124
+ 'mt-2.5 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
125
+ {
126
+ variants: {
127
+ variant: {
128
+ default: '',
129
+ },
130
+ },
131
+ defaultVariants: {
132
+ variant: 'default',
133
+ },
134
+ },
135
+ );
136
+
137
+ // Context
138
+ type TabsContextType = {
139
+ variant?: 'default' | 'button' | 'line';
140
+ size?: 'lg' | 'sm' | 'xs' | 'md';
141
+ };
142
+ const TabsContext = React.createContext<TabsContextType>({
143
+ variant: 'default',
144
+ size: 'md',
145
+ });
146
+
147
+ // Components
148
+ function Tabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
149
+ return <TabsPrimitive.Root data-slot="tabs" className={cn('', className)} {...props} />;
150
+ }
151
+
152
+ function TabsList({
153
+ className,
154
+ variant = 'default',
155
+ shape = 'default',
156
+ size = 'md',
157
+ ...props
158
+ }: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>) {
159
+ return (
160
+ <TabsContext.Provider value={{ variant: variant || 'default', size: size || 'md' }}>
161
+ <TabsPrimitive.List
162
+ data-slot="tabs-list"
163
+ className={cn(tabsListVariants({ variant, shape, size }), className)}
164
+ {...props}
165
+ />
166
+ </TabsContext.Provider>
167
+ );
168
+ }
169
+
170
+ function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
171
+ const { variant, size } = React.useContext(TabsContext);
172
+
173
+ return (
174
+ <TabsPrimitive.Trigger
175
+ data-slot="tabs-trigger"
176
+ className={cn(tabsTriggerVariants({ variant, size }), className)}
177
+ {...props}
178
+ />
179
+ );
180
+ }
181
+
182
+ function TabsContent({
183
+ className,
184
+ variant,
185
+ ...props
186
+ }: React.ComponentProps<typeof TabsPrimitive.Content> & VariantProps<typeof tabsContentVariants>) {
187
+ return (
188
+ <TabsPrimitive.Content
189
+ data-slot="tabs-content"
190
+ className={cn(tabsContentVariants({ variant }), className)}
191
+ {...props}
192
+ />
193
+ );
194
+ }
195
+
196
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Timeline — vertical event timeline for activity logs / status history.
3
+ *
4
+ * <Timeline>
5
+ * <TimelineItem icon={CheckCircle} datetime='2026-05-18T10:24Z' title='Approved'>
6
+ * by admin@example.com
7
+ * </TimelineItem>
8
+ * </Timeline>
9
+ */
10
+ import type { LucideIcon } from 'lucide-react';
11
+ import { formatLocalDateTime } from '../../lib/datetime';
12
+ import { Circle } from 'lucide-react';
13
+ import * as React from 'react';
14
+
15
+ import { cn } from '../../lib/utils';
16
+
17
+ export function Timeline({
18
+ children,
19
+ className,
20
+ }: {
21
+ children: React.ReactNode;
22
+ className?: string;
23
+ }) {
24
+ return (
25
+ <ol
26
+ className={cn(
27
+ 'relative space-y-6 border-l border-border pl-6',
28
+ className,
29
+ )}
30
+ >
31
+ {children}
32
+ </ol>
33
+ );
34
+ }
35
+
36
+ type Variant = 'default' | 'success' | 'warning' | 'danger' | 'info';
37
+
38
+ const VARIANT_STYLES: Record<Variant, string> = {
39
+ default: 'bg-muted text-muted-foreground',
40
+ success: 'bg-success-subtle text-success',
41
+ warning: 'bg-warning-subtle text-warning',
42
+ danger: 'bg-danger-subtle text-destructive',
43
+ info: 'bg-info-subtle text-info',
44
+ };
45
+
46
+ type ItemProps = {
47
+ icon?: LucideIcon;
48
+ title: React.ReactNode;
49
+ datetime?: string;
50
+ variant?: Variant;
51
+ children?: React.ReactNode;
52
+ };
53
+
54
+ export function TimelineItem({
55
+ icon: Icon = Circle,
56
+ title,
57
+ datetime,
58
+ variant = 'default',
59
+ children,
60
+ }: ItemProps) {
61
+ return (
62
+ <li className="relative">
63
+ <span
64
+ aria-hidden="true"
65
+ className={cn(
66
+ 'absolute -left-9 flex h-6 w-6 items-center justify-center rounded-full ring-4 ring-background',
67
+ VARIANT_STYLES[variant],
68
+ )}
69
+ >
70
+ <Icon className="size-3.5" />
71
+ </span>
72
+ <div className="flex items-baseline justify-between gap-3">
73
+ <p className="text-sm font-medium text-foreground">{title}</p>
74
+ {datetime && (
75
+ <time
76
+ dateTime={datetime}
77
+ className="text-xs text-muted-foreground"
78
+ >
79
+ {formatLocalDateTime(datetime)}
80
+ </time>
81
+ )}
82
+ </div>
83
+ {children && (
84
+ <div className="mt-1 text-sm text-muted-foreground">
85
+ {children}
86
+ </div>
87
+ )}
88
+ </li>
89
+ );
90
+ }
@@ -0,0 +1,73 @@
1
+ 'use client';
2
+
3
+ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
4
+ import { type VariantProps } from "class-variance-authority"
5
+ import * as React from "react"
6
+
7
+ import { toggleVariants } from "./toggle"
8
+ import { cn } from "../../lib/utils"
9
+
10
+ const ToggleGroupContext = React.createContext<
11
+ VariantProps<typeof toggleVariants>
12
+ >({
13
+ size: "default",
14
+ variant: "default",
15
+ })
16
+
17
+ function ToggleGroup({
18
+ className,
19
+ variant,
20
+ size,
21
+ children,
22
+ ...props
23
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
24
+ VariantProps<typeof toggleVariants>) {
25
+ return (
26
+ <ToggleGroupPrimitive.Root
27
+ data-slot="toggle-group"
28
+ data-variant={variant}
29
+ data-size={size}
30
+ className={cn(
31
+ "group/toggle-group flex items-center rounded-md data-[variant=outline]:shadow-xs",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ <ToggleGroupContext.Provider value={{ variant, size }}>
37
+ {children}
38
+ </ToggleGroupContext.Provider>
39
+ </ToggleGroupPrimitive.Root>
40
+ )
41
+ }
42
+
43
+ function ToggleGroupItem({
44
+ className,
45
+ children,
46
+ variant,
47
+ size,
48
+ ...props
49
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
50
+ VariantProps<typeof toggleVariants>) {
51
+ const context = React.useContext(ToggleGroupContext)
52
+
53
+ return (
54
+ <ToggleGroupPrimitive.Item
55
+ data-slot="toggle-group-item"
56
+ data-variant={context.variant || variant}
57
+ data-size={context.size || size}
58
+ className={cn(
59
+ toggleVariants({
60
+ variant: context.variant || variant,
61
+ size: context.size || size,
62
+ }),
63
+ "min-w-0 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
64
+ className
65
+ )}
66
+ {...props}
67
+ >
68
+ {children}
69
+ </ToggleGroupPrimitive.Item>
70
+ )
71
+ }
72
+
73
+ export { ToggleGroup, ToggleGroupItem }
@@ -0,0 +1,45 @@
1
+ import * as TogglePrimitive from "@radix-ui/react-toggle"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ const toggleVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-transparent",
13
+ outline:
14
+ "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
15
+ },
16
+ size: {
17
+ default: "h-9 px-2 min-w-9",
18
+ sm: "h-8 px-1.5 min-w-8",
19
+ lg: "h-10 px-2.5 min-w-10",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ size: "default",
25
+ },
26
+ }
27
+ )
28
+
29
+ function Toggle({
30
+ className,
31
+ variant,
32
+ size,
33
+ ...props
34
+ }: React.ComponentProps<typeof TogglePrimitive.Root> &
35
+ VariantProps<typeof toggleVariants>) {
36
+ return (
37
+ <TogglePrimitive.Root
38
+ data-slot="toggle"
39
+ className={cn(toggleVariants({ variant, size, className }))}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export { Toggle, toggleVariants }
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { cn } from '../../lib/utils';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
7
+
8
+ function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
9
+ return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />;
10
+ }
11
+
12
+ function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
13
+ return (
14
+ <TooltipProvider>
15
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
16
+ </TooltipProvider>
17
+ );
18
+ }
19
+
20
+ function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
21
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
22
+ }
23
+
24
+ const tooltipVariants = cva(
25
+ 'z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
26
+ {
27
+ variants: {
28
+ variant: {
29
+ light: 'border border-border bg-background text-foreground shadow-md shadow-black/5',
30
+ dark: 'dark:border dark:border-border bg-zinc-950 text-white dark:bg-zinc-300 dark:text-black shadow-md shadow-black/5',
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: 'dark',
35
+ },
36
+ },
37
+ );
38
+
39
+ function TooltipContent({
40
+ className,
41
+ sideOffset = 4,
42
+ variant,
43
+ ...props
44
+ }: React.ComponentProps<typeof TooltipPrimitive.Content> & VariantProps<typeof tooltipVariants>) {
45
+ return (
46
+ <TooltipPrimitive.Content
47
+ data-slot="tooltip-content"
48
+ sideOffset={sideOffset}
49
+ className={cn(tooltipVariants({ variant }), className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
3
+ import { useInitials } from '../hooks/use-initials';
4
+ import type { User } from '../types/auth';
5
+
6
+ export function UserInfo({
7
+ user,
8
+ showEmail = false,
9
+ }: {
10
+ user: User;
11
+ showEmail?: boolean;
12
+ }) {
13
+ const getInitials = useInitials();
14
+
15
+ return (
16
+ <>
17
+ <Avatar className="h-8 w-8 overflow-hidden rounded-full">
18
+ <AvatarImage src={user.avatar} alt={user.name} />
19
+ <AvatarFallback className="rounded-lg bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white">
20
+ {getInitials(user.name)}
21
+ </AvatarFallback>
22
+ </Avatar>
23
+ <div className="grid flex-1 text-left text-sm leading-tight">
24
+ <span className="truncate font-medium">{user.name}</span>
25
+ {showEmail && (
26
+ <span className="truncate text-xs text-muted-foreground">
27
+ {user.email}
28
+ </span>
29
+ )}
30
+ </div>
31
+ </>
32
+ );
33
+ }
@@ -0,0 +1,53 @@
1
+ import { PlatformLink } from '../platform/context';
2
+ import { LogOut, Settings } from 'lucide-react';
3
+ import {
4
+ DropdownMenuGroup,
5
+ DropdownMenuItem,
6
+ DropdownMenuLabel,
7
+ DropdownMenuSeparator,
8
+ } from './ui/dropdown-menu';
9
+ import { UserInfo } from './user-info';
10
+ import type { User } from '../types/auth';
11
+
12
+ type Props = {
13
+ user: User;
14
+ settingsUrl?: string;
15
+ logoutUrl?: string;
16
+ };
17
+
18
+ export function UserMenuContent({
19
+ user,
20
+ settingsUrl = '/settings/profile',
21
+ logoutUrl = '/logout',
22
+ }: Props) {
23
+ return (
24
+ <>
25
+ <DropdownMenuLabel className="p-0 font-normal">
26
+ <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
27
+ <UserInfo user={user} showEmail={true} />
28
+ </div>
29
+ </DropdownMenuLabel>
30
+ <DropdownMenuSeparator />
31
+ <DropdownMenuGroup>
32
+ <DropdownMenuItem asChild>
33
+ <PlatformLink className="block w-full cursor-pointer" href={settingsUrl} prefetch>
34
+ <Settings className="mr-2" />
35
+ Settings
36
+ </PlatformLink>
37
+ </DropdownMenuItem>
38
+ </DropdownMenuGroup>
39
+ <DropdownMenuSeparator />
40
+ <DropdownMenuItem asChild>
41
+ <PlatformLink
42
+ className="block w-full cursor-pointer"
43
+ href={logoutUrl}
44
+ as="button"
45
+ data-test="logout-button"
46
+ >
47
+ <LogOut className="mr-2" />
48
+ Log out
49
+ </PlatformLink>
50
+ </DropdownMenuItem>
51
+ </>
52
+ );
53
+ }