@motor-hero/ui-kit 0.35.0 → 0.36.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/index.js CHANGED
@@ -1,419 +1,372 @@
1
- // src/components/theme-provider.tsx
2
- import { createContext, useContext, useEffect, useState } from "react";
1
+ // src/components/app-shell.tsx
2
+ import { ChevronLeft, ChevronRight, Menu } from "lucide-react";
3
+ import { useState as useState3 } from "react";
3
4
 
4
- // src/lib/company-theme.ts
5
- var VAR_MAP = {
6
- primary: "--color-primary",
7
- primaryForeground: "--color-primary-foreground",
8
- secondary: "--color-secondary",
9
- secondaryForeground: "--color-secondary-foreground",
10
- accent: "--color-accent",
11
- accentForeground: "--color-accent-foreground",
12
- ring: "--color-ring"
13
- };
14
- function definedTokens(tokens) {
15
- if (!tokens) return {};
16
- return Object.fromEntries(
17
- Object.entries(tokens).filter(([, value]) => value != null)
18
- );
19
- }
20
- function applyCompanyTheme(root, companyTheme, mode) {
21
- const tokens = companyTheme ? mode === "dark" ? { ...companyTheme.light, ...definedTokens(companyTheme.dark) } : companyTheme.light : {};
22
- for (const key of Object.keys(VAR_MAP)) {
23
- const value = tokens[key];
24
- if (value) root.style.setProperty(VAR_MAP[key], value);
25
- else root.style.removeProperty(VAR_MAP[key]);
26
- }
5
+ // src/lib/utils.ts
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
27
10
  }
28
11
 
29
- // src/components/theme-provider.tsx
12
+ // src/components/sidebar-nav.tsx
13
+ import { Fragment } from "react";
14
+
15
+ // src/components/types.tsx
30
16
  import { jsx } from "react/jsx-runtime";
31
- var initialState = {
32
- theme: "system",
33
- setTheme: () => null
34
- };
35
- var ThemeProviderContext = createContext(initialState);
36
- function ThemeProvider({
37
- children,
38
- defaultTheme = "system",
39
- storageKey = "ui-theme",
40
- companyTheme,
41
- ...props
42
- }) {
43
- const [theme, setTheme] = useState(
44
- () => localStorage.getItem(storageKey) || defaultTheme
45
- );
46
- useEffect(() => {
47
- const root = window.document.documentElement;
48
- const applyResolved = (resolved) => {
49
- root.classList.remove("light", "dark");
50
- root.classList.add(resolved);
51
- applyCompanyTheme(root, companyTheme, resolved);
52
- };
53
- if (theme === "system") {
54
- const mql = window.matchMedia("(prefers-color-scheme: dark)");
55
- applyResolved(mql.matches ? "dark" : "light");
56
- const onChange = (e) => applyResolved(e.matches ? "dark" : "light");
57
- mql.addEventListener("change", onChange);
58
- return () => mql.removeEventListener("change", onChange);
59
- }
60
- applyResolved(theme);
61
- }, [theme, companyTheme]);
62
- const value = {
63
- theme,
64
- setTheme: (theme2) => {
65
- localStorage.setItem(storageKey, theme2);
66
- setTheme(theme2);
67
- }
68
- };
69
- return /* @__PURE__ */ jsx(ThemeProviderContext.Provider, { ...props, value, children });
17
+ var defaultRenderLink = ({ children, ...props }) => /* @__PURE__ */ jsx("a", { ...props, children });
18
+
19
+ // src/components/sidebar-nav.tsx
20
+ import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
21
+ function defaultIsActive(item, activePath) {
22
+ if (item.href === "/") return activePath === "/";
23
+ return activePath === item.href || activePath.startsWith(item.href + "/");
70
24
  }
71
- function useTheme() {
72
- const context = useContext(ThemeProviderContext);
73
- if (context === void 0) {
74
- throw new Error("useTheme must be used within a ThemeProvider");
75
- }
76
- return context;
25
+ function SidebarNav({
26
+ items,
27
+ activePath,
28
+ isActive = defaultIsActive,
29
+ isAdmin = false,
30
+ isCollapsed = false,
31
+ renderLink = defaultRenderLink,
32
+ onNavigate
33
+ }) {
34
+ const visible = items.filter((item) => !item.adminOnly || isAdmin);
35
+ return /* @__PURE__ */ jsx2("nav", { className: "flex flex-col gap-1", children: visible.map((item) => {
36
+ const active = isActive(item, activePath);
37
+ const link = renderLink({
38
+ href: item.href,
39
+ onClick: onNavigate,
40
+ title: isCollapsed ? item.label : void 0,
41
+ "aria-current": active ? "page" : void 0,
42
+ className: cn(
43
+ "flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
44
+ active ? "bg-accent text-accent-foreground font-semibold" : "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
45
+ isCollapsed && "justify-center px-2"
46
+ ),
47
+ children: /* @__PURE__ */ jsxs(Fragment2, { children: [
48
+ item.icon && /* @__PURE__ */ jsx2("span", { className: "shrink-0", children: item.icon }),
49
+ !isCollapsed && /* @__PURE__ */ jsx2("span", { className: "truncate", children: item.label })
50
+ ] })
51
+ });
52
+ return /* @__PURE__ */ jsx2(Fragment, { children: link }, item.href);
53
+ }) });
77
54
  }
78
55
 
79
- // src/hooks/use-company-subdomain.ts
56
+ // src/components/user-menu.tsx
57
+ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
58
+ import { LogOut, User } from "lucide-react";
80
59
  import { useState as useState2 } from "react";
81
- function getSubdomain(hostname) {
82
- const labels = hostname.split(".");
83
- if (labels.length < 2) return void 0;
84
- const first = labels[0];
85
- if (!first || first === "www" || first === "localhost") return void 0;
86
- return first;
60
+ import { Drawer as DrawerPrimitive } from "vaul";
61
+
62
+ // src/hooks/use-is-desktop.ts
63
+ import * as React from "react";
64
+ var DESKTOP_QUERY = "(min-width: 640px)";
65
+ function useIsDesktop() {
66
+ const [isDesktop, setIsDesktop] = React.useState(
67
+ () => typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
68
+ );
69
+ React.useEffect(() => {
70
+ const mql = window.matchMedia(DESKTOP_QUERY);
71
+ const onChange = () => setIsDesktop(mql.matches);
72
+ onChange();
73
+ mql.addEventListener("change", onChange);
74
+ return () => mql.removeEventListener("change", onChange);
75
+ }, []);
76
+ return isDesktop;
87
77
  }
88
- function resolveOverride(storageKey) {
89
- if (typeof window === "undefined") return void 0;
90
- const params = new URLSearchParams(window.location.search);
91
- if (params.has("company")) {
92
- const value = params.get("company");
93
- if (value) {
94
- window.localStorage.setItem(storageKey, value);
95
- return value;
96
- }
97
- window.localStorage.removeItem(storageKey);
98
- return void 0;
99
- }
100
- return window.localStorage.getItem(storageKey) ?? void 0;
78
+
79
+ // src/components/user-menu.tsx
80
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
81
+ function initials(user) {
82
+ const source = user?.name?.trim() || user?.email?.trim();
83
+ if (!source) return "";
84
+ const parts = source.split(/\s+/);
85
+ if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
86
+ return source.slice(0, 2).toUpperCase();
101
87
  }
102
- function useCompanySubdomain(opts = {}) {
103
- const { enableOverride = false, overrideStorageKey = "company-subdomain" } = opts;
104
- const [subdomain] = useState2(() => {
105
- if (enableOverride) {
106
- const override = resolveOverride(overrideStorageKey);
107
- if (override) return override;
88
+ var itemClass = "flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent";
89
+ var sheetItemClass = "flex w-full cursor-pointer select-none items-center gap-3 rounded-md px-3 py-3 text-base outline-none transition-colors hover:bg-accent active:bg-accent";
90
+ function UserMenu({
91
+ user,
92
+ items = [],
93
+ onLogout,
94
+ logoutLabel = "Sair",
95
+ renderLink = defaultRenderLink,
96
+ align = "end"
97
+ }) {
98
+ const isDesktop = useIsDesktop();
99
+ const [open, setOpen] = useState2(false);
100
+ const label = initials(user);
101
+ const trigger = /* @__PURE__ */ jsx3(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ className: "inline-flex h-9 w-9 items-center justify-center overflow-hidden rounded-full border border-input bg-background text-xs font-semibold shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
106
+ "aria-label": "Menu do usu\xE1rio",
107
+ "data-testid": "user-menu",
108
+ children: user?.avatarUrl ? /* @__PURE__ */ jsx3(
109
+ "img",
110
+ {
111
+ src: user.avatarUrl,
112
+ alt: user.name ?? user.email ?? "Avatar",
113
+ className: "h-full w-full object-cover"
114
+ }
115
+ ) : label ? /* @__PURE__ */ jsx3("span", { children: label }) : /* @__PURE__ */ jsx3(User, { className: "h-4 w-4" })
108
116
  }
109
- if (typeof window === "undefined") return void 0;
110
- return getSubdomain(window.location.hostname);
111
- });
112
- return { subdomain };
113
- }
114
-
115
- // src/components/mode-toggle.tsx
116
- import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
117
- import { Moon, Sun, Monitor } from "lucide-react";
118
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
119
- function ModeToggle() {
120
- const { setTheme } = useTheme();
121
- return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
122
- /* @__PURE__ */ jsx2(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
123
- "button",
124
- {
125
- type: "button",
126
- className: "relative inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
127
- "aria-label": "Alternar tema",
128
- children: [
129
- /* @__PURE__ */ jsx2(Sun, { className: "h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }),
130
- /* @__PURE__ */ jsx2(Moon, { className: "absolute inset-0 m-auto h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" })
131
- ]
132
- }
133
- ) }),
134
- /* @__PURE__ */ jsx2(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(
135
- DropdownMenu.Content,
136
- {
137
- align: "end",
138
- className: "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
139
- children: [
140
- /* @__PURE__ */ jsxs(
141
- DropdownMenu.Item,
117
+ );
118
+ const userInfo = (user?.name || user?.email) && /* @__PURE__ */ jsxs2("div", { className: "px-2 py-1.5", children: [
119
+ user.name && /* @__PURE__ */ jsx3("p", { className: "truncate text-sm font-medium", children: user.name }),
120
+ user.email && /* @__PURE__ */ jsx3("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
121
+ ] });
122
+ if (isDesktop) {
123
+ return /* @__PURE__ */ jsxs2(DropdownMenu.Root, { open, onOpenChange: setOpen, children: [
124
+ /* @__PURE__ */ jsx3(DropdownMenu.Trigger, { asChild: true, children: trigger }),
125
+ /* @__PURE__ */ jsx3(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs2(
126
+ DropdownMenu.Content,
127
+ {
128
+ align,
129
+ sideOffset: 6,
130
+ className: "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
131
+ children: [
132
+ userInfo && /* @__PURE__ */ jsxs2(Fragment3, { children: [
133
+ userInfo,
134
+ /* @__PURE__ */ jsx3(DropdownMenu.Separator, { className: "-mx-1 my-1 h-px bg-border" })
135
+ ] }),
136
+ items.map(
137
+ (item) => item.href ? /* @__PURE__ */ jsx3(DropdownMenu.Item, { asChild: true, children: renderLink({
138
+ href: item.href,
139
+ className: itemClass,
140
+ children: /* @__PURE__ */ jsxs2(Fragment3, { children: [
141
+ item.icon,
142
+ item.label
143
+ ] })
144
+ }) }, item.label) : /* @__PURE__ */ jsxs2(
145
+ DropdownMenu.Item,
146
+ {
147
+ className: itemClass,
148
+ onClick: item.onClick,
149
+ children: [
150
+ item.icon,
151
+ item.label
152
+ ]
153
+ },
154
+ item.label
155
+ )
156
+ ),
157
+ onLogout && /* @__PURE__ */ jsxs2(Fragment3, { children: [
158
+ items.length > 0 && /* @__PURE__ */ jsx3(DropdownMenu.Separator, { className: "-mx-1 my-1 h-px bg-border" }),
159
+ /* @__PURE__ */ jsxs2(
160
+ DropdownMenu.Item,
161
+ {
162
+ className: cn(
163
+ itemClass,
164
+ "text-destructive focus:bg-destructive/10"
165
+ ),
166
+ onClick: onLogout,
167
+ children: [
168
+ /* @__PURE__ */ jsx3(LogOut, { className: "h-4 w-4" }),
169
+ logoutLabel
170
+ ]
171
+ }
172
+ )
173
+ ] })
174
+ ]
175
+ }
176
+ ) })
177
+ ] });
178
+ }
179
+ return /* @__PURE__ */ jsxs2(DrawerPrimitive.Root, { open, onOpenChange: setOpen, children: [
180
+ /* @__PURE__ */ jsx3(DrawerPrimitive.Trigger, { asChild: true, children: trigger }),
181
+ /* @__PURE__ */ jsxs2(DrawerPrimitive.Portal, { children: [
182
+ /* @__PURE__ */ jsx3(DrawerPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
183
+ /* @__PURE__ */ jsxs2(DrawerPrimitive.Content, { className: "fixed inset-x-0 bottom-0 z-50 flex flex-col rounded-t-2xl border bg-popover p-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-none", children: [
184
+ /* @__PURE__ */ jsx3(DrawerPrimitive.Title, { className: "sr-only", children: "Menu do usu\xE1rio" }),
185
+ /* @__PURE__ */ jsx3("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
186
+ userInfo && /* @__PURE__ */ jsxs2(Fragment3, { children: [
187
+ userInfo,
188
+ /* @__PURE__ */ jsx3("div", { className: "-mx-1 my-1 h-px bg-border" })
189
+ ] }),
190
+ items.map(
191
+ (item) => item.href ? /* @__PURE__ */ jsx3(
192
+ "span",
142
193
  {
143
- className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
144
- onClick: () => setTheme("light"),
145
- children: [
146
- /* @__PURE__ */ jsx2(Sun, { className: "h-4 w-4" }),
147
- " Claro"
148
- ]
149
- }
150
- ),
151
- /* @__PURE__ */ jsxs(
152
- DropdownMenu.Item,
194
+ onClick: () => setOpen(false),
195
+ onKeyDown: () => setOpen(false),
196
+ children: renderLink({
197
+ href: item.href,
198
+ className: sheetItemClass,
199
+ children: /* @__PURE__ */ jsxs2(Fragment3, { children: [
200
+ item.icon,
201
+ item.label
202
+ ] })
203
+ })
204
+ },
205
+ item.label
206
+ ) : /* @__PURE__ */ jsxs2(
207
+ "button",
153
208
  {
154
- className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
155
- onClick: () => setTheme("dark"),
209
+ type: "button",
210
+ className: sheetItemClass,
211
+ onClick: () => {
212
+ item.onClick?.();
213
+ setOpen(false);
214
+ },
156
215
  children: [
157
- /* @__PURE__ */ jsx2(Moon, { className: "h-4 w-4" }),
158
- " Escuro"
216
+ item.icon,
217
+ item.label
159
218
  ]
160
- }
161
- ),
162
- /* @__PURE__ */ jsxs(
163
- DropdownMenu.Item,
219
+ },
220
+ item.label
221
+ )
222
+ ),
223
+ onLogout && /* @__PURE__ */ jsxs2(Fragment3, { children: [
224
+ items.length > 0 && /* @__PURE__ */ jsx3("div", { className: "-mx-1 my-1 h-px bg-border" }),
225
+ /* @__PURE__ */ jsxs2(
226
+ "button",
164
227
  {
165
- className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
166
- onClick: () => setTheme("system"),
228
+ type: "button",
229
+ className: cn(sheetItemClass, "text-destructive"),
230
+ onClick: () => {
231
+ onLogout();
232
+ setOpen(false);
233
+ },
167
234
  children: [
168
- /* @__PURE__ */ jsx2(Monitor, { className: "h-4 w-4" }),
169
- " Sistema"
235
+ /* @__PURE__ */ jsx3(LogOut, { className: "h-4 w-4" }),
236
+ logoutLabel
170
237
  ]
171
238
  }
172
239
  )
173
- ]
174
- }
175
- ) })
176
- ] });
177
- }
178
-
179
- // src/components/empty-state.tsx
180
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
181
- function EmptyState({ icon, title, description, action, className }) {
182
- return /* @__PURE__ */ jsxs2("div", { className: `flex flex-col items-center justify-center py-16 text-center ${className ?? ""}`, children: [
183
- icon && /* @__PURE__ */ jsx3("div", { className: "mb-4 text-muted-foreground", children: icon }),
184
- /* @__PURE__ */ jsx3("h3", { className: "text-lg font-semibold tracking-tight", children: title }),
185
- description && /* @__PURE__ */ jsx3("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: description }),
186
- action && /* @__PURE__ */ jsx3("div", { className: "mt-4", children: action })
187
- ] });
188
- }
189
-
190
- // src/components/confirm-dialog.tsx
191
- import * as AlertDialog from "@radix-ui/react-alert-dialog";
192
-
193
- // src/lib/utils.ts
194
- import { clsx } from "clsx";
195
- import { twMerge } from "tailwind-merge";
196
- function cn(...inputs) {
197
- return twMerge(clsx(inputs));
198
- }
199
-
200
- // src/components/confirm-dialog.tsx
201
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
202
- function ConfirmDialog({
203
- open,
204
- onOpenChange,
205
- onConfirm,
206
- title,
207
- description,
208
- confirmLabel = "Confirmar",
209
- cancelLabel = "Cancelar",
210
- loading = false,
211
- variant = "default",
212
- blurBackdrop = true,
213
- backdropClassName
214
- }) {
215
- return /* @__PURE__ */ jsx4(AlertDialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs3(AlertDialog.Portal, { children: [
216
- /* @__PURE__ */ jsx4(
217
- AlertDialog.Overlay,
218
- {
219
- className: cn(
220
- "fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
221
- blurBackdrop && "backdrop-blur-sm",
222
- backdropClassName
223
- )
224
- }
225
- ),
226
- /* @__PURE__ */ jsxs3(AlertDialog.Content, { className: "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", children: [
227
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-col space-y-2 text-center sm:text-left", children: [
228
- /* @__PURE__ */ jsx4(AlertDialog.Title, { className: "text-lg font-semibold", children: title }),
229
- /* @__PURE__ */ jsx4(AlertDialog.Description, { className: "text-sm text-muted-foreground", children: description })
230
- ] }),
231
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [
232
- /* @__PURE__ */ jsx4(AlertDialog.Cancel, { className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer", children: cancelLabel }),
233
- /* @__PURE__ */ jsx4(
234
- AlertDialog.Action,
235
- {
236
- onClick: onConfirm,
237
- disabled: loading,
238
- className: `inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer ${variant === "destructive" ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "bg-primary text-primary-foreground hover:bg-primary/90"}`,
239
- children: loading ? "Aguarde..." : confirmLabel
240
- }
241
- )
240
+ ] })
242
241
  ] })
243
242
  ] })
244
- ] }) });
245
- }
246
-
247
- // src/components/page-header.tsx
248
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
249
- function PageHeader({ title, description, action, className }) {
250
- return /* @__PURE__ */ jsxs4(
251
- "div",
252
- {
253
- className: `flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between ${className ?? ""}`,
254
- children: [
255
- /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
256
- /* @__PURE__ */ jsx5("h1", { className: "truncate text-2xl font-semibold tracking-tight", children: title }),
257
- description && /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: description })
258
- ] }),
259
- action && /* @__PURE__ */ jsx5("div", { className: "shrink-0", children: action })
260
- ]
261
- }
262
- );
263
- }
264
-
265
- // src/components/status-dot.tsx
266
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
267
- function StatusDot({ active, label, className }) {
268
- return /* @__PURE__ */ jsxs5("span", { className: `inline-flex items-center gap-2 ${className ?? ""}`, children: [
269
- /* @__PURE__ */ jsx6(
270
- "span",
271
- {
272
- className: `h-2 w-2 rounded-full ${active ? "bg-green-500" : "bg-red-500"}`
273
- }
274
- ),
275
- label && /* @__PURE__ */ jsx6("span", { children: label })
276
243
  ] });
277
244
  }
278
245
 
279
- // src/components/status-badge.tsx
280
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
281
- var variantClasses = {
282
- neutral: {
283
- badge: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300",
284
- dot: "bg-gray-400"
285
- },
286
- info: {
287
- badge: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-400",
288
- dot: "bg-blue-500"
289
- },
290
- success: {
291
- badge: "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-400",
292
- dot: "bg-green-500"
293
- },
294
- danger: {
295
- badge: "bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-400",
296
- dot: "bg-red-500"
297
- },
298
- warning: {
299
- badge: "bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-400",
300
- dot: "bg-amber-500"
301
- }
302
- };
303
- function StatusBadge({
304
- variant = "neutral",
305
- dot = true,
306
- className,
246
+ // src/components/app-shell.tsx
247
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
248
+ function AppShell({
249
+ brand,
250
+ brandCollapsed,
251
+ navItems,
252
+ activePath,
253
+ isActive,
254
+ isAdmin = false,
255
+ user,
256
+ userMenuItems,
257
+ onLogout,
258
+ renderLink = defaultRenderLink,
259
+ headerActions,
260
+ collapsible = true,
261
+ defaultCollapsed = false,
307
262
  children
308
263
  }) {
309
- const classes = variantClasses[variant];
310
- return /* @__PURE__ */ jsxs6(
311
- "span",
264
+ const [collapsed, setCollapsed] = useState3(defaultCollapsed);
265
+ const [mobileOpen, setMobileOpen] = useState3(false);
266
+ const nav = (isCollapsed, onNavigate) => /* @__PURE__ */ jsx4(
267
+ SidebarNav,
312
268
  {
313
- className: `inline-flex items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium ${classes.badge} ${className ?? ""}`,
314
- children: [
315
- dot && /* @__PURE__ */ jsx7(
316
- "span",
317
- {
318
- className: `inline-block h-1.5 w-1.5 shrink-0 rounded-full ${classes.dot}`
319
- }
320
- ),
321
- children
322
- ]
269
+ items: navItems,
270
+ activePath,
271
+ isActive,
272
+ isAdmin,
273
+ isCollapsed,
274
+ renderLink,
275
+ onNavigate
323
276
  }
324
277
  );
325
- }
326
-
327
- // src/components/progress-bar.tsx
328
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
329
- var sizeClasses = {
330
- sm: "h-1.5",
331
- md: "h-2"
332
- };
333
- function ProgressBar({
334
- value,
335
- size = "md",
336
- label,
337
- valueLabel,
338
- className,
339
- indicatorClassName
340
- }) {
341
- const clamped = Math.min(100, Math.max(0, value));
342
- return /* @__PURE__ */ jsxs7("div", { className: `space-y-1.5 ${className ?? ""}`, children: [
343
- (label || valueLabel) && /* @__PURE__ */ jsxs7("div", { className: "flex items-baseline justify-between gap-2", children: [
344
- label && /* @__PURE__ */ jsx8("span", { className: "text-sm text-muted-foreground", children: label }),
345
- valueLabel && /* @__PURE__ */ jsx8("span", { className: "text-sm font-semibold", children: valueLabel })
346
- ] }),
347
- /* @__PURE__ */ jsx8(
348
- "div",
278
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-screen overflow-hidden bg-background", children: [
279
+ /* @__PURE__ */ jsxs3(
280
+ "aside",
349
281
  {
350
- className: `w-full overflow-hidden rounded-full bg-muted ${sizeClasses[size]}`,
351
- children: /* @__PURE__ */ jsx8(
352
- "div",
353
- {
354
- className: `h-full rounded-full transition-all duration-300 ${indicatorClassName ?? "bg-primary"}`,
355
- style: { width: `${clamped}%` }
356
- }
357
- )
282
+ className: cn(
283
+ "hidden shrink-0 flex-col border-r bg-card transition-all duration-300 md:flex",
284
+ collapsed ? "w-16" : "w-60"
285
+ ),
286
+ children: [
287
+ /* @__PURE__ */ jsx4("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: collapsed ? brandCollapsed ?? brand : brand }),
288
+ /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(collapsed) }),
289
+ collapsible && /* @__PURE__ */ jsx4("div", { className: "border-t p-2", children: /* @__PURE__ */ jsx4(
290
+ "button",
291
+ {
292
+ type: "button",
293
+ onClick: () => setCollapsed((v) => !v),
294
+ className: "flex w-full items-center justify-center rounded-md px-2 py-2 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
295
+ "aria-label": collapsed ? "Expandir menu" : "Recolher menu",
296
+ children: collapsed ? /* @__PURE__ */ jsx4(ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx4(ChevronLeft, { className: "h-4 w-4" })
297
+ }
298
+ ) })
299
+ ]
358
300
  }
359
- )
360
- ] });
361
- }
362
-
363
- // src/components/form-field.tsx
364
- import { cloneElement, isValidElement, useId } from "react";
365
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
366
- function FormField({ label, htmlFor, error, required, children, className }) {
367
- const errorId = useId();
368
- const field = error && isValidElement(children) ? cloneElement(children, {
369
- "aria-invalid": true,
370
- "aria-describedby": [
371
- children.props["aria-describedby"],
372
- errorId
373
- ].filter(Boolean).join(" ")
374
- }) : children;
375
- return /* @__PURE__ */ jsxs8("div", { className: `space-y-2 ${className ?? ""}`, children: [
376
- /* @__PURE__ */ jsxs8(
377
- "label",
301
+ ),
302
+ /* @__PURE__ */ jsxs3(
303
+ "div",
378
304
  {
379
- htmlFor,
380
- className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
305
+ className: cn(
306
+ "fixed inset-0 z-50 transition-opacity duration-300 md:hidden",
307
+ mobileOpen ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
308
+ ),
309
+ onClick: () => setMobileOpen(false),
381
310
  children: [
382
- label,
383
- required && /* @__PURE__ */ jsx9("span", { className: "ml-1 text-destructive", children: "*" })
311
+ /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 bg-black/50" }),
312
+ /* @__PURE__ */ jsxs3(
313
+ "div",
314
+ {
315
+ className: cn(
316
+ "absolute left-0 top-0 flex h-full w-64 flex-col border-r bg-card transition-transform duration-300",
317
+ mobileOpen ? "translate-x-0" : "-translate-x-full"
318
+ ),
319
+ onClick: (e) => e.stopPropagation(),
320
+ children: [
321
+ /* @__PURE__ */ jsx4("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: brand }),
322
+ /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(false, () => setMobileOpen(false)) })
323
+ ]
324
+ }
325
+ )
384
326
  ]
385
327
  }
386
328
  ),
387
- field,
388
- error && /* @__PURE__ */ jsx9("p", { id: errorId, className: "text-sm text-destructive", children: error })
329
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
330
+ /* @__PURE__ */ jsxs3("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b bg-background px-4", children: [
331
+ /* @__PURE__ */ jsx4(
332
+ "button",
333
+ {
334
+ type: "button",
335
+ onClick: () => setMobileOpen(true),
336
+ className: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground md:hidden",
337
+ "aria-label": "Abrir menu",
338
+ children: /* @__PURE__ */ jsx4(Menu, { className: "h-4 w-4" })
339
+ }
340
+ ),
341
+ /* @__PURE__ */ jsxs3("div", { className: "ml-auto flex items-center gap-2", children: [
342
+ headerActions,
343
+ (user || onLogout) && /* @__PURE__ */ jsx4(UserMenu, { user, items: userMenuItems, onLogout, renderLink })
344
+ ] })
345
+ ] }),
346
+ /* @__PURE__ */ jsx4("main", { className: "flex-1 overflow-y-auto", children })
347
+ ] })
389
348
  ] });
390
349
  }
391
350
 
351
+ // src/components/auth-card.tsx
352
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
353
+ function AuthCard({ title, description, children, footer }) {
354
+ return /* @__PURE__ */ jsx5("div", { className: "flex min-h-screen items-center justify-center px-4", children: /* @__PURE__ */ jsxs4("div", { className: "w-full max-w-sm rounded-lg border bg-card p-6 shadow-sm", children: [
355
+ /* @__PURE__ */ jsxs4("div", { className: "mb-6 text-center", children: [
356
+ /* @__PURE__ */ jsx5("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
357
+ description && /* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
358
+ ] }),
359
+ /* @__PURE__ */ jsx5("div", { className: "space-y-4", children }),
360
+ footer && /* @__PURE__ */ jsx5("div", { className: "mt-4", children: footer })
361
+ ] }) });
362
+ }
363
+
392
364
  // src/components/base-dialog.tsx
393
365
  import * as Dialog from "@radix-ui/react-dialog";
394
366
  import { X } from "lucide-react";
395
367
  import * as React2 from "react";
396
-
397
- // src/hooks/use-is-desktop.ts
398
- import * as React from "react";
399
- var DESKTOP_QUERY = "(min-width: 640px)";
400
- function useIsDesktop() {
401
- const [isDesktop, setIsDesktop] = React.useState(
402
- () => typeof window !== "undefined" ? window.matchMedia(DESKTOP_QUERY).matches : true
403
- );
404
- React.useEffect(() => {
405
- const mql = window.matchMedia(DESKTOP_QUERY);
406
- const onChange = () => setIsDesktop(mql.matches);
407
- onChange();
408
- mql.addEventListener("change", onChange);
409
- return () => mql.removeEventListener("change", onChange);
410
- }, []);
411
- return isDesktop;
412
- }
413
-
414
- // src/components/base-dialog.tsx
415
- import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
416
- var sizeClasses2 = {
368
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
369
+ var sizeClasses = {
417
370
  sm: "sm:max-w-sm",
418
371
  md: "sm:max-w-md",
419
372
  lg: "sm:max-w-lg",
@@ -449,27 +402,27 @@ function BaseDialog({
449
402
  }, 0);
450
403
  return () => window.clearTimeout(id);
451
404
  }, [open]);
452
- const body = /* @__PURE__ */ jsxs9(Fragment, { children: [
453
- /* @__PURE__ */ jsxs9("div", { className: "flex shrink-0 items-start justify-between gap-4 border-b px-6 py-3", children: [
454
- /* @__PURE__ */ jsxs9("div", { className: "min-w-0 space-y-1", children: [
455
- /* @__PURE__ */ jsx10(Dialog.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: title }),
456
- description && /* @__PURE__ */ jsx10(Dialog.Description, { className: "text-sm text-muted-foreground", children: description })
405
+ const body = /* @__PURE__ */ jsxs5(Fragment4, { children: [
406
+ /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-start justify-between gap-4 border-b px-6 py-3", children: [
407
+ /* @__PURE__ */ jsxs5("div", { className: "min-w-0 space-y-1", children: [
408
+ /* @__PURE__ */ jsx6(Dialog.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: title }),
409
+ description && /* @__PURE__ */ jsx6(Dialog.Description, { className: "text-sm text-muted-foreground", children: description })
457
410
  ] }),
458
- /* @__PURE__ */ jsx10(
411
+ /* @__PURE__ */ jsx6(
459
412
  Dialog.Close,
460
413
  {
461
414
  className: "-mr-1 shrink-0 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
462
415
  "aria-label": "Fechar",
463
- children: /* @__PURE__ */ jsx10(X, { className: "h-5 w-5" })
416
+ children: /* @__PURE__ */ jsx6(X, { className: "h-5 w-5" })
464
417
  }
465
418
  )
466
419
  ] }),
467
- /* @__PURE__ */ jsx10("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children }),
468
- footer && /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 flex-col-reverse gap-2 border-t px-6 py-3 sm:flex-row sm:justify-end", children: footer })
420
+ /* @__PURE__ */ jsx6("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children }),
421
+ footer && /* @__PURE__ */ jsx6("div", { className: "flex shrink-0 flex-col-reverse gap-2 border-t px-6 py-3 sm:flex-row sm:justify-end", children: footer })
469
422
  ] });
470
423
  const isModal = modal ?? !isDesktop;
471
- return /* @__PURE__ */ jsx10(Dialog.Root, { open, onOpenChange, modal: isModal, children: /* @__PURE__ */ jsxs9(Dialog.Portal, { children: [
472
- isModal ? /* @__PURE__ */ jsx10(
424
+ return /* @__PURE__ */ jsx6(Dialog.Root, { open, onOpenChange, modal: isModal, children: /* @__PURE__ */ jsxs5(Dialog.Portal, { children: [
425
+ isModal ? /* @__PURE__ */ jsx6(
473
426
  Dialog.Overlay,
474
427
  {
475
428
  className: cn(
@@ -482,7 +435,7 @@ function BaseDialog({
482
435
  // Radix skips Dialog.Overlay when non-modal, so the backdrop is
483
436
  // rendered by hand; pointer-events-none keeps outside interactions
484
437
  // working (the reason the dialog is non-modal on desktop).
485
- (blurBackdrop || backdropClassName) && /* @__PURE__ */ jsx10(
438
+ (blurBackdrop || backdropClassName) && /* @__PURE__ */ jsx6(
486
439
  "div",
487
440
  {
488
441
  "aria-hidden": true,
@@ -494,250 +447,157 @@ function BaseDialog({
494
447
  }
495
448
  )
496
449
  ),
497
- /* @__PURE__ */ jsx10(
450
+ /* @__PURE__ */ jsx6(
498
451
  Dialog.Content,
499
452
  {
500
453
  onCloseAutoFocus: () => {
501
- document.body.style.pointerEvents = "";
502
- },
503
- className: cn(
504
- "fixed z-50 flex max-h-[92vh] flex-col bg-background shadow-lg outline-none",
505
- "inset-x-0 bottom-0 rounded-t-2xl",
506
- "sm:inset-x-auto sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:max-h-[92vh] sm:w-full sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-lg",
507
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
508
- sizeClasses2[size],
509
- className
510
- ),
511
- children: contentWrapper(body)
512
- }
513
- )
514
- ] }) });
515
- }
516
-
517
- // src/components/form-dialog.tsx
518
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
519
- function FormDialogLayout({
520
- title,
521
- children,
522
- onSubmit,
523
- submitLabel = "Salvar",
524
- cancelLabel = "Cancelar",
525
- onCancel,
526
- isSubmitting = false,
527
- isDisabled = false
528
- }) {
529
- return /* @__PURE__ */ jsxs10("form", { onSubmit, children: [
530
- /* @__PURE__ */ jsx11("div", { className: "flex flex-col space-y-1.5 text-center sm:text-left", children: /* @__PURE__ */ jsx11("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: title }) }),
531
- /* @__PURE__ */ jsx11("div", { className: "space-y-4 py-4", children }),
532
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [
533
- /* @__PURE__ */ jsx11(
534
- "button",
535
- {
536
- type: "button",
537
- onClick: onCancel,
538
- className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
539
- children: cancelLabel
540
- }
541
- ),
542
- /* @__PURE__ */ jsx11(
543
- "button",
544
- {
545
- type: "submit",
546
- disabled: isSubmitting || isDisabled,
547
- className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 cursor-pointer disabled:pointer-events-none disabled:opacity-50",
548
- children: isSubmitting ? "Salvando..." : submitLabel
549
- }
550
- )
551
- ] })
552
- ] });
553
- }
554
- function FormDialog({ onSubmit, ...rest }) {
555
- return /* @__PURE__ */ jsx11(
556
- BaseDialog,
557
- {
558
- ...rest,
559
- contentWrapper: onSubmit ? (content) => /* @__PURE__ */ jsx11("form", { onSubmit, className: "flex min-h-0 flex-1 flex-col", children: content }) : void 0
560
- }
561
- );
562
- }
563
-
564
- // src/components/auth-card.tsx
565
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
566
- function AuthCard({ title, description, children, footer }) {
567
- return /* @__PURE__ */ jsx12("div", { className: "flex min-h-screen items-center justify-center px-4", children: /* @__PURE__ */ jsxs11("div", { className: "w-full max-w-sm rounded-lg border bg-card p-6 shadow-sm", children: [
568
- /* @__PURE__ */ jsxs11("div", { className: "mb-6 text-center", children: [
569
- /* @__PURE__ */ jsx12("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
570
- description && /* @__PURE__ */ jsx12("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
571
- ] }),
572
- /* @__PURE__ */ jsx12("div", { className: "space-y-4", children }),
573
- footer && /* @__PURE__ */ jsx12("div", { className: "mt-4", children: footer })
574
- ] }) });
575
- }
576
-
577
- // src/components/pagination.tsx
578
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
579
- var navButtonClass = "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50";
580
- function Pagination({
581
- page,
582
- onPageChange,
583
- hasNextPage,
584
- hasPreviousPage,
585
- totalPages,
586
- showPageSizeSelector = false,
587
- pageSize,
588
- onPageSizeChange,
589
- pageSizeOptions,
590
- className
591
- }) {
592
- return /* @__PURE__ */ jsxs12(
593
- "div",
594
- {
595
- className: `flex items-center ${showPageSizeSelector ? "justify-between" : "justify-end"} gap-4 ${className ?? ""}`,
596
- children: [
597
- showPageSizeSelector && /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
598
- /* @__PURE__ */ jsx13("label", { htmlFor: "pagination-page-size", className: "text-sm text-muted-foreground", children: "Itens por p\xE1gina" }),
599
- /* @__PURE__ */ jsx13(
600
- "select",
601
- {
602
- id: "pagination-page-size",
603
- value: pageSize,
604
- onChange: (e) => onPageSizeChange?.(Number(e.target.value)),
605
- className: "h-8 rounded-md border border-input bg-background px-2 text-xs font-medium shadow-sm transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer",
606
- children: (pageSizeOptions ?? []).map((size) => /* @__PURE__ */ jsx13("option", { value: size, children: size }, size))
607
- }
608
- )
609
- ] }),
610
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-4", children: [
611
- /* @__PURE__ */ jsx13(
612
- "button",
613
- {
614
- type: "button",
615
- onClick: () => onPageChange(page - 1),
616
- disabled: !hasPreviousPage,
617
- className: navButtonClass,
618
- children: "Anterior"
619
- }
620
- ),
621
- /* @__PURE__ */ jsxs12("span", { className: "text-sm text-muted-foreground", children: [
622
- "P\xE1gina ",
623
- page,
624
- totalPages ? ` de ${totalPages}` : ""
625
- ] }),
626
- /* @__PURE__ */ jsx13(
627
- "button",
628
- {
629
- type: "button",
630
- onClick: () => onPageChange(page + 1),
631
- disabled: !hasNextPage,
632
- className: navButtonClass,
633
- children: "Pr\xF3ximo"
634
- }
635
- )
636
- ] })
637
- ]
638
- }
639
- );
640
- }
641
-
642
- // src/components/table-skeleton.tsx
643
- import { Fragment as Fragment2, jsx as jsx14 } from "react/jsx-runtime";
644
- function TableSkeleton({ rows = 5, columns = 4 }) {
645
- return /* @__PURE__ */ jsx14(Fragment2, { children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx14("tr", { className: "border-b transition-colors", children: Array.from({ length: columns }).map((_2, j) => /* @__PURE__ */ jsx14("td", { className: "p-4 align-middle", children: /* @__PURE__ */ jsx14("div", { className: "h-5 w-full animate-pulse rounded bg-muted" }) }, j)) }, i)) });
646
- }
647
-
648
- // src/components/search-input.tsx
649
- import * as React3 from "react";
650
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
651
- var SearchInput = React3.forwardRef(
652
- ({ containerClassName, className, ...props }, ref) => {
653
- return /* @__PURE__ */ jsxs13("div", { className: `relative flex-1 ${containerClassName ?? ""}`, children: [
654
- /* @__PURE__ */ jsxs13(
655
- "svg",
656
- {
657
- xmlns: "http://www.w3.org/2000/svg",
658
- width: "16",
659
- height: "16",
660
- viewBox: "0 0 24 24",
661
- fill: "none",
662
- stroke: "currentColor",
663
- strokeWidth: "2",
664
- strokeLinecap: "round",
665
- strokeLinejoin: "round",
666
- className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground",
667
- children: [
668
- /* @__PURE__ */ jsx15("circle", { cx: "11", cy: "11", r: "8" }),
669
- /* @__PURE__ */ jsx15("path", { d: "m21 21-4.3-4.3" })
670
- ]
671
- }
672
- ),
673
- /* @__PURE__ */ jsx15(
674
- "input",
675
- {
676
- ref,
677
- type: "text",
678
- className: `flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 pl-10 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ""}`,
679
- ...props
680
- }
681
- )
682
- ] });
683
- }
684
- );
685
- SearchInput.displayName = "SearchInput";
686
-
687
- // src/components/password-input.tsx
688
- import { Eye, EyeOff } from "lucide-react";
689
- import * as React4 from "react";
690
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
691
- var PasswordInput = React4.forwardRef(({ containerClassName, className, ...props }, ref) => {
692
- const [visible, setVisible] = React4.useState(false);
693
- return /* @__PURE__ */ jsxs14("div", { className: `relative ${containerClassName ?? ""}`, children: [
694
- /* @__PURE__ */ jsx16(
695
- "input",
696
- {
697
- ref,
698
- type: visible ? "text" : "password",
699
- className: `flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 pr-10 text-base shadow-sm transition-all duration-200 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive md:text-sm ${className ?? ""}`,
700
- ...props
454
+ document.body.style.pointerEvents = "";
455
+ },
456
+ className: cn(
457
+ "fixed z-50 flex max-h-[92vh] flex-col bg-background shadow-lg outline-none",
458
+ "inset-x-0 bottom-0 rounded-t-2xl",
459
+ "sm:inset-x-auto sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:max-h-[92vh] sm:w-full sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-lg",
460
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
461
+ sizeClasses[size],
462
+ className
463
+ ),
464
+ children: contentWrapper(body)
701
465
  }
702
- ),
703
- /* @__PURE__ */ jsx16(
466
+ )
467
+ ] }) });
468
+ }
469
+
470
+ // src/components/color-picker.tsx
471
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
472
+ import { ChevronsUpDown, X as X2 } from "lucide-react";
473
+ import * as React3 from "react";
474
+ import {
475
+ HexAlphaColorPicker,
476
+ HexColorInput,
477
+ HexColorPicker
478
+ } from "react-colorful";
479
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
480
+ function ColorPicker({
481
+ value,
482
+ onChange,
483
+ placeholder = "Selecionar cor",
484
+ clearable = true,
485
+ alpha = false,
486
+ disabled,
487
+ id,
488
+ className,
489
+ "aria-invalid": ariaInvalid,
490
+ "aria-describedby": ariaDescribedby
491
+ }) {
492
+ const [open, setOpen] = React3.useState(false);
493
+ const color = value || "";
494
+ const showClear = clearable && !!color && !disabled;
495
+ const clear = (event) => {
496
+ event.stopPropagation();
497
+ event.preventDefault();
498
+ onChange("");
499
+ };
500
+ return /* @__PURE__ */ jsxs6(PopoverPrimitive.Root, { open, onOpenChange: setOpen, children: [
501
+ /* @__PURE__ */ jsx7(PopoverPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs6(
704
502
  "button",
705
503
  {
706
504
  type: "button",
707
- onClick: () => setVisible((prev) => !prev),
708
- "aria-label": visible ? "Ocultar senha" : "Mostrar senha",
709
- tabIndex: -1,
710
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground",
711
- children: visible ? /* @__PURE__ */ jsx16(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx16(Eye, { className: "h-4 w-4" })
505
+ id,
506
+ "aria-invalid": ariaInvalid,
507
+ "aria-describedby": ariaDescribedby,
508
+ disabled,
509
+ className: cn(
510
+ "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
511
+ className
512
+ ),
513
+ children: [
514
+ /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-2 truncate", children: [
515
+ /* @__PURE__ */ jsx7(
516
+ "span",
517
+ {
518
+ "aria-hidden": true,
519
+ className: "h-5 w-5 shrink-0 rounded border border-border bg-[length:8px_8px] bg-[position:0_0,4px_4px]",
520
+ style: {
521
+ // The checkerboard stays under the color layer so a
522
+ // translucent value reads as translucent in the swatch.
523
+ backgroundImage: `${color ? `linear-gradient(${color},${color}),` : ""}linear-gradient(45deg,var(--color-muted) 25%,transparent 25%,transparent 75%,var(--color-muted) 75%),linear-gradient(45deg,var(--color-muted) 25%,transparent 25%,transparent 75%,var(--color-muted) 75%)`
524
+ }
525
+ }
526
+ ),
527
+ /* @__PURE__ */ jsx7("span", { className: cn("truncate", !color && "text-muted-foreground"), children: color || placeholder })
528
+ ] }),
529
+ /* @__PURE__ */ jsxs6("span", { className: "flex shrink-0 items-center gap-1", children: [
530
+ showClear && // Not a <button> — nesting one inside the trigger button is
531
+ // invalid HTML.
532
+ /* @__PURE__ */ jsx7(
533
+ "span",
534
+ {
535
+ role: "button",
536
+ tabIndex: 0,
537
+ "aria-label": "Limpar cor",
538
+ onClick: clear,
539
+ onKeyDown: (event) => {
540
+ if (event.key === "Enter" || event.key === " ") clear(event);
541
+ },
542
+ className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
543
+ children: /* @__PURE__ */ jsx7(X2, { className: "h-3.5 w-3.5" })
544
+ }
545
+ ),
546
+ /* @__PURE__ */ jsx7(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
547
+ ] })
548
+ ]
712
549
  }
713
- )
550
+ ) }),
551
+ /* @__PURE__ */ jsx7(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx7(
552
+ PopoverPrimitive.Content,
553
+ {
554
+ align: "start",
555
+ sideOffset: 4,
556
+ className: "z-50 w-56 rounded-md border bg-popover p-3 text-popover-foreground shadow-md",
557
+ children: /* @__PURE__ */ jsxs6("div", { className: "mh-color-picker flex flex-col gap-3", children: [
558
+ alpha ? /* @__PURE__ */ jsx7(HexAlphaColorPicker, { color, onChange }) : /* @__PURE__ */ jsx7(HexColorPicker, { color, onChange }),
559
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
560
+ /* @__PURE__ */ jsx7("span", { className: "text-sm text-muted-foreground", children: "#" }),
561
+ /* @__PURE__ */ jsx7(
562
+ HexColorInput,
563
+ {
564
+ color,
565
+ onChange,
566
+ prefixed: false,
567
+ alpha,
568
+ className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm uppercase shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
569
+ }
570
+ )
571
+ ] })
572
+ ] })
573
+ }
574
+ ) })
714
575
  ] });
715
- });
716
- PasswordInput.displayName = "PasswordInput";
576
+ }
717
577
 
718
578
  // src/components/combobox.tsx
719
- import * as PopoverPrimitive from "@radix-ui/react-popover";
579
+ import * as PopoverPrimitive2 from "@radix-ui/react-popover";
720
580
  import { Command as CommandPrimitive } from "cmdk";
721
- import { Check, ChevronsUpDown, Loader2, Plus, Search } from "lucide-react";
722
- import * as React6 from "react";
723
- import { Drawer as DrawerPrimitive } from "vaul";
581
+ import { Check, ChevronsUpDown as ChevronsUpDown2, Loader2, Plus, Search } from "lucide-react";
582
+ import * as React5 from "react";
583
+ import { Drawer as DrawerPrimitive2 } from "vaul";
724
584
 
725
585
  // src/hooks/use-infinite-scroll-sentinel.ts
726
- import * as React5 from "react";
586
+ import * as React4 from "react";
727
587
  function useInfiniteScrollSentinel({
728
588
  enabled,
729
589
  loading,
730
590
  onLoadMore
731
591
  }) {
732
- const listRef = React5.useRef(null);
733
- const sentinelRef = React5.useRef(null);
734
- const loadingRef = React5.useRef(loading);
735
- const onLoadMoreRef = React5.useRef(onLoadMore);
736
- React5.useEffect(() => {
592
+ const listRef = React4.useRef(null);
593
+ const sentinelRef = React4.useRef(null);
594
+ const loadingRef = React4.useRef(loading);
595
+ const onLoadMoreRef = React4.useRef(onLoadMore);
596
+ React4.useEffect(() => {
737
597
  loadingRef.current = loading;
738
598
  onLoadMoreRef.current = onLoadMore;
739
599
  });
740
- React5.useEffect(() => {
600
+ React4.useEffect(() => {
741
601
  if (!enabled) return;
742
602
  const root = listRef.current;
743
603
  const sentinel = sentinelRef.current;
@@ -745,190 +605,20 @@ function useInfiniteScrollSentinel({
745
605
  const observer = new IntersectionObserver(
746
606
  (entries) => {
747
607
  if (entries[0]?.isIntersecting && !loadingRef.current) {
748
- onLoadMoreRef.current?.();
749
- }
750
- },
751
- { root, rootMargin: "0px 0px 100px 0px" }
752
- );
753
- observer.observe(sentinel);
754
- return () => observer.disconnect();
755
- }, [enabled]);
756
- return { listRef, sentinelRef };
757
- }
758
-
759
- // src/components/combobox.tsx
760
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
761
- function Combobox({
762
- options,
763
- value,
764
- onChange,
765
- placeholder = "Selecione...",
766
- searchPlaceholder = "Buscar...",
767
- emptyMessage = "Nenhum resultado encontrado.",
768
- disabled,
769
- id,
770
- className,
771
- "aria-invalid": ariaInvalid,
772
- "aria-describedby": ariaDescribedby,
773
- onSearchChange,
774
- onLoadMore,
775
- loading,
776
- hasMore,
777
- selectedOption,
778
- creatable,
779
- formatCreateLabel
780
- }) {
781
- const [open, setOpen] = React6.useState(false);
782
- const [search, setSearch] = React6.useState("");
783
- const isDesktop = useIsDesktop();
784
- const isServer = onSearchChange !== void 0;
785
- const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value) ?? (creatable && value ? { value, label: value } : void 0);
786
- const handleOpenChange = (next) => {
787
- setOpen(next);
788
- if (!next) setSearch("");
789
- };
790
- const handleSelect = (next) => {
791
- onChange(next);
792
- handleOpenChange(false);
793
- };
794
- const handleSearchChange = (next) => {
795
- setSearch(next);
796
- onSearchChange?.(next);
797
- };
798
- const trimmedSearch = search.trim();
799
- const showCreate = !!creatable && trimmedSearch.length > 0 && !options.some(
800
- (option) => option.value.toLowerCase() === trimmedSearch.toLowerCase()
801
- );
802
- const { listRef, sentinelRef } = useInfiniteScrollSentinel({
803
- enabled: isServer && !!hasMore,
804
- loading,
805
- onLoadMore
806
- });
807
- const trigger = /* @__PURE__ */ jsxs15(
808
- "button",
809
- {
810
- type: "button",
811
- id,
812
- role: "combobox",
813
- "aria-expanded": open,
814
- "aria-invalid": ariaInvalid,
815
- "aria-describedby": ariaDescribedby,
816
- disabled,
817
- className: cn(
818
- "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
819
- className
820
- ),
821
- children: [
822
- /* @__PURE__ */ jsx17("span", { className: cn("truncate", !selected && "text-muted-foreground"), children: selected ? selected.label : placeholder }),
823
- /* @__PURE__ */ jsx17(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
824
- ]
825
- }
826
- );
827
- const command = /* @__PURE__ */ jsxs15(
828
- CommandPrimitive,
829
- {
830
- shouldFilter: !isServer,
831
- className: "flex h-full w-full flex-col overflow-hidden",
832
- children: [
833
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center border-b px-3", children: [
834
- /* @__PURE__ */ jsx17(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
835
- /* @__PURE__ */ jsx17(
836
- CommandPrimitive.Input,
837
- {
838
- placeholder: searchPlaceholder,
839
- onValueChange: isServer || creatable ? handleSearchChange : void 0,
840
- className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
841
- }
842
- )
843
- ] }),
844
- /* @__PURE__ */ jsxs15(
845
- CommandPrimitive.List,
846
- {
847
- ref: listRef,
848
- className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
849
- children: [
850
- !isServer && !showCreate && /* @__PURE__ */ jsx17(CommandPrimitive.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
851
- showCreate && /* @__PURE__ */ jsxs15(
852
- CommandPrimitive.Item,
853
- {
854
- value: trimmedSearch,
855
- onSelect: () => handleSelect(trimmedSearch),
856
- className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground sm:py-1.5",
857
- children: [
858
- /* @__PURE__ */ jsx17(Plus, { className: "h-4 w-4 shrink-0" }),
859
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: (formatCreateLabel ?? ((text) => `Usar "${text}"`))(
860
- trimmedSearch
861
- ) })
862
- ]
863
- }
864
- ),
865
- options.map((option) => /* @__PURE__ */ jsxs15(
866
- CommandPrimitive.Item,
867
- {
868
- value: isServer ? option.value : option.label,
869
- disabled: option.disabled,
870
- onSelect: () => handleSelect(option.value),
871
- className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
872
- children: [
873
- /* @__PURE__ */ jsx17(
874
- Check,
875
- {
876
- className: cn(
877
- "h-4 w-4 shrink-0",
878
- value === option.value ? "opacity-100" : "opacity-0"
879
- )
880
- }
881
- ),
882
- /* @__PURE__ */ jsx17("span", { className: "truncate", children: option.label })
883
- ]
884
- },
885
- option.value
886
- )),
887
- isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx17("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
888
- isServer && loading && /* @__PURE__ */ jsx17("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx17(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
889
- isServer && hasMore && /* @__PURE__ */ jsx17("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
890
- ]
891
- }
892
- )
893
- ]
894
- }
895
- );
896
- if (isDesktop) {
897
- return /* @__PURE__ */ jsxs15(PopoverPrimitive.Root, { open, onOpenChange: handleOpenChange, children: [
898
- /* @__PURE__ */ jsx17(PopoverPrimitive.Trigger, { asChild: true, children: trigger }),
899
- /* @__PURE__ */ jsx17(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx17(
900
- PopoverPrimitive.Content,
901
- {
902
- align: "start",
903
- sideOffset: 4,
904
- className: "pointer-events-auto z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
905
- style: { width: "var(--radix-popover-trigger-width)" },
906
- children: command
907
- }
908
- ) })
909
- ] });
910
- }
911
- return /* @__PURE__ */ jsxs15(DrawerPrimitive.Root, { open, onOpenChange: handleOpenChange, children: [
912
- /* @__PURE__ */ jsx17(DrawerPrimitive.Trigger, { asChild: true, children: trigger }),
913
- /* @__PURE__ */ jsxs15(DrawerPrimitive.Portal, { children: [
914
- /* @__PURE__ */ jsx17(DrawerPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
915
- /* @__PURE__ */ jsxs15(DrawerPrimitive.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
916
- /* @__PURE__ */ jsx17(DrawerPrimitive.Title, { className: "sr-only", children: placeholder }),
917
- /* @__PURE__ */ jsx17("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
918
- command
919
- ] })
920
- ] })
921
- ] });
608
+ onLoadMoreRef.current?.();
609
+ }
610
+ },
611
+ { root, rootMargin: "0px 0px 100px 0px" }
612
+ );
613
+ observer.observe(sentinel);
614
+ return () => observer.disconnect();
615
+ }, [enabled]);
616
+ return { listRef, sentinelRef };
922
617
  }
923
618
 
924
- // src/components/multi-combobox.tsx
925
- import * as PopoverPrimitive2 from "@radix-ui/react-popover";
926
- import { Command as CommandPrimitive2 } from "cmdk";
927
- import { Check as Check2, ChevronsUpDown as ChevronsUpDown2, Loader2 as Loader22, Search as Search2, X as X2 } from "lucide-react";
928
- import * as React7 from "react";
929
- import { Drawer as DrawerPrimitive2 } from "vaul";
930
- import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
931
- function MultiCombobox({
619
+ // src/components/combobox.tsx
620
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
621
+ function Combobox({
932
622
  options,
933
623
  value,
934
624
  onChange,
@@ -944,130 +634,119 @@ function MultiCombobox({
944
634
  onLoadMore,
945
635
  loading,
946
636
  hasMore,
947
- selectedOptions
637
+ selectedOption,
638
+ creatable,
639
+ formatCreateLabel
948
640
  }) {
949
- const [open, setOpen] = React7.useState(false);
641
+ const [open, setOpen] = React5.useState(false);
642
+ const [search, setSearch] = React5.useState("");
950
643
  const isDesktop = useIsDesktop();
951
644
  const isServer = onSearchChange !== void 0;
952
- const seen = /* @__PURE__ */ new Set();
953
- const selected = [...selectedOptions ?? [], ...options].filter((option) => {
954
- if (!value.includes(option.value) || seen.has(option.value)) return false;
955
- seen.add(option.value);
956
- return true;
957
- });
645
+ const selected = selectedOption?.value === value ? selectedOption : options.find((option) => option.value === value) ?? (creatable && value ? { value, label: value } : void 0);
646
+ const handleOpenChange = (next) => {
647
+ setOpen(next);
648
+ if (!next) setSearch("");
649
+ };
650
+ const handleSelect = (next) => {
651
+ onChange(next);
652
+ handleOpenChange(false);
653
+ };
654
+ const handleSearchChange = (next) => {
655
+ setSearch(next);
656
+ onSearchChange?.(next);
657
+ };
658
+ const trimmedSearch = search.trim();
659
+ const showCreate = !!creatable && trimmedSearch.length > 0 && !options.some(
660
+ (option) => option.value.toLowerCase() === trimmedSearch.toLowerCase()
661
+ );
958
662
  const { listRef, sentinelRef } = useInfiniteScrollSentinel({
959
663
  enabled: isServer && !!hasMore,
960
664
  loading,
961
665
  onLoadMore
962
666
  });
963
- const toggleValue = (optionValue) => {
964
- onChange(
965
- value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
966
- );
967
- };
968
- const removeValue = (optionValue) => {
969
- onChange(value.filter((v) => v !== optionValue));
970
- };
971
- const trigger = /* @__PURE__ */ jsxs16(
972
- "div",
667
+ const trigger = /* @__PURE__ */ jsxs7(
668
+ "button",
973
669
  {
670
+ type: "button",
974
671
  id,
975
672
  role: "combobox",
976
- tabIndex: disabled ? -1 : 0,
977
673
  "aria-expanded": open,
978
674
  "aria-invalid": ariaInvalid,
979
675
  "aria-describedby": ariaDescribedby,
980
- "aria-disabled": disabled,
981
- onKeyDown: (event) => {
982
- if (disabled) return;
983
- if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
984
- event.preventDefault();
985
- setOpen(true);
986
- }
987
- },
676
+ disabled,
988
677
  className: cn(
989
- "flex min-h-9 w-full cursor-pointer flex-wrap items-center gap-1 rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
678
+ "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
990
679
  className
991
680
  ),
992
681
  children: [
993
- selected.length === 0 ? /* @__PURE__ */ jsx18("span", { className: "text-muted-foreground", children: placeholder }) : selected.map((option) => /* @__PURE__ */ jsxs16(
994
- "span",
995
- {
996
- className: "inline-flex items-center gap-1 rounded bg-secondary px-1.5 py-0.5 text-xs text-secondary-foreground",
997
- children: [
998
- option.label,
999
- /* @__PURE__ */ jsx18(
1000
- "button",
1001
- {
1002
- type: "button",
1003
- tabIndex: -1,
1004
- "aria-label": `Remover ${option.label}`,
1005
- onClick: (event) => {
1006
- event.stopPropagation();
1007
- removeValue(option.value);
1008
- },
1009
- onPointerDown: (event) => event.stopPropagation(),
1010
- className: "rounded-sm opacity-60 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1011
- children: /* @__PURE__ */ jsx18(X2, { className: "h-3 w-3" })
1012
- }
1013
- )
1014
- ]
1015
- },
1016
- option.value
1017
- )),
1018
- /* @__PURE__ */ jsx18(ChevronsUpDown2, { className: "ml-auto h-4 w-4 shrink-0 opacity-50" })
682
+ /* @__PURE__ */ jsx8("span", { className: cn("truncate", !selected && "text-muted-foreground"), children: selected ? selected.label : placeholder }),
683
+ /* @__PURE__ */ jsx8(ChevronsUpDown2, { className: "h-4 w-4 shrink-0 opacity-50" })
1019
684
  ]
1020
685
  }
1021
686
  );
1022
- const command = /* @__PURE__ */ jsxs16(
1023
- CommandPrimitive2,
687
+ const command = /* @__PURE__ */ jsxs7(
688
+ CommandPrimitive,
1024
689
  {
1025
690
  shouldFilter: !isServer,
1026
691
  className: "flex h-full w-full flex-col overflow-hidden",
1027
692
  children: [
1028
- /* @__PURE__ */ jsxs16("div", { className: "flex items-center border-b px-3", children: [
1029
- /* @__PURE__ */ jsx18(Search2, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
1030
- /* @__PURE__ */ jsx18(
1031
- CommandPrimitive2.Input,
693
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center border-b px-3", children: [
694
+ /* @__PURE__ */ jsx8(Search, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
695
+ /* @__PURE__ */ jsx8(
696
+ CommandPrimitive.Input,
1032
697
  {
1033
698
  placeholder: searchPlaceholder,
1034
- onValueChange: isServer ? onSearchChange : void 0,
699
+ onValueChange: isServer || creatable ? handleSearchChange : void 0,
1035
700
  className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
1036
701
  }
1037
702
  )
1038
703
  ] }),
1039
- /* @__PURE__ */ jsxs16(
1040
- CommandPrimitive2.List,
704
+ /* @__PURE__ */ jsxs7(
705
+ CommandPrimitive.List,
1041
706
  {
1042
707
  ref: listRef,
1043
708
  className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
1044
709
  children: [
1045
- !isServer && /* @__PURE__ */ jsx18(CommandPrimitive2.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
1046
- options.map((option) => /* @__PURE__ */ jsxs16(
1047
- CommandPrimitive2.Item,
710
+ !isServer && !showCreate && /* @__PURE__ */ jsx8(CommandPrimitive.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
711
+ showCreate && /* @__PURE__ */ jsxs7(
712
+ CommandPrimitive.Item,
713
+ {
714
+ value: trimmedSearch,
715
+ onSelect: () => handleSelect(trimmedSearch),
716
+ className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground sm:py-1.5",
717
+ children: [
718
+ /* @__PURE__ */ jsx8(Plus, { className: "h-4 w-4 shrink-0" }),
719
+ /* @__PURE__ */ jsx8("span", { className: "truncate", children: (formatCreateLabel ?? ((text) => `Usar "${text}"`))(
720
+ trimmedSearch
721
+ ) })
722
+ ]
723
+ }
724
+ ),
725
+ options.map((option) => /* @__PURE__ */ jsxs7(
726
+ CommandPrimitive.Item,
1048
727
  {
1049
728
  value: isServer ? option.value : option.label,
1050
729
  disabled: option.disabled,
1051
- onSelect: () => toggleValue(option.value),
730
+ onSelect: () => handleSelect(option.value),
1052
731
  className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
1053
732
  children: [
1054
- /* @__PURE__ */ jsx18(
1055
- Check2,
733
+ /* @__PURE__ */ jsx8(
734
+ Check,
1056
735
  {
1057
736
  className: cn(
1058
737
  "h-4 w-4 shrink-0",
1059
- value.includes(option.value) ? "opacity-100" : "opacity-0"
738
+ value === option.value ? "opacity-100" : "opacity-0"
1060
739
  )
1061
740
  }
1062
741
  ),
1063
- /* @__PURE__ */ jsx18("span", { className: "truncate", children: option.label })
742
+ /* @__PURE__ */ jsx8("span", { className: "truncate", children: option.label })
1064
743
  ]
1065
744
  },
1066
745
  option.value
1067
746
  )),
1068
- isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx18("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
1069
- isServer && loading && /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx18(Loader22, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
1070
- isServer && hasMore && /* @__PURE__ */ jsx18("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
747
+ isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx8("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
748
+ isServer && loading && /* @__PURE__ */ jsx8("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx8(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
749
+ isServer && hasMore && /* @__PURE__ */ jsx8("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
1071
750
  ]
1072
751
  }
1073
752
  )
@@ -1075,9 +754,9 @@ function MultiCombobox({
1075
754
  }
1076
755
  );
1077
756
  if (isDesktop) {
1078
- return /* @__PURE__ */ jsxs16(PopoverPrimitive2.Root, { open, onOpenChange: setOpen, children: [
1079
- /* @__PURE__ */ jsx18(PopoverPrimitive2.Trigger, { asChild: true, disabled, children: trigger }),
1080
- /* @__PURE__ */ jsx18(PopoverPrimitive2.Portal, { children: /* @__PURE__ */ jsx18(
757
+ return /* @__PURE__ */ jsxs7(PopoverPrimitive2.Root, { open, onOpenChange: handleOpenChange, children: [
758
+ /* @__PURE__ */ jsx8(PopoverPrimitive2.Trigger, { asChild: true, children: trigger }),
759
+ /* @__PURE__ */ jsx8(PopoverPrimitive2.Portal, { children: /* @__PURE__ */ jsx8(
1081
760
  PopoverPrimitive2.Content,
1082
761
  {
1083
762
  align: "start",
@@ -1089,137 +768,127 @@ function MultiCombobox({
1089
768
  ) })
1090
769
  ] });
1091
770
  }
1092
- return /* @__PURE__ */ jsxs16(DrawerPrimitive2.Root, { open, onOpenChange: setOpen, children: [
1093
- /* @__PURE__ */ jsx18(DrawerPrimitive2.Trigger, { asChild: true, disabled, children: trigger }),
1094
- /* @__PURE__ */ jsxs16(DrawerPrimitive2.Portal, { children: [
1095
- /* @__PURE__ */ jsx18(DrawerPrimitive2.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1096
- /* @__PURE__ */ jsxs16(DrawerPrimitive2.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
1097
- /* @__PURE__ */ jsx18(DrawerPrimitive2.Title, { className: "sr-only", children: placeholder }),
1098
- /* @__PURE__ */ jsx18("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
771
+ return /* @__PURE__ */ jsxs7(DrawerPrimitive2.Root, { open, onOpenChange: handleOpenChange, children: [
772
+ /* @__PURE__ */ jsx8(DrawerPrimitive2.Trigger, { asChild: true, children: trigger }),
773
+ /* @__PURE__ */ jsxs7(DrawerPrimitive2.Portal, { children: [
774
+ /* @__PURE__ */ jsx8(DrawerPrimitive2.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
775
+ /* @__PURE__ */ jsxs7(DrawerPrimitive2.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
776
+ /* @__PURE__ */ jsx8(DrawerPrimitive2.Title, { className: "sr-only", children: placeholder }),
777
+ /* @__PURE__ */ jsx8("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1099
778
  command
1100
779
  ] })
1101
780
  ] })
1102
781
  ] });
1103
782
  }
1104
783
 
1105
- // src/components/color-picker.tsx
1106
- import * as PopoverPrimitive3 from "@radix-ui/react-popover";
1107
- import { ChevronsUpDown as ChevronsUpDown3, X as X3 } from "lucide-react";
1108
- import * as React8 from "react";
1109
- import {
1110
- HexAlphaColorPicker,
1111
- HexColorInput,
1112
- HexColorPicker
1113
- } from "react-colorful";
1114
- import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1115
- function ColorPicker({
1116
- value,
1117
- onChange,
1118
- placeholder = "Selecionar cor",
1119
- clearable = true,
1120
- alpha = false,
1121
- disabled,
1122
- id,
1123
- className,
1124
- "aria-invalid": ariaInvalid,
1125
- "aria-describedby": ariaDescribedby
784
+ // src/components/confirm-dialog.tsx
785
+ import * as AlertDialog from "@radix-ui/react-alert-dialog";
786
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
787
+ function ConfirmDialog({
788
+ open,
789
+ onOpenChange,
790
+ onConfirm,
791
+ title,
792
+ description,
793
+ confirmLabel = "Confirmar",
794
+ cancelLabel = "Cancelar",
795
+ loading = false,
796
+ variant = "default",
797
+ blurBackdrop = true,
798
+ backdropClassName
1126
799
  }) {
1127
- const [open, setOpen] = React8.useState(false);
1128
- const color = value || "";
1129
- const showClear = clearable && !!color && !disabled;
1130
- const clear = (event) => {
1131
- event.stopPropagation();
1132
- event.preventDefault();
1133
- onChange("");
1134
- };
1135
- return /* @__PURE__ */ jsxs17(PopoverPrimitive3.Root, { open, onOpenChange: setOpen, children: [
1136
- /* @__PURE__ */ jsx19(PopoverPrimitive3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs17(
1137
- "button",
800
+ return /* @__PURE__ */ jsx9(AlertDialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs8(AlertDialog.Portal, { children: [
801
+ /* @__PURE__ */ jsx9(
802
+ AlertDialog.Overlay,
1138
803
  {
1139
- type: "button",
1140
- id,
1141
- "aria-invalid": ariaInvalid,
1142
- "aria-describedby": ariaDescribedby,
1143
- disabled,
1144
804
  className: cn(
1145
- "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
1146
- className
1147
- ),
1148
- children: [
1149
- /* @__PURE__ */ jsxs17("span", { className: "flex items-center gap-2 truncate", children: [
1150
- /* @__PURE__ */ jsx19(
1151
- "span",
1152
- {
1153
- "aria-hidden": true,
1154
- className: "h-5 w-5 shrink-0 rounded border border-border bg-[length:8px_8px] bg-[position:0_0,4px_4px]",
1155
- style: {
1156
- // The checkerboard stays under the color layer so a
1157
- // translucent value reads as translucent in the swatch.
1158
- backgroundImage: `${color ? `linear-gradient(${color},${color}),` : ""}linear-gradient(45deg,var(--color-muted) 25%,transparent 25%,transparent 75%,var(--color-muted) 75%),linear-gradient(45deg,var(--color-muted) 25%,transparent 25%,transparent 75%,var(--color-muted) 75%)`
1159
- }
1160
- }
1161
- ),
1162
- /* @__PURE__ */ jsx19("span", { className: cn("truncate", !color && "text-muted-foreground"), children: color || placeholder })
1163
- ] }),
1164
- /* @__PURE__ */ jsxs17("span", { className: "flex shrink-0 items-center gap-1", children: [
1165
- showClear && // Not a <button> — nesting one inside the trigger button is
1166
- // invalid HTML.
1167
- /* @__PURE__ */ jsx19(
1168
- "span",
1169
- {
1170
- role: "button",
1171
- tabIndex: 0,
1172
- "aria-label": "Limpar cor",
1173
- onClick: clear,
1174
- onKeyDown: (event) => {
1175
- if (event.key === "Enter" || event.key === " ") clear(event);
1176
- },
1177
- className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1178
- children: /* @__PURE__ */ jsx19(X3, { className: "h-3.5 w-3.5" })
1179
- }
1180
- ),
1181
- /* @__PURE__ */ jsx19(ChevronsUpDown3, { className: "h-4 w-4 shrink-0 opacity-50" })
1182
- ] })
1183
- ]
1184
- }
1185
- ) }),
1186
- /* @__PURE__ */ jsx19(PopoverPrimitive3.Portal, { children: /* @__PURE__ */ jsx19(
1187
- PopoverPrimitive3.Content,
1188
- {
1189
- align: "start",
1190
- sideOffset: 4,
1191
- className: "z-50 w-56 rounded-md border bg-popover p-3 text-popover-foreground shadow-md",
1192
- children: /* @__PURE__ */ jsxs17("div", { className: "mh-color-picker flex flex-col gap-3", children: [
1193
- alpha ? /* @__PURE__ */ jsx19(HexAlphaColorPicker, { color, onChange }) : /* @__PURE__ */ jsx19(HexColorPicker, { color, onChange }),
1194
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
1195
- /* @__PURE__ */ jsx19("span", { className: "text-sm text-muted-foreground", children: "#" }),
1196
- /* @__PURE__ */ jsx19(
1197
- HexColorInput,
1198
- {
1199
- color,
1200
- onChange,
1201
- prefixed: false,
1202
- alpha,
1203
- className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm uppercase shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1204
- }
1205
- )
1206
- ] })
1207
- ] })
805
+ "fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
806
+ blurBackdrop && "backdrop-blur-sm",
807
+ backdropClassName
808
+ )
1208
809
  }
1209
- ) })
810
+ ),
811
+ /* @__PURE__ */ jsxs8(AlertDialog.Content, { className: "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", children: [
812
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-col space-y-2 text-center sm:text-left", children: [
813
+ /* @__PURE__ */ jsx9(AlertDialog.Title, { className: "text-lg font-semibold", children: title }),
814
+ /* @__PURE__ */ jsx9(AlertDialog.Description, { className: "text-sm text-muted-foreground", children: description })
815
+ ] }),
816
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [
817
+ /* @__PURE__ */ jsx9(AlertDialog.Cancel, { className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer", children: cancelLabel }),
818
+ /* @__PURE__ */ jsx9(
819
+ AlertDialog.Action,
820
+ {
821
+ onClick: onConfirm,
822
+ disabled: loading,
823
+ className: `inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer ${variant === "destructive" ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "bg-primary text-primary-foreground hover:bg-primary/90"}`,
824
+ children: loading ? "Aguarde..." : confirmLabel
825
+ }
826
+ )
827
+ ] })
828
+ ] })
829
+ ] }) });
830
+ }
831
+
832
+ // src/components/data-table-wrapper.tsx
833
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
834
+ function DataTableWrapper({
835
+ children,
836
+ isEmpty,
837
+ isLoading,
838
+ emptyIcon,
839
+ emptyTitle = "Nenhum registro encontrado",
840
+ emptyDescription,
841
+ page,
842
+ onPageChange,
843
+ hasNextPage = false,
844
+ hasPreviousPage = false
845
+ }) {
846
+ return /* @__PURE__ */ jsxs9("div", { className: "space-y-4", children: [
847
+ /* @__PURE__ */ jsx10("div", { className: "overflow-x-auto rounded-md border", children }),
848
+ !isLoading && isEmpty && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
849
+ emptyIcon && /* @__PURE__ */ jsx10("div", { className: "mb-4 text-muted-foreground", children: emptyIcon }),
850
+ /* @__PURE__ */ jsx10("h3", { className: "text-lg font-semibold tracking-tight", children: emptyTitle }),
851
+ emptyDescription && /* @__PURE__ */ jsx10("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: emptyDescription })
852
+ ] }),
853
+ page !== void 0 && onPageChange && /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-end gap-4", children: [
854
+ /* @__PURE__ */ jsx10(
855
+ "button",
856
+ {
857
+ type: "button",
858
+ onClick: () => onPageChange(page - 1),
859
+ disabled: !hasPreviousPage,
860
+ className: "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
861
+ children: "Anterior"
862
+ }
863
+ ),
864
+ /* @__PURE__ */ jsxs9("span", { className: "text-sm text-muted-foreground", children: [
865
+ "P\xE1gina ",
866
+ page
867
+ ] }),
868
+ /* @__PURE__ */ jsx10(
869
+ "button",
870
+ {
871
+ type: "button",
872
+ onClick: () => onPageChange(page + 1),
873
+ disabled: !hasNextPage,
874
+ className: "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
875
+ children: "Pr\xF3ximo"
876
+ }
877
+ )
878
+ ] })
1210
879
  ] });
1211
880
  }
1212
881
 
1213
882
  // src/components/date-picker.tsx
1214
- import * as PopoverPrimitive4 from "@radix-ui/react-popover";
883
+ import * as PopoverPrimitive3 from "@radix-ui/react-popover";
1215
884
  import { InputMask } from "@react-input/mask";
1216
885
  import { endOfYear, format, isValid, parse, subYears } from "date-fns";
1217
886
  import { ptBR } from "date-fns/locale";
1218
- import { CalendarIcon, ChevronDown, ChevronLeft, ChevronRight, Clock } from "lucide-react";
1219
- import * as React9 from "react";
887
+ import { CalendarIcon, ChevronDown, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2, Clock } from "lucide-react";
888
+ import * as React6 from "react";
1220
889
  import { DayPicker } from "react-day-picker";
1221
890
  import { Drawer as DrawerPrimitive3 } from "vaul";
1222
- import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
891
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1223
892
  var DATE_MASK = "__/__/____";
1224
893
  var TIME_MASK = "__:__";
1225
894
  var MASK_REPLACEMENT = { _: /\d/ };
@@ -1261,7 +930,7 @@ function toMatchers(minDate, maxDate, disabledDates) {
1261
930
  }
1262
931
  var triggerInputClass = "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 pr-10 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive";
1263
932
  var fieldInputClass = "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
1264
- var navButtonClass2 = "inline-flex size-(--cell-size) shrink-0 select-none items-center justify-center rounded-md p-0 transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50";
933
+ var navButtonClass = "inline-flex size-(--cell-size) shrink-0 select-none items-center justify-center rounded-md p-0 transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50";
1265
934
  var calendarClassNames = {
1266
935
  months: "relative flex flex-col gap-4",
1267
936
  month: "flex w-full flex-col gap-4",
@@ -1271,8 +940,8 @@ var calendarClassNames = {
1271
940
  dropdown_root: "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
1272
941
  dropdown: "absolute inset-0 bg-popover opacity-0",
1273
942
  nav: "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
1274
- button_previous: navButtonClass2,
1275
- button_next: navButtonClass2,
943
+ button_previous: navButtonClass,
944
+ button_next: navButtonClass,
1276
945
  month_grid: "w-full border-collapse",
1277
946
  weekdays: "flex",
1278
947
  weekday: "flex-1 select-none rounded-md text-[0.8rem] font-normal text-muted-foreground",
@@ -1286,9 +955,9 @@ var calendarClassNames = {
1286
955
  };
1287
956
  var calendarComponents = {
1288
957
  Chevron: ({ orientation }) => {
1289
- if (orientation === "left") return /* @__PURE__ */ jsx20(ChevronLeft, { className: "size-4" });
1290
- if (orientation === "right") return /* @__PURE__ */ jsx20(ChevronRight, { className: "size-4" });
1291
- return /* @__PURE__ */ jsx20(ChevronDown, { className: "size-4" });
958
+ if (orientation === "left") return /* @__PURE__ */ jsx11(ChevronLeft2, { className: "size-4" });
959
+ if (orientation === "right") return /* @__PURE__ */ jsx11(ChevronRight2, { className: "size-4" });
960
+ return /* @__PURE__ */ jsx11(ChevronDown, { className: "size-4" });
1292
961
  }
1293
962
  };
1294
963
  var formatMonthDropdown = (date) => date.toLocaleString("pt-BR", { month: "short" });
@@ -1307,12 +976,12 @@ function DatePickerImpl({
1307
976
  "aria-describedby": ariaDescribedby
1308
977
  }) {
1309
978
  const isDesktop = useIsDesktop();
1310
- const triggerRef = React9.useRef(null);
1311
- const [open, setOpenRaw] = React9.useState(false);
1312
- const [focused, setFocused] = React9.useState(false);
1313
- const [editText, setEditText] = React9.useState("");
1314
- const [pendingDate, setPendingDate] = React9.useState();
1315
- const [pendingTime, setPendingTime] = React9.useState("");
979
+ const triggerRef = React6.useRef(null);
980
+ const [open, setOpenRaw] = React6.useState(false);
981
+ const [focused, setFocused] = React6.useState(false);
982
+ const [editText, setEditText] = React6.useState("");
983
+ const [pendingDate, setPendingDate] = React6.useState();
984
+ const [pendingTime, setPendingTime] = React6.useState("");
1316
985
  const committedDate = withTime ? dateTimeToDate(value) : dateOnlyToDate(value);
1317
986
  const displayValue = displayFor(value, withTime);
1318
987
  const matchers = toMatchers(minDate, maxDate, disabledDates);
@@ -1373,8 +1042,8 @@ function DatePickerImpl({
1373
1042
  }
1374
1043
  };
1375
1044
  const canApply = withTime && !!pendingDate && !!parseStrict(pendingTime, TIME_FORMAT);
1376
- const trigger = /* @__PURE__ */ jsxs18("div", { ref: triggerRef, className: "relative", children: [
1377
- /* @__PURE__ */ jsx20(
1045
+ const trigger = /* @__PURE__ */ jsxs10("div", { ref: triggerRef, className: "relative", children: [
1046
+ /* @__PURE__ */ jsx11(
1378
1047
  InputMask,
1379
1048
  {
1380
1049
  id,
@@ -1394,7 +1063,7 @@ function DatePickerImpl({
1394
1063
  className: cn(triggerInputClass, className)
1395
1064
  }
1396
1065
  ),
1397
- /* @__PURE__ */ jsx20(
1066
+ /* @__PURE__ */ jsx11(
1398
1067
  "button",
1399
1068
  {
1400
1069
  type: "button",
@@ -1404,12 +1073,12 @@ function DatePickerImpl({
1404
1073
  "aria-expanded": open,
1405
1074
  onClick: () => setOpen(!open),
1406
1075
  className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
1407
- children: withTime ? /* @__PURE__ */ jsx20(Clock, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx20(CalendarIcon, { className: "h-4 w-4" })
1076
+ children: withTime ? /* @__PURE__ */ jsx11(Clock, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx11(CalendarIcon, { className: "h-4 w-4" })
1408
1077
  }
1409
1078
  )
1410
1079
  ] });
1411
- const content = /* @__PURE__ */ jsxs18("div", { className: withTime ? "p-3" : "", children: [
1412
- /* @__PURE__ */ jsx20(
1080
+ const content = /* @__PURE__ */ jsxs10("div", { className: withTime ? "p-3" : "", children: [
1081
+ /* @__PURE__ */ jsx11(
1413
1082
  DayPicker,
1414
1083
  {
1415
1084
  mode: "single",
@@ -1427,10 +1096,10 @@ function DatePickerImpl({
1427
1096
  components: calendarComponents
1428
1097
  }
1429
1098
  ),
1430
- withTime && /* @__PURE__ */ jsxs18(Fragment3, { children: [
1431
- /* @__PURE__ */ jsxs18("div", { className: "mt-2 flex items-center gap-2 border-t pt-3", children: [
1432
- /* @__PURE__ */ jsx20("span", { className: "text-sm text-muted-foreground", children: "Hor\xE1rio" }),
1433
- /* @__PURE__ */ jsx20(
1099
+ withTime && /* @__PURE__ */ jsxs10(Fragment5, { children: [
1100
+ /* @__PURE__ */ jsxs10("div", { className: "mt-2 flex items-center gap-2 border-t pt-3", children: [
1101
+ /* @__PURE__ */ jsx11("span", { className: "text-sm text-muted-foreground", children: "Hor\xE1rio" }),
1102
+ /* @__PURE__ */ jsx11(
1434
1103
  InputMask,
1435
1104
  {
1436
1105
  mask: TIME_MASK,
@@ -1443,7 +1112,7 @@ function DatePickerImpl({
1443
1112
  }
1444
1113
  )
1445
1114
  ] }),
1446
- /* @__PURE__ */ jsx20(
1115
+ /* @__PURE__ */ jsx11(
1447
1116
  "button",
1448
1117
  {
1449
1118
  type: "button",
@@ -1456,10 +1125,10 @@ function DatePickerImpl({
1456
1125
  ] })
1457
1126
  ] });
1458
1127
  if (isDesktop) {
1459
- return /* @__PURE__ */ jsxs18(PopoverPrimitive4.Root, { open, onOpenChange: setOpen, children: [
1460
- /* @__PURE__ */ jsx20(PopoverPrimitive4.Anchor, { asChild: true, children: trigger }),
1461
- /* @__PURE__ */ jsx20(PopoverPrimitive4.Portal, { children: /* @__PURE__ */ jsx20(
1462
- PopoverPrimitive4.Content,
1128
+ return /* @__PURE__ */ jsxs10(PopoverPrimitive3.Root, { open, onOpenChange: setOpen, children: [
1129
+ /* @__PURE__ */ jsx11(PopoverPrimitive3.Anchor, { asChild: true, children: trigger }),
1130
+ /* @__PURE__ */ jsx11(PopoverPrimitive3.Portal, { children: /* @__PURE__ */ jsx11(
1131
+ PopoverPrimitive3.Content,
1463
1132
  {
1464
1133
  align: "start",
1465
1134
  sideOffset: 4,
@@ -1473,11 +1142,11 @@ function DatePickerImpl({
1473
1142
  ) })
1474
1143
  ] });
1475
1144
  }
1476
- return /* @__PURE__ */ jsxs18(DrawerPrimitive3.Root, { open, onOpenChange: setOpen, children: [
1145
+ return /* @__PURE__ */ jsxs10(DrawerPrimitive3.Root, { open, onOpenChange: setOpen, children: [
1477
1146
  trigger,
1478
- /* @__PURE__ */ jsxs18(DrawerPrimitive3.Portal, { children: [
1479
- /* @__PURE__ */ jsx20(DrawerPrimitive3.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1480
- /* @__PURE__ */ jsxs18(
1147
+ /* @__PURE__ */ jsxs10(DrawerPrimitive3.Portal, { children: [
1148
+ /* @__PURE__ */ jsx11(DrawerPrimitive3.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1149
+ /* @__PURE__ */ jsxs10(
1481
1150
  DrawerPrimitive3.Content,
1482
1151
  {
1483
1152
  onOpenAutoFocus: (event) => event.preventDefault(),
@@ -1486,9 +1155,9 @@ function DatePickerImpl({
1486
1155
  },
1487
1156
  className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none",
1488
1157
  children: [
1489
- /* @__PURE__ */ jsx20(DrawerPrimitive3.Title, { className: "sr-only", children: withTime ? "Selecione a data e o hor\xE1rio" : "Selecione a data" }),
1490
- /* @__PURE__ */ jsx20("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1491
- /* @__PURE__ */ jsx20("div", { className: "flex justify-center overflow-y-auto pb-4", children: content })
1158
+ /* @__PURE__ */ jsx11(DrawerPrimitive3.Title, { className: "sr-only", children: withTime ? "Selecione a data e o hor\xE1rio" : "Selecione a data" }),
1159
+ /* @__PURE__ */ jsx11("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1160
+ /* @__PURE__ */ jsx11("div", { className: "flex justify-center overflow-y-auto pb-4", children: content })
1492
1161
  ]
1493
1162
  }
1494
1163
  )
@@ -1496,88 +1165,263 @@ function DatePickerImpl({
1496
1165
  ] });
1497
1166
  }
1498
1167
  function DatePicker(props) {
1499
- return /* @__PURE__ */ jsx20(DatePickerImpl, { ...props });
1168
+ return /* @__PURE__ */ jsx11(DatePickerImpl, { ...props });
1500
1169
  }
1501
1170
  function DateTimePicker(props) {
1502
- return /* @__PURE__ */ jsx20(DatePickerImpl, { ...props, withTime: true });
1171
+ return /* @__PURE__ */ jsx11(DatePickerImpl, { ...props, withTime: true });
1503
1172
  }
1504
1173
 
1505
- // src/components/stat-card.tsx
1506
- import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
1507
- function StatCard({ label, value, detail, icon, isLoading }) {
1508
- if (isLoading) {
1509
- return /* @__PURE__ */ jsxs19("div", { className: "rounded-lg border bg-card p-6 shadow-sm", children: [
1510
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between pb-2", children: [
1511
- /* @__PURE__ */ jsx21("div", { className: "h-4 w-24 animate-pulse rounded bg-muted" }),
1512
- /* @__PURE__ */ jsx21("div", { className: "h-4 w-4 animate-pulse rounded bg-muted" })
1513
- ] }),
1514
- /* @__PURE__ */ jsx21("div", { className: "mt-2 h-7 w-16 animate-pulse rounded bg-muted" }),
1515
- /* @__PURE__ */ jsx21("div", { className: "mt-1 h-4 w-20 animate-pulse rounded bg-muted" })
1174
+ // src/components/empty-state.tsx
1175
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1176
+ function EmptyState({ icon, title, description, action, className }) {
1177
+ return /* @__PURE__ */ jsxs11("div", { className: `flex flex-col items-center justify-center py-16 text-center ${className ?? ""}`, children: [
1178
+ icon && /* @__PURE__ */ jsx12("div", { className: "mb-4 text-muted-foreground", children: icon }),
1179
+ /* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold tracking-tight", children: title }),
1180
+ description && /* @__PURE__ */ jsx12("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: description }),
1181
+ action && /* @__PURE__ */ jsx12("div", { className: "mt-4", children: action })
1182
+ ] });
1183
+ }
1184
+
1185
+ // src/components/filter-toolbar.tsx
1186
+ import * as PopoverPrimitive4 from "@radix-ui/react-popover";
1187
+ import { ChevronDown as ChevronDown2, X as X3 } from "lucide-react";
1188
+ import * as React7 from "react";
1189
+ import { Drawer as DrawerPrimitive4 } from "vaul";
1190
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1191
+ function FilterToolbar({
1192
+ children,
1193
+ isFilterActive = false,
1194
+ onClear,
1195
+ clearLabel = "Limpar filtros",
1196
+ className
1197
+ }) {
1198
+ return /* @__PURE__ */ jsxs12(
1199
+ "div",
1200
+ {
1201
+ className: cn(
1202
+ "mb-6 flex flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center",
1203
+ className
1204
+ ),
1205
+ children: [
1206
+ children,
1207
+ isFilterActive && onClear && /* @__PURE__ */ jsxs12(
1208
+ "button",
1209
+ {
1210
+ type: "button",
1211
+ onClick: onClear,
1212
+ className: "inline-flex h-9 shrink-0 items-center justify-center gap-1.5 self-start rounded-md px-2.5 text-sm text-muted-foreground transition-colors hover:text-foreground sm:self-auto",
1213
+ children: [
1214
+ /* @__PURE__ */ jsx13(X3, { className: "h-4 w-4" }),
1215
+ clearLabel
1216
+ ]
1217
+ }
1218
+ )
1219
+ ]
1220
+ }
1221
+ );
1222
+ }
1223
+ function FilterPopover({
1224
+ label,
1225
+ icon: Icon,
1226
+ active = false,
1227
+ badge,
1228
+ contentClassName,
1229
+ align = "start",
1230
+ title,
1231
+ children
1232
+ }) {
1233
+ const [open, setOpen] = React7.useState(false);
1234
+ const isDesktop = useIsDesktop();
1235
+ const close = React7.useCallback(() => setOpen(false), []);
1236
+ const content = typeof children === "function" ? children({ close }) : children;
1237
+ const trigger = /* @__PURE__ */ jsxs12(
1238
+ "button",
1239
+ {
1240
+ type: "button",
1241
+ "aria-expanded": open,
1242
+ className: cn(
1243
+ "flex h-9 w-full shrink-0 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-input bg-transparent px-3 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:w-auto sm:justify-start",
1244
+ active ? "border-ring/60 text-foreground" : "text-muted-foreground"
1245
+ ),
1246
+ children: [
1247
+ Icon && /* @__PURE__ */ jsx13(Icon, { className: "h-4 w-4 shrink-0 opacity-70" }),
1248
+ /* @__PURE__ */ jsx13("span", { className: "truncate", children: label }),
1249
+ active && badge != null && /* @__PURE__ */ jsx13("span", { className: "inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground", children: badge }),
1250
+ /* @__PURE__ */ jsx13(ChevronDown2, { className: "h-4 w-4 shrink-0 opacity-50" })
1251
+ ]
1252
+ }
1253
+ );
1254
+ if (isDesktop) {
1255
+ return /* @__PURE__ */ jsxs12(PopoverPrimitive4.Root, { open, onOpenChange: setOpen, children: [
1256
+ /* @__PURE__ */ jsx13(PopoverPrimitive4.Trigger, { asChild: true, children: trigger }),
1257
+ /* @__PURE__ */ jsx13(PopoverPrimitive4.Portal, { children: /* @__PURE__ */ jsx13(
1258
+ PopoverPrimitive4.Content,
1259
+ {
1260
+ align,
1261
+ sideOffset: 4,
1262
+ onInteractOutside: (event) => {
1263
+ const target = event.target;
1264
+ if (target?.closest("[data-radix-popper-content-wrapper]")) {
1265
+ event.preventDefault();
1266
+ }
1267
+ },
1268
+ className: cn(
1269
+ "pointer-events-auto z-50 w-60 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
1270
+ contentClassName
1271
+ ),
1272
+ children: content
1273
+ }
1274
+ ) })
1516
1275
  ] });
1517
1276
  }
1518
- return /* @__PURE__ */ jsxs19("div", { className: "rounded-lg border bg-card p-6 shadow-sm", children: [
1519
- /* @__PURE__ */ jsxs19("div", { className: "flex items-center justify-between pb-2", children: [
1520
- /* @__PURE__ */ jsx21("span", { className: "text-sm font-medium text-muted-foreground", children: label }),
1521
- icon && /* @__PURE__ */ jsx21("span", { className: "text-muted-foreground", children: icon })
1522
- ] }),
1523
- /* @__PURE__ */ jsx21("div", { className: "text-2xl font-bold", children: value }),
1524
- detail && /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: detail })
1277
+ return /* @__PURE__ */ jsxs12(DrawerPrimitive4.Root, { open, onOpenChange: setOpen, children: [
1278
+ /* @__PURE__ */ jsx13(DrawerPrimitive4.Trigger, { asChild: true, children: trigger }),
1279
+ /* @__PURE__ */ jsxs12(DrawerPrimitive4.Portal, { children: [
1280
+ /* @__PURE__ */ jsx13(DrawerPrimitive4.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1281
+ /* @__PURE__ */ jsxs12(DrawerPrimitive4.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
1282
+ /* @__PURE__ */ jsx13(DrawerPrimitive4.Title, { className: "sr-only", children: title ?? label }),
1283
+ /* @__PURE__ */ jsx13("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1284
+ /* @__PURE__ */ jsx13("div", { className: "overflow-y-auto p-2", children: content })
1285
+ ] })
1286
+ ] })
1287
+ ] });
1288
+ }
1289
+ function FilterOptionList({
1290
+ options,
1291
+ value,
1292
+ onChange,
1293
+ multiple = false,
1294
+ clearable = false,
1295
+ clearLabel = "Limpar"
1296
+ }) {
1297
+ const selected = Array.isArray(value) ? value : value ? [value] : [];
1298
+ const hasSelection = selected.length > 0;
1299
+ const toggle = (optionValue) => {
1300
+ if (!multiple) {
1301
+ onChange(optionValue);
1302
+ return;
1303
+ }
1304
+ onChange(
1305
+ selected.includes(optionValue) ? selected.filter((v) => v !== optionValue) : [...selected, optionValue]
1306
+ );
1307
+ };
1308
+ return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col p-1", children: [
1309
+ options.map((option) => {
1310
+ const isChecked = selected.includes(option.value);
1311
+ return /* @__PURE__ */ jsxs12(
1312
+ "button",
1313
+ {
1314
+ type: "button",
1315
+ onClick: () => toggle(option.value),
1316
+ className: "flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground",
1317
+ children: [
1318
+ option.dotClassName && /* @__PURE__ */ jsx13(
1319
+ "span",
1320
+ {
1321
+ className: cn(
1322
+ "h-2 w-2 shrink-0 rounded-full",
1323
+ option.dotClassName
1324
+ )
1325
+ }
1326
+ ),
1327
+ /* @__PURE__ */ jsx13("span", { className: "flex-1 truncate", children: option.label }),
1328
+ isChecked && /* @__PURE__ */ jsx13("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-primary" })
1329
+ ]
1330
+ },
1331
+ option.value
1332
+ );
1333
+ }),
1334
+ clearable && hasSelection && /* @__PURE__ */ jsx13(
1335
+ "button",
1336
+ {
1337
+ type: "button",
1338
+ onClick: () => onChange(multiple ? [] : ""),
1339
+ className: "mt-1 border-t px-2 pt-1.5 pb-0.5 text-center text-xs text-muted-foreground transition-colors hover:text-foreground",
1340
+ children: clearLabel
1341
+ }
1342
+ )
1525
1343
  ] });
1526
1344
  }
1527
1345
 
1528
- // src/components/data-table-wrapper.tsx
1529
- import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
1530
- function DataTableWrapper({
1346
+ // src/components/form-dialog.tsx
1347
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1348
+ function FormDialogLayout({
1349
+ title,
1531
1350
  children,
1532
- isEmpty,
1533
- isLoading,
1534
- emptyIcon,
1535
- emptyTitle = "Nenhum registro encontrado",
1536
- emptyDescription,
1537
- page,
1538
- onPageChange,
1539
- hasNextPage = false,
1540
- hasPreviousPage = false
1351
+ onSubmit,
1352
+ submitLabel = "Salvar",
1353
+ cancelLabel = "Cancelar",
1354
+ onCancel,
1355
+ isSubmitting = false,
1356
+ isDisabled = false
1541
1357
  }) {
1542
- return /* @__PURE__ */ jsxs20("div", { className: "space-y-4", children: [
1543
- /* @__PURE__ */ jsx22("div", { className: "overflow-x-auto rounded-md border", children }),
1544
- !isLoading && isEmpty && /* @__PURE__ */ jsxs20("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
1545
- emptyIcon && /* @__PURE__ */ jsx22("div", { className: "mb-4 text-muted-foreground", children: emptyIcon }),
1546
- /* @__PURE__ */ jsx22("h3", { className: "text-lg font-semibold tracking-tight", children: emptyTitle }),
1547
- emptyDescription && /* @__PURE__ */ jsx22("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: emptyDescription })
1548
- ] }),
1549
- page !== void 0 && onPageChange && /* @__PURE__ */ jsxs20("div", { className: "flex items-center justify-end gap-4", children: [
1550
- /* @__PURE__ */ jsx22(
1358
+ return /* @__PURE__ */ jsxs13("form", { onSubmit, children: [
1359
+ /* @__PURE__ */ jsx14("div", { className: "flex flex-col space-y-1.5 text-center sm:text-left", children: /* @__PURE__ */ jsx14("h2", { className: "text-lg font-semibold leading-none tracking-tight", children: title }) }),
1360
+ /* @__PURE__ */ jsx14("div", { className: "space-y-4 py-4", children }),
1361
+ /* @__PURE__ */ jsxs13("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [
1362
+ /* @__PURE__ */ jsx14(
1551
1363
  "button",
1552
1364
  {
1553
1365
  type: "button",
1554
- onClick: () => onPageChange(page - 1),
1555
- disabled: !hasPreviousPage,
1556
- className: "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
1557
- children: "Anterior"
1366
+ onClick: onCancel,
1367
+ className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
1368
+ children: cancelLabel
1558
1369
  }
1559
1370
  ),
1560
- /* @__PURE__ */ jsxs20("span", { className: "text-sm text-muted-foreground", children: [
1561
- "P\xE1gina ",
1562
- page
1563
- ] }),
1564
- /* @__PURE__ */ jsx22(
1371
+ /* @__PURE__ */ jsx14(
1565
1372
  "button",
1566
1373
  {
1567
- type: "button",
1568
- onClick: () => onPageChange(page + 1),
1569
- disabled: !hasNextPage,
1570
- className: "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50",
1571
- children: "Pr\xF3ximo"
1374
+ type: "submit",
1375
+ disabled: isSubmitting || isDisabled,
1376
+ className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 cursor-pointer disabled:pointer-events-none disabled:opacity-50",
1377
+ children: isSubmitting ? "Salvando..." : submitLabel
1572
1378
  }
1573
1379
  )
1574
1380
  ] })
1575
1381
  ] });
1576
1382
  }
1383
+ function FormDialog({ onSubmit, ...rest }) {
1384
+ return /* @__PURE__ */ jsx14(
1385
+ BaseDialog,
1386
+ {
1387
+ ...rest,
1388
+ contentWrapper: onSubmit ? (content) => /* @__PURE__ */ jsx14("form", { onSubmit, className: "flex min-h-0 flex-1 flex-col", children: content }) : void 0
1389
+ }
1390
+ );
1391
+ }
1392
+
1393
+ // src/components/form-field.tsx
1394
+ import { cloneElement, isValidElement, useId } from "react";
1395
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1396
+ function FormField({ label, htmlFor, error, required, children, className }) {
1397
+ const errorId = useId();
1398
+ const field = error && isValidElement(children) ? cloneElement(children, {
1399
+ "aria-invalid": true,
1400
+ "aria-describedby": [
1401
+ children.props["aria-describedby"],
1402
+ errorId
1403
+ ].filter(Boolean).join(" ")
1404
+ }) : children;
1405
+ return /* @__PURE__ */ jsxs14("div", { className: `space-y-2 ${className ?? ""}`, children: [
1406
+ /* @__PURE__ */ jsxs14(
1407
+ "label",
1408
+ {
1409
+ htmlFor,
1410
+ className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
1411
+ children: [
1412
+ label,
1413
+ required && /* @__PURE__ */ jsx15("span", { className: "ml-1 text-destructive", children: "*" })
1414
+ ]
1415
+ }
1416
+ ),
1417
+ field,
1418
+ error && /* @__PURE__ */ jsx15("p", { id: errorId, className: "text-sm text-destructive", children: error })
1419
+ ] });
1420
+ }
1577
1421
 
1578
1422
  // src/components/mobile-card-list.tsx
1579
1423
  import { useRef as useRef3 } from "react";
1580
- import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
1424
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1581
1425
  var SCROLL_THRESHOLD = 8;
1582
1426
  function MobileCardList({
1583
1427
  data,
@@ -1608,407 +1452,533 @@ function MobileCardList({
1608
1452
  }
1609
1453
  };
1610
1454
  if (isLoading) {
1611
- return /* @__PURE__ */ jsx23("div", { className: `space-y-3 ${className ?? ""}`, children: Array.from({ length: loadingCount }).map((_, i) => /* @__PURE__ */ jsx23("div", { className: "rounded-xl border p-4", children: /* @__PURE__ */ jsxs21("div", { className: "space-y-3", children: [
1612
- /* @__PURE__ */ jsxs21("div", { className: "flex justify-between", children: [
1613
- /* @__PURE__ */ jsx23("div", { className: "h-5 w-32 animate-pulse rounded bg-muted" }),
1614
- /* @__PURE__ */ jsx23("div", { className: "h-5 w-16 animate-pulse rounded bg-muted" })
1455
+ return /* @__PURE__ */ jsx16("div", { className: `space-y-3 ${className ?? ""}`, children: Array.from({ length: loadingCount }).map((_, i) => /* @__PURE__ */ jsx16("div", { className: "rounded-xl border p-4", children: /* @__PURE__ */ jsxs15("div", { className: "space-y-3", children: [
1456
+ /* @__PURE__ */ jsxs15("div", { className: "flex justify-between", children: [
1457
+ /* @__PURE__ */ jsx16("div", { className: "h-5 w-32 animate-pulse rounded bg-muted" }),
1458
+ /* @__PURE__ */ jsx16("div", { className: "h-5 w-16 animate-pulse rounded bg-muted" })
1615
1459
  ] }),
1616
- /* @__PURE__ */ jsx23("div", { className: "h-4 w-48 animate-pulse rounded bg-muted" }),
1617
- /* @__PURE__ */ jsxs21("div", { className: "flex justify-between", children: [
1618
- /* @__PURE__ */ jsx23("div", { className: "h-4 w-24 animate-pulse rounded bg-muted" }),
1619
- /* @__PURE__ */ jsx23("div", { className: "h-4 w-20 animate-pulse rounded bg-muted" })
1460
+ /* @__PURE__ */ jsx16("div", { className: "h-4 w-48 animate-pulse rounded bg-muted" }),
1461
+ /* @__PURE__ */ jsxs15("div", { className: "flex justify-between", children: [
1462
+ /* @__PURE__ */ jsx16("div", { className: "h-4 w-24 animate-pulse rounded bg-muted" }),
1463
+ /* @__PURE__ */ jsx16("div", { className: "h-4 w-20 animate-pulse rounded bg-muted" })
1620
1464
  ] })
1621
- ] }) }, i)) });
1622
- }
1623
- return /* @__PURE__ */ jsx23(
1624
- "div",
1625
- {
1626
- className: `space-y-3 ${className ?? ""}`,
1627
- onTouchStart,
1628
- onTouchMove,
1629
- onClickCapture,
1630
- children: data.map((item, index) => /* @__PURE__ */ jsx23(
1631
- "div",
1632
- {
1633
- className: "rounded-xl border p-4 transition-all duration-150 hover:border-foreground/20 active:scale-[0.99]",
1634
- children: renderCard(item, index)
1635
- },
1636
- keyExtractor(item)
1637
- ))
1638
- }
1639
- );
1640
- }
1641
-
1642
- // src/components/responsive-data-view.tsx
1643
- import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
1644
- function ResponsiveDataView({
1645
- table,
1646
- cards,
1647
- isEmpty,
1648
- isLoading,
1649
- emptyIcon,
1650
- emptyTitle = "Nenhum registro encontrado",
1651
- emptyDescription,
1652
- pagination
1653
- }) {
1654
- return /* @__PURE__ */ jsxs22("div", { className: "space-y-4", children: [
1655
- /* @__PURE__ */ jsx24("div", { className: "hidden overflow-x-auto rounded-md border md:block", children: table }),
1656
- /* @__PURE__ */ jsx24("div", { className: "md:hidden", children: cards }),
1657
- !isLoading && isEmpty && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
1658
- emptyIcon && /* @__PURE__ */ jsx24("div", { className: "mb-4 text-muted-foreground", children: emptyIcon }),
1659
- /* @__PURE__ */ jsx24("h3", { className: "text-lg font-semibold tracking-tight", children: emptyTitle }),
1660
- emptyDescription && /* @__PURE__ */ jsx24("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: emptyDescription })
1661
- ] }),
1662
- pagination
1663
- ] });
1664
- }
1665
-
1666
- // src/components/toaster.tsx
1667
- import { Toaster as Sonner } from "sonner";
1668
- import { jsx as jsx25 } from "react/jsx-runtime";
1669
- function Toaster(props) {
1670
- const { theme = "system" } = useTheme();
1671
- return /* @__PURE__ */ jsx25(
1672
- Sonner,
1673
- {
1674
- theme,
1675
- className: "toaster group",
1676
- toastOptions: {
1677
- classNames: {
1678
- toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-md",
1679
- description: "group-[.toast]:text-muted-foreground",
1680
- actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
1681
- cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
1682
- success: "group-[.toaster]:!bg-background group-[.toaster]:!text-foreground group-[.toaster]:!border-success/40",
1683
- error: "group-[.toaster]:!bg-background group-[.toaster]:!text-foreground group-[.toaster]:!border-destructive/40"
1684
- }
1685
- },
1686
- ...props
1465
+ ] }) }, i)) });
1466
+ }
1467
+ return /* @__PURE__ */ jsx16(
1468
+ "div",
1469
+ {
1470
+ className: `space-y-3 ${className ?? ""}`,
1471
+ onTouchStart,
1472
+ onTouchMove,
1473
+ onClickCapture,
1474
+ children: data.map((item, index) => /* @__PURE__ */ jsx16(
1475
+ "div",
1476
+ {
1477
+ className: "rounded-xl border p-4 transition-all duration-150 hover:border-foreground/20 active:scale-[0.99]",
1478
+ children: renderCard(item, index)
1479
+ },
1480
+ keyExtractor(item)
1481
+ ))
1687
1482
  }
1688
1483
  );
1689
1484
  }
1690
1485
 
1691
- // src/components/app-shell.tsx
1692
- import { ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight2, Menu } from "lucide-react";
1693
- import { useState as useState10 } from "react";
1694
-
1695
- // src/components/sidebar-nav.tsx
1696
- import { Fragment as Fragment4 } from "react";
1486
+ // src/components/mode-toggle.tsx
1487
+ import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
1488
+ import { Moon, Sun, Monitor } from "lucide-react";
1697
1489
 
1698
- // src/components/types.tsx
1699
- import { jsx as jsx26 } from "react/jsx-runtime";
1700
- var defaultRenderLink = ({ children, ...props }) => /* @__PURE__ */ jsx26("a", { ...props, children });
1490
+ // src/components/theme-provider.tsx
1491
+ import { createContext, useContext, useEffect as useEffect4, useState as useState8 } from "react";
1701
1492
 
1702
- // src/components/sidebar-nav.tsx
1703
- import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
1704
- function defaultIsActive(item, activePath) {
1705
- if (item.href === "/") return activePath === "/";
1706
- return activePath === item.href || activePath.startsWith(item.href + "/");
1493
+ // src/lib/company-theme.ts
1494
+ var VAR_MAP = {
1495
+ primary: "--color-primary",
1496
+ primaryForeground: "--color-primary-foreground",
1497
+ secondary: "--color-secondary",
1498
+ secondaryForeground: "--color-secondary-foreground",
1499
+ accent: "--color-accent",
1500
+ accentForeground: "--color-accent-foreground",
1501
+ ring: "--color-ring"
1502
+ };
1503
+ function definedTokens(tokens) {
1504
+ if (!tokens) return {};
1505
+ return Object.fromEntries(
1506
+ Object.entries(tokens).filter(([, value]) => value != null)
1507
+ );
1707
1508
  }
1708
- function SidebarNav({
1709
- items,
1710
- activePath,
1711
- isActive = defaultIsActive,
1712
- isAdmin = false,
1713
- isCollapsed = false,
1714
- renderLink = defaultRenderLink,
1715
- onNavigate
1509
+ function applyCompanyTheme(root, companyTheme, mode) {
1510
+ const tokens = companyTheme ? mode === "dark" ? { ...companyTheme.light, ...definedTokens(companyTheme.dark) } : companyTheme.light : {};
1511
+ for (const key of Object.keys(VAR_MAP)) {
1512
+ const value = tokens[key];
1513
+ if (value) root.style.setProperty(VAR_MAP[key], value);
1514
+ else root.style.removeProperty(VAR_MAP[key]);
1515
+ }
1516
+ }
1517
+
1518
+ // src/components/theme-provider.tsx
1519
+ import { jsx as jsx17 } from "react/jsx-runtime";
1520
+ var initialState = {
1521
+ theme: "system",
1522
+ setTheme: () => null
1523
+ };
1524
+ var ThemeProviderContext = createContext(initialState);
1525
+ function ThemeProvider({
1526
+ children,
1527
+ defaultTheme = "system",
1528
+ storageKey = "ui-theme",
1529
+ companyTheme,
1530
+ ...props
1716
1531
  }) {
1717
- const visible = items.filter((item) => !item.adminOnly || isAdmin);
1718
- return /* @__PURE__ */ jsx27("nav", { className: "flex flex-col gap-1", children: visible.map((item) => {
1719
- const active = isActive(item, activePath);
1720
- const link = renderLink({
1721
- href: item.href,
1722
- onClick: onNavigate,
1723
- title: isCollapsed ? item.label : void 0,
1724
- "aria-current": active ? "page" : void 0,
1725
- className: cn(
1726
- "flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
1727
- active ? "bg-accent text-accent-foreground font-semibold" : "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
1728
- isCollapsed && "justify-center px-2"
1729
- ),
1730
- children: /* @__PURE__ */ jsxs23(Fragment5, { children: [
1731
- item.icon && /* @__PURE__ */ jsx27("span", { className: "shrink-0", children: item.icon }),
1732
- !isCollapsed && /* @__PURE__ */ jsx27("span", { className: "truncate", children: item.label })
1733
- ] })
1734
- });
1735
- return /* @__PURE__ */ jsx27(Fragment4, { children: link }, item.href);
1736
- }) });
1532
+ const [theme, setTheme] = useState8(
1533
+ () => localStorage.getItem(storageKey) || defaultTheme
1534
+ );
1535
+ useEffect4(() => {
1536
+ const root = window.document.documentElement;
1537
+ const applyResolved = (resolved) => {
1538
+ root.classList.remove("light", "dark");
1539
+ root.classList.add(resolved);
1540
+ applyCompanyTheme(root, companyTheme, resolved);
1541
+ };
1542
+ if (theme === "system") {
1543
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
1544
+ applyResolved(mql.matches ? "dark" : "light");
1545
+ const onChange = (e) => applyResolved(e.matches ? "dark" : "light");
1546
+ mql.addEventListener("change", onChange);
1547
+ return () => mql.removeEventListener("change", onChange);
1548
+ }
1549
+ applyResolved(theme);
1550
+ }, [theme, companyTheme]);
1551
+ const value = {
1552
+ theme,
1553
+ setTheme: (theme2) => {
1554
+ localStorage.setItem(storageKey, theme2);
1555
+ setTheme(theme2);
1556
+ }
1557
+ };
1558
+ return /* @__PURE__ */ jsx17(ThemeProviderContext.Provider, { ...props, value, children });
1559
+ }
1560
+ function useTheme() {
1561
+ const context = useContext(ThemeProviderContext);
1562
+ if (context === void 0) {
1563
+ throw new Error("useTheme must be used within a ThemeProvider");
1564
+ }
1565
+ return context;
1737
1566
  }
1738
1567
 
1739
- // src/components/user-menu.tsx
1740
- import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
1741
- import { LogOut, User } from "lucide-react";
1742
- import { useState as useState9 } from "react";
1743
- import { Drawer as DrawerPrimitive4 } from "vaul";
1744
- import { Fragment as Fragment6, jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
1745
- function initials(user) {
1746
- const source = user?.name?.trim() || user?.email?.trim();
1747
- if (!source) return "";
1748
- const parts = source.split(/\s+/);
1749
- if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
1750
- return source.slice(0, 2).toUpperCase();
1568
+ // src/components/mode-toggle.tsx
1569
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1570
+ function ModeToggle() {
1571
+ const { setTheme } = useTheme();
1572
+ return /* @__PURE__ */ jsxs16(DropdownMenu2.Root, { children: [
1573
+ /* @__PURE__ */ jsx18(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs16(
1574
+ "button",
1575
+ {
1576
+ type: "button",
1577
+ className: "relative inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1578
+ "aria-label": "Alternar tema",
1579
+ children: [
1580
+ /* @__PURE__ */ jsx18(Sun, { className: "h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }),
1581
+ /* @__PURE__ */ jsx18(Moon, { className: "absolute inset-0 m-auto h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" })
1582
+ ]
1583
+ }
1584
+ ) }),
1585
+ /* @__PURE__ */ jsx18(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs16(
1586
+ DropdownMenu2.Content,
1587
+ {
1588
+ align: "end",
1589
+ className: "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
1590
+ children: [
1591
+ /* @__PURE__ */ jsxs16(
1592
+ DropdownMenu2.Item,
1593
+ {
1594
+ className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
1595
+ onClick: () => setTheme("light"),
1596
+ children: [
1597
+ /* @__PURE__ */ jsx18(Sun, { className: "h-4 w-4" }),
1598
+ " Claro"
1599
+ ]
1600
+ }
1601
+ ),
1602
+ /* @__PURE__ */ jsxs16(
1603
+ DropdownMenu2.Item,
1604
+ {
1605
+ className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
1606
+ onClick: () => setTheme("dark"),
1607
+ children: [
1608
+ /* @__PURE__ */ jsx18(Moon, { className: "h-4 w-4" }),
1609
+ " Escuro"
1610
+ ]
1611
+ }
1612
+ ),
1613
+ /* @__PURE__ */ jsxs16(
1614
+ DropdownMenu2.Item,
1615
+ {
1616
+ className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent",
1617
+ onClick: () => setTheme("system"),
1618
+ children: [
1619
+ /* @__PURE__ */ jsx18(Monitor, { className: "h-4 w-4" }),
1620
+ " Sistema"
1621
+ ]
1622
+ }
1623
+ )
1624
+ ]
1625
+ }
1626
+ ) })
1627
+ ] });
1751
1628
  }
1752
- var itemClass = "flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent";
1753
- var sheetItemClass = "flex w-full cursor-pointer select-none items-center gap-3 rounded-md px-3 py-3 text-base outline-none transition-colors hover:bg-accent active:bg-accent";
1754
- function UserMenu({
1755
- user,
1756
- items = [],
1757
- onLogout,
1758
- logoutLabel = "Sair",
1759
- renderLink = defaultRenderLink,
1760
- align = "end"
1629
+
1630
+ // src/components/multi-combobox.tsx
1631
+ import * as PopoverPrimitive5 from "@radix-ui/react-popover";
1632
+ import { Command as CommandPrimitive2 } from "cmdk";
1633
+ import { Check as Check2, ChevronsUpDown as ChevronsUpDown3, Loader2 as Loader22, Search as Search2, X as X4 } from "lucide-react";
1634
+ import * as React8 from "react";
1635
+ import { Drawer as DrawerPrimitive5 } from "vaul";
1636
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
1637
+ function MultiCombobox({
1638
+ options,
1639
+ value,
1640
+ onChange,
1641
+ placeholder = "Selecione...",
1642
+ searchPlaceholder = "Buscar...",
1643
+ emptyMessage = "Nenhum resultado encontrado.",
1644
+ disabled,
1645
+ id,
1646
+ className,
1647
+ "aria-invalid": ariaInvalid,
1648
+ "aria-describedby": ariaDescribedby,
1649
+ onSearchChange,
1650
+ onLoadMore,
1651
+ loading,
1652
+ hasMore,
1653
+ selectedOptions
1761
1654
  }) {
1655
+ const [open, setOpen] = React8.useState(false);
1762
1656
  const isDesktop = useIsDesktop();
1763
- const [open, setOpen] = useState9(false);
1764
- const label = initials(user);
1765
- const trigger = /* @__PURE__ */ jsx28(
1766
- "button",
1657
+ const isServer = onSearchChange !== void 0;
1658
+ const seen = /* @__PURE__ */ new Set();
1659
+ const selected = [...selectedOptions ?? [], ...options].filter((option) => {
1660
+ if (!value.includes(option.value) || seen.has(option.value)) return false;
1661
+ seen.add(option.value);
1662
+ return true;
1663
+ });
1664
+ const { listRef, sentinelRef } = useInfiniteScrollSentinel({
1665
+ enabled: isServer && !!hasMore,
1666
+ loading,
1667
+ onLoadMore
1668
+ });
1669
+ const toggleValue = (optionValue) => {
1670
+ onChange(
1671
+ value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
1672
+ );
1673
+ };
1674
+ const removeValue = (optionValue) => {
1675
+ onChange(value.filter((v) => v !== optionValue));
1676
+ };
1677
+ const trigger = /* @__PURE__ */ jsxs17(
1678
+ "div",
1767
1679
  {
1768
- type: "button",
1769
- className: "inline-flex h-9 w-9 items-center justify-center overflow-hidden rounded-full border border-input bg-background text-xs font-semibold shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1770
- "aria-label": "Menu do usu\xE1rio",
1771
- "data-testid": "user-menu",
1772
- children: user?.avatarUrl ? /* @__PURE__ */ jsx28(
1773
- "img",
1774
- {
1775
- src: user.avatarUrl,
1776
- alt: user.name ?? user.email ?? "Avatar",
1777
- className: "h-full w-full object-cover"
1680
+ id,
1681
+ role: "combobox",
1682
+ tabIndex: disabled ? -1 : 0,
1683
+ "aria-expanded": open,
1684
+ "aria-invalid": ariaInvalid,
1685
+ "aria-describedby": ariaDescribedby,
1686
+ "aria-disabled": disabled,
1687
+ onKeyDown: (event) => {
1688
+ if (disabled) return;
1689
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
1690
+ event.preventDefault();
1691
+ setOpen(true);
1778
1692
  }
1779
- ) : label ? /* @__PURE__ */ jsx28("span", { children: label }) : /* @__PURE__ */ jsx28(User, { className: "h-4 w-4" })
1693
+ },
1694
+ className: cn(
1695
+ "flex min-h-9 w-full cursor-pointer flex-wrap items-center gap-1 rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-sm transition-colors hover:border-ring/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive",
1696
+ className
1697
+ ),
1698
+ children: [
1699
+ selected.length === 0 ? /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: placeholder }) : selected.map((option) => /* @__PURE__ */ jsxs17(
1700
+ "span",
1701
+ {
1702
+ className: "inline-flex items-center gap-1 rounded bg-secondary px-1.5 py-0.5 text-xs text-secondary-foreground",
1703
+ children: [
1704
+ option.label,
1705
+ /* @__PURE__ */ jsx19(
1706
+ "button",
1707
+ {
1708
+ type: "button",
1709
+ tabIndex: -1,
1710
+ "aria-label": `Remover ${option.label}`,
1711
+ onClick: (event) => {
1712
+ event.stopPropagation();
1713
+ removeValue(option.value);
1714
+ },
1715
+ onPointerDown: (event) => event.stopPropagation(),
1716
+ className: "rounded-sm opacity-60 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1717
+ children: /* @__PURE__ */ jsx19(X4, { className: "h-3 w-3" })
1718
+ }
1719
+ )
1720
+ ]
1721
+ },
1722
+ option.value
1723
+ )),
1724
+ /* @__PURE__ */ jsx19(ChevronsUpDown3, { className: "ml-auto h-4 w-4 shrink-0 opacity-50" })
1725
+ ]
1780
1726
  }
1781
1727
  );
1782
- const userInfo = (user?.name || user?.email) && /* @__PURE__ */ jsxs24("div", { className: "px-2 py-1.5", children: [
1783
- user.name && /* @__PURE__ */ jsx28("p", { className: "truncate text-sm font-medium", children: user.name }),
1784
- user.email && /* @__PURE__ */ jsx28("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
1785
- ] });
1786
- if (isDesktop) {
1787
- return /* @__PURE__ */ jsxs24(DropdownMenu2.Root, { open, onOpenChange: setOpen, children: [
1788
- /* @__PURE__ */ jsx28(DropdownMenu2.Trigger, { asChild: true, children: trigger }),
1789
- /* @__PURE__ */ jsx28(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs24(
1790
- DropdownMenu2.Content,
1791
- {
1792
- align,
1793
- sideOffset: 6,
1794
- className: "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
1795
- children: [
1796
- userInfo && /* @__PURE__ */ jsxs24(Fragment6, { children: [
1797
- userInfo,
1798
- /* @__PURE__ */ jsx28(DropdownMenu2.Separator, { className: "-mx-1 my-1 h-px bg-border" })
1799
- ] }),
1800
- items.map(
1801
- (item) => item.href ? /* @__PURE__ */ jsx28(DropdownMenu2.Item, { asChild: true, children: renderLink({
1802
- href: item.href,
1803
- className: itemClass,
1804
- children: /* @__PURE__ */ jsxs24(Fragment6, { children: [
1805
- item.icon,
1806
- item.label
1807
- ] })
1808
- }) }, item.label) : /* @__PURE__ */ jsxs24(
1809
- DropdownMenu2.Item,
1728
+ const command = /* @__PURE__ */ jsxs17(
1729
+ CommandPrimitive2,
1730
+ {
1731
+ shouldFilter: !isServer,
1732
+ className: "flex h-full w-full flex-col overflow-hidden",
1733
+ children: [
1734
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center border-b px-3", children: [
1735
+ /* @__PURE__ */ jsx19(Search2, { className: "mr-2 h-4 w-4 shrink-0 opacity-50" }),
1736
+ /* @__PURE__ */ jsx19(
1737
+ CommandPrimitive2.Input,
1738
+ {
1739
+ placeholder: searchPlaceholder,
1740
+ onValueChange: isServer ? onSearchChange : void 0,
1741
+ className: "flex h-11 w-full bg-transparent py-2 text-base outline-none placeholder:text-muted-foreground sm:h-9 sm:text-sm"
1742
+ }
1743
+ )
1744
+ ] }),
1745
+ /* @__PURE__ */ jsxs17(
1746
+ CommandPrimitive2.List,
1747
+ {
1748
+ ref: listRef,
1749
+ className: "max-h-[50vh] overflow-y-auto overflow-x-hidden p-1 sm:max-h-60",
1750
+ children: [
1751
+ !isServer && /* @__PURE__ */ jsx19(CommandPrimitive2.Empty, { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
1752
+ options.map((option) => /* @__PURE__ */ jsxs17(
1753
+ CommandPrimitive2.Item,
1810
1754
  {
1811
- className: itemClass,
1812
- onClick: item.onClick,
1755
+ value: isServer ? option.value : option.label,
1756
+ disabled: option.disabled,
1757
+ onSelect: () => toggleValue(option.value),
1758
+ className: "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-2.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 sm:py-1.5",
1813
1759
  children: [
1814
- item.icon,
1815
- item.label
1760
+ /* @__PURE__ */ jsx19(
1761
+ Check2,
1762
+ {
1763
+ className: cn(
1764
+ "h-4 w-4 shrink-0",
1765
+ value.includes(option.value) ? "opacity-100" : "opacity-0"
1766
+ )
1767
+ }
1768
+ ),
1769
+ /* @__PURE__ */ jsx19("span", { className: "truncate", children: option.label })
1816
1770
  ]
1817
1771
  },
1818
- item.label
1819
- )
1820
- ),
1821
- onLogout && /* @__PURE__ */ jsxs24(Fragment6, { children: [
1822
- items.length > 0 && /* @__PURE__ */ jsx28(DropdownMenu2.Separator, { className: "-mx-1 my-1 h-px bg-border" }),
1823
- /* @__PURE__ */ jsxs24(
1824
- DropdownMenu2.Item,
1825
- {
1826
- className: cn(
1827
- itemClass,
1828
- "text-destructive focus:bg-destructive/10"
1829
- ),
1830
- onClick: onLogout,
1831
- children: [
1832
- /* @__PURE__ */ jsx28(LogOut, { className: "h-4 w-4" }),
1833
- logoutLabel
1834
- ]
1835
- }
1836
- )
1837
- ] })
1838
- ]
1772
+ option.value
1773
+ )),
1774
+ isServer && !loading && options.length === 0 && /* @__PURE__ */ jsx19("div", { className: "py-6 text-center text-sm text-muted-foreground", children: emptyMessage }),
1775
+ isServer && loading && /* @__PURE__ */ jsx19("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx19(Loader22, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }),
1776
+ isServer && hasMore && /* @__PURE__ */ jsx19("div", { ref: sentinelRef, "aria-hidden": true, className: "h-1" })
1777
+ ]
1778
+ }
1779
+ )
1780
+ ]
1781
+ }
1782
+ );
1783
+ if (isDesktop) {
1784
+ return /* @__PURE__ */ jsxs17(PopoverPrimitive5.Root, { open, onOpenChange: setOpen, children: [
1785
+ /* @__PURE__ */ jsx19(PopoverPrimitive5.Trigger, { asChild: true, disabled, children: trigger }),
1786
+ /* @__PURE__ */ jsx19(PopoverPrimitive5.Portal, { children: /* @__PURE__ */ jsx19(
1787
+ PopoverPrimitive5.Content,
1788
+ {
1789
+ align: "start",
1790
+ sideOffset: 4,
1791
+ className: "pointer-events-auto z-50 overflow-hidden rounded-md border bg-popover p-0 text-popover-foreground shadow-md",
1792
+ style: { width: "var(--radix-popover-trigger-width)" },
1793
+ children: command
1839
1794
  }
1840
1795
  ) })
1841
1796
  ] });
1842
1797
  }
1843
- return /* @__PURE__ */ jsxs24(DrawerPrimitive4.Root, { open, onOpenChange: setOpen, children: [
1844
- /* @__PURE__ */ jsx28(DrawerPrimitive4.Trigger, { asChild: true, children: trigger }),
1845
- /* @__PURE__ */ jsxs24(DrawerPrimitive4.Portal, { children: [
1846
- /* @__PURE__ */ jsx28(DrawerPrimitive4.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1847
- /* @__PURE__ */ jsxs24(DrawerPrimitive4.Content, { className: "fixed inset-x-0 bottom-0 z-50 flex flex-col rounded-t-2xl border bg-popover p-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-none", children: [
1848
- /* @__PURE__ */ jsx28(DrawerPrimitive4.Title, { className: "sr-only", children: "Menu do usu\xE1rio" }),
1849
- /* @__PURE__ */ jsx28("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1850
- userInfo && /* @__PURE__ */ jsxs24(Fragment6, { children: [
1851
- userInfo,
1852
- /* @__PURE__ */ jsx28("div", { className: "-mx-1 my-1 h-px bg-border" })
1798
+ return /* @__PURE__ */ jsxs17(DrawerPrimitive5.Root, { open, onOpenChange: setOpen, children: [
1799
+ /* @__PURE__ */ jsx19(DrawerPrimitive5.Trigger, { asChild: true, disabled, children: trigger }),
1800
+ /* @__PURE__ */ jsxs17(DrawerPrimitive5.Portal, { children: [
1801
+ /* @__PURE__ */ jsx19(DrawerPrimitive5.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
1802
+ /* @__PURE__ */ jsxs17(DrawerPrimitive5.Content, { className: "fixed inset-x-0 bottom-0 z-50 mt-24 flex max-h-[85vh] flex-col rounded-t-2xl border bg-popover text-popover-foreground outline-none", children: [
1803
+ /* @__PURE__ */ jsx19(DrawerPrimitive5.Title, { className: "sr-only", children: placeholder }),
1804
+ /* @__PURE__ */ jsx19("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
1805
+ command
1806
+ ] })
1807
+ ] })
1808
+ ] });
1809
+ }
1810
+
1811
+ // src/components/page-header.tsx
1812
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1813
+ function PageHeader({ title, description, action, className }) {
1814
+ return /* @__PURE__ */ jsxs18(
1815
+ "div",
1816
+ {
1817
+ className: `flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between ${className ?? ""}`,
1818
+ children: [
1819
+ /* @__PURE__ */ jsxs18("div", { className: "min-w-0", children: [
1820
+ /* @__PURE__ */ jsx20("h1", { className: "truncate text-2xl font-semibold tracking-tight", children: title }),
1821
+ description && /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: description })
1853
1822
  ] }),
1854
- items.map(
1855
- (item) => item.href ? /* @__PURE__ */ jsx28(
1856
- "span",
1823
+ action && /* @__PURE__ */ jsx20("div", { className: "shrink-0", children: action })
1824
+ ]
1825
+ }
1826
+ );
1827
+ }
1828
+
1829
+ // src/components/pagination.tsx
1830
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
1831
+ var navButtonClass2 = "inline-flex h-8 items-center justify-center rounded-md border border-input bg-background px-3 text-xs font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer disabled:pointer-events-none disabled:opacity-50";
1832
+ function Pagination({
1833
+ page,
1834
+ onPageChange,
1835
+ hasNextPage,
1836
+ hasPreviousPage,
1837
+ totalPages,
1838
+ showPageSizeSelector = false,
1839
+ pageSize,
1840
+ onPageSizeChange,
1841
+ pageSizeOptions,
1842
+ className
1843
+ }) {
1844
+ return /* @__PURE__ */ jsxs19(
1845
+ "div",
1846
+ {
1847
+ className: `flex items-center ${showPageSizeSelector ? "justify-between" : "justify-end"} gap-4 ${className ?? ""}`,
1848
+ children: [
1849
+ showPageSizeSelector && /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
1850
+ /* @__PURE__ */ jsx21("label", { htmlFor: "pagination-page-size", className: "text-sm text-muted-foreground", children: "Itens por p\xE1gina" }),
1851
+ /* @__PURE__ */ jsx21(
1852
+ "select",
1857
1853
  {
1858
- onClick: () => setOpen(false),
1859
- onKeyDown: () => setOpen(false),
1860
- children: renderLink({
1861
- href: item.href,
1862
- className: sheetItemClass,
1863
- children: /* @__PURE__ */ jsxs24(Fragment6, { children: [
1864
- item.icon,
1865
- item.label
1866
- ] })
1867
- })
1868
- },
1869
- item.label
1870
- ) : /* @__PURE__ */ jsxs24(
1854
+ id: "pagination-page-size",
1855
+ value: pageSize,
1856
+ onChange: (e) => onPageSizeChange?.(Number(e.target.value)),
1857
+ className: "h-8 rounded-md border border-input bg-background px-2 text-xs font-medium shadow-sm transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer",
1858
+ children: (pageSizeOptions ?? []).map((size) => /* @__PURE__ */ jsx21("option", { value: size, children: size }, size))
1859
+ }
1860
+ )
1861
+ ] }),
1862
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-4", children: [
1863
+ /* @__PURE__ */ jsx21(
1871
1864
  "button",
1872
1865
  {
1873
1866
  type: "button",
1874
- className: sheetItemClass,
1875
- onClick: () => {
1876
- item.onClick?.();
1877
- setOpen(false);
1878
- },
1879
- children: [
1880
- item.icon,
1881
- item.label
1882
- ]
1883
- },
1884
- item.label
1885
- )
1886
- ),
1887
- onLogout && /* @__PURE__ */ jsxs24(Fragment6, { children: [
1888
- items.length > 0 && /* @__PURE__ */ jsx28("div", { className: "-mx-1 my-1 h-px bg-border" }),
1889
- /* @__PURE__ */ jsxs24(
1867
+ onClick: () => onPageChange(page - 1),
1868
+ disabled: !hasPreviousPage,
1869
+ className: navButtonClass2,
1870
+ children: "Anterior"
1871
+ }
1872
+ ),
1873
+ /* @__PURE__ */ jsxs19("span", { className: "text-sm text-muted-foreground", children: [
1874
+ "P\xE1gina ",
1875
+ page,
1876
+ totalPages ? ` de ${totalPages}` : ""
1877
+ ] }),
1878
+ /* @__PURE__ */ jsx21(
1890
1879
  "button",
1891
1880
  {
1892
1881
  type: "button",
1893
- className: cn(sheetItemClass, "text-destructive"),
1894
- onClick: () => {
1895
- onLogout();
1896
- setOpen(false);
1897
- },
1898
- children: [
1899
- /* @__PURE__ */ jsx28(LogOut, { className: "h-4 w-4" }),
1900
- logoutLabel
1901
- ]
1882
+ onClick: () => onPageChange(page + 1),
1883
+ disabled: !hasNextPage,
1884
+ className: navButtonClass2,
1885
+ children: "Pr\xF3ximo"
1902
1886
  }
1903
1887
  )
1904
1888
  ] })
1905
- ] })
1906
- ] })
1907
- ] });
1908
- }
1909
-
1910
- // src/components/app-shell.tsx
1911
- import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
1912
- function AppShell({
1913
- brand,
1914
- brandCollapsed,
1915
- navItems,
1916
- activePath,
1917
- isActive,
1918
- isAdmin = false,
1919
- user,
1920
- userMenuItems,
1921
- onLogout,
1922
- renderLink = defaultRenderLink,
1923
- headerActions,
1924
- collapsible = true,
1925
- defaultCollapsed = false,
1926
- children
1927
- }) {
1928
- const [collapsed, setCollapsed] = useState10(defaultCollapsed);
1929
- const [mobileOpen, setMobileOpen] = useState10(false);
1930
- const nav = (isCollapsed, onNavigate) => /* @__PURE__ */ jsx29(
1931
- SidebarNav,
1932
- {
1933
- items: navItems,
1934
- activePath,
1935
- isActive,
1936
- isAdmin,
1937
- isCollapsed,
1938
- renderLink,
1939
- onNavigate
1889
+ ]
1940
1890
  }
1941
1891
  );
1942
- return /* @__PURE__ */ jsxs25("div", { className: "flex h-screen overflow-hidden bg-background", children: [
1943
- /* @__PURE__ */ jsxs25(
1944
- "aside",
1892
+ }
1893
+
1894
+ // src/components/password-input.tsx
1895
+ import { Eye, EyeOff } from "lucide-react";
1896
+ import * as React9 from "react";
1897
+ import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
1898
+ var PasswordInput = React9.forwardRef(({ containerClassName, className, ...props }, ref) => {
1899
+ const [visible, setVisible] = React9.useState(false);
1900
+ return /* @__PURE__ */ jsxs20("div", { className: `relative ${containerClassName ?? ""}`, children: [
1901
+ /* @__PURE__ */ jsx22(
1902
+ "input",
1945
1903
  {
1946
- className: cn(
1947
- "hidden shrink-0 flex-col border-r bg-card transition-all duration-300 md:flex",
1948
- collapsed ? "w-16" : "w-60"
1949
- ),
1950
- children: [
1951
- /* @__PURE__ */ jsx29("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: collapsed ? brandCollapsed ?? brand : brand }),
1952
- /* @__PURE__ */ jsx29("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(collapsed) }),
1953
- collapsible && /* @__PURE__ */ jsx29("div", { className: "border-t p-2", children: /* @__PURE__ */ jsx29(
1954
- "button",
1955
- {
1956
- type: "button",
1957
- onClick: () => setCollapsed((v) => !v),
1958
- className: "flex w-full items-center justify-center rounded-md px-2 py-2 text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground",
1959
- "aria-label": collapsed ? "Expandir menu" : "Recolher menu",
1960
- children: collapsed ? /* @__PURE__ */ jsx29(ChevronRight2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx29(ChevronLeft2, { className: "h-4 w-4" })
1961
- }
1962
- ) })
1963
- ]
1904
+ ref,
1905
+ type: visible ? "text" : "password",
1906
+ className: `flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 pr-10 text-base shadow-sm transition-all duration-200 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive md:text-sm ${className ?? ""}`,
1907
+ ...props
1908
+ }
1909
+ ),
1910
+ /* @__PURE__ */ jsx22(
1911
+ "button",
1912
+ {
1913
+ type: "button",
1914
+ onClick: () => setVisible((prev) => !prev),
1915
+ "aria-label": visible ? "Ocultar senha" : "Mostrar senha",
1916
+ tabIndex: -1,
1917
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground",
1918
+ children: visible ? /* @__PURE__ */ jsx22(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx22(Eye, { className: "h-4 w-4" })
1964
1919
  }
1965
- ),
1966
- /* @__PURE__ */ jsxs25(
1920
+ )
1921
+ ] });
1922
+ });
1923
+ PasswordInput.displayName = "PasswordInput";
1924
+
1925
+ // src/components/progress-bar.tsx
1926
+ import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
1927
+ var sizeClasses2 = {
1928
+ sm: "h-1.5",
1929
+ md: "h-2"
1930
+ };
1931
+ function ProgressBar({
1932
+ value,
1933
+ size = "md",
1934
+ label,
1935
+ valueLabel,
1936
+ className,
1937
+ indicatorClassName
1938
+ }) {
1939
+ const clamped = Math.min(100, Math.max(0, value));
1940
+ return /* @__PURE__ */ jsxs21("div", { className: `space-y-1.5 ${className ?? ""}`, children: [
1941
+ (label || valueLabel) && /* @__PURE__ */ jsxs21("div", { className: "flex items-baseline justify-between gap-2", children: [
1942
+ label && /* @__PURE__ */ jsx23("span", { className: "text-sm text-muted-foreground", children: label }),
1943
+ valueLabel && /* @__PURE__ */ jsx23("span", { className: "text-sm font-semibold", children: valueLabel })
1944
+ ] }),
1945
+ /* @__PURE__ */ jsx23(
1967
1946
  "div",
1968
1947
  {
1969
- className: cn(
1970
- "fixed inset-0 z-50 transition-opacity duration-300 md:hidden",
1971
- mobileOpen ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
1972
- ),
1973
- onClick: () => setMobileOpen(false),
1974
- children: [
1975
- /* @__PURE__ */ jsx29("div", { className: "absolute inset-0 bg-black/50" }),
1976
- /* @__PURE__ */ jsxs25(
1977
- "div",
1978
- {
1979
- className: cn(
1980
- "absolute left-0 top-0 flex h-full w-64 flex-col border-r bg-card transition-transform duration-300",
1981
- mobileOpen ? "translate-x-0" : "-translate-x-full"
1982
- ),
1983
- onClick: (e) => e.stopPropagation(),
1984
- children: [
1985
- /* @__PURE__ */ jsx29("div", { className: "flex h-14 shrink-0 items-center border-b px-4", children: brand }),
1986
- /* @__PURE__ */ jsx29("div", { className: "flex-1 overflow-y-auto px-2 py-4", children: nav(false, () => setMobileOpen(false)) })
1987
- ]
1988
- }
1989
- )
1990
- ]
1991
- }
1992
- ),
1993
- /* @__PURE__ */ jsxs25("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
1994
- /* @__PURE__ */ jsxs25("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b bg-background px-4", children: [
1995
- /* @__PURE__ */ jsx29(
1996
- "button",
1948
+ className: `w-full overflow-hidden rounded-full bg-muted ${sizeClasses2[size]}`,
1949
+ children: /* @__PURE__ */ jsx23(
1950
+ "div",
1997
1951
  {
1998
- type: "button",
1999
- onClick: () => setMobileOpen(true),
2000
- className: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-input bg-background shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground md:hidden",
2001
- "aria-label": "Abrir menu",
2002
- children: /* @__PURE__ */ jsx29(Menu, { className: "h-4 w-4" })
1952
+ className: `h-full rounded-full transition-all duration-300 ${indicatorClassName ?? "bg-primary"}`,
1953
+ style: { width: `${clamped}%` }
2003
1954
  }
2004
- ),
2005
- /* @__PURE__ */ jsxs25("div", { className: "ml-auto flex items-center gap-2", children: [
2006
- headerActions,
2007
- (user || onLogout) && /* @__PURE__ */ jsx29(UserMenu, { user, items: userMenuItems, onLogout, renderLink })
2008
- ] })
2009
- ] }),
2010
- /* @__PURE__ */ jsx29("main", { className: "flex-1 overflow-y-auto", children })
2011
- ] })
1955
+ )
1956
+ }
1957
+ )
1958
+ ] });
1959
+ }
1960
+
1961
+ // src/components/responsive-data-view.tsx
1962
+ import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
1963
+ function ResponsiveDataView({
1964
+ table,
1965
+ cards,
1966
+ isEmpty,
1967
+ isLoading,
1968
+ emptyIcon,
1969
+ emptyTitle = "Nenhum registro encontrado",
1970
+ emptyDescription,
1971
+ pagination
1972
+ }) {
1973
+ return /* @__PURE__ */ jsxs22("div", { className: "space-y-4", children: [
1974
+ /* @__PURE__ */ jsx24("div", { className: "hidden overflow-x-auto rounded-md border md:block", children: table }),
1975
+ /* @__PURE__ */ jsx24("div", { className: "md:hidden", children: cards }),
1976
+ !isLoading && isEmpty && /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
1977
+ emptyIcon && /* @__PURE__ */ jsx24("div", { className: "mb-4 text-muted-foreground", children: emptyIcon }),
1978
+ /* @__PURE__ */ jsx24("h3", { className: "text-lg font-semibold tracking-tight", children: emptyTitle }),
1979
+ emptyDescription && /* @__PURE__ */ jsx24("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: emptyDescription })
1980
+ ] }),
1981
+ pagination
2012
1982
  ] });
2013
1983
  }
2014
1984
 
@@ -2016,8 +1986,8 @@ function AppShell({
2016
1986
  import * as DropdownMenu3 from "@radix-ui/react-dropdown-menu";
2017
1987
  import { MoreVertical } from "lucide-react";
2018
1988
  import { useState as useState11 } from "react";
2019
- import { Drawer as DrawerPrimitive5 } from "vaul";
2020
- import { Fragment as Fragment7, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
1989
+ import { Drawer as DrawerPrimitive6 } from "vaul";
1990
+ import { Fragment as Fragment6, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
2021
1991
  var triggerClass = "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50";
2022
1992
  var itemClass2 = "flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent focus:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
2023
1993
  var sheetItemClass2 = "flex w-full cursor-pointer select-none items-center gap-3 rounded-md px-3 py-3 text-base outline-none transition-colors hover:bg-accent active:bg-accent disabled:pointer-events-none disabled:opacity-50";
@@ -2031,20 +2001,20 @@ function RowActionsMenu({
2031
2001
  const isDesktop = useIsDesktop();
2032
2002
  const [open, setOpen] = useState11(false);
2033
2003
  if (actions.length === 0) return null;
2034
- const trigger = /* @__PURE__ */ jsx30(
2004
+ const trigger = /* @__PURE__ */ jsx25(
2035
2005
  "button",
2036
2006
  {
2037
2007
  type: "button",
2038
2008
  disabled,
2039
2009
  "aria-label": label,
2040
2010
  className: triggerClass,
2041
- children: /* @__PURE__ */ jsx30(MoreVertical, { className: "h-4 w-4" })
2011
+ children: /* @__PURE__ */ jsx25(MoreVertical, { className: "h-4 w-4" })
2042
2012
  }
2043
2013
  );
2044
2014
  if (isDesktop) {
2045
- return /* @__PURE__ */ jsxs26(DropdownMenu3.Root, { open, onOpenChange: setOpen, modal: false, children: [
2046
- /* @__PURE__ */ jsx30(DropdownMenu3.Trigger, { asChild: true, children: trigger }),
2047
- /* @__PURE__ */ jsx30(DropdownMenu3.Portal, { children: /* @__PURE__ */ jsx30(
2015
+ return /* @__PURE__ */ jsxs23(DropdownMenu3.Root, { open, onOpenChange: setOpen, modal: false, children: [
2016
+ /* @__PURE__ */ jsx25(DropdownMenu3.Trigger, { asChild: true, children: trigger }),
2017
+ /* @__PURE__ */ jsx25(DropdownMenu3.Portal, { children: /* @__PURE__ */ jsx25(
2048
2018
  DropdownMenu3.Content,
2049
2019
  {
2050
2020
  align,
@@ -2055,7 +2025,7 @@ function RowActionsMenu({
2055
2025
  itemClass2,
2056
2026
  action.destructive && "text-destructive focus:bg-destructive/10"
2057
2027
  );
2058
- return action.href ? /* @__PURE__ */ jsx30(
2028
+ return action.href ? /* @__PURE__ */ jsx25(
2059
2029
  DropdownMenu3.Item,
2060
2030
  {
2061
2031
  asChild: true,
@@ -2063,14 +2033,14 @@ function RowActionsMenu({
2063
2033
  children: renderLink({
2064
2034
  href: action.href,
2065
2035
  className: cls,
2066
- children: /* @__PURE__ */ jsxs26(Fragment7, { children: [
2036
+ children: /* @__PURE__ */ jsxs23(Fragment6, { children: [
2067
2037
  action.icon,
2068
2038
  action.label
2069
2039
  ] })
2070
2040
  })
2071
2041
  },
2072
2042
  action.label
2073
- ) : /* @__PURE__ */ jsxs26(
2043
+ ) : /* @__PURE__ */ jsxs23(
2074
2044
  DropdownMenu3.Item,
2075
2045
  {
2076
2046
  className: cls,
@@ -2088,23 +2058,23 @@ function RowActionsMenu({
2088
2058
  ) })
2089
2059
  ] });
2090
2060
  }
2091
- return /* @__PURE__ */ jsxs26(DrawerPrimitive5.Root, { open, onOpenChange: setOpen, children: [
2092
- /* @__PURE__ */ jsx30(DrawerPrimitive5.Trigger, { asChild: true, children: trigger }),
2093
- /* @__PURE__ */ jsxs26(DrawerPrimitive5.Portal, { children: [
2094
- /* @__PURE__ */ jsx30(DrawerPrimitive5.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
2095
- /* @__PURE__ */ jsxs26(DrawerPrimitive5.Content, { className: "fixed inset-x-0 bottom-0 z-50 flex flex-col rounded-t-2xl border bg-popover p-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-none", children: [
2096
- /* @__PURE__ */ jsx30(DrawerPrimitive5.Title, { className: "sr-only", children: label }),
2097
- /* @__PURE__ */ jsx30("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
2061
+ return /* @__PURE__ */ jsxs23(DrawerPrimitive6.Root, { open, onOpenChange: setOpen, children: [
2062
+ /* @__PURE__ */ jsx25(DrawerPrimitive6.Trigger, { asChild: true, children: trigger }),
2063
+ /* @__PURE__ */ jsxs23(DrawerPrimitive6.Portal, { children: [
2064
+ /* @__PURE__ */ jsx25(DrawerPrimitive6.Overlay, { className: "fixed inset-0 z-50 bg-black/80" }),
2065
+ /* @__PURE__ */ jsxs23(DrawerPrimitive6.Content, { className: "fixed inset-x-0 bottom-0 z-50 flex flex-col rounded-t-2xl border bg-popover p-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] text-popover-foreground outline-none", children: [
2066
+ /* @__PURE__ */ jsx25(DrawerPrimitive6.Title, { className: "sr-only", children: label }),
2067
+ /* @__PURE__ */ jsx25("div", { className: "mx-auto my-3 h-1.5 w-12 shrink-0 rounded-full bg-muted" }),
2098
2068
  actions.map((action) => {
2099
2069
  const cls = cn(
2100
2070
  sheetItemClass2,
2101
2071
  action.destructive && "text-destructive"
2102
2072
  );
2103
- const inner = /* @__PURE__ */ jsxs26(Fragment7, { children: [
2073
+ const inner = /* @__PURE__ */ jsxs23(Fragment6, { children: [
2104
2074
  action.icon,
2105
2075
  action.label
2106
2076
  ] });
2107
- return action.href ? /* @__PURE__ */ jsx30(
2077
+ return action.href ? /* @__PURE__ */ jsx25(
2108
2078
  "span",
2109
2079
  {
2110
2080
  onClick: () => setOpen(false),
@@ -2116,7 +2086,7 @@ function RowActionsMenu({
2116
2086
  })
2117
2087
  },
2118
2088
  action.label
2119
- ) : /* @__PURE__ */ jsx30(
2089
+ ) : /* @__PURE__ */ jsx25(
2120
2090
  "button",
2121
2091
  {
2122
2092
  type: "button",
@@ -2136,30 +2106,212 @@ function RowActionsMenu({
2136
2106
  ] });
2137
2107
  }
2138
2108
 
2139
- // src/lib/api-error.ts
2140
- function extractApiError(err, fallbackMessage = "Ocorreu um erro inesperado.") {
2141
- const detail = err?.body?.detail || err?.message;
2142
- if (Array.isArray(detail) && detail.length > 0) {
2143
- return detail[0].msg;
2109
+ // src/components/search-input.tsx
2110
+ import * as React10 from "react";
2111
+ import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
2112
+ var SearchInput = React10.forwardRef(
2113
+ ({ containerClassName, className, ...props }, ref) => {
2114
+ return /* @__PURE__ */ jsxs24("div", { className: `relative flex-1 ${containerClassName ?? ""}`, children: [
2115
+ /* @__PURE__ */ jsxs24(
2116
+ "svg",
2117
+ {
2118
+ xmlns: "http://www.w3.org/2000/svg",
2119
+ width: "16",
2120
+ height: "16",
2121
+ viewBox: "0 0 24 24",
2122
+ fill: "none",
2123
+ stroke: "currentColor",
2124
+ strokeWidth: "2",
2125
+ strokeLinecap: "round",
2126
+ strokeLinejoin: "round",
2127
+ className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground",
2128
+ children: [
2129
+ /* @__PURE__ */ jsx26("circle", { cx: "11", cy: "11", r: "8" }),
2130
+ /* @__PURE__ */ jsx26("path", { d: "m21 21-4.3-4.3" })
2131
+ ]
2132
+ }
2133
+ ),
2134
+ /* @__PURE__ */ jsx26(
2135
+ "input",
2136
+ {
2137
+ ref,
2138
+ type: "text",
2139
+ className: `flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 pl-10 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ""}`,
2140
+ ...props
2141
+ }
2142
+ )
2143
+ ] });
2144
2144
  }
2145
- return detail || fallbackMessage;
2145
+ );
2146
+ SearchInput.displayName = "SearchInput";
2147
+
2148
+ // src/components/stat-card.tsx
2149
+ import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
2150
+ function StatCard({ label, value, detail, icon, isLoading }) {
2151
+ if (isLoading) {
2152
+ return /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border bg-card p-6 shadow-sm", children: [
2153
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between pb-2", children: [
2154
+ /* @__PURE__ */ jsx27("div", { className: "h-4 w-24 animate-pulse rounded bg-muted" }),
2155
+ /* @__PURE__ */ jsx27("div", { className: "h-4 w-4 animate-pulse rounded bg-muted" })
2156
+ ] }),
2157
+ /* @__PURE__ */ jsx27("div", { className: "mt-2 h-7 w-16 animate-pulse rounded bg-muted" }),
2158
+ /* @__PURE__ */ jsx27("div", { className: "mt-1 h-4 w-20 animate-pulse rounded bg-muted" })
2159
+ ] });
2160
+ }
2161
+ return /* @__PURE__ */ jsxs25("div", { className: "rounded-lg border bg-card p-6 shadow-sm", children: [
2162
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center justify-between pb-2", children: [
2163
+ /* @__PURE__ */ jsx27("span", { className: "text-sm font-medium text-muted-foreground", children: label }),
2164
+ icon && /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: icon })
2165
+ ] }),
2166
+ /* @__PURE__ */ jsx27("div", { className: "text-2xl font-bold", children: value }),
2167
+ detail && /* @__PURE__ */ jsx27("p", { className: "text-xs text-muted-foreground", children: detail })
2168
+ ] });
2169
+ }
2170
+
2171
+ // src/components/status-badge.tsx
2172
+ import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
2173
+ var variantClasses = {
2174
+ neutral: {
2175
+ badge: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300",
2176
+ dot: "bg-gray-400"
2177
+ },
2178
+ info: {
2179
+ badge: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-400",
2180
+ dot: "bg-blue-500"
2181
+ },
2182
+ success: {
2183
+ badge: "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-400",
2184
+ dot: "bg-green-500"
2185
+ },
2186
+ danger: {
2187
+ badge: "bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-400",
2188
+ dot: "bg-red-500"
2189
+ },
2190
+ warning: {
2191
+ badge: "bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-400",
2192
+ dot: "bg-amber-500"
2193
+ }
2194
+ };
2195
+ function StatusBadge({
2196
+ variant = "neutral",
2197
+ dot = true,
2198
+ className,
2199
+ children
2200
+ }) {
2201
+ const classes = variantClasses[variant];
2202
+ return /* @__PURE__ */ jsxs26(
2203
+ "span",
2204
+ {
2205
+ className: `inline-flex items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium ${classes.badge} ${className ?? ""}`,
2206
+ children: [
2207
+ dot && /* @__PURE__ */ jsx28(
2208
+ "span",
2209
+ {
2210
+ className: `inline-block h-1.5 w-1.5 shrink-0 rounded-full ${classes.dot}`
2211
+ }
2212
+ ),
2213
+ children
2214
+ ]
2215
+ }
2216
+ );
2217
+ }
2218
+
2219
+ // src/components/status-dot.tsx
2220
+ import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
2221
+ function StatusDot({ active, label, className }) {
2222
+ return /* @__PURE__ */ jsxs27("span", { className: `inline-flex items-center gap-2 ${className ?? ""}`, children: [
2223
+ /* @__PURE__ */ jsx29(
2224
+ "span",
2225
+ {
2226
+ className: `h-2 w-2 rounded-full ${active ? "bg-green-500" : "bg-red-500"}`
2227
+ }
2228
+ ),
2229
+ label && /* @__PURE__ */ jsx29("span", { children: label })
2230
+ ] });
2231
+ }
2232
+
2233
+ // src/components/table-skeleton.tsx
2234
+ import { Fragment as Fragment7, jsx as jsx30 } from "react/jsx-runtime";
2235
+ function TableSkeleton({ rows = 5, columns = 4 }) {
2236
+ return /* @__PURE__ */ jsx30(Fragment7, { children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx30("tr", { className: "border-b transition-colors", children: Array.from({ length: columns }).map((_2, j) => /* @__PURE__ */ jsx30("td", { className: "p-4 align-middle", children: /* @__PURE__ */ jsx30("div", { className: "h-5 w-full animate-pulse rounded bg-muted" }) }, j)) }, i)) });
2237
+ }
2238
+
2239
+ // src/components/toaster.tsx
2240
+ import { Toaster as Sonner } from "sonner";
2241
+ import { jsx as jsx31 } from "react/jsx-runtime";
2242
+ function Toaster(props) {
2243
+ const { theme = "system" } = useTheme();
2244
+ return /* @__PURE__ */ jsx31(
2245
+ Sonner,
2246
+ {
2247
+ theme,
2248
+ className: "toaster group",
2249
+ toastOptions: {
2250
+ classNames: {
2251
+ toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-md",
2252
+ description: "group-[.toast]:text-muted-foreground",
2253
+ actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
2254
+ cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
2255
+ success: "group-[.toaster]:!bg-background group-[.toaster]:!text-foreground group-[.toaster]:!border-success/40",
2256
+ error: "group-[.toaster]:!bg-background group-[.toaster]:!text-foreground group-[.toaster]:!border-destructive/40"
2257
+ }
2258
+ },
2259
+ ...props
2260
+ }
2261
+ );
2262
+ }
2263
+
2264
+ // src/hooks/use-company-subdomain.ts
2265
+ import { useState as useState12 } from "react";
2266
+ function getSubdomain(hostname) {
2267
+ const labels = hostname.split(".");
2268
+ if (labels.length < 2) return void 0;
2269
+ const first = labels[0];
2270
+ if (!first || first === "www" || first === "localhost") return void 0;
2271
+ return first;
2272
+ }
2273
+ function resolveOverride(storageKey) {
2274
+ if (typeof window === "undefined") return void 0;
2275
+ const params = new URLSearchParams(window.location.search);
2276
+ if (params.has("company")) {
2277
+ const value = params.get("company");
2278
+ if (value) {
2279
+ window.localStorage.setItem(storageKey, value);
2280
+ return value;
2281
+ }
2282
+ window.localStorage.removeItem(storageKey);
2283
+ return void 0;
2284
+ }
2285
+ return window.localStorage.getItem(storageKey) ?? void 0;
2286
+ }
2287
+ function useCompanySubdomain(opts = {}) {
2288
+ const { enableOverride = false, overrideStorageKey = "company-subdomain" } = opts;
2289
+ const [subdomain] = useState12(() => {
2290
+ if (enableOverride) {
2291
+ const override = resolveOverride(overrideStorageKey);
2292
+ if (override) return override;
2293
+ }
2294
+ if (typeof window === "undefined") return void 0;
2295
+ return getSubdomain(window.location.hostname);
2296
+ });
2297
+ return { subdomain };
2146
2298
  }
2147
2299
 
2148
2300
  // src/hooks/use-disclosure.ts
2149
- import { useCallback, useState as useState12 } from "react";
2301
+ import { useCallback as useCallback2, useState as useState13 } from "react";
2150
2302
  function useDisclosure(initial = false) {
2151
- const [open, setOpen] = useState12(initial);
2152
- const onOpen = useCallback(() => setOpen(true), []);
2153
- const onClose = useCallback(() => setOpen(false), []);
2154
- const onToggle = useCallback(() => setOpen((v) => !v), []);
2303
+ const [open, setOpen] = useState13(initial);
2304
+ const onOpen = useCallback2(() => setOpen(true), []);
2305
+ const onClose = useCallback2(() => setOpen(false), []);
2306
+ const onToggle = useCallback2(() => setOpen((v) => !v), []);
2155
2307
  return { open, onOpen, onClose, onToggle, setOpen };
2156
2308
  }
2157
2309
 
2158
2310
  // src/hooks/use-toast.ts
2159
2311
  import { toast } from "sonner";
2160
- import { useCallback as useCallback2 } from "react";
2312
+ import { useCallback as useCallback3 } from "react";
2161
2313
  function useCustomToast() {
2162
- const showToast = useCallback2(
2314
+ const showToast = useCallback3(
2163
2315
  (title, description, status = "success") => {
2164
2316
  switch (status) {
2165
2317
  case "success":
@@ -2180,6 +2332,15 @@ function useCustomToast() {
2180
2332
  );
2181
2333
  return showToast;
2182
2334
  }
2335
+
2336
+ // src/lib/api-error.ts
2337
+ function extractApiError(err, fallbackMessage = "Ocorreu um erro inesperado.") {
2338
+ const detail = err?.body?.detail || err?.message;
2339
+ if (Array.isArray(detail) && detail.length > 0) {
2340
+ return detail[0].msg;
2341
+ }
2342
+ return detail || fallbackMessage;
2343
+ }
2183
2344
  export {
2184
2345
  AppShell,
2185
2346
  AuthCard,
@@ -2191,6 +2352,9 @@ export {
2191
2352
  DatePicker,
2192
2353
  DateTimePicker,
2193
2354
  EmptyState,
2355
+ FilterOptionList,
2356
+ FilterPopover,
2357
+ FilterToolbar,
2194
2358
  FormDialog,
2195
2359
  FormDialogLayout,
2196
2360
  FormField,