@fayz-ai/ui 0.2.0 → 0.5.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 (57) hide show
  1. package/dist/chunk-22J2RTMN.js +1397 -0
  2. package/dist/chunk-22J2RTMN.js.map +1 -0
  3. package/dist/chunk-2P7T6QQK.cjs +206 -0
  4. package/dist/chunk-2P7T6QQK.cjs.map +1 -0
  5. package/dist/chunk-7TBDPNLY.cjs +649 -0
  6. package/dist/chunk-7TBDPNLY.cjs.map +1 -0
  7. package/dist/chunk-CZ6X6PZT.js +1237 -0
  8. package/dist/chunk-CZ6X6PZT.js.map +1 -0
  9. package/dist/chunk-D6SXPF4F.cjs +397 -0
  10. package/dist/chunk-D6SXPF4F.cjs.map +1 -0
  11. package/dist/chunk-DNYVEOAX.js +11 -0
  12. package/dist/chunk-DNYVEOAX.js.map +1 -0
  13. package/dist/chunk-E526FHZ4.js +169 -0
  14. package/dist/chunk-E526FHZ4.js.map +1 -0
  15. package/dist/chunk-GE6XUREM.cjs +1277 -0
  16. package/dist/chunk-GE6XUREM.cjs.map +1 -0
  17. package/dist/chunk-GFH5HOCO.js +605 -0
  18. package/dist/chunk-GFH5HOCO.js.map +1 -0
  19. package/dist/chunk-IWBXYV4U.js +203 -0
  20. package/dist/chunk-IWBXYV4U.js.map +1 -0
  21. package/dist/chunk-MYZT2OG7.js +350 -0
  22. package/dist/chunk-MYZT2OG7.js.map +1 -0
  23. package/dist/chunk-O5F4LLSL.js +445 -0
  24. package/dist/chunk-O5F4LLSL.js.map +1 -0
  25. package/dist/chunk-TDGSY5W6.cjs +474 -0
  26. package/dist/chunk-TDGSY5W6.cjs.map +1 -0
  27. package/dist/chunk-VVVZO2LE.cjs +13 -0
  28. package/dist/chunk-VVVZO2LE.cjs.map +1 -0
  29. package/dist/chunk-W4XXXJRW.cjs +196 -0
  30. package/dist/chunk-W4XXXJRW.cjs.map +1 -0
  31. package/dist/chunk-YB5ZJKNG.cjs +1484 -0
  32. package/dist/chunk-YB5ZJKNG.cjs.map +1 -0
  33. package/dist/dashboard/index.cjs +102 -0
  34. package/dist/dashboard/index.cjs.map +1 -0
  35. package/dist/dashboard/index.js +5 -0
  36. package/dist/dashboard/index.js.map +1 -0
  37. package/dist/index.cjs +569 -4304
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.js +15 -4152
  40. package/dist/index.js.map +1 -1
  41. package/dist/layout/index.cjs +90 -0
  42. package/dist/layout/index.cjs.map +1 -0
  43. package/dist/layout/index.js +5 -0
  44. package/dist/layout/index.js.map +1 -0
  45. package/dist/preset.cjs +18 -0
  46. package/dist/preset.cjs.map +1 -0
  47. package/dist/preset.js +3 -0
  48. package/dist/preset.js.map +1 -0
  49. package/dist/primitives/index.cjs +347 -0
  50. package/dist/primitives/index.cjs.map +1 -0
  51. package/dist/primitives/index.js +6 -0
  52. package/dist/primitives/index.js.map +1 -0
  53. package/dist/theme/index.cjs +41 -0
  54. package/dist/theme/index.cjs.map +1 -0
  55. package/dist/theme/index.js +4 -0
  56. package/dist/theme/index.js.map +1 -0
  57. package/package.json +10 -10
@@ -0,0 +1,1237 @@
1
+ import { useCrossModuleBack, useBackHandler, SaveBarProvider, SaveBar } from './chunk-E526FHZ4.js';
2
+ import { cn } from './chunk-DNYVEOAX.js';
3
+ import * as React3 from 'react';
4
+ import { Workflow, Star, MessageSquare, LayoutDashboard, Award, Zap, Mic, UsersRound, Disc3, ListMusic, Eye, Music, Layers, Banknote, CalendarDays, Radio, PartyPopper, Wheat, Egg, Apple, LeafyGreen, LayoutTemplate, Heart, PawPrint, Cat, Dog, Search, List, Plus, Filter, Building2, Contact, Handshake, MapPin, UtensilsCrossed, Camera, Tag, Percent, Globe, MessageCircle, BookOpen, UserCog, Briefcase, ClipboardList, Wrench, Target, ShoppingCart, Megaphone, DollarSign, Mail, FileText, BarChart3, Package, Calendar, Bell, CreditCard, Settings, Users, Home, PanelLeftClose, PanelLeft, Menu, ChevronRight, ChevronLeft, TreePalm, UserPlus, ArrowDownRight, ArrowUpRight, Ruler, Warehouse, Wallet, Sparkles, AlertTriangle, Clock, CircleDollarSign, TrendingUp, Receipt, Landmark, TableProperties, SlidersHorizontal, Plug, ArrowLeftRight, ArrowUpCircle, ArrowDownCircle, ChevronDown } from 'lucide-react';
5
+ import * as Tooltip from '@radix-ui/react-tooltip';
6
+ import { create } from 'zustand';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import * as Popover from '@radix-ui/react-popover';
9
+ import { createPortal } from 'react-dom';
10
+ import { useTranslation } from '@fayz-ai/core';
11
+
12
+ var useLayoutStore = create((set) => ({
13
+ sidebarOpen: true,
14
+ sidebarCollapsed: false,
15
+ mobileDrawerOpen: false,
16
+ commandPaletteOpen: false,
17
+ toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
18
+ setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
19
+ toggleMobileDrawer: () => set((state) => ({ mobileDrawerOpen: !state.mobileDrawerOpen })),
20
+ setCommandPaletteOpen: (open) => set({ commandPaletteOpen: open })
21
+ }));
22
+ var ICON_MAP = {
23
+ Home,
24
+ Users,
25
+ Settings,
26
+ CreditCard,
27
+ Bell,
28
+ Calendar,
29
+ Package,
30
+ BarChart3,
31
+ FileText,
32
+ Mail,
33
+ DollarSign,
34
+ Megaphone,
35
+ ShoppingCart,
36
+ Target,
37
+ Wrench,
38
+ ClipboardList,
39
+ Briefcase,
40
+ UserCog,
41
+ BookOpen,
42
+ MessageCircle,
43
+ Globe,
44
+ Percent,
45
+ Tag,
46
+ Camera,
47
+ UtensilsCrossed,
48
+ MapPin,
49
+ Handshake,
50
+ Contact,
51
+ Building2,
52
+ Filter,
53
+ Plus,
54
+ List,
55
+ Search,
56
+ Dog,
57
+ Cat,
58
+ PawPrint,
59
+ Heart,
60
+ LayoutTemplate,
61
+ LeafyGreen,
62
+ Apple,
63
+ Egg,
64
+ Wheat,
65
+ PartyPopper,
66
+ Radio,
67
+ CalendarDays,
68
+ Banknote,
69
+ Layers,
70
+ Music,
71
+ Eye,
72
+ ListMusic,
73
+ Disc3,
74
+ UsersRound,
75
+ Mic,
76
+ Zap,
77
+ Award,
78
+ LayoutDashboard,
79
+ MessageSquare,
80
+ Star,
81
+ Workflow
82
+ };
83
+ function getIcon(name) {
84
+ return ICON_MAP[name] ?? Home;
85
+ }
86
+ function NavItem({
87
+ item,
88
+ collapsed,
89
+ isActive,
90
+ onNavigate
91
+ }) {
92
+ const Icon = getIcon(item.icon);
93
+ const content = /* @__PURE__ */ jsxs(
94
+ "button",
95
+ {
96
+ onClick: () => onNavigate(item.route),
97
+ className: cn(
98
+ "flex w-full items-center gap-3 rounded-md px-2.5 py-1.5 text-[13px] font-medium transition-colors",
99
+ "hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
100
+ isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
101
+ collapsed && "justify-center px-2"
102
+ ),
103
+ children: [
104
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }),
105
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
106
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: item.label }),
107
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-auto rounded-full bg-primary px-2 py-0.5 text-xs font-semibold text-primary-foreground", children: item.badge })
108
+ ] })
109
+ ]
110
+ }
111
+ );
112
+ if (collapsed) {
113
+ return /* @__PURE__ */ jsxs(Tooltip.Root, { delayDuration: 0, children: [
114
+ /* @__PURE__ */ jsx(Tooltip.Trigger, { asChild: true, children: content }),
115
+ /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsxs(
116
+ Tooltip.Content,
117
+ {
118
+ side: "right",
119
+ sideOffset: 8,
120
+ className: "fayz-glass-surface z-50 rounded-md bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md",
121
+ children: [
122
+ item.label,
123
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-2 rounded-full bg-primary px-1.5 py-0.5 text-xs text-primary-foreground", children: item.badge }),
124
+ /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "fill-popover" })
125
+ ]
126
+ }
127
+ ) })
128
+ ] });
129
+ }
130
+ return content;
131
+ }
132
+ function NavGroup({
133
+ item,
134
+ collapsed,
135
+ currentPath,
136
+ onNavigate
137
+ }) {
138
+ const [open, setOpen] = React3.useState(
139
+ () => item.children?.some((c) => currentPath === c.route || currentPath.startsWith(c.route + "/")) ?? false
140
+ );
141
+ const Icon = getIcon(item.icon);
142
+ const isChildActive = item.children?.some(
143
+ (c) => currentPath === c.route || currentPath.startsWith(c.route + "/")
144
+ );
145
+ if (collapsed) {
146
+ return /* @__PURE__ */ jsxs(Tooltip.Root, { delayDuration: 0, children: [
147
+ /* @__PURE__ */ jsx(Tooltip.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
148
+ "button",
149
+ {
150
+ onClick: () => onNavigate(item.route),
151
+ className: cn(
152
+ "flex w-full items-center justify-center rounded-md px-2 py-2 text-sm font-medium transition-colors",
153
+ "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
154
+ isChildActive ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-muted"
155
+ ),
156
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" })
157
+ }
158
+ ) }),
159
+ /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsxs(Tooltip.Content, { side: "right", sideOffset: 8, className: "fayz-glass-surface z-50 rounded-md bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md", children: [
160
+ item.label,
161
+ /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "fill-popover" })
162
+ ] }) })
163
+ ] });
164
+ }
165
+ return /* @__PURE__ */ jsxs("div", { children: [
166
+ /* @__PURE__ */ jsxs(
167
+ "button",
168
+ {
169
+ onClick: () => setOpen(!open),
170
+ className: cn(
171
+ "flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
172
+ "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
173
+ isChildActive ? "text-sidebar-accent-foreground" : "text-sidebar-muted"
174
+ ),
175
+ children: [
176
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }),
177
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: item.label }),
178
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180") })
179
+ ]
180
+ }
181
+ ),
182
+ open && /* @__PURE__ */ jsx("div", { className: "ml-4 mt-0.5 space-y-0.5 border-l border-sidebar-border/50 pl-3", children: item.children?.map((child) => {
183
+ const ChildIcon = getIcon(child.icon);
184
+ const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
185
+ return /* @__PURE__ */ jsxs(
186
+ "button",
187
+ {
188
+ onClick: () => onNavigate(child.route),
189
+ className: cn(
190
+ "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors",
191
+ isActive ? "font-medium text-sidebar-accent-foreground" : "text-sidebar-muted hover:text-sidebar-accent-foreground"
192
+ ),
193
+ children: [
194
+ /* @__PURE__ */ jsx(ChildIcon, { className: "h-3.5 w-3.5 shrink-0" }),
195
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: child.label })
196
+ ]
197
+ },
198
+ child.id
199
+ );
200
+ }) })
201
+ ] });
202
+ }
203
+ function Sidebar({
204
+ navigation,
205
+ logo,
206
+ collapsed,
207
+ onToggle,
208
+ onNavigate,
209
+ currentPath: currentPathProp = "/",
210
+ user: _user,
211
+ orgSwitcher,
212
+ topContent,
213
+ footerContent,
214
+ borderless,
215
+ userMenuSlot,
216
+ notificationSlot,
217
+ unreadCount = 0
218
+ }) {
219
+ const currentPath = currentPathProp;
220
+ const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
221
+ const mainItems = navigation.filter((item) => item.section === "main");
222
+ const secondaryItems = navigation.filter((item) => item.section === "secondary");
223
+ const settingsItems = navigation.filter((item) => item.section === "settings");
224
+ const handleNavigate = (route) => {
225
+ onNavigate?.(route);
226
+ };
227
+ return /* @__PURE__ */ jsx(Tooltip.Provider, { children: /* @__PURE__ */ jsxs(
228
+ "aside",
229
+ {
230
+ "data-print": "hide",
231
+ className: cn(
232
+ "flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
233
+ !borderless && "border-r border-sidebar-border",
234
+ collapsed ? "w-16" : "w-64"
235
+ ),
236
+ children: [
237
+ /* @__PURE__ */ jsxs(
238
+ "div",
239
+ {
240
+ className: cn(
241
+ "flex h-14 items-center justify-between px-4",
242
+ !borderless && "border-b border-sidebar-border",
243
+ collapsed && "justify-center px-2"
244
+ ),
245
+ children: [
246
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
247
+ logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
248
+ /* @__PURE__ */ jsx(
249
+ "button",
250
+ {
251
+ onClick: onToggle,
252
+ className: "ml-auto rounded-md p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
253
+ "aria-label": "Collapse sidebar",
254
+ children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
255
+ }
256
+ )
257
+ ] }),
258
+ collapsed && /* @__PURE__ */ jsx(
259
+ "button",
260
+ {
261
+ onClick: onToggle,
262
+ className: "rounded-md p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
263
+ "aria-label": "Expand sidebar",
264
+ children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
265
+ }
266
+ )
267
+ ]
268
+ }
269
+ ),
270
+ /* @__PURE__ */ jsxs("nav", { className: "flex-1 space-y-1 overflow-y-auto p-2", children: [
271
+ topContent && /* @__PURE__ */ jsx("div", { className: "mb-4 space-y-2", children: topContent }),
272
+ mainItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-1", children: mainItems.map(
273
+ (item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, currentPath, onNavigate: handleNavigate }, item.id) : /* @__PURE__ */ jsx(
274
+ NavItem,
275
+ {
276
+ item,
277
+ collapsed,
278
+ isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
279
+ onNavigate: handleNavigate
280
+ },
281
+ item.id
282
+ )
283
+ ) }),
284
+ secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6 space-y-1", children: secondaryItems.map(
285
+ (item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, currentPath, onNavigate: handleNavigate }, item.id) : /* @__PURE__ */ jsx(
286
+ NavItem,
287
+ {
288
+ item,
289
+ collapsed,
290
+ isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
291
+ onNavigate: handleNavigate
292
+ },
293
+ item.id
294
+ )
295
+ ) })
296
+ ] }),
297
+ settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
298
+ NavItem,
299
+ {
300
+ item,
301
+ collapsed,
302
+ isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
303
+ onNavigate: handleNavigate
304
+ },
305
+ item.id
306
+ )) }),
307
+ footerContent && /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsx("div", { className: "space-y-2", children: footerContent }) }),
308
+ orgSwitcher && React3.isValidElement(orgSwitcher) ? React3.cloneElement(orgSwitcher, { collapsed }) : orgSwitcher,
309
+ /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
310
+ "flex items-center rounded-md",
311
+ collapsed ? "flex-col gap-1" : "gap-1 px-1"
312
+ ), children: [
313
+ userMenuSlot,
314
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
315
+ /* @__PURE__ */ jsx(
316
+ "button",
317
+ {
318
+ onClick: () => setCommandPaletteOpen(true),
319
+ className: "inline-flex shrink-0 items-center justify-center rounded-md p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
320
+ "aria-label": "Search",
321
+ children: /* @__PURE__ */ jsx(Search, { className: "h-4 w-4" })
322
+ }
323
+ ),
324
+ notificationSlot ?? /* @__PURE__ */ jsxs(
325
+ "button",
326
+ {
327
+ className: "relative inline-flex shrink-0 items-center justify-center rounded-md p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
328
+ "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
329
+ children: [
330
+ /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
331
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
332
+ ]
333
+ }
334
+ )
335
+ ] }) })
336
+ ]
337
+ }
338
+ ) });
339
+ }
340
+ function TopbarUserMenu({
341
+ user,
342
+ onSignOut,
343
+ onProfile,
344
+ onSettings,
345
+ onBilling,
346
+ userMenuExtras
347
+ }) {
348
+ const [open, setOpen] = React3.useState(false);
349
+ if (!user) return null;
350
+ const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
351
+ const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
352
+ "button",
353
+ {
354
+ onClick: () => {
355
+ setOpen(false);
356
+ onClick();
357
+ },
358
+ 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",
359
+ children: [
360
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
361
+ /* @__PURE__ */ jsx("span", { children: label })
362
+ ]
363
+ },
364
+ label
365
+ ) : null;
366
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
367
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
368
+ "button",
369
+ {
370
+ "aria-label": "User menu",
371
+ 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",
372
+ children: user.avatarUrl ? (
373
+ // eslint-disable-next-line @next/next/no-img-element
374
+ /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
375
+ ) : initial
376
+ }
377
+ ) }),
378
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
379
+ Popover.Content,
380
+ {
381
+ align: "end",
382
+ sideOffset: 8,
383
+ 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",
384
+ children: [
385
+ /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
386
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
387
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
388
+ ] }),
389
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
390
+ item("Perfil", onProfile, UserCog),
391
+ item("Configura\xE7\xF5es", onSettings, Settings),
392
+ item("Cobran\xE7a", onBilling, CreditCard),
393
+ userMenuExtras?.map((e) => item(e.label, e.onClick)),
394
+ onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
395
+ /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
396
+ item("Sair", onSignOut)
397
+ ] })
398
+ ] })
399
+ ]
400
+ }
401
+ ) })
402
+ ] });
403
+ }
404
+ var ICON_MAP2 = {
405
+ Home: Home,
406
+ Users: Users,
407
+ Settings: Settings,
408
+ CreditCard: CreditCard,
409
+ Bell: Bell,
410
+ Calendar: Calendar,
411
+ Package: Package,
412
+ BarChart3: BarChart3,
413
+ FileText: FileText,
414
+ Mail: Mail,
415
+ DollarSign: DollarSign,
416
+ Megaphone: Megaphone,
417
+ ShoppingCart: ShoppingCart,
418
+ Target: Target,
419
+ Wrench: Wrench,
420
+ ClipboardList: ClipboardList,
421
+ Briefcase: Briefcase,
422
+ UserCog: UserCog,
423
+ BookOpen: BookOpen,
424
+ MessageCircle: MessageCircle,
425
+ Globe: Globe,
426
+ Percent: Percent,
427
+ Tag: Tag,
428
+ Camera: Camera,
429
+ UtensilsCrossed: UtensilsCrossed,
430
+ Search: Search,
431
+ MapPin: MapPin,
432
+ Handshake: Handshake,
433
+ Contact: Contact,
434
+ Building2: Building2,
435
+ ChevronDown: ChevronDown,
436
+ Filter: Filter,
437
+ Plus: Plus,
438
+ List: List,
439
+ Dog: Dog,
440
+ Cat: Cat,
441
+ PawPrint: PawPrint,
442
+ Heart: Heart,
443
+ LayoutTemplate: LayoutTemplate,
444
+ LeafyGreen: LeafyGreen,
445
+ Apple: Apple,
446
+ Egg: Egg,
447
+ Wheat: Wheat,
448
+ ArrowDownCircle,
449
+ ArrowUpCircle,
450
+ ArrowLeftRight,
451
+ Plug,
452
+ SlidersHorizontal,
453
+ TableProperties,
454
+ Landmark,
455
+ Receipt,
456
+ TrendingUp,
457
+ CircleDollarSign,
458
+ Clock,
459
+ AlertTriangle,
460
+ Sparkles,
461
+ Wallet,
462
+ Warehouse,
463
+ Ruler,
464
+ ArrowUpRight,
465
+ ArrowDownRight,
466
+ UserPlus,
467
+ TreePalm,
468
+ PartyPopper: PartyPopper,
469
+ Radio: Radio,
470
+ CalendarDays: CalendarDays,
471
+ Banknote: Banknote,
472
+ Layers: Layers,
473
+ Music: Music,
474
+ Eye: Eye,
475
+ ListMusic: ListMusic,
476
+ Disc3: Disc3,
477
+ UsersRound: UsersRound,
478
+ Mic: Mic
479
+ };
480
+ function getIcon2(name) {
481
+ return ICON_MAP2[name] ?? Home;
482
+ }
483
+ function NavDropdown({ item, currentPath, onNavigate }) {
484
+ const [open, setOpen] = React3.useState(false);
485
+ const Icon = getIcon2(item.icon);
486
+ const isChildActive = item.children?.some(
487
+ (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
488
+ );
489
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
490
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
491
+ "button",
492
+ {
493
+ "data-nav-active": isChildActive || void 0,
494
+ className: cn(
495
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
496
+ isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
497
+ ),
498
+ children: [
499
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
500
+ /* @__PURE__ */ jsx("span", { children: item.label }),
501
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
502
+ ]
503
+ }
504
+ ) }),
505
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
506
+ Popover.Content,
507
+ {
508
+ align: "start",
509
+ sideOffset: 4,
510
+ 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",
511
+ children: item.children?.map((child) => {
512
+ const ChildIcon = getIcon2(child.icon);
513
+ const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
514
+ return /* @__PURE__ */ jsxs(
515
+ "button",
516
+ {
517
+ onClick: () => {
518
+ onNavigate(child.route);
519
+ setOpen(false);
520
+ },
521
+ className: cn(
522
+ "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
523
+ isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
524
+ ),
525
+ children: [
526
+ /* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
527
+ /* @__PURE__ */ jsx("span", { children: child.label })
528
+ ]
529
+ },
530
+ child.id
531
+ );
532
+ })
533
+ }
534
+ ) })
535
+ ] });
536
+ }
537
+ function Topbar({
538
+ navigation,
539
+ logo,
540
+ user,
541
+ onMenuClick,
542
+ leftContent,
543
+ rightContent,
544
+ currentPath = "/",
545
+ onNavigate,
546
+ searchPlaceholder = "Search...",
547
+ onSignOut,
548
+ onProfile,
549
+ onSettings,
550
+ onBilling,
551
+ userMenuExtras
552
+ }) {
553
+ const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
554
+ const mainNav = navigation.filter((item) => item.section === "main");
555
+ const secondaryNav = navigation.filter((item) => item.section === "secondary");
556
+ const allNav = [...mainNav, ...secondaryNav];
557
+ const handleNavigate = (route) => {
558
+ onNavigate?.(route);
559
+ };
560
+ return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
561
+ /* @__PURE__ */ jsx("div", { className: "border-b border-sidebar-border bg-sidebar text-sidebar-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
562
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
563
+ /* @__PURE__ */ jsx(
564
+ "button",
565
+ {
566
+ onClick: onMenuClick,
567
+ 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",
568
+ "aria-label": "Open menu",
569
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
570
+ }
571
+ ),
572
+ /* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
573
+ ] }),
574
+ /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
575
+ "button",
576
+ {
577
+ onClick: () => setCommandPaletteOpen(true),
578
+ 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:bg-card",
579
+ children: [
580
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
581
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
582
+ /* @__PURE__ */ jsx("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", children: "\u2318K" })
583
+ ]
584
+ }
585
+ ) }),
586
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
587
+ leftContent,
588
+ rightContent,
589
+ /* @__PURE__ */ jsx(
590
+ TopbarUserMenu,
591
+ {
592
+ user,
593
+ onSignOut,
594
+ onProfile,
595
+ onSettings,
596
+ onBilling,
597
+ userMenuExtras
598
+ }
599
+ )
600
+ ] })
601
+ ] }) }),
602
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
603
+ if (!el) return;
604
+ const active = el.querySelector('[data-nav-active="true"]');
605
+ if (active) {
606
+ const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
607
+ el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
608
+ }
609
+ }, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-hide", children: allNav.map((item) => {
610
+ if (item.children && item.children.length > 0) {
611
+ return /* @__PURE__ */ jsx(
612
+ NavDropdown,
613
+ {
614
+ item,
615
+ currentPath,
616
+ onNavigate: handleNavigate
617
+ },
618
+ item.id
619
+ );
620
+ }
621
+ const Icon = getIcon2(item.icon);
622
+ const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
623
+ return /* @__PURE__ */ jsxs(
624
+ "button",
625
+ {
626
+ "data-nav-active": isActive || void 0,
627
+ onClick: () => handleNavigate(item.route),
628
+ className: cn(
629
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
630
+ isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
631
+ ),
632
+ children: [
633
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
634
+ /* @__PURE__ */ jsx("span", { children: item.label }),
635
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
636
+ ]
637
+ },
638
+ item.id
639
+ );
640
+ }) }) }) })
641
+ ] });
642
+ }
643
+ var ModuleHeaderSlotContext = React3.createContext(null);
644
+ function useModuleHeaderSlot() {
645
+ return React3.useContext(ModuleHeaderSlotContext);
646
+ }
647
+ var ModuleHeaderActionsSlotContext = React3.createContext(null);
648
+ function useModuleHeaderActionsSlot() {
649
+ return React3.useContext(ModuleHeaderActionsSlotContext);
650
+ }
651
+ function SidebarLayout({
652
+ navigation = [],
653
+ logo,
654
+ user,
655
+ children,
656
+ onNavigate,
657
+ onSignOut,
658
+ onProfile,
659
+ onSettings,
660
+ onBilling,
661
+ userMenuExtras: _userMenuExtras,
662
+ orgSwitcher,
663
+ sidebarTopContent,
664
+ sidebarFooterContent,
665
+ frame,
666
+ currentPath,
667
+ pageTitle,
668
+ topbarStart,
669
+ topbarEnd,
670
+ userMenuSlot,
671
+ notificationSlot,
672
+ unreadCount = 0
673
+ }) {
674
+ const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
675
+ const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
676
+ const [mobileMenuOpen, setMobileMenuOpen] = React3.useState(false);
677
+ const [headerSlot, setHeaderSlot] = React3.useState(null);
678
+ const [actionsSlot, setActionsSlot] = React3.useState(null);
679
+ return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsxs("div", { className: "flex h-screen overflow-hidden bg-background", children: [
680
+ /* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(
681
+ Sidebar,
682
+ {
683
+ navigation,
684
+ logo,
685
+ collapsed: sidebarCollapsed,
686
+ onToggle: () => setSidebarCollapsed(!sidebarCollapsed),
687
+ onNavigate,
688
+ currentPath,
689
+ user,
690
+ onSignOut,
691
+ onProfile,
692
+ onSettings,
693
+ onBilling,
694
+ orgSwitcher,
695
+ topContent: sidebarTopContent,
696
+ footerContent: sidebarFooterContent,
697
+ borderless: false,
698
+ userMenuSlot,
699
+ notificationSlot,
700
+ unreadCount
701
+ }
702
+ ) }),
703
+ /* @__PURE__ */ jsxs("div", { className: cn(
704
+ "relative flex flex-1 flex-col overflow-hidden",
705
+ frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-card"
706
+ ), children: [
707
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border bg-card px-4 py-2", children: [
708
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
709
+ /* @__PURE__ */ jsx(
710
+ "button",
711
+ {
712
+ onClick: () => setMobileMenuOpen(true),
713
+ className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
714
+ "aria-label": "Open menu",
715
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
716
+ }
717
+ ),
718
+ pageTitle && /* @__PURE__ */ jsx("h2", { className: "font-semibold text-lg", children: pageTitle }),
719
+ topbarStart,
720
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
721
+ ] }),
722
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
723
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
724
+ topbarEnd
725
+ ] })
726
+ ] }),
727
+ /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children }),
728
+ /* @__PURE__ */ jsx(SaveBar, {})
729
+ ] }),
730
+ mobileMenuOpen && /* @__PURE__ */ jsx(
731
+ MobileOverlay,
732
+ {
733
+ navigation,
734
+ logo,
735
+ user,
736
+ currentPath,
737
+ onNavigate,
738
+ onSignOut,
739
+ onProfile,
740
+ onSettings,
741
+ onBilling,
742
+ orgSwitcher,
743
+ topContent: sidebarTopContent,
744
+ footerContent: sidebarFooterContent,
745
+ userMenuSlot,
746
+ notificationSlot,
747
+ unreadCount,
748
+ onClose: () => setMobileMenuOpen(false)
749
+ }
750
+ )
751
+ ] }) }) }) });
752
+ }
753
+ function TopbarLayout({
754
+ navigation = [],
755
+ logo,
756
+ user,
757
+ children,
758
+ onNavigate,
759
+ onSignOut,
760
+ onProfile,
761
+ onSettings,
762
+ onBilling,
763
+ userMenuExtras,
764
+ frame,
765
+ currentPath,
766
+ topbarStart,
767
+ topbarEnd
768
+ }) {
769
+ const [mobileMenuOpen, setMobileMenuOpen] = React3.useState(false);
770
+ return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-background", children: [
771
+ /* @__PURE__ */ jsx(
772
+ Topbar,
773
+ {
774
+ navigation,
775
+ logo,
776
+ user,
777
+ currentPath,
778
+ onNavigate,
779
+ onSignOut,
780
+ onProfile,
781
+ onSettings,
782
+ onBilling,
783
+ userMenuExtras,
784
+ leftContent: topbarStart,
785
+ rightContent: topbarEnd,
786
+ onMenuClick: () => setMobileMenuOpen(true)
787
+ }
788
+ ),
789
+ /* @__PURE__ */ jsx("main", { className: cn("flex-1 overflow-y-auto", frame && "md:p-2"), children: /* @__PURE__ */ jsx("div", { className: cn("min-h-full", frame && "md:rounded-xl md:border md:border-border md:bg-card"), children }) }),
790
+ /* @__PURE__ */ jsx(SaveBar, {}),
791
+ mobileMenuOpen && /* @__PURE__ */ jsx(
792
+ MobileOverlay,
793
+ {
794
+ navigation,
795
+ logo,
796
+ user,
797
+ currentPath,
798
+ onNavigate,
799
+ onSignOut,
800
+ onProfile,
801
+ onSettings,
802
+ onBilling,
803
+ userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
804
+ onClose: () => setMobileMenuOpen(false)
805
+ }
806
+ )
807
+ ] }) });
808
+ }
809
+ function DrawerUserRow({ user }) {
810
+ const label = user.fullName || user.email;
811
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 px-1", children: [
812
+ user.avatarUrl ? /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-7 w-7 shrink-0 rounded-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-semibold text-primary-foreground", children: (label || "?").charAt(0).toUpperCase() }),
813
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-sidebar-foreground", children: label })
814
+ ] });
815
+ }
816
+ function MinimalLayout({
817
+ logo,
818
+ user: _user,
819
+ children,
820
+ headerEnd
821
+ }) {
822
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-screen flex-col", children: [
823
+ /* @__PURE__ */ jsxs("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-border bg-card px-4 md:px-6", children: [
824
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
825
+ headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
826
+ ] }),
827
+ /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children })
828
+ ] });
829
+ }
830
+ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
831
+ const [visible, setVisible] = React3.useState(false);
832
+ React3.useEffect(() => {
833
+ const id = requestAnimationFrame(() => setVisible(true));
834
+ return () => cancelAnimationFrame(id);
835
+ }, []);
836
+ const close = React3.useCallback(() => {
837
+ setVisible(false);
838
+ window.setTimeout(onClose, 250);
839
+ }, [onClose]);
840
+ React3.useEffect(() => {
841
+ const previous = document.body.style.overflow;
842
+ document.body.style.overflow = "hidden";
843
+ return () => {
844
+ document.body.style.overflow = previous;
845
+ };
846
+ }, []);
847
+ React3.useEffect(() => {
848
+ const onKey = (e) => {
849
+ if (e.key === "Escape") close();
850
+ };
851
+ window.addEventListener("keydown", onKey);
852
+ return () => window.removeEventListener("keydown", onKey);
853
+ }, [close]);
854
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[60] md:hidden", children: [
855
+ /* @__PURE__ */ jsx(
856
+ "div",
857
+ {
858
+ className: cn(
859
+ "absolute inset-0 bg-black/50 transition-opacity duration-300 ease-out",
860
+ visible ? "opacity-100" : "opacity-0"
861
+ ),
862
+ onClick: close
863
+ }
864
+ ),
865
+ /* @__PURE__ */ jsx(
866
+ "div",
867
+ {
868
+ className: cn(
869
+ "absolute inset-y-0 left-0 shadow-2xl transition-transform duration-300 ease-out will-change-transform",
870
+ visible ? "translate-x-0" : "-translate-x-full"
871
+ ),
872
+ children: /* @__PURE__ */ jsx(
873
+ Sidebar,
874
+ {
875
+ ...sidebarProps,
876
+ collapsed: false,
877
+ onToggle: close,
878
+ borderless: true,
879
+ onNavigate: (route) => {
880
+ onNavigate?.(route);
881
+ close();
882
+ }
883
+ }
884
+ )
885
+ }
886
+ )
887
+ ] });
888
+ }
889
+ function AppShell({
890
+ variant,
891
+ contentFrame,
892
+ navigation = [],
893
+ logo,
894
+ user,
895
+ children,
896
+ onNavigate,
897
+ onSignOut,
898
+ onProfile,
899
+ onSettings,
900
+ onBilling,
901
+ pageTitle,
902
+ currentPath,
903
+ userMenuExtras,
904
+ orgSwitcher,
905
+ topbarStart,
906
+ topbarEnd,
907
+ sidebarTopContent,
908
+ sidebarFooterContent,
909
+ userMenuSlot,
910
+ notificationSlot,
911
+ unreadCount = 0
912
+ }) {
913
+ switch (variant) {
914
+ case "sidebar":
915
+ return /* @__PURE__ */ jsx(
916
+ SidebarLayout,
917
+ {
918
+ navigation,
919
+ logo,
920
+ user,
921
+ onNavigate,
922
+ onSignOut,
923
+ onProfile,
924
+ onSettings,
925
+ onBilling,
926
+ userMenuExtras,
927
+ orgSwitcher,
928
+ sidebarTopContent,
929
+ sidebarFooterContent,
930
+ frame: contentFrame,
931
+ currentPath,
932
+ pageTitle,
933
+ topbarStart,
934
+ topbarEnd,
935
+ userMenuSlot,
936
+ notificationSlot,
937
+ unreadCount,
938
+ children
939
+ }
940
+ );
941
+ case "topbar":
942
+ return /* @__PURE__ */ jsx(
943
+ TopbarLayout,
944
+ {
945
+ navigation,
946
+ logo,
947
+ user,
948
+ onNavigate,
949
+ onSignOut,
950
+ onProfile,
951
+ onSettings,
952
+ onBilling,
953
+ userMenuExtras,
954
+ frame: contentFrame,
955
+ currentPath,
956
+ topbarStart,
957
+ topbarEnd,
958
+ children
959
+ }
960
+ );
961
+ case "minimal":
962
+ return /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
963
+ default: {
964
+ return null;
965
+ }
966
+ }
967
+ }
968
+ var NavTransitionContext = React3.createContext("slide");
969
+ function NavTransitionProvider({ value, children }) {
970
+ return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
971
+ }
972
+ function useNavTransition() {
973
+ return React3.useContext(NavTransitionContext);
974
+ }
975
+ function resolveTransitionClass(transition, direction) {
976
+ if (transition === "none") return "";
977
+ if (transition === "fade") return "saas-page-enter";
978
+ if (direction === "back") return "saas-nav-back";
979
+ if (direction === "forward") return "saas-nav-forward";
980
+ return "saas-page-enter";
981
+ }
982
+ function PageTransition({
983
+ transitionKey,
984
+ direction,
985
+ className,
986
+ children
987
+ }) {
988
+ const transition = useNavTransition();
989
+ const animClass = resolveTransitionClass(transition, direction);
990
+ return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
991
+ }
992
+ function PageHeaderActions({ children }) {
993
+ const slot = useModuleHeaderActionsSlot();
994
+ if (slot) return createPortal(children, slot);
995
+ return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
996
+ }
997
+ var ModuleLayoutContext = React3.createContext("rail");
998
+ function ModuleLayoutProvider({ variant, children }) {
999
+ return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
1000
+ }
1001
+ function useModuleLayout() {
1002
+ return React3.useContext(ModuleLayoutContext);
1003
+ }
1004
+ function NavItem2({ item }) {
1005
+ const [expanded, setExpanded] = React3.useState(true);
1006
+ const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
1007
+ const hasChildren = item.children && item.children.length > 0;
1008
+ const hasActiveChild = hasChildren && item.children.some((c) => c.active);
1009
+ const isActive = item.active || hasActiveChild;
1010
+ return /* @__PURE__ */ jsxs("div", { children: [
1011
+ /* @__PURE__ */ jsxs(
1012
+ "button",
1013
+ {
1014
+ type: "button",
1015
+ onClick: () => hasChildren ? setExpanded(!expanded) : item.onClick?.(),
1016
+ className: cn(
1017
+ "flex items-center gap-2 w-full rounded-md px-2.5 py-1.5 text-sm transition-colors",
1018
+ isActive ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
1019
+ ),
1020
+ children: [
1021
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }),
1022
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: item.label }),
1023
+ hasChildren && (expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 opacity-50" }))
1024
+ ]
1025
+ }
1026
+ ),
1027
+ hasChildren && expanded && /* @__PURE__ */ jsx("div", { className: "ml-4 mt-0.5 space-y-0.5 border-l pl-2", children: item.children.map((child) => /* @__PURE__ */ jsx(
1028
+ "button",
1029
+ {
1030
+ type: "button",
1031
+ onClick: child.onClick,
1032
+ className: cn(
1033
+ "flex items-center w-full rounded-md px-2.5 py-1 text-xs transition-colors",
1034
+ child.active ? "text-primary font-medium bg-primary/5" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
1035
+ ),
1036
+ children: child.label
1037
+ },
1038
+ child.id
1039
+ )) })
1040
+ ] });
1041
+ }
1042
+ function MobileTabs({ nav }) {
1043
+ const scrollRef = React3.useRef(null);
1044
+ const [expandedId, setExpandedId] = React3.useState(null);
1045
+ React3.useEffect(() => {
1046
+ const container = scrollRef.current;
1047
+ if (!container) return;
1048
+ const activeEl = container.querySelector('[data-active="true"]');
1049
+ if (activeEl) {
1050
+ const left = activeEl.offsetLeft - container.offsetWidth / 2 + activeEl.offsetWidth / 2;
1051
+ container.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
1052
+ }
1053
+ }, [nav]);
1054
+ return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
1055
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-hide", children: nav.map((item) => {
1056
+ const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
1057
+ const hasChildren = item.children && item.children.length > 0;
1058
+ const isActive = item.active || item.children?.some((c) => c.active);
1059
+ const isExpanded = expandedId === item.id;
1060
+ return /* @__PURE__ */ jsxs(
1061
+ "button",
1062
+ {
1063
+ type: "button",
1064
+ "data-active": isActive || void 0,
1065
+ onClick: () => {
1066
+ if (hasChildren) {
1067
+ setExpandedId(isExpanded ? null : item.id);
1068
+ } else {
1069
+ item.onClick?.();
1070
+ setExpandedId(null);
1071
+ }
1072
+ },
1073
+ className: cn(
1074
+ "flex items-center gap-1.5 whitespace-nowrap rounded-full px-3 py-1.5 text-xs font-medium transition-colors shrink-0",
1075
+ isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
1076
+ ),
1077
+ children: [
1078
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
1079
+ item.label,
1080
+ hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
1081
+ ]
1082
+ },
1083
+ item.id
1084
+ );
1085
+ }) }),
1086
+ expandedId && (() => {
1087
+ const item = nav.find((n) => n.id === expandedId);
1088
+ if (!item?.children) return null;
1089
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-1 px-2 pb-2 animate-in fade-in-0 slide-in-from-top-1", children: item.children.map((child) => /* @__PURE__ */ jsx(
1090
+ "button",
1091
+ {
1092
+ type: "button",
1093
+ onClick: () => {
1094
+ child.onClick?.();
1095
+ setExpandedId(null);
1096
+ },
1097
+ className: cn(
1098
+ "whitespace-nowrap rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors shrink-0",
1099
+ child.active ? "bg-foreground/10 text-foreground" : "text-muted-foreground hover:text-foreground"
1100
+ ),
1101
+ children: child.label
1102
+ },
1103
+ child.id
1104
+ )) });
1105
+ })()
1106
+ ] });
1107
+ }
1108
+ var BackStyleContext = React3.createContext("link");
1109
+ function BackStyleProvider({ style, children }) {
1110
+ return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
1111
+ }
1112
+ function useBackStyle() {
1113
+ return React3.useContext(BackStyleContext);
1114
+ }
1115
+ function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle }) {
1116
+ const t = useTranslation();
1117
+ const appStyle = useBackStyle();
1118
+ const { onBack: effectiveOnBack, parentLabel: effectiveParentLabel } = useCrossModuleBack(onBack, parentLabel);
1119
+ useBackHandler(effectiveOnBack);
1120
+ const Icon = icon ? ICON_MAP2[icon] ?? null : null;
1121
+ const style = !effectiveOnBack ? "icon" : backStyle ?? appStyle;
1122
+ const effectiveStyle = style !== "icon" && !effectiveParentLabel ? "icon" : style;
1123
+ const titleBlock = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
1124
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1125
+ effectiveStyle === "icon" && effectiveOnBack && /* @__PURE__ */ jsx(
1126
+ "button",
1127
+ {
1128
+ onClick: effectiveOnBack,
1129
+ className: "flex h-8 w-8 items-center justify-center rounded-lg border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors",
1130
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
1131
+ }
1132
+ ),
1133
+ Icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-muted", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }) }),
1134
+ /* @__PURE__ */ jsxs("div", { children: [
1135
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: title }),
1136
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
1137
+ ] })
1138
+ ] }),
1139
+ actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
1140
+ ] });
1141
+ if (effectiveStyle === "breadcrumb") {
1142
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 mb-6", children: [
1143
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
1144
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
1145
+ /* @__PURE__ */ jsx("button", { onClick: effectiveOnBack, className: "hover:text-foreground transition-colors", children: effectiveParentLabel }),
1146
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" }),
1147
+ /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: title })
1148
+ ] }),
1149
+ actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
1150
+ ] }),
1151
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground -mt-2", children: subtitle })
1152
+ ] });
1153
+ }
1154
+ if (effectiveStyle === "link") {
1155
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 mb-6", children: [
1156
+ /* @__PURE__ */ jsxs(
1157
+ "button",
1158
+ {
1159
+ onClick: effectiveOnBack,
1160
+ className: "inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors",
1161
+ children: [
1162
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
1163
+ t("crud.detail.backTo", { entities: effectiveParentLabel })
1164
+ ]
1165
+ }
1166
+ ),
1167
+ titleBlock
1168
+ ] });
1169
+ }
1170
+ return /* @__PURE__ */ jsx("div", { className: "space-y-4 mb-6", children: titleBlock });
1171
+ }
1172
+ function ModuleHeader({ nav }) {
1173
+ const slot = useModuleHeaderSlot();
1174
+ function defaultChild(item) {
1175
+ const kids = item.children ?? [];
1176
+ return kids.find((c) => /list/i.test(c.id) || /^list$/i.test(c.label)) ?? kids[kids.length - 1];
1177
+ }
1178
+ const content = /* @__PURE__ */ jsx("div", { className: "flex w-full min-w-0 items-center gap-4", children: /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-center gap-0.5 overflow-x-auto scrollbar-hide", children: nav.map((item) => {
1179
+ const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
1180
+ const hasChildren = item.children && item.children.length > 0;
1181
+ const isActive = item.active || item.children?.some((c) => c.active);
1182
+ return /* @__PURE__ */ jsxs(
1183
+ "button",
1184
+ {
1185
+ type: "button",
1186
+ onClick: () => hasChildren ? defaultChild(item)?.onClick?.() : item.onClick?.(),
1187
+ className: cn(
1188
+ "flex shrink-0 items-center gap-1.5 whitespace-nowrap border-b-2 px-2.5 py-1.5 text-sm font-medium transition-colors",
1189
+ isActive ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
1190
+ ),
1191
+ children: [
1192
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
1193
+ item.label
1194
+ ]
1195
+ },
1196
+ item.id
1197
+ );
1198
+ }) }) });
1199
+ if (slot) return createPortal(content, slot);
1200
+ return /* @__PURE__ */ jsx("div", { className: "mb-5 border-b border-border pb-2", children: content });
1201
+ }
1202
+ function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
1203
+ const ctxVariant = useModuleLayout();
1204
+ const variant = navVariant ?? ctxVariant;
1205
+ const transitionKey = viewKey ?? "view";
1206
+ if (variant === "tabs") {
1207
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1208
+ /* @__PURE__ */ jsx(ModuleHeader, { nav }),
1209
+ showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
1210
+ /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
1211
+ ] });
1212
+ }
1213
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-6 -mt-2", className), children: [
1214
+ /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0 hidden md:block no-print", children: /* @__PURE__ */ jsxs("div", { className: cn(sidebarFooter && "sticky top-4 flex flex-col"), style: sidebarFooter ? { height: "calc(100vh - 8rem)" } : void 0, children: [
1215
+ /* @__PURE__ */ jsxs("div", { children: [
1216
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-2.5 mb-2", children: title }),
1217
+ /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
1218
+ ] }),
1219
+ sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
1220
+ ] }) }),
1221
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1222
+ showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
1223
+ /* @__PURE__ */ jsxs("div", { children: [
1224
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
1225
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
1226
+ ] }),
1227
+ headerAction
1228
+ ] }),
1229
+ /* @__PURE__ */ jsx(MobileTabs, { nav }),
1230
+ /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
1231
+ ] })
1232
+ ] });
1233
+ }
1234
+
1235
+ export { AppShell, BackStyleProvider, ICON_MAP, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeaderActions, PageTransition, Sidebar, SubpageHeader, Topbar, useBackStyle, useLayoutStore, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition };
1236
+ //# sourceMappingURL=chunk-CZ6X6PZT.js.map
1237
+ //# sourceMappingURL=chunk-CZ6X6PZT.js.map