@arqel-dev/ui 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/SKILL.md +5 -2
  2. package/dist/AreaChart-NMQ2RQGA.js +33 -0
  3. package/dist/AreaChart-NMQ2RQGA.js.map +1 -0
  4. package/dist/ArqelCreatePage-P3G6TKIK.js +8 -0
  5. package/dist/ArqelCreatePage-P3G6TKIK.js.map +1 -0
  6. package/dist/ArqelDashboardPage-PX3Y75AA.js +6 -0
  7. package/dist/ArqelDashboardPage-PX3Y75AA.js.map +1 -0
  8. package/dist/ArqelEditPage-HKQSE6E5.js +8 -0
  9. package/dist/ArqelEditPage-HKQSE6E5.js.map +1 -0
  10. package/dist/ArqelIndexPage-MRH3572Z.js +12 -0
  11. package/dist/ArqelIndexPage-MRH3572Z.js.map +1 -0
  12. package/dist/ArqelShowPage-4FS5MTUN.js +6 -0
  13. package/dist/ArqelShowPage-4FS5MTUN.js.map +1 -0
  14. package/dist/BarChart-KLHKTLO2.js +19 -0
  15. package/dist/BarChart-KLHKTLO2.js.map +1 -0
  16. package/dist/DonutChart-L56R4QF2.js +11 -0
  17. package/dist/DonutChart-L56R4QF2.js.map +1 -0
  18. package/dist/LineChart-GHMPHFZZ.js +30 -0
  19. package/dist/LineChart-GHMPHFZZ.js.map +1 -0
  20. package/dist/PieChart-T2VQOFPP.js +4 -0
  21. package/dist/PieChart-T2VQOFPP.js.map +1 -0
  22. package/dist/RadarChart-CSWR5AO7.js +32 -0
  23. package/dist/RadarChart-CSWR5AO7.js.map +1 -0
  24. package/dist/Topbar-DffcNdcJ.d.ts +50 -0
  25. package/dist/action.d.ts +12 -2
  26. package/dist/action.js +6 -1090
  27. package/dist/action.js.map +1 -1
  28. package/dist/auth.js +1 -10
  29. package/dist/auth.js.map +1 -1
  30. package/dist/chunk-2LTZJ4R7.js +110 -0
  31. package/dist/chunk-2LTZJ4R7.js.map +1 -0
  32. package/dist/chunk-3JJOHYD7.js +57 -0
  33. package/dist/chunk-3JJOHYD7.js.map +1 -0
  34. package/dist/chunk-47HD7ALI.js +370 -0
  35. package/dist/chunk-47HD7ALI.js.map +1 -0
  36. package/dist/chunk-6UJQP7KU.js +737 -0
  37. package/dist/chunk-6UJQP7KU.js.map +1 -0
  38. package/dist/chunk-7LSZADUC.js +50 -0
  39. package/dist/chunk-7LSZADUC.js.map +1 -0
  40. package/dist/chunk-B7ULUJVO.js +11 -0
  41. package/dist/chunk-B7ULUJVO.js.map +1 -0
  42. package/dist/chunk-C5H44NSP.js +37 -0
  43. package/dist/chunk-C5H44NSP.js.map +1 -0
  44. package/dist/chunk-DSULVRB6.js +101 -0
  45. package/dist/chunk-DSULVRB6.js.map +1 -0
  46. package/dist/chunk-ERT7LL6C.js +199 -0
  47. package/dist/chunk-ERT7LL6C.js.map +1 -0
  48. package/dist/chunk-EYAUPJ6J.js +64 -0
  49. package/dist/chunk-EYAUPJ6J.js.map +1 -0
  50. package/dist/chunk-FHRQ7PWE.js +58 -0
  51. package/dist/chunk-FHRQ7PWE.js.map +1 -0
  52. package/dist/chunk-FV7RESKL.js +285 -0
  53. package/dist/chunk-FV7RESKL.js.map +1 -0
  54. package/dist/chunk-K7QY56XN.js +610 -0
  55. package/dist/chunk-K7QY56XN.js.map +1 -0
  56. package/dist/chunk-L3JBBN4H.js +27 -0
  57. package/dist/chunk-L3JBBN4H.js.map +1 -0
  58. package/dist/chunk-MSRTGCTJ.js +597 -0
  59. package/dist/chunk-MSRTGCTJ.js.map +1 -0
  60. package/dist/chunk-MTNFFSVA.js +92 -0
  61. package/dist/chunk-MTNFFSVA.js.map +1 -0
  62. package/dist/chunk-MTZJDFQ6.js +32 -0
  63. package/dist/chunk-MTZJDFQ6.js.map +1 -0
  64. package/dist/chunk-MXUXMHOK.js +16 -0
  65. package/dist/chunk-MXUXMHOK.js.map +1 -0
  66. package/dist/chunk-PXON56HL.js +35 -0
  67. package/dist/chunk-PXON56HL.js.map +1 -0
  68. package/dist/chunk-PZEVKMZX.js +598 -0
  69. package/dist/chunk-PZEVKMZX.js.map +1 -0
  70. package/dist/chunk-R4BOUU6Y.js +55 -0
  71. package/dist/chunk-R4BOUU6Y.js.map +1 -0
  72. package/dist/chunk-RMPOR2YZ.js +12 -0
  73. package/dist/chunk-RMPOR2YZ.js.map +1 -0
  74. package/dist/chunk-RNAMHQM6.js +12 -0
  75. package/dist/chunk-RNAMHQM6.js.map +1 -0
  76. package/dist/chunk-TNJEHYRF.js +387 -0
  77. package/dist/chunk-TNJEHYRF.js.map +1 -0
  78. package/dist/chunk-VSTRVXXZ.js +49 -0
  79. package/dist/chunk-VSTRVXXZ.js.map +1 -0
  80. package/dist/chunk-WKWV36PG.js +140 -0
  81. package/dist/chunk-WKWV36PG.js.map +1 -0
  82. package/dist/cn-By8XWiSA.d.ts +12 -0
  83. package/dist/flash.js +2 -104
  84. package/dist/flash.js.map +1 -1
  85. package/dist/form.js +4 -673
  86. package/dist/form.js.map +1 -1
  87. package/dist/index.d.ts +2 -2
  88. package/dist/index.js +18 -3706
  89. package/dist/index.js.map +1 -1
  90. package/dist/pages.js +23 -3025
  91. package/dist/pages.js.map +1 -1
  92. package/dist/palette.js +1 -283
  93. package/dist/palette.js.map +1 -1
  94. package/dist/primitives.js +3 -658
  95. package/dist/primitives.js.map +1 -1
  96. package/dist/resource.js +5 -720
  97. package/dist/resource.js.map +1 -1
  98. package/dist/shell.d.ts +26 -46
  99. package/dist/shell.js +46 -1039
  100. package/dist/shell.js.map +1 -1
  101. package/dist/table.js +4 -618
  102. package/dist/table.js.map +1 -1
  103. package/dist/utility.js +3 -104
  104. package/dist/utility.js.map +1 -1
  105. package/dist/utils.d.ts +12 -6
  106. package/dist/utils.js +2 -9
  107. package/dist/utils.js.map +1 -1
  108. package/dist/widgets.d.ts +7 -2
  109. package/dist/widgets.js +3 -675
  110. package/dist/widgets.js.map +1 -1
  111. package/package.json +15 -14
package/dist/pages.js CHANGED
@@ -1,3032 +1,30 @@
1
- import { clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
- import { useMemo, useState, useRef, useEffect, useCallback, Suspense, useId, lazy } from 'react';
4
- import { cva } from 'class-variance-authority';
5
- import { Slot, DropdownMenu as DropdownMenu$1, Dialog as Dialog$1 } from 'radix-ui';
6
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
- import { isFieldEntry, isLayoutEntry } from '@arqel-dev/types/forms';
8
- import { useArqelForm } from '@arqel-dev/hooks';
9
- import { usePage, router } from '@inertiajs/react';
10
- import { ResponsiveContainer, LineChart as LineChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Line, BarChart as BarChart$1, Bar, AreaChart as AreaChart$1, Area, PieChart as PieChart$1, Pie, Cell, RadarChart as RadarChart$1, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar } from 'recharts';
11
- import { XIcon } from 'lucide-react';
12
- import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table';
13
-
14
- var __defProp = Object.defineProperty;
15
- var __getOwnPropNames = Object.getOwnPropertyNames;
16
- var __esm = (fn, res) => function __init() {
17
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
18
- };
19
- var __export = (target, all) => {
20
- for (var name in all)
21
- __defProp(target, name, { get: all[name], enumerable: true });
22
- };
23
- function cn(...inputs) {
24
- return twMerge(clsx(inputs));
25
- }
26
- var init_cn = __esm({
27
- "src/utils/cn.ts"() {
28
- }
29
- });
30
- function Button({
31
- className,
32
- variant = "default",
33
- size = "default",
34
- asChild = false,
35
- ...props
36
- }) {
37
- const Comp = asChild ? Slot.Root : "button";
38
- return /* @__PURE__ */ jsx(
39
- Comp,
40
- {
41
- "data-slot": "button",
42
- "data-variant": variant,
43
- "data-size": size,
44
- className: cn(buttonVariants({ variant, size, className })),
45
- ...props
46
- }
47
- );
48
- }
49
- var buttonVariants;
50
- var init_button = __esm({
51
- "src/shadcn/ui/button.tsx"() {
52
- init_cn();
53
- buttonVariants = cva(
54
- "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
55
- {
56
- variants: {
57
- variant: {
58
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
59
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
60
- outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
61
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
62
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
63
- link: "text-primary underline-offset-4 hover:underline"
64
- },
65
- size: {
66
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
67
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
68
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
69
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
70
- icon: "size-9",
71
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
72
- "icon-sm": "size-8",
73
- "icon-lg": "size-10"
74
- }
75
- },
76
- defaultVariants: {
77
- variant: "default",
78
- size: "default"
79
- }
80
- }
81
- );
82
- }
83
- });
84
-
85
- // src/action/Button.tsx
86
- var init_Button = __esm({
87
- "src/action/Button.tsx"() {
88
- init_button();
89
- }
90
- });
91
- function FormActions({
92
- submitLabel = "Save",
93
- cancelLabel = "Cancel",
94
- onCancel,
95
- processing = false,
96
- disabled = false,
97
- extra,
98
- className
99
- }) {
100
- return /* @__PURE__ */ jsxs(
101
- "div",
102
- {
103
- className: cn(
104
- "flex flex-wrap items-center justify-end gap-2 border-t border-border pt-4",
105
- className
106
- ),
107
- children: [
108
- extra && /* @__PURE__ */ jsx("div", { className: "mr-auto", children: extra }),
109
- onCancel && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: processing, children: cancelLabel }),
110
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: processing || disabled, children: processing ? "Saving\u2026" : submitLabel })
111
- ]
112
- }
113
- );
114
- }
115
- var init_FormActions = __esm({
116
- "src/form/FormActions.tsx"() {
117
- init_Button();
118
- init_cn();
119
- }
120
- });
121
-
122
- // src/form/FieldRegistry.tsx
123
- function getFieldComponent(name) {
124
- return registry.get(name);
125
- }
126
- var registry;
127
- var init_FieldRegistry = __esm({
128
- "src/form/FieldRegistry.tsx"() {
129
- registry = /* @__PURE__ */ new Map();
130
- }
131
- });
132
- function NativeFieldInput(props) {
133
- const { field } = props;
134
- switch (field.type) {
135
- case "text":
136
- case "email":
137
- case "url":
138
- case "password":
139
- case "slug":
140
- return /* @__PURE__ */ jsx(TextLike, { ...props, field });
141
- case "textarea":
142
- return /* @__PURE__ */ jsx(Textarea, { ...props, field });
143
- case "number":
144
- case "currency":
145
- return /* @__PURE__ */ jsx(NumericInput, { ...props, field });
146
- case "boolean":
147
- case "toggle":
148
- return /* @__PURE__ */ jsx(Checkbox, { ...props, field });
149
- case "select":
150
- return /* @__PURE__ */ jsx(SelectInput, { ...props, field });
151
- case "multiSelect":
152
- case "hasMany":
153
- return /* @__PURE__ */ jsx(MultiSelectInput, { ...props, field });
154
- case "radio":
155
- return /* @__PURE__ */ jsx(RadioGroup, { ...props, field });
156
- case "belongsTo":
157
- return /* @__PURE__ */ jsx(BelongsToInput, { ...props, field });
158
- case "date":
159
- case "dateTime":
160
- return /* @__PURE__ */ jsx(DateInput, { ...props, field });
161
- case "file":
162
- case "image":
163
- return /* @__PURE__ */ jsx(FileInput, { ...props });
164
- default:
165
- return null;
166
- }
167
- }
168
- function TextLike({
169
- field,
170
- value,
171
- onChange,
172
- disabled,
173
- inputId,
174
- describedBy
175
- }) {
176
- const htmlType = field.type === "email" ? "email" : field.type === "password" ? "password" : field.type === "url" ? "url" : "text";
177
- const isDisabled = disabled || field.disabled || field.readonly;
178
- return /* @__PURE__ */ jsx(
179
- "input",
180
- {
181
- id: inputId,
182
- type: htmlType,
183
- className: inputClasses,
184
- value: typeof value === "string" ? value : (value ?? "")?.toString() ?? "",
185
- placeholder: field.placeholder ?? void 0,
186
- disabled: isDisabled,
187
- readOnly: field.readonly === true,
188
- required: field.required === true,
189
- "aria-describedby": describedBy,
190
- onChange: (e) => onChange(e.target.value)
191
- }
192
- );
193
- }
194
- function Textarea({
195
- field,
196
- value,
197
- onChange,
198
- disabled,
199
- inputId,
200
- describedBy
201
- }) {
202
- return /* @__PURE__ */ jsx(
203
- "textarea",
204
- {
205
- id: inputId,
206
- className: cn(inputClasses, "h-auto min-h-[5rem] py-2"),
207
- value: typeof value === "string" ? value : "",
208
- placeholder: field.placeholder ?? void 0,
209
- disabled: disabled || field.disabled || field.readonly,
210
- readOnly: field.readonly === true,
211
- required: field.required === true,
212
- rows: 4,
213
- "aria-describedby": describedBy,
214
- onChange: (e) => onChange(e.target.value)
215
- }
216
- );
217
- }
218
- function NumericInput({
219
- field,
220
- value,
221
- onChange,
222
- disabled,
223
- inputId,
224
- describedBy
225
- }) {
226
- return /* @__PURE__ */ jsx(
227
- "input",
228
- {
229
- id: inputId,
230
- type: "number",
231
- className: inputClasses,
232
- value: value === null || value === void 0 ? "" : String(value),
233
- placeholder: field.placeholder ?? void 0,
234
- disabled: disabled || field.disabled || field.readonly,
235
- readOnly: field.readonly === true,
236
- required: field.required === true,
237
- "aria-describedby": describedBy,
238
- onChange: (e) => {
239
- const raw = e.target.value;
240
- if (raw === "") {
241
- onChange(null);
242
- return;
243
- }
244
- const num = Number(raw);
245
- onChange(Number.isNaN(num) ? null : num);
246
- }
247
- }
248
- );
249
- }
250
- function Checkbox({
251
- field,
252
- value,
253
- onChange,
254
- disabled,
255
- inputId,
256
- describedBy
257
- }) {
258
- return /* @__PURE__ */ jsx(
259
- "input",
260
- {
261
- id: inputId,
262
- type: "checkbox",
263
- className: "h-4 w-4 rounded border-[var(--input)]",
264
- checked: value === true,
265
- disabled: disabled || field.disabled || field.readonly,
266
- "aria-describedby": describedBy,
267
- onChange: (e) => onChange(e.target.checked)
268
- }
269
- );
270
- }
271
- function normaliseOptions(options) {
272
- if (!options) return [];
273
- if (Array.isArray(options)) return options;
274
- return Object.entries(options).map(([value, label]) => ({ value, label }));
275
- }
276
- function SelectInput({
277
- field,
278
- value,
279
- onChange,
280
- disabled,
281
- inputId,
282
- describedBy
283
- }) {
284
- const options = normaliseOptions(field.props.options);
285
- return /* @__PURE__ */ jsxs(
286
- "select",
287
- {
288
- id: inputId,
289
- className: inputClasses,
290
- value: value === null || value === void 0 ? "" : String(value),
291
- disabled: disabled || field.disabled || field.readonly,
292
- required: field.required === true,
293
- "aria-describedby": describedBy,
294
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
295
- children: [
296
- field.placeholder && /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder }),
297
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
298
- ]
299
- }
300
- );
301
- }
302
- function MultiSelectInput({
303
- field,
304
- value,
305
- onChange,
306
- disabled,
307
- inputId,
308
- describedBy
309
- }) {
310
- const arr = Array.isArray(value) ? value : [];
311
- const options = field.type === "multiSelect" ? normaliseOptions(field.props.options) : [];
312
- return /* @__PURE__ */ jsx(
313
- "select",
314
- {
315
- id: inputId,
316
- multiple: true,
317
- className: cn(inputClasses, "h-auto min-h-[6rem] py-1"),
318
- value: arr.map(String),
319
- disabled: disabled || field.disabled || field.readonly,
320
- "aria-describedby": describedBy,
321
- onChange: (e) => {
322
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
323
- onChange(next);
324
- },
325
- children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
326
- }
327
- );
328
- }
329
- function RadioGroup({
330
- field,
331
- value,
332
- onChange,
333
- disabled,
334
- inputId,
335
- describedBy
336
- }) {
337
- return /* @__PURE__ */ jsx(
338
- "div",
339
- {
340
- id: inputId,
341
- className: "flex flex-col gap-1",
342
- "aria-describedby": describedBy,
343
- role: "radiogroup",
344
- children: (field.props.options ?? []).map((opt) => /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm", children: [
345
- /* @__PURE__ */ jsx(
346
- "input",
347
- {
348
- type: "radio",
349
- name: field.name,
350
- value: String(opt.value),
351
- checked: String(value ?? "") === String(opt.value),
352
- disabled: disabled || field.disabled || field.readonly,
353
- onChange: () => onChange(opt.value)
354
- }
355
- ),
356
- opt.label
357
- ] }, opt.value))
358
- }
359
- );
360
- }
361
- function BelongsToInput({
362
- field,
363
- value,
364
- onChange,
365
- disabled,
366
- inputId,
367
- describedBy
368
- }) {
369
- return /* @__PURE__ */ jsx(
370
- "input",
371
- {
372
- id: inputId,
373
- type: "text",
374
- className: inputClasses,
375
- value: value === null || value === void 0 ? "" : String(value),
376
- placeholder: field.placeholder ?? `Search ${field.props.relatedResource}\u2026`,
377
- disabled: disabled || field.disabled || field.readonly,
378
- readOnly: field.readonly === true,
379
- "aria-describedby": describedBy,
380
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
381
- }
382
- );
383
- }
384
- function DateInput({
385
- field,
386
- value,
387
- onChange,
388
- disabled,
389
- inputId,
390
- describedBy
391
- }) {
392
- return /* @__PURE__ */ jsx(
393
- "input",
394
- {
395
- id: inputId,
396
- type: field.type === "dateTime" ? "datetime-local" : "date",
397
- className: inputClasses,
398
- value: typeof value === "string" ? value : "",
399
- disabled: disabled || field.disabled || field.readonly,
400
- readOnly: field.readonly === true,
401
- required: field.required === true,
402
- "aria-describedby": describedBy,
403
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
404
- }
405
- );
406
- }
407
- function FileInput({
408
- field,
409
- onChange,
410
- disabled,
411
- inputId,
412
- describedBy
413
- }) {
414
- return /* @__PURE__ */ jsx(
415
- "input",
416
- {
417
- id: inputId,
418
- type: "file",
419
- className: "text-sm",
420
- disabled: disabled || field.disabled || field.readonly,
421
- "aria-describedby": describedBy,
422
- onChange: (e) => onChange(e.target.files?.[0] ?? null)
423
- }
424
- );
425
- }
426
- var inputClasses;
427
- var init_nativeFields = __esm({
428
- "src/form/nativeFields.tsx"() {
429
- init_cn();
430
- inputClasses = cn(
431
- "h-9 rounded-sm border border-[var(--input)]",
432
- "bg-background px-3 text-sm",
433
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
434
- "disabled:cursor-not-allowed disabled:opacity-50"
435
- );
436
- }
437
- });
438
- function FieldRenderer(props) {
439
- const { field, errors, className } = props;
440
- const id = useId();
441
- const errorId = `${id}-error`;
442
- const helperId = `${id}-helper`;
443
- const hasError = errors && errors.length > 0;
444
- const Custom = field.component ? getFieldComponent(field.component) : void 0;
445
- const Input = Custom ?? NativeFieldInput;
446
- const describedBy = [field.helperText ? helperId : null, hasError ? errorId : null].filter(Boolean).join(" ");
447
- return /* @__PURE__ */ jsxs(
448
- "div",
449
- {
450
- "data-arqel-field": field.name,
451
- "data-field-type": field.type,
452
- className: cn("flex flex-col gap-1", className),
453
- children: [
454
- field.label !== null && field.label !== "" && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-sm font-medium", children: [
455
- field.label,
456
- field.required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })
457
- ] }),
458
- /* @__PURE__ */ jsx(Input, { ...props, inputId: id, describedBy: describedBy || void 0 }),
459
- field.helperText && !hasError && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-xs text-muted-foreground", children: field.helperText }),
460
- hasError && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-xs text-destructive", children: errors[0] })
461
- ]
462
- }
463
- );
464
- }
465
- var init_FieldRenderer = __esm({
466
- "src/form/FieldRenderer.tsx"() {
467
- init_cn();
468
- init_FieldRegistry();
469
- init_nativeFields();
470
- }
471
- });
472
- function FormFieldset({ config, children, className }) {
473
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("rounded-md border border-border p-4", className), children: [
474
- /* @__PURE__ */ jsx("legend", { className: "px-1 text-sm font-medium", children: config.legend }),
475
- /* @__PURE__ */ jsx(
476
- "div",
477
- {
478
- className: "grid gap-4",
479
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
480
- children
481
- }
482
- )
483
- ] });
484
- }
485
- var init_FormFieldset = __esm({
486
- "src/form/FormFieldset.tsx"() {
487
- init_cn();
488
- }
489
- });
490
- function FormGrid({ config, children, className }) {
491
- const columns = typeof config.columns === "number" ? config.columns : config.columns["md"] ?? 1;
492
- return /* @__PURE__ */ jsx(
493
- "div",
494
- {
495
- className: cn("grid", className),
496
- style: {
497
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
498
- gap: config.gap ?? "1rem"
499
- },
500
- children
501
- }
502
- );
503
- }
504
- var init_FormGrid = __esm({
505
- "src/form/FormGrid.tsx"() {
506
- init_cn();
507
- }
508
- });
509
- function FormSection({ config, children, className }) {
510
- const [open, setOpen] = useState(!config.collapsed);
511
- const collapsible = config.collapsible === true;
512
- const heading = /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
513
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
514
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: config.heading }),
515
- config.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: config.description })
516
- ] }),
517
- collapsible && /* @__PURE__ */ jsx(
518
- "button",
519
- {
520
- type: "button",
521
- "aria-expanded": open,
522
- onClick: () => setOpen((v) => !v),
523
- className: "text-sm text-muted-foreground hover:underline",
524
- children: open ? "Hide" : "Show"
525
- }
526
- )
527
- ] });
528
- const body = open && /* @__PURE__ */ jsx(
529
- "div",
530
- {
531
- className: "grid gap-4",
532
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
533
- children
534
- }
535
- );
536
- return /* @__PURE__ */ jsx(
537
- "section",
538
- {
539
- "data-arqel-section": "",
540
- className: cn(
541
- "rounded-md border border-border bg-background",
542
- config.compact ? "p-3" : "p-4",
543
- className
544
- ),
545
- children: config.aside ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[16rem_1fr]", children: [
546
- /* @__PURE__ */ jsx("div", { children: heading }),
547
- /* @__PURE__ */ jsx("div", { children: body })
548
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
549
- /* @__PURE__ */ jsx("div", { className: "mb-3", children: heading }),
550
- body
551
- ] })
552
- }
553
- );
554
- }
555
- var init_FormSection = __esm({
556
- "src/form/FormSection.tsx"() {
557
- init_cn();
558
- }
559
- });
560
- function FormTabs({ config, tabs, className }) {
561
- const [active, setActive] = useState(config.defaultTab ?? tabs[0]?.id ?? null);
562
- const baseId = useId();
563
- const refs = useRef([]);
564
- if (tabs.length === 0 || active === null) return null;
565
- const activeIndex = Math.max(
566
- 0,
567
- tabs.findIndex((t) => t.id === active)
568
- );
569
- const activeTab = tabs[activeIndex];
570
- if (!activeTab) return null;
571
- const focus = (i) => {
572
- const target = tabs[i];
573
- if (!target) return;
574
- setActive(target.id);
575
- refs.current[i]?.focus();
576
- };
577
- const onKeyDown = (e) => {
578
- if (e.key === "ArrowRight") {
579
- e.preventDefault();
580
- focus((activeIndex + 1) % tabs.length);
581
- } else if (e.key === "ArrowLeft") {
582
- e.preventDefault();
583
- focus((activeIndex - 1 + tabs.length) % tabs.length);
584
- } else if (e.key === "Home") {
585
- e.preventDefault();
586
- focus(0);
587
- } else if (e.key === "End") {
588
- e.preventDefault();
589
- focus(tabs.length - 1);
590
- }
591
- };
592
- return /* @__PURE__ */ jsxs("div", { className, children: [
593
- /* @__PURE__ */ jsx(
594
- "div",
595
- {
596
- role: "tablist",
597
- "aria-orientation": config.orientation,
598
- className: "flex gap-1 border-b border-border",
599
- children: tabs.map((tab, i) => {
600
- const selected = tab.id === active;
601
- return /* @__PURE__ */ jsxs(
602
- "button",
603
- {
604
- ref: (el) => {
605
- refs.current[i] = el;
606
- },
607
- type: "button",
608
- role: "tab",
609
- id: `${baseId}-tab-${tab.id}`,
610
- "aria-controls": `${baseId}-panel-${tab.id}`,
611
- "aria-selected": selected,
612
- tabIndex: selected ? 0 : -1,
613
- onClick: () => setActive(tab.id),
614
- onKeyDown,
615
- className: cn(
616
- "-mb-px border-b-2 px-3 py-2 text-sm transition-colors",
617
- selected ? "border-primary font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
618
- ),
619
- children: [
620
- tab.label,
621
- tab.badge !== void 0 && tab.badge !== null && /* @__PURE__ */ jsx("span", { className: "ml-1 rounded-full bg-muted px-1.5 text-xs", children: tab.badge })
622
- ]
623
- },
624
- tab.id
625
- );
626
- })
627
- }
628
- ),
629
- /* @__PURE__ */ jsx(
630
- "div",
631
- {
632
- role: "tabpanel",
633
- id: `${baseId}-panel-${activeTab.id}`,
634
- "aria-labelledby": `${baseId}-tab-${activeTab.id}`,
635
- className: "pt-4",
636
- children: activeTab.content
637
- }
638
- )
639
- ] });
640
- }
641
- var init_FormTabs = __esm({
642
- "src/form/FormTabs.tsx"() {
643
- init_cn();
644
- }
645
- });
646
- function FormRenderer({
647
- schema,
648
- fields,
649
- values,
650
- errors = {},
651
- onChange,
652
- disabled = false,
653
- className
654
- }) {
655
- const fieldsByName = new Map(fields.map((f) => [f.name, f]));
656
- const renderEntry = (entry, key) => {
657
- if (isFieldEntry(entry)) {
658
- return renderFieldEntry(entry, key);
659
- }
660
- if (isLayoutEntry(entry)) {
661
- return renderLayoutEntry(entry, key);
662
- }
663
- return null;
664
- };
665
- const renderFieldEntry = (entry, key) => {
666
- const field = fieldsByName.get(entry.name);
667
- if (!field) return null;
668
- return /* @__PURE__ */ jsx(
669
- FieldRenderer,
670
- {
671
- field,
672
- value: values[entry.name],
673
- errors: errors[entry.name],
674
- disabled: disabled || schema.disabled === true,
675
- onChange: (value) => onChange(entry.name, value)
676
- },
677
- key
678
- );
679
- };
680
- const renderChildren = (entries) => {
681
- if (!entries) return null;
682
- return entries.map((child, i) => renderEntry(child, `${i}`));
683
- };
684
- const renderLayoutEntry = (entry, key) => {
685
- switch (entry.type) {
686
- case "section":
687
- return /* @__PURE__ */ jsx(FormSection, { config: entry.props, children: renderChildren(entry.schema) }, key);
688
- case "fieldset":
689
- return /* @__PURE__ */ jsx(FormFieldset, { config: entry.props, children: renderChildren(entry.schema) }, key);
690
- case "grid":
691
- case "columns":
692
- return /* @__PURE__ */ jsx(
693
- FormGrid,
694
- {
695
- config: entry.type === "columns" ? { columns: entry.props.columns } : entry.props,
696
- children: renderChildren(entry.schema)
697
- },
698
- key
699
- );
700
- case "group":
701
- return /* @__PURE__ */ jsx(
702
- "div",
703
- {
704
- className: cn(
705
- "flex gap-4",
706
- entry.props.orientation === "vertical" ? "flex-col" : "flex-row"
707
- ),
708
- children: renderChildren(entry.schema)
709
- },
710
- key
711
- );
712
- case "tabs": {
713
- const tabs = (entry.schema ?? []).filter((c) => isLayoutEntry(c) && c.type === "tab").map((tab) => ({
714
- id: tab.props.id,
715
- label: tab.props.label,
716
- badge: tab.props.badge,
717
- content: renderChildren(tab.schema) ?? /* @__PURE__ */ jsx("span", {}, `tab-${tab.props.id}-empty`)
718
- }));
719
- return /* @__PURE__ */ jsx(FormTabs, { config: entry.props, tabs }, key);
720
- }
721
- case "tab":
722
- return null;
723
- }
724
- };
725
- const columns = schema.columns ?? 1;
726
- return /* @__PURE__ */ jsx(
727
- "div",
728
- {
729
- "data-arqel-form": "",
730
- className: cn("grid gap-4", className),
731
- style: columns > 1 ? { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` } : void 0,
732
- children: schema.schema.map((entry, i) => renderEntry(entry, `${i}`))
733
- }
734
- );
735
- }
736
- var init_FormRenderer = __esm({
737
- "src/form/FormRenderer.tsx"() {
738
- init_cn();
739
- init_FieldRenderer();
740
- init_FormFieldset();
741
- init_FormGrid();
742
- init_FormSection();
743
- init_FormTabs();
744
- }
745
- });
746
- function PageHeader({ title, description, actions, className }) {
747
- return /* @__PURE__ */ jsxs("header", { className: cn("flex flex-wrap items-start gap-4", className), children: [
748
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
749
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
750
- description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
751
- ] }),
752
- actions && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions })
753
- ] });
754
- }
755
- var init_PageHeader = __esm({
756
- "src/utility/PageHeader.tsx"() {
757
- init_cn();
758
- }
759
- });
760
-
761
- // src/pages/ArqelCreatePage.tsx
762
- var ArqelCreatePage_exports = {};
763
- __export(ArqelCreatePage_exports, {
764
- default: () => ArqelCreatePage
765
- });
766
- function ArqelCreatePage() {
767
- const page = usePage();
768
- const props = page.props;
769
- const fields = props.fields ?? [];
770
- const schema = props.form ?? FALLBACK_SCHEMA;
771
- const form = useArqelForm({ fields });
772
- const submit = (e) => {
773
- e.preventDefault();
774
- const slug = props.resource?.slug ?? "";
775
- const basePath = props.resource?.panelPath ?? "/admin";
776
- router.post(`${basePath}/${slug}`, form.data, {
777
- preserveScroll: true
778
- });
779
- };
780
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
781
- /* @__PURE__ */ jsx(PageHeader, { title: `Create ${props.resource?.label ?? "record"}`, description: null }),
782
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-6", children: [
783
- /* @__PURE__ */ jsx(
784
- FormRenderer,
785
- {
786
- schema,
787
- fields,
788
- values: form.data,
789
- onChange: (name, value) => form.setData(name, value),
790
- errors: form.errors
791
- }
792
- ),
793
- /* @__PURE__ */ jsx(FormActions, { processing: form.processing, onCancel: () => window.history.back() })
794
- ] })
795
- ] });
796
- }
797
- var FALLBACK_SCHEMA;
798
- var init_ArqelCreatePage = __esm({
799
- "src/pages/ArqelCreatePage.tsx"() {
800
- init_FormActions();
801
- init_FormRenderer();
802
- init_PageHeader();
803
- FALLBACK_SCHEMA = {
804
- schema: [],
805
- columns: 1,
806
- model: null,
807
- inline: false,
808
- disabled: false
809
- };
810
- }
811
- });
812
- function resolveColumnSpan(columnSpan) {
813
- if (columnSpan === void 0) return void 0;
814
- if (typeof columnSpan === "number") return `col-span-${columnSpan}`;
815
- return columnSpan;
816
- }
817
- function WidgetWrapper({
818
- heading,
819
- description,
820
- loading = false,
821
- error = null,
822
- onRetry,
823
- columnSpan,
824
- className,
825
- children
826
- }) {
827
- const baseClass = cn(
828
- "rounded border bg-background p-4",
829
- resolveColumnSpan(columnSpan),
830
- className
831
- );
832
- if (loading) {
833
- return /* @__PURE__ */ jsx(
834
- "section",
835
- {
836
- "aria-label": heading,
837
- "aria-busy": "true",
838
- className: baseClass,
839
- "data-widget-state": "loading",
840
- children: /* @__PURE__ */ jsx("div", { className: "animate-pulse h-32 bg-muted rounded" })
841
- }
842
- );
843
- }
844
- if (error) {
845
- return /* @__PURE__ */ jsx("section", { "aria-label": heading, className: baseClass, "data-widget-state": "error", children: /* @__PURE__ */ jsxs("div", { role: "alert", className: "flex flex-col gap-2 text-sm text-destructive", children: [
846
- /* @__PURE__ */ jsx("p", { children: error.message }),
847
- onRetry && /* @__PURE__ */ jsx(
848
- "button",
849
- {
850
- type: "button",
851
- onClick: onRetry,
852
- className: "self-start rounded border px-2 py-1 text-xs text-foreground hover:bg-muted",
853
- children: "Retry"
854
- }
855
- )
856
- ] }) });
857
- }
858
- return /* @__PURE__ */ jsxs("section", { "aria-label": heading, className: baseClass, "data-widget-state": "ready", children: [
859
- heading && /* @__PURE__ */ jsx("h2", { className: "text-sm font-medium text-muted-foreground", children: heading }),
860
- description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: description }),
861
- /* @__PURE__ */ jsx("div", { className: cn(heading || description ? "mt-3" : void 0), children })
862
- ] });
863
- }
864
- var init_WidgetWrapper = __esm({
865
- "src/widgets/WidgetWrapper.tsx"() {
866
- init_cn();
867
- }
868
- });
869
-
870
- // src/widgets/types.ts
871
- function toRowShape(data) {
872
- return data.labels.map((label, i) => {
873
- const row = { name: label };
874
- data.datasets.forEach((ds, j) => {
875
- row[`dataset${j}`] = ds.data[i] ?? 0;
876
- });
877
- return row;
878
- });
879
- }
880
- function colorFor(dataset, index) {
881
- return dataset.color ?? PALETTE[index % PALETTE.length] ?? "#2563eb";
882
- }
883
- var PALETTE;
884
- var init_types = __esm({
885
- "src/widgets/types.ts"() {
886
- PALETTE = [
887
- "#2563eb",
888
- "#10b981",
889
- "#f59e0b",
890
- "#ef4444",
891
- "#8b5cf6",
892
- "#ec4899",
893
- "#14b8a6",
894
- "#f97316"
895
- ];
896
- }
897
- });
898
-
899
- // src/widgets/charts/LineChart.tsx
900
- var LineChart_exports = {};
901
- __export(LineChart_exports, {
902
- LineChart: () => LineChart
903
- });
904
- function LineChart({ chartData, height, showLegend, showGrid }) {
905
- const rows = toRowShape(chartData);
906
- return /* @__PURE__ */ jsx("div", { "data-testid": "chart-line", style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(LineChart$1, { data: rows, children: [
907
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
908
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
909
- /* @__PURE__ */ jsx(YAxis, {}),
910
- /* @__PURE__ */ jsx(Tooltip, {}),
911
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
912
- chartData.datasets.map((ds, i) => /* @__PURE__ */ jsx(
913
- Line,
914
- {
915
- type: "monotone",
916
- dataKey: `dataset${i}`,
917
- name: ds.label,
918
- stroke: colorFor(ds, i),
919
- strokeWidth: 2,
920
- dot: false
921
- },
922
- ds.label
923
- ))
924
- ] }) }) });
925
- }
926
- var init_LineChart = __esm({
927
- "src/widgets/charts/LineChart.tsx"() {
928
- init_types();
929
- }
930
- });
931
-
932
- // src/widgets/charts/BarChart.tsx
933
- var BarChart_exports = {};
934
- __export(BarChart_exports, {
935
- BarChart: () => BarChart
936
- });
937
- function BarChart({ chartData, height, showLegend, showGrid }) {
938
- const rows = toRowShape(chartData);
939
- return /* @__PURE__ */ jsx("div", { "data-testid": "chart-bar", style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart$1, { data: rows, children: [
940
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
941
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
942
- /* @__PURE__ */ jsx(YAxis, {}),
943
- /* @__PURE__ */ jsx(Tooltip, {}),
944
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
945
- chartData.datasets.map((ds, i) => /* @__PURE__ */ jsx(Bar, { dataKey: `dataset${i}`, name: ds.label, fill: colorFor(ds, i) }, ds.label))
946
- ] }) }) });
947
- }
948
- var init_BarChart = __esm({
949
- "src/widgets/charts/BarChart.tsx"() {
950
- init_types();
951
- }
952
- });
953
-
954
- // src/widgets/charts/AreaChart.tsx
955
- var AreaChart_exports = {};
956
- __export(AreaChart_exports, {
957
- AreaChart: () => AreaChart
958
- });
959
- function AreaChart({ chartData, height, showLegend, showGrid }) {
960
- const rows = toRowShape(chartData);
961
- return /* @__PURE__ */ jsx("div", { "data-testid": "chart-area", style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(AreaChart$1, { data: rows, children: [
962
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
963
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
964
- /* @__PURE__ */ jsx(YAxis, {}),
965
- /* @__PURE__ */ jsx(Tooltip, {}),
966
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
967
- chartData.datasets.map((ds, i) => {
968
- const color = colorFor(ds, i);
969
- return /* @__PURE__ */ jsx(
970
- Area,
971
- {
972
- type: "monotone",
973
- dataKey: `dataset${i}`,
974
- name: ds.label,
975
- stroke: color,
976
- fill: color,
977
- fillOpacity: 0.3
978
- },
979
- ds.label
980
- );
981
- })
982
- ] }) }) });
983
- }
984
- var init_AreaChart = __esm({
985
- "src/widgets/charts/AreaChart.tsx"() {
986
- init_types();
987
- }
988
- });
989
-
990
- // src/widgets/charts/PieChart.tsx
991
- var PieChart_exports = {};
992
- __export(PieChart_exports, {
993
- PieChart: () => PieChart
994
- });
995
- function PieChart({
996
- chartData,
997
- height,
998
- showLegend,
999
- innerRadius = 0,
1000
- testId = "chart-pie"
1001
- }) {
1002
- const first = chartData.datasets[0];
1003
- const slices = (first?.data ?? []).map((value, i) => ({
1004
- name: chartData.labels[i] ?? `Slice ${i + 1}`,
1005
- value
1006
- }));
1007
- return /* @__PURE__ */ jsx("div", { "data-testid": testId, style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart$1, { children: [
1008
- /* @__PURE__ */ jsx(
1009
- Pie,
1010
- {
1011
- data: slices,
1012
- dataKey: "value",
1013
- nameKey: "name",
1014
- innerRadius,
1015
- outerRadius: "80%",
1016
- label: true,
1017
- children: slices.map((slice, i) => /* @__PURE__ */ jsx(Cell, { fill: colorFor(first ?? { }, i) }, slice.name))
1018
- }
1019
- ),
1020
- /* @__PURE__ */ jsx(Tooltip, {}),
1021
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
1022
- ] }) }) });
1023
- }
1024
- var init_PieChart = __esm({
1025
- "src/widgets/charts/PieChart.tsx"() {
1026
- init_types();
1027
- }
1028
- });
1029
-
1030
- // src/widgets/charts/DonutChart.tsx
1031
- var DonutChart_exports = {};
1032
- __export(DonutChart_exports, {
1033
- DonutChart: () => DonutChart
1034
- });
1035
- function DonutChart(props) {
1036
- return /* @__PURE__ */ jsx(PieChart, { ...props, innerRadius: 60, testId: "chart-donut" });
1037
- }
1038
- var init_DonutChart = __esm({
1039
- "src/widgets/charts/DonutChart.tsx"() {
1040
- init_PieChart();
1041
- }
1042
- });
1043
-
1044
- // src/widgets/charts/RadarChart.tsx
1045
- var RadarChart_exports = {};
1046
- __export(RadarChart_exports, {
1047
- RadarChart: () => RadarChart
1048
- });
1049
- function RadarChart({ chartData, height, showLegend, showGrid }) {
1050
- const rows = toRowShape(chartData);
1051
- return /* @__PURE__ */ jsx("div", { "data-testid": "chart-radar", style: { width: "100%", height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(RadarChart$1, { data: rows, children: [
1052
- showGrid ? /* @__PURE__ */ jsx(PolarGrid, {}) : null,
1053
- /* @__PURE__ */ jsx(PolarAngleAxis, { dataKey: "name" }),
1054
- /* @__PURE__ */ jsx(PolarRadiusAxis, {}),
1055
- chartData.datasets.map((ds, i) => {
1056
- const color = colorFor(ds, i);
1057
- return /* @__PURE__ */ jsx(
1058
- Radar,
1059
- {
1060
- dataKey: `dataset${i}`,
1061
- name: ds.label,
1062
- stroke: color,
1063
- fill: color,
1064
- fillOpacity: 0.3
1065
- },
1066
- ds.label
1067
- );
1068
- }),
1069
- /* @__PURE__ */ jsx(Tooltip, {}),
1070
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
1071
- ] }) }) });
1072
- }
1073
- var init_RadarChart = __esm({
1074
- "src/widgets/charts/RadarChart.tsx"() {
1075
- init_types();
1076
- }
1077
- });
1078
- function ChartCard({ widget }) {
1079
- const Chart = REGISTRY[widget.chartType] ?? LineChart2;
1080
- return /* @__PURE__ */ jsx(WidgetWrapper, { heading: widget.heading, description: widget.description, children: /* @__PURE__ */ jsx(
1081
- Suspense,
1082
- {
1083
- fallback: /* @__PURE__ */ jsx(
1084
- "div",
1085
- {
1086
- role: "status",
1087
- "aria-label": "Loading chart",
1088
- "data-testid": "chart-fallback",
1089
- className: "animate-pulse rounded bg-muted",
1090
- style: { height: widget.height }
1091
- }
1092
- ),
1093
- children: /* @__PURE__ */ jsx(
1094
- Chart,
1095
- {
1096
- chartData: widget.chartData,
1097
- chartOptions: widget.chartOptions,
1098
- height: widget.height,
1099
- showLegend: widget.showLegend,
1100
- showGrid: widget.showGrid
1101
- }
1102
- )
1103
- }
1104
- ) });
1105
- }
1106
- var LineChart2, BarChart2, AreaChart2, PieChart2, DonutChart2, RadarChart2, REGISTRY;
1107
- var init_ChartCard = __esm({
1108
- "src/widgets/ChartCard.tsx"() {
1109
- init_WidgetWrapper();
1110
- LineChart2 = lazy(() => Promise.resolve().then(() => (init_LineChart(), LineChart_exports)).then((m) => ({ default: m.LineChart })));
1111
- BarChart2 = lazy(() => Promise.resolve().then(() => (init_BarChart(), BarChart_exports)).then((m) => ({ default: m.BarChart })));
1112
- AreaChart2 = lazy(() => Promise.resolve().then(() => (init_AreaChart(), AreaChart_exports)).then((m) => ({ default: m.AreaChart })));
1113
- PieChart2 = lazy(() => Promise.resolve().then(() => (init_PieChart(), PieChart_exports)).then((m) => ({ default: m.PieChart })));
1114
- DonutChart2 = lazy(
1115
- () => Promise.resolve().then(() => (init_DonutChart(), DonutChart_exports)).then((m) => ({ default: m.DonutChart }))
1116
- );
1117
- RadarChart2 = lazy(
1118
- () => Promise.resolve().then(() => (init_RadarChart(), RadarChart_exports)).then((m) => ({ default: m.RadarChart }))
1119
- );
1120
- REGISTRY = {
1121
- line: LineChart2,
1122
- bar: BarChart2,
1123
- area: AreaChart2,
1124
- pie: PieChart2,
1125
- donut: DonutChart2,
1126
- radar: RadarChart2
1127
- };
1128
- }
1129
- });
1130
- function DashboardFilters({ filters, values, onChange, className }) {
1131
- if (!filters || filters.length === 0) return null;
1132
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-2", className), "data-testid": "dashboard-filters", children: filters.map((filter) => /* @__PURE__ */ jsx(
1133
- FilterControl,
1134
- {
1135
- filter,
1136
- value: values[filter.name],
1137
- onChange: (v) => onChange(filter.name, v)
1138
- },
1139
- filter.name
1140
- )) });
1141
- }
1142
- function FilterControl({
1143
- filter,
1144
- value,
1145
- onChange
1146
- }) {
1147
- if (filter.type === "select") {
1148
- return /* @__PURE__ */ jsx(SelectControl, { filter, value, onChange });
1149
- }
1150
- if (filter.type === "date_range") {
1151
- return /* @__PURE__ */ jsx(
1152
- DateRangeControl,
1153
- {
1154
- filter,
1155
- value,
1156
- onChange
1157
- }
1158
- );
1159
- }
1160
- return null;
1161
- }
1162
- function controlClasses() {
1163
- return cn(
1164
- "h-9 rounded-sm border border-[var(--input)]",
1165
- "bg-background px-2 text-sm",
1166
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1167
- );
1168
- }
1169
- function normaliseOptions2(raw) {
1170
- if (!raw) return [];
1171
- if (Array.isArray(raw)) {
1172
- return raw.map((o) => ({ value: String(o.value), label: String(o.label ?? o.value) }));
1173
- }
1174
- return Object.entries(raw).map(([k, v]) => ({ value: String(k), label: String(v) }));
1175
- }
1176
- function SelectControl({
1177
- filter,
1178
- value,
1179
- onChange
1180
- }) {
1181
- const options = normaliseOptions2(filter.options);
1182
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1183
- filter.label ?? filter.name,
1184
- /* @__PURE__ */ jsxs(
1185
- "select",
1186
- {
1187
- "aria-label": filter.label ?? filter.name,
1188
- className: controlClasses(),
1189
- value: value === void 0 || value === null ? "" : String(value),
1190
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
1191
- children: [
1192
- /* @__PURE__ */ jsx("option", { value: "", children: "All" }),
1193
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
1194
- ]
1195
- }
1196
- )
1197
- ] });
1198
- }
1199
- function DateRangeControl({
1200
- filter,
1201
- value,
1202
- onChange
1203
- }) {
1204
- const range = value && typeof value === "object" ? value : {};
1205
- const fromValue = typeof range.from === "string" ? range.from : "";
1206
- const toValue = typeof range.to === "string" ? range.to : "";
1207
- return /* @__PURE__ */ jsxs("fieldset", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1208
- /* @__PURE__ */ jsx("legend", { children: filter.label ?? filter.name }),
1209
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1210
- /* @__PURE__ */ jsx(
1211
- "input",
1212
- {
1213
- type: "date",
1214
- "aria-label": `${filter.label ?? filter.name} from`,
1215
- className: controlClasses(),
1216
- value: fromValue,
1217
- onChange: (e) => onChange({ from: e.target.value || null, to: range.to ?? null })
1218
- }
1219
- ),
1220
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2013" }),
1221
- /* @__PURE__ */ jsx(
1222
- "input",
1223
- {
1224
- type: "date",
1225
- "aria-label": `${filter.label ?? filter.name} to`,
1226
- className: controlClasses(),
1227
- value: toValue,
1228
- onChange: (e) => onChange({ from: range.from ?? null, to: e.target.value || null })
1229
- }
1230
- )
1231
- ] })
1232
- ] });
1233
- }
1234
- var init_DashboardFilters = __esm({
1235
- "src/widgets/DashboardFilters.tsx"() {
1236
- init_cn();
1237
- }
1238
- });
1239
- function Sparkline({ points, color }) {
1240
- if (points.length < 2) return null;
1241
- const min = Math.min(...points);
1242
- const max = Math.max(...points);
1243
- const range = max - min || 1;
1244
- const stepX = 100 / (points.length - 1);
1245
- const coords = points.map((value, index) => {
1246
- const x = index * stepX;
1247
- const y = 30 - (value - min) / range * 30;
1248
- return `${x.toFixed(2)},${y.toFixed(2)}`;
1249
- }).join(" ");
1250
- return /* @__PURE__ */ jsx(
1251
- "svg",
1252
- {
1253
- viewBox: "0 0 100 30",
1254
- preserveAspectRatio: "none",
1255
- className: cn("mt-3 h-8 w-full", COLOR_STROKE[color]),
1256
- role: "img",
1257
- "aria-label": "Trend sparkline",
1258
- "data-testid": "stat-card-sparkline",
1259
- children: /* @__PURE__ */ jsx(
1260
- "polyline",
1261
- {
1262
- fill: "none",
1263
- stroke: "currentColor",
1264
- strokeWidth: "1.5",
1265
- strokeLinecap: "round",
1266
- strokeLinejoin: "round",
1267
- points: coords
1268
- }
1269
- )
1270
- }
1271
- );
1272
- }
1273
- function StatCard({ widget, className, columnSpan }) {
1274
- const { heading, description, value, statDescription, descriptionIcon, color, chart, url } = widget;
1275
- const hasChart = Array.isArray(chart) && chart.length >= 2;
1276
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
1277
- /* @__PURE__ */ jsx("div", { className: cn("text-4xl font-bold tabular-nums", COLOR_TEXT[color]), children: value ?? "\u2014" }),
1278
- statDescription && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-1 text-xs text-muted-foreground", children: [
1279
- descriptionIcon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-testid": "stat-card-icon", children: descriptionIcon }),
1280
- /* @__PURE__ */ jsx("span", { children: statDescription })
1281
- ] }),
1282
- hasChart && /* @__PURE__ */ jsx(Sparkline, { points: chart, color })
1283
- ] });
1284
- const inner = url ? /* @__PURE__ */ jsx(
1285
- "a",
1286
- {
1287
- href: url,
1288
- className: "block rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
1289
- "data-testid": "stat-card-link",
1290
- children: body
1291
- }
1292
- ) : body;
1293
- return /* @__PURE__ */ jsx(
1294
- WidgetWrapper,
1295
- {
1296
- heading,
1297
- description,
1298
- columnSpan,
1299
- className,
1300
- children: inner
1301
- }
1302
- );
1303
- }
1304
- var COLOR_TEXT, COLOR_STROKE;
1305
- var init_StatCard = __esm({
1306
- "src/widgets/StatCard.tsx"() {
1307
- init_cn();
1308
- init_WidgetWrapper();
1309
- COLOR_TEXT = {
1310
- primary: "text-primary",
1311
- secondary: "text-secondary-foreground",
1312
- success: "text-green-600",
1313
- warning: "text-amber-600",
1314
- danger: "text-red-600",
1315
- info: "text-sky-600"
1316
- };
1317
- COLOR_STROKE = {
1318
- primary: "stroke-primary",
1319
- secondary: "stroke-secondary-foreground",
1320
- success: "stroke-green-500",
1321
- warning: "stroke-amber-500",
1322
- danger: "stroke-red-500",
1323
- info: "stroke-sky-500"
1324
- };
1325
- }
1326
- });
1327
- function TableCard({ widget, className }) {
1328
- return /* @__PURE__ */ jsx(
1329
- WidgetWrapper,
1330
- {
1331
- heading: widget.heading ?? void 0,
1332
- description: widget.description ?? void 0,
1333
- className,
1334
- children: widget.loadError ? /* @__PURE__ */ jsx("div", { role: "alert", className: "text-sm text-red-600", children: widget.loadError }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1335
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: cn("w-full border-collapse text-sm", "text-foreground"), children: [
1336
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border text-left", children: widget.columns.map((column) => /* @__PURE__ */ jsx(
1337
- "th",
1338
- {
1339
- scope: "col",
1340
- className: "px-2 py-1.5 font-medium text-muted-foreground",
1341
- children: column.label ?? column.name
1342
- },
1343
- column.name
1344
- )) }) }),
1345
- /* @__PURE__ */ jsx("tbody", { children: widget.records.map((record, rowIndex) => /* @__PURE__ */ jsx(
1346
- "tr",
1347
- {
1348
- className: "border-b border-border last:border-0",
1349
- children: widget.columns.map((column) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5", children: formatCell(record[column.name]) }, column.name))
1350
- },
1351
- rowIndex
1352
- )) })
1353
- ] }) }),
1354
- widget.seeAllUrl && /* @__PURE__ */ jsx("div", { className: "mt-3 text-right", children: /* @__PURE__ */ jsx("a", { href: widget.seeAllUrl, className: "text-sm text-primary", children: "See all \u2192" }) })
1355
- ] })
1356
- }
1357
- );
1358
- }
1359
- function formatCell(value) {
1360
- if (value === null || value === void 0) return "";
1361
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1362
- return String(value);
1363
- }
1364
- try {
1365
- return JSON.stringify(value);
1366
- } catch {
1367
- return "";
1368
- }
1369
- }
1370
- var init_TableCard = __esm({
1371
- "src/widgets/TableCard.tsx"() {
1372
- init_cn();
1373
- init_WidgetWrapper();
1374
- }
1375
- });
1376
- function WidgetRenderer({ widget, filterValues, fetcher }) {
1377
- const [data, setData] = useState(widget.data ?? null);
1378
- const filterRef = useRef(filterValues);
1379
- filterRef.current = filterValues;
1380
- const dashboardId = widget.dashboardId;
1381
- const widgetId = widget.widgetId ?? widget.name;
1382
- const poll = typeof widget.poll === "number" ? widget.poll : 0;
1383
- const deferred = widget.deferred === true;
1384
- const hasInlineDataRef = useRef(widget.data !== null && widget.data !== void 0);
1385
- useEffect(() => {
1386
- if (!dashboardId || !widgetId) return void 0;
1387
- const url = `/admin/dashboards/${dashboardId}/widgets/${widgetId}/data`;
1388
- const doFetch = async () => {
1389
- try {
1390
- const result = fetcher ? await fetcher(url) : await fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
1391
- setData(result);
1392
- } catch {
1393
- }
1394
- };
1395
- let interval;
1396
- if (deferred && !hasInlineDataRef.current) {
1397
- void doFetch();
1398
- }
1399
- if (poll > 0) {
1400
- interval = setInterval(() => {
1401
- void doFetch();
1402
- }, poll * 1e3);
1403
- }
1404
- return () => {
1405
- if (interval) clearInterval(interval);
1406
- };
1407
- }, [dashboardId, widgetId, poll, deferred, fetcher]);
1408
- const merged = mergeData(widget, data);
1409
- switch (widget.type) {
1410
- case "stat":
1411
- return /* @__PURE__ */ jsx(StatCard, { widget: merged });
1412
- case "chart":
1413
- return /* @__PURE__ */ jsx(ChartCard, { widget: merged });
1414
- case "table":
1415
- return /* @__PURE__ */ jsx(TableCard, { widget: merged });
1416
- default:
1417
- return /* @__PURE__ */ jsxs("div", { role: "alert", className: "text-sm text-red-600", children: [
1418
- "Widget type ",
1419
- widget.type,
1420
- " not registered"
1421
- ] });
1422
- }
1423
- }
1424
- function mergeData(widget, data) {
1425
- if (data && typeof data === "object" && !Array.isArray(data)) {
1426
- return { ...widget, ...data };
1427
- }
1428
- return { ...widget, data };
1429
- }
1430
- var init_WidgetRenderer = __esm({
1431
- "src/widgets/WidgetRenderer.tsx"() {
1432
- init_ChartCard();
1433
- init_StatCard();
1434
- init_TableCard();
1435
- }
1436
- });
1437
- function DashboardGrid({
1438
- dashboard,
1439
- filterValues,
1440
- onFilterChange,
1441
- className
1442
- }) {
1443
- const [internalValues, setInternalValues] = useState(filterValues ?? {});
1444
- const values = filterValues ?? internalValues;
1445
- const handleChange = (name, value) => {
1446
- if (onFilterChange) {
1447
- onFilterChange(name, value);
1448
- } else {
1449
- setInternalValues((prev) => ({ ...prev, [name]: value }));
1450
- }
1451
- };
1452
- const filterPayload = normaliseFilters(dashboard.filters);
1453
- const heading = dashboard.heading ?? dashboard.label;
1454
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
1455
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [
1456
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1457
- heading && /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: heading }),
1458
- dashboard.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: dashboard.description })
1459
- ] }),
1460
- filterPayload.length > 0 && /* @__PURE__ */ jsx(DashboardFilters, { filters: filterPayload, values, onChange: handleChange })
1461
- ] }),
1462
- /* @__PURE__ */ jsx(
1463
- "div",
1464
- {
1465
- className: cn("grid gap-4", gridColsClass(dashboard.columns)),
1466
- "data-testid": "dashboard-grid",
1467
- children: dashboard.widgets.map((widget) => {
1468
- const rawSpan = widget.columnSpan;
1469
- const span = typeof rawSpan === "number" ? rawSpan : 1;
1470
- const style = { gridColumn: `span ${span}` };
1471
- return /* @__PURE__ */ jsx("div", { style, "data-widget-slot": widget.name, children: /* @__PURE__ */ jsx(WidgetRenderer, { widget, filterValues: values }) }, widget.name);
1472
- })
1473
- }
1474
- )
1475
- ] });
1476
- }
1477
- function gridColsClass(columns) {
1478
- if (typeof columns === "number") {
1479
- return `grid-cols-${Math.max(1, Math.min(12, columns))}`;
1480
- }
1481
- const parts = [];
1482
- parts.push("grid-cols-1");
1483
- for (const bp of ["sm", "md", "lg", "xl", "2xl"]) {
1484
- const value = columns[bp];
1485
- if (typeof value === "number") {
1486
- parts.push(`${BREAKPOINT_PREFIX[bp]}grid-cols-${Math.max(1, Math.min(12, value))}`);
1487
- }
1488
- }
1489
- return parts.join(" ");
1490
- }
1491
- function normaliseFilters(raw) {
1492
- if (!raw) return [];
1493
- if (Array.isArray(raw)) return raw;
1494
- return [];
1495
- }
1496
- var BREAKPOINT_PREFIX;
1497
- var init_DashboardGrid = __esm({
1498
- "src/widgets/DashboardGrid.tsx"() {
1499
- init_cn();
1500
- init_DashboardFilters();
1501
- init_WidgetRenderer();
1502
- BREAKPOINT_PREFIX = {
1503
- sm: "sm:",
1504
- md: "md:",
1505
- lg: "lg:",
1506
- xl: "xl:",
1507
- "2xl": "2xl:"
1508
- };
1509
- }
1510
- });
1511
-
1512
- // src/widgets/index.ts
1513
- var init_widgets = __esm({
1514
- "src/widgets/index.ts"() {
1515
- init_DashboardGrid();
1516
- }
1517
- });
1518
-
1519
- // src/pages/ArqelDashboardPage.tsx
1520
- var ArqelDashboardPage_exports = {};
1521
- __export(ArqelDashboardPage_exports, {
1522
- default: () => ArqelDashboardPage
1523
- });
1524
- function ArqelDashboardPage() {
1525
- const { dashboard, filterValues } = usePage().props;
1526
- const handleFilterChange = (name, value) => {
1527
- const current = filterValues ?? {};
1528
- const next = { ...current };
1529
- if (value === void 0 || value === null || value === "") {
1530
- delete next[name];
1531
- } else {
1532
- next[name] = value;
1533
- }
1534
- const path = dashboard.path ?? `/admin/dashboards/${dashboard.id}`;
1535
- const params = Object.keys(next).length > 0 ? { filters: next } : {};
1536
- router.get(path, params, {
1537
- preserveState: true,
1538
- preserveScroll: true,
1539
- replace: true
1540
- });
1541
- };
1542
- return /* @__PURE__ */ jsx(
1543
- DashboardGrid,
1544
- {
1545
- dashboard,
1546
- filterValues: filterValues ?? {},
1547
- onFilterChange: handleFilterChange
1548
- }
1549
- );
1550
- }
1551
- var init_ArqelDashboardPage = __esm({
1552
- "src/pages/ArqelDashboardPage.tsx"() {
1553
- init_widgets();
1554
- }
1555
- });
1556
-
1557
- // src/pages/ArqelEditPage.tsx
1558
- var ArqelEditPage_exports = {};
1559
- __export(ArqelEditPage_exports, {
1560
- default: () => ArqelEditPage
1561
- });
1562
- function ArqelEditPage() {
1563
- const page = usePage();
1564
- const props = page.props;
1565
- const fields = props.fields ?? [];
1566
- const record = props.record;
1567
- const schema = props.form ?? FALLBACK_SCHEMA2;
1568
- const form = useArqelForm({ fields, record });
1569
- const submit = (e) => {
1570
- e.preventDefault();
1571
- const id = String(record?.id ?? "");
1572
- const slug = props.resource?.slug ?? "";
1573
- const basePath = props.resource?.panelPath ?? "/admin";
1574
- router.put(`${basePath}/${slug}/${id}`, form.data, {
1575
- preserveScroll: true
1576
- });
1577
- };
1578
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1579
- /* @__PURE__ */ jsx(
1580
- PageHeader,
1581
- {
1582
- title: props.recordTitle ?? `Edit ${props.resource?.label ?? "record"}`,
1583
- description: props.recordSubtitle ?? null
1584
- }
1585
- ),
1586
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-6", children: [
1587
- /* @__PURE__ */ jsx(
1588
- FormRenderer,
1589
- {
1590
- schema,
1591
- fields,
1592
- values: form.data,
1593
- onChange: (name, value) => form.setData(name, value),
1594
- errors: form.errors
1595
- }
1596
- ),
1597
- /* @__PURE__ */ jsx(FormActions, { processing: form.processing, onCancel: () => window.history.back() })
1598
- ] })
1599
- ] });
1600
- }
1601
- var FALLBACK_SCHEMA2;
1602
- var init_ArqelEditPage = __esm({
1603
- "src/pages/ArqelEditPage.tsx"() {
1604
- init_FormActions();
1605
- init_FormRenderer();
1606
- init_PageHeader();
1607
- FALLBACK_SCHEMA2 = {
1608
- schema: [],
1609
- columns: 1,
1610
- model: null,
1611
- inline: false,
1612
- disabled: false
1613
- };
1614
- }
1615
- });
1616
- function DropdownMenu({
1617
- ...props
1618
- }) {
1619
- return /* @__PURE__ */ jsx(DropdownMenu$1.Root, { "data-slot": "dropdown-menu", ...props });
1620
- }
1621
- function DropdownMenuTrigger({
1622
- ...props
1623
- }) {
1624
- return /* @__PURE__ */ jsx(
1625
- DropdownMenu$1.Trigger,
1626
- {
1627
- "data-slot": "dropdown-menu-trigger",
1628
- ...props
1629
- }
1630
- );
1631
- }
1632
- function DropdownMenuContent({
1633
- className,
1634
- sideOffset = 4,
1635
- ...props
1636
- }) {
1637
- return /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(
1638
- DropdownMenu$1.Content,
1639
- {
1640
- "data-slot": "dropdown-menu-content",
1641
- sideOffset,
1642
- className: cn(
1643
- "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1644
- className
1645
- ),
1646
- ...props
1647
- }
1648
- ) });
1649
- }
1650
- function DropdownMenuItem({
1651
- className,
1652
- inset,
1653
- variant = "default",
1654
- ...props
1655
- }) {
1656
- return /* @__PURE__ */ jsx(
1657
- DropdownMenu$1.Item,
1658
- {
1659
- "data-slot": "dropdown-menu-item",
1660
- "data-inset": inset,
1661
- "data-variant": variant,
1662
- className: cn(
1663
- "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
1664
- className
1665
- ),
1666
- ...props
1667
- }
1668
- );
1669
- }
1670
- var init_dropdown_menu = __esm({
1671
- "src/shadcn/ui/dropdown-menu.tsx"() {
1672
- "use client";
1673
- init_cn();
1674
- }
1675
- });
1676
- function Dialog({
1677
- ...props
1678
- }) {
1679
- return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
1680
- }
1681
- function DialogPortal({
1682
- ...props
1683
- }) {
1684
- return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
1685
- }
1686
- function DialogClose({
1687
- ...props
1688
- }) {
1689
- return /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
1690
- }
1691
- function DialogOverlay({
1692
- className,
1693
- ...props
1694
- }) {
1695
- return /* @__PURE__ */ jsx(
1696
- Dialog$1.Overlay,
1697
- {
1698
- "data-slot": "dialog-overlay",
1699
- className: cn(
1700
- "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
1701
- className
1702
- ),
1703
- ...props
1704
- }
1705
- );
1706
- }
1707
- function DialogContent({
1708
- className,
1709
- children,
1710
- showCloseButton = true,
1711
- ...props
1712
- }) {
1713
- return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
1714
- /* @__PURE__ */ jsx(DialogOverlay, {}),
1715
- /* @__PURE__ */ jsxs(
1716
- Dialog$1.Content,
1717
- {
1718
- "data-slot": "dialog-content",
1719
- className: cn(
1720
- "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
1721
- className
1722
- ),
1723
- ...props,
1724
- children: [
1725
- children,
1726
- showCloseButton && /* @__PURE__ */ jsxs(
1727
- Dialog$1.Close,
1728
- {
1729
- "data-slot": "dialog-close",
1730
- className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1731
- children: [
1732
- /* @__PURE__ */ jsx(XIcon, {}),
1733
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1734
- ]
1735
- }
1736
- )
1737
- ]
1738
- }
1739
- )
1740
- ] });
1741
- }
1742
- function DialogHeader({ className, ...props }) {
1743
- return /* @__PURE__ */ jsx(
1744
- "div",
1745
- {
1746
- "data-slot": "dialog-header",
1747
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1748
- ...props
1749
- }
1750
- );
1751
- }
1752
- function DialogFooter({
1753
- className,
1754
- showCloseButton = false,
1755
- children,
1756
- ...props
1757
- }) {
1758
- return /* @__PURE__ */ jsxs(
1759
- "div",
1760
- {
1761
- "data-slot": "dialog-footer",
1762
- className: cn(
1763
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1764
- className
1765
- ),
1766
- ...props,
1767
- children: [
1768
- children,
1769
- showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
1770
- ]
1771
- }
1772
- );
1773
- }
1774
- function DialogTitle({
1775
- className,
1776
- ...props
1777
- }) {
1778
- return /* @__PURE__ */ jsx(
1779
- Dialog$1.Title,
1780
- {
1781
- "data-slot": "dialog-title",
1782
- className: cn("text-lg leading-none font-semibold", className),
1783
- ...props
1784
- }
1785
- );
1786
- }
1787
- function DialogDescription({
1788
- className,
1789
- ...props
1790
- }) {
1791
- return /* @__PURE__ */ jsx(
1792
- Dialog$1.Description,
1793
- {
1794
- "data-slot": "dialog-description",
1795
- className: cn("text-sm text-muted-foreground", className),
1796
- ...props
1797
- }
1798
- );
1799
- }
1800
- var init_dialog = __esm({
1801
- "src/shadcn/ui/dialog.tsx"() {
1802
- init_cn();
1803
- init_button();
1804
- }
1805
- });
1806
- function ActionFormModal({
1807
- open,
1808
- onOpenChange,
1809
- action,
1810
- fields,
1811
- onSubmit,
1812
- processing = false,
1813
- errors = {},
1814
- initialValues = {}
1815
- }) {
1816
- const [values, setValues] = useState(initialValues);
1817
- const size = action.modalSize ?? "md";
1818
- const schema = useMemo(
1819
- () => ({
1820
- schema: (action.form ?? []).map((entry) => ({
1821
- kind: "field",
1822
- name: entry.name,
1823
- type: entry.type
1824
- })),
1825
- columns: 1,
1826
- model: null,
1827
- inline: false,
1828
- disabled: false
1829
- }),
1830
- [action.form]
1831
- );
1832
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: cn("overflow-auto", SIZE_CLASSES[size]), children: [
1833
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: action.label }) }),
1834
- /* @__PURE__ */ jsxs(
1835
- "form",
1836
- {
1837
- className: "flex flex-col gap-4",
1838
- onSubmit: (e) => {
1839
- e.preventDefault();
1840
- onSubmit(values);
1841
- },
1842
- children: [
1843
- /* @__PURE__ */ jsx(
1844
- FormRenderer,
1845
- {
1846
- schema,
1847
- fields,
1848
- values,
1849
- errors,
1850
- onChange: (name, value) => setValues((prev) => ({ ...prev, [name]: value })),
1851
- disabled: processing
1852
- }
1853
- ),
1854
- /* @__PURE__ */ jsx(
1855
- FormActions,
1856
- {
1857
- processing,
1858
- submitLabel: action.label,
1859
- extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: "Cancel" }) })
1860
- }
1861
- )
1862
- ]
1863
- }
1864
- )
1865
- ] }) });
1866
- }
1867
- var SIZE_CLASSES;
1868
- var init_ActionFormModal = __esm({
1869
- "src/action/ActionFormModal.tsx"() {
1870
- init_FormActions();
1871
- init_FormRenderer();
1872
- init_dialog();
1873
- init_cn();
1874
- init_Button();
1875
- SIZE_CLASSES = {
1876
- sm: "sm:max-w-[24rem]",
1877
- md: "sm:max-w-[32rem]",
1878
- lg: "sm:max-w-[44rem]",
1879
- xl: "sm:max-w-[56rem]",
1880
- full: "sm:max-w-[90vw] h-[90vh]"
1881
- };
1882
- }
1883
- });
1884
- function ConfirmDialog({
1885
- open,
1886
- onOpenChange,
1887
- config,
1888
- onConfirm,
1889
- processing = false
1890
- }) {
1891
- const [typed, setTyped] = useState("");
1892
- useEffect(() => {
1893
- if (!open) setTyped("");
1894
- }, [open]);
1895
- const requiresText = config?.requiresText;
1896
- const submitDisabled = processing || requiresText !== void 0 && typed !== requiresText;
1897
- const variant = COLOR_VARIANT[config?.color ?? "destructive"];
1898
- const submitLabel = config?.submitLabel ?? (config?.color === "info" ? "Confirm" : "Delete");
1899
- const cancelLabel = config?.cancelLabel ?? "Cancel";
1900
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[28rem]", showCloseButton: false, children: [
1901
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
1902
- /* @__PURE__ */ jsx(DialogTitle, { children: config?.heading ?? "Are you sure?" }),
1903
- config?.description && /* @__PURE__ */ jsx(DialogDescription, { children: config.description })
1904
- ] }),
1905
- requiresText !== void 0 && /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1906
- /* @__PURE__ */ jsxs("span", { children: [
1907
- "Type ",
1908
- /* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: requiresText }),
1909
- " to confirm"
1910
- ] }),
1911
- /* @__PURE__ */ jsx(
1912
- "input",
1913
- {
1914
- type: "text",
1915
- ref: (el) => el?.focus(),
1916
- value: typed,
1917
- onChange: (e) => setTyped(e.target.value),
1918
- onKeyDown: (e) => {
1919
- if (e.key === "Enter" && !submitDisabled) {
1920
- e.preventDefault();
1921
- onConfirm();
1922
- }
1923
- },
1924
- className: "h-9 rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1925
- }
1926
- )
1927
- ] }),
1928
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
1929
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: cancelLabel }) }),
1930
- /* @__PURE__ */ jsx(Button, { type: "button", variant, disabled: submitDisabled, onClick: onConfirm, children: processing ? "Working\u2026" : submitLabel })
1931
- ] })
1932
- ] }) });
1933
- }
1934
- var COLOR_VARIANT;
1935
- var init_ConfirmDialog = __esm({
1936
- "src/action/ConfirmDialog.tsx"() {
1937
- init_dialog();
1938
- init_Button();
1939
- COLOR_VARIANT = {
1940
- destructive: "destructive",
1941
- warning: "default",
1942
- info: "default"
1943
- };
1944
- }
1945
- });
1946
- function ActionButton({
1947
- action,
1948
- formFields = [],
1949
- onInvoke,
1950
- processing = false,
1951
- errors,
1952
- size = "default",
1953
- className
1954
- }) {
1955
- const [confirmOpen, setConfirmOpen] = useState(false);
1956
- const [formOpen, setFormOpen] = useState(false);
1957
- const variant = VARIANT_OVERRIDE[action.variant] ?? COLOR_TO_VARIANT[action.color];
1958
- const handleClick = () => {
1959
- if (action.disabled) return;
1960
- if (action.requiresConfirmation) {
1961
- setConfirmOpen(true);
1962
- } else if (action.form && action.form.length > 0) {
1963
- setFormOpen(true);
1964
- } else {
1965
- onInvoke();
1966
- }
1967
- };
1968
- const handleConfirm = () => {
1969
- setConfirmOpen(false);
1970
- if (action.form && action.form.length > 0) {
1971
- setFormOpen(true);
1972
- } else {
1973
- onInvoke();
1974
- }
1975
- };
1976
- const handleFormSubmit = (values) => {
1977
- onInvoke(values);
1978
- setFormOpen(false);
1979
- };
1980
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1981
- /* @__PURE__ */ jsx(
1982
- Button,
1983
- {
1984
- variant,
1985
- size,
1986
- disabled: action.disabled === true || processing,
1987
- title: action.tooltip,
1988
- onClick: handleClick,
1989
- className,
1990
- children: action.label
1991
- }
1992
- ),
1993
- action.requiresConfirmation && /* @__PURE__ */ jsx(
1994
- ConfirmDialog,
1995
- {
1996
- open: confirmOpen,
1997
- onOpenChange: setConfirmOpen,
1998
- config: action.confirmation,
1999
- onConfirm: handleConfirm,
2000
- processing
2001
- }
2002
- ),
2003
- action.form && action.form.length > 0 && /* @__PURE__ */ jsx(
2004
- ActionFormModal,
2005
- {
2006
- open: formOpen,
2007
- onOpenChange: setFormOpen,
2008
- action,
2009
- fields: formFields,
2010
- onSubmit: handleFormSubmit,
2011
- processing,
2012
- errors
2013
- }
2014
- )
2015
- ] });
2016
- }
2017
- var COLOR_TO_VARIANT, VARIANT_OVERRIDE;
2018
- var init_ActionButton = __esm({
2019
- "src/action/ActionButton.tsx"() {
2020
- init_ActionFormModal();
2021
- init_Button();
2022
- init_ConfirmDialog();
2023
- COLOR_TO_VARIANT = {
2024
- primary: "default",
2025
- secondary: "outline",
2026
- destructive: "destructive",
2027
- success: "default",
2028
- warning: "default",
2029
- info: "default"
2030
- };
2031
- VARIANT_OVERRIDE = {
2032
- default: "default",
2033
- outline: "outline",
2034
- ghost: "ghost",
2035
- destructive: "destructive"
2036
- };
2037
- }
2038
- });
2039
- function ActionMenu({
2040
- actions,
2041
- formFieldsByAction = {},
2042
- onInvoke,
2043
- inlineThreshold = 3,
2044
- processing = false,
2045
- trigger,
2046
- className
2047
- }) {
2048
- if (actions.length === 0) return null;
2049
- if (actions.length <= inlineThreshold) {
2050
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-1", className), children: actions.map((action) => /* @__PURE__ */ jsx(
2051
- ActionButton,
2052
- {
2053
- action,
2054
- formFields: formFieldsByAction[action.name] ?? [],
2055
- onInvoke: (values) => onInvoke(action, values),
2056
- processing,
2057
- size: "sm"
2058
- },
2059
- action.name
2060
- )) });
2061
- }
2062
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
2063
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Actions", children: "\u22EF" }) }),
2064
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", sideOffset: 6, className: cn("min-w-[12rem]", className), children: actions.map((action) => /* @__PURE__ */ jsx(
2065
- DropdownMenuItem,
2066
- {
2067
- disabled: action.disabled === true,
2068
- variant: action.color === "destructive" ? "destructive" : "default",
2069
- onSelect: () => {
2070
- if (action.disabled) return;
2071
- onInvoke(action);
2072
- },
2073
- children: action.label
2074
- },
2075
- action.name
2076
- )) })
2077
- ] });
2078
- }
2079
- var init_ActionMenu = __esm({
2080
- "src/action/ActionMenu.tsx"() {
2081
- init_dropdown_menu();
2082
- init_cn();
2083
- init_ActionButton();
2084
- init_Button();
2085
- }
2086
- });
2087
- function TableCell({ column, value }) {
2088
- switch (column.type) {
2089
- case "text":
2090
- return /* @__PURE__ */ jsx(TextCell, { column, value });
2091
- case "badge":
2092
- return /* @__PURE__ */ jsx(BadgeCell, { column, value });
2093
- case "boolean":
2094
- return /* @__PURE__ */ jsx(BooleanCell, { column, value });
2095
- case "date":
2096
- return /* @__PURE__ */ jsx(DateCell, { column, value });
2097
- case "number":
2098
- return /* @__PURE__ */ jsx(NumberCell, { column, value });
2099
- case "icon":
2100
- return /* @__PURE__ */ jsx(IconCell, { column });
2101
- case "image":
2102
- return /* @__PURE__ */ jsx(ImageCell, { column, value });
2103
- case "relationship":
2104
- return /* @__PURE__ */ jsx(RelationshipCell, { column, value });
2105
- case "computed":
2106
- return /* @__PURE__ */ jsx(ComputedCell, { column, value });
2107
- }
2108
- }
2109
- function asString(value) {
2110
- if (value === null || value === void 0) return "";
2111
- if (typeof value === "string") return value;
2112
- if (typeof value === "number" || typeof value === "boolean") return String(value);
2113
- return JSON.stringify(value);
2114
- }
2115
- function TextCell({ column, value }) {
2116
- const str = asString(value);
2117
- const truncated = column.props.truncate && str.length > column.props.truncate ? `${str.slice(0, column.props.truncate)}\u2026` : str;
2118
- return /* @__PURE__ */ jsx(
2119
- "span",
2120
- {
2121
- className: cn(
2122
- column.props.weight === "bold" && "font-bold",
2123
- column.props.weight === "medium" && "font-medium"
2124
- ),
2125
- children: truncated
2126
- }
2127
- );
2128
- }
2129
- function BadgeCell({ column, value }) {
2130
- const option = column.props.options?.find((o) => o.value === value);
2131
- const label = option?.label ?? asString(value);
2132
- return /* @__PURE__ */ jsx(
2133
- "span",
2134
- {
2135
- className: cn(
2136
- "inline-flex items-center px-2 py-0.5 text-xs",
2137
- column.props.pill ? "rounded-full" : "rounded-sm",
2138
- "bg-muted text-foreground"
2139
- ),
2140
- children: label
2141
- }
2142
- );
2143
- }
2144
- function BooleanCell({ column, value }) {
2145
- const truthy = Boolean(value);
2146
- return /* @__PURE__ */ jsx("span", { role: "img", "aria-label": truthy ? "true" : "false", children: truthy ? column.props.trueIcon ?? "\u2713" : column.props.falseIcon ?? "\u2014" });
2147
- }
2148
- function DateCell({ column, value }) {
2149
- if (!value) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
2150
- const raw = String(value);
2151
- const date = new Date(raw);
2152
- if (Number.isNaN(date.getTime())) return /* @__PURE__ */ jsx("span", { children: raw });
2153
- if (column.props.mode === "since") {
2154
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: formatRelative(date) });
2155
- }
2156
- if (column.props.mode === "datetime") {
2157
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: date.toLocaleString() });
2158
- }
2159
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: date.toLocaleDateString() });
2160
- }
2161
- function formatRelative(date) {
2162
- const seconds = Math.round((Date.now() - date.getTime()) / 1e3);
2163
- if (seconds < 60) return `${seconds}s ago`;
2164
- const minutes = Math.round(seconds / 60);
2165
- if (minutes < 60) return `${minutes}m ago`;
2166
- const hours = Math.round(minutes / 60);
2167
- if (hours < 24) return `${hours}h ago`;
2168
- const days = Math.round(hours / 24);
2169
- return `${days}d ago`;
2170
- }
2171
- function NumberCell({ column, value }) {
2172
- if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", { children: "\u2014" });
2173
- const num = typeof value === "number" ? value : Number(value);
2174
- if (Number.isNaN(num)) return /* @__PURE__ */ jsx("span", { children: asString(value) });
2175
- const formatted = num.toFixed(column.props.decimals ?? 0);
2176
- const [whole, dec] = formatted.split(".");
2177
- const safeWhole = whole ?? "0";
2178
- const ts = column.props.thousandsSeparator;
2179
- const finalWhole = ts ? safeWhole.replace(/\B(?=(\d{3})+(?!\d))/g, ts) : safeWhole;
2180
- const ds = column.props.decimalSeparator ?? ".";
2181
- return /* @__PURE__ */ jsxs("span", { children: [
2182
- column.props.prefix ?? "",
2183
- finalWhole,
2184
- dec ? `${ds}${dec}` : "",
2185
- column.props.suffix ?? ""
2186
- ] });
2187
- }
2188
- function IconCell({ column }) {
2189
- return /* @__PURE__ */ jsx("span", { role: "img", "aria-label": column.props.icon, title: column.props.icon, children: column.props.icon });
2190
- }
2191
- function ImageCell({ column, value }) {
2192
- const src = asString(value);
2193
- if (!src) return null;
2194
- const size = column.props.size ?? 32;
2195
- return /* @__PURE__ */ jsx(
2196
- "img",
2197
- {
2198
- src,
2199
- alt: "",
2200
- style: { width: size, height: size },
2201
- className: cn(column.props.shape === "circular" ? "rounded-full" : "rounded")
2202
- }
2203
- );
2204
- }
2205
- function RelationshipCell({ column, value }) {
2206
- if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", { children: "\u2014" });
2207
- if (typeof value === "object") {
2208
- const attr = value[column.props.attribute];
2209
- return /* @__PURE__ */ jsx("span", { children: asString(attr) });
2210
- }
2211
- return /* @__PURE__ */ jsx("span", { children: asString(value) });
2212
- }
2213
- function ComputedCell({ column, value }) {
2214
- if (value === null || value === void 0) {
2215
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: column.props.placeholder ?? "\u2014" });
2216
- }
2217
- return /* @__PURE__ */ jsx("span", { children: asString(value) });
2218
- }
2219
- var init_cells = __esm({
2220
- "src/table/cells.tsx"() {
2221
- init_cn();
2222
- }
2223
- });
2224
- function DataTable({
2225
- columns,
2226
- records,
2227
- enableSelection = false,
2228
- selectedIds = [],
2229
- onSelectionChange,
2230
- sort = null,
2231
- onSortChange,
2232
- rowActions,
2233
- loading = false,
2234
- emptyState,
2235
- className
2236
- }) {
2237
- const lastClickedIndexRef = useRef(null);
2238
- const visibleColumns = useMemo(() => columns.filter((col) => !col.hidden), [columns]);
2239
- const tableColumns = useMemo(
2240
- () => visibleColumns.map((col) => ({
2241
- id: col.name,
2242
- accessorFn: (row) => row[col.name],
2243
- header: () => col.label ?? col.name,
2244
- cell: (info) => /* @__PURE__ */ jsx(TableCell, { column: col, value: info.getValue() }),
2245
- enableSorting: col.sortable
2246
- })),
2247
- [visibleColumns]
2248
- );
2249
- const table = useReactTable({
2250
- data: records,
2251
- columns: tableColumns,
2252
- getCoreRowModel: getCoreRowModel()
2253
- });
2254
- const allSelected = enableSelection && records.length > 0 && selectedIds.length === records.length;
2255
- const someSelected = enableSelection && selectedIds.length > 0 && selectedIds.length < records.length;
2256
- const toggleAll = useCallback(() => {
2257
- if (!onSelectionChange) return;
2258
- if (allSelected) onSelectionChange([]);
2259
- else onSelectionChange(records.map((r) => r.id));
2260
- }, [allSelected, records, onSelectionChange]);
2261
- const toggleRow = useCallback(
2262
- (id, index, shiftKey) => {
2263
- if (!onSelectionChange) return;
2264
- const set = new Set(selectedIds);
2265
- const lastIndex = lastClickedIndexRef.current;
2266
- if (shiftKey && lastIndex !== null) {
2267
- const [from, to] = lastIndex < index ? [lastIndex, index] : [index, lastIndex];
2268
- const shouldSelect = !set.has(id);
2269
- for (let i = from; i <= to; i++) {
2270
- const rowId = records[i]?.id;
2271
- if (rowId === void 0) continue;
2272
- if (shouldSelect) set.add(rowId);
2273
- else set.delete(rowId);
2274
- }
2275
- } else if (set.has(id)) {
2276
- set.delete(id);
2277
- } else {
2278
- set.add(id);
2279
- }
2280
- lastClickedIndexRef.current = index;
2281
- onSelectionChange(Array.from(set));
2282
- },
2283
- [records, selectedIds, onSelectionChange]
2284
- );
2285
- const headerSortDirection = (columnName) => {
2286
- if (!sort || sort.column !== columnName) return null;
2287
- return sort.direction === "desc" ? "desc" : "asc";
2288
- };
2289
- return /* @__PURE__ */ jsx("div", { className: cn("w-full overflow-x-auto", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
2290
- /* @__PURE__ */ jsx("thead", { className: "sticky top-0 bg-background", children: table.getHeaderGroups().map((group) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border", children: [
2291
- enableSelection && /* @__PURE__ */ jsx("th", { scope: "col", className: "w-10 px-3 py-2 text-left", children: /* @__PURE__ */ jsx(
2292
- "input",
2293
- {
2294
- type: "checkbox",
2295
- "aria-label": "Select all rows",
2296
- checked: allSelected,
2297
- ref: (el) => {
2298
- if (el) el.indeterminate = someSelected;
2299
- },
2300
- onChange: toggleAll
2301
- }
2302
- ) }),
2303
- group.headers.map((header) => {
2304
- const col = visibleColumns.find((c) => c.name === header.column.id);
2305
- const sortable = col?.sortable && onSortChange;
2306
- const direction = headerSortDirection(header.column.id);
2307
- return /* @__PURE__ */ jsx(
2308
- "th",
2309
- {
2310
- scope: "col",
2311
- className: cn(
2312
- "px-3 py-2 text-left font-medium text-muted-foreground",
2313
- col?.align === "center" && "text-center",
2314
- col?.align === "end" && "text-right"
2315
- ),
2316
- "aria-sort": direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none",
2317
- children: sortable ? /* @__PURE__ */ jsxs(
2318
- "button",
2319
- {
2320
- type: "button",
2321
- className: "inline-flex items-center gap-1 hover:text-foreground",
2322
- onClick: () => onSortChange(header.column.id, direction === "asc" ? "desc" : "asc"),
2323
- children: [
2324
- flexRender(header.column.columnDef.header, header.getContext()),
2325
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: direction === "asc" ? "\u25B2" : direction === "desc" ? "\u25BC" : "\u2195" })
2326
- ]
2327
- }
2328
- ) : flexRender(header.column.columnDef.header, header.getContext())
2329
- },
2330
- header.id
2331
- );
2332
- }),
2333
- rowActions && /* @__PURE__ */ jsx("th", { scope: "col", className: "w-24 px-3 py-2 text-right", "aria-label": "Actions" })
2334
- ] }, group.id)) }),
2335
- /* @__PURE__ */ jsxs("tbody", { children: [
2336
- loading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
2337
- "td",
2338
- {
2339
- colSpan: visibleColumns.length + (enableSelection ? 1 : 0) + (rowActions ? 1 : 0),
2340
- className: "px-3 py-4 text-center text-muted-foreground",
2341
- children: "Loading\u2026"
2342
- }
2343
- ) }),
2344
- !loading && records.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
2345
- "td",
2346
- {
2347
- colSpan: visibleColumns.length + (enableSelection ? 1 : 0) + (rowActions ? 1 : 0),
2348
- className: "px-3 py-8 text-center text-muted-foreground",
2349
- children: emptyState ?? "No records found."
2350
- }
2351
- ) }),
2352
- !loading && table.getRowModel().rows.map((row, index) => {
2353
- const record = row.original;
2354
- const checked = selectedIds.includes(record.id);
2355
- return /* @__PURE__ */ jsxs(
2356
- "tr",
2357
- {
2358
- "data-selected": checked || void 0,
2359
- className: cn("border-b border-border", checked && "bg-muted"),
2360
- children: [
2361
- enableSelection && /* @__PURE__ */ jsx("td", { className: "w-10 px-3 py-2", children: /* @__PURE__ */ jsx(
2362
- "input",
2363
- {
2364
- type: "checkbox",
2365
- "aria-label": `Select row ${record.id}`,
2366
- checked,
2367
- onChange: (event) => {
2368
- const native = event.nativeEvent;
2369
- toggleRow(record.id, index, native.shiftKey === true);
2370
- }
2371
- }
2372
- ) }),
2373
- row.getVisibleCells().map((cell) => {
2374
- const col = visibleColumns.find((c) => c.name === cell.column.id);
2375
- return /* @__PURE__ */ jsx(
2376
- "td",
2377
- {
2378
- className: cn(
2379
- "px-3 py-2",
2380
- col?.align === "center" && "text-center",
2381
- col?.align === "end" && "text-right",
2382
- col?.hiddenOnMobile && "hidden md:table-cell"
2383
- ),
2384
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
2385
- },
2386
- cell.id
2387
- );
2388
- }),
2389
- rowActions && /* @__PURE__ */ jsx("td", { className: "w-24 px-3 py-2 text-right", children: rowActions(record) })
2390
- ]
2391
- },
2392
- row.id
2393
- );
2394
- })
2395
- ] })
2396
- ] }) });
2397
- }
2398
- var init_DataTable = __esm({
2399
- "src/table/DataTable.tsx"() {
2400
- init_cn();
2401
- init_cells();
2402
- }
2403
- });
2404
- function TableFilters({
2405
- filters,
2406
- values,
2407
- onChange,
2408
- onClearAll,
2409
- className
2410
- }) {
2411
- if (filters.length === 0) return null;
2412
- const activeCount = filters.reduce((acc, f) => {
2413
- const v = values[f.name];
2414
- return v !== void 0 && v !== null && v !== "" ? acc + 1 : acc;
2415
- }, 0);
2416
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("flex flex-wrap items-end gap-2 border-0 p-0", className), children: [
2417
- /* @__PURE__ */ jsx("legend", { className: "sr-only", children: "Filters" }),
2418
- filters.map((filter) => /* @__PURE__ */ jsx(
2419
- FilterControl2,
2420
- {
2421
- filter,
2422
- value: values[filter.name],
2423
- onChange: (v) => onChange(filter.name, v)
2424
- },
2425
- filter.name
2426
- )),
2427
- activeCount > 0 && onClearAll && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: [
2428
- "Clear filters (",
2429
- activeCount,
2430
- ")"
2431
- ] })
2432
- ] });
2433
- }
2434
- function FilterControl2({
2435
- filter,
2436
- value,
2437
- onChange
2438
- }) {
2439
- switch (filter.type) {
2440
- case "select":
2441
- return /* @__PURE__ */ jsx(SelectFilter, { filter, value, onChange });
2442
- case "multiSelect":
2443
- return /* @__PURE__ */ jsx(MultiSelectFilter, { filter, value, onChange });
2444
- case "text":
2445
- return /* @__PURE__ */ jsx(TextFilter, { filter, value, onChange });
2446
- case "ternary":
2447
- return /* @__PURE__ */ jsx(TernaryFilter, { filter, value, onChange });
2448
- case "dateRange":
2449
- case "scope":
2450
- return null;
2451
- }
2452
- }
2453
- function controlClasses2() {
2454
- return cn(
2455
- "h-9 rounded-sm border border-[var(--input)]",
2456
- "bg-background px-3 text-sm",
2457
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
2458
- );
2459
- }
2460
- function SelectFilter({
2461
- filter,
2462
- value,
2463
- onChange
2464
- }) {
2465
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
2466
- filter.label ?? filter.name,
2467
- /* @__PURE__ */ jsxs(
2468
- "select",
2469
- {
2470
- className: controlClasses2(),
2471
- value: value === void 0 || value === null ? "" : String(value),
2472
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
2473
- children: [
2474
- /* @__PURE__ */ jsx("option", { value: "", children: "All" }),
2475
- filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
2476
- ]
2477
- }
2478
- )
2479
- ] });
2480
- }
2481
- function MultiSelectFilter({
2482
- filter,
2483
- value,
2484
- onChange
2485
- }) {
2486
- const arr = Array.isArray(value) ? value : [];
2487
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
2488
- filter.label ?? filter.name,
2489
- /* @__PURE__ */ jsx(
2490
- "select",
2491
- {
2492
- multiple: true,
2493
- className: cn(controlClasses2(), "h-auto min-h-[2.25rem] py-1"),
2494
- value: arr.map(String),
2495
- onChange: (e) => {
2496
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
2497
- onChange(next.length === 0 ? null : next);
2498
- },
2499
- children: filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
2500
- }
2501
- )
2502
- ] });
2503
- }
2504
- function TextFilter({
2505
- filter,
2506
- value,
2507
- onChange
2508
- }) {
2509
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
2510
- filter.label ?? filter.name,
2511
- /* @__PURE__ */ jsx(
2512
- "input",
2513
- {
2514
- type: "text",
2515
- className: controlClasses2(),
2516
- value: typeof value === "string" ? value : "",
2517
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
2518
- }
2519
- )
2520
- ] });
2521
- }
2522
- function TernaryFilter({
2523
- filter,
2524
- value,
2525
- onChange
2526
- }) {
2527
- const current = value === void 0 || value === null ? "all" : String(value);
2528
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
2529
- filter.label ?? filter.name,
2530
- /* @__PURE__ */ jsxs(
2531
- "select",
2532
- {
2533
- className: controlClasses2(),
2534
- value: current,
2535
- onChange: (e) => {
2536
- const v = e.target.value;
2537
- onChange(v === "all" ? null : v);
2538
- },
2539
- children: [
2540
- /* @__PURE__ */ jsx("option", { value: "all", children: filter.props.allLabel ?? "All" }),
2541
- /* @__PURE__ */ jsx("option", { value: "true", children: filter.props.trueLabel ?? "Yes" }),
2542
- /* @__PURE__ */ jsx("option", { value: "false", children: filter.props.falseLabel ?? "No" })
2543
- ]
2544
- }
2545
- )
2546
- ] });
2547
- }
2548
- var init_TableFilters = __esm({
2549
- "src/table/TableFilters.tsx"() {
2550
- init_Button();
2551
- init_cn();
2552
- }
2553
- });
2554
- function TablePagination({
2555
- meta,
2556
- onPageChange,
2557
- onPerPageChange,
2558
- perPageOptions = [10, 25, 50, 100],
2559
- className
2560
- }) {
2561
- const from = (meta.currentPage - 1) * meta.perPage + 1;
2562
- const to = Math.min(meta.currentPage * meta.perPage, meta.total);
2563
- const isFirst = meta.currentPage <= 1;
2564
- const isLast = meta.currentPage >= meta.lastPage;
2565
- return /* @__PURE__ */ jsxs(
2566
- "nav",
2567
- {
2568
- className: cn(
2569
- "flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm",
2570
- className
2571
- ),
2572
- "aria-label": "Pagination",
2573
- children: [
2574
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: meta.total === 0 ? "No results" : `${from}\u2013${to} of ${meta.total}` }),
2575
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2576
- onPerPageChange && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
2577
- "Per page",
2578
- /* @__PURE__ */ jsx(
2579
- "select",
2580
- {
2581
- className: "h-8 rounded-sm border border-[var(--input)] bg-background px-2",
2582
- value: meta.perPage,
2583
- onChange: (e) => onPerPageChange(Number(e.target.value)),
2584
- children: perPageOptions.map((n) => /* @__PURE__ */ jsx("option", { value: n, children: n }, n))
2585
- }
2586
- )
2587
- ] }),
2588
- /* @__PURE__ */ jsx(
2589
- Button,
2590
- {
2591
- variant: "outline",
2592
- size: "sm",
2593
- disabled: isFirst,
2594
- onClick: () => onPageChange(meta.currentPage - 1),
2595
- "aria-label": "Previous page",
2596
- children: "Prev"
2597
- }
2598
- ),
2599
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
2600
- meta.currentPage,
2601
- " / ",
2602
- meta.lastPage
2603
- ] }),
2604
- /* @__PURE__ */ jsx(
2605
- Button,
2606
- {
2607
- variant: "outline",
2608
- size: "sm",
2609
- disabled: isLast,
2610
- onClick: () => onPageChange(meta.currentPage + 1),
2611
- "aria-label": "Next page",
2612
- children: "Next"
2613
- }
2614
- )
2615
- ] })
2616
- ]
2617
- }
2618
- );
2619
- }
2620
- var init_TablePagination = __esm({
2621
- "src/table/TablePagination.tsx"() {
2622
- init_Button();
2623
- init_cn();
2624
- }
2625
- });
2626
- function TableToolbar({
2627
- search,
2628
- filters,
2629
- selectedCount = 0,
2630
- bulkActions,
2631
- onClearSelection,
2632
- className
2633
- }) {
2634
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3 px-3 py-3", className), children: [
2635
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
2636
- search,
2637
- filters
2638
- ] }),
2639
- selectedCount > 0 && /* @__PURE__ */ jsxs(
2640
- "section",
2641
- {
2642
- className: "flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2",
2643
- "aria-label": "Bulk actions",
2644
- children: [
2645
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
2646
- selectedCount,
2647
- " selected"
2648
- ] }),
2649
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
2650
- bulkActions,
2651
- onClearSelection && /* @__PURE__ */ jsx(
2652
- "button",
2653
- {
2654
- type: "button",
2655
- className: "text-sm text-muted-foreground hover:underline",
2656
- onClick: onClearSelection,
2657
- children: "Clear"
2658
- }
2659
- )
2660
- ] })
2661
- ]
2662
- }
2663
- )
2664
- ] });
2665
- }
2666
- var init_TableToolbar = __esm({
2667
- "src/table/TableToolbar.tsx"() {
2668
- init_cn();
2669
- }
2670
- });
2671
- function ResourceIndex({
2672
- resource,
2673
- records,
2674
- pagination,
2675
- columns,
2676
- filters,
2677
- search,
2678
- sort,
2679
- selectedIds = [],
2680
- filterValues = {},
2681
- onSelectionChange,
2682
- onSortChange,
2683
- onPageChange,
2684
- onPerPageChange,
2685
- onSearchChange,
2686
- onFilterChange,
2687
- onClearFilters,
2688
- searchSlot,
2689
- toolbarActions,
2690
- rowActions,
2691
- bulkActions,
2692
- emptyState,
2693
- loading = false,
2694
- className
2695
- }) {
2696
- const enableSelection = Boolean(onSelectionChange);
2697
- const renderSearch = () => {
2698
- if (searchSlot) return searchSlot;
2699
- if (!onSearchChange) return null;
2700
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
2701
- "Search",
2702
- /* @__PURE__ */ jsx(
2703
- "input",
2704
- {
2705
- type: "search",
2706
- placeholder: `Search ${resource.pluralLabel.toLowerCase()}\u2026`,
2707
- className: "h-9 w-64 rounded-sm border border-[var(--input)] bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2708
- value: search ?? "",
2709
- onChange: (e) => onSearchChange(e.target.value)
2710
- }
2711
- )
2712
- ] });
2713
- };
2714
- const normalisedSort = sort?.column ? {
2715
- column: sort.column,
2716
- direction: sort.direction === "desc" ? "desc" : "asc"
2717
- } : null;
2718
- return /* @__PURE__ */ jsxs(
2719
- "section",
2720
- {
2721
- className: cn("flex flex-col rounded-md border border-border bg-background", className),
2722
- "aria-label": resource.pluralLabel,
2723
- children: [
2724
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-center gap-3 border-b border-border px-4 py-3", children: [
2725
- /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold", children: resource.pluralLabel }),
2726
- /* @__PURE__ */ jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: toolbarActions })
2727
- ] }),
2728
- /* @__PURE__ */ jsx(
2729
- TableToolbar,
2730
- {
2731
- search: renderSearch(),
2732
- filters: filters.length > 0 ? /* @__PURE__ */ jsx(
2733
- TableFilters,
2734
- {
2735
- filters,
2736
- values: filterValues,
2737
- onChange: (name, value) => onFilterChange?.(name, value),
2738
- onClearAll: onClearFilters
2739
- }
2740
- ) : null,
2741
- selectedCount: selectedIds.length,
2742
- bulkActions,
2743
- onClearSelection: onSelectionChange ? () => onSelectionChange([]) : void 0
2744
- }
2745
- ),
2746
- /* @__PURE__ */ jsx(
2747
- DataTable,
2748
- {
2749
- columns,
2750
- records,
2751
- enableSelection,
2752
- selectedIds,
2753
- onSelectionChange,
2754
- sort: normalisedSort,
2755
- onSortChange,
2756
- rowActions: rowActions ? (record) => rowActions(record) : void 0,
2757
- loading,
2758
- emptyState
2759
- }
2760
- ),
2761
- pagination && /* @__PURE__ */ jsx(
2762
- TablePagination,
2763
- {
2764
- meta: pagination,
2765
- onPageChange: (page) => onPageChange?.(page),
2766
- onPerPageChange
2767
- }
2768
- )
2769
- ]
2770
- }
2771
- );
2772
- }
2773
- var init_ResourceIndex = __esm({
2774
- "src/resource/ResourceIndex.tsx"() {
2775
- init_DataTable();
2776
- init_TableFilters();
2777
- init_TablePagination();
2778
- init_TableToolbar();
2779
- init_cn();
2780
- }
2781
- });
2782
-
2783
- // src/pages/ArqelIndexPage.tsx
2784
- var ArqelIndexPage_exports = {};
2785
- __export(ArqelIndexPage_exports, {
2786
- default: () => ArqelIndexPage
2787
- });
2788
- function pruneEmptyFilters(values) {
2789
- const out = {};
2790
- for (const [k, v] of Object.entries(values)) {
2791
- if (v === void 0 || v === null || v === "") continue;
2792
- if (Array.isArray(v) && v.length === 0) continue;
2793
- out[k] = v;
2794
- }
2795
- return out;
2796
- }
2797
- function invokeAction(action, record, formValues) {
2798
- const recordId = record?.id;
2799
- const url = action.url ? action.url.replace("{id}", String(recordId ?? "")) : `/arqel-dev/actions/${action.name}`;
2800
- const method = action.method.toLowerCase();
2801
- const data = { ...formValues ?? {} };
2802
- if (recordId !== void 0) data["record_id"] = recordId;
2803
- router.visit(url, {
2804
- method,
2805
- data,
2806
- preserveScroll: true
2807
- });
2808
- }
2809
- function buildQuery(params) {
2810
- const data = {};
2811
- const filters = pruneEmptyFilters(params.filters);
2812
- if (Object.keys(filters).length > 0) data["filter"] = filters;
2813
- if (params.search !== void 0) data["search"] = params.search;
2814
- if (params.sort?.column) {
2815
- data["sort"] = params.sort.column;
2816
- if (params.sort.direction) data["direction"] = params.sort.direction;
2817
- }
2818
- if (params.page && params.page > 1) data["page"] = params.page;
2819
- if (params.perPage) data["per_page"] = params.perPage;
2820
- return data;
2821
- }
2822
- function ArqelIndexPage() {
2823
- const page = usePage();
2824
- const props = page.props;
2825
- const initialFilters = useMemo(() => {
2826
- if (typeof window === "undefined") return {};
2827
- const sp = new URLSearchParams(window.location.search);
2828
- const fromUrl = {};
2829
- for (const [key, value] of sp.entries()) {
2830
- const m = key.match(/^filter\[([^\]]+)\]$/);
2831
- if (m?.[1]) fromUrl[m[1]] = value;
2832
- }
2833
- return fromUrl;
2834
- }, []);
2835
- const [filterValues, setFilterValues] = useState(initialFilters);
2836
- const [searchValue, setSearchValue] = useState(props.search ?? "");
2837
- const [perPage, setPerPage] = useState(props.pagination?.perPage);
2838
- const [selectedIds, setSelectedIds] = useState([]);
2839
- const searchDebounceRef = useRef(null);
2840
- const visit = (data) => {
2841
- const url = typeof window !== "undefined" ? window.location.pathname : page.url.split("?")[0];
2842
- router.get(url ?? "/", data, {
2843
- preserveState: true,
2844
- preserveScroll: true,
2845
- replace: true
2846
- });
2847
- };
2848
- const currentSort = props.sort ?? null;
2849
- const handleFilterChange = (name, value) => {
2850
- const nextFilters = { ...filterValues };
2851
- if (value === void 0 || value === null || value === "") {
2852
- delete nextFilters[name];
2853
- } else {
2854
- nextFilters[name] = value;
2855
- }
2856
- setFilterValues(nextFilters);
2857
- visit(buildQuery({ filters: nextFilters, search: searchValue, sort: currentSort, perPage }));
2858
- };
2859
- const handleClearFilters = () => {
2860
- setFilterValues({});
2861
- visit(buildQuery({ filters: {}, search: searchValue, sort: currentSort, perPage }));
2862
- };
2863
- const handleSearchChange = (value) => {
2864
- setSearchValue(value);
2865
- if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
2866
- searchDebounceRef.current = setTimeout(() => {
2867
- visit(buildQuery({ filters: filterValues, search: value, sort: currentSort, perPage }));
2868
- }, 300);
2869
- };
2870
- useEffect(
2871
- () => () => {
2872
- if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
2873
- },
2874
- []
2875
- );
2876
- const handleSortChange = (column, direction) => {
2877
- visit(
2878
- buildQuery({
2879
- filters: filterValues,
2880
- search: searchValue,
2881
- sort: { column, direction },
2882
- perPage
2883
- })
2884
- );
2885
- };
2886
- const handlePageChange = (pageNum) => {
2887
- visit(
2888
- buildQuery({
2889
- filters: filterValues,
2890
- search: searchValue,
2891
- sort: currentSort,
2892
- page: pageNum,
2893
- perPage
2894
- })
2895
- );
2896
- };
2897
- const handlePerPageChange = (newPerPage) => {
2898
- setPerPage(newPerPage);
2899
- visit(
2900
- buildQuery({
2901
- filters: filterValues,
2902
- search: searchValue,
2903
- sort: currentSort,
2904
- perPage: newPerPage
2905
- })
2906
- );
2907
- };
2908
- const bulkActionsList = props.actions?.bulk ?? [];
2909
- const bulkActions = bulkActionsList.length > 0 && selectedIds.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: bulkActionsList.map((action) => /* @__PURE__ */ jsx(
2910
- "button",
2911
- {
2912
- type: "button",
2913
- onClick: () => {
2914
- const url = action.url ?? `/arqel-dev/actions/${action.name}`;
2915
- const method = action.method.toLowerCase();
2916
- router.visit(url, {
2917
- method,
2918
- data: { record_ids: selectedIds },
2919
- preserveScroll: true,
2920
- onSuccess: () => setSelectedIds([])
2921
- });
2922
- },
2923
- className: "inline-flex h-8 items-center gap-1 rounded-sm border border-destructive/40 bg-destructive/10 px-3 text-xs font-medium text-destructive hover:bg-destructive/20",
2924
- children: action.label
2925
- },
2926
- action.name
2927
- )) }) : void 0;
2928
- const rowActionsList = props.actions?.row ?? [];
2929
- const rowActions = rowActionsList.length > 0 ? (record) => /* @__PURE__ */ jsx(
2930
- ActionMenu,
2931
- {
2932
- actions: rowActionsList,
2933
- onInvoke: (action, formValues) => invokeAction(action, record, formValues)
2934
- }
2935
- ) : void 0;
2936
- return /* @__PURE__ */ jsx(
2937
- ResourceIndex,
2938
- {
2939
- ...props,
2940
- filterValues,
2941
- onFilterChange: handleFilterChange,
2942
- onClearFilters: handleClearFilters,
2943
- onSearchChange: handleSearchChange,
2944
- onSortChange: handleSortChange,
2945
- onPageChange: handlePageChange,
2946
- onPerPageChange: handlePerPageChange,
2947
- search: searchValue,
2948
- selectedIds,
2949
- onSelectionChange: (ids) => setSelectedIds(ids),
2950
- ...rowActions ? { rowActions } : {},
2951
- ...bulkActions ? { bulkActions } : {}
2952
- }
2953
- );
2954
- }
2955
- var init_ArqelIndexPage = __esm({
2956
- "src/pages/ArqelIndexPage.tsx"() {
2957
- init_ActionMenu();
2958
- init_ResourceIndex();
2959
- }
2960
- });
2961
-
2962
- // src/pages/ArqelShowPage.tsx
2963
- var ArqelShowPage_exports = {};
2964
- __export(ArqelShowPage_exports, {
2965
- default: () => ArqelShowPage
2966
- });
2967
- function ArqelShowPage() {
2968
- const page = usePage();
2969
- const props = page.props;
2970
- const fields = props.fields ?? [];
2971
- const values = {};
2972
- for (const field of fields) {
2973
- const name = field.name;
2974
- if (typeof name === "string" && props.record !== null && typeof props.record === "object") {
2975
- values[name] = props.record[name];
2976
- }
2977
- }
2978
- const declaredForm = props.form;
2979
- const schema = declaredForm ? { ...declaredForm, disabled: true } : FALLBACK_SCHEMA3;
2980
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2981
- /* @__PURE__ */ jsx(
2982
- PageHeader,
2983
- {
2984
- title: props.recordTitle ?? props.resource?.label ?? "Record",
2985
- description: props.recordSubtitle ?? null
2986
- }
2987
- ),
2988
- /* @__PURE__ */ jsx(
2989
- FormRenderer,
2990
- {
2991
- schema,
2992
- fields,
2993
- values,
2994
- onChange: () => {
2995
- },
2996
- errors: {}
2997
- }
2998
- )
2999
- ] });
3000
- }
3001
- var FALLBACK_SCHEMA3;
3002
- var init_ArqelShowPage = __esm({
3003
- "src/pages/ArqelShowPage.tsx"() {
3004
- init_FormRenderer();
3005
- init_PageHeader();
3006
- FALLBACK_SCHEMA3 = {
3007
- schema: [],
3008
- columns: 1,
3009
- model: null,
3010
- inline: false,
3011
- disabled: true
3012
- };
3013
- }
3014
- });
1
+ export { ArqelShowPage } from './chunk-7LSZADUC.js';
2
+ export { ArqelDashboardPage } from './chunk-PXON56HL.js';
3
+ export { ArqelEditPage } from './chunk-3JJOHYD7.js';
4
+ export { ArqelIndexPage } from './chunk-ERT7LL6C.js';
5
+ import './chunk-47HD7ALI.js';
6
+ export { ArqelCreatePage } from './chunk-VSTRVXXZ.js';
7
+ import './chunk-WKWV36PG.js';
8
+ import './chunk-MXUXMHOK.js';
9
+ import './chunk-2LTZJ4R7.js';
10
+ import './chunk-PZEVKMZX.js';
11
+ import './chunk-RMPOR2YZ.js';
12
+ import './chunk-TNJEHYRF.js';
13
+ import './chunk-EYAUPJ6J.js';
14
+ import './chunk-MTZJDFQ6.js';
15
+ import './chunk-MSRTGCTJ.js';
16
+ import './chunk-FHRQ7PWE.js';
17
+ import './chunk-B7ULUJVO.js';
3015
18
 
3016
19
  // src/pages/index.ts
3017
- init_ArqelCreatePage();
3018
- init_ArqelDashboardPage();
3019
- init_ArqelEditPage();
3020
- init_ArqelIndexPage();
3021
- init_ArqelShowPage();
3022
20
  var arqelPages = {
3023
- "arqel::index": () => Promise.resolve().then(() => (init_ArqelIndexPage(), ArqelIndexPage_exports)),
3024
- "arqel::create": () => Promise.resolve().then(() => (init_ArqelCreatePage(), ArqelCreatePage_exports)),
3025
- "arqel::edit": () => Promise.resolve().then(() => (init_ArqelEditPage(), ArqelEditPage_exports)),
3026
- "arqel::show": () => Promise.resolve().then(() => (init_ArqelShowPage(), ArqelShowPage_exports)),
3027
- "arqel::dashboard": () => Promise.resolve().then(() => (init_ArqelDashboardPage(), ArqelDashboardPage_exports))
21
+ "arqel::index": () => import('./ArqelIndexPage-MRH3572Z.js'),
22
+ "arqel::create": () => import('./ArqelCreatePage-P3G6TKIK.js'),
23
+ "arqel::edit": () => import('./ArqelEditPage-HKQSE6E5.js'),
24
+ "arqel::show": () => import('./ArqelShowPage-4FS5MTUN.js'),
25
+ "arqel::dashboard": () => import('./ArqelDashboardPage-PX3Y75AA.js')
3028
26
  };
3029
27
 
3030
- export { ArqelCreatePage, ArqelDashboardPage, ArqelEditPage, ArqelIndexPage, ArqelShowPage, arqelPages };
28
+ export { arqelPages };
3031
29
  //# sourceMappingURL=pages.js.map
3032
30
  //# sourceMappingURL=pages.js.map