@fayz-ai/ui 0.2.0 → 0.2.1
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.
- package/dist/chunk-22J2RTMN.js +1397 -0
- package/dist/chunk-22J2RTMN.js.map +1 -0
- package/dist/chunk-2P7T6QQK.cjs +206 -0
- package/dist/chunk-2P7T6QQK.cjs.map +1 -0
- package/dist/chunk-7TBDPNLY.cjs +649 -0
- package/dist/chunk-7TBDPNLY.cjs.map +1 -0
- package/dist/chunk-CZ6X6PZT.js +1237 -0
- package/dist/chunk-CZ6X6PZT.js.map +1 -0
- package/dist/chunk-D6SXPF4F.cjs +397 -0
- package/dist/chunk-D6SXPF4F.cjs.map +1 -0
- package/dist/chunk-DNYVEOAX.js +11 -0
- package/dist/chunk-DNYVEOAX.js.map +1 -0
- package/dist/chunk-E526FHZ4.js +169 -0
- package/dist/chunk-E526FHZ4.js.map +1 -0
- package/dist/chunk-GE6XUREM.cjs +1277 -0
- package/dist/chunk-GE6XUREM.cjs.map +1 -0
- package/dist/chunk-GFH5HOCO.js +605 -0
- package/dist/chunk-GFH5HOCO.js.map +1 -0
- package/dist/chunk-IWBXYV4U.js +203 -0
- package/dist/chunk-IWBXYV4U.js.map +1 -0
- package/dist/chunk-MYZT2OG7.js +350 -0
- package/dist/chunk-MYZT2OG7.js.map +1 -0
- package/dist/chunk-O5F4LLSL.js +445 -0
- package/dist/chunk-O5F4LLSL.js.map +1 -0
- package/dist/chunk-TDGSY5W6.cjs +474 -0
- package/dist/chunk-TDGSY5W6.cjs.map +1 -0
- package/dist/chunk-VVVZO2LE.cjs +13 -0
- package/dist/chunk-VVVZO2LE.cjs.map +1 -0
- package/dist/chunk-W4XXXJRW.cjs +196 -0
- package/dist/chunk-W4XXXJRW.cjs.map +1 -0
- package/dist/chunk-YB5ZJKNG.cjs +1484 -0
- package/dist/chunk-YB5ZJKNG.cjs.map +1 -0
- package/dist/dashboard/index.cjs +102 -0
- package/dist/dashboard/index.cjs.map +1 -0
- package/dist/dashboard/index.js +5 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/index.cjs +569 -4304
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -4152
- package/dist/index.js.map +1 -1
- package/dist/layout/index.cjs +90 -0
- package/dist/layout/index.cjs.map +1 -0
- package/dist/layout/index.js +5 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/preset.cjs +18 -0
- package/dist/preset.cjs.map +1 -0
- package/dist/preset.js +3 -0
- package/dist/preset.js.map +1 -0
- package/dist/primitives/index.cjs +347 -0
- package/dist/primitives/index.cjs.map +1 -0
- package/dist/primitives/index.js +6 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/theme/index.cjs +41 -0
- package/dist/theme/index.cjs.map +1 -0
- package/dist/theme/index.js +4 -0
- package/dist/theme/index.js.map +1 -0
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,3562 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
12
|
-
import { useReactTable, getSortedRowModel, getCoreRowModel, flexRender } from '@tanstack/react-table';
|
|
13
|
-
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
14
|
-
import { Toaster } from 'sonner';
|
|
15
|
-
export { toast } from 'sonner';
|
|
16
|
-
import { hashRouterAdapter, useTranslation, getCurrentLocale, usePluginRuntime, getDashboardWidgets, useNavReferrer, routeModule, navHistoryBack, resolvePluginComponent } from '@fayz-ai/core';
|
|
17
|
-
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
18
|
-
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
|
19
|
-
import * as Tooltip2 from '@radix-ui/react-tooltip';
|
|
20
|
-
import { createPortal } from 'react-dom';
|
|
21
|
-
import * as Popover2 from '@radix-ui/react-popover';
|
|
22
|
-
import { create } from 'zustand';
|
|
1
|
+
export { Badge, Breadcrumb, Checkbox, ConfirmDialog, CurrencyInput, DatePicker, Input, ListView, Modal, ModalBody, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalPortal, ModalTitle, ModalTrigger, Popover, PopoverContent, PopoverTrigger, SearchSelect, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, TableSkeleton, Tabs, TabsContent, TabsList, TabsTrigger, TimePicker, ToastProvider, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, toast } from './chunk-22J2RTMN.js';
|
|
2
|
+
export { AppShell, BackStyleProvider, ICON_MAP, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeaderActions, PageTransition, Sidebar, SubpageHeader, Topbar, useBackStyle, useLayoutStore, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition } from './chunk-CZ6X6PZT.js';
|
|
3
|
+
export { SaveBar, SaveBarProvider, useBackHandler, useSaveBar } from './chunk-E526FHZ4.js';
|
|
4
|
+
export { ThemeProvider, createFayzTheme, createTheme, darkTheme, fayzThemePresets, lightTheme, useTheme, useThemeStore } from './chunk-O5F4LLSL.js';
|
|
5
|
+
export { ChartWidget, DEFAULT_RANGE_OPTIONS, DashboardCanvas, DashboardCustomizeMenu, DashboardGrid, DashboardNavigateProvider, DashboardRangeControl, DashboardRangeProvider, KpiCard, OnboardingWidget, TableWidget, WidgetGrid, defineChartWidget, defineCustomWidget, defineKpiWidget, defineOnboardingWidget, defineTableWidget, formatKpiValue, renderIcon, resolveDashboardLayout, useDashboardNavigate, useDashboardPreferences, useDashboardRange } from './chunk-GFH5HOCO.js';
|
|
6
|
+
export { Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, DataTable, Dropdown, DropdownCheckboxItem, DropdownContent, DropdownGroup, DropdownItem, DropdownLabel, DropdownPortal, DropdownRadioGroup, DropdownRadioItem, DropdownSeparator, DropdownShortcut, DropdownSub, DropdownSubContent, DropdownSubTrigger, DropdownTrigger, SegmentedControl, buttonVariants } from './chunk-MYZT2OG7.js';
|
|
7
|
+
import { cn } from './chunk-DNYVEOAX.js';
|
|
8
|
+
export { cn } from './chunk-DNYVEOAX.js';
|
|
9
|
+
import './chunk-IWBXYV4U.js';
|
|
10
|
+
import * as React from 'react';
|
|
23
11
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
24
|
-
import {
|
|
12
|
+
import { jsx } from 'react/jsx-runtime';
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
function cn(...inputs) {
|
|
28
|
-
return twMerge(clsx(inputs));
|
|
29
|
-
}
|
|
30
|
-
var buttonVariants = cva(
|
|
31
|
-
"inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-button text-sm font-semibold ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
32
|
-
{
|
|
33
|
-
variants: {
|
|
34
|
-
variant: {
|
|
35
|
-
// Polaris primary — near-black brand with the stronger primary bevel
|
|
36
|
-
default: "bg-primary text-primary-foreground border border-primary shadow-button-primary hover:bg-primary/90 active:shadow-button-inset",
|
|
37
|
-
destructive: "bg-destructive text-destructive-foreground border border-destructive shadow-button-primary hover:bg-destructive/90 active:shadow-button-inset",
|
|
38
|
-
// Default/secondary surface — white with hairline + bevel
|
|
39
|
-
outline: "border border-border bg-card text-foreground shadow-button hover:bg-muted active:shadow-button-inset",
|
|
40
|
-
secondary: "border border-border bg-card text-foreground shadow-button hover:bg-muted active:shadow-button-inset",
|
|
41
|
-
ghost: "hover:bg-muted hover:text-foreground",
|
|
42
|
-
link: "text-info underline-offset-4 hover:underline"
|
|
43
|
-
},
|
|
44
|
-
size: {
|
|
45
|
-
// Polaris button sizes: md 30px, sm 26px, lg 36px
|
|
46
|
-
default: "h-[30px] px-3 py-1",
|
|
47
|
-
sm: "h-[26px] rounded-button px-2.5 text-xs",
|
|
48
|
-
lg: "h-9 rounded-button px-4 text-sm",
|
|
49
|
-
icon: "h-[30px] w-[30px]"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
defaultVariants: {
|
|
53
|
-
variant: "default",
|
|
54
|
-
size: "default"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
var Button = React10.forwardRef(
|
|
59
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
60
|
-
const Comp = asChild ? Slot : "button";
|
|
61
|
-
return /* @__PURE__ */ jsx(
|
|
62
|
-
Comp,
|
|
63
|
-
{
|
|
64
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
65
|
-
ref,
|
|
66
|
-
...props
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
Button.displayName = "Button";
|
|
72
|
-
var Input = React10.forwardRef(
|
|
73
|
-
({ className, type, error, description, id, ...props }, ref) => {
|
|
74
|
-
const inputId = id ?? React10.useId();
|
|
75
|
-
const descriptionId = description ? `${inputId}-description` : void 0;
|
|
76
|
-
const errorId = error ? `${inputId}-error` : void 0;
|
|
77
|
-
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
78
|
-
/* @__PURE__ */ jsx(
|
|
79
|
-
"input",
|
|
80
|
-
{
|
|
81
|
-
type,
|
|
82
|
-
id: inputId,
|
|
83
|
-
className: cn(
|
|
84
|
-
"flex h-8 w-full rounded-input border border-input bg-card px-2 py-1 text-sm text-foreground shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
85
|
-
error && "border-destructive focus-visible:ring-destructive",
|
|
86
|
-
className
|
|
87
|
-
),
|
|
88
|
-
ref,
|
|
89
|
-
"aria-invalid": error ? true : void 0,
|
|
90
|
-
"aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || void 0,
|
|
91
|
-
...props
|
|
92
|
-
}
|
|
93
|
-
),
|
|
94
|
-
description && !error && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-1.5 text-xs text-muted-foreground", children: description }),
|
|
95
|
-
error && /* @__PURE__ */ jsx("p", { id: errorId, className: "mt-1.5 text-xs text-destructive", children: error })
|
|
96
|
-
] });
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
Input.displayName = "Input";
|
|
100
|
-
var badgeVariants = cva(
|
|
101
|
-
"inline-flex items-center gap-1.5 rounded px-2 py-0.5 text-xs font-medium leading-[18px] transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
102
|
-
{
|
|
103
|
-
variants: {
|
|
104
|
-
variant: {
|
|
105
|
-
default: "bg-muted text-muted-foreground",
|
|
106
|
-
secondary: "bg-muted text-muted-foreground",
|
|
107
|
-
info: "bg-info-soft text-info-soft-foreground",
|
|
108
|
-
success: "bg-success-soft text-success-soft-foreground",
|
|
109
|
-
warning: "bg-warning-soft text-warning-soft-foreground",
|
|
110
|
-
destructive: "bg-destructive-soft text-destructive-soft-foreground",
|
|
111
|
-
magic: "bg-magic-soft text-magic-soft-foreground",
|
|
112
|
-
strong: "bg-primary text-primary-foreground",
|
|
113
|
-
outline: "border border-border text-foreground"
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
defaultVariants: {
|
|
117
|
-
variant: "default"
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
var VARIANTS_WITH_DOT = /* @__PURE__ */ new Set([
|
|
122
|
-
"info",
|
|
123
|
-
"success",
|
|
124
|
-
"warning",
|
|
125
|
-
"destructive",
|
|
126
|
-
"magic"
|
|
127
|
-
]);
|
|
128
|
-
var DOT_COLOR = {
|
|
129
|
-
info: "hsl(var(--info))",
|
|
130
|
-
success: "hsl(var(--success))",
|
|
131
|
-
warning: "hsl(var(--warning))",
|
|
132
|
-
destructive: "hsl(var(--destructive))",
|
|
133
|
-
magic: "hsl(var(--magic))"
|
|
134
|
-
};
|
|
135
|
-
function Badge({ className, variant, dot, children, ...props }) {
|
|
136
|
-
const v = variant ?? "default";
|
|
137
|
-
const showDot = dot ?? VARIANTS_WITH_DOT.has(v);
|
|
138
|
-
return /* @__PURE__ */ jsxs("div", { className: cn(badgeVariants({ variant }), className), ...props, children: [
|
|
139
|
-
showDot && /* @__PURE__ */ jsx(
|
|
140
|
-
"span",
|
|
141
|
-
{
|
|
142
|
-
className: "inline-block size-1.5 rounded-full",
|
|
143
|
-
style: { background: DOT_COLOR[v] ?? "currentColor" }
|
|
144
|
-
}
|
|
145
|
-
),
|
|
146
|
-
children
|
|
147
|
-
] });
|
|
148
|
-
}
|
|
149
|
-
var Card = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
150
|
-
"div",
|
|
151
|
-
{
|
|
152
|
-
ref,
|
|
153
|
-
className: cn(
|
|
154
|
-
"fayz-glass-surface rounded-card border bg-card text-card-foreground shadow-sm",
|
|
155
|
-
className
|
|
156
|
-
),
|
|
157
|
-
...props
|
|
158
|
-
}
|
|
159
|
-
));
|
|
160
|
-
Card.displayName = "Card";
|
|
161
|
-
var CardHeader = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
162
|
-
"div",
|
|
163
|
-
{
|
|
164
|
-
ref,
|
|
165
|
-
className: cn("flex flex-col space-y-1 p-4", className),
|
|
166
|
-
...props
|
|
167
|
-
}
|
|
168
|
-
));
|
|
169
|
-
CardHeader.displayName = "CardHeader";
|
|
170
|
-
var CardTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
171
|
-
"h3",
|
|
172
|
-
{
|
|
173
|
-
ref,
|
|
174
|
-
className: cn(
|
|
175
|
-
"text-sm font-semibold leading-tight tracking-tight",
|
|
176
|
-
className
|
|
177
|
-
),
|
|
178
|
-
...props
|
|
179
|
-
}
|
|
180
|
-
));
|
|
181
|
-
CardTitle.displayName = "CardTitle";
|
|
182
|
-
var CardDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
183
|
-
"p",
|
|
184
|
-
{
|
|
185
|
-
ref,
|
|
186
|
-
className: cn("text-xs text-muted-foreground", className),
|
|
187
|
-
...props
|
|
188
|
-
}
|
|
189
|
-
));
|
|
190
|
-
CardDescription.displayName = "CardDescription";
|
|
191
|
-
var CardContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-4 pt-0", className), ...props }));
|
|
192
|
-
CardContent.displayName = "CardContent";
|
|
193
|
-
var CardFooter = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
194
|
-
"div",
|
|
195
|
-
{
|
|
196
|
-
ref,
|
|
197
|
-
className: cn("flex items-center p-4 pt-0", className),
|
|
198
|
-
...props
|
|
199
|
-
}
|
|
200
|
-
));
|
|
201
|
-
CardFooter.displayName = "CardFooter";
|
|
202
|
-
var SIZE_MAP = {
|
|
203
|
-
sm: "sm:max-w-sm",
|
|
204
|
-
md: "sm:max-w-md",
|
|
205
|
-
lg: "sm:max-w-lg",
|
|
206
|
-
xl: "sm:max-w-xl",
|
|
207
|
-
"2xl": "sm:max-w-2xl",
|
|
208
|
-
"3xl": "sm:max-w-3xl",
|
|
209
|
-
full: "sm:max-w-[calc(100vw-4rem)]"
|
|
210
|
-
};
|
|
211
|
-
var Modal = DialogPrimitive.Root;
|
|
212
|
-
var ModalTrigger = DialogPrimitive.Trigger;
|
|
213
|
-
var ModalClose = DialogPrimitive.Close;
|
|
214
|
-
var ModalPortal = DialogPrimitive.Portal;
|
|
215
|
-
var ModalOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
216
|
-
DialogPrimitive.Overlay,
|
|
217
|
-
{
|
|
218
|
-
ref,
|
|
219
|
-
className: cn(
|
|
220
|
-
"fayz-modal-overlay saas-mdl-ov fixed inset-0 z-50",
|
|
221
|
-
"flex items-center justify-center p-0 sm:p-6",
|
|
222
|
-
className
|
|
223
|
-
),
|
|
224
|
-
...props
|
|
225
|
-
}
|
|
226
|
-
));
|
|
227
|
-
ModalOverlay.displayName = "ModalOverlay";
|
|
228
|
-
var ModalContent = React10.forwardRef(({ className, children, size = "lg", hideClose, noPadding, onPointerDownOutside, onInteractOutside, onCloseAutoFocus, ...props }, ref) => /* @__PURE__ */ jsx(ModalPortal, { children: /* @__PURE__ */ jsx(ModalOverlay, { children: /* @__PURE__ */ jsxs(
|
|
229
|
-
DialogPrimitive.Content,
|
|
230
|
-
{
|
|
231
|
-
ref,
|
|
232
|
-
onCloseAutoFocus: (e) => {
|
|
233
|
-
e.preventDefault();
|
|
234
|
-
onCloseAutoFocus?.(e);
|
|
235
|
-
},
|
|
236
|
-
onPointerDownOutside: (e) => {
|
|
237
|
-
const target = e.target;
|
|
238
|
-
if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
|
|
239
|
-
e.preventDefault();
|
|
240
|
-
}
|
|
241
|
-
onPointerDownOutside?.(e);
|
|
242
|
-
},
|
|
243
|
-
onInteractOutside: (e) => {
|
|
244
|
-
const target = e.target;
|
|
245
|
-
if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
|
|
246
|
-
e.preventDefault();
|
|
247
|
-
}
|
|
248
|
-
onInteractOutside?.(e);
|
|
249
|
-
},
|
|
250
|
-
className: cn(
|
|
251
|
-
"fayz-glass-surface fayz-modal-content saas-mdl-ct relative z-50 flex flex-col w-full outline-none",
|
|
252
|
-
// Mobile fullscreen
|
|
253
|
-
"h-full rounded-none border-0",
|
|
254
|
-
// Desktop centered card
|
|
255
|
-
"sm:h-auto sm:max-h-[85vh] sm:rounded-2xl sm:border",
|
|
256
|
-
SIZE_MAP[size],
|
|
257
|
-
noPadding ? "" : "p-6 gap-4",
|
|
258
|
-
className
|
|
259
|
-
),
|
|
260
|
-
...props,
|
|
261
|
-
children: [
|
|
262
|
-
children,
|
|
263
|
-
!hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
|
|
264
|
-
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
265
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
266
|
-
] })
|
|
267
|
-
]
|
|
268
|
-
}
|
|
269
|
-
) }) }));
|
|
270
|
-
ModalContent.displayName = "ModalContent";
|
|
271
|
-
function ModalHeader({ className, ...props }) {
|
|
272
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left shrink-0", className), ...props });
|
|
273
|
-
}
|
|
274
|
-
function ModalBody({ className, ...props }) {
|
|
275
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex-1 overflow-y-auto min-h-0", className), ...props });
|
|
276
|
-
}
|
|
277
|
-
function ModalFooter({ className, ...props }) {
|
|
278
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 shrink-0", className), ...props });
|
|
279
|
-
}
|
|
280
|
-
var ModalTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props }));
|
|
281
|
-
ModalTitle.displayName = "ModalTitle";
|
|
282
|
-
var ModalDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
283
|
-
ModalDescription.displayName = "ModalDescription";
|
|
284
|
-
var Dropdown = DropdownMenuPrimitive.Root;
|
|
285
|
-
var DropdownTrigger = DropdownMenuPrimitive.Trigger;
|
|
286
|
-
var DropdownGroup = DropdownMenuPrimitive.Group;
|
|
287
|
-
var DropdownPortal = DropdownMenuPrimitive.Portal;
|
|
288
|
-
var DropdownSub = DropdownMenuPrimitive.Sub;
|
|
289
|
-
var DropdownRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
290
|
-
var DropdownSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
291
|
-
DropdownMenuPrimitive.SubTrigger,
|
|
292
|
-
{
|
|
293
|
-
ref,
|
|
294
|
-
className: cn(
|
|
295
|
-
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-muted data-[state=open]:bg-muted",
|
|
296
|
-
inset && "pl-8",
|
|
297
|
-
className
|
|
298
|
-
),
|
|
299
|
-
...props,
|
|
300
|
-
children: [
|
|
301
|
-
children,
|
|
302
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto h-4 w-4" })
|
|
303
|
-
]
|
|
304
|
-
}
|
|
305
|
-
));
|
|
306
|
-
DropdownSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
307
|
-
var DropdownSubContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
308
|
-
DropdownMenuPrimitive.SubContent,
|
|
309
|
-
{
|
|
310
|
-
ref,
|
|
311
|
-
className: cn(
|
|
312
|
-
"fayz-glass-surface z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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",
|
|
313
|
-
className
|
|
314
|
-
),
|
|
315
|
-
...props
|
|
316
|
-
}
|
|
317
|
-
));
|
|
318
|
-
DropdownSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
319
|
-
var DropdownContent = React10.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
320
|
-
DropdownMenuPrimitive.Content,
|
|
321
|
-
{
|
|
322
|
-
ref,
|
|
323
|
-
sideOffset,
|
|
324
|
-
className: cn(
|
|
325
|
-
"fayz-glass-surface z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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",
|
|
326
|
-
className
|
|
327
|
-
),
|
|
328
|
-
...props
|
|
329
|
-
}
|
|
330
|
-
) }));
|
|
331
|
-
DropdownContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
332
|
-
var DropdownItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
333
|
-
DropdownMenuPrimitive.Item,
|
|
334
|
-
{
|
|
335
|
-
ref,
|
|
336
|
-
className: cn(
|
|
337
|
-
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
338
|
-
inset && "pl-8",
|
|
339
|
-
className
|
|
340
|
-
),
|
|
341
|
-
...props
|
|
342
|
-
}
|
|
343
|
-
));
|
|
344
|
-
DropdownItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
345
|
-
var DropdownCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
346
|
-
DropdownMenuPrimitive.CheckboxItem,
|
|
347
|
-
{
|
|
348
|
-
ref,
|
|
349
|
-
className: cn(
|
|
350
|
-
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
351
|
-
className
|
|
352
|
-
),
|
|
353
|
-
checked,
|
|
354
|
-
...props,
|
|
355
|
-
children: [
|
|
356
|
-
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
|
|
357
|
-
children
|
|
358
|
-
]
|
|
359
|
-
}
|
|
360
|
-
));
|
|
361
|
-
DropdownCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
362
|
-
var DropdownRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
363
|
-
DropdownMenuPrimitive.RadioItem,
|
|
364
|
-
{
|
|
365
|
-
ref,
|
|
366
|
-
className: cn(
|
|
367
|
-
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
368
|
-
className
|
|
369
|
-
),
|
|
370
|
-
...props,
|
|
371
|
-
children: [
|
|
372
|
-
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
373
|
-
children
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
));
|
|
377
|
-
DropdownRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
378
|
-
var DropdownLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
379
|
-
DropdownMenuPrimitive.Label,
|
|
380
|
-
{
|
|
381
|
-
ref,
|
|
382
|
-
className: cn(
|
|
383
|
-
"px-2 py-1.5 text-sm font-semibold",
|
|
384
|
-
inset && "pl-8",
|
|
385
|
-
className
|
|
386
|
-
),
|
|
387
|
-
...props
|
|
388
|
-
}
|
|
389
|
-
));
|
|
390
|
-
DropdownLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
391
|
-
var DropdownSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
392
|
-
DropdownMenuPrimitive.Separator,
|
|
393
|
-
{
|
|
394
|
-
ref,
|
|
395
|
-
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
396
|
-
...props
|
|
397
|
-
}
|
|
398
|
-
));
|
|
399
|
-
DropdownSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
400
|
-
var DropdownShortcut = ({
|
|
401
|
-
className,
|
|
402
|
-
...props
|
|
403
|
-
}) => {
|
|
404
|
-
return /* @__PURE__ */ jsx(
|
|
405
|
-
"span",
|
|
406
|
-
{
|
|
407
|
-
className: cn("ml-auto text-xs tracking-widest opacity-60", className),
|
|
408
|
-
...props
|
|
409
|
-
}
|
|
410
|
-
);
|
|
411
|
-
};
|
|
412
|
-
DropdownShortcut.displayName = "DropdownShortcut";
|
|
413
|
-
function DataTable({
|
|
414
|
-
columns,
|
|
415
|
-
data,
|
|
416
|
-
loading = false,
|
|
417
|
-
skeletonRows = 3,
|
|
418
|
-
emptyMessage = "No results.",
|
|
419
|
-
onRowClick,
|
|
420
|
-
variant = "card",
|
|
421
|
-
compact = false
|
|
422
|
-
}) {
|
|
423
|
-
const isCard = variant === "card";
|
|
424
|
-
const wrapperCls = isCard ? "rounded-lg border bg-card shadow-sm overflow-x-auto" : "rounded-lg border overflow-x-auto";
|
|
425
|
-
const headerCls = isCard ? "border-b bg-muted/50" : "border-b bg-muted/30";
|
|
426
|
-
const thCls = compact ? "px-3 py-1.5 text-left text-[11px] font-medium text-muted-foreground" : "px-4 py-2.5 text-left font-medium text-muted-foreground";
|
|
427
|
-
const tdCls = compact ? "px-3 py-1.5 align-middle text-xs" : "px-4 py-3 align-middle";
|
|
428
|
-
const tableCls = compact ? "w-full text-xs min-w-max" : "w-full text-sm min-w-max";
|
|
429
|
-
const rowHoverCls = isCard ? "hover:bg-muted/30" : "hover:bg-muted/20";
|
|
430
|
-
const [sorting, setSorting] = React10.useState([]);
|
|
431
|
-
const table = useReactTable({
|
|
432
|
-
data,
|
|
433
|
-
columns,
|
|
434
|
-
getCoreRowModel: getCoreRowModel(),
|
|
435
|
-
getSortedRowModel: getSortedRowModel(),
|
|
436
|
-
onSortingChange: setSorting,
|
|
437
|
-
state: { sorting }
|
|
438
|
-
});
|
|
439
|
-
if (loading) {
|
|
440
|
-
return /* @__PURE__ */ jsx("div", { className: wrapperCls, children: /* @__PURE__ */ jsxs("table", { className: tableCls, children: [
|
|
441
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: headerCls, children: columns.map((_, i) => /* @__PURE__ */ jsx("th", { className: thCls, children: /* @__PURE__ */ jsx("div", { className: "h-3.5 w-20 animate-pulse rounded bg-muted" }) }, i)) }) }),
|
|
442
|
-
/* @__PURE__ */ jsx("tbody", { children: Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsx("tr", { className: "border-b last:border-0", children: columns.map((_2, j) => /* @__PURE__ */ jsx("td", { className: tdCls, children: /* @__PURE__ */ jsx("div", { className: "h-3.5 w-full animate-pulse rounded bg-muted" }) }, j)) }, i)) })
|
|
443
|
-
] }) });
|
|
444
|
-
}
|
|
445
|
-
return /* @__PURE__ */ jsx("div", { className: wrapperCls, children: /* @__PURE__ */ jsxs("table", { className: tableCls, children: [
|
|
446
|
-
/* @__PURE__ */ jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx("tr", { className: headerCls, children: headerGroup.headers.map((header) => {
|
|
447
|
-
const sorted = header.column.getIsSorted();
|
|
448
|
-
return /* @__PURE__ */ jsx(
|
|
449
|
-
"th",
|
|
450
|
-
{
|
|
451
|
-
className: thCls,
|
|
452
|
-
children: header.isPlaceholder ? null : header.column.getCanSort() ? /* @__PURE__ */ jsxs(
|
|
453
|
-
"button",
|
|
454
|
-
{
|
|
455
|
-
className: "flex items-center gap-1 hover:text-foreground transition-colors",
|
|
456
|
-
onClick: header.column.getToggleSortingHandler(),
|
|
457
|
-
children: [
|
|
458
|
-
flexRender(header.column.columnDef.header, header.getContext()),
|
|
459
|
-
sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-3 w-3" }) : sorted === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "h-3 w-3 opacity-40" })
|
|
460
|
-
]
|
|
461
|
-
}
|
|
462
|
-
) : flexRender(header.column.columnDef.header, header.getContext())
|
|
463
|
-
},
|
|
464
|
-
header.id
|
|
465
|
-
);
|
|
466
|
-
}) }, headerGroup.id)) }),
|
|
467
|
-
/* @__PURE__ */ jsx("tbody", { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(
|
|
468
|
-
"tr",
|
|
469
|
-
{
|
|
470
|
-
className: cn(
|
|
471
|
-
`border-b last:border-0 transition-colors ${rowHoverCls}`,
|
|
472
|
-
onRowClick && "cursor-pointer"
|
|
473
|
-
),
|
|
474
|
-
onClick: () => onRowClick?.(row.original),
|
|
475
|
-
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx("td", { className: tdCls, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
|
|
476
|
-
},
|
|
477
|
-
row.id
|
|
478
|
-
)) : /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "h-24 text-center text-muted-foreground", children: emptyMessage }) }) })
|
|
479
|
-
] }) });
|
|
480
|
-
}
|
|
481
|
-
var COLOR_CLASSES = {
|
|
482
|
-
primary: "border-primary bg-primary text-primary-foreground",
|
|
483
|
-
success: "border-success bg-success text-white dark:border-success dark:bg-success",
|
|
484
|
-
warning: "border-warning bg-warning text-white",
|
|
485
|
-
destructive: "border-destructive bg-destructive text-destructive-foreground"
|
|
486
|
-
};
|
|
487
|
-
var Checkbox = React10.forwardRef(
|
|
488
|
-
({ checked = false, onChange, disabled, className, color = "primary", id, ...props }, ref) => {
|
|
489
|
-
return /* @__PURE__ */ jsx(
|
|
490
|
-
"button",
|
|
491
|
-
{
|
|
492
|
-
ref,
|
|
493
|
-
id,
|
|
494
|
-
type: "button",
|
|
495
|
-
role: "checkbox",
|
|
496
|
-
"aria-checked": checked,
|
|
497
|
-
disabled,
|
|
498
|
-
onClick: () => onChange?.(!checked),
|
|
499
|
-
className: cn(
|
|
500
|
-
"peer inline-flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border-2 transition-all duration-150",
|
|
501
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
502
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
503
|
-
checked ? `${COLOR_CLASSES[color]} shadow-sm` : "border-muted-foreground/40 bg-background hover:border-muted-foreground/60",
|
|
504
|
-
className
|
|
505
|
-
),
|
|
506
|
-
...props,
|
|
507
|
-
children: checked && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 })
|
|
508
|
-
}
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
);
|
|
512
|
-
Checkbox.displayName = "Checkbox";
|
|
513
|
-
var STYLE_ID = "saas-sheet-styles";
|
|
514
|
-
if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
|
|
515
|
-
const style = document.createElement("style");
|
|
516
|
-
style.id = STYLE_ID;
|
|
517
|
-
style.textContent = `
|
|
518
|
-
@keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
|
|
519
|
-
@keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
|
|
520
|
-
@keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
|
|
521
|
-
@keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
|
|
522
|
-
.saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
|
|
523
|
-
.saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
|
|
524
|
-
.saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
|
|
525
|
-
.saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
|
|
526
|
-
`;
|
|
527
|
-
document.head.appendChild(style);
|
|
528
|
-
}
|
|
529
|
-
var Sheet = DialogPrimitive.Root;
|
|
530
|
-
var SheetTrigger = DialogPrimitive.Trigger;
|
|
531
|
-
var SheetClose = DialogPrimitive.Close;
|
|
532
|
-
var SheetPortal = DialogPrimitive.Portal;
|
|
533
|
-
var SheetOverlay = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
534
|
-
DialogPrimitive.Overlay,
|
|
535
|
-
{
|
|
536
|
-
ref,
|
|
537
|
-
className: cn(
|
|
538
|
-
"saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
|
|
539
|
-
className
|
|
540
|
-
),
|
|
541
|
-
...props
|
|
542
|
-
}
|
|
543
|
-
));
|
|
544
|
-
SheetOverlay.displayName = "SheetOverlay";
|
|
545
|
-
var SheetContent = React10.forwardRef(({ className, children, width, hideClose, overlay = "blur", ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
546
|
-
/* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
|
|
547
|
-
/* @__PURE__ */ jsxs(
|
|
548
|
-
DialogPrimitive.Content,
|
|
549
|
-
{
|
|
550
|
-
ref,
|
|
551
|
-
className: cn(
|
|
552
|
-
"fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-50 flex flex-col border-l bg-card shadow-2xl outline-none",
|
|
553
|
-
"w-full",
|
|
554
|
-
width ?? "max-w-md",
|
|
555
|
-
className
|
|
556
|
-
),
|
|
557
|
-
...props,
|
|
558
|
-
children: [
|
|
559
|
-
children,
|
|
560
|
-
!hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
|
|
561
|
-
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
562
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
563
|
-
] })
|
|
564
|
-
]
|
|
565
|
-
}
|
|
566
|
-
)
|
|
567
|
-
] }));
|
|
568
|
-
SheetContent.displayName = "SheetContent";
|
|
569
|
-
function SheetHeader({ className, ...props }) {
|
|
570
|
-
return /* @__PURE__ */ jsx(
|
|
571
|
-
"div",
|
|
572
|
-
{
|
|
573
|
-
className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
|
|
574
|
-
...props
|
|
575
|
-
}
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
SheetHeader.displayName = "SheetHeader";
|
|
579
|
-
function SheetFooter({ className, ...props }) {
|
|
580
|
-
return /* @__PURE__ */ jsx(
|
|
581
|
-
"div",
|
|
582
|
-
{
|
|
583
|
-
className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
|
|
584
|
-
...props
|
|
585
|
-
}
|
|
586
|
-
);
|
|
587
|
-
}
|
|
588
|
-
SheetFooter.displayName = "SheetFooter";
|
|
589
|
-
var SheetTitle = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
590
|
-
DialogPrimitive.Title,
|
|
591
|
-
{
|
|
592
|
-
ref,
|
|
593
|
-
className: cn("text-sm font-semibold leading-none tracking-tight", className),
|
|
594
|
-
...props
|
|
595
|
-
}
|
|
596
|
-
));
|
|
597
|
-
SheetTitle.displayName = "SheetTitle";
|
|
598
|
-
var SheetDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
599
|
-
DialogPrimitive.Description,
|
|
600
|
-
{
|
|
601
|
-
ref,
|
|
602
|
-
className: cn("text-xs text-muted-foreground", className),
|
|
603
|
-
...props
|
|
604
|
-
}
|
|
605
|
-
));
|
|
606
|
-
SheetDescription.displayName = "SheetDescription";
|
|
607
|
-
function SheetBody({ className, ...props }) {
|
|
608
|
-
return /* @__PURE__ */ jsx(
|
|
609
|
-
"div",
|
|
610
|
-
{
|
|
611
|
-
className: cn("flex-1 overflow-y-auto px-5 py-5", className),
|
|
612
|
-
...props
|
|
613
|
-
}
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
|
-
SheetBody.displayName = "SheetBody";
|
|
617
|
-
var Select = SelectPrimitive.Root;
|
|
618
|
-
var SelectGroup = SelectPrimitive.Group;
|
|
619
|
-
var SelectValue = SelectPrimitive.Value;
|
|
620
|
-
var SelectTrigger = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
621
|
-
SelectPrimitive.Trigger,
|
|
622
|
-
{
|
|
623
|
-
ref,
|
|
624
|
-
className: cn(
|
|
625
|
-
"flex h-10 w-full items-center justify-between rounded-input border border-input bg-background px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
626
|
-
className
|
|
627
|
-
),
|
|
628
|
-
...props,
|
|
629
|
-
children: [
|
|
630
|
-
children,
|
|
631
|
-
/* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
632
|
-
]
|
|
633
|
-
}
|
|
634
|
-
));
|
|
635
|
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
636
|
-
var SelectScrollUpButton = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
637
|
-
SelectPrimitive.ScrollUpButton,
|
|
638
|
-
{
|
|
639
|
-
ref,
|
|
640
|
-
className: cn(
|
|
641
|
-
"flex cursor-default items-center justify-center py-1",
|
|
642
|
-
className
|
|
643
|
-
),
|
|
644
|
-
...props,
|
|
645
|
-
children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
|
|
646
|
-
}
|
|
647
|
-
));
|
|
648
|
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
649
|
-
var SelectScrollDownButton = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
650
|
-
SelectPrimitive.ScrollDownButton,
|
|
651
|
-
{
|
|
652
|
-
ref,
|
|
653
|
-
className: cn(
|
|
654
|
-
"flex cursor-default items-center justify-center py-1",
|
|
655
|
-
className
|
|
656
|
-
),
|
|
657
|
-
...props,
|
|
658
|
-
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
|
|
659
|
-
}
|
|
660
|
-
));
|
|
661
|
-
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
662
|
-
var SelectContent = React10.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
663
|
-
SelectPrimitive.Content,
|
|
664
|
-
{
|
|
665
|
-
ref,
|
|
666
|
-
className: cn(
|
|
667
|
-
"fayz-glass-surface relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-card border bg-popover text-popover-foreground shadow-md",
|
|
668
|
-
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
669
|
-
className
|
|
670
|
-
),
|
|
671
|
-
position,
|
|
672
|
-
...props,
|
|
673
|
-
children: [
|
|
674
|
-
/* @__PURE__ */ jsx(SelectScrollUpButton, {}),
|
|
675
|
-
/* @__PURE__ */ jsx(
|
|
676
|
-
SelectPrimitive.Viewport,
|
|
677
|
-
{
|
|
678
|
-
className: cn(
|
|
679
|
-
"p-1",
|
|
680
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
681
|
-
),
|
|
682
|
-
children
|
|
683
|
-
}
|
|
684
|
-
),
|
|
685
|
-
/* @__PURE__ */ jsx(SelectScrollDownButton, {})
|
|
686
|
-
]
|
|
687
|
-
}
|
|
688
|
-
) }));
|
|
689
|
-
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
690
|
-
var SelectLabel = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
691
|
-
SelectPrimitive.Label,
|
|
692
|
-
{
|
|
693
|
-
ref,
|
|
694
|
-
className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
|
|
695
|
-
...props
|
|
696
|
-
}
|
|
697
|
-
));
|
|
698
|
-
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
699
|
-
var SelectItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
700
|
-
SelectPrimitive.Item,
|
|
701
|
-
{
|
|
702
|
-
ref,
|
|
703
|
-
className: cn(
|
|
704
|
-
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-muted focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
705
|
-
className
|
|
706
|
-
),
|
|
707
|
-
...props,
|
|
708
|
-
children: [
|
|
709
|
-
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
|
|
710
|
-
/* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
|
|
711
|
-
]
|
|
712
|
-
}
|
|
713
|
-
));
|
|
714
|
-
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
715
|
-
var SelectSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
716
|
-
SelectPrimitive.Separator,
|
|
717
|
-
{
|
|
718
|
-
ref,
|
|
719
|
-
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
720
|
-
...props
|
|
721
|
-
}
|
|
722
|
-
));
|
|
723
|
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
724
|
-
var SaveBarApiContext = React10.createContext(null);
|
|
725
|
-
var SaveBarStateContext = React10.createContext({ visible: false, saving: false });
|
|
726
|
-
var _saveBarVisible = false;
|
|
727
|
-
var _saveBarSubs = /* @__PURE__ */ new Set();
|
|
728
|
-
function setSaveBarVisibleSignal(v) {
|
|
729
|
-
if (v === _saveBarVisible) return;
|
|
730
|
-
_saveBarVisible = v;
|
|
731
|
-
_saveBarSubs.forEach((fn) => fn());
|
|
732
|
-
}
|
|
733
|
-
function useSaveBarVisible() {
|
|
734
|
-
return React10.useSyncExternalStore(
|
|
735
|
-
(cb) => {
|
|
736
|
-
_saveBarSubs.add(cb);
|
|
737
|
-
return () => {
|
|
738
|
-
_saveBarSubs.delete(cb);
|
|
739
|
-
};
|
|
740
|
-
},
|
|
741
|
-
() => _saveBarVisible,
|
|
742
|
-
() => false
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
function SaveBarProvider({ children }) {
|
|
746
|
-
const [state, setState] = React10.useState({ visible: false, saving: false });
|
|
747
|
-
const handlers = React10.useRef({});
|
|
748
|
-
const api = React10.useMemo(() => ({ setState, handlers }), []);
|
|
749
|
-
const backStack = React10.useRef([]);
|
|
750
|
-
const backApi = React10.useMemo(() => ({ stack: backStack }), []);
|
|
751
|
-
React10.useEffect(() => {
|
|
752
|
-
setSaveBarVisibleSignal(state.visible);
|
|
753
|
-
}, [state.visible]);
|
|
754
|
-
React10.useEffect(() => {
|
|
755
|
-
function onKey(e) {
|
|
756
|
-
if (state.visible && !state.saving) {
|
|
757
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
758
|
-
e.preventDefault();
|
|
759
|
-
handlers.current.onSave?.();
|
|
760
|
-
} else if (e.key === "Escape") {
|
|
761
|
-
e.preventDefault();
|
|
762
|
-
handlers.current.onDiscard?.();
|
|
763
|
-
}
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
if (e.key === "Escape" && !e.defaultPrevented && !isEditableTarget(e.target)) {
|
|
767
|
-
for (let i = backStack.current.length - 1; i >= 0; i--) {
|
|
768
|
-
const back = backStack.current[i].current;
|
|
769
|
-
if (back) {
|
|
770
|
-
e.preventDefault();
|
|
771
|
-
back();
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
window.addEventListener("keydown", onKey);
|
|
778
|
-
return () => window.removeEventListener("keydown", onKey);
|
|
779
|
-
}, [state.visible, state.saving]);
|
|
780
|
-
return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
|
|
781
|
-
}
|
|
782
|
-
var BackNavApiContext = React10.createContext(null);
|
|
783
|
-
function isEditableTarget(target) {
|
|
784
|
-
const el = target;
|
|
785
|
-
if (!el || !el.tagName) return false;
|
|
786
|
-
const tag = el.tagName.toLowerCase();
|
|
787
|
-
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
|
788
|
-
}
|
|
789
|
-
function useBackHandler(onBack) {
|
|
790
|
-
const api = React10.useContext(BackNavApiContext);
|
|
791
|
-
const ref = React10.useRef(onBack);
|
|
792
|
-
React10.useEffect(() => {
|
|
793
|
-
ref.current = onBack;
|
|
794
|
-
});
|
|
795
|
-
React10.useEffect(() => {
|
|
796
|
-
if (!api) return;
|
|
797
|
-
const stack = api.stack.current;
|
|
798
|
-
stack.push(ref);
|
|
799
|
-
return () => {
|
|
800
|
-
const i = stack.indexOf(ref);
|
|
801
|
-
if (i !== -1) stack.splice(i, 1);
|
|
802
|
-
};
|
|
803
|
-
}, [api]);
|
|
804
|
-
}
|
|
805
|
-
function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
|
|
806
|
-
const api = React10.useContext(SaveBarApiContext);
|
|
807
|
-
React10.useEffect(() => {
|
|
808
|
-
if (api) api.handlers.current = { onSave, onDiscard };
|
|
809
|
-
});
|
|
810
|
-
React10.useEffect(() => {
|
|
811
|
-
if (!api) return;
|
|
812
|
-
api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
|
|
813
|
-
return () => api.setState((s) => ({ ...s, visible: false }));
|
|
814
|
-
}, [api, dirty, saving, message, saveLabel, discardLabel]);
|
|
815
|
-
}
|
|
816
|
-
function SaveBar({ className }) {
|
|
817
|
-
const state = React10.useContext(SaveBarStateContext);
|
|
818
|
-
const api = React10.useContext(SaveBarApiContext);
|
|
819
|
-
const t = useTranslation();
|
|
820
|
-
const [rendered, setRendered] = React10.useState(state.visible);
|
|
821
|
-
const [closing, setClosing] = React10.useState(false);
|
|
822
|
-
React10.useEffect(() => {
|
|
823
|
-
if (state.visible) {
|
|
824
|
-
setRendered(true);
|
|
825
|
-
setClosing(false);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
if (!rendered) return;
|
|
829
|
-
setClosing(true);
|
|
830
|
-
const id = setTimeout(() => {
|
|
831
|
-
setRendered(false);
|
|
832
|
-
setClosing(false);
|
|
833
|
-
}, 200);
|
|
834
|
-
return () => clearTimeout(id);
|
|
835
|
-
}, [state.visible, rendered]);
|
|
836
|
-
if (!api || !rendered) return null;
|
|
837
|
-
const { saving, message, saveLabel, discardLabel } = state;
|
|
838
|
-
return /* @__PURE__ */ jsx("div", { className: cn("pointer-events-none fixed inset-x-0 bottom-5 z-40 flex justify-center px-4", className), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
839
|
-
"pointer-events-auto flex items-center gap-5 rounded-full bg-foreground py-2 pl-5 pr-2 text-background shadow-xl ring-1 ring-black/5 duration-200",
|
|
840
|
-
closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
|
|
841
|
-
), children: [
|
|
842
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
843
|
-
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
|
|
844
|
-
message ?? t("saveBar.unsaved")
|
|
845
|
-
] }),
|
|
846
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
847
|
-
/* @__PURE__ */ jsx(
|
|
848
|
-
"button",
|
|
849
|
-
{
|
|
850
|
-
type: "button",
|
|
851
|
-
disabled: saving,
|
|
852
|
-
onClick: () => api.handlers.current.onDiscard?.(),
|
|
853
|
-
className: "rounded-full px-3 py-1.5 text-sm font-medium text-background/70 transition-colors hover:bg-background/10 hover:text-background disabled:opacity-50",
|
|
854
|
-
children: discardLabel ?? t("common.discard")
|
|
855
|
-
}
|
|
856
|
-
),
|
|
857
|
-
/* @__PURE__ */ jsxs(
|
|
858
|
-
"button",
|
|
859
|
-
{
|
|
860
|
-
type: "button",
|
|
861
|
-
disabled: saving,
|
|
862
|
-
onClick: () => api.handlers.current.onSave?.(),
|
|
863
|
-
className: "inline-flex items-center rounded-full bg-background px-4 py-1.5 text-sm font-semibold text-foreground transition-colors hover:bg-background/90 disabled:opacity-60",
|
|
864
|
-
children: [
|
|
865
|
-
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
|
|
866
|
-
saveLabel ?? t("common.save")
|
|
867
|
-
]
|
|
868
|
-
}
|
|
869
|
-
)
|
|
870
|
-
] })
|
|
871
|
-
] }) });
|
|
872
|
-
}
|
|
873
|
-
function ToastProvider({
|
|
874
|
-
position = "bottom-center",
|
|
875
|
-
theme = "system"
|
|
876
|
-
}) {
|
|
877
|
-
const saveBarVisible = useSaveBarVisible();
|
|
878
|
-
const bottomOffset = position.startsWith("bottom") && saveBarVisible ? 84 : 24;
|
|
879
|
-
return /* @__PURE__ */ jsx(
|
|
880
|
-
Toaster,
|
|
881
|
-
{
|
|
882
|
-
position,
|
|
883
|
-
theme,
|
|
884
|
-
offset: bottomOffset,
|
|
885
|
-
closeButton: true,
|
|
886
|
-
richColors: true,
|
|
887
|
-
toastOptions: {
|
|
888
|
-
classNames: {
|
|
889
|
-
// Format harmonized with the SaveBar island: soft ring + xl shadow,
|
|
890
|
-
// generous rounded-2xl radius (full-pill looks wrong on multi-line
|
|
891
|
-
// toasts). Colors stay semantic (richColors).
|
|
892
|
-
toast: "group !gap-3 !rounded-3xl !border !px-4 !py-3 !shadow-xl !ring-1 !ring-black/5",
|
|
893
|
-
title: "!text-sm !font-semibold",
|
|
894
|
-
description: "!text-xs !mt-0.5",
|
|
895
|
-
closeButton: "!opacity-0 group-hover:!opacity-60 hover:!opacity-100 !transition-opacity"
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
);
|
|
900
|
-
}
|
|
901
|
-
var Tabs = TabsPrimitive.Root;
|
|
902
|
-
var TabsList = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
903
|
-
TabsPrimitive.List,
|
|
904
|
-
{
|
|
905
|
-
ref,
|
|
906
|
-
className: cn(
|
|
907
|
-
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
908
|
-
className
|
|
909
|
-
),
|
|
910
|
-
...props
|
|
911
|
-
}
|
|
912
|
-
));
|
|
913
|
-
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
914
|
-
var TabsTrigger = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
915
|
-
TabsPrimitive.Trigger,
|
|
916
|
-
{
|
|
917
|
-
ref,
|
|
918
|
-
className: cn(
|
|
919
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
920
|
-
className
|
|
921
|
-
),
|
|
922
|
-
...props
|
|
923
|
-
}
|
|
924
|
-
));
|
|
925
|
-
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
926
|
-
var TabsContent = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
927
|
-
TabsPrimitive.Content,
|
|
928
|
-
{
|
|
929
|
-
ref,
|
|
930
|
-
className: cn(
|
|
931
|
-
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
932
|
-
className
|
|
933
|
-
),
|
|
934
|
-
...props
|
|
935
|
-
}
|
|
936
|
-
));
|
|
937
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
938
|
-
var Separator3 = React10.forwardRef(
|
|
939
|
-
({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
940
|
-
SeparatorPrimitive.Root,
|
|
941
|
-
{
|
|
942
|
-
ref,
|
|
943
|
-
decorative,
|
|
944
|
-
orientation,
|
|
945
|
-
className: cn(
|
|
946
|
-
"shrink-0 bg-border",
|
|
947
|
-
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
948
|
-
className
|
|
949
|
-
),
|
|
950
|
-
...props
|
|
951
|
-
}
|
|
952
|
-
)
|
|
953
|
-
);
|
|
954
|
-
Separator3.displayName = SeparatorPrimitive.Root.displayName;
|
|
955
|
-
function useCrossModuleBack(onBack, parentLabel) {
|
|
956
|
-
const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
|
|
957
|
-
const referrer = useNavReferrer(currentPath);
|
|
958
|
-
const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath) ? referrer : null;
|
|
959
|
-
return {
|
|
960
|
-
onBack: cross ? () => navHistoryBack(onBack) : onBack,
|
|
961
|
-
parentLabel: cross ? cross.label : parentLabel,
|
|
962
|
-
isCrossModule: !!cross
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
function Breadcrumb({ parent, current, onBack }) {
|
|
966
|
-
const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parent);
|
|
967
|
-
useBackHandler(effectiveOnBack);
|
|
968
|
-
return /* @__PURE__ */ jsxs("nav", { className: "flex items-center gap-1.5 text-sm text-muted-foreground", children: [
|
|
969
|
-
/* @__PURE__ */ jsxs(
|
|
970
|
-
"button",
|
|
971
|
-
{
|
|
972
|
-
type: "button",
|
|
973
|
-
onClick: effectiveOnBack,
|
|
974
|
-
className: "inline-flex items-center gap-1 hover:text-foreground transition-colors",
|
|
975
|
-
children: [
|
|
976
|
-
/* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
977
|
-
effectiveParent
|
|
978
|
-
]
|
|
979
|
-
}
|
|
980
|
-
),
|
|
981
|
-
current !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
982
|
-
/* @__PURE__ */ jsx("span", { children: "/" }),
|
|
983
|
-
current ? /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium truncate max-w-[200px]", children: current }) : /* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 rounded bg-muted animate-pulse align-middle" })
|
|
984
|
-
] })
|
|
985
|
-
] });
|
|
986
|
-
}
|
|
987
|
-
var TooltipProvider = Tooltip2.Provider;
|
|
988
|
-
var Tooltip = Tooltip2.Root;
|
|
989
|
-
var TooltipTrigger = Tooltip2.Trigger;
|
|
990
|
-
var TooltipContent = React10.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
991
|
-
Tooltip2.Content,
|
|
992
|
-
{
|
|
993
|
-
ref,
|
|
994
|
-
sideOffset,
|
|
995
|
-
className: cn(
|
|
996
|
-
"fayz-glass-surface z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md 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",
|
|
997
|
-
className
|
|
998
|
-
),
|
|
999
|
-
...props
|
|
1000
|
-
}
|
|
1001
|
-
));
|
|
1002
|
-
TooltipContent.displayName = Tooltip2.Content.displayName;
|
|
1003
|
-
function formatTime12(h, m) {
|
|
1004
|
-
const period = h >= 12 ? "pm" : "am";
|
|
1005
|
-
const h12 = h % 12 || 12;
|
|
1006
|
-
return `${h12}:${String(m).padStart(2, "0")}${period}`;
|
|
1007
|
-
}
|
|
1008
|
-
function formatTime24(h, m) {
|
|
1009
|
-
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
1010
|
-
}
|
|
1011
|
-
function TimePicker({ value, onChange, interval = 15, min = "00:00", max = "23:45", use12h = false, readOnly, className, defaultOpen, onOpenChange }) {
|
|
1012
|
-
const [open, setOpen] = useState(defaultOpen ?? false);
|
|
1013
|
-
const ref = useRef(null);
|
|
1014
|
-
const triggerRef = useRef(null);
|
|
1015
|
-
const listRef = useRef(null);
|
|
1016
|
-
const [pos, setPos] = useState({ top: 0, left: 0, width: 0 });
|
|
1017
|
-
useEffect(() => {
|
|
1018
|
-
if (!open || !triggerRef.current) return;
|
|
1019
|
-
const rect = triggerRef.current.getBoundingClientRect();
|
|
1020
|
-
setPos({ top: rect.bottom + 4, left: rect.left, width: Math.max(rect.width, 120) });
|
|
1021
|
-
}, [open]);
|
|
1022
|
-
useEffect(() => {
|
|
1023
|
-
if (!open) return;
|
|
1024
|
-
const handler = (e) => {
|
|
1025
|
-
const target = e.target;
|
|
1026
|
-
if (ref.current?.contains(target)) return;
|
|
1027
|
-
if (listRef.current?.contains(target)) return;
|
|
1028
|
-
setOpen(false);
|
|
1029
|
-
onOpenChange?.(false);
|
|
1030
|
-
};
|
|
1031
|
-
document.addEventListener("mousedown", handler);
|
|
1032
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
1033
|
-
}, [open]);
|
|
1034
|
-
useEffect(() => {
|
|
1035
|
-
if (!open || !listRef.current || !value) return;
|
|
1036
|
-
const el = listRef.current.querySelector('[data-selected="true"]');
|
|
1037
|
-
if (el) el.scrollIntoView({ block: "center" });
|
|
1038
|
-
}, [open, value]);
|
|
1039
|
-
const slots = useMemo(() => {
|
|
1040
|
-
const [minH, minM] = min.split(":").map(Number);
|
|
1041
|
-
const [maxH, maxM] = max.split(":").map(Number);
|
|
1042
|
-
const minTotal = minH * 60 + minM;
|
|
1043
|
-
const maxTotal = maxH * 60 + maxM;
|
|
1044
|
-
const result = [];
|
|
1045
|
-
for (let t = minTotal; t <= maxTotal; t += interval) {
|
|
1046
|
-
const h = Math.floor(t / 60);
|
|
1047
|
-
const m = t % 60;
|
|
1048
|
-
result.push({
|
|
1049
|
-
value: formatTime24(h, m),
|
|
1050
|
-
label: use12h ? formatTime12(h, m) : formatTime24(h, m)
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
return result;
|
|
1054
|
-
}, [interval, min, max, use12h]);
|
|
1055
|
-
const displayValue = useMemo(() => {
|
|
1056
|
-
if (!value) return "";
|
|
1057
|
-
const [h, m] = value.split(":").map(Number);
|
|
1058
|
-
return use12h ? formatTime12(h, m) : formatTime24(h, m);
|
|
1059
|
-
}, [value, use12h]);
|
|
1060
|
-
function handleKeyDown(e) {
|
|
1061
|
-
if (readOnly) return;
|
|
1062
|
-
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
1063
|
-
e.preventDefault();
|
|
1064
|
-
const idx = slots.findIndex((s) => s.value === value);
|
|
1065
|
-
const next = e.key === "ArrowDown" ? Math.min(idx + 1, slots.length - 1) : Math.max(idx - 1, 0);
|
|
1066
|
-
if (slots[next]) onChange(slots[next].value);
|
|
1067
|
-
} else if (e.key === "Enter" || e.key === "Escape") {
|
|
1068
|
-
setOpen(false);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: `relative ${className ?? ""}`, children: [
|
|
1072
|
-
/* @__PURE__ */ jsxs(
|
|
1073
|
-
"button",
|
|
1074
|
-
{
|
|
1075
|
-
ref: triggerRef,
|
|
1076
|
-
type: "button",
|
|
1077
|
-
onClick: () => !readOnly && setOpen((p) => !p),
|
|
1078
|
-
onKeyDown: handleKeyDown,
|
|
1079
|
-
disabled: readOnly,
|
|
1080
|
-
className: `flex items-center gap-2 rounded-lg border px-3 py-2 text-sm w-full text-left transition-colors ${readOnly ? "bg-muted/40 text-muted-foreground cursor-default" : "bg-background hover:bg-muted/30"}`,
|
|
1081
|
-
children: [
|
|
1082
|
-
/* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
|
|
1083
|
-
/* @__PURE__ */ jsx("span", { children: displayValue || "--:--" })
|
|
1084
|
-
]
|
|
1085
|
-
}
|
|
1086
|
-
),
|
|
1087
|
-
open && !readOnly && createPortal(
|
|
1088
|
-
/* @__PURE__ */ jsx(
|
|
1089
|
-
"div",
|
|
1090
|
-
{
|
|
1091
|
-
ref: listRef,
|
|
1092
|
-
"data-modal-passthrough": true,
|
|
1093
|
-
style: { position: "fixed", top: pos.top, left: pos.left, width: pos.width, zIndex: 9999, pointerEvents: "auto" },
|
|
1094
|
-
className: "fayz-glass-surface min-w-[120px] max-h-48 overflow-y-auto rounded-xl border bg-popover shadow-xl py-1",
|
|
1095
|
-
children: slots.map((slot) => {
|
|
1096
|
-
const selected = slot.value === value;
|
|
1097
|
-
return /* @__PURE__ */ jsx(
|
|
1098
|
-
"button",
|
|
1099
|
-
{
|
|
1100
|
-
type: "button",
|
|
1101
|
-
"data-selected": selected,
|
|
1102
|
-
onClick: () => {
|
|
1103
|
-
onChange(slot.value);
|
|
1104
|
-
setOpen(false);
|
|
1105
|
-
},
|
|
1106
|
-
className: `block w-full px-3 py-1.5 text-sm text-left transition-colors ${selected ? "bg-primary/10 text-primary font-medium" : "hover:bg-muted/50"}`,
|
|
1107
|
-
children: slot.label
|
|
1108
|
-
},
|
|
1109
|
-
slot.value
|
|
1110
|
-
);
|
|
1111
|
-
})
|
|
1112
|
-
}
|
|
1113
|
-
),
|
|
1114
|
-
document.body
|
|
1115
|
-
)
|
|
1116
|
-
] });
|
|
1117
|
-
}
|
|
1118
|
-
function Skeleton({
|
|
1119
|
-
className,
|
|
1120
|
-
...props
|
|
1121
|
-
}) {
|
|
1122
|
-
return /* @__PURE__ */ jsx(
|
|
1123
|
-
"div",
|
|
1124
|
-
{
|
|
1125
|
-
className: cn("animate-pulse rounded-md bg-muted", className),
|
|
1126
|
-
...props
|
|
1127
|
-
}
|
|
1128
|
-
);
|
|
1129
|
-
}
|
|
1130
|
-
function TableSkeleton({ columns = 4, rows = 2 }) {
|
|
1131
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border overflow-hidden", children: [
|
|
1132
|
-
/* @__PURE__ */ jsx("div", { className: "border-b bg-muted/30 flex gap-4 px-4 py-2.5", children: Array.from({ length: columns }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-3 flex-1 rounded bg-muted-foreground/10" }, i)) }),
|
|
1133
|
-
/* @__PURE__ */ jsx("div", { className: "divide-y", children: Array.from({ length: rows }).map((_, r) => /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 px-4 py-3.5", children: Array.from({ length: columns }).map((_2, c) => /* @__PURE__ */ jsx(
|
|
1134
|
-
Skeleton,
|
|
1135
|
-
{
|
|
1136
|
-
className: cn(
|
|
1137
|
-
"h-3 rounded",
|
|
1138
|
-
c === 0 ? "flex-[2]" : "flex-1"
|
|
1139
|
-
)
|
|
1140
|
-
},
|
|
1141
|
-
c
|
|
1142
|
-
)) }, r)) })
|
|
1143
|
-
] });
|
|
1144
|
-
}
|
|
1145
|
-
function ListView({
|
|
1146
|
-
columns,
|
|
1147
|
-
data,
|
|
1148
|
-
loading = false,
|
|
1149
|
-
searchPlaceholder = "Search...",
|
|
1150
|
-
search: controlledSearch,
|
|
1151
|
-
onSearchChange,
|
|
1152
|
-
searchDebounce = 300,
|
|
1153
|
-
tags,
|
|
1154
|
-
allTagLabel = "All",
|
|
1155
|
-
activeTag,
|
|
1156
|
-
onTagChange,
|
|
1157
|
-
newLabel,
|
|
1158
|
-
onNew,
|
|
1159
|
-
onRowClick,
|
|
1160
|
-
emptyIcon: EmptyIcon,
|
|
1161
|
-
emptyMessage = "No results found.",
|
|
1162
|
-
emptyActionLabel,
|
|
1163
|
-
onEmptyAction,
|
|
1164
|
-
total,
|
|
1165
|
-
page = 1,
|
|
1166
|
-
pageSize = 25,
|
|
1167
|
-
onPageChange,
|
|
1168
|
-
toolbar,
|
|
1169
|
-
className
|
|
1170
|
-
}) {
|
|
1171
|
-
const [internalSearch, setInternalSearch] = useState(controlledSearch ?? "");
|
|
1172
|
-
const debounceRef = useRef();
|
|
1173
|
-
useEffect(() => {
|
|
1174
|
-
if (controlledSearch !== void 0) setInternalSearch(controlledSearch);
|
|
1175
|
-
}, [controlledSearch]);
|
|
1176
|
-
const handleSearchChange = useCallback((value) => {
|
|
1177
|
-
setInternalSearch(value);
|
|
1178
|
-
if (searchDebounce === 0) {
|
|
1179
|
-
onSearchChange?.(value);
|
|
1180
|
-
} else {
|
|
1181
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
1182
|
-
debounceRef.current = setTimeout(() => onSearchChange?.(value), searchDebounce);
|
|
1183
|
-
}
|
|
1184
|
-
}, [onSearchChange, searchDebounce]);
|
|
1185
|
-
const totalItems = total ?? data.length;
|
|
1186
|
-
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
1187
|
-
const showPagination = onPageChange && totalItems > pageSize;
|
|
1188
|
-
const showSearch = !!onSearchChange;
|
|
1189
|
-
const showTags = tags && tags.length > 0 && onTagChange;
|
|
1190
|
-
const showToolbar = showSearch || onNew || showTags;
|
|
1191
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
|
|
1192
|
-
showToolbar && /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between", children: [
|
|
1193
|
-
showSearch && /* @__PURE__ */ jsxs("div", { className: "relative flex-1 max-w-sm", children: [
|
|
1194
|
-
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" }),
|
|
1195
|
-
/* @__PURE__ */ jsx(
|
|
1196
|
-
"input",
|
|
1197
|
-
{
|
|
1198
|
-
type: "text",
|
|
1199
|
-
placeholder: searchPlaceholder,
|
|
1200
|
-
value: internalSearch,
|
|
1201
|
-
onChange: (e) => handleSearchChange(e.target.value),
|
|
1202
|
-
className: "w-full rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] pl-8 pr-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1203
|
-
}
|
|
1204
|
-
)
|
|
1205
|
-
] }),
|
|
1206
|
-
onNew && newLabel && /* @__PURE__ */ jsxs(Button, { variant: "default", size: "default", onClick: onNew, className: "shrink-0", children: [
|
|
1207
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
1208
|
-
newLabel
|
|
1209
|
-
] })
|
|
1210
|
-
] }),
|
|
1211
|
-
showTags && /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 flex-wrap", children: [
|
|
1212
|
-
/* @__PURE__ */ jsx(
|
|
1213
|
-
"button",
|
|
1214
|
-
{
|
|
1215
|
-
onClick: () => onTagChange(void 0),
|
|
1216
|
-
className: cn(
|
|
1217
|
-
"rounded-full px-2.5 py-1 text-xs font-medium transition-colors",
|
|
1218
|
-
!activeTag ? "bg-primary text-primary-foreground" : "bg-muted/50 text-muted-foreground hover:bg-muted"
|
|
1219
|
-
),
|
|
1220
|
-
children: allTagLabel
|
|
1221
|
-
}
|
|
1222
|
-
),
|
|
1223
|
-
tags.map((tag) => /* @__PURE__ */ jsx(
|
|
1224
|
-
"button",
|
|
1225
|
-
{
|
|
1226
|
-
onClick: () => onTagChange(tag.value),
|
|
1227
|
-
className: cn(
|
|
1228
|
-
"rounded-full px-2.5 py-1 text-xs font-medium transition-colors",
|
|
1229
|
-
activeTag === tag.value ? "bg-primary text-primary-foreground" : "bg-muted/50 text-muted-foreground hover:bg-muted"
|
|
1230
|
-
),
|
|
1231
|
-
children: tag.label
|
|
1232
|
-
},
|
|
1233
|
-
tag.value
|
|
1234
|
-
))
|
|
1235
|
-
] }),
|
|
1236
|
-
toolbar,
|
|
1237
|
-
loading ? /* @__PURE__ */ jsx(DataTable, { columns, data: [], loading: true, variant: "card" }) : data.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center rounded-lg border-2 border-dashed border-muted", children: [
|
|
1238
|
-
EmptyIcon && /* @__PURE__ */ jsx(EmptyIcon, { className: "h-8 w-8 text-muted-foreground/30 mb-2" }),
|
|
1239
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage }),
|
|
1240
|
-
emptyActionLabel && onEmptyAction && /* @__PURE__ */ jsx("button", { onClick: onEmptyAction, className: "text-xs text-primary hover:underline mt-1", children: emptyActionLabel })
|
|
1241
|
-
] }) : /* @__PURE__ */ jsx(
|
|
1242
|
-
DataTable,
|
|
1243
|
-
{
|
|
1244
|
-
columns,
|
|
1245
|
-
data,
|
|
1246
|
-
onRowClick,
|
|
1247
|
-
variant: "card"
|
|
1248
|
-
}
|
|
1249
|
-
),
|
|
1250
|
-
showPagination && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-1", children: [
|
|
1251
|
-
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1252
|
-
(page - 1) * pageSize + 1,
|
|
1253
|
-
"\u2013",
|
|
1254
|
-
Math.min(page * pageSize, totalItems),
|
|
1255
|
-
" of ",
|
|
1256
|
-
totalItems
|
|
1257
|
-
] }),
|
|
1258
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1259
|
-
/* @__PURE__ */ jsx(
|
|
1260
|
-
"button",
|
|
1261
|
-
{
|
|
1262
|
-
onClick: () => onPageChange(page - 1),
|
|
1263
|
-
disabled: page <= 1,
|
|
1264
|
-
className: "flex h-7 w-7 items-center justify-center rounded-md border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
|
|
1265
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" })
|
|
1266
|
-
}
|
|
1267
|
-
),
|
|
1268
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground px-2", children: [
|
|
1269
|
-
page,
|
|
1270
|
-
" / ",
|
|
1271
|
-
totalPages
|
|
1272
|
-
] }),
|
|
1273
|
-
/* @__PURE__ */ jsx(
|
|
1274
|
-
"button",
|
|
1275
|
-
{
|
|
1276
|
-
onClick: () => onPageChange(page + 1),
|
|
1277
|
-
disabled: page >= totalPages,
|
|
1278
|
-
className: "flex h-7 w-7 items-center justify-center rounded-md border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
|
|
1279
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })
|
|
1280
|
-
}
|
|
1281
|
-
)
|
|
1282
|
-
] })
|
|
1283
|
-
] })
|
|
1284
|
-
] });
|
|
1285
|
-
}
|
|
1286
|
-
var DAY_LABELS = {
|
|
1287
|
-
en: ["S", "M", "T", "W", "T", "F", "S"],
|
|
1288
|
-
"pt-BR": ["D", "S", "T", "Q", "Q", "S", "S"]
|
|
1289
|
-
};
|
|
1290
|
-
function toDateLocale(locale) {
|
|
1291
|
-
if (locale === "pt-BR") return "pt-BR";
|
|
1292
|
-
return "en-US";
|
|
1293
|
-
}
|
|
1294
|
-
function isSameDay(a, b) {
|
|
1295
|
-
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1296
|
-
}
|
|
1297
|
-
function isToday(d) {
|
|
1298
|
-
return isSameDay(d, /* @__PURE__ */ new Date());
|
|
1299
|
-
}
|
|
1300
|
-
function formatDate(date, locale) {
|
|
1301
|
-
return date.toLocaleDateString(toDateLocale(locale), { month: "short", day: "numeric", year: "numeric" });
|
|
1302
|
-
}
|
|
1303
|
-
function toDateStr(d) {
|
|
1304
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
1305
|
-
}
|
|
1306
|
-
function parseDate(s) {
|
|
1307
|
-
const [y, m, d] = s.split("-").map(Number);
|
|
1308
|
-
return new Date(y, m - 1, d);
|
|
1309
|
-
}
|
|
1310
|
-
function DatePicker({ value, onChange, className, defaultOpen, onOpenChange }) {
|
|
1311
|
-
const t = useTranslation();
|
|
1312
|
-
const locale = getCurrentLocale();
|
|
1313
|
-
const [open, setOpen] = useState(defaultOpen ?? false);
|
|
1314
|
-
const ref = useRef(null);
|
|
1315
|
-
const triggerRef = useRef(null);
|
|
1316
|
-
const dropdownRef = useRef(null);
|
|
1317
|
-
const selectedDate = value ? parseDate(value) : /* @__PURE__ */ new Date();
|
|
1318
|
-
const [viewMonth, setViewMonth] = useState(new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
|
|
1319
|
-
const [pos, setPos] = useState({ top: 0, left: 0 });
|
|
1320
|
-
const dayLabels = DAY_LABELS[locale] ?? DAY_LABELS.en;
|
|
1321
|
-
const dateLocale = toDateLocale(locale);
|
|
1322
|
-
useEffect(() => {
|
|
1323
|
-
if (!open || !triggerRef.current) return;
|
|
1324
|
-
const rect = triggerRef.current.getBoundingClientRect();
|
|
1325
|
-
const dropW = 260;
|
|
1326
|
-
let left = rect.left;
|
|
1327
|
-
if (left + dropW > window.innerWidth - 12) left = window.innerWidth - dropW - 12;
|
|
1328
|
-
if (left < 8) left = 8;
|
|
1329
|
-
setPos({ top: rect.bottom + 4, left });
|
|
1330
|
-
}, [open]);
|
|
1331
|
-
useEffect(() => {
|
|
1332
|
-
if (!open) return;
|
|
1333
|
-
const handler = (e) => {
|
|
1334
|
-
const target = e.target;
|
|
1335
|
-
if (ref.current?.contains(target)) return;
|
|
1336
|
-
if (dropdownRef.current?.contains(target)) return;
|
|
1337
|
-
setOpen(false);
|
|
1338
|
-
onOpenChange?.(false);
|
|
1339
|
-
};
|
|
1340
|
-
document.addEventListener("mousedown", handler);
|
|
1341
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
1342
|
-
}, [open, onOpenChange]);
|
|
1343
|
-
useEffect(() => {
|
|
1344
|
-
if (value) {
|
|
1345
|
-
const d = parseDate(value);
|
|
1346
|
-
setViewMonth(new Date(d.getFullYear(), d.getMonth(), 1));
|
|
1347
|
-
}
|
|
1348
|
-
}, [value]);
|
|
1349
|
-
const monthLabel = viewMonth.toLocaleDateString(dateLocale, { month: "long", year: "numeric" });
|
|
1350
|
-
const weeks = useMemo(() => {
|
|
1351
|
-
const year = viewMonth.getFullYear();
|
|
1352
|
-
const month = viewMonth.getMonth();
|
|
1353
|
-
const firstDay = new Date(year, month, 1);
|
|
1354
|
-
const startOffset = firstDay.getDay();
|
|
1355
|
-
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
1356
|
-
const prevMonthDays = new Date(year, month, 0).getDate();
|
|
1357
|
-
const cells = [];
|
|
1358
|
-
for (let i = startOffset - 1; i >= 0; i--) {
|
|
1359
|
-
cells.push({ date: new Date(year, month - 1, prevMonthDays - i), inMonth: false });
|
|
1360
|
-
}
|
|
1361
|
-
for (let d = 1; d <= daysInMonth; d++) {
|
|
1362
|
-
cells.push({ date: new Date(year, month, d), inMonth: true });
|
|
1363
|
-
}
|
|
1364
|
-
while (cells.length % 7 !== 0) {
|
|
1365
|
-
cells.push({ date: new Date(year, month + 1, cells.length - startOffset - daysInMonth + 1), inMonth: false });
|
|
1366
|
-
}
|
|
1367
|
-
const rows = [];
|
|
1368
|
-
for (let i = 0; i < cells.length; i += 7) rows.push(cells.slice(i, i + 7));
|
|
1369
|
-
return rows;
|
|
1370
|
-
}, [viewMonth]);
|
|
1371
|
-
function handleSelect(date) {
|
|
1372
|
-
onChange(toDateStr(date));
|
|
1373
|
-
setOpen(false);
|
|
1374
|
-
}
|
|
1375
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: `relative ${className ?? ""}`, children: [
|
|
1376
|
-
/* @__PURE__ */ jsxs(
|
|
1377
|
-
"button",
|
|
1378
|
-
{
|
|
1379
|
-
ref: triggerRef,
|
|
1380
|
-
type: "button",
|
|
1381
|
-
onClick: () => setOpen((p) => !p),
|
|
1382
|
-
className: "flex items-center gap-2 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm hover:bg-muted/30 transition-colors w-full text-left",
|
|
1383
|
-
children: [
|
|
1384
|
-
/* @__PURE__ */ jsx(Calendar, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
|
|
1385
|
-
/* @__PURE__ */ jsx("span", { children: value ? formatDate(selectedDate, locale) : t("common.selectDate") })
|
|
1386
|
-
]
|
|
1387
|
-
}
|
|
1388
|
-
),
|
|
1389
|
-
open && createPortal(
|
|
1390
|
-
/* @__PURE__ */ jsxs(
|
|
1391
|
-
"div",
|
|
1392
|
-
{
|
|
1393
|
-
ref: dropdownRef,
|
|
1394
|
-
"data-modal-passthrough": true,
|
|
1395
|
-
style: { position: "fixed", top: pos.top, left: pos.left, zIndex: 9999, pointerEvents: "auto" },
|
|
1396
|
-
className: "fayz-glass-surface rounded-xl border bg-popover shadow-xl p-3 w-[260px]",
|
|
1397
|
-
children: [
|
|
1398
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1399
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium capitalize", children: monthLabel }),
|
|
1400
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
|
|
1401
|
-
/* @__PURE__ */ jsx(
|
|
1402
|
-
"button",
|
|
1403
|
-
{
|
|
1404
|
-
type: "button",
|
|
1405
|
-
onClick: () => setViewMonth(new Date(viewMonth.getFullYear(), viewMonth.getMonth() - 1, 1)),
|
|
1406
|
-
className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors",
|
|
1407
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
1408
|
-
}
|
|
1409
|
-
),
|
|
1410
|
-
/* @__PURE__ */ jsx(
|
|
1411
|
-
"button",
|
|
1412
|
-
{
|
|
1413
|
-
type: "button",
|
|
1414
|
-
onClick: () => setViewMonth(new Date(viewMonth.getFullYear(), viewMonth.getMonth() + 1, 1)),
|
|
1415
|
-
className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors",
|
|
1416
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
1417
|
-
}
|
|
1418
|
-
)
|
|
1419
|
-
] })
|
|
1420
|
-
] }),
|
|
1421
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 mb-1", children: dayLabels.map((l, i) => /* @__PURE__ */ jsx("div", { className: "flex h-8 items-center justify-center text-[11px] font-medium text-muted-foreground", children: l }, i)) }),
|
|
1422
|
-
weeks.map((week, wi) => /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: week.map(({ date, inMonth }, di) => {
|
|
1423
|
-
const sel = value && isSameDay(date, selectedDate);
|
|
1424
|
-
const today = isToday(date);
|
|
1425
|
-
return /* @__PURE__ */ jsx(
|
|
1426
|
-
"button",
|
|
1427
|
-
{
|
|
1428
|
-
type: "button",
|
|
1429
|
-
onClick: () => handleSelect(date),
|
|
1430
|
-
className: `flex h-8 w-8 items-center justify-center rounded-full text-xs mx-auto transition-colors ${sel ? "bg-primary text-primary-foreground font-semibold" : today ? "bg-primary/10 text-primary font-semibold" : inMonth ? "hover:bg-muted text-foreground" : "text-muted-foreground/40 hover:bg-muted/50"}`,
|
|
1431
|
-
children: date.getDate()
|
|
1432
|
-
},
|
|
1433
|
-
di
|
|
1434
|
-
);
|
|
1435
|
-
}) }, wi)),
|
|
1436
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2 pt-2 border-t", children: /* @__PURE__ */ jsx(
|
|
1437
|
-
"button",
|
|
1438
|
-
{
|
|
1439
|
-
type: "button",
|
|
1440
|
-
onClick: () => {
|
|
1441
|
-
handleSelect(/* @__PURE__ */ new Date());
|
|
1442
|
-
},
|
|
1443
|
-
className: "text-xs text-primary font-medium hover:underline",
|
|
1444
|
-
children: t("agenda.calendar.today")
|
|
1445
|
-
}
|
|
1446
|
-
) })
|
|
1447
|
-
]
|
|
1448
|
-
}
|
|
1449
|
-
),
|
|
1450
|
-
document.body
|
|
1451
|
-
)
|
|
1452
|
-
] });
|
|
1453
|
-
}
|
|
1454
|
-
function SearchSelect({
|
|
1455
|
-
value,
|
|
1456
|
-
displayValue,
|
|
1457
|
-
onChange,
|
|
1458
|
-
onSearch,
|
|
1459
|
-
placeholder = "Search...",
|
|
1460
|
-
label,
|
|
1461
|
-
required,
|
|
1462
|
-
debounce = 250,
|
|
1463
|
-
minChars = 1,
|
|
1464
|
-
allowCreate,
|
|
1465
|
-
createLabel = "Add",
|
|
1466
|
-
onCreate,
|
|
1467
|
-
renderOption,
|
|
1468
|
-
loadOnOpen,
|
|
1469
|
-
className,
|
|
1470
|
-
disabled
|
|
1471
|
-
}) {
|
|
1472
|
-
const [open, setOpen] = React10.useState(false);
|
|
1473
|
-
const [query, setQuery] = React10.useState("");
|
|
1474
|
-
const [results, setResults] = React10.useState([]);
|
|
1475
|
-
const [loading, setLoading] = React10.useState(false);
|
|
1476
|
-
const [creating, setCreating] = React10.useState(false);
|
|
1477
|
-
const [selectedLabel, setSelectedLabel] = React10.useState(displayValue ?? "");
|
|
1478
|
-
const containerRef = React10.useRef(null);
|
|
1479
|
-
const inputRef = React10.useRef(null);
|
|
1480
|
-
const timerRef = React10.useRef();
|
|
1481
|
-
React10.useEffect(() => {
|
|
1482
|
-
function handler(e) {
|
|
1483
|
-
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
1484
|
-
setOpen(false);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
document.addEventListener("mousedown", handler);
|
|
1488
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
1489
|
-
}, []);
|
|
1490
|
-
React10.useEffect(() => {
|
|
1491
|
-
if (displayValue !== void 0) setSelectedLabel(displayValue);
|
|
1492
|
-
}, [displayValue]);
|
|
1493
|
-
React10.useEffect(() => {
|
|
1494
|
-
if (open && loadOnOpen && query.length === 0 && results.length === 0 && !loading) {
|
|
1495
|
-
setLoading(true);
|
|
1496
|
-
Promise.resolve(onSearch("")).then(setResults).catch(() => setResults([])).finally(() => setLoading(false));
|
|
1497
|
-
}
|
|
1498
|
-
}, [open, loadOnOpen]);
|
|
1499
|
-
function handleQueryChange(q) {
|
|
1500
|
-
setQuery(q);
|
|
1501
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1502
|
-
if (q.length < minChars && !(loadOnOpen && q.length === 0)) {
|
|
1503
|
-
setResults([]);
|
|
1504
|
-
setLoading(false);
|
|
1505
|
-
return;
|
|
1506
|
-
}
|
|
1507
|
-
setLoading(true);
|
|
1508
|
-
timerRef.current = setTimeout(async () => {
|
|
1509
|
-
try {
|
|
1510
|
-
const res = await onSearch(q);
|
|
1511
|
-
setResults(res);
|
|
1512
|
-
} catch {
|
|
1513
|
-
setResults([]);
|
|
1514
|
-
}
|
|
1515
|
-
setLoading(false);
|
|
1516
|
-
}, debounce);
|
|
1517
|
-
}
|
|
1518
|
-
function handleSelect(option) {
|
|
1519
|
-
onChange(option.id, option);
|
|
1520
|
-
setSelectedLabel(option.label);
|
|
1521
|
-
setOpen(false);
|
|
1522
|
-
setQuery("");
|
|
1523
|
-
setResults([]);
|
|
1524
|
-
}
|
|
1525
|
-
function handleClear() {
|
|
1526
|
-
onChange("", null);
|
|
1527
|
-
setSelectedLabel("");
|
|
1528
|
-
setQuery("");
|
|
1529
|
-
setResults([]);
|
|
1530
|
-
}
|
|
1531
|
-
async function handleCreate(name) {
|
|
1532
|
-
if (!onCreate) return;
|
|
1533
|
-
setCreating(true);
|
|
1534
|
-
setSelectedLabel(name);
|
|
1535
|
-
setOpen(false);
|
|
1536
|
-
setQuery("");
|
|
1537
|
-
setResults([]);
|
|
1538
|
-
try {
|
|
1539
|
-
await onCreate(name);
|
|
1540
|
-
} finally {
|
|
1541
|
-
setCreating(false);
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
function handleOpen() {
|
|
1545
|
-
if (disabled || creating) return;
|
|
1546
|
-
setOpen(true);
|
|
1547
|
-
setQuery("");
|
|
1548
|
-
setTimeout(() => inputRef.current?.focus(), 0);
|
|
1549
|
-
}
|
|
1550
|
-
const groups = React10.useMemo(() => {
|
|
1551
|
-
const map = /* @__PURE__ */ new Map();
|
|
1552
|
-
for (const opt of results) {
|
|
1553
|
-
const group = opt.group ?? "";
|
|
1554
|
-
if (!map.has(group)) map.set(group, []);
|
|
1555
|
-
map.get(group).push(opt);
|
|
1556
|
-
}
|
|
1557
|
-
return map;
|
|
1558
|
-
}, [results]);
|
|
1559
|
-
const hasValue = !!value && !!selectedLabel;
|
|
1560
|
-
const showDropdown = open && query.length >= minChars;
|
|
1561
|
-
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("relative", className), children: [
|
|
1562
|
-
label && /* @__PURE__ */ jsxs("label", { className: "text-xs font-medium text-muted-foreground", children: [
|
|
1563
|
-
label,
|
|
1564
|
-
required && " *"
|
|
1565
|
-
] }),
|
|
1566
|
-
/* @__PURE__ */ jsxs(
|
|
1567
|
-
"div",
|
|
1568
|
-
{
|
|
1569
|
-
className: cn(
|
|
1570
|
-
"mt-1 flex items-center gap-2 rounded-lg border bg-background transition-colors",
|
|
1571
|
-
open ? "ring-2 ring-primary/20" : "hover:bg-muted/30",
|
|
1572
|
-
disabled && "opacity-50 cursor-not-allowed"
|
|
1573
|
-
),
|
|
1574
|
-
children: [
|
|
1575
|
-
/* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 text-muted-foreground ml-3 shrink-0" }),
|
|
1576
|
-
open ? /* @__PURE__ */ jsx(
|
|
1577
|
-
"input",
|
|
1578
|
-
{
|
|
1579
|
-
ref: inputRef,
|
|
1580
|
-
type: "text",
|
|
1581
|
-
value: query,
|
|
1582
|
-
onChange: (e) => handleQueryChange(e.target.value),
|
|
1583
|
-
placeholder,
|
|
1584
|
-
className: "flex-1 bg-transparent py-2 pr-3 text-sm outline-none",
|
|
1585
|
-
onKeyDown: (e) => {
|
|
1586
|
-
if (e.key === "Escape") setOpen(false);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
) : /* @__PURE__ */ jsx(
|
|
1590
|
-
"button",
|
|
1591
|
-
{
|
|
1592
|
-
type: "button",
|
|
1593
|
-
onClick: handleOpen,
|
|
1594
|
-
className: "flex-1 text-left py-2 pr-3 text-sm outline-none",
|
|
1595
|
-
disabled: disabled || creating,
|
|
1596
|
-
children: creating ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [
|
|
1597
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }),
|
|
1598
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: selectedLabel })
|
|
1599
|
-
] }) : hasValue ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: selectedLabel }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder })
|
|
1600
|
-
}
|
|
1601
|
-
),
|
|
1602
|
-
hasValue && !open && /* @__PURE__ */ jsx("button", { onClick: handleClear, className: "pr-3 text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) }),
|
|
1603
|
-
!hasValue && !open && /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground mr-3 shrink-0" })
|
|
1604
|
-
]
|
|
1605
|
-
}
|
|
1606
|
-
),
|
|
1607
|
-
showDropdown && /* @__PURE__ */ jsx("div", { className: "fayz-glass-surface absolute top-full left-0 right-0 z-50 mt-1 rounded-lg border bg-popover shadow-lg overflow-hidden", children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-6 text-xs text-muted-foreground", children: [
|
|
1608
|
-
/* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
1609
|
-
" Searching..."
|
|
1610
|
-
] }) : results.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "px-4 py-4 text-center", children: [
|
|
1611
|
-
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1612
|
-
'No results for "',
|
|
1613
|
-
query,
|
|
1614
|
-
'"'
|
|
1615
|
-
] }),
|
|
1616
|
-
allowCreate && onCreate && /* @__PURE__ */ jsxs(
|
|
1617
|
-
"button",
|
|
1618
|
-
{
|
|
1619
|
-
onClick: () => handleCreate(query),
|
|
1620
|
-
className: "mt-2 text-xs text-primary hover:underline",
|
|
1621
|
-
children: [
|
|
1622
|
-
"+ ",
|
|
1623
|
-
createLabel,
|
|
1624
|
-
' "',
|
|
1625
|
-
query,
|
|
1626
|
-
'"'
|
|
1627
|
-
]
|
|
1628
|
-
}
|
|
1629
|
-
)
|
|
1630
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "max-h-64 overflow-auto py-1", children: [
|
|
1631
|
-
[...groups.entries()].map(([group, options]) => /* @__PURE__ */ jsxs(React10.Fragment, { children: [
|
|
1632
|
-
group && /* @__PURE__ */ jsx("div", { className: "sticky top-0 bg-muted/50 px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
|
|
1633
|
-
options.map((opt) => /* @__PURE__ */ jsx(
|
|
1634
|
-
"button",
|
|
1635
|
-
{
|
|
1636
|
-
onClick: () => handleSelect(opt),
|
|
1637
|
-
disabled: opt.disabled,
|
|
1638
|
-
className: cn(
|
|
1639
|
-
"w-full text-left px-3 py-2 hover:bg-muted/50 transition-colors flex items-center gap-2.5",
|
|
1640
|
-
opt.disabled && "opacity-40 cursor-not-allowed",
|
|
1641
|
-
opt.id === value && "bg-muted/30"
|
|
1642
|
-
),
|
|
1643
|
-
children: renderOption ? renderOption(opt) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1644
|
-
opt.icon,
|
|
1645
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1646
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm truncate", children: opt.label }),
|
|
1647
|
-
opt.subtitle && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground truncate", children: opt.subtitle })
|
|
1648
|
-
] })
|
|
1649
|
-
] })
|
|
1650
|
-
},
|
|
1651
|
-
opt.id
|
|
1652
|
-
))
|
|
1653
|
-
] }, group)),
|
|
1654
|
-
allowCreate && onCreate && /* @__PURE__ */ jsxs(
|
|
1655
|
-
"button",
|
|
1656
|
-
{
|
|
1657
|
-
onClick: () => handleCreate(query),
|
|
1658
|
-
className: "w-full text-left px-3 py-2 text-xs text-primary hover:bg-muted/50 transition-colors border-t",
|
|
1659
|
-
children: [
|
|
1660
|
-
"+ ",
|
|
1661
|
-
createLabel,
|
|
1662
|
-
' "',
|
|
1663
|
-
query,
|
|
1664
|
-
'"'
|
|
1665
|
-
]
|
|
1666
|
-
}
|
|
1667
|
-
)
|
|
1668
|
-
] }) })
|
|
1669
|
-
] });
|
|
1670
|
-
}
|
|
1671
|
-
function CurrencyInput({
|
|
1672
|
-
value,
|
|
1673
|
-
onChange,
|
|
1674
|
-
symbol = "R$",
|
|
1675
|
-
locale = "pt-BR",
|
|
1676
|
-
currencyCode = "BRL",
|
|
1677
|
-
label,
|
|
1678
|
-
placeholder,
|
|
1679
|
-
className,
|
|
1680
|
-
disabled
|
|
1681
|
-
}) {
|
|
1682
|
-
const inputRef = React10.useRef(null);
|
|
1683
|
-
const [focused, setFocused] = React10.useState(false);
|
|
1684
|
-
const format = React10.useCallback((v) => {
|
|
1685
|
-
return new Intl.NumberFormat(locale, {
|
|
1686
|
-
minimumFractionDigits: 2,
|
|
1687
|
-
maximumFractionDigits: 2
|
|
1688
|
-
}).format(v);
|
|
1689
|
-
}, [locale]);
|
|
1690
|
-
const displayValue = format(value);
|
|
1691
|
-
function handleKeyDown(e) {
|
|
1692
|
-
if (disabled) return;
|
|
1693
|
-
if (["Tab", "Escape", "ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
|
|
1694
|
-
e.preventDefault();
|
|
1695
|
-
if (e.key === "Backspace" || e.key === "Delete") {
|
|
1696
|
-
const cents = Math.floor(Math.round(value * 100) / 10);
|
|
1697
|
-
onChange(cents / 100);
|
|
1698
|
-
return;
|
|
1699
|
-
}
|
|
1700
|
-
if (!/^\d$/.test(e.key)) return;
|
|
1701
|
-
const currentCents = Math.round(value * 100);
|
|
1702
|
-
const newCents = currentCents * 10 + parseInt(e.key, 10);
|
|
1703
|
-
if (newCents > 99999999999) return;
|
|
1704
|
-
onChange(newCents / 100);
|
|
1705
|
-
}
|
|
1706
|
-
function handlePaste(e) {
|
|
1707
|
-
e.preventDefault();
|
|
1708
|
-
const text = e.clipboardData.getData("text");
|
|
1709
|
-
const cleaned = text.replace(/[^\d.,]/g, "");
|
|
1710
|
-
const normalized = cleaned.replace(",", ".");
|
|
1711
|
-
const parsed = parseFloat(normalized);
|
|
1712
|
-
if (!isNaN(parsed)) {
|
|
1713
|
-
onChange(Math.round(parsed * 100) / 100);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
1717
|
-
label && /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
1718
|
-
/* @__PURE__ */ jsxs(
|
|
1719
|
-
"div",
|
|
1720
|
-
{
|
|
1721
|
-
className: cn(
|
|
1722
|
-
"flex items-center rounded-lg border border-input bg-background ring-offset-background transition-colors cursor-text",
|
|
1723
|
-
label && "mt-1",
|
|
1724
|
-
focused && "border-ring ring-2 ring-ring ring-offset-2",
|
|
1725
|
-
disabled && "opacity-50 cursor-not-allowed"
|
|
1726
|
-
),
|
|
1727
|
-
onClick: () => inputRef.current?.focus(),
|
|
1728
|
-
children: [
|
|
1729
|
-
/* @__PURE__ */ jsx("span", { className: "pl-3 pr-1 text-sm font-medium text-muted-foreground select-none", children: symbol }),
|
|
1730
|
-
/* @__PURE__ */ jsx(
|
|
1731
|
-
"input",
|
|
1732
|
-
{
|
|
1733
|
-
ref: inputRef,
|
|
1734
|
-
type: "text",
|
|
1735
|
-
inputMode: "numeric",
|
|
1736
|
-
value: displayValue,
|
|
1737
|
-
onKeyDown: handleKeyDown,
|
|
1738
|
-
onPaste: handlePaste,
|
|
1739
|
-
onFocus: () => setFocused(true),
|
|
1740
|
-
onBlur: () => setFocused(false),
|
|
1741
|
-
placeholder: placeholder ?? format(0),
|
|
1742
|
-
disabled,
|
|
1743
|
-
readOnly: false,
|
|
1744
|
-
className: "flex-1 bg-transparent px-2 py-2 text-sm outline-none text-right tabular-nums caret-primary selection:bg-primary/20",
|
|
1745
|
-
onChange: () => {
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
)
|
|
1749
|
-
]
|
|
1750
|
-
}
|
|
1751
|
-
)
|
|
1752
|
-
] });
|
|
1753
|
-
}
|
|
1754
|
-
var VARIANT_CONFIG = {
|
|
1755
|
-
default: {
|
|
1756
|
-
icon: Info,
|
|
1757
|
-
iconBg: "bg-info/10",
|
|
1758
|
-
iconColor: "text-info",
|
|
1759
|
-
buttonVariant: "default"
|
|
1760
|
-
},
|
|
1761
|
-
destructive: {
|
|
1762
|
-
icon: XCircle,
|
|
1763
|
-
iconBg: "bg-destructive/10",
|
|
1764
|
-
iconColor: "text-destructive",
|
|
1765
|
-
buttonVariant: "destructive"
|
|
1766
|
-
},
|
|
1767
|
-
warning: {
|
|
1768
|
-
icon: AlertTriangle,
|
|
1769
|
-
iconBg: "bg-warning/10",
|
|
1770
|
-
iconColor: "text-warning",
|
|
1771
|
-
buttonVariant: "destructive"
|
|
1772
|
-
},
|
|
1773
|
-
success: {
|
|
1774
|
-
icon: CheckCircle2,
|
|
1775
|
-
iconBg: "bg-success/10",
|
|
1776
|
-
iconColor: "text-success",
|
|
1777
|
-
buttonVariant: "default"
|
|
1778
|
-
}
|
|
1779
|
-
};
|
|
1780
|
-
function ConfirmDialog({
|
|
1781
|
-
open,
|
|
1782
|
-
onConfirm,
|
|
1783
|
-
onCancel,
|
|
1784
|
-
title,
|
|
1785
|
-
description,
|
|
1786
|
-
confirmLabel = "Confirm",
|
|
1787
|
-
cancelLabel = "Cancel",
|
|
1788
|
-
variant = "default",
|
|
1789
|
-
loading = false
|
|
1790
|
-
}) {
|
|
1791
|
-
const config = VARIANT_CONFIG[variant];
|
|
1792
|
-
const Icon2 = config.icon;
|
|
1793
|
-
React10.useEffect(() => {
|
|
1794
|
-
if (!open) return;
|
|
1795
|
-
function handleKey(e) {
|
|
1796
|
-
if (e.key === "Escape") onCancel();
|
|
1797
|
-
}
|
|
1798
|
-
document.addEventListener("keydown", handleKey);
|
|
1799
|
-
return () => document.removeEventListener("keydown", handleKey);
|
|
1800
|
-
}, [open, onCancel]);
|
|
1801
|
-
if (!open) return null;
|
|
1802
|
-
return createPortal(
|
|
1803
|
-
/* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[60] flex items-center justify-center", children: [
|
|
1804
|
-
/* @__PURE__ */ jsx(
|
|
1805
|
-
"div",
|
|
1806
|
-
{
|
|
1807
|
-
className: "absolute inset-0 bg-black/50 backdrop-blur-[2px] animate-fade-in",
|
|
1808
|
-
onClick: onCancel
|
|
1809
|
-
}
|
|
1810
|
-
),
|
|
1811
|
-
/* @__PURE__ */ jsx(
|
|
1812
|
-
"div",
|
|
1813
|
-
{
|
|
1814
|
-
className: "relative z-10 w-full max-w-sm mx-4 rounded-modal border border-border bg-card shadow-lg animate-zoom-in",
|
|
1815
|
-
role: "alertdialog",
|
|
1816
|
-
"aria-modal": "true",
|
|
1817
|
-
"aria-labelledby": "confirm-title",
|
|
1818
|
-
"aria-describedby": description ? "confirm-desc" : void 0,
|
|
1819
|
-
children: /* @__PURE__ */ jsxs("div", { className: "p-6", children: [
|
|
1820
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
1821
|
-
/* @__PURE__ */ jsx("div", { className: cn("flex h-10 w-10 items-center justify-center rounded-full shrink-0", config.iconBg), children: /* @__PURE__ */ jsx(Icon2, { className: cn("h-5 w-5", config.iconColor) }) }),
|
|
1822
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 pt-0.5", children: [
|
|
1823
|
-
/* @__PURE__ */ jsx("h3", { id: "confirm-title", className: "text-sm font-semibold text-foreground", children: title }),
|
|
1824
|
-
description && /* @__PURE__ */ jsx("p", { id: "confirm-desc", className: "text-xs text-muted-foreground mt-1 leading-relaxed", children: description })
|
|
1825
|
-
] })
|
|
1826
|
-
] }),
|
|
1827
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-5", children: [
|
|
1828
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onCancel, disabled: loading, children: cancelLabel }),
|
|
1829
|
-
/* @__PURE__ */ jsx(Button, { variant: config.buttonVariant, size: "sm", onClick: onConfirm, disabled: loading, children: loading ? "Processing..." : confirmLabel })
|
|
1830
|
-
] })
|
|
1831
|
-
] })
|
|
1832
|
-
}
|
|
1833
|
-
)
|
|
1834
|
-
] }),
|
|
1835
|
-
document.body
|
|
1836
|
-
);
|
|
1837
|
-
}
|
|
1838
|
-
var Popover = Popover2.Root;
|
|
1839
|
-
var PopoverTrigger = Popover2.Trigger;
|
|
1840
|
-
var PopoverContent = React10.forwardRef(({ className, align = "start", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(Popover2.Portal, { children: /* @__PURE__ */ jsx(
|
|
1841
|
-
Popover2.Content,
|
|
1842
|
-
{
|
|
1843
|
-
ref,
|
|
1844
|
-
align,
|
|
1845
|
-
sideOffset,
|
|
1846
|
-
className: cn(
|
|
1847
|
-
"fayz-glass-surface z-[110] w-72 rounded-xl border bg-popover p-0 text-popover-foreground shadow-lg outline-none",
|
|
1848
|
-
"data-[state=open]:animate-zoom-in data-[state=closed]:animate-zoom-out",
|
|
1849
|
-
className
|
|
1850
|
-
),
|
|
1851
|
-
...props
|
|
1852
|
-
}
|
|
1853
|
-
) }));
|
|
1854
|
-
PopoverContent.displayName = Popover2.Content.displayName;
|
|
1855
|
-
var SIZE = {
|
|
1856
|
-
sm: { container: "h-[26px]", segment: "px-2.5 text-xs" },
|
|
1857
|
-
md: { container: "h-[30px]", segment: "px-3 text-sm" }
|
|
1858
|
-
};
|
|
1859
|
-
function SegmentedControl({
|
|
1860
|
-
options,
|
|
1861
|
-
value,
|
|
1862
|
-
onChange,
|
|
1863
|
-
size = "sm",
|
|
1864
|
-
className,
|
|
1865
|
-
...rest
|
|
1866
|
-
}) {
|
|
1867
|
-
const items = options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
|
|
1868
|
-
return /* @__PURE__ */ jsx(
|
|
1869
|
-
"div",
|
|
1870
|
-
{
|
|
1871
|
-
role: "tablist",
|
|
1872
|
-
"aria-label": rest["aria-label"],
|
|
1873
|
-
className: cn(
|
|
1874
|
-
"inline-flex items-center rounded-button border border-border bg-card p-0.5 shadow-button",
|
|
1875
|
-
SIZE[size].container,
|
|
1876
|
-
className
|
|
1877
|
-
),
|
|
1878
|
-
children: items.map((item) => {
|
|
1879
|
-
const active = item.value === value;
|
|
1880
|
-
return /* @__PURE__ */ jsx(
|
|
1881
|
-
"button",
|
|
1882
|
-
{
|
|
1883
|
-
type: "button",
|
|
1884
|
-
role: "tab",
|
|
1885
|
-
"aria-selected": active,
|
|
1886
|
-
onClick: () => onChange(item.value),
|
|
1887
|
-
className: cn(
|
|
1888
|
-
"flex h-full items-center rounded-button font-semibold transition-colors",
|
|
1889
|
-
SIZE[size].segment,
|
|
1890
|
-
active ? "bg-foreground text-background" : "text-muted-foreground hover:bg-muted/60"
|
|
1891
|
-
),
|
|
1892
|
-
children: item.label
|
|
1893
|
-
},
|
|
1894
|
-
item.value
|
|
1895
|
-
);
|
|
1896
|
-
})
|
|
1897
|
-
}
|
|
1898
|
-
);
|
|
1899
|
-
}
|
|
1900
|
-
var useLayoutStore = create((set) => ({
|
|
1901
|
-
sidebarOpen: true,
|
|
1902
|
-
sidebarCollapsed: false,
|
|
1903
|
-
mobileDrawerOpen: false,
|
|
1904
|
-
commandPaletteOpen: false,
|
|
1905
|
-
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
|
|
1906
|
-
setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
|
|
1907
|
-
toggleMobileDrawer: () => set((state) => ({ mobileDrawerOpen: !state.mobileDrawerOpen })),
|
|
1908
|
-
setCommandPaletteOpen: (open) => set({ commandPaletteOpen: open })
|
|
1909
|
-
}));
|
|
1910
|
-
var ICON_MAP = {
|
|
1911
|
-
Home,
|
|
1912
|
-
Users,
|
|
1913
|
-
Settings,
|
|
1914
|
-
CreditCard,
|
|
1915
|
-
Bell,
|
|
1916
|
-
Calendar: Calendar,
|
|
1917
|
-
Package,
|
|
1918
|
-
BarChart3,
|
|
1919
|
-
FileText,
|
|
1920
|
-
Mail,
|
|
1921
|
-
DollarSign,
|
|
1922
|
-
Megaphone,
|
|
1923
|
-
ShoppingCart,
|
|
1924
|
-
Target,
|
|
1925
|
-
Wrench,
|
|
1926
|
-
ClipboardList,
|
|
1927
|
-
Briefcase,
|
|
1928
|
-
UserCog,
|
|
1929
|
-
BookOpen,
|
|
1930
|
-
MessageCircle,
|
|
1931
|
-
Globe,
|
|
1932
|
-
Percent,
|
|
1933
|
-
Tag,
|
|
1934
|
-
Camera,
|
|
1935
|
-
UtensilsCrossed,
|
|
1936
|
-
MapPin,
|
|
1937
|
-
Handshake,
|
|
1938
|
-
Contact,
|
|
1939
|
-
Building2,
|
|
1940
|
-
Filter,
|
|
1941
|
-
Plus: Plus,
|
|
1942
|
-
List: List,
|
|
1943
|
-
Search: Search,
|
|
1944
|
-
Dog,
|
|
1945
|
-
Cat,
|
|
1946
|
-
PawPrint,
|
|
1947
|
-
Heart,
|
|
1948
|
-
LayoutTemplate,
|
|
1949
|
-
LeafyGreen,
|
|
1950
|
-
Apple,
|
|
1951
|
-
Egg,
|
|
1952
|
-
Wheat,
|
|
1953
|
-
PartyPopper,
|
|
1954
|
-
Radio,
|
|
1955
|
-
CalendarDays,
|
|
1956
|
-
Banknote,
|
|
1957
|
-
Layers,
|
|
1958
|
-
Music,
|
|
1959
|
-
Eye,
|
|
1960
|
-
ListMusic,
|
|
1961
|
-
Disc3,
|
|
1962
|
-
UsersRound,
|
|
1963
|
-
Mic,
|
|
1964
|
-
Zap,
|
|
1965
|
-
Award,
|
|
1966
|
-
LayoutDashboard,
|
|
1967
|
-
MessageSquare,
|
|
1968
|
-
Star,
|
|
1969
|
-
Workflow
|
|
1970
|
-
};
|
|
1971
|
-
function getIcon(name) {
|
|
1972
|
-
return ICON_MAP[name] ?? Home;
|
|
1973
|
-
}
|
|
1974
|
-
function NavItem({
|
|
1975
|
-
item,
|
|
1976
|
-
collapsed,
|
|
1977
|
-
isActive,
|
|
1978
|
-
onNavigate
|
|
1979
|
-
}) {
|
|
1980
|
-
const Icon2 = getIcon(item.icon);
|
|
1981
|
-
const content = /* @__PURE__ */ jsxs(
|
|
1982
|
-
"button",
|
|
1983
|
-
{
|
|
1984
|
-
onClick: () => onNavigate(item.route),
|
|
1985
|
-
className: cn(
|
|
1986
|
-
"flex w-full items-center gap-3 rounded-md px-2.5 py-1.5 text-[13px] font-medium transition-colors",
|
|
1987
|
-
"hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
|
|
1988
|
-
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
|
|
1989
|
-
collapsed && "justify-center px-2"
|
|
1990
|
-
),
|
|
1991
|
-
children: [
|
|
1992
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 shrink-0" }),
|
|
1993
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1994
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: item.label }),
|
|
1995
|
-
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 })
|
|
1996
|
-
] })
|
|
1997
|
-
]
|
|
1998
|
-
}
|
|
1999
|
-
);
|
|
2000
|
-
if (collapsed) {
|
|
2001
|
-
return /* @__PURE__ */ jsxs(Tooltip2.Root, { delayDuration: 0, children: [
|
|
2002
|
-
/* @__PURE__ */ jsx(Tooltip2.Trigger, { asChild: true, children: content }),
|
|
2003
|
-
/* @__PURE__ */ jsx(Tooltip2.Portal, { children: /* @__PURE__ */ jsxs(
|
|
2004
|
-
Tooltip2.Content,
|
|
2005
|
-
{
|
|
2006
|
-
side: "right",
|
|
2007
|
-
sideOffset: 8,
|
|
2008
|
-
className: "fayz-glass-surface z-50 rounded-md bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md",
|
|
2009
|
-
children: [
|
|
2010
|
-
item.label,
|
|
2011
|
-
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 }),
|
|
2012
|
-
/* @__PURE__ */ jsx(Tooltip2.Arrow, { className: "fill-popover" })
|
|
2013
|
-
]
|
|
2014
|
-
}
|
|
2015
|
-
) })
|
|
2016
|
-
] });
|
|
2017
|
-
}
|
|
2018
|
-
return content;
|
|
2019
|
-
}
|
|
2020
|
-
function NavGroup({
|
|
2021
|
-
item,
|
|
2022
|
-
collapsed,
|
|
2023
|
-
currentPath,
|
|
2024
|
-
onNavigate
|
|
2025
|
-
}) {
|
|
2026
|
-
const [open, setOpen] = React10.useState(
|
|
2027
|
-
() => item.children?.some((c) => currentPath === c.route || currentPath.startsWith(c.route + "/")) ?? false
|
|
2028
|
-
);
|
|
2029
|
-
const Icon2 = getIcon(item.icon);
|
|
2030
|
-
const isChildActive = item.children?.some(
|
|
2031
|
-
(c) => currentPath === c.route || currentPath.startsWith(c.route + "/")
|
|
2032
|
-
);
|
|
2033
|
-
if (collapsed) {
|
|
2034
|
-
return /* @__PURE__ */ jsxs(Tooltip2.Root, { delayDuration: 0, children: [
|
|
2035
|
-
/* @__PURE__ */ jsx(Tooltip2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
2036
|
-
"button",
|
|
2037
|
-
{
|
|
2038
|
-
onClick: () => onNavigate(item.route),
|
|
2039
|
-
className: cn(
|
|
2040
|
-
"flex w-full items-center justify-center rounded-md px-2 py-2 text-sm font-medium transition-colors",
|
|
2041
|
-
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
2042
|
-
isChildActive ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-muted"
|
|
2043
|
-
),
|
|
2044
|
-
children: /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 shrink-0" })
|
|
2045
|
-
}
|
|
2046
|
-
) }),
|
|
2047
|
-
/* @__PURE__ */ jsx(Tooltip2.Portal, { children: /* @__PURE__ */ jsxs(Tooltip2.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: [
|
|
2048
|
-
item.label,
|
|
2049
|
-
/* @__PURE__ */ jsx(Tooltip2.Arrow, { className: "fill-popover" })
|
|
2050
|
-
] }) })
|
|
2051
|
-
] });
|
|
2052
|
-
}
|
|
2053
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2054
|
-
/* @__PURE__ */ jsxs(
|
|
2055
|
-
"button",
|
|
2056
|
-
{
|
|
2057
|
-
onClick: () => setOpen(!open),
|
|
2058
|
-
className: cn(
|
|
2059
|
-
"flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
|
|
2060
|
-
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
2061
|
-
isChildActive ? "text-sidebar-accent-foreground" : "text-sidebar-muted"
|
|
2062
|
-
),
|
|
2063
|
-
children: [
|
|
2064
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 shrink-0" }),
|
|
2065
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: item.label }),
|
|
2066
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3.5 w-3.5 shrink-0 transition-transform", open && "rotate-180") })
|
|
2067
|
-
]
|
|
2068
|
-
}
|
|
2069
|
-
),
|
|
2070
|
-
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) => {
|
|
2071
|
-
const ChildIcon = getIcon(child.icon);
|
|
2072
|
-
const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
|
|
2073
|
-
return /* @__PURE__ */ jsxs(
|
|
2074
|
-
"button",
|
|
2075
|
-
{
|
|
2076
|
-
onClick: () => onNavigate(child.route),
|
|
2077
|
-
className: cn(
|
|
2078
|
-
"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors",
|
|
2079
|
-
isActive ? "font-medium text-sidebar-accent-foreground" : "text-sidebar-muted hover:text-sidebar-accent-foreground"
|
|
2080
|
-
),
|
|
2081
|
-
children: [
|
|
2082
|
-
/* @__PURE__ */ jsx(ChildIcon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
2083
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: child.label })
|
|
2084
|
-
]
|
|
2085
|
-
},
|
|
2086
|
-
child.id
|
|
2087
|
-
);
|
|
2088
|
-
}) })
|
|
2089
|
-
] });
|
|
2090
|
-
}
|
|
2091
|
-
function Sidebar({
|
|
2092
|
-
navigation,
|
|
2093
|
-
logo,
|
|
2094
|
-
collapsed,
|
|
2095
|
-
onToggle,
|
|
2096
|
-
onNavigate,
|
|
2097
|
-
currentPath: currentPathProp = "/",
|
|
2098
|
-
user: _user,
|
|
2099
|
-
orgSwitcher,
|
|
2100
|
-
topContent,
|
|
2101
|
-
footerContent,
|
|
2102
|
-
borderless,
|
|
2103
|
-
userMenuSlot,
|
|
2104
|
-
notificationSlot,
|
|
2105
|
-
unreadCount = 0
|
|
2106
|
-
}) {
|
|
2107
|
-
const currentPath = currentPathProp;
|
|
2108
|
-
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
2109
|
-
const mainItems = navigation.filter((item) => item.section === "main");
|
|
2110
|
-
const secondaryItems = navigation.filter((item) => item.section === "secondary");
|
|
2111
|
-
const settingsItems = navigation.filter((item) => item.section === "settings");
|
|
2112
|
-
const handleNavigate = (route) => {
|
|
2113
|
-
onNavigate?.(route);
|
|
2114
|
-
};
|
|
2115
|
-
return /* @__PURE__ */ jsx(Tooltip2.Provider, { children: /* @__PURE__ */ jsxs(
|
|
2116
|
-
"aside",
|
|
2117
|
-
{
|
|
2118
|
-
"data-print": "hide",
|
|
2119
|
-
className: cn(
|
|
2120
|
-
"flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
|
|
2121
|
-
!borderless && "border-r border-sidebar-border",
|
|
2122
|
-
collapsed ? "w-16" : "w-64"
|
|
2123
|
-
),
|
|
2124
|
-
children: [
|
|
2125
|
-
/* @__PURE__ */ jsxs(
|
|
2126
|
-
"div",
|
|
2127
|
-
{
|
|
2128
|
-
className: cn(
|
|
2129
|
-
"flex h-14 items-center justify-between px-4",
|
|
2130
|
-
!borderless && "border-b border-sidebar-border",
|
|
2131
|
-
collapsed && "justify-center px-2"
|
|
2132
|
-
),
|
|
2133
|
-
children: [
|
|
2134
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2135
|
-
logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
|
|
2136
|
-
/* @__PURE__ */ jsx(
|
|
2137
|
-
"button",
|
|
2138
|
-
{
|
|
2139
|
-
onClick: onToggle,
|
|
2140
|
-
className: "ml-auto rounded-md p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
2141
|
-
"aria-label": "Collapse sidebar",
|
|
2142
|
-
children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
2143
|
-
}
|
|
2144
|
-
)
|
|
2145
|
-
] }),
|
|
2146
|
-
collapsed && /* @__PURE__ */ jsx(
|
|
2147
|
-
"button",
|
|
2148
|
-
{
|
|
2149
|
-
onClick: onToggle,
|
|
2150
|
-
className: "rounded-md p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
2151
|
-
"aria-label": "Expand sidebar",
|
|
2152
|
-
children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
|
|
2153
|
-
}
|
|
2154
|
-
)
|
|
2155
|
-
]
|
|
2156
|
-
}
|
|
2157
|
-
),
|
|
2158
|
-
/* @__PURE__ */ jsxs("nav", { className: "flex-1 space-y-1 overflow-y-auto p-2", children: [
|
|
2159
|
-
topContent && /* @__PURE__ */ jsx("div", { className: "mb-4 space-y-2", children: topContent }),
|
|
2160
|
-
mainItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-1", children: mainItems.map(
|
|
2161
|
-
(item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, currentPath, onNavigate: handleNavigate }, item.id) : /* @__PURE__ */ jsx(
|
|
2162
|
-
NavItem,
|
|
2163
|
-
{
|
|
2164
|
-
item,
|
|
2165
|
-
collapsed,
|
|
2166
|
-
isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
|
|
2167
|
-
onNavigate: handleNavigate
|
|
2168
|
-
},
|
|
2169
|
-
item.id
|
|
2170
|
-
)
|
|
2171
|
-
) }),
|
|
2172
|
-
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6 space-y-1", children: secondaryItems.map(
|
|
2173
|
-
(item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, currentPath, onNavigate: handleNavigate }, item.id) : /* @__PURE__ */ jsx(
|
|
2174
|
-
NavItem,
|
|
2175
|
-
{
|
|
2176
|
-
item,
|
|
2177
|
-
collapsed,
|
|
2178
|
-
isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
|
|
2179
|
-
onNavigate: handleNavigate
|
|
2180
|
-
},
|
|
2181
|
-
item.id
|
|
2182
|
-
)
|
|
2183
|
-
) })
|
|
2184
|
-
] }),
|
|
2185
|
-
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(
|
|
2186
|
-
NavItem,
|
|
2187
|
-
{
|
|
2188
|
-
item,
|
|
2189
|
-
collapsed,
|
|
2190
|
-
isActive: currentPath === item.route || currentPath.startsWith(item.route + "/"),
|
|
2191
|
-
onNavigate: handleNavigate
|
|
2192
|
-
},
|
|
2193
|
-
item.id
|
|
2194
|
-
)) }),
|
|
2195
|
-
footerContent && /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsx("div", { className: "space-y-2", children: footerContent }) }),
|
|
2196
|
-
orgSwitcher && React10.isValidElement(orgSwitcher) ? React10.cloneElement(orgSwitcher, { collapsed }) : orgSwitcher,
|
|
2197
|
-
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
2198
|
-
"flex items-center rounded-md",
|
|
2199
|
-
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
2200
|
-
), children: [
|
|
2201
|
-
userMenuSlot,
|
|
2202
|
-
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
2203
|
-
/* @__PURE__ */ jsx(
|
|
2204
|
-
"button",
|
|
2205
|
-
{
|
|
2206
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
2207
|
-
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",
|
|
2208
|
-
"aria-label": "Search",
|
|
2209
|
-
children: /* @__PURE__ */ jsx(Search, { className: "h-4 w-4" })
|
|
2210
|
-
}
|
|
2211
|
-
),
|
|
2212
|
-
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
2213
|
-
"button",
|
|
2214
|
-
{
|
|
2215
|
-
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",
|
|
2216
|
-
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
2217
|
-
children: [
|
|
2218
|
-
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
2219
|
-
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
2220
|
-
]
|
|
2221
|
-
}
|
|
2222
|
-
)
|
|
2223
|
-
] }) })
|
|
2224
|
-
]
|
|
2225
|
-
}
|
|
2226
|
-
) });
|
|
2227
|
-
}
|
|
2228
|
-
function TopbarUserMenu({
|
|
2229
|
-
user,
|
|
2230
|
-
onSignOut,
|
|
2231
|
-
onProfile,
|
|
2232
|
-
onSettings,
|
|
2233
|
-
onBilling,
|
|
2234
|
-
userMenuExtras
|
|
2235
|
-
}) {
|
|
2236
|
-
const [open, setOpen] = React10.useState(false);
|
|
2237
|
-
if (!user) return null;
|
|
2238
|
-
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
2239
|
-
const item = (label, onClick, Icon2) => onClick ? /* @__PURE__ */ jsxs(
|
|
2240
|
-
"button",
|
|
2241
|
-
{
|
|
2242
|
-
onClick: () => {
|
|
2243
|
-
setOpen(false);
|
|
2244
|
-
onClick();
|
|
2245
|
-
},
|
|
2246
|
-
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",
|
|
2247
|
-
children: [
|
|
2248
|
-
Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 text-muted-foreground" }),
|
|
2249
|
-
/* @__PURE__ */ jsx("span", { children: label })
|
|
2250
|
-
]
|
|
2251
|
-
},
|
|
2252
|
-
label
|
|
2253
|
-
) : null;
|
|
2254
|
-
return /* @__PURE__ */ jsxs(Popover2.Root, { open, onOpenChange: setOpen, children: [
|
|
2255
|
-
/* @__PURE__ */ jsx(Popover2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
2256
|
-
"button",
|
|
2257
|
-
{
|
|
2258
|
-
"aria-label": "User menu",
|
|
2259
|
-
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",
|
|
2260
|
-
children: user.avatarUrl ? (
|
|
2261
|
-
// eslint-disable-next-line @next/next/no-img-element
|
|
2262
|
-
/* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
|
|
2263
|
-
) : initial
|
|
2264
|
-
}
|
|
2265
|
-
) }),
|
|
2266
|
-
/* @__PURE__ */ jsx(Popover2.Portal, { children: /* @__PURE__ */ jsxs(
|
|
2267
|
-
Popover2.Content,
|
|
2268
|
-
{
|
|
2269
|
-
align: "end",
|
|
2270
|
-
sideOffset: 8,
|
|
2271
|
-
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",
|
|
2272
|
-
children: [
|
|
2273
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
2274
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
|
|
2275
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
2276
|
-
] }),
|
|
2277
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
|
|
2278
|
-
item("Perfil", onProfile, UserCog),
|
|
2279
|
-
item("Configura\xE7\xF5es", onSettings, Settings),
|
|
2280
|
-
item("Cobran\xE7a", onBilling, CreditCard),
|
|
2281
|
-
userMenuExtras?.map((e) => item(e.label, e.onClick)),
|
|
2282
|
-
onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2283
|
-
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
2284
|
-
item("Sair", onSignOut)
|
|
2285
|
-
] })
|
|
2286
|
-
] })
|
|
2287
|
-
]
|
|
2288
|
-
}
|
|
2289
|
-
) })
|
|
2290
|
-
] });
|
|
2291
|
-
}
|
|
2292
|
-
var ICON_MAP2 = {
|
|
2293
|
-
Home: Home,
|
|
2294
|
-
Users: Users,
|
|
2295
|
-
Settings: Settings,
|
|
2296
|
-
CreditCard: CreditCard,
|
|
2297
|
-
Bell: Bell,
|
|
2298
|
-
Calendar: Calendar,
|
|
2299
|
-
Package: Package,
|
|
2300
|
-
BarChart3: BarChart3,
|
|
2301
|
-
FileText: FileText,
|
|
2302
|
-
Mail: Mail,
|
|
2303
|
-
DollarSign: DollarSign,
|
|
2304
|
-
Megaphone: Megaphone,
|
|
2305
|
-
ShoppingCart: ShoppingCart,
|
|
2306
|
-
Target: Target,
|
|
2307
|
-
Wrench: Wrench,
|
|
2308
|
-
ClipboardList: ClipboardList,
|
|
2309
|
-
Briefcase: Briefcase,
|
|
2310
|
-
UserCog: UserCog,
|
|
2311
|
-
BookOpen: BookOpen,
|
|
2312
|
-
MessageCircle: MessageCircle,
|
|
2313
|
-
Globe: Globe,
|
|
2314
|
-
Percent: Percent,
|
|
2315
|
-
Tag: Tag,
|
|
2316
|
-
Camera: Camera,
|
|
2317
|
-
UtensilsCrossed: UtensilsCrossed,
|
|
2318
|
-
Search: Search,
|
|
2319
|
-
MapPin: MapPin,
|
|
2320
|
-
Handshake: Handshake,
|
|
2321
|
-
Contact: Contact,
|
|
2322
|
-
Building2: Building2,
|
|
2323
|
-
ChevronDown: ChevronDown,
|
|
2324
|
-
Filter: Filter,
|
|
2325
|
-
Plus: Plus,
|
|
2326
|
-
List: List,
|
|
2327
|
-
Dog: Dog,
|
|
2328
|
-
Cat: Cat,
|
|
2329
|
-
PawPrint: PawPrint,
|
|
2330
|
-
Heart: Heart,
|
|
2331
|
-
LayoutTemplate: LayoutTemplate,
|
|
2332
|
-
LeafyGreen: LeafyGreen,
|
|
2333
|
-
Apple: Apple,
|
|
2334
|
-
Egg: Egg,
|
|
2335
|
-
Wheat: Wheat,
|
|
2336
|
-
ArrowDownCircle,
|
|
2337
|
-
ArrowUpCircle,
|
|
2338
|
-
ArrowLeftRight,
|
|
2339
|
-
Plug,
|
|
2340
|
-
SlidersHorizontal,
|
|
2341
|
-
TableProperties,
|
|
2342
|
-
Landmark,
|
|
2343
|
-
Receipt,
|
|
2344
|
-
TrendingUp,
|
|
2345
|
-
CircleDollarSign,
|
|
2346
|
-
Clock: Clock,
|
|
2347
|
-
AlertTriangle: AlertTriangle,
|
|
2348
|
-
Sparkles,
|
|
2349
|
-
Wallet,
|
|
2350
|
-
Warehouse,
|
|
2351
|
-
Ruler,
|
|
2352
|
-
ArrowUpRight,
|
|
2353
|
-
ArrowDownRight,
|
|
2354
|
-
UserPlus,
|
|
2355
|
-
TreePalm,
|
|
2356
|
-
PartyPopper: PartyPopper,
|
|
2357
|
-
Radio: Radio,
|
|
2358
|
-
CalendarDays: CalendarDays,
|
|
2359
|
-
Banknote: Banknote,
|
|
2360
|
-
Layers: Layers,
|
|
2361
|
-
Music: Music,
|
|
2362
|
-
Eye: Eye,
|
|
2363
|
-
ListMusic: ListMusic,
|
|
2364
|
-
Disc3: Disc3,
|
|
2365
|
-
UsersRound: UsersRound,
|
|
2366
|
-
Mic: Mic
|
|
2367
|
-
};
|
|
2368
|
-
function getIcon2(name) {
|
|
2369
|
-
return ICON_MAP2[name] ?? Home;
|
|
2370
|
-
}
|
|
2371
|
-
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
2372
|
-
const [open, setOpen] = React10.useState(false);
|
|
2373
|
-
const Icon2 = getIcon2(item.icon);
|
|
2374
|
-
const isChildActive = item.children?.some(
|
|
2375
|
-
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
2376
|
-
);
|
|
2377
|
-
return /* @__PURE__ */ jsxs(Popover2.Root, { open, onOpenChange: setOpen, children: [
|
|
2378
|
-
/* @__PURE__ */ jsx(Popover2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
2379
|
-
"button",
|
|
2380
|
-
{
|
|
2381
|
-
"data-nav-active": isChildActive || void 0,
|
|
2382
|
-
className: cn(
|
|
2383
|
-
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
2384
|
-
isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
2385
|
-
),
|
|
2386
|
-
children: [
|
|
2387
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
|
|
2388
|
-
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
2389
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
|
|
2390
|
-
]
|
|
2391
|
-
}
|
|
2392
|
-
) }),
|
|
2393
|
-
/* @__PURE__ */ jsx(Popover2.Portal, { children: /* @__PURE__ */ jsx(
|
|
2394
|
-
Popover2.Content,
|
|
2395
|
-
{
|
|
2396
|
-
align: "start",
|
|
2397
|
-
sideOffset: 4,
|
|
2398
|
-
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",
|
|
2399
|
-
children: item.children?.map((child) => {
|
|
2400
|
-
const ChildIcon = getIcon2(child.icon);
|
|
2401
|
-
const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
|
|
2402
|
-
return /* @__PURE__ */ jsxs(
|
|
2403
|
-
"button",
|
|
2404
|
-
{
|
|
2405
|
-
onClick: () => {
|
|
2406
|
-
onNavigate(child.route);
|
|
2407
|
-
setOpen(false);
|
|
2408
|
-
},
|
|
2409
|
-
className: cn(
|
|
2410
|
-
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
|
|
2411
|
-
isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
|
|
2412
|
-
),
|
|
2413
|
-
children: [
|
|
2414
|
-
/* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
2415
|
-
/* @__PURE__ */ jsx("span", { children: child.label })
|
|
2416
|
-
]
|
|
2417
|
-
},
|
|
2418
|
-
child.id
|
|
2419
|
-
);
|
|
2420
|
-
})
|
|
2421
|
-
}
|
|
2422
|
-
) })
|
|
2423
|
-
] });
|
|
2424
|
-
}
|
|
2425
|
-
function Topbar({
|
|
2426
|
-
navigation,
|
|
2427
|
-
logo,
|
|
2428
|
-
user,
|
|
2429
|
-
onMenuClick,
|
|
2430
|
-
leftContent,
|
|
2431
|
-
rightContent,
|
|
2432
|
-
currentPath = "/",
|
|
2433
|
-
onNavigate,
|
|
2434
|
-
searchPlaceholder = "Search...",
|
|
2435
|
-
onSignOut,
|
|
2436
|
-
onProfile,
|
|
2437
|
-
onSettings,
|
|
2438
|
-
onBilling,
|
|
2439
|
-
userMenuExtras
|
|
2440
|
-
}) {
|
|
2441
|
-
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
2442
|
-
const mainNav = navigation.filter((item) => item.section === "main");
|
|
2443
|
-
const secondaryNav = navigation.filter((item) => item.section === "secondary");
|
|
2444
|
-
const allNav = [...mainNav, ...secondaryNav];
|
|
2445
|
-
const handleNavigate = (route) => {
|
|
2446
|
-
onNavigate?.(route);
|
|
2447
|
-
};
|
|
2448
|
-
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
2449
|
-
/* @__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: [
|
|
2450
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2451
|
-
/* @__PURE__ */ jsx(
|
|
2452
|
-
"button",
|
|
2453
|
-
{
|
|
2454
|
-
onClick: onMenuClick,
|
|
2455
|
-
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",
|
|
2456
|
-
"aria-label": "Open menu",
|
|
2457
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
2458
|
-
}
|
|
2459
|
-
),
|
|
2460
|
-
/* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
2461
|
-
] }),
|
|
2462
|
-
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
2463
|
-
"button",
|
|
2464
|
-
{
|
|
2465
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
2466
|
-
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",
|
|
2467
|
-
children: [
|
|
2468
|
-
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
|
|
2469
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
|
|
2470
|
-
/* @__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" })
|
|
2471
|
-
]
|
|
2472
|
-
}
|
|
2473
|
-
) }),
|
|
2474
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2475
|
-
leftContent,
|
|
2476
|
-
rightContent,
|
|
2477
|
-
/* @__PURE__ */ jsx(
|
|
2478
|
-
TopbarUserMenu,
|
|
2479
|
-
{
|
|
2480
|
-
user,
|
|
2481
|
-
onSignOut,
|
|
2482
|
-
onProfile,
|
|
2483
|
-
onSettings,
|
|
2484
|
-
onBilling,
|
|
2485
|
-
userMenuExtras
|
|
2486
|
-
}
|
|
2487
|
-
)
|
|
2488
|
-
] })
|
|
2489
|
-
] }) }),
|
|
2490
|
-
/* @__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) => {
|
|
2491
|
-
if (!el) return;
|
|
2492
|
-
const active = el.querySelector('[data-nav-active="true"]');
|
|
2493
|
-
if (active) {
|
|
2494
|
-
const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
|
|
2495
|
-
el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
2496
|
-
}
|
|
2497
|
-
}, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-hide", children: allNav.map((item) => {
|
|
2498
|
-
if (item.children && item.children.length > 0) {
|
|
2499
|
-
return /* @__PURE__ */ jsx(
|
|
2500
|
-
NavDropdown,
|
|
2501
|
-
{
|
|
2502
|
-
item,
|
|
2503
|
-
currentPath,
|
|
2504
|
-
onNavigate: handleNavigate
|
|
2505
|
-
},
|
|
2506
|
-
item.id
|
|
2507
|
-
);
|
|
2508
|
-
}
|
|
2509
|
-
const Icon2 = getIcon2(item.icon);
|
|
2510
|
-
const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
|
|
2511
|
-
return /* @__PURE__ */ jsxs(
|
|
2512
|
-
"button",
|
|
2513
|
-
{
|
|
2514
|
-
"data-nav-active": isActive || void 0,
|
|
2515
|
-
onClick: () => handleNavigate(item.route),
|
|
2516
|
-
className: cn(
|
|
2517
|
-
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
2518
|
-
isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
2519
|
-
),
|
|
2520
|
-
children: [
|
|
2521
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
|
|
2522
|
-
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
2523
|
-
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 })
|
|
2524
|
-
]
|
|
2525
|
-
},
|
|
2526
|
-
item.id
|
|
2527
|
-
);
|
|
2528
|
-
}) }) }) })
|
|
2529
|
-
] });
|
|
2530
|
-
}
|
|
2531
|
-
var ModuleHeaderSlotContext = React10.createContext(null);
|
|
2532
|
-
function useModuleHeaderSlot() {
|
|
2533
|
-
return React10.useContext(ModuleHeaderSlotContext);
|
|
2534
|
-
}
|
|
2535
|
-
var ModuleHeaderActionsSlotContext = React10.createContext(null);
|
|
2536
|
-
function useModuleHeaderActionsSlot() {
|
|
2537
|
-
return React10.useContext(ModuleHeaderActionsSlotContext);
|
|
2538
|
-
}
|
|
2539
|
-
function SidebarLayout({
|
|
2540
|
-
navigation = [],
|
|
2541
|
-
logo,
|
|
2542
|
-
user,
|
|
2543
|
-
children,
|
|
2544
|
-
onNavigate,
|
|
2545
|
-
onSignOut,
|
|
2546
|
-
onProfile,
|
|
2547
|
-
onSettings,
|
|
2548
|
-
onBilling,
|
|
2549
|
-
userMenuExtras: _userMenuExtras,
|
|
2550
|
-
orgSwitcher,
|
|
2551
|
-
sidebarTopContent,
|
|
2552
|
-
sidebarFooterContent,
|
|
2553
|
-
frame,
|
|
2554
|
-
currentPath,
|
|
2555
|
-
pageTitle,
|
|
2556
|
-
topbarStart,
|
|
2557
|
-
topbarEnd,
|
|
2558
|
-
userMenuSlot,
|
|
2559
|
-
notificationSlot,
|
|
2560
|
-
unreadCount = 0
|
|
2561
|
-
}) {
|
|
2562
|
-
const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
|
|
2563
|
-
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
2564
|
-
const [mobileMenuOpen, setMobileMenuOpen] = React10.useState(false);
|
|
2565
|
-
const [headerSlot, setHeaderSlot] = React10.useState(null);
|
|
2566
|
-
const [actionsSlot, setActionsSlot] = React10.useState(null);
|
|
2567
|
-
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: [
|
|
2568
|
-
/* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(
|
|
2569
|
-
Sidebar,
|
|
2570
|
-
{
|
|
2571
|
-
navigation,
|
|
2572
|
-
logo,
|
|
2573
|
-
collapsed: sidebarCollapsed,
|
|
2574
|
-
onToggle: () => setSidebarCollapsed(!sidebarCollapsed),
|
|
2575
|
-
onNavigate,
|
|
2576
|
-
currentPath,
|
|
2577
|
-
user,
|
|
2578
|
-
onSignOut,
|
|
2579
|
-
onProfile,
|
|
2580
|
-
onSettings,
|
|
2581
|
-
onBilling,
|
|
2582
|
-
orgSwitcher,
|
|
2583
|
-
topContent: sidebarTopContent,
|
|
2584
|
-
footerContent: sidebarFooterContent,
|
|
2585
|
-
borderless: false,
|
|
2586
|
-
userMenuSlot,
|
|
2587
|
-
notificationSlot,
|
|
2588
|
-
unreadCount
|
|
2589
|
-
}
|
|
2590
|
-
) }),
|
|
2591
|
-
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
2592
|
-
"relative flex flex-1 flex-col overflow-hidden",
|
|
2593
|
-
frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-card"
|
|
2594
|
-
), children: [
|
|
2595
|
-
/* @__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: [
|
|
2596
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
2597
|
-
/* @__PURE__ */ jsx(
|
|
2598
|
-
"button",
|
|
2599
|
-
{
|
|
2600
|
-
onClick: () => setMobileMenuOpen(true),
|
|
2601
|
-
className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
|
|
2602
|
-
"aria-label": "Open menu",
|
|
2603
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
2604
|
-
}
|
|
2605
|
-
),
|
|
2606
|
-
pageTitle && /* @__PURE__ */ jsx("h2", { className: "font-semibold text-lg", children: pageTitle }),
|
|
2607
|
-
topbarStart,
|
|
2608
|
-
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
2609
|
-
] }),
|
|
2610
|
-
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
2611
|
-
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
|
|
2612
|
-
topbarEnd
|
|
2613
|
-
] })
|
|
2614
|
-
] }),
|
|
2615
|
-
/* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children }),
|
|
2616
|
-
/* @__PURE__ */ jsx(SaveBar, {})
|
|
2617
|
-
] }),
|
|
2618
|
-
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
2619
|
-
MobileOverlay,
|
|
2620
|
-
{
|
|
2621
|
-
navigation,
|
|
2622
|
-
logo,
|
|
2623
|
-
user,
|
|
2624
|
-
currentPath,
|
|
2625
|
-
onNavigate,
|
|
2626
|
-
onSignOut,
|
|
2627
|
-
onProfile,
|
|
2628
|
-
onSettings,
|
|
2629
|
-
onBilling,
|
|
2630
|
-
orgSwitcher,
|
|
2631
|
-
topContent: sidebarTopContent,
|
|
2632
|
-
footerContent: sidebarFooterContent,
|
|
2633
|
-
userMenuSlot,
|
|
2634
|
-
notificationSlot,
|
|
2635
|
-
unreadCount,
|
|
2636
|
-
onClose: () => setMobileMenuOpen(false)
|
|
2637
|
-
}
|
|
2638
|
-
)
|
|
2639
|
-
] }) }) }) });
|
|
2640
|
-
}
|
|
2641
|
-
function TopbarLayout({
|
|
2642
|
-
navigation = [],
|
|
2643
|
-
logo,
|
|
2644
|
-
user,
|
|
2645
|
-
children,
|
|
2646
|
-
onNavigate,
|
|
2647
|
-
onSignOut,
|
|
2648
|
-
onProfile,
|
|
2649
|
-
onSettings,
|
|
2650
|
-
onBilling,
|
|
2651
|
-
userMenuExtras,
|
|
2652
|
-
frame,
|
|
2653
|
-
currentPath,
|
|
2654
|
-
topbarStart,
|
|
2655
|
-
topbarEnd
|
|
2656
|
-
}) {
|
|
2657
|
-
const [mobileMenuOpen, setMobileMenuOpen] = React10.useState(false);
|
|
2658
|
-
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-background", children: [
|
|
2659
|
-
/* @__PURE__ */ jsx(
|
|
2660
|
-
Topbar,
|
|
2661
|
-
{
|
|
2662
|
-
navigation,
|
|
2663
|
-
logo,
|
|
2664
|
-
user,
|
|
2665
|
-
currentPath,
|
|
2666
|
-
onNavigate,
|
|
2667
|
-
onSignOut,
|
|
2668
|
-
onProfile,
|
|
2669
|
-
onSettings,
|
|
2670
|
-
onBilling,
|
|
2671
|
-
userMenuExtras,
|
|
2672
|
-
leftContent: topbarStart,
|
|
2673
|
-
rightContent: topbarEnd,
|
|
2674
|
-
onMenuClick: () => setMobileMenuOpen(true)
|
|
2675
|
-
}
|
|
2676
|
-
),
|
|
2677
|
-
/* @__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 }) }),
|
|
2678
|
-
/* @__PURE__ */ jsx(SaveBar, {}),
|
|
2679
|
-
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
2680
|
-
MobileOverlay,
|
|
2681
|
-
{
|
|
2682
|
-
navigation,
|
|
2683
|
-
logo,
|
|
2684
|
-
user,
|
|
2685
|
-
currentPath,
|
|
2686
|
-
onNavigate,
|
|
2687
|
-
onSignOut,
|
|
2688
|
-
onProfile,
|
|
2689
|
-
onSettings,
|
|
2690
|
-
onBilling,
|
|
2691
|
-
userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
|
|
2692
|
-
onClose: () => setMobileMenuOpen(false)
|
|
2693
|
-
}
|
|
2694
|
-
)
|
|
2695
|
-
] }) });
|
|
2696
|
-
}
|
|
2697
|
-
function DrawerUserRow({ user }) {
|
|
2698
|
-
const label = user.fullName || user.email;
|
|
2699
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 px-1", children: [
|
|
2700
|
-
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() }),
|
|
2701
|
-
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-sidebar-foreground", children: label })
|
|
2702
|
-
] });
|
|
2703
|
-
}
|
|
2704
|
-
function MinimalLayout({
|
|
2705
|
-
logo,
|
|
2706
|
-
user: _user,
|
|
2707
|
-
children,
|
|
2708
|
-
headerEnd
|
|
2709
|
-
}) {
|
|
2710
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex h-screen flex-col", children: [
|
|
2711
|
-
/* @__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: [
|
|
2712
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
|
|
2713
|
-
headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
|
|
2714
|
-
] }),
|
|
2715
|
-
/* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children })
|
|
2716
|
-
] });
|
|
2717
|
-
}
|
|
2718
|
-
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
2719
|
-
const [visible, setVisible] = React10.useState(false);
|
|
2720
|
-
React10.useEffect(() => {
|
|
2721
|
-
const id = requestAnimationFrame(() => setVisible(true));
|
|
2722
|
-
return () => cancelAnimationFrame(id);
|
|
2723
|
-
}, []);
|
|
2724
|
-
const close = React10.useCallback(() => {
|
|
2725
|
-
setVisible(false);
|
|
2726
|
-
window.setTimeout(onClose, 250);
|
|
2727
|
-
}, [onClose]);
|
|
2728
|
-
React10.useEffect(() => {
|
|
2729
|
-
const previous = document.body.style.overflow;
|
|
2730
|
-
document.body.style.overflow = "hidden";
|
|
2731
|
-
return () => {
|
|
2732
|
-
document.body.style.overflow = previous;
|
|
2733
|
-
};
|
|
2734
|
-
}, []);
|
|
2735
|
-
React10.useEffect(() => {
|
|
2736
|
-
const onKey = (e) => {
|
|
2737
|
-
if (e.key === "Escape") close();
|
|
2738
|
-
};
|
|
2739
|
-
window.addEventListener("keydown", onKey);
|
|
2740
|
-
return () => window.removeEventListener("keydown", onKey);
|
|
2741
|
-
}, [close]);
|
|
2742
|
-
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[60] md:hidden", children: [
|
|
2743
|
-
/* @__PURE__ */ jsx(
|
|
2744
|
-
"div",
|
|
2745
|
-
{
|
|
2746
|
-
className: cn(
|
|
2747
|
-
"absolute inset-0 bg-black/50 transition-opacity duration-300 ease-out",
|
|
2748
|
-
visible ? "opacity-100" : "opacity-0"
|
|
2749
|
-
),
|
|
2750
|
-
onClick: close
|
|
2751
|
-
}
|
|
2752
|
-
),
|
|
2753
|
-
/* @__PURE__ */ jsx(
|
|
2754
|
-
"div",
|
|
2755
|
-
{
|
|
2756
|
-
className: cn(
|
|
2757
|
-
"absolute inset-y-0 left-0 shadow-2xl transition-transform duration-300 ease-out will-change-transform",
|
|
2758
|
-
visible ? "translate-x-0" : "-translate-x-full"
|
|
2759
|
-
),
|
|
2760
|
-
children: /* @__PURE__ */ jsx(
|
|
2761
|
-
Sidebar,
|
|
2762
|
-
{
|
|
2763
|
-
...sidebarProps,
|
|
2764
|
-
collapsed: false,
|
|
2765
|
-
onToggle: close,
|
|
2766
|
-
borderless: true,
|
|
2767
|
-
onNavigate: (route) => {
|
|
2768
|
-
onNavigate?.(route);
|
|
2769
|
-
close();
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
)
|
|
2773
|
-
}
|
|
2774
|
-
)
|
|
2775
|
-
] });
|
|
2776
|
-
}
|
|
2777
|
-
function AppShell({
|
|
2778
|
-
variant,
|
|
2779
|
-
contentFrame,
|
|
2780
|
-
navigation = [],
|
|
2781
|
-
logo,
|
|
2782
|
-
user,
|
|
2783
|
-
children,
|
|
2784
|
-
onNavigate,
|
|
2785
|
-
onSignOut,
|
|
2786
|
-
onProfile,
|
|
2787
|
-
onSettings,
|
|
2788
|
-
onBilling,
|
|
2789
|
-
pageTitle,
|
|
2790
|
-
currentPath,
|
|
2791
|
-
userMenuExtras,
|
|
2792
|
-
orgSwitcher,
|
|
2793
|
-
topbarStart,
|
|
2794
|
-
topbarEnd,
|
|
2795
|
-
sidebarTopContent,
|
|
2796
|
-
sidebarFooterContent,
|
|
2797
|
-
userMenuSlot,
|
|
2798
|
-
notificationSlot,
|
|
2799
|
-
unreadCount = 0
|
|
2800
|
-
}) {
|
|
2801
|
-
switch (variant) {
|
|
2802
|
-
case "sidebar":
|
|
2803
|
-
return /* @__PURE__ */ jsx(
|
|
2804
|
-
SidebarLayout,
|
|
2805
|
-
{
|
|
2806
|
-
navigation,
|
|
2807
|
-
logo,
|
|
2808
|
-
user,
|
|
2809
|
-
onNavigate,
|
|
2810
|
-
onSignOut,
|
|
2811
|
-
onProfile,
|
|
2812
|
-
onSettings,
|
|
2813
|
-
onBilling,
|
|
2814
|
-
userMenuExtras,
|
|
2815
|
-
orgSwitcher,
|
|
2816
|
-
sidebarTopContent,
|
|
2817
|
-
sidebarFooterContent,
|
|
2818
|
-
frame: contentFrame,
|
|
2819
|
-
currentPath,
|
|
2820
|
-
pageTitle,
|
|
2821
|
-
topbarStart,
|
|
2822
|
-
topbarEnd,
|
|
2823
|
-
userMenuSlot,
|
|
2824
|
-
notificationSlot,
|
|
2825
|
-
unreadCount,
|
|
2826
|
-
children
|
|
2827
|
-
}
|
|
2828
|
-
);
|
|
2829
|
-
case "topbar":
|
|
2830
|
-
return /* @__PURE__ */ jsx(
|
|
2831
|
-
TopbarLayout,
|
|
2832
|
-
{
|
|
2833
|
-
navigation,
|
|
2834
|
-
logo,
|
|
2835
|
-
user,
|
|
2836
|
-
onNavigate,
|
|
2837
|
-
onSignOut,
|
|
2838
|
-
onProfile,
|
|
2839
|
-
onSettings,
|
|
2840
|
-
onBilling,
|
|
2841
|
-
userMenuExtras,
|
|
2842
|
-
frame: contentFrame,
|
|
2843
|
-
currentPath,
|
|
2844
|
-
topbarStart,
|
|
2845
|
-
topbarEnd,
|
|
2846
|
-
children
|
|
2847
|
-
}
|
|
2848
|
-
);
|
|
2849
|
-
case "minimal":
|
|
2850
|
-
return /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
|
|
2851
|
-
default: {
|
|
2852
|
-
return null;
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2856
|
-
var NavTransitionContext = React10.createContext("slide");
|
|
2857
|
-
function NavTransitionProvider({ value, children }) {
|
|
2858
|
-
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
2859
|
-
}
|
|
2860
|
-
function useNavTransition() {
|
|
2861
|
-
return React10.useContext(NavTransitionContext);
|
|
2862
|
-
}
|
|
2863
|
-
function resolveTransitionClass(transition, direction) {
|
|
2864
|
-
if (transition === "none") return "";
|
|
2865
|
-
if (transition === "fade") return "saas-page-enter";
|
|
2866
|
-
if (direction === "back") return "saas-nav-back";
|
|
2867
|
-
if (direction === "forward") return "saas-nav-forward";
|
|
2868
|
-
return "saas-page-enter";
|
|
2869
|
-
}
|
|
2870
|
-
function PageTransition({
|
|
2871
|
-
transitionKey,
|
|
2872
|
-
direction,
|
|
2873
|
-
className,
|
|
2874
|
-
children
|
|
2875
|
-
}) {
|
|
2876
|
-
const transition = useNavTransition();
|
|
2877
|
-
const animClass = resolveTransitionClass(transition, direction);
|
|
2878
|
-
return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
|
|
2879
|
-
}
|
|
2880
|
-
function PageHeaderActions({ children }) {
|
|
2881
|
-
const slot = useModuleHeaderActionsSlot();
|
|
2882
|
-
if (slot) return createPortal(children, slot);
|
|
2883
|
-
return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
|
|
2884
|
-
}
|
|
2885
|
-
var ModuleLayoutContext = React10.createContext("rail");
|
|
2886
|
-
function ModuleLayoutProvider({ variant, children }) {
|
|
2887
|
-
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
2888
|
-
}
|
|
2889
|
-
function useModuleLayout() {
|
|
2890
|
-
return React10.useContext(ModuleLayoutContext);
|
|
2891
|
-
}
|
|
2892
|
-
function NavItem2({ item }) {
|
|
2893
|
-
const [expanded, setExpanded] = React10.useState(true);
|
|
2894
|
-
const Icon2 = item.icon ? ICON_MAP2[item.icon] ?? null : null;
|
|
2895
|
-
const hasChildren = item.children && item.children.length > 0;
|
|
2896
|
-
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
2897
|
-
const isActive = item.active || hasActiveChild;
|
|
2898
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2899
|
-
/* @__PURE__ */ jsxs(
|
|
2900
|
-
"button",
|
|
2901
|
-
{
|
|
2902
|
-
type: "button",
|
|
2903
|
-
onClick: () => hasChildren ? setExpanded(!expanded) : item.onClick?.(),
|
|
2904
|
-
className: cn(
|
|
2905
|
-
"flex items-center gap-2 w-full rounded-md px-2.5 py-1.5 text-sm transition-colors",
|
|
2906
|
-
isActive ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
|
|
2907
|
-
),
|
|
2908
|
-
children: [
|
|
2909
|
-
Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4 shrink-0" }),
|
|
2910
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: item.label }),
|
|
2911
|
-
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" }))
|
|
2912
|
-
]
|
|
2913
|
-
}
|
|
2914
|
-
),
|
|
2915
|
-
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(
|
|
2916
|
-
"button",
|
|
2917
|
-
{
|
|
2918
|
-
type: "button",
|
|
2919
|
-
onClick: child.onClick,
|
|
2920
|
-
className: cn(
|
|
2921
|
-
"flex items-center w-full rounded-md px-2.5 py-1 text-xs transition-colors",
|
|
2922
|
-
child.active ? "text-primary font-medium bg-primary/5" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
|
|
2923
|
-
),
|
|
2924
|
-
children: child.label
|
|
2925
|
-
},
|
|
2926
|
-
child.id
|
|
2927
|
-
)) })
|
|
2928
|
-
] });
|
|
2929
|
-
}
|
|
2930
|
-
function MobileTabs({ nav }) {
|
|
2931
|
-
const scrollRef = React10.useRef(null);
|
|
2932
|
-
const [expandedId, setExpandedId] = React10.useState(null);
|
|
2933
|
-
React10.useEffect(() => {
|
|
2934
|
-
const container = scrollRef.current;
|
|
2935
|
-
if (!container) return;
|
|
2936
|
-
const activeEl = container.querySelector('[data-active="true"]');
|
|
2937
|
-
if (activeEl) {
|
|
2938
|
-
const left = activeEl.offsetLeft - container.offsetWidth / 2 + activeEl.offsetWidth / 2;
|
|
2939
|
-
container.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
2940
|
-
}
|
|
2941
|
-
}, [nav]);
|
|
2942
|
-
return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
|
|
2943
|
-
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-hide", children: nav.map((item) => {
|
|
2944
|
-
const Icon2 = item.icon ? ICON_MAP2[item.icon] ?? null : null;
|
|
2945
|
-
const hasChildren = item.children && item.children.length > 0;
|
|
2946
|
-
const isActive = item.active || item.children?.some((c) => c.active);
|
|
2947
|
-
const isExpanded = expandedId === item.id;
|
|
2948
|
-
return /* @__PURE__ */ jsxs(
|
|
2949
|
-
"button",
|
|
2950
|
-
{
|
|
2951
|
-
type: "button",
|
|
2952
|
-
"data-active": isActive || void 0,
|
|
2953
|
-
onClick: () => {
|
|
2954
|
-
if (hasChildren) {
|
|
2955
|
-
setExpandedId(isExpanded ? null : item.id);
|
|
2956
|
-
} else {
|
|
2957
|
-
item.onClick?.();
|
|
2958
|
-
setExpandedId(null);
|
|
2959
|
-
}
|
|
2960
|
-
},
|
|
2961
|
-
className: cn(
|
|
2962
|
-
"flex items-center gap-1.5 whitespace-nowrap rounded-full px-3 py-1.5 text-xs font-medium transition-colors shrink-0",
|
|
2963
|
-
isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
|
|
2964
|
-
),
|
|
2965
|
-
children: [
|
|
2966
|
-
Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3" }),
|
|
2967
|
-
item.label,
|
|
2968
|
-
hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
|
|
2969
|
-
]
|
|
2970
|
-
},
|
|
2971
|
-
item.id
|
|
2972
|
-
);
|
|
2973
|
-
}) }),
|
|
2974
|
-
expandedId && (() => {
|
|
2975
|
-
const item = nav.find((n) => n.id === expandedId);
|
|
2976
|
-
if (!item?.children) return null;
|
|
2977
|
-
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(
|
|
2978
|
-
"button",
|
|
2979
|
-
{
|
|
2980
|
-
type: "button",
|
|
2981
|
-
onClick: () => {
|
|
2982
|
-
child.onClick?.();
|
|
2983
|
-
setExpandedId(null);
|
|
2984
|
-
},
|
|
2985
|
-
className: cn(
|
|
2986
|
-
"whitespace-nowrap rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors shrink-0",
|
|
2987
|
-
child.active ? "bg-foreground/10 text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2988
|
-
),
|
|
2989
|
-
children: child.label
|
|
2990
|
-
},
|
|
2991
|
-
child.id
|
|
2992
|
-
)) });
|
|
2993
|
-
})()
|
|
2994
|
-
] });
|
|
2995
|
-
}
|
|
2996
|
-
var BackStyleContext = React10.createContext("link");
|
|
2997
|
-
function BackStyleProvider({ style, children }) {
|
|
2998
|
-
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
2999
|
-
}
|
|
3000
|
-
function useBackStyle() {
|
|
3001
|
-
return React10.useContext(BackStyleContext);
|
|
3002
|
-
}
|
|
3003
|
-
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle }) {
|
|
3004
|
-
const t = useTranslation();
|
|
3005
|
-
const appStyle = useBackStyle();
|
|
3006
|
-
const { onBack: effectiveOnBack, parentLabel: effectiveParentLabel } = useCrossModuleBack(onBack, parentLabel);
|
|
3007
|
-
useBackHandler(effectiveOnBack);
|
|
3008
|
-
const Icon2 = icon ? ICON_MAP2[icon] ?? null : null;
|
|
3009
|
-
const style = !effectiveOnBack ? "icon" : backStyle ?? appStyle;
|
|
3010
|
-
const effectiveStyle = style !== "icon" && !effectiveParentLabel ? "icon" : style;
|
|
3011
|
-
const titleBlock = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
3012
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3013
|
-
effectiveStyle === "icon" && effectiveOnBack && /* @__PURE__ */ jsx(
|
|
3014
|
-
"button",
|
|
3015
|
-
{
|
|
3016
|
-
onClick: effectiveOnBack,
|
|
3017
|
-
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",
|
|
3018
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
3019
|
-
}
|
|
3020
|
-
),
|
|
3021
|
-
Icon2 && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-muted", children: /* @__PURE__ */ jsx(Icon2, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
3022
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3023
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: title }),
|
|
3024
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3025
|
-
] })
|
|
3026
|
-
] }),
|
|
3027
|
-
actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
|
|
3028
|
-
] });
|
|
3029
|
-
if (effectiveStyle === "breadcrumb") {
|
|
3030
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 mb-6", children: [
|
|
3031
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
3032
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
3033
|
-
/* @__PURE__ */ jsx("button", { onClick: effectiveOnBack, className: "hover:text-foreground transition-colors", children: effectiveParentLabel }),
|
|
3034
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" }),
|
|
3035
|
-
/* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: title })
|
|
3036
|
-
] }),
|
|
3037
|
-
actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
|
|
3038
|
-
] }),
|
|
3039
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground -mt-2", children: subtitle })
|
|
3040
|
-
] });
|
|
3041
|
-
}
|
|
3042
|
-
if (effectiveStyle === "link") {
|
|
3043
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 mb-6", children: [
|
|
3044
|
-
/* @__PURE__ */ jsxs(
|
|
3045
|
-
"button",
|
|
3046
|
-
{
|
|
3047
|
-
onClick: effectiveOnBack,
|
|
3048
|
-
className: "inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
3049
|
-
children: [
|
|
3050
|
-
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
|
|
3051
|
-
t("crud.detail.backTo", { entities: effectiveParentLabel })
|
|
3052
|
-
]
|
|
3053
|
-
}
|
|
3054
|
-
),
|
|
3055
|
-
titleBlock
|
|
3056
|
-
] });
|
|
3057
|
-
}
|
|
3058
|
-
return /* @__PURE__ */ jsx("div", { className: "space-y-4 mb-6", children: titleBlock });
|
|
3059
|
-
}
|
|
3060
|
-
function ModuleHeader({ nav }) {
|
|
3061
|
-
const slot = useModuleHeaderSlot();
|
|
3062
|
-
function defaultChild(item) {
|
|
3063
|
-
const kids = item.children ?? [];
|
|
3064
|
-
return kids.find((c) => /list/i.test(c.id) || /^list$/i.test(c.label)) ?? kids[kids.length - 1];
|
|
3065
|
-
}
|
|
3066
|
-
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) => {
|
|
3067
|
-
const Icon2 = item.icon ? ICON_MAP2[item.icon] ?? null : null;
|
|
3068
|
-
const hasChildren = item.children && item.children.length > 0;
|
|
3069
|
-
const isActive = item.active || item.children?.some((c) => c.active);
|
|
3070
|
-
return /* @__PURE__ */ jsxs(
|
|
3071
|
-
"button",
|
|
3072
|
-
{
|
|
3073
|
-
type: "button",
|
|
3074
|
-
onClick: () => hasChildren ? defaultChild(item)?.onClick?.() : item.onClick?.(),
|
|
3075
|
-
className: cn(
|
|
3076
|
-
"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",
|
|
3077
|
-
isActive ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
|
|
3078
|
-
),
|
|
3079
|
-
children: [
|
|
3080
|
-
Icon2 && /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
|
|
3081
|
-
item.label
|
|
3082
|
-
]
|
|
3083
|
-
},
|
|
3084
|
-
item.id
|
|
3085
|
-
);
|
|
3086
|
-
}) }) });
|
|
3087
|
-
if (slot) return createPortal(content, slot);
|
|
3088
|
-
return /* @__PURE__ */ jsx("div", { className: "mb-5 border-b border-border pb-2", children: content });
|
|
3089
|
-
}
|
|
3090
|
-
function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
|
|
3091
|
-
const ctxVariant = useModuleLayout();
|
|
3092
|
-
const variant = navVariant ?? ctxVariant;
|
|
3093
|
-
const transitionKey = viewKey ?? "view";
|
|
3094
|
-
if (variant === "tabs") {
|
|
3095
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3096
|
-
/* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
3097
|
-
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
3098
|
-
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
|
|
3099
|
-
] });
|
|
3100
|
-
}
|
|
3101
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-6 -mt-2", className), children: [
|
|
3102
|
-
/* @__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: [
|
|
3103
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3104
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-2.5 mb-2", children: title }),
|
|
3105
|
-
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
|
|
3106
|
-
] }),
|
|
3107
|
-
sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
|
|
3108
|
-
] }) }),
|
|
3109
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
3110
|
-
showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
|
|
3111
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
3112
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
|
|
3113
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
3114
|
-
] }),
|
|
3115
|
-
headerAction
|
|
3116
|
-
] }),
|
|
3117
|
-
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
3118
|
-
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
3119
|
-
] })
|
|
3120
|
-
] });
|
|
3121
|
-
}
|
|
3122
|
-
var lightTheme = {
|
|
3123
|
-
name: "light",
|
|
3124
|
-
colors: {
|
|
3125
|
-
primary: "0 0% 18.8%",
|
|
3126
|
-
primaryForeground: "0 0% 100%",
|
|
3127
|
-
secondary: "0 0% 96.9%",
|
|
3128
|
-
secondaryForeground: "0 0% 18.8%",
|
|
3129
|
-
accent: "0 0% 94.5%",
|
|
3130
|
-
accentForeground: "0 0% 18.8%",
|
|
3131
|
-
background: "0 0% 94.5%",
|
|
3132
|
-
foreground: "0 0% 18.8%",
|
|
3133
|
-
card: "0 0% 100%",
|
|
3134
|
-
cardForeground: "0 0% 18.8%",
|
|
3135
|
-
muted: "0 0% 96.9%",
|
|
3136
|
-
mutedForeground: "0 0% 38%",
|
|
3137
|
-
border: "0 0% 89%",
|
|
3138
|
-
input: "0 0% 54.1%",
|
|
3139
|
-
ring: "214 100% 41.4%",
|
|
3140
|
-
popover: "0 0% 100%",
|
|
3141
|
-
popoverForeground: "0 0% 18.8%",
|
|
3142
|
-
destructive: "354 92% 41%",
|
|
3143
|
-
destructiveForeground: "0 0% 100%",
|
|
3144
|
-
success: "167 95% 25.1%",
|
|
3145
|
-
successForeground: "0 0% 100%",
|
|
3146
|
-
warning: "43 100% 50%",
|
|
3147
|
-
warningForeground: "0 0% 18.8%",
|
|
3148
|
-
sidebar: "0 0% 92.2%",
|
|
3149
|
-
sidebarForeground: "0 0% 18.8%",
|
|
3150
|
-
sidebarBorder: "0 0% 86.3%",
|
|
3151
|
-
sidebarAccent: "0 0% 100%",
|
|
3152
|
-
sidebarAccentForeground: "0 0% 18.8%",
|
|
3153
|
-
sidebarMuted: "0 0% 38%",
|
|
3154
|
-
content: "0 0% 94.5%"
|
|
3155
|
-
},
|
|
3156
|
-
perception: {
|
|
3157
|
-
buttonRadius: "0.5rem",
|
|
3158
|
-
cardRadius: "0.5rem",
|
|
3159
|
-
inputRadius: "0.5rem",
|
|
3160
|
-
modalRadius: "1rem",
|
|
3161
|
-
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, sans-serif",
|
|
3162
|
-
fontFamilyMono: "JetBrains Mono, ui-monospace, SFMono-Regular, monospace",
|
|
3163
|
-
shadowSm: "0 1px 0 rgba(0, 0, 0, 0.05), 0 1px 3px 0 rgba(26, 26, 26, 0.06), 0 1px 2px -1px rgba(26, 26, 26, 0.06)",
|
|
3164
|
-
shadowMd: "0 1px 1px -1px rgba(26, 26, 26, 0.07), 0 4px 8px -2px rgba(26, 26, 26, 0.10)",
|
|
3165
|
-
shadowLg: "0 4px 12px -2px rgba(26, 26, 26, 0.15), 0 1px 3px rgba(26, 26, 26, 0.08)",
|
|
3166
|
-
shadowButton: "inset 0 -1px 0 rgba(0,0,0,0.10), inset 0 1px 0 rgba(255,255,255,0.50), 0 1px 0 rgba(0,0,0,0.05)",
|
|
3167
|
-
shadowButtonPrimary: "inset 0 -1px 0 rgba(0,0,0,0.20), inset 0 1px 0 rgba(255,255,255,0.20), 0 1px 1.5px rgba(0,0,0,0.20)",
|
|
3168
|
-
shadowButtonInset: "inset 0 2px 1px -1px rgba(0,0,0,0.20), inset 0 0 0 1px rgba(0,0,0,0.05)",
|
|
3169
|
-
surfaceBackdropFilter: "none",
|
|
3170
|
-
buttonBackground: "hsl(var(--card))",
|
|
3171
|
-
buttonBackgroundHover: "hsl(var(--muted))",
|
|
3172
|
-
buttonBorder: "hsl(var(--border))",
|
|
3173
|
-
buttonPrimaryBackground: "hsl(var(--primary))",
|
|
3174
|
-
buttonPrimaryBackgroundHover: "hsl(var(--primary) / 0.9)",
|
|
3175
|
-
buttonPrimaryBorder: "hsl(var(--primary))",
|
|
3176
|
-
buttonBackdropFilter: "none"
|
|
3177
|
-
}
|
|
3178
|
-
};
|
|
3179
|
-
var darkTheme = {
|
|
3180
|
-
name: "dark",
|
|
3181
|
-
colors: {
|
|
3182
|
-
primary: "0 0% 100%",
|
|
3183
|
-
primaryForeground: "0 0% 10.2%",
|
|
3184
|
-
secondary: "0 0% 18.8%",
|
|
3185
|
-
secondaryForeground: "0 0% 89%",
|
|
3186
|
-
accent: "0 0% 22.7%",
|
|
3187
|
-
accentForeground: "0 0% 100%",
|
|
3188
|
-
background: "0 0% 10.2%",
|
|
3189
|
-
foreground: "0 0% 89%",
|
|
3190
|
-
card: "0 0% 14.9%",
|
|
3191
|
-
cardForeground: "0 0% 89%",
|
|
3192
|
-
muted: "0 0% 18.8%",
|
|
3193
|
-
mutedForeground: "0 0% 64.7%",
|
|
3194
|
-
border: "0 0% 22.7%",
|
|
3195
|
-
input: "0 0% 32.2%",
|
|
3196
|
-
ring: "217 100% 68.4%",
|
|
3197
|
-
popover: "0 0% 18.8%",
|
|
3198
|
-
popoverForeground: "0 0% 89%",
|
|
3199
|
-
destructive: "7 100% 44.9%",
|
|
3200
|
-
destructiveForeground: "0 0% 100%",
|
|
3201
|
-
success: "156 71% 42.5%",
|
|
3202
|
-
successForeground: "0 0% 0%",
|
|
3203
|
-
warning: "41 100% 65.1%",
|
|
3204
|
-
warningForeground: "0 0% 10.2%",
|
|
3205
|
-
sidebar: "0 0% 10.2%",
|
|
3206
|
-
sidebarForeground: "0 0% 89%",
|
|
3207
|
-
sidebarBorder: "0 0% 16.5%",
|
|
3208
|
-
sidebarAccent: "0 0% 18.8%",
|
|
3209
|
-
sidebarAccentForeground: "0 0% 100%",
|
|
3210
|
-
sidebarMuted: "0 0% 64.7%",
|
|
3211
|
-
content: "0 0% 10.2%"
|
|
3212
|
-
},
|
|
3213
|
-
perception: {
|
|
3214
|
-
buttonRadius: "0.5rem",
|
|
3215
|
-
cardRadius: "0.5rem",
|
|
3216
|
-
inputRadius: "0.5rem",
|
|
3217
|
-
modalRadius: "1rem",
|
|
3218
|
-
fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, sans-serif",
|
|
3219
|
-
fontFamilyMono: "JetBrains Mono, ui-monospace, SFMono-Regular, monospace",
|
|
3220
|
-
shadowSm: "0 1px 0 rgba(0, 0, 0, 0.4), 0 1px 3px 0 rgba(0, 0, 0, 0.35), 0 1px 2px -1px rgba(0, 0, 0, 0.35)",
|
|
3221
|
-
shadowMd: "0 1px 1px -1px rgba(0, 0, 0, 0.5), 0 4px 8px -2px rgba(0, 0, 0, 0.6)",
|
|
3222
|
-
shadowLg: "0 4px 12px -2px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.4)",
|
|
3223
|
-
shadowButton: "inset 0 -1px 0 rgba(0,0,0,0.40), inset 0 1px 0 rgba(255,255,255,0.08), 0 1px 0 rgba(0,0,0,0.30)",
|
|
3224
|
-
shadowButtonPrimary: "inset 0 -1px 0 rgba(0,0,0,0.20), inset 0 1px 0 rgba(255,255,255,0.40), 0 1px 1.5px rgba(0,0,0,0.40)",
|
|
3225
|
-
shadowButtonInset: "inset 0 2px 1px -1px rgba(0,0,0,0.50), inset 0 0 0 1px rgba(0,0,0,0.20)",
|
|
3226
|
-
surfaceBackdropFilter: "none",
|
|
3227
|
-
buttonBackground: "hsl(var(--card))",
|
|
3228
|
-
buttonBackgroundHover: "hsl(var(--muted))",
|
|
3229
|
-
buttonBorder: "hsl(var(--border))",
|
|
3230
|
-
buttonPrimaryBackground: "hsl(var(--primary))",
|
|
3231
|
-
buttonPrimaryBackgroundHover: "hsl(var(--primary) / 0.9)",
|
|
3232
|
-
buttonPrimaryBorder: "hsl(var(--primary))",
|
|
3233
|
-
buttonBackdropFilter: "none"
|
|
3234
|
-
}
|
|
3235
|
-
};
|
|
3236
|
-
var colorVarMap = {
|
|
3237
|
-
primary: "--primary",
|
|
3238
|
-
primaryForeground: "--primary-foreground",
|
|
3239
|
-
secondary: "--secondary",
|
|
3240
|
-
secondaryForeground: "--secondary-foreground",
|
|
3241
|
-
accent: "--accent",
|
|
3242
|
-
accentForeground: "--accent-foreground",
|
|
3243
|
-
background: "--background",
|
|
3244
|
-
foreground: "--foreground",
|
|
3245
|
-
card: "--card",
|
|
3246
|
-
cardForeground: "--card-foreground",
|
|
3247
|
-
muted: "--muted",
|
|
3248
|
-
mutedForeground: "--muted-foreground",
|
|
3249
|
-
border: "--border",
|
|
3250
|
-
input: "--input",
|
|
3251
|
-
ring: "--ring",
|
|
3252
|
-
popover: "--popover",
|
|
3253
|
-
popoverForeground: "--popover-foreground",
|
|
3254
|
-
destructive: "--destructive",
|
|
3255
|
-
destructiveForeground: "--destructive-foreground",
|
|
3256
|
-
success: "--success",
|
|
3257
|
-
successForeground: "--success-foreground",
|
|
3258
|
-
warning: "--warning",
|
|
3259
|
-
warningForeground: "--warning-foreground",
|
|
3260
|
-
sidebar: "--sidebar",
|
|
3261
|
-
sidebarForeground: "--sidebar-foreground",
|
|
3262
|
-
sidebarBorder: "--sidebar-border",
|
|
3263
|
-
sidebarAccent: "--sidebar-accent",
|
|
3264
|
-
sidebarAccentForeground: "--sidebar-accent-foreground",
|
|
3265
|
-
sidebarMuted: "--sidebar-muted",
|
|
3266
|
-
content: "--content"
|
|
3267
|
-
};
|
|
3268
|
-
var perceptionVarMap = {
|
|
3269
|
-
buttonRadius: "--button-radius",
|
|
3270
|
-
cardRadius: "--card-radius",
|
|
3271
|
-
inputRadius: "--input-radius",
|
|
3272
|
-
modalRadius: "--modal-radius",
|
|
3273
|
-
fontFamily: "--font-family",
|
|
3274
|
-
fontFamilyMono: "--font-family-mono",
|
|
3275
|
-
shadowSm: "--shadow-sm",
|
|
3276
|
-
shadowMd: "--shadow-md",
|
|
3277
|
-
shadowLg: "--shadow-lg",
|
|
3278
|
-
shadowButton: "--shadow-button",
|
|
3279
|
-
shadowButtonPrimary: "--shadow-button-primary",
|
|
3280
|
-
shadowButtonInset: "--shadow-button-inset",
|
|
3281
|
-
surfaceBackdropFilter: "--surface-backdrop-filter",
|
|
3282
|
-
modalBackground: "--modal-bg",
|
|
3283
|
-
modalBorder: "--modal-border",
|
|
3284
|
-
modalOverlayBackground: "--modal-overlay-bg",
|
|
3285
|
-
modalOverlayBackdropFilter: "--modal-overlay-backdrop-filter",
|
|
3286
|
-
modalShadow: "--modal-shadow",
|
|
3287
|
-
glassEdgeGradient: "--glass-edge-gradient",
|
|
3288
|
-
glassPrimaryEdgeGradient: "--glass-primary-edge-gradient",
|
|
3289
|
-
glassInnerHighlight: "--glass-inner-highlight",
|
|
3290
|
-
fieldBackground: "--field-bg",
|
|
3291
|
-
fieldBorder: "--field-border",
|
|
3292
|
-
fieldShadow: "--field-shadow",
|
|
3293
|
-
buttonBackground: "--button-bg",
|
|
3294
|
-
buttonBackgroundHover: "--button-hover-bg",
|
|
3295
|
-
buttonBorder: "--button-border",
|
|
3296
|
-
buttonPrimaryBackground: "--button-primary-bg",
|
|
3297
|
-
buttonPrimaryBackgroundHover: "--button-primary-hover-bg",
|
|
3298
|
-
buttonPrimaryBorder: "--button-primary-border",
|
|
3299
|
-
buttonBackdropFilter: "--button-backdrop-filter"
|
|
3300
|
-
};
|
|
3301
|
-
function applyTheme(theme, element) {
|
|
3302
|
-
const target = document.documentElement;
|
|
3303
|
-
for (const [key, cssVar] of Object.entries(colorVarMap)) {
|
|
3304
|
-
const value = theme.colors[key];
|
|
3305
|
-
if (value) {
|
|
3306
|
-
target.style.setProperty(cssVar, value);
|
|
3307
|
-
}
|
|
3308
|
-
}
|
|
3309
|
-
for (const [key, cssVar] of Object.entries(perceptionVarMap)) {
|
|
3310
|
-
const value = theme.perception[key];
|
|
3311
|
-
if (value) {
|
|
3312
|
-
target.style.setProperty(cssVar, value);
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
var STORAGE_KEY = "fayz-ui:theme-mode";
|
|
3317
|
-
function getSystemPreference() {
|
|
3318
|
-
if (typeof window === "undefined") return "light";
|
|
3319
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
3320
|
-
}
|
|
3321
|
-
function getSavedMode() {
|
|
3322
|
-
if (typeof window === "undefined") return "light";
|
|
3323
|
-
const saved = localStorage.getItem(STORAGE_KEY);
|
|
3324
|
-
if (saved === "light" || saved === "dark" || saved === "system") return saved;
|
|
3325
|
-
return "light";
|
|
3326
|
-
}
|
|
3327
|
-
function resolveMode(mode) {
|
|
3328
|
-
return mode === "system" ? getSystemPreference() : mode;
|
|
3329
|
-
}
|
|
3330
|
-
function buildTheme(base, overrides) {
|
|
3331
|
-
if (!overrides) return base;
|
|
3332
|
-
const isDark = base.name === "dark";
|
|
3333
|
-
const colors = { ...base.colors };
|
|
3334
|
-
if (overrides.brand) {
|
|
3335
|
-
colors.primary = overrides.brand;
|
|
3336
|
-
colors.primaryForeground = "0 0% 100%";
|
|
3337
|
-
colors.ring = overrides.brand;
|
|
3338
|
-
const parts = overrides.brand.split(" ");
|
|
3339
|
-
if (parts.length >= 3) {
|
|
3340
|
-
const hue = (parseFloat(parts[0]) - 50 + 360) % 360;
|
|
3341
|
-
colors.accent = `${hue} ${parts[1]} ${parts[2]}`;
|
|
3342
|
-
colors.accentForeground = "0 0% 100%";
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
const colorOverrides = isDark && overrides.darkColors ? overrides.darkColors : overrides.colors;
|
|
3346
|
-
if (colorOverrides) {
|
|
3347
|
-
for (const [key, value] of Object.entries(colorOverrides)) {
|
|
3348
|
-
if (value !== void 0) {
|
|
3349
|
-
colors[key] = value;
|
|
3350
|
-
}
|
|
3351
|
-
}
|
|
3352
|
-
}
|
|
3353
|
-
return {
|
|
3354
|
-
name: overrides.name ?? base.name,
|
|
3355
|
-
colors,
|
|
3356
|
-
perception: {
|
|
3357
|
-
...base.perception,
|
|
3358
|
-
...overrides.perception ?? {}
|
|
3359
|
-
}
|
|
3360
|
-
};
|
|
3361
|
-
}
|
|
3362
|
-
var STORE_KEY = "__fayz_ui_theme_store__";
|
|
3363
|
-
function createThemeStore() {
|
|
3364
|
-
if (typeof window !== "undefined" && window[STORE_KEY]) {
|
|
3365
|
-
return window[STORE_KEY];
|
|
3366
|
-
}
|
|
3367
|
-
const store = create((set, get) => ({
|
|
3368
|
-
mode: getSavedMode(),
|
|
3369
|
-
resolvedMode: "light",
|
|
3370
|
-
overrides: null,
|
|
3371
|
-
setMode: (mode) => {
|
|
3372
|
-
const resolved = resolveMode(mode);
|
|
3373
|
-
set({ mode, resolvedMode: resolved });
|
|
3374
|
-
if (typeof window !== "undefined") {
|
|
3375
|
-
localStorage.setItem(STORAGE_KEY, mode);
|
|
3376
|
-
}
|
|
3377
|
-
const { overrides } = get();
|
|
3378
|
-
const baseTheme = resolved === "dark" ? darkTheme : lightTheme;
|
|
3379
|
-
const theme = buildTheme(baseTheme, overrides);
|
|
3380
|
-
if (typeof document !== "undefined") {
|
|
3381
|
-
document.documentElement.classList.add("theme-transitioning");
|
|
3382
|
-
applyTheme(theme);
|
|
3383
|
-
document.documentElement.classList.toggle("dark", resolved === "dark");
|
|
3384
|
-
setTimeout(() => {
|
|
3385
|
-
document.documentElement.classList.remove("theme-transitioning");
|
|
3386
|
-
}, 450);
|
|
3387
|
-
} else {
|
|
3388
|
-
applyTheme(theme);
|
|
3389
|
-
}
|
|
3390
|
-
},
|
|
3391
|
-
setOverrides: (overrides) => {
|
|
3392
|
-
set({ overrides });
|
|
3393
|
-
const { resolvedMode } = get();
|
|
3394
|
-
const baseTheme = resolvedMode === "dark" ? darkTheme : lightTheme;
|
|
3395
|
-
const theme = buildTheme(baseTheme, overrides);
|
|
3396
|
-
applyTheme(theme);
|
|
3397
|
-
},
|
|
3398
|
-
// Legacy compat
|
|
3399
|
-
setCustomTheme: (_theme) => {
|
|
3400
|
-
},
|
|
3401
|
-
initialize: () => {
|
|
3402
|
-
const { mode, setMode } = get();
|
|
3403
|
-
setMode(mode);
|
|
3404
|
-
if (typeof window !== "undefined") {
|
|
3405
|
-
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
|
3406
|
-
const current = get();
|
|
3407
|
-
if (current.mode === "system") {
|
|
3408
|
-
current.setMode("system");
|
|
3409
|
-
}
|
|
3410
|
-
});
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
}));
|
|
3414
|
-
if (typeof window !== "undefined") {
|
|
3415
|
-
window[STORE_KEY] = store;
|
|
3416
|
-
}
|
|
3417
|
-
return store;
|
|
3418
|
-
}
|
|
3419
|
-
var useThemeStore = createThemeStore();
|
|
3420
|
-
|
|
3421
|
-
// src/theme/presets.ts
|
|
3422
|
-
function mergeThemeOptions(base, overrides = {}) {
|
|
3423
|
-
return {
|
|
3424
|
-
...base,
|
|
3425
|
-
...overrides,
|
|
3426
|
-
colors: {
|
|
3427
|
-
...base.colors ?? {},
|
|
3428
|
-
...overrides.colors ?? {}
|
|
3429
|
-
},
|
|
3430
|
-
darkColors: {
|
|
3431
|
-
...base.darkColors ?? {},
|
|
3432
|
-
...overrides.darkColors ?? {}
|
|
3433
|
-
},
|
|
3434
|
-
perception: {
|
|
3435
|
-
...base.perception ?? {},
|
|
3436
|
-
...overrides.perception ?? {}
|
|
3437
|
-
}
|
|
3438
|
-
};
|
|
3439
|
-
}
|
|
3440
|
-
var fayzThemePresets = {
|
|
3441
|
-
classic_admin: {
|
|
3442
|
-
name: "classic_admin",
|
|
3443
|
-
colors: lightTheme.colors,
|
|
3444
|
-
darkColors: darkTheme.colors,
|
|
3445
|
-
perception: lightTheme.perception
|
|
3446
|
-
},
|
|
3447
|
-
liquid_glass: {
|
|
3448
|
-
name: "liquid_glass",
|
|
3449
|
-
colors: {
|
|
3450
|
-
background: "260 38% 96%",
|
|
3451
|
-
content: "260 36% 96.5%",
|
|
3452
|
-
card: "0 0% 100% / 0.84",
|
|
3453
|
-
popover: "0 0% 100% / 0.96",
|
|
3454
|
-
secondary: "260 34% 93% / 0.82",
|
|
3455
|
-
muted: "260 30% 91% / 0.78",
|
|
3456
|
-
border: "260 26% 76% / 0.58",
|
|
3457
|
-
input: "260 24% 68% / 0.62"
|
|
3458
|
-
},
|
|
3459
|
-
darkColors: {
|
|
3460
|
-
background: "260 28% 9%",
|
|
3461
|
-
content: "260 28% 10%",
|
|
3462
|
-
card: "260 24% 16% / 0.72",
|
|
3463
|
-
popover: "260 24% 14% / 0.9",
|
|
3464
|
-
secondary: "260 20% 22% / 0.72",
|
|
3465
|
-
muted: "260 18% 20% / 0.68",
|
|
3466
|
-
border: "260 20% 35% / 0.58",
|
|
3467
|
-
input: "260 20% 38% / 0.68"
|
|
3468
|
-
},
|
|
3469
|
-
perception: {
|
|
3470
|
-
buttonRadius: "9999px",
|
|
3471
|
-
cardRadius: "1.25rem",
|
|
3472
|
-
inputRadius: "1rem",
|
|
3473
|
-
modalRadius: "1.5rem",
|
|
3474
|
-
surfaceBackdropFilter: "blur(16px) saturate(1.24) brightness(1.03)",
|
|
3475
|
-
modalBackground: "linear-gradient(145deg, rgb(255 255 255 / 0.82), rgb(249 247 255 / 0.72) 48%, rgb(255 255 255 / 0.64))",
|
|
3476
|
-
modalBorder: "rgb(255 255 255 / 0.74)",
|
|
3477
|
-
modalOverlayBackground: "rgb(26 20 38 / 0.22)",
|
|
3478
|
-
modalOverlayBackdropFilter: "blur(5px) saturate(1.08)",
|
|
3479
|
-
modalShadow: "0 32px 82px -46px rgb(31 18 61 / 0.38), 0 1px 4px rgb(31 18 61 / 0.08), inset 0 1px 0 rgb(255 255 255 / 0.78), inset 0 0 0 1px rgb(255 255 255 / 0.28)",
|
|
3480
|
-
glassEdgeGradient: "linear-gradient(145deg, rgb(255 255 255 / 0.96) 0%, rgb(255 255 255 / 0.44) 24%, rgb(171 151 211 / 0.34) 48%, rgb(45 35 76 / 0.22) 62%, rgb(255 255 255 / 0.7) 100%)",
|
|
3481
|
-
glassPrimaryEdgeGradient: "linear-gradient(145deg, rgb(255 255 255 / 0.74) 0%, rgb(255 255 255 / 0.2) 28%, rgb(45 35 76 / 0.34) 58%, rgb(255 255 255 / 0.5) 100%)",
|
|
3482
|
-
glassInnerHighlight: "inset 0 1px 0 rgb(255 255 255 / 0.72), inset 1px 0 0 rgb(255 255 255 / 0.28), inset 0 -1px 0 rgb(45 35 76 / 0.1)",
|
|
3483
|
-
fieldBackground: "linear-gradient(135deg, rgb(255 255 255 / 0.9) 0%, rgb(250 248 255 / 0.82) 46%, rgb(242 238 252 / 0.74) 100%)",
|
|
3484
|
-
fieldBorder: "rgb(174 158 205 / 0.58)",
|
|
3485
|
-
fieldShadow: "inset 0 1px 0 rgb(255 255 255 / 0.8), inset 0 -1px 0 rgb(45 35 76 / 0.08), 0 12px 28px -24px rgb(31 18 61 / 0.28)",
|
|
3486
|
-
shadowSm: "0 10px 28px -20px rgb(31 18 61 / 0.34), inset 0 1px 0 rgb(255 255 255 / 0.54)",
|
|
3487
|
-
shadowMd: "0 22px 54px -32px rgb(31 18 61 / 0.42), inset 0 1px 0 rgb(255 255 255 / 0.58)",
|
|
3488
|
-
shadowLg: "0 34px 88px -42px rgb(31 18 61 / 0.48), inset 0 1px 0 rgb(255 255 255 / 0.62)",
|
|
3489
|
-
shadowButton: "inset 0 1px 0 rgb(255 255 255 / 0.82), inset 0 -1px 0 rgb(31 18 61 / 0.08), 0 14px 30px -18px rgb(31 18 61 / 0.56)",
|
|
3490
|
-
shadowButtonPrimary: "inset 0 1px 0 rgb(255 255 255 / 0.5), inset 0 -1px 0 rgb(31 18 61 / 0.26), 0 18px 36px -18px rgb(31 18 61 / 0.86)",
|
|
3491
|
-
shadowButtonInset: "inset 0 2px 4px rgb(31 18 61 / 0.22), inset 0 0 0 1px rgb(255 255 255 / 0.24)",
|
|
3492
|
-
buttonBackground: "linear-gradient(135deg, rgb(255 255 255 / 0.92) 0%, rgb(247 242 255 / 0.74) 48%, rgb(229 220 248 / 0.64) 100%)",
|
|
3493
|
-
buttonBackgroundHover: "linear-gradient(135deg, rgb(255 255 255 / 0.98) 0%, rgb(249 244 255 / 0.86) 46%, rgb(231 222 250 / 0.78) 100%)",
|
|
3494
|
-
buttonBorder: "rgb(194 178 221 / 0.86)",
|
|
3495
|
-
buttonPrimaryBackground: "linear-gradient(135deg, rgb(255 255 255 / 0.28) 0%, hsl(var(--primary) / 0.94) 36%, hsl(278 42% 58% / 0.88) 100%)",
|
|
3496
|
-
buttonPrimaryBackgroundHover: "linear-gradient(135deg, rgb(255 255 255 / 0.36) 0%, hsl(var(--primary) / 1) 34%, hsl(278 52% 62% / 0.94) 100%)",
|
|
3497
|
-
buttonPrimaryBorder: "rgb(255 255 255 / 0.38)",
|
|
3498
|
-
buttonBackdropFilter: "blur(18px) saturate(1.45)"
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
};
|
|
3502
|
-
function createFayzTheme(preset = "classic_admin", overrides = {}) {
|
|
3503
|
-
return mergeThemeOptions(fayzThemePresets[preset] ?? fayzThemePresets.classic_admin, overrides);
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
// src/theme/index.ts
|
|
3507
|
-
function ThemeProvider({ children, overrides, defaultMode }) {
|
|
3508
|
-
const initialize = useThemeStore((s) => s.initialize);
|
|
3509
|
-
const setMode = useThemeStore((s) => s.setMode);
|
|
3510
|
-
const setOverrides = useThemeStore((s) => s.setOverrides);
|
|
3511
|
-
React10.useEffect(() => {
|
|
3512
|
-
if (defaultMode) {
|
|
3513
|
-
setMode(defaultMode);
|
|
3514
|
-
}
|
|
3515
|
-
if (overrides !== void 0) {
|
|
3516
|
-
setOverrides(overrides);
|
|
3517
|
-
}
|
|
3518
|
-
initialize();
|
|
3519
|
-
}, []);
|
|
3520
|
-
return children;
|
|
3521
|
-
}
|
|
3522
|
-
function useTheme() {
|
|
3523
|
-
const mode = useThemeStore((s) => s.mode);
|
|
3524
|
-
const resolvedMode = useThemeStore((s) => s.resolvedMode);
|
|
3525
|
-
const overrides = useThemeStore((s) => s.overrides);
|
|
3526
|
-
const setMode = useThemeStore((s) => s.setMode);
|
|
3527
|
-
const setOverrides = useThemeStore((s) => s.setOverrides);
|
|
3528
|
-
return {
|
|
3529
|
-
mode,
|
|
3530
|
-
resolvedMode,
|
|
3531
|
-
isDark: resolvedMode === "dark",
|
|
3532
|
-
overrides,
|
|
3533
|
-
setMode,
|
|
3534
|
-
setOverrides
|
|
3535
|
-
};
|
|
3536
|
-
}
|
|
3537
|
-
function createTheme(overrides) {
|
|
3538
|
-
const colors = { ...lightTheme.colors, ...overrides.colors };
|
|
3539
|
-
if (overrides.brand) {
|
|
3540
|
-
colors.primary = overrides.brand;
|
|
3541
|
-
colors.primaryForeground = "0 0% 100%";
|
|
3542
|
-
colors.ring = overrides.brand;
|
|
3543
|
-
const parts = overrides.brand.split(" ");
|
|
3544
|
-
if (parts.length >= 3) {
|
|
3545
|
-
const hue = (parseFloat(parts[0]) - 50 + 360) % 360;
|
|
3546
|
-
colors.accent = `${hue} ${parts[1]} ${parts[2]}`;
|
|
3547
|
-
colors.accentForeground = "0 0% 100%";
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
return {
|
|
3551
|
-
name: overrides.name ?? lightTheme.name,
|
|
3552
|
-
colors,
|
|
3553
|
-
perception: {
|
|
3554
|
-
...lightTheme.perception,
|
|
3555
|
-
...overrides.perception
|
|
3556
|
-
}
|
|
3557
|
-
};
|
|
3558
|
-
}
|
|
3559
|
-
var Avatar = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
14
|
+
var Avatar = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3560
15
|
AvatarPrimitive.Root,
|
|
3561
16
|
{
|
|
3562
17
|
ref,
|
|
@@ -3568,7 +23,7 @@ var Avatar = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
3568
23
|
}
|
|
3569
24
|
));
|
|
3570
25
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
3571
|
-
var AvatarImage =
|
|
26
|
+
var AvatarImage = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3572
27
|
AvatarPrimitive.Image,
|
|
3573
28
|
{
|
|
3574
29
|
ref,
|
|
@@ -3577,7 +32,7 @@ var AvatarImage = React10.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
3577
32
|
}
|
|
3578
33
|
));
|
|
3579
34
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
3580
|
-
var AvatarFallback =
|
|
35
|
+
var AvatarFallback = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3581
36
|
AvatarPrimitive.Fallback,
|
|
3582
37
|
{
|
|
3583
38
|
ref,
|
|
@@ -3589,599 +44,7 @@ var AvatarFallback = React10.forwardRef(({ className, ...props }, ref) => /* @__
|
|
|
3589
44
|
}
|
|
3590
45
|
));
|
|
3591
46
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
3592
|
-
var SPAN_CLASS = {
|
|
3593
|
-
1: "",
|
|
3594
|
-
2: "sm:col-span-2",
|
|
3595
|
-
3: "sm:col-span-2 lg:col-span-3",
|
|
3596
|
-
4: "sm:col-span-2 lg:col-span-4"
|
|
3597
|
-
};
|
|
3598
|
-
function DashboardGrid({ items, className }) {
|
|
3599
|
-
return /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", className), children: items.map((item) => /* @__PURE__ */ jsx("div", { className: SPAN_CLASS[Math.min(Math.max(item.span ?? 1, 1), 4)] ?? "", children: item.node }, item.id)) });
|
|
3600
|
-
}
|
|
3601
|
-
|
|
3602
|
-
// src/dashboard/layout.ts
|
|
3603
|
-
function resolveDashboardLayout(registered, appConfig, userPrefs, forceVisibleByDefault = false) {
|
|
3604
|
-
const overrides = /* @__PURE__ */ new Map();
|
|
3605
|
-
const merge = (cfg) => {
|
|
3606
|
-
for (const w of cfg?.widgets ?? []) {
|
|
3607
|
-
overrides.set(w.id, { ...overrides.get(w.id), ...w });
|
|
3608
|
-
}
|
|
3609
|
-
};
|
|
3610
|
-
merge(appConfig);
|
|
3611
|
-
merge(userPrefs);
|
|
3612
|
-
return registered.map((def, index) => {
|
|
3613
|
-
const o = overrides.get(def.id);
|
|
3614
|
-
const baseVisible = forceVisibleByDefault ? true : def.defaultVisible ?? true;
|
|
3615
|
-
const visible = o?.visible ?? baseVisible;
|
|
3616
|
-
const order = o?.order ?? def.defaultOrder ?? index;
|
|
3617
|
-
const span = Math.min(Math.max(o?.span ?? def.span ?? 1, 1), 4);
|
|
3618
|
-
return { def, visible, order, span };
|
|
3619
|
-
}).filter((x) => x.visible).sort((a, b) => a.order - b.order).map(({ def, span, order }) => ({ def, span, order }));
|
|
3620
|
-
}
|
|
3621
|
-
var KEY = (surface, scope) => `fayz.dashboard.prefs.${scope}.${surface}`;
|
|
3622
|
-
function read(key) {
|
|
3623
|
-
if (typeof window === "undefined") return null;
|
|
3624
|
-
try {
|
|
3625
|
-
const raw = window.localStorage.getItem(key);
|
|
3626
|
-
return raw ? JSON.parse(raw) : null;
|
|
3627
|
-
} catch {
|
|
3628
|
-
return null;
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
function useDashboardPreferences(surface, scope = "home") {
|
|
3632
|
-
const key = KEY(surface, scope);
|
|
3633
|
-
const [prefs, setPrefs] = React10.useState(() => read(key));
|
|
3634
|
-
React10.useEffect(() => {
|
|
3635
|
-
setPrefs(read(key));
|
|
3636
|
-
}, [key]);
|
|
3637
|
-
const save = React10.useCallback((next) => {
|
|
3638
|
-
setPrefs(next);
|
|
3639
|
-
if (typeof window !== "undefined") {
|
|
3640
|
-
try {
|
|
3641
|
-
window.localStorage.setItem(key, JSON.stringify(next));
|
|
3642
|
-
} catch {
|
|
3643
|
-
}
|
|
3644
|
-
}
|
|
3645
|
-
}, [key]);
|
|
3646
|
-
const setVisible = React10.useCallback((widgetId, visible) => {
|
|
3647
|
-
setPrefs((cur) => {
|
|
3648
|
-
const widgets = [...cur?.widgets ?? []];
|
|
3649
|
-
const i = widgets.findIndex((w) => w.id === widgetId);
|
|
3650
|
-
if (i >= 0) widgets[i] = { ...widgets[i], visible };
|
|
3651
|
-
else widgets.push({ id: widgetId, visible });
|
|
3652
|
-
const next = { ...cur, widgets };
|
|
3653
|
-
if (typeof window !== "undefined") {
|
|
3654
|
-
try {
|
|
3655
|
-
window.localStorage.setItem(key, JSON.stringify(next));
|
|
3656
|
-
} catch {
|
|
3657
|
-
}
|
|
3658
|
-
}
|
|
3659
|
-
return next;
|
|
3660
|
-
});
|
|
3661
|
-
}, [key]);
|
|
3662
|
-
const reset = React10.useCallback(() => {
|
|
3663
|
-
setPrefs(null);
|
|
3664
|
-
if (typeof window !== "undefined") {
|
|
3665
|
-
try {
|
|
3666
|
-
window.localStorage.removeItem(key);
|
|
3667
|
-
} catch {
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
}, [key]);
|
|
3671
|
-
return { prefs, save, setVisible, reset };
|
|
3672
|
-
}
|
|
3673
|
-
var DEFAULT_RANGE_OPTIONS = ["7d", "30d", "90d"];
|
|
3674
|
-
var DashboardRangeContext = React10.createContext(null);
|
|
3675
|
-
function useDashboardRange() {
|
|
3676
|
-
return React10.useContext(DashboardRangeContext);
|
|
3677
|
-
}
|
|
3678
|
-
function DashboardRangeProvider({
|
|
3679
|
-
options = DEFAULT_RANGE_OPTIONS,
|
|
3680
|
-
defaultRange,
|
|
3681
|
-
onChange,
|
|
3682
|
-
children
|
|
3683
|
-
}) {
|
|
3684
|
-
const [range, setRangeState] = React10.useState(defaultRange ?? options[1] ?? options[0] ?? "30d");
|
|
3685
|
-
const setRange = React10.useCallback((r) => {
|
|
3686
|
-
setRangeState(r);
|
|
3687
|
-
onChange?.(r);
|
|
3688
|
-
}, [onChange]);
|
|
3689
|
-
const value = React10.useMemo(() => ({ range, setRange, options }), [range, setRange, options]);
|
|
3690
|
-
return /* @__PURE__ */ jsx(DashboardRangeContext.Provider, { value, children });
|
|
3691
|
-
}
|
|
3692
|
-
function DashboardRangeControl({ className }) {
|
|
3693
|
-
const ctx = useDashboardRange();
|
|
3694
|
-
if (!ctx) return null;
|
|
3695
|
-
return /* @__PURE__ */ jsx(
|
|
3696
|
-
SegmentedControl,
|
|
3697
|
-
{
|
|
3698
|
-
options: ctx.options,
|
|
3699
|
-
value: ctx.range,
|
|
3700
|
-
onChange: ctx.setRange,
|
|
3701
|
-
className,
|
|
3702
|
-
"aria-label": "Time range"
|
|
3703
|
-
}
|
|
3704
|
-
);
|
|
3705
|
-
}
|
|
3706
|
-
function DashboardCustomizeMenu({ widgets, visibleIds, onToggle, onReset, label }) {
|
|
3707
|
-
const t = useTranslation();
|
|
3708
|
-
if (widgets.length === 0) return null;
|
|
3709
|
-
return /* @__PURE__ */ jsxs(Dropdown, { children: [
|
|
3710
|
-
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", children: [
|
|
3711
|
-
/* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }),
|
|
3712
|
-
label ?? "Customize"
|
|
3713
|
-
] }) }),
|
|
3714
|
-
/* @__PURE__ */ jsxs(DropdownContent, { align: "end", className: "max-h-[70vh] w-60 overflow-y-auto", children: [
|
|
3715
|
-
/* @__PURE__ */ jsx(DropdownLabel, { children: label ?? "Show widgets" }),
|
|
3716
|
-
/* @__PURE__ */ jsx(DropdownSeparator, {}),
|
|
3717
|
-
widgets.map((w) => /* @__PURE__ */ jsx(
|
|
3718
|
-
DropdownCheckboxItem,
|
|
3719
|
-
{
|
|
3720
|
-
checked: visibleIds.has(w.id),
|
|
3721
|
-
onCheckedChange: (checked) => onToggle(w.id, Boolean(checked)),
|
|
3722
|
-
onSelect: (e) => e.preventDefault(),
|
|
3723
|
-
children: t(w.title)
|
|
3724
|
-
},
|
|
3725
|
-
w.id
|
|
3726
|
-
)),
|
|
3727
|
-
/* @__PURE__ */ jsx(DropdownSeparator, {}),
|
|
3728
|
-
/* @__PURE__ */ jsxs(DropdownItem, { onSelect: () => onReset(), children: [
|
|
3729
|
-
/* @__PURE__ */ jsx(RotateCcw, { className: "mr-2 h-3.5 w-3.5" }),
|
|
3730
|
-
"Reset to defaults"
|
|
3731
|
-
] })
|
|
3732
|
-
] })
|
|
3733
|
-
] });
|
|
3734
|
-
}
|
|
3735
|
-
function HoverHide({ id, onHide, children }) {
|
|
3736
|
-
if (!onHide) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
3737
|
-
return /* @__PURE__ */ jsxs("div", { className: "group relative h-full", children: [
|
|
3738
|
-
children,
|
|
3739
|
-
/* @__PURE__ */ jsx(
|
|
3740
|
-
"button",
|
|
3741
|
-
{
|
|
3742
|
-
type: "button",
|
|
3743
|
-
onClick: () => onHide(id),
|
|
3744
|
-
title: "Hide widget",
|
|
3745
|
-
"aria-label": "Hide widget",
|
|
3746
|
-
className: "absolute right-2 top-2 z-10 inline-flex h-6 w-6 items-center justify-center rounded-md border border-border bg-background/90 text-muted-foreground opacity-0 shadow-sm transition-opacity hover:text-foreground group-hover:opacity-100",
|
|
3747
|
-
children: /* @__PURE__ */ jsx(EyeOff, { className: "h-3.5 w-3.5" })
|
|
3748
|
-
}
|
|
3749
|
-
)
|
|
3750
|
-
] });
|
|
3751
|
-
}
|
|
3752
|
-
function toGridItems(laidOut, onHide) {
|
|
3753
|
-
return laidOut.map(({ def, span }) => {
|
|
3754
|
-
const Component = resolvePluginComponent(def);
|
|
3755
|
-
const node = Component ? /* @__PURE__ */ jsx(Component, { ...def.props ?? {} }) : null;
|
|
3756
|
-
return { id: def.id, span, node: /* @__PURE__ */ jsx(HoverHide, { id: def.id, onHide, children: node }) };
|
|
3757
|
-
});
|
|
3758
|
-
}
|
|
3759
|
-
var DashboardNavigateContext = React10.createContext(
|
|
3760
|
-
(path) => hashRouterAdapter().navigate(path)
|
|
3761
|
-
);
|
|
3762
|
-
var DashboardNavigateProvider = DashboardNavigateContext.Provider;
|
|
3763
|
-
function useDashboardNavigate() {
|
|
3764
|
-
return React10.useContext(DashboardNavigateContext);
|
|
3765
|
-
}
|
|
3766
|
-
function WidgetGrid({ widgets, appLayout, surface }) {
|
|
3767
|
-
const { prefs, setVisible } = useDashboardPreferences(surface);
|
|
3768
|
-
const laidOut = resolveDashboardLayout(widgets, appLayout, prefs ?? void 0);
|
|
3769
|
-
return /* @__PURE__ */ jsx(DashboardGrid, { items: toGridItems(laidOut, (id) => setVisible(id, false)) });
|
|
3770
|
-
}
|
|
3771
|
-
function DashboardCanvas({
|
|
3772
|
-
surface,
|
|
3773
|
-
domain,
|
|
3774
|
-
title,
|
|
3775
|
-
subtitle,
|
|
3776
|
-
showHeader = true,
|
|
3777
|
-
appLayout,
|
|
3778
|
-
range,
|
|
3779
|
-
customizable = true,
|
|
3780
|
-
customizeLabel,
|
|
3781
|
-
initialVisibility,
|
|
3782
|
-
onNavigate,
|
|
3783
|
-
className
|
|
3784
|
-
}) {
|
|
3785
|
-
const runtime = usePluginRuntime();
|
|
3786
|
-
const widgets = getDashboardWidgets(runtime, { surface, domain });
|
|
3787
|
-
const rangeOpts = range ? range === true ? {} : range : null;
|
|
3788
|
-
const forceAll = (initialVisibility ?? (surface === "plugin-home" ? "all" : "default")) === "all";
|
|
3789
|
-
const { prefs, setVisible, reset } = useDashboardPreferences(surface, domain ?? "home");
|
|
3790
|
-
const laidOut = resolveDashboardLayout(widgets, appLayout, prefs ?? void 0, forceAll);
|
|
3791
|
-
const visibleIds = React10.useMemo(() => new Set(laidOut.map((w) => w.def.id)), [laidOut]);
|
|
3792
|
-
const hasControls = Boolean(rangeOpts) || customizable && widgets.length > 0;
|
|
3793
|
-
const headerBlock = showHeader && (title || subtitle) ? /* @__PURE__ */ jsxs("div", { children: [
|
|
3794
|
-
title ? /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }) : null,
|
|
3795
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: subtitle }) : null
|
|
3796
|
-
] }) : null;
|
|
3797
|
-
const topRow = headerBlock || hasControls ? /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
3798
|
-
headerBlock ?? /* @__PURE__ */ jsx("span", {}),
|
|
3799
|
-
hasControls ? /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
3800
|
-
customizable && widgets.length > 0 ? /* @__PURE__ */ jsx(
|
|
3801
|
-
DashboardCustomizeMenu,
|
|
3802
|
-
{
|
|
3803
|
-
widgets,
|
|
3804
|
-
visibleIds,
|
|
3805
|
-
onToggle: setVisible,
|
|
3806
|
-
onReset: reset,
|
|
3807
|
-
label: customizeLabel
|
|
3808
|
-
}
|
|
3809
|
-
) : null,
|
|
3810
|
-
rangeOpts ? /* @__PURE__ */ jsx(DashboardRangeControl, {}) : null
|
|
3811
|
-
] }) : null
|
|
3812
|
-
] }) : null;
|
|
3813
|
-
const content = /* @__PURE__ */ jsxs("div", { className: className ?? "space-y-6 p-6", children: [
|
|
3814
|
-
topRow,
|
|
3815
|
-
widgets.length === 0 ? /* @__PURE__ */ jsxs(Card, { children: [
|
|
3816
|
-
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
3817
|
-
/* @__PURE__ */ jsx(CardTitle, { children: "Welcome!" }),
|
|
3818
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "Your dashboard will show key metrics and information here." })
|
|
3819
|
-
] }),
|
|
3820
|
-
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Enable plugins or register dashboard widgets to populate this view." }) })
|
|
3821
|
-
] }) : laidOut.length === 0 ? /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "p-6 text-sm text-muted-foreground", children: "All widgets are hidden. Use \u201CCustomize\u201D to choose what to show." }) }) : /* @__PURE__ */ jsx(DashboardGrid, { items: toGridItems(laidOut) })
|
|
3822
|
-
] });
|
|
3823
|
-
const navWrapped = onNavigate ? /* @__PURE__ */ jsx(DashboardNavigateProvider, { value: onNavigate, children: content }) : content;
|
|
3824
|
-
return rangeOpts ? /* @__PURE__ */ jsx(DashboardRangeProvider, { options: rangeOpts.options, defaultRange: rangeOpts.default, onChange: rangeOpts.onChange, children: navWrapped }) : navWrapped;
|
|
3825
|
-
}
|
|
3826
|
-
function renderIcon(icon, className = "h-4 w-4") {
|
|
3827
|
-
if (!icon) return null;
|
|
3828
|
-
if (typeof icon === "string") {
|
|
3829
|
-
const Comp = LucideIcons[icon];
|
|
3830
|
-
return Comp ? React10.createElement(Comp, { className }) : null;
|
|
3831
|
-
}
|
|
3832
|
-
return React10.createElement(icon, { className });
|
|
3833
|
-
}
|
|
3834
|
-
function formatKpiValue(value, format, currency) {
|
|
3835
|
-
const locale = currency?.locale ?? "pt-BR";
|
|
3836
|
-
switch (format) {
|
|
3837
|
-
case "currency":
|
|
3838
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency: currency?.code ?? "BRL", maximumFractionDigits: 0 }).format(value);
|
|
3839
|
-
case "percent":
|
|
3840
|
-
return `${new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(value)}%`;
|
|
3841
|
-
case "duration":
|
|
3842
|
-
return `${Math.round(value)} min`;
|
|
3843
|
-
default:
|
|
3844
|
-
return new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(value);
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
function deriveTrend(current, previous, explicit) {
|
|
3848
|
-
if (explicit) return explicit;
|
|
3849
|
-
if (previous == null) return void 0;
|
|
3850
|
-
return current > previous ? "up" : current < previous ? "down" : "neutral";
|
|
3851
|
-
}
|
|
3852
|
-
function TrendChip({ trend, delta, invert }) {
|
|
3853
|
-
if (trend === "neutral") return null;
|
|
3854
|
-
const good = invert ? trend === "down" : trend === "up";
|
|
3855
|
-
const Icon2 = trend === "up" ? TrendingUp : TrendingDown;
|
|
3856
|
-
return /* @__PURE__ */ jsxs("span", { className: cn(
|
|
3857
|
-
"inline-flex items-center gap-0.5 text-xs font-medium",
|
|
3858
|
-
good ? "text-emerald-600" : "text-rose-600"
|
|
3859
|
-
), children: [
|
|
3860
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3" }),
|
|
3861
|
-
delta != null ? `${delta > 0 ? "+" : ""}${delta}%` : null
|
|
3862
|
-
] });
|
|
3863
|
-
}
|
|
3864
|
-
function trendColor(trend, invert) {
|
|
3865
|
-
if (!trend || trend === "neutral") return "hsl(var(--info))";
|
|
3866
|
-
const good = invert ? trend === "down" : trend === "up";
|
|
3867
|
-
return good ? "#10b981" : "#f43f5e";
|
|
3868
|
-
}
|
|
3869
|
-
function Sparkline({ data, color }) {
|
|
3870
|
-
const gid = React10.useId();
|
|
3871
|
-
if (!data || data.length < 2) return null;
|
|
3872
|
-
const W = 96, H = 32, P = 3;
|
|
3873
|
-
const min = Math.min(...data);
|
|
3874
|
-
const max = Math.max(...data);
|
|
3875
|
-
const span = max - min || 1;
|
|
3876
|
-
const step = (W - P * 2) / (data.length - 1);
|
|
3877
|
-
const pts = data.map((d, i) => {
|
|
3878
|
-
const x = P + i * step;
|
|
3879
|
-
const y = P + (H - P * 2) * (1 - (d - min) / span);
|
|
3880
|
-
return [x, y];
|
|
3881
|
-
});
|
|
3882
|
-
const line = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ");
|
|
3883
|
-
const area = `${line} L${pts[pts.length - 1][0].toFixed(1)},${H} L${pts[0][0].toFixed(1)},${H} Z`;
|
|
3884
|
-
return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "h-8 w-24 shrink-0 overflow-visible", preserveAspectRatio: "none", "aria-hidden": true, children: [
|
|
3885
|
-
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gid, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
3886
|
-
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: color, stopOpacity: "0.22" }),
|
|
3887
|
-
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: color, stopOpacity: "0" })
|
|
3888
|
-
] }) }),
|
|
3889
|
-
/* @__PURE__ */ jsx("path", { d: area, fill: `url(#${gid})` }),
|
|
3890
|
-
/* @__PURE__ */ jsx("path", { d: line, fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" })
|
|
3891
|
-
] });
|
|
3892
|
-
}
|
|
3893
|
-
function Gauge({ value, goal, color, children }) {
|
|
3894
|
-
const pct = goal > 0 ? Math.min(1, Math.max(0, value / goal)) : 0;
|
|
3895
|
-
const R = 40, CX = 50, CY = 46, SW = 9;
|
|
3896
|
-
const len = Math.PI * R;
|
|
3897
|
-
const d = `M ${CX - R} ${CY} A ${R} ${R} 0 0 1 ${CX + R} ${CY}`;
|
|
3898
|
-
return /* @__PURE__ */ jsxs("div", { className: "relative mx-auto w-full max-w-[160px]", children: [
|
|
3899
|
-
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 52", className: "w-full", "aria-hidden": true, children: [
|
|
3900
|
-
/* @__PURE__ */ jsx("path", { d, fill: "none", stroke: "hsl(var(--muted))", strokeWidth: SW, strokeLinecap: "round" }),
|
|
3901
|
-
/* @__PURE__ */ jsx(
|
|
3902
|
-
"path",
|
|
3903
|
-
{
|
|
3904
|
-
d,
|
|
3905
|
-
fill: "none",
|
|
3906
|
-
stroke: color,
|
|
3907
|
-
strokeWidth: SW,
|
|
3908
|
-
strokeLinecap: "round",
|
|
3909
|
-
strokeDasharray: len,
|
|
3910
|
-
strokeDashoffset: len * (1 - pct)
|
|
3911
|
-
}
|
|
3912
|
-
)
|
|
3913
|
-
] }),
|
|
3914
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 bottom-0 text-center text-2xl font-semibold tracking-tight text-foreground", children })
|
|
3915
|
-
] });
|
|
3916
|
-
}
|
|
3917
|
-
function KpiCard(props) {
|
|
3918
|
-
const { label, icon, sub, format, currency, invertTrend } = props;
|
|
3919
|
-
const [computed, setComputed] = React10.useState(null);
|
|
3920
|
-
const [errored, setErrored] = React10.useState(false);
|
|
3921
|
-
React10.useEffect(() => {
|
|
3922
|
-
if (!props.compute) return;
|
|
3923
|
-
let cancelled = false;
|
|
3924
|
-
props.compute().then((v) => {
|
|
3925
|
-
if (!cancelled) setComputed(v);
|
|
3926
|
-
}).catch(() => {
|
|
3927
|
-
if (!cancelled) setErrored(true);
|
|
3928
|
-
});
|
|
3929
|
-
return () => {
|
|
3930
|
-
cancelled = true;
|
|
3931
|
-
};
|
|
3932
|
-
}, [props.compute]);
|
|
3933
|
-
const isAsync = Boolean(props.compute);
|
|
3934
|
-
const loading = props.loading || isAsync && computed === null && !errored;
|
|
3935
|
-
let display = props.value;
|
|
3936
|
-
let current = props.current;
|
|
3937
|
-
let previous = props.previous;
|
|
3938
|
-
let trend = props.trend;
|
|
3939
|
-
let unit;
|
|
3940
|
-
let spark = props.spark;
|
|
3941
|
-
if (isAsync && computed) {
|
|
3942
|
-
display = formatKpiValue(computed.value, format, currency);
|
|
3943
|
-
current = computed.value;
|
|
3944
|
-
previous = computed.previousValue;
|
|
3945
|
-
trend = computed.trend;
|
|
3946
|
-
unit = computed.unit;
|
|
3947
|
-
spark = computed.spark ?? spark;
|
|
3948
|
-
} else if (display == null && props.amount != null) {
|
|
3949
|
-
display = formatKpiValue(props.amount, format, currency);
|
|
3950
|
-
current = current ?? props.amount;
|
|
3951
|
-
}
|
|
3952
|
-
const resolvedTrend = current != null ? deriveTrend(current, previous, trend) : trend;
|
|
3953
|
-
const delta = current != null && previous != null && previous !== 0 ? Math.round((current - previous) / previous * 100) : null;
|
|
3954
|
-
const accent = props.accent ?? trendColor(resolvedTrend, invertTrend);
|
|
3955
|
-
const valueText = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3956
|
-
display,
|
|
3957
|
-
unit ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm font-normal text-muted-foreground", children: unit }) : null
|
|
3958
|
-
] });
|
|
3959
|
-
const trendChip = resolvedTrend ? /* @__PURE__ */ jsx(TrendChip, { trend: resolvedTrend, delta, invert: invertTrend }) : null;
|
|
3960
|
-
const isGauge = props.goal != null && current != null;
|
|
3961
|
-
const hasSpark = Boolean(spark && spark.length >= 2);
|
|
3962
|
-
let body;
|
|
3963
|
-
if (errored) {
|
|
3964
|
-
body = /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u2014" });
|
|
3965
|
-
} else if (loading) {
|
|
3966
|
-
body = isGauge ? /* @__PURE__ */ jsx("div", { className: "mx-auto h-16 w-32 animate-pulse rounded bg-muted" }) : /* @__PURE__ */ jsx("div", { className: "h-7 w-20 animate-pulse rounded bg-muted" });
|
|
3967
|
-
} else if (isGauge) {
|
|
3968
|
-
body = /* @__PURE__ */ jsxs("div", { children: [
|
|
3969
|
-
/* @__PURE__ */ jsx(Gauge, { value: current, goal: props.goal, color: accent, children: valueText }),
|
|
3970
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-center gap-2 text-xs text-muted-foreground", children: [
|
|
3971
|
-
trendChip,
|
|
3972
|
-
/* @__PURE__ */ jsx("span", { children: sub ?? `out of ${formatKpiValue(props.goal, format, currency)}` })
|
|
3973
|
-
] })
|
|
3974
|
-
] });
|
|
3975
|
-
} else if (hasSpark) {
|
|
3976
|
-
body = /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3", children: [
|
|
3977
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
3978
|
-
/* @__PURE__ */ jsx("div", { className: "text-2xl font-semibold tracking-tight text-foreground", children: valueText }),
|
|
3979
|
-
trendChip ? /* @__PURE__ */ jsx("div", { className: "mt-0.5", children: trendChip }) : null
|
|
3980
|
-
] }),
|
|
3981
|
-
/* @__PURE__ */ jsx(Sparkline, { data: spark, color: accent })
|
|
3982
|
-
] });
|
|
3983
|
-
} else {
|
|
3984
|
-
body = /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
3985
|
-
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold tracking-tight text-foreground", children: valueText }),
|
|
3986
|
-
trendChip ? /* @__PURE__ */ jsx("span", { className: "mb-0.5", children: trendChip }) : null
|
|
3987
|
-
] });
|
|
3988
|
-
}
|
|
3989
|
-
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "p-4", children: [
|
|
3990
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
3991
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: label }),
|
|
3992
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: renderIcon(icon) })
|
|
3993
|
-
] }),
|
|
3994
|
-
body,
|
|
3995
|
-
sub && !isGauge ? /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: sub }) : null
|
|
3996
|
-
] }) });
|
|
3997
|
-
}
|
|
3998
|
-
var PALETTE = [
|
|
3999
|
-
"hsl(var(--info))",
|
|
4000
|
-
"hsl(var(--magic))",
|
|
4001
|
-
"hsl(var(--success))",
|
|
4002
|
-
"hsl(var(--warning))",
|
|
4003
|
-
"hsl(var(--destructive))"
|
|
4004
|
-
];
|
|
4005
|
-
var AXIS = "hsl(var(--muted-foreground))";
|
|
4006
|
-
var GRID = "hsl(var(--border))";
|
|
4007
|
-
function ChartWidget({
|
|
4008
|
-
type,
|
|
4009
|
-
data,
|
|
4010
|
-
series,
|
|
4011
|
-
categoryKey = "name",
|
|
4012
|
-
title,
|
|
4013
|
-
icon,
|
|
4014
|
-
headerAction,
|
|
4015
|
-
height = 240,
|
|
4016
|
-
className
|
|
4017
|
-
}) {
|
|
4018
|
-
const color = (i, explicit) => explicit ?? PALETTE[i % PALETTE.length];
|
|
4019
|
-
return /* @__PURE__ */ jsxs(Card, { className, children: [
|
|
4020
|
-
(title || headerAction) && /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0", children: [
|
|
4021
|
-
title ? /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
|
|
4022
|
-
renderIcon(icon),
|
|
4023
|
-
title
|
|
4024
|
-
] }) : /* @__PURE__ */ jsx("span", {}),
|
|
4025
|
-
headerAction
|
|
4026
|
-
] }),
|
|
4027
|
-
/* @__PURE__ */ jsx(CardContent, { className: "p-4 pt-2", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height, children: type === "pie" ? /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
4028
|
-
/* @__PURE__ */ jsx(Tooltip$1, {}),
|
|
4029
|
-
/* @__PURE__ */ jsx(Pie, { data, dataKey: series[0]?.dataKey ?? "value", nameKey: categoryKey, innerRadius: 50, outerRadius: 80, paddingAngle: 2, children: data.map((_, i) => /* @__PURE__ */ jsx(Cell, { fill: color(i, series[0]?.color) }, i)) }),
|
|
4030
|
-
/* @__PURE__ */ jsx(Legend, {})
|
|
4031
|
-
] }) : type === "bar" ? /* @__PURE__ */ jsxs(BarChart, { data, children: [
|
|
4032
|
-
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: GRID, vertical: false }),
|
|
4033
|
-
/* @__PURE__ */ jsx(XAxis, { dataKey: categoryKey, stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false }),
|
|
4034
|
-
/* @__PURE__ */ jsx(YAxis, { stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false, width: 36 }),
|
|
4035
|
-
/* @__PURE__ */ jsx(Tooltip$1, { cursor: { fill: GRID, opacity: 0.3 } }),
|
|
4036
|
-
series.length > 1 && /* @__PURE__ */ jsx(Legend, {}),
|
|
4037
|
-
series.map((s, i) => /* @__PURE__ */ jsx(Bar, { dataKey: s.dataKey, name: s.label ?? s.dataKey, fill: color(i, s.color), radius: [4, 4, 0, 0] }, s.dataKey))
|
|
4038
|
-
] }) : type === "area" ? /* @__PURE__ */ jsxs(AreaChart, { data, children: [
|
|
4039
|
-
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: GRID, vertical: false }),
|
|
4040
|
-
/* @__PURE__ */ jsx(XAxis, { dataKey: categoryKey, stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false }),
|
|
4041
|
-
/* @__PURE__ */ jsx(YAxis, { stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false, width: 36 }),
|
|
4042
|
-
/* @__PURE__ */ jsx(Tooltip$1, {}),
|
|
4043
|
-
series.length > 1 && /* @__PURE__ */ jsx(Legend, {}),
|
|
4044
|
-
series.map((s, i) => /* @__PURE__ */ jsx(Area, { dataKey: s.dataKey, name: s.label ?? s.dataKey, stroke: color(i, s.color), fill: color(i, s.color), fillOpacity: 0.15 }, s.dataKey))
|
|
4045
|
-
] }) : /* @__PURE__ */ jsxs(LineChart, { data, children: [
|
|
4046
|
-
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: GRID, vertical: false }),
|
|
4047
|
-
/* @__PURE__ */ jsx(XAxis, { dataKey: categoryKey, stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false }),
|
|
4048
|
-
/* @__PURE__ */ jsx(YAxis, { stroke: AXIS, fontSize: 12, tickLine: false, axisLine: false, width: 36 }),
|
|
4049
|
-
/* @__PURE__ */ jsx(Tooltip$1, {}),
|
|
4050
|
-
series.length > 1 && /* @__PURE__ */ jsx(Legend, {}),
|
|
4051
|
-
series.map((s, i) => /* @__PURE__ */ jsx(Line, { dataKey: s.dataKey, name: s.label ?? s.dataKey, stroke: color(i, s.color), strokeWidth: 2, dot: false }, s.dataKey))
|
|
4052
|
-
] }) }) })
|
|
4053
|
-
] });
|
|
4054
|
-
}
|
|
4055
|
-
function TableWidget({
|
|
4056
|
-
title,
|
|
4057
|
-
icon,
|
|
4058
|
-
columns,
|
|
4059
|
-
data,
|
|
4060
|
-
loading,
|
|
4061
|
-
emptyMessage,
|
|
4062
|
-
onRowClick,
|
|
4063
|
-
headerAction,
|
|
4064
|
-
className
|
|
4065
|
-
}) {
|
|
4066
|
-
return /* @__PURE__ */ jsxs(Card, { className, children: [
|
|
4067
|
-
(title || headerAction) && /* @__PURE__ */ jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0", children: [
|
|
4068
|
-
title ? /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
|
|
4069
|
-
renderIcon(icon),
|
|
4070
|
-
title
|
|
4071
|
-
] }) : /* @__PURE__ */ jsx("span", {}),
|
|
4072
|
-
headerAction
|
|
4073
|
-
] }),
|
|
4074
|
-
/* @__PURE__ */ jsx(CardContent, { className: "p-0", children: /* @__PURE__ */ jsx(
|
|
4075
|
-
DataTable,
|
|
4076
|
-
{
|
|
4077
|
-
columns,
|
|
4078
|
-
data,
|
|
4079
|
-
loading,
|
|
4080
|
-
emptyMessage,
|
|
4081
|
-
onRowClick,
|
|
4082
|
-
variant: "flat",
|
|
4083
|
-
compact: true
|
|
4084
|
-
}
|
|
4085
|
-
) })
|
|
4086
|
-
] });
|
|
4087
|
-
}
|
|
4088
|
-
function OnboardingWidget({
|
|
4089
|
-
title = "Getting started",
|
|
4090
|
-
subtitle,
|
|
4091
|
-
steps,
|
|
4092
|
-
onNavigate,
|
|
4093
|
-
hideWhenComplete = true,
|
|
4094
|
-
className
|
|
4095
|
-
}) {
|
|
4096
|
-
const [completed, setCompleted] = React10.useState(
|
|
4097
|
-
() => Object.fromEntries(steps.map((s) => [s.id, Boolean(s.done)]))
|
|
4098
|
-
);
|
|
4099
|
-
React10.useEffect(() => {
|
|
4100
|
-
let cancelled = false;
|
|
4101
|
-
Promise.all(steps.map(async (s) => {
|
|
4102
|
-
if (s.done != null) return [s.id, s.done];
|
|
4103
|
-
if (!s.check) return [s.id, false];
|
|
4104
|
-
try {
|
|
4105
|
-
return [s.id, await s.check()];
|
|
4106
|
-
} catch {
|
|
4107
|
-
return [s.id, false];
|
|
4108
|
-
}
|
|
4109
|
-
})).then((entries) => {
|
|
4110
|
-
if (!cancelled) setCompleted(Object.fromEntries(entries));
|
|
4111
|
-
});
|
|
4112
|
-
return () => {
|
|
4113
|
-
cancelled = true;
|
|
4114
|
-
};
|
|
4115
|
-
}, [steps]);
|
|
4116
|
-
const doneCount = steps.filter((s) => completed[s.id]).length;
|
|
4117
|
-
const allDone = doneCount === steps.length;
|
|
4118
|
-
if (allDone && hideWhenComplete) return null;
|
|
4119
|
-
const runAction = (action) => {
|
|
4120
|
-
if (!action) return;
|
|
4121
|
-
if (typeof action === "string") onNavigate?.(action);
|
|
4122
|
-
else action();
|
|
4123
|
-
};
|
|
4124
|
-
return /* @__PURE__ */ jsxs(Card, { className, children: [
|
|
4125
|
-
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
4126
|
-
/* @__PURE__ */ jsx(CardTitle, { children: title }),
|
|
4127
|
-
subtitle ? /* @__PURE__ */ jsx(CardDescription, { children: subtitle }) : null,
|
|
4128
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-3", children: [
|
|
4129
|
-
/* @__PURE__ */ jsx("div", { className: "h-1.5 flex-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
|
|
4130
|
-
"div",
|
|
4131
|
-
{
|
|
4132
|
-
className: "h-full rounded-full bg-primary transition-all",
|
|
4133
|
-
style: { width: `${steps.length ? doneCount / steps.length * 100 : 0}%` }
|
|
4134
|
-
}
|
|
4135
|
-
) }),
|
|
4136
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-muted-foreground", children: [
|
|
4137
|
-
doneCount,
|
|
4138
|
-
"/",
|
|
4139
|
-
steps.length
|
|
4140
|
-
] })
|
|
4141
|
-
] })
|
|
4142
|
-
] }),
|
|
4143
|
-
/* @__PURE__ */ jsx(CardContent, { className: "space-y-1 pt-0", children: steps.map((step) => {
|
|
4144
|
-
const isDone = completed[step.id];
|
|
4145
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-lg px-2 py-2.5 hover:bg-muted/50", children: [
|
|
4146
|
-
/* @__PURE__ */ jsx("span", { className: cn(
|
|
4147
|
-
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full",
|
|
4148
|
-
isDone ? "bg-emerald-100 text-emerald-600" : "bg-muted text-muted-foreground"
|
|
4149
|
-
), children: isDone ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : renderIcon(step.icon, "h-4 w-4") ?? /* @__PURE__ */ jsx(Circle, { className: "h-4 w-4" }) }),
|
|
4150
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
4151
|
-
/* @__PURE__ */ jsx("p", { className: cn("text-sm font-medium", isDone && "text-muted-foreground line-through"), children: step.title }),
|
|
4152
|
-
step.description ? /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: step.description }) : null
|
|
4153
|
-
] }),
|
|
4154
|
-
!isDone && step.action ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: () => runAction(step.action), children: step.actionLabel ?? "Start" }) : null
|
|
4155
|
-
] }, step.id);
|
|
4156
|
-
}) })
|
|
4157
|
-
] });
|
|
4158
|
-
}
|
|
4159
|
-
|
|
4160
|
-
// src/dashboard/define.ts
|
|
4161
|
-
function build(kind, defaultSpan, input) {
|
|
4162
|
-
return {
|
|
4163
|
-
kind,
|
|
4164
|
-
span: input.span ?? defaultSpan,
|
|
4165
|
-
defaultVisible: input.defaultVisible ?? true,
|
|
4166
|
-
id: input.id,
|
|
4167
|
-
title: input.title,
|
|
4168
|
-
description: input.description,
|
|
4169
|
-
icon: input.icon,
|
|
4170
|
-
domain: input.domain,
|
|
4171
|
-
defaultOrder: input.defaultOrder,
|
|
4172
|
-
surfaces: input.surfaces,
|
|
4173
|
-
verticalId: input.verticalId,
|
|
4174
|
-
permission: input.permission,
|
|
4175
|
-
component: input.component,
|
|
4176
|
-
props: input.props
|
|
4177
|
-
};
|
|
4178
|
-
}
|
|
4179
|
-
var defineKpiWidget = (input) => build("kpi", 1, input);
|
|
4180
|
-
var defineChartWidget = (input) => build("chart", 2, input);
|
|
4181
|
-
var defineTableWidget = (input) => build("table", 4, input);
|
|
4182
|
-
var defineOnboardingWidget = (input) => build("onboarding", 2, input);
|
|
4183
|
-
var defineCustomWidget = (input) => build("custom", 2, input);
|
|
4184
47
|
|
|
4185
|
-
export {
|
|
48
|
+
export { Avatar, AvatarFallback, AvatarImage };
|
|
4186
49
|
//# sourceMappingURL=index.js.map
|
|
4187
50
|
//# sourceMappingURL=index.js.map
|