@fayz-ai/ui 0.9.0 → 0.10.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 (156) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics/AnalyticsCard.d.ts +10 -0
  3. package/dist/analytics/AnalyticsCard.d.ts.map +1 -0
  4. package/dist/analytics/AnalyticsControls.d.ts +18 -0
  5. package/dist/analytics/AnalyticsControls.d.ts.map +1 -0
  6. package/dist/analytics/AnalyticsGrid.d.ts +16 -0
  7. package/dist/analytics/AnalyticsGrid.d.ts.map +1 -0
  8. package/dist/analytics/context.d.ts +58 -0
  9. package/dist/analytics/context.d.ts.map +1 -0
  10. package/dist/analytics/derive.d.ts +81 -0
  11. package/dist/analytics/derive.d.ts.map +1 -0
  12. package/dist/analytics/index.d.ts +13 -0
  13. package/dist/analytics/index.d.ts.map +1 -0
  14. package/dist/analytics/range.d.ts +22 -0
  15. package/dist/analytics/range.d.ts.map +1 -0
  16. package/dist/{chunk-PVIB724L.js → chunk-22EVFMS3.js} +5 -3
  17. package/dist/chunk-22EVFMS3.js.map +1 -0
  18. package/dist/{chunk-G6OKZ7WL.js → chunk-6V5MGYXH.js} +13 -9
  19. package/dist/chunk-6V5MGYXH.js.map +1 -0
  20. package/dist/{chunk-KV3MHB6N.js → chunk-H63TOSQ7.js} +196 -53
  21. package/dist/chunk-H63TOSQ7.js.map +1 -0
  22. package/dist/{chunk-OUPJDGKE.js → chunk-I6CPKAOD.js} +134 -37
  23. package/dist/chunk-I6CPKAOD.js.map +1 -0
  24. package/dist/{chunk-YRVE4FOD.js → chunk-OKZ6WQB3.js} +3 -3
  25. package/dist/chunk-OKZ6WQB3.js.map +1 -0
  26. package/dist/{chunk-S5KNM4D2.js → chunk-SEIY2F5F.js} +235 -107
  27. package/dist/chunk-SEIY2F5F.js.map +1 -0
  28. package/dist/{chunk-75EFDLST.js → chunk-VAKWUUVC.js} +157 -6
  29. package/dist/chunk-VAKWUUVC.js.map +1 -0
  30. package/dist/dashboard/OnboardingWidget.d.ts +2 -14
  31. package/dist/dashboard/OnboardingWidget.d.ts.map +1 -1
  32. package/dist/dashboard/SidebarOnboarding.d.ts +28 -0
  33. package/dist/dashboard/SidebarOnboarding.d.ts.map +1 -0
  34. package/dist/dashboard/index.d.ts +2 -0
  35. package/dist/dashboard/index.d.ts.map +1 -1
  36. package/dist/dashboard/index.js +4 -4
  37. package/dist/dashboard/types.d.ts +18 -0
  38. package/dist/dashboard/types.d.ts.map +1 -1
  39. package/dist/dashboard/useOnboardingProgress.d.ts +28 -0
  40. package/dist/dashboard/useOnboardingProgress.d.ts.map +1 -0
  41. package/dist/icons.js +1 -1
  42. package/dist/index.d.ts +5 -4
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +960 -14
  45. package/dist/index.js.map +1 -1
  46. package/dist/layout/AppShell.d.ts +14 -1
  47. package/dist/layout/AppShell.d.ts.map +1 -1
  48. package/dist/layout/ModulePage.d.ts +11 -2
  49. package/dist/layout/ModulePage.d.ts.map +1 -1
  50. package/dist/layout/Sidebar.d.ts +8 -1
  51. package/dist/layout/Sidebar.d.ts.map +1 -1
  52. package/dist/layout/index.js +4 -4
  53. package/dist/lib/cn.d.ts.map +1 -0
  54. package/dist/preset.d.ts +3 -0
  55. package/dist/preset.d.ts.map +1 -0
  56. package/dist/primitives/data-table.d.ts +19 -4
  57. package/dist/primitives/data-table.d.ts.map +1 -1
  58. package/dist/primitives/date-picker.d.ts +4 -2
  59. package/dist/primitives/date-picker.d.ts.map +1 -1
  60. package/dist/primitives/filter-chip.d.ts +26 -0
  61. package/dist/primitives/filter-chip.d.ts.map +1 -0
  62. package/dist/primitives/header-tabs.d.ts +32 -0
  63. package/dist/primitives/header-tabs.d.ts.map +1 -0
  64. package/dist/primitives/hint.d.ts +25 -0
  65. package/dist/primitives/hint.d.ts.map +1 -0
  66. package/dist/primitives/index.d.ts +7 -0
  67. package/dist/primitives/index.d.ts.map +1 -1
  68. package/dist/primitives/index.js +4 -4
  69. package/dist/primitives/list-view.d.ts +14 -1
  70. package/dist/primitives/list-view.d.ts.map +1 -1
  71. package/dist/primitives/page-header.d.ts +23 -0
  72. package/dist/primitives/page-header.d.ts.map +1 -0
  73. package/dist/primitives/stat-tiles.d.ts +30 -0
  74. package/dist/primitives/stat-tiles.d.ts.map +1 -0
  75. package/dist/primitives/switch.d.ts +11 -0
  76. package/dist/primitives/switch.d.ts.map +1 -0
  77. package/dist/stores/index.d.ts +1 -0
  78. package/dist/stores/index.d.ts.map +1 -1
  79. package/dist/stores/page-header.store.d.ts +69 -0
  80. package/dist/stores/page-header.store.d.ts.map +1 -0
  81. package/package.json +4 -13
  82. package/dist/chunk-75EFDLST.js.map +0 -1
  83. package/dist/chunk-G6OKZ7WL.js.map +0 -1
  84. package/dist/chunk-KV3MHB6N.js.map +0 -1
  85. package/dist/chunk-OUPJDGKE.js.map +0 -1
  86. package/dist/chunk-PVIB724L.js.map +0 -1
  87. package/dist/chunk-S5KNM4D2.js.map +0 -1
  88. package/dist/chunk-YRVE4FOD.js.map +0 -1
  89. package/dist/utils/cn.d.ts.map +0 -1
  90. package/src/dashboard/ChartWidget.tsx +0 -50
  91. package/src/dashboard/ChartWidgetInner.tsx +0 -83
  92. package/src/dashboard/DashboardCanvas.tsx +0 -210
  93. package/src/dashboard/DashboardCustomizeMenu.tsx +0 -53
  94. package/src/dashboard/DashboardGrid.tsx +0 -35
  95. package/src/dashboard/DashboardRange.tsx +0 -62
  96. package/src/dashboard/GoldCard.tsx +0 -38
  97. package/src/dashboard/KpiCard.tsx +0 -238
  98. package/src/dashboard/OnboardingWidget.tsx +0 -140
  99. package/src/dashboard/TableWidget.tsx +0 -51
  100. package/src/dashboard/define.ts +0 -53
  101. package/src/dashboard/icon.ts +0 -13
  102. package/src/dashboard/index.ts +0 -38
  103. package/src/dashboard/layout.ts +0 -50
  104. package/src/dashboard/preferences.ts +0 -71
  105. package/src/dashboard/types.ts +0 -41
  106. package/src/icons.ts +0 -66
  107. package/src/index.ts +0 -238
  108. package/src/layout/AppShell.tsx +0 -987
  109. package/src/layout/ContentSplit.tsx +0 -153
  110. package/src/layout/ModulePage.tsx +0 -466
  111. package/src/layout/PageTransition.tsx +0 -57
  112. package/src/layout/SaveBar.tsx +0 -247
  113. package/src/layout/Sidebar.tsx +0 -535
  114. package/src/layout/Topbar.tsx +0 -462
  115. package/src/layout/index.ts +0 -39
  116. package/src/layout/useCrossModuleBack.ts +0 -30
  117. package/src/primitives/Avatar.tsx +0 -48
  118. package/src/primitives/SearchCombobox.tsx +0 -216
  119. package/src/primitives/badge.tsx +0 -68
  120. package/src/primitives/breadcrumb.tsx +0 -43
  121. package/src/primitives/button.tsx +0 -60
  122. package/src/primitives/card.tsx +0 -79
  123. package/src/primitives/checkbox.tsx +0 -53
  124. package/src/primitives/confirm-dialog.tsx +0 -125
  125. package/src/primitives/currency-input.tsx +0 -121
  126. package/src/primitives/data-table.tsx +0 -160
  127. package/src/primitives/date-picker.tsx +0 -225
  128. package/src/primitives/dropdown.tsx +0 -196
  129. package/src/primitives/index.ts +0 -87
  130. package/src/primitives/input.tsx +0 -50
  131. package/src/primitives/list-view.tsx +0 -252
  132. package/src/primitives/markdown-editor.tsx +0 -75
  133. package/src/primitives/markdown.tsx +0 -169
  134. package/src/primitives/modal.tsx +0 -156
  135. package/src/primitives/phone-link.tsx +0 -138
  136. package/src/primitives/popover.tsx +0 -30
  137. package/src/primitives/search-select.tsx +0 -314
  138. package/src/primitives/segmented-control.tsx +0 -66
  139. package/src/primitives/select.tsx +0 -157
  140. package/src/primitives/separator.tsx +0 -29
  141. package/src/primitives/sheet.tsx +0 -181
  142. package/src/primitives/skeleton.tsx +0 -46
  143. package/src/primitives/tabs.tsx +0 -53
  144. package/src/primitives/time-picker.tsx +0 -148
  145. package/src/primitives/toast.tsx +0 -46
  146. package/src/primitives/tooltip.tsx +0 -28
  147. package/src/stores/index.ts +0 -10
  148. package/src/stores/layout.store.ts +0 -31
  149. package/src/stores/theme.store.ts +0 -457
  150. package/src/styles.css +0 -461
  151. package/src/tailwind.ts +0 -43
  152. package/src/theme/index.ts +0 -94
  153. package/src/theme/preset.ts +0 -209
  154. package/src/theme/presets.ts +0 -106
  155. package/src/utils/cn.ts +0 -6
  156. /package/dist/{utils → lib}/cn.d.ts +0 -0
@@ -1,462 +0,0 @@
1
- import * as React from 'react'
2
- import * as Popover from '@radix-ui/react-popover'
3
- import {
4
- Menu,
5
- Search,
6
- ChevronDown,
7
- Filter,
8
- Home,
9
- Users,
10
- Settings,
11
- CreditCard,
12
- Bell,
13
- Calendar,
14
- CalendarClock,
15
- Package,
16
- Activity,
17
- BarChart3,
18
- Clapperboard,
19
- Newspaper,
20
- FileText,
21
- Mail,
22
- DollarSign,
23
- Megaphone,
24
- ShoppingCart,
25
- ShoppingBag,
26
- Target,
27
- Wrench,
28
- ClipboardList,
29
- ClipboardCheck,
30
- Briefcase,
31
- UserCog,
32
- BookOpen,
33
- BookOpenCheck,
34
- GraduationCap,
35
- Repeat,
36
- MessageCircle,
37
- Globe,
38
- Percent,
39
- Tag,
40
- Tags,
41
- Camera,
42
- UtensilsCrossed,
43
- MapPin,
44
- Handshake,
45
- Contact,
46
- Building2,
47
- Plus,
48
- Shield,
49
- List,
50
- ListChecks,
51
- FileCheck2,
52
- FolderOpen,
53
- Dog,
54
- Cat,
55
- PawPrint,
56
- Heart,
57
- LayoutTemplate,
58
- LeafyGreen,
59
- Apple,
60
- Egg,
61
- Wheat,
62
- ArrowDownCircle,
63
- ArrowUpCircle,
64
- Landmark,
65
- Receipt,
66
- TrendingUp,
67
- CircleDollarSign,
68
- Wallet,
69
- Clock,
70
- AlertTriangle,
71
- Sparkles,
72
- Warehouse,
73
- Ruler,
74
- ArrowUpRight,
75
- ArrowDownRight,
76
- ArrowLeftRight,
77
- Plug,
78
- SlidersHorizontal,
79
- TableProperties,
80
- UserPlus,
81
- UserCheck,
82
- UserX,
83
- TreePalm,
84
- Boxes,
85
- Box,
86
- PartyPopper,
87
- Radio,
88
- CalendarDays,
89
- CalendarCheck2,
90
- CalendarX,
91
- BadgeDollarSign,
92
- Banknote,
93
- Layers,
94
- Music,
95
- Eye,
96
- ListMusic,
97
- ListPlus,
98
- Map,
99
- Ban,
100
- Clock3,
101
- Disc3,
102
- UsersRound,
103
- Mic,
104
- Inbox,
105
- Star,
106
- Zap,
107
- Crown,
108
- LogOut,
109
- type LucideIcon,
110
- } from 'lucide-react'
111
- import { cn } from '../utils/cn'
112
- import { useLayoutStore } from '../stores/layout.store'
113
-
114
- export interface NavigationItem {
115
- id: string
116
- label: string
117
- icon: string
118
- route: string
119
- section: 'main' | 'secondary' | 'settings'
120
- badge?: string | number
121
- children?: NavigationItem[]
122
- permission?: { feature: string; action: 'read' | 'create' | 'edit' | 'delete' }
123
- /** Freemium discovery: role-allowed but not entitled by the current plan.
124
- * Renders a small Crown badge; clicking leads to the plan's UpgradePrompt. */
125
- premium?: boolean
126
- }
127
-
128
- export interface TopbarProps {
129
- navigation: NavigationItem[]
130
- logo?: React.ReactNode
131
- user?: { fullName: string; avatarUrl?: string; email: string }
132
- onMenuClick?: () => void
133
- /** Workspace/org switcher rendered right after the logo (desktop). */
134
- orgSwitcher?: React.ReactNode
135
- leftContent?: React.ReactNode
136
- rightContent?: React.ReactNode
137
- currentPath?: string
138
- onNavigate?: (route: string) => void
139
- searchPlaceholder?: string
140
- onSignOut?: () => void
141
- onProfile?: () => void
142
- onSettings?: () => void
143
- onBilling?: () => void
144
- /** Current-plan pill shown next to the user in the dropdown header. */
145
- userPlan?: { label: string; paid: boolean }
146
- /** i18n label for the billing/subscription menu item (host owns translation). */
147
- billingLabel?: string
148
- userMenuExtras?: { label: string; icon?: React.ReactNode; onClick: () => void }[]
149
- }
150
-
151
- function TopbarUserMenu({
152
- user,
153
- userPlan,
154
- billingLabel = 'Subscription',
155
- onSignOut,
156
- onProfile,
157
- onSettings,
158
- onBilling,
159
- userMenuExtras,
160
- }: Pick<TopbarProps, 'user' | 'userPlan' | 'billingLabel' | 'onSignOut' | 'onProfile' | 'onSettings' | 'onBilling' | 'userMenuExtras'>) {
161
- const [open, setOpen] = React.useState(false)
162
- if (!user) return null
163
- const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? '?').toUpperCase()
164
- const item = (label: string, onClick?: () => void, Icon?: LucideIcon) =>
165
- onClick ? (
166
- <button
167
- key={label}
168
- onClick={() => { setOpen(false); onClick() }}
169
- className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70"
170
- >
171
- {Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
172
- <span>{label}</span>
173
- </button>
174
- ) : null
175
- return (
176
- <Popover.Root open={open} onOpenChange={setOpen}>
177
- <Popover.Trigger asChild>
178
- <button
179
- aria-label="User menu"
180
- className="ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border"
181
- >
182
- {user.avatarUrl ? (
183
- // eslint-disable-next-line @next/next/no-img-element
184
- <img src={user.avatarUrl} alt="" className="h-full w-full object-cover" />
185
- ) : (
186
- initial
187
- )}
188
- </button>
189
- </Popover.Trigger>
190
- <Popover.Portal>
191
- <Popover.Content
192
- align="end"
193
- sideOffset={8}
194
- className="fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95"
195
- >
196
- <div className="border-b border-border px-3 py-2">
197
- <p className="truncate text-sm font-medium text-foreground">{user.fullName || user.email}</p>
198
- <p className="truncate text-xs text-muted-foreground">{user.email}</p>
199
- </div>
200
- <div className="mt-1 space-y-0.5">
201
- {item('Perfil', onProfile, UserCog)}
202
- {item('Configurações', onSettings, Settings)}
203
- {onBilling && (
204
- <button
205
- key="billing"
206
- onClick={() => { setOpen(false); onBilling() }}
207
- className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70"
208
- >
209
- <CreditCard className="h-4 w-4 text-muted-foreground" />
210
- <span className="flex-1 text-left">{billingLabel}</span>
211
- {userPlan && (
212
- <span
213
- className={
214
- userPlan.paid
215
- ? 'inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300'
216
- : 'inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground'
217
- }
218
- >
219
- {userPlan.paid ? <Crown className="h-3 w-3" /> : <Sparkles className="h-3 w-3" />}
220
- {userPlan.label}
221
- </span>
222
- )}
223
- </button>
224
- )}
225
- {userMenuExtras?.map((e) => item(e.label, e.onClick))}
226
- {onSignOut && (
227
- <>
228
- <div className="my-1 border-t border-border" />
229
- {item('Sair', onSignOut, LogOut)}
230
- </>
231
- )}
232
- </div>
233
- </Popover.Content>
234
- </Popover.Portal>
235
- </Popover.Root>
236
- )
237
- }
238
-
239
- export const ICON_MAP: Record<string, LucideIcon> = {
240
- Home, Users, Settings, CreditCard, Bell, Calendar, CalendarClock, Package, Activity, BarChart3,
241
- FileText, Mail, DollarSign, Megaphone, ShoppingCart, ShoppingBag, Target, Wrench,
242
- ClipboardList, ClipboardCheck, Briefcase, UserCog, BookOpen, MessageCircle, Globe,
243
- BookOpenCheck, Percent, Tag, Tags, Camera, UtensilsCrossed, Search, MapPin, Handshake,
244
- Contact, Building2, ChevronDown, Filter, Plus, Shield, List, ListChecks, FolderOpen, Dog, Cat, PawPrint, Heart, LayoutTemplate, LeafyGreen, Apple, Egg, Wheat,
245
- ArrowDownCircle, ArrowUpCircle, ArrowLeftRight, Plug, SlidersHorizontal, TableProperties, Landmark, Receipt, TrendingUp, CircleDollarSign, Clock, AlertTriangle, Sparkles, Wallet, Warehouse, Ruler, ArrowUpRight, ArrowDownRight, UserPlus, UserX, TreePalm,
246
- Boxes, Box, FileCheck2, PartyPopper, Radio, CalendarDays, BadgeDollarSign, Banknote, Layers, Music, Eye, ListMusic, Disc3, UsersRound, Mic,
247
- CalendarCheck2, CalendarX, ListPlus, Map, Ban, Clock3, Inbox, UserCheck, Star, Zap, Clapperboard, Newspaper, GraduationCap, Repeat,
248
- }
249
-
250
- function getIcon(name: string): LucideIcon {
251
- return ICON_MAP[name] ?? Home
252
- }
253
-
254
- function NavDropdown({ item, currentPath, onNavigate }: {
255
- item: NavigationItem
256
- currentPath: string
257
- onNavigate: (route: string) => void
258
- }) {
259
- const [open, setOpen] = React.useState(false)
260
- const Icon = getIcon(item.icon)
261
- const isChildActive = item.children?.some(
262
- (child) => currentPath === child.route || currentPath.startsWith(child.route + '/')
263
- )
264
-
265
- return (
266
- <Popover.Root open={open} onOpenChange={setOpen}>
267
- <Popover.Trigger asChild>
268
- <button
269
- data-nav-active={isChildActive || undefined}
270
- className={cn(
271
- 'flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors',
272
- isChildActive
273
- ? 'border-sidebar-accent-foreground font-semibold text-sidebar-foreground'
274
- : 'border-transparent text-sidebar-muted hover:text-sidebar-foreground'
275
- )}
276
- >
277
- <Icon className="h-4 w-4" />
278
- <span>{item.label}</span>
279
- <ChevronDown className={cn('h-3 w-3 transition-transform', open && 'rotate-180')} />
280
- </button>
281
- </Popover.Trigger>
282
- <Popover.Portal>
283
- <Popover.Content
284
- align="start"
285
- sideOffset={4}
286
- className="fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95"
287
- >
288
- {item.children?.map((child) => {
289
- const ChildIcon = getIcon(child.icon)
290
- const isActive = currentPath === child.route || currentPath.startsWith(child.route + '/')
291
- return (
292
- <button
293
- key={child.id}
294
- onClick={() => {
295
- onNavigate(child.route)
296
- setOpen(false)
297
- }}
298
- className={cn(
299
- 'flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors',
300
- isActive
301
- ? 'bg-muted font-medium text-foreground'
302
- : 'text-popover-foreground hover:bg-muted/70'
303
- )}
304
- >
305
- <ChildIcon className="h-4 w-4 text-muted-foreground" />
306
- <span>{child.label}</span>
307
- </button>
308
- )
309
- })}
310
- </Popover.Content>
311
- </Popover.Portal>
312
- </Popover.Root>
313
- )
314
- }
315
-
316
- export function Topbar({
317
- navigation,
318
- logo,
319
- user,
320
- onMenuClick,
321
- orgSwitcher,
322
- leftContent,
323
- rightContent,
324
- currentPath = '/',
325
- onNavigate,
326
- searchPlaceholder = 'Search...',
327
- onSignOut,
328
- onProfile,
329
- onSettings,
330
- onBilling,
331
- userPlan,
332
- billingLabel,
333
- userMenuExtras,
334
- }: TopbarProps) {
335
- const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen)
336
-
337
- const mainNav = navigation.filter((item) => item.section === 'main')
338
- const secondaryNav = navigation.filter((item) => item.section === 'secondary')
339
- const allNav = [...mainNav, ...secondaryNav]
340
-
341
- const handleNavigate = (route: string) => {
342
- onNavigate?.(route)
343
- }
344
-
345
- return (
346
- <header data-print="hide" className="sticky top-0 z-50 w-full shrink-0">
347
- {/* Row 1: Logo + Search + Actions */}
348
- <div className="border-b border-sidebar-border bg-sidebar text-sidebar-foreground">
349
- <div className="flex h-14 w-full items-center justify-between px-4 md:px-6">
350
- {/* Left: Mobile menu + Logo — the hamburger is only rendered when an
351
- onMenuClick handler is supplied. Mobile-first apps with a bottom
352
- nav omit it so the bottom bar is the primary navigation. */}
353
- <div className="flex items-center gap-3">
354
- {onMenuClick && (
355
- <button
356
- onClick={onMenuClick}
357
- className="inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden"
358
- aria-label="Open menu"
359
- >
360
- <Menu className="h-5 w-5" />
361
- </button>
362
- )}
363
- <div className="hidden shrink-0 items-center md:flex">
364
- {logo ?? <span className="text-lg font-bold">App</span>}
365
- </div>
366
- {orgSwitcher && (
367
- <div className="hidden min-w-0 items-center md:flex">{orgSwitcher}</div>
368
- )}
369
- </div>
370
-
371
- {/* Center: Search (desktop only) */}
372
- <div className="mx-8 hidden max-w-sm flex-1 md:flex">
373
- <button
374
- onClick={() => setCommandPaletteOpen(true)}
375
- className="relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground"
376
- >
377
- <Search className="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" />
378
- <span className="text-xs">{searchPlaceholder}</span>
379
- <kbd className="ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block">
380
- ⌘K
381
- </kbd>
382
- </button>
383
- </div>
384
-
385
- {/* Right: Actions */}
386
- <div className="flex items-center gap-1">
387
- {leftContent}
388
- {rightContent}
389
- <TopbarUserMenu
390
- user={user}
391
- userPlan={userPlan}
392
- billingLabel={billingLabel}
393
- onSignOut={onSignOut}
394
- onProfile={onProfile}
395
- onSettings={onSettings}
396
- onBilling={onBilling}
397
- userMenuExtras={userMenuExtras}
398
- />
399
- </div>
400
- </div>
401
- </div>
402
-
403
- {/* Row 2: Navigation — desktop only */}
404
- <div className="hidden md:block bg-sidebar">
405
- <div className="flex h-11 w-full items-center px-4 md:px-6">
406
- <nav ref={(el) => {
407
- if (!el) return
408
- const active = el.querySelector('[data-nav-active="true"]') as HTMLElement
409
- if (active) {
410
- const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2
411
- el.scrollTo({ left: Math.max(0, left), behavior: 'smooth' })
412
- }
413
- }} className="flex items-center gap-0.5 overflow-x-auto scrollbar-none">
414
- {allNav.map((item) => {
415
- if (item.children && item.children.length > 0) {
416
- return (
417
- <NavDropdown
418
- key={item.id}
419
- item={item}
420
- currentPath={currentPath}
421
- onNavigate={handleNavigate}
422
- />
423
- )
424
- }
425
-
426
- const Icon = getIcon(item.icon)
427
- const isActive =
428
- item.route === '/'
429
- ? currentPath === '/'
430
- : currentPath === item.route || currentPath.startsWith(item.route + '/')
431
-
432
- return (
433
- <button
434
- key={item.id}
435
- data-nav-active={isActive || undefined}
436
- onClick={() => handleNavigate(item.route)}
437
- className={cn(
438
- 'flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors',
439
- isActive
440
- ? 'border-primary font-semibold text-sidebar-foreground'
441
- : 'border-transparent text-sidebar-muted hover:text-sidebar-foreground'
442
- )}
443
- >
444
- <Icon className="h-4 w-4" />
445
- <span>{item.label}</span>
446
- {item.premium && (
447
- <Crown className="h-3 w-3 text-amber-500" aria-hidden />
448
- )}
449
- {item.badge !== undefined && (
450
- <span className="rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground">
451
- {item.badge}
452
- </span>
453
- )}
454
- </button>
455
- )
456
- })}
457
- </nav>
458
- </div>
459
- </div>
460
- </header>
461
- )
462
- }
@@ -1,39 +0,0 @@
1
- export {
2
- AppShell,
3
- type AppShellProps,
4
- type AppShellUser,
5
- type BottomNavItem,
6
- type BottomNavRouteItem,
7
- type BottomNavActionItem,
8
- type MobileHeaderVariant,
9
- } from './AppShell'
10
- export { Sidebar, type SidebarProps, type SidebarUser, type NavigationItem, ICON_MAP } from './Sidebar'
11
- export { Topbar, type TopbarProps } from './Topbar'
12
- export {
13
- ModulePage,
14
- SubpageHeader,
15
- ModuleLayoutProvider,
16
- useModuleLayout,
17
- PageHeaderActions,
18
- BackStyleProvider,
19
- useBackStyle,
20
- type ModulePageProps,
21
- type ModuleNavItem,
22
- type ModuleNavVariant,
23
- type SubpageHeaderProps,
24
- type BackButtonStyle,
25
- } from './ModulePage'
26
- export { useModuleHeaderSlot, useModuleHeaderActionsSlot } from './AppShell'
27
- export { SaveBar, SaveBarProvider, useSaveBar, useBackHandler, type SaveBarRegistration } from './SaveBar'
28
- export {
29
- PageTransition,
30
- NavTransitionProvider,
31
- useNavTransition,
32
- type NavTransition,
33
- } from './PageTransition'
34
- export {
35
- ContentSplit,
36
- ContentSplitTrigger,
37
- type ContentSplitProps,
38
- type ContentSplitTriggerProps,
39
- } from './ContentSplit'
@@ -1,30 +0,0 @@
1
- import { useNavReferrer, navHistoryBack, routeModule } from '@fayz-ai/core'
2
-
3
- /**
4
- * Context-aware back target shared by all header/breadcrumb components.
5
- *
6
- * When the user arrived from a *different* module (tracked in the global
7
- * navigation history), a page's own onBack/parentLabel point at its local
8
- * parent — wrong for the referrer. In that case we return the referrer's label
9
- * and a back action that pops the browser history to the actual previous page.
10
- * Within the same module the page's own (more specific) values pass through
11
- * unchanged, so existing behavior is preserved.
12
- */
13
- export function useCrossModuleBack(onBack?: () => void, parentLabel?: string): {
14
- onBack?: () => void
15
- parentLabel?: string
16
- isCrossModule: boolean
17
- } {
18
- const currentPath = typeof window !== 'undefined'
19
- ? ((window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || '/')
20
- : '/'
21
- const referrer = useNavReferrer(currentPath)
22
- const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath)
23
- ? referrer
24
- : null
25
- return {
26
- onBack: cross ? () => navHistoryBack(onBack) : onBack,
27
- parentLabel: cross ? cross.label : parentLabel,
28
- isCrossModule: !!cross,
29
- }
30
- }
@@ -1,48 +0,0 @@
1
- import * as React from 'react'
2
- import * as AvatarPrimitive from '@radix-ui/react-avatar'
3
-
4
- import { cn } from '../utils/cn'
5
-
6
- const Avatar = React.forwardRef<
7
- React.ElementRef<typeof AvatarPrimitive.Root>,
8
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
9
- >(({ className, ...props }, ref) => (
10
- <AvatarPrimitive.Root
11
- ref={ref}
12
- className={cn(
13
- 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
14
- className
15
- )}
16
- {...props}
17
- />
18
- ))
19
- Avatar.displayName = AvatarPrimitive.Root.displayName
20
-
21
- const AvatarImage = React.forwardRef<
22
- React.ElementRef<typeof AvatarPrimitive.Image>,
23
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
24
- >(({ className, ...props }, ref) => (
25
- <AvatarPrimitive.Image
26
- ref={ref}
27
- className={cn('aspect-square h-full w-full', className)}
28
- {...props}
29
- />
30
- ))
31
- AvatarImage.displayName = AvatarPrimitive.Image.displayName
32
-
33
- const AvatarFallback = React.forwardRef<
34
- React.ElementRef<typeof AvatarPrimitive.Fallback>,
35
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
36
- >(({ className, ...props }, ref) => (
37
- <AvatarPrimitive.Fallback
38
- ref={ref}
39
- className={cn(
40
- 'flex h-full w-full items-center justify-center rounded-full bg-muted',
41
- className
42
- )}
43
- {...props}
44
- />
45
- ))
46
- AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
47
-
48
- export { Avatar, AvatarImage, AvatarFallback }