@arqel-dev/ui 0.10.1 → 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 (112) 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.d.ts +19 -1
  91. package/dist/pages.js +23 -2278
  92. package/dist/pages.js.map +1 -1
  93. package/dist/palette.js +1 -283
  94. package/dist/palette.js.map +1 -1
  95. package/dist/primitives.js +3 -658
  96. package/dist/primitives.js.map +1 -1
  97. package/dist/resource.js +5 -720
  98. package/dist/resource.js.map +1 -1
  99. package/dist/shell.d.ts +26 -46
  100. package/dist/shell.js +46 -1039
  101. package/dist/shell.js.map +1 -1
  102. package/dist/table.js +4 -618
  103. package/dist/table.js.map +1 -1
  104. package/dist/utility.js +3 -104
  105. package/dist/utility.js.map +1 -1
  106. package/dist/utils.d.ts +12 -6
  107. package/dist/utils.js +2 -9
  108. package/dist/utils.js.map +1 -1
  109. package/dist/widgets.d.ts +7 -2
  110. package/dist/widgets.js +3 -675
  111. package/dist/widgets.js.map +1 -1
  112. package/package.json +15 -14
package/dist/pages.js CHANGED
@@ -1,2285 +1,30 @@
1
- import { clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
- import { useMemo, useState, useRef, useEffect, useCallback, useId } 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 { XIcon } from 'lucide-react';
11
- import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table';
12
-
13
- var __defProp = Object.defineProperty;
14
- var __getOwnPropNames = Object.getOwnPropertyNames;
15
- var __esm = (fn, res) => function __init() {
16
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
- };
18
- var __export = (target, all) => {
19
- for (var name in all)
20
- __defProp(target, name, { get: all[name], enumerable: true });
21
- };
22
- function cn(...inputs) {
23
- return twMerge(clsx(inputs));
24
- }
25
- var init_cn = __esm({
26
- "src/utils/cn.ts"() {
27
- }
28
- });
29
- function Button({
30
- className,
31
- variant = "default",
32
- size = "default",
33
- asChild = false,
34
- ...props
35
- }) {
36
- const Comp = asChild ? Slot.Root : "button";
37
- return /* @__PURE__ */ jsx(
38
- Comp,
39
- {
40
- "data-slot": "button",
41
- "data-variant": variant,
42
- "data-size": size,
43
- className: cn(buttonVariants({ variant, size, className })),
44
- ...props
45
- }
46
- );
47
- }
48
- var buttonVariants;
49
- var init_button = __esm({
50
- "src/shadcn/ui/button.tsx"() {
51
- init_cn();
52
- buttonVariants = cva(
53
- "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",
54
- {
55
- variants: {
56
- variant: {
57
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
58
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
59
- 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",
60
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
61
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
62
- link: "text-primary underline-offset-4 hover:underline"
63
- },
64
- size: {
65
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
66
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
67
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
68
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
69
- icon: "size-9",
70
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
71
- "icon-sm": "size-8",
72
- "icon-lg": "size-10"
73
- }
74
- },
75
- defaultVariants: {
76
- variant: "default",
77
- size: "default"
78
- }
79
- }
80
- );
81
- }
82
- });
83
-
84
- // src/action/Button.tsx
85
- var init_Button = __esm({
86
- "src/action/Button.tsx"() {
87
- init_button();
88
- }
89
- });
90
- function FormActions({
91
- submitLabel = "Save",
92
- cancelLabel = "Cancel",
93
- onCancel,
94
- processing = false,
95
- disabled = false,
96
- extra,
97
- className
98
- }) {
99
- return /* @__PURE__ */ jsxs(
100
- "div",
101
- {
102
- className: cn(
103
- "flex flex-wrap items-center justify-end gap-2 border-t border-border pt-4",
104
- className
105
- ),
106
- children: [
107
- extra && /* @__PURE__ */ jsx("div", { className: "mr-auto", children: extra }),
108
- onCancel && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: processing, children: cancelLabel }),
109
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: processing || disabled, children: processing ? "Saving\u2026" : submitLabel })
110
- ]
111
- }
112
- );
113
- }
114
- var init_FormActions = __esm({
115
- "src/form/FormActions.tsx"() {
116
- init_Button();
117
- init_cn();
118
- }
119
- });
120
-
121
- // src/form/FieldRegistry.tsx
122
- function getFieldComponent(name) {
123
- return registry.get(name);
124
- }
125
- var registry;
126
- var init_FieldRegistry = __esm({
127
- "src/form/FieldRegistry.tsx"() {
128
- registry = /* @__PURE__ */ new Map();
129
- }
130
- });
131
- function NativeFieldInput(props) {
132
- const { field } = props;
133
- switch (field.type) {
134
- case "text":
135
- case "email":
136
- case "url":
137
- case "password":
138
- case "slug":
139
- return /* @__PURE__ */ jsx(TextLike, { ...props, field });
140
- case "textarea":
141
- return /* @__PURE__ */ jsx(Textarea, { ...props, field });
142
- case "number":
143
- case "currency":
144
- return /* @__PURE__ */ jsx(NumericInput, { ...props, field });
145
- case "boolean":
146
- case "toggle":
147
- return /* @__PURE__ */ jsx(Checkbox, { ...props, field });
148
- case "select":
149
- return /* @__PURE__ */ jsx(SelectInput, { ...props, field });
150
- case "multiSelect":
151
- case "hasMany":
152
- return /* @__PURE__ */ jsx(MultiSelectInput, { ...props, field });
153
- case "radio":
154
- return /* @__PURE__ */ jsx(RadioGroup, { ...props, field });
155
- case "belongsTo":
156
- return /* @__PURE__ */ jsx(BelongsToInput, { ...props, field });
157
- case "date":
158
- case "dateTime":
159
- return /* @__PURE__ */ jsx(DateInput, { ...props, field });
160
- case "file":
161
- case "image":
162
- return /* @__PURE__ */ jsx(FileInput, { ...props });
163
- default:
164
- return null;
165
- }
166
- }
167
- function TextLike({
168
- field,
169
- value,
170
- onChange,
171
- disabled,
172
- inputId,
173
- describedBy
174
- }) {
175
- const htmlType = field.type === "email" ? "email" : field.type === "password" ? "password" : field.type === "url" ? "url" : "text";
176
- const isDisabled = disabled || field.disabled || field.readonly;
177
- return /* @__PURE__ */ jsx(
178
- "input",
179
- {
180
- id: inputId,
181
- type: htmlType,
182
- className: inputClasses,
183
- value: typeof value === "string" ? value : (value ?? "")?.toString() ?? "",
184
- placeholder: field.placeholder ?? void 0,
185
- disabled: isDisabled,
186
- readOnly: field.readonly === true,
187
- required: field.required === true,
188
- "aria-describedby": describedBy,
189
- onChange: (e) => onChange(e.target.value)
190
- }
191
- );
192
- }
193
- function Textarea({
194
- field,
195
- value,
196
- onChange,
197
- disabled,
198
- inputId,
199
- describedBy
200
- }) {
201
- return /* @__PURE__ */ jsx(
202
- "textarea",
203
- {
204
- id: inputId,
205
- className: cn(inputClasses, "h-auto min-h-[5rem] py-2"),
206
- value: typeof value === "string" ? value : "",
207
- placeholder: field.placeholder ?? void 0,
208
- disabled: disabled || field.disabled || field.readonly,
209
- readOnly: field.readonly === true,
210
- required: field.required === true,
211
- rows: 4,
212
- "aria-describedby": describedBy,
213
- onChange: (e) => onChange(e.target.value)
214
- }
215
- );
216
- }
217
- function NumericInput({
218
- field,
219
- value,
220
- onChange,
221
- disabled,
222
- inputId,
223
- describedBy
224
- }) {
225
- return /* @__PURE__ */ jsx(
226
- "input",
227
- {
228
- id: inputId,
229
- type: "number",
230
- className: inputClasses,
231
- value: value === null || value === void 0 ? "" : String(value),
232
- placeholder: field.placeholder ?? void 0,
233
- disabled: disabled || field.disabled || field.readonly,
234
- readOnly: field.readonly === true,
235
- required: field.required === true,
236
- "aria-describedby": describedBy,
237
- onChange: (e) => {
238
- const raw = e.target.value;
239
- if (raw === "") {
240
- onChange(null);
241
- return;
242
- }
243
- const num = Number(raw);
244
- onChange(Number.isNaN(num) ? null : num);
245
- }
246
- }
247
- );
248
- }
249
- function Checkbox({
250
- field,
251
- value,
252
- onChange,
253
- disabled,
254
- inputId,
255
- describedBy
256
- }) {
257
- return /* @__PURE__ */ jsx(
258
- "input",
259
- {
260
- id: inputId,
261
- type: "checkbox",
262
- className: "h-4 w-4 rounded border-[var(--input)]",
263
- checked: value === true,
264
- disabled: disabled || field.disabled || field.readonly,
265
- "aria-describedby": describedBy,
266
- onChange: (e) => onChange(e.target.checked)
267
- }
268
- );
269
- }
270
- function normaliseOptions(options) {
271
- if (!options) return [];
272
- if (Array.isArray(options)) return options;
273
- return Object.entries(options).map(([value, label]) => ({ value, label }));
274
- }
275
- function SelectInput({
276
- field,
277
- value,
278
- onChange,
279
- disabled,
280
- inputId,
281
- describedBy
282
- }) {
283
- const options = normaliseOptions(field.props.options);
284
- return /* @__PURE__ */ jsxs(
285
- "select",
286
- {
287
- id: inputId,
288
- className: inputClasses,
289
- value: value === null || value === void 0 ? "" : String(value),
290
- disabled: disabled || field.disabled || field.readonly,
291
- required: field.required === true,
292
- "aria-describedby": describedBy,
293
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
294
- children: [
295
- field.placeholder && /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder }),
296
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
297
- ]
298
- }
299
- );
300
- }
301
- function MultiSelectInput({
302
- field,
303
- value,
304
- onChange,
305
- disabled,
306
- inputId,
307
- describedBy
308
- }) {
309
- const arr = Array.isArray(value) ? value : [];
310
- const options = field.type === "multiSelect" ? normaliseOptions(field.props.options) : [];
311
- return /* @__PURE__ */ jsx(
312
- "select",
313
- {
314
- id: inputId,
315
- multiple: true,
316
- className: cn(inputClasses, "h-auto min-h-[6rem] py-1"),
317
- value: arr.map(String),
318
- disabled: disabled || field.disabled || field.readonly,
319
- "aria-describedby": describedBy,
320
- onChange: (e) => {
321
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
322
- onChange(next);
323
- },
324
- children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
325
- }
326
- );
327
- }
328
- function RadioGroup({
329
- field,
330
- value,
331
- onChange,
332
- disabled,
333
- inputId,
334
- describedBy
335
- }) {
336
- return /* @__PURE__ */ jsx(
337
- "div",
338
- {
339
- id: inputId,
340
- className: "flex flex-col gap-1",
341
- "aria-describedby": describedBy,
342
- role: "radiogroup",
343
- children: (field.props.options ?? []).map((opt) => /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm", children: [
344
- /* @__PURE__ */ jsx(
345
- "input",
346
- {
347
- type: "radio",
348
- name: field.name,
349
- value: String(opt.value),
350
- checked: String(value ?? "") === String(opt.value),
351
- disabled: disabled || field.disabled || field.readonly,
352
- onChange: () => onChange(opt.value)
353
- }
354
- ),
355
- opt.label
356
- ] }, opt.value))
357
- }
358
- );
359
- }
360
- function BelongsToInput({
361
- field,
362
- value,
363
- onChange,
364
- disabled,
365
- inputId,
366
- describedBy
367
- }) {
368
- return /* @__PURE__ */ jsx(
369
- "input",
370
- {
371
- id: inputId,
372
- type: "text",
373
- className: inputClasses,
374
- value: value === null || value === void 0 ? "" : String(value),
375
- placeholder: field.placeholder ?? `Search ${field.props.relatedResource}\u2026`,
376
- disabled: disabled || field.disabled || field.readonly,
377
- readOnly: field.readonly === true,
378
- "aria-describedby": describedBy,
379
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
380
- }
381
- );
382
- }
383
- function DateInput({
384
- field,
385
- value,
386
- onChange,
387
- disabled,
388
- inputId,
389
- describedBy
390
- }) {
391
- return /* @__PURE__ */ jsx(
392
- "input",
393
- {
394
- id: inputId,
395
- type: field.type === "dateTime" ? "datetime-local" : "date",
396
- className: inputClasses,
397
- value: typeof value === "string" ? value : "",
398
- disabled: disabled || field.disabled || field.readonly,
399
- readOnly: field.readonly === true,
400
- required: field.required === true,
401
- "aria-describedby": describedBy,
402
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
403
- }
404
- );
405
- }
406
- function FileInput({
407
- field,
408
- onChange,
409
- disabled,
410
- inputId,
411
- describedBy
412
- }) {
413
- return /* @__PURE__ */ jsx(
414
- "input",
415
- {
416
- id: inputId,
417
- type: "file",
418
- className: "text-sm",
419
- disabled: disabled || field.disabled || field.readonly,
420
- "aria-describedby": describedBy,
421
- onChange: (e) => onChange(e.target.files?.[0] ?? null)
422
- }
423
- );
424
- }
425
- var inputClasses;
426
- var init_nativeFields = __esm({
427
- "src/form/nativeFields.tsx"() {
428
- init_cn();
429
- inputClasses = cn(
430
- "h-9 rounded-sm border border-[var(--input)]",
431
- "bg-background px-3 text-sm",
432
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
433
- "disabled:cursor-not-allowed disabled:opacity-50"
434
- );
435
- }
436
- });
437
- function FieldRenderer(props) {
438
- const { field, errors, className } = props;
439
- const id = useId();
440
- const errorId = `${id}-error`;
441
- const helperId = `${id}-helper`;
442
- const hasError = errors && errors.length > 0;
443
- const Custom = field.component ? getFieldComponent(field.component) : void 0;
444
- const Input = Custom ?? NativeFieldInput;
445
- const describedBy = [field.helperText ? helperId : null, hasError ? errorId : null].filter(Boolean).join(" ");
446
- return /* @__PURE__ */ jsxs(
447
- "div",
448
- {
449
- "data-arqel-field": field.name,
450
- "data-field-type": field.type,
451
- className: cn("flex flex-col gap-1", className),
452
- children: [
453
- field.label !== null && field.label !== "" && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-sm font-medium", children: [
454
- field.label,
455
- field.required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })
456
- ] }),
457
- /* @__PURE__ */ jsx(Input, { ...props, inputId: id, describedBy: describedBy || void 0 }),
458
- field.helperText && !hasError && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-xs text-muted-foreground", children: field.helperText }),
459
- hasError && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-xs text-destructive", children: errors[0] })
460
- ]
461
- }
462
- );
463
- }
464
- var init_FieldRenderer = __esm({
465
- "src/form/FieldRenderer.tsx"() {
466
- init_cn();
467
- init_FieldRegistry();
468
- init_nativeFields();
469
- }
470
- });
471
- function FormFieldset({ config, children, className }) {
472
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("rounded-md border border-border p-4", className), children: [
473
- /* @__PURE__ */ jsx("legend", { className: "px-1 text-sm font-medium", children: config.legend }),
474
- /* @__PURE__ */ jsx(
475
- "div",
476
- {
477
- className: "grid gap-4",
478
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
479
- children
480
- }
481
- )
482
- ] });
483
- }
484
- var init_FormFieldset = __esm({
485
- "src/form/FormFieldset.tsx"() {
486
- init_cn();
487
- }
488
- });
489
- function FormGrid({ config, children, className }) {
490
- const columns = typeof config.columns === "number" ? config.columns : config.columns["md"] ?? 1;
491
- return /* @__PURE__ */ jsx(
492
- "div",
493
- {
494
- className: cn("grid", className),
495
- style: {
496
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
497
- gap: config.gap ?? "1rem"
498
- },
499
- children
500
- }
501
- );
502
- }
503
- var init_FormGrid = __esm({
504
- "src/form/FormGrid.tsx"() {
505
- init_cn();
506
- }
507
- });
508
- function FormSection({ config, children, className }) {
509
- const [open, setOpen] = useState(!config.collapsed);
510
- const collapsible = config.collapsible === true;
511
- const heading = /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
512
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
513
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: config.heading }),
514
- config.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: config.description })
515
- ] }),
516
- collapsible && /* @__PURE__ */ jsx(
517
- "button",
518
- {
519
- type: "button",
520
- "aria-expanded": open,
521
- onClick: () => setOpen((v) => !v),
522
- className: "text-sm text-muted-foreground hover:underline",
523
- children: open ? "Hide" : "Show"
524
- }
525
- )
526
- ] });
527
- const body = open && /* @__PURE__ */ jsx(
528
- "div",
529
- {
530
- className: "grid gap-4",
531
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
532
- children
533
- }
534
- );
535
- return /* @__PURE__ */ jsx(
536
- "section",
537
- {
538
- "data-arqel-section": "",
539
- className: cn(
540
- "rounded-md border border-border bg-background",
541
- config.compact ? "p-3" : "p-4",
542
- className
543
- ),
544
- children: config.aside ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[16rem_1fr]", children: [
545
- /* @__PURE__ */ jsx("div", { children: heading }),
546
- /* @__PURE__ */ jsx("div", { children: body })
547
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
548
- /* @__PURE__ */ jsx("div", { className: "mb-3", children: heading }),
549
- body
550
- ] })
551
- }
552
- );
553
- }
554
- var init_FormSection = __esm({
555
- "src/form/FormSection.tsx"() {
556
- init_cn();
557
- }
558
- });
559
- function FormTabs({ config, tabs, className }) {
560
- const [active, setActive] = useState(config.defaultTab ?? tabs[0]?.id ?? null);
561
- const baseId = useId();
562
- const refs = useRef([]);
563
- if (tabs.length === 0 || active === null) return null;
564
- const activeIndex = Math.max(
565
- 0,
566
- tabs.findIndex((t) => t.id === active)
567
- );
568
- const activeTab = tabs[activeIndex];
569
- if (!activeTab) return null;
570
- const focus = (i) => {
571
- const target = tabs[i];
572
- if (!target) return;
573
- setActive(target.id);
574
- refs.current[i]?.focus();
575
- };
576
- const onKeyDown = (e) => {
577
- if (e.key === "ArrowRight") {
578
- e.preventDefault();
579
- focus((activeIndex + 1) % tabs.length);
580
- } else if (e.key === "ArrowLeft") {
581
- e.preventDefault();
582
- focus((activeIndex - 1 + tabs.length) % tabs.length);
583
- } else if (e.key === "Home") {
584
- e.preventDefault();
585
- focus(0);
586
- } else if (e.key === "End") {
587
- e.preventDefault();
588
- focus(tabs.length - 1);
589
- }
590
- };
591
- return /* @__PURE__ */ jsxs("div", { className, children: [
592
- /* @__PURE__ */ jsx(
593
- "div",
594
- {
595
- role: "tablist",
596
- "aria-orientation": config.orientation,
597
- className: "flex gap-1 border-b border-border",
598
- children: tabs.map((tab, i) => {
599
- const selected = tab.id === active;
600
- return /* @__PURE__ */ jsxs(
601
- "button",
602
- {
603
- ref: (el) => {
604
- refs.current[i] = el;
605
- },
606
- type: "button",
607
- role: "tab",
608
- id: `${baseId}-tab-${tab.id}`,
609
- "aria-controls": `${baseId}-panel-${tab.id}`,
610
- "aria-selected": selected,
611
- tabIndex: selected ? 0 : -1,
612
- onClick: () => setActive(tab.id),
613
- onKeyDown,
614
- className: cn(
615
- "-mb-px border-b-2 px-3 py-2 text-sm transition-colors",
616
- selected ? "border-primary font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
617
- ),
618
- children: [
619
- tab.label,
620
- 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 })
621
- ]
622
- },
623
- tab.id
624
- );
625
- })
626
- }
627
- ),
628
- /* @__PURE__ */ jsx(
629
- "div",
630
- {
631
- role: "tabpanel",
632
- id: `${baseId}-panel-${activeTab.id}`,
633
- "aria-labelledby": `${baseId}-tab-${activeTab.id}`,
634
- className: "pt-4",
635
- children: activeTab.content
636
- }
637
- )
638
- ] });
639
- }
640
- var init_FormTabs = __esm({
641
- "src/form/FormTabs.tsx"() {
642
- init_cn();
643
- }
644
- });
645
- function FormRenderer({
646
- schema,
647
- fields,
648
- values,
649
- errors = {},
650
- onChange,
651
- disabled = false,
652
- className
653
- }) {
654
- const fieldsByName = new Map(fields.map((f) => [f.name, f]));
655
- const renderEntry = (entry, key) => {
656
- if (isFieldEntry(entry)) {
657
- return renderFieldEntry(entry, key);
658
- }
659
- if (isLayoutEntry(entry)) {
660
- return renderLayoutEntry(entry, key);
661
- }
662
- return null;
663
- };
664
- const renderFieldEntry = (entry, key) => {
665
- const field = fieldsByName.get(entry.name);
666
- if (!field) return null;
667
- return /* @__PURE__ */ jsx(
668
- FieldRenderer,
669
- {
670
- field,
671
- value: values[entry.name],
672
- errors: errors[entry.name],
673
- disabled: disabled || schema.disabled === true,
674
- onChange: (value) => onChange(entry.name, value)
675
- },
676
- key
677
- );
678
- };
679
- const renderChildren = (entries) => {
680
- if (!entries) return null;
681
- return entries.map((child, i) => renderEntry(child, `${i}`));
682
- };
683
- const renderLayoutEntry = (entry, key) => {
684
- switch (entry.type) {
685
- case "section":
686
- return /* @__PURE__ */ jsx(FormSection, { config: entry.props, children: renderChildren(entry.schema) }, key);
687
- case "fieldset":
688
- return /* @__PURE__ */ jsx(FormFieldset, { config: entry.props, children: renderChildren(entry.schema) }, key);
689
- case "grid":
690
- case "columns":
691
- return /* @__PURE__ */ jsx(
692
- FormGrid,
693
- {
694
- config: entry.type === "columns" ? { columns: entry.props.columns } : entry.props,
695
- children: renderChildren(entry.schema)
696
- },
697
- key
698
- );
699
- case "group":
700
- return /* @__PURE__ */ jsx(
701
- "div",
702
- {
703
- className: cn(
704
- "flex gap-4",
705
- entry.props.orientation === "vertical" ? "flex-col" : "flex-row"
706
- ),
707
- children: renderChildren(entry.schema)
708
- },
709
- key
710
- );
711
- case "tabs": {
712
- const tabs = (entry.schema ?? []).filter((c) => isLayoutEntry(c) && c.type === "tab").map((tab) => ({
713
- id: tab.props.id,
714
- label: tab.props.label,
715
- badge: tab.props.badge,
716
- content: renderChildren(tab.schema) ?? /* @__PURE__ */ jsx("span", {}, `tab-${tab.props.id}-empty`)
717
- }));
718
- return /* @__PURE__ */ jsx(FormTabs, { config: entry.props, tabs }, key);
719
- }
720
- case "tab":
721
- return null;
722
- }
723
- };
724
- const columns = schema.columns ?? 1;
725
- return /* @__PURE__ */ jsx(
726
- "div",
727
- {
728
- "data-arqel-form": "",
729
- className: cn("grid gap-4", className),
730
- style: columns > 1 ? { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` } : void 0,
731
- children: schema.schema.map((entry, i) => renderEntry(entry, `${i}`))
732
- }
733
- );
734
- }
735
- var init_FormRenderer = __esm({
736
- "src/form/FormRenderer.tsx"() {
737
- init_cn();
738
- init_FieldRenderer();
739
- init_FormFieldset();
740
- init_FormGrid();
741
- init_FormSection();
742
- init_FormTabs();
743
- }
744
- });
745
- function PageHeader({ title, description, actions, className }) {
746
- return /* @__PURE__ */ jsxs("header", { className: cn("flex flex-wrap items-start gap-4", className), children: [
747
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
748
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
749
- description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description })
750
- ] }),
751
- actions && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: actions })
752
- ] });
753
- }
754
- var init_PageHeader = __esm({
755
- "src/utility/PageHeader.tsx"() {
756
- init_cn();
757
- }
758
- });
759
-
760
- // src/pages/ArqelCreatePage.tsx
761
- var ArqelCreatePage_exports = {};
762
- __export(ArqelCreatePage_exports, {
763
- default: () => ArqelCreatePage
764
- });
765
- function ArqelCreatePage() {
766
- const page = usePage();
767
- const props = page.props;
768
- const fields = props.fields ?? [];
769
- const schema = props.form ?? FALLBACK_SCHEMA;
770
- const form = useArqelForm({ fields });
771
- const submit = (e) => {
772
- e.preventDefault();
773
- const slug = props.resource?.slug ?? "";
774
- const basePath = props.resource?.panelPath ?? "/admin";
775
- router.post(`${basePath}/${slug}`, form.data, {
776
- preserveScroll: true
777
- });
778
- };
779
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
780
- /* @__PURE__ */ jsx(PageHeader, { title: `Create ${props.resource?.label ?? "record"}`, description: null }),
781
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-6", children: [
782
- /* @__PURE__ */ jsx(
783
- FormRenderer,
784
- {
785
- schema,
786
- fields,
787
- values: form.data,
788
- onChange: (name, value) => form.setData(name, value),
789
- errors: form.errors
790
- }
791
- ),
792
- /* @__PURE__ */ jsx(FormActions, { processing: form.processing, onCancel: () => window.history.back() })
793
- ] })
794
- ] });
795
- }
796
- var FALLBACK_SCHEMA;
797
- var init_ArqelCreatePage = __esm({
798
- "src/pages/ArqelCreatePage.tsx"() {
799
- init_FormActions();
800
- init_FormRenderer();
801
- init_PageHeader();
802
- FALLBACK_SCHEMA = {
803
- schema: [],
804
- columns: 1,
805
- model: null,
806
- inline: false,
807
- disabled: false
808
- };
809
- }
810
- });
811
-
812
- // src/pages/ArqelEditPage.tsx
813
- var ArqelEditPage_exports = {};
814
- __export(ArqelEditPage_exports, {
815
- default: () => ArqelEditPage
816
- });
817
- function ArqelEditPage() {
818
- const page = usePage();
819
- const props = page.props;
820
- const fields = props.fields ?? [];
821
- const record = props.record;
822
- const schema = props.form ?? FALLBACK_SCHEMA2;
823
- const form = useArqelForm({ fields, record });
824
- const submit = (e) => {
825
- e.preventDefault();
826
- const id = String(record?.id ?? "");
827
- const slug = props.resource?.slug ?? "";
828
- const basePath = props.resource?.panelPath ?? "/admin";
829
- router.put(`${basePath}/${slug}/${id}`, form.data, {
830
- preserveScroll: true
831
- });
832
- };
833
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
834
- /* @__PURE__ */ jsx(
835
- PageHeader,
836
- {
837
- title: props.recordTitle ?? `Edit ${props.resource?.label ?? "record"}`,
838
- description: props.recordSubtitle ?? null
839
- }
840
- ),
841
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-6", children: [
842
- /* @__PURE__ */ jsx(
843
- FormRenderer,
844
- {
845
- schema,
846
- fields,
847
- values: form.data,
848
- onChange: (name, value) => form.setData(name, value),
849
- errors: form.errors
850
- }
851
- ),
852
- /* @__PURE__ */ jsx(FormActions, { processing: form.processing, onCancel: () => window.history.back() })
853
- ] })
854
- ] });
855
- }
856
- var FALLBACK_SCHEMA2;
857
- var init_ArqelEditPage = __esm({
858
- "src/pages/ArqelEditPage.tsx"() {
859
- init_FormActions();
860
- init_FormRenderer();
861
- init_PageHeader();
862
- FALLBACK_SCHEMA2 = {
863
- schema: [],
864
- columns: 1,
865
- model: null,
866
- inline: false,
867
- disabled: false
868
- };
869
- }
870
- });
871
- function DropdownMenu({
872
- ...props
873
- }) {
874
- return /* @__PURE__ */ jsx(DropdownMenu$1.Root, { "data-slot": "dropdown-menu", ...props });
875
- }
876
- function DropdownMenuTrigger({
877
- ...props
878
- }) {
879
- return /* @__PURE__ */ jsx(
880
- DropdownMenu$1.Trigger,
881
- {
882
- "data-slot": "dropdown-menu-trigger",
883
- ...props
884
- }
885
- );
886
- }
887
- function DropdownMenuContent({
888
- className,
889
- sideOffset = 4,
890
- ...props
891
- }) {
892
- return /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(
893
- DropdownMenu$1.Content,
894
- {
895
- "data-slot": "dropdown-menu-content",
896
- sideOffset,
897
- className: cn(
898
- "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",
899
- className
900
- ),
901
- ...props
902
- }
903
- ) });
904
- }
905
- function DropdownMenuItem({
906
- className,
907
- inset,
908
- variant = "default",
909
- ...props
910
- }) {
911
- return /* @__PURE__ */ jsx(
912
- DropdownMenu$1.Item,
913
- {
914
- "data-slot": "dropdown-menu-item",
915
- "data-inset": inset,
916
- "data-variant": variant,
917
- className: cn(
918
- "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!",
919
- className
920
- ),
921
- ...props
922
- }
923
- );
924
- }
925
- var init_dropdown_menu = __esm({
926
- "src/shadcn/ui/dropdown-menu.tsx"() {
927
- "use client";
928
- init_cn();
929
- }
930
- });
931
- function Dialog({
932
- ...props
933
- }) {
934
- return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
935
- }
936
- function DialogPortal({
937
- ...props
938
- }) {
939
- return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
940
- }
941
- function DialogClose({
942
- ...props
943
- }) {
944
- return /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
945
- }
946
- function DialogOverlay({
947
- className,
948
- ...props
949
- }) {
950
- return /* @__PURE__ */ jsx(
951
- Dialog$1.Overlay,
952
- {
953
- "data-slot": "dialog-overlay",
954
- className: cn(
955
- "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",
956
- className
957
- ),
958
- ...props
959
- }
960
- );
961
- }
962
- function DialogContent({
963
- className,
964
- children,
965
- showCloseButton = true,
966
- ...props
967
- }) {
968
- return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
969
- /* @__PURE__ */ jsx(DialogOverlay, {}),
970
- /* @__PURE__ */ jsxs(
971
- Dialog$1.Content,
972
- {
973
- "data-slot": "dialog-content",
974
- className: cn(
975
- "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",
976
- className
977
- ),
978
- ...props,
979
- children: [
980
- children,
981
- showCloseButton && /* @__PURE__ */ jsxs(
982
- Dialog$1.Close,
983
- {
984
- "data-slot": "dialog-close",
985
- 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",
986
- children: [
987
- /* @__PURE__ */ jsx(XIcon, {}),
988
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
989
- ]
990
- }
991
- )
992
- ]
993
- }
994
- )
995
- ] });
996
- }
997
- function DialogHeader({ className, ...props }) {
998
- return /* @__PURE__ */ jsx(
999
- "div",
1000
- {
1001
- "data-slot": "dialog-header",
1002
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1003
- ...props
1004
- }
1005
- );
1006
- }
1007
- function DialogFooter({
1008
- className,
1009
- showCloseButton = false,
1010
- children,
1011
- ...props
1012
- }) {
1013
- return /* @__PURE__ */ jsxs(
1014
- "div",
1015
- {
1016
- "data-slot": "dialog-footer",
1017
- className: cn(
1018
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1019
- className
1020
- ),
1021
- ...props,
1022
- children: [
1023
- children,
1024
- showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
1025
- ]
1026
- }
1027
- );
1028
- }
1029
- function DialogTitle({
1030
- className,
1031
- ...props
1032
- }) {
1033
- return /* @__PURE__ */ jsx(
1034
- Dialog$1.Title,
1035
- {
1036
- "data-slot": "dialog-title",
1037
- className: cn("text-lg leading-none font-semibold", className),
1038
- ...props
1039
- }
1040
- );
1041
- }
1042
- function DialogDescription({
1043
- className,
1044
- ...props
1045
- }) {
1046
- return /* @__PURE__ */ jsx(
1047
- Dialog$1.Description,
1048
- {
1049
- "data-slot": "dialog-description",
1050
- className: cn("text-sm text-muted-foreground", className),
1051
- ...props
1052
- }
1053
- );
1054
- }
1055
- var init_dialog = __esm({
1056
- "src/shadcn/ui/dialog.tsx"() {
1057
- init_cn();
1058
- init_button();
1059
- }
1060
- });
1061
- function ActionFormModal({
1062
- open,
1063
- onOpenChange,
1064
- action,
1065
- fields,
1066
- onSubmit,
1067
- processing = false,
1068
- errors = {},
1069
- initialValues = {}
1070
- }) {
1071
- const [values, setValues] = useState(initialValues);
1072
- const size = action.modalSize ?? "md";
1073
- const schema = useMemo(
1074
- () => ({
1075
- schema: (action.form ?? []).map((entry) => ({
1076
- kind: "field",
1077
- name: entry.name,
1078
- type: entry.type
1079
- })),
1080
- columns: 1,
1081
- model: null,
1082
- inline: false,
1083
- disabled: false
1084
- }),
1085
- [action.form]
1086
- );
1087
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: cn("overflow-auto", SIZE_CLASSES[size]), children: [
1088
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: action.label }) }),
1089
- /* @__PURE__ */ jsxs(
1090
- "form",
1091
- {
1092
- className: "flex flex-col gap-4",
1093
- onSubmit: (e) => {
1094
- e.preventDefault();
1095
- onSubmit(values);
1096
- },
1097
- children: [
1098
- /* @__PURE__ */ jsx(
1099
- FormRenderer,
1100
- {
1101
- schema,
1102
- fields,
1103
- values,
1104
- errors,
1105
- onChange: (name, value) => setValues((prev) => ({ ...prev, [name]: value })),
1106
- disabled: processing
1107
- }
1108
- ),
1109
- /* @__PURE__ */ jsx(
1110
- FormActions,
1111
- {
1112
- processing,
1113
- submitLabel: action.label,
1114
- extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: "Cancel" }) })
1115
- }
1116
- )
1117
- ]
1118
- }
1119
- )
1120
- ] }) });
1121
- }
1122
- var SIZE_CLASSES;
1123
- var init_ActionFormModal = __esm({
1124
- "src/action/ActionFormModal.tsx"() {
1125
- init_FormActions();
1126
- init_FormRenderer();
1127
- init_dialog();
1128
- init_cn();
1129
- init_Button();
1130
- SIZE_CLASSES = {
1131
- sm: "sm:max-w-[24rem]",
1132
- md: "sm:max-w-[32rem]",
1133
- lg: "sm:max-w-[44rem]",
1134
- xl: "sm:max-w-[56rem]",
1135
- full: "sm:max-w-[90vw] h-[90vh]"
1136
- };
1137
- }
1138
- });
1139
- function ConfirmDialog({
1140
- open,
1141
- onOpenChange,
1142
- config,
1143
- onConfirm,
1144
- processing = false
1145
- }) {
1146
- const [typed, setTyped] = useState("");
1147
- useEffect(() => {
1148
- if (!open) setTyped("");
1149
- }, [open]);
1150
- const requiresText = config?.requiresText;
1151
- const submitDisabled = processing || requiresText !== void 0 && typed !== requiresText;
1152
- const variant = COLOR_VARIANT[config?.color ?? "destructive"];
1153
- const submitLabel = config?.submitLabel ?? (config?.color === "info" ? "Confirm" : "Delete");
1154
- const cancelLabel = config?.cancelLabel ?? "Cancel";
1155
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[28rem]", showCloseButton: false, children: [
1156
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
1157
- /* @__PURE__ */ jsx(DialogTitle, { children: config?.heading ?? "Are you sure?" }),
1158
- config?.description && /* @__PURE__ */ jsx(DialogDescription, { children: config.description })
1159
- ] }),
1160
- requiresText !== void 0 && /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1161
- /* @__PURE__ */ jsxs("span", { children: [
1162
- "Type ",
1163
- /* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: requiresText }),
1164
- " to confirm"
1165
- ] }),
1166
- /* @__PURE__ */ jsx(
1167
- "input",
1168
- {
1169
- type: "text",
1170
- ref: (el) => el?.focus(),
1171
- value: typed,
1172
- onChange: (e) => setTyped(e.target.value),
1173
- onKeyDown: (e) => {
1174
- if (e.key === "Enter" && !submitDisabled) {
1175
- e.preventDefault();
1176
- onConfirm();
1177
- }
1178
- },
1179
- 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"
1180
- }
1181
- )
1182
- ] }),
1183
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
1184
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: cancelLabel }) }),
1185
- /* @__PURE__ */ jsx(Button, { type: "button", variant, disabled: submitDisabled, onClick: onConfirm, children: processing ? "Working\u2026" : submitLabel })
1186
- ] })
1187
- ] }) });
1188
- }
1189
- var COLOR_VARIANT;
1190
- var init_ConfirmDialog = __esm({
1191
- "src/action/ConfirmDialog.tsx"() {
1192
- init_dialog();
1193
- init_Button();
1194
- COLOR_VARIANT = {
1195
- destructive: "destructive",
1196
- warning: "default",
1197
- info: "default"
1198
- };
1199
- }
1200
- });
1201
- function ActionButton({
1202
- action,
1203
- formFields = [],
1204
- onInvoke,
1205
- processing = false,
1206
- errors,
1207
- size = "default",
1208
- className
1209
- }) {
1210
- const [confirmOpen, setConfirmOpen] = useState(false);
1211
- const [formOpen, setFormOpen] = useState(false);
1212
- const variant = VARIANT_OVERRIDE[action.variant] ?? COLOR_TO_VARIANT[action.color];
1213
- const handleClick = () => {
1214
- if (action.disabled) return;
1215
- if (action.requiresConfirmation) {
1216
- setConfirmOpen(true);
1217
- } else if (action.form && action.form.length > 0) {
1218
- setFormOpen(true);
1219
- } else {
1220
- onInvoke();
1221
- }
1222
- };
1223
- const handleConfirm = () => {
1224
- setConfirmOpen(false);
1225
- if (action.form && action.form.length > 0) {
1226
- setFormOpen(true);
1227
- } else {
1228
- onInvoke();
1229
- }
1230
- };
1231
- const handleFormSubmit = (values) => {
1232
- onInvoke(values);
1233
- setFormOpen(false);
1234
- };
1235
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1236
- /* @__PURE__ */ jsx(
1237
- Button,
1238
- {
1239
- variant,
1240
- size,
1241
- disabled: action.disabled === true || processing,
1242
- title: action.tooltip,
1243
- onClick: handleClick,
1244
- className,
1245
- children: action.label
1246
- }
1247
- ),
1248
- action.requiresConfirmation && /* @__PURE__ */ jsx(
1249
- ConfirmDialog,
1250
- {
1251
- open: confirmOpen,
1252
- onOpenChange: setConfirmOpen,
1253
- config: action.confirmation,
1254
- onConfirm: handleConfirm,
1255
- processing
1256
- }
1257
- ),
1258
- action.form && action.form.length > 0 && /* @__PURE__ */ jsx(
1259
- ActionFormModal,
1260
- {
1261
- open: formOpen,
1262
- onOpenChange: setFormOpen,
1263
- action,
1264
- fields: formFields,
1265
- onSubmit: handleFormSubmit,
1266
- processing,
1267
- errors
1268
- }
1269
- )
1270
- ] });
1271
- }
1272
- var COLOR_TO_VARIANT, VARIANT_OVERRIDE;
1273
- var init_ActionButton = __esm({
1274
- "src/action/ActionButton.tsx"() {
1275
- init_ActionFormModal();
1276
- init_Button();
1277
- init_ConfirmDialog();
1278
- COLOR_TO_VARIANT = {
1279
- primary: "default",
1280
- secondary: "outline",
1281
- destructive: "destructive",
1282
- success: "default",
1283
- warning: "default",
1284
- info: "default"
1285
- };
1286
- VARIANT_OVERRIDE = {
1287
- default: "default",
1288
- outline: "outline",
1289
- ghost: "ghost",
1290
- destructive: "destructive"
1291
- };
1292
- }
1293
- });
1294
- function ActionMenu({
1295
- actions,
1296
- formFieldsByAction = {},
1297
- onInvoke,
1298
- inlineThreshold = 3,
1299
- processing = false,
1300
- trigger,
1301
- className
1302
- }) {
1303
- if (actions.length === 0) return null;
1304
- if (actions.length <= inlineThreshold) {
1305
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-1", className), children: actions.map((action) => /* @__PURE__ */ jsx(
1306
- ActionButton,
1307
- {
1308
- action,
1309
- formFields: formFieldsByAction[action.name] ?? [],
1310
- onInvoke: (values) => onInvoke(action, values),
1311
- processing,
1312
- size: "sm"
1313
- },
1314
- action.name
1315
- )) });
1316
- }
1317
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1318
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Actions", children: "\u22EF" }) }),
1319
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", sideOffset: 6, className: cn("min-w-[12rem]", className), children: actions.map((action) => /* @__PURE__ */ jsx(
1320
- DropdownMenuItem,
1321
- {
1322
- disabled: action.disabled === true,
1323
- variant: action.color === "destructive" ? "destructive" : "default",
1324
- onSelect: () => {
1325
- if (action.disabled) return;
1326
- onInvoke(action);
1327
- },
1328
- children: action.label
1329
- },
1330
- action.name
1331
- )) })
1332
- ] });
1333
- }
1334
- var init_ActionMenu = __esm({
1335
- "src/action/ActionMenu.tsx"() {
1336
- init_dropdown_menu();
1337
- init_cn();
1338
- init_ActionButton();
1339
- init_Button();
1340
- }
1341
- });
1342
- function TableCell({ column, value }) {
1343
- switch (column.type) {
1344
- case "text":
1345
- return /* @__PURE__ */ jsx(TextCell, { column, value });
1346
- case "badge":
1347
- return /* @__PURE__ */ jsx(BadgeCell, { column, value });
1348
- case "boolean":
1349
- return /* @__PURE__ */ jsx(BooleanCell, { column, value });
1350
- case "date":
1351
- return /* @__PURE__ */ jsx(DateCell, { column, value });
1352
- case "number":
1353
- return /* @__PURE__ */ jsx(NumberCell, { column, value });
1354
- case "icon":
1355
- return /* @__PURE__ */ jsx(IconCell, { column });
1356
- case "image":
1357
- return /* @__PURE__ */ jsx(ImageCell, { column, value });
1358
- case "relationship":
1359
- return /* @__PURE__ */ jsx(RelationshipCell, { column, value });
1360
- case "computed":
1361
- return /* @__PURE__ */ jsx(ComputedCell, { column, value });
1362
- }
1363
- }
1364
- function asString(value) {
1365
- if (value === null || value === void 0) return "";
1366
- if (typeof value === "string") return value;
1367
- if (typeof value === "number" || typeof value === "boolean") return String(value);
1368
- return JSON.stringify(value);
1369
- }
1370
- function TextCell({ column, value }) {
1371
- const str = asString(value);
1372
- const truncated = column.props.truncate && str.length > column.props.truncate ? `${str.slice(0, column.props.truncate)}\u2026` : str;
1373
- return /* @__PURE__ */ jsx(
1374
- "span",
1375
- {
1376
- className: cn(
1377
- column.props.weight === "bold" && "font-bold",
1378
- column.props.weight === "medium" && "font-medium"
1379
- ),
1380
- children: truncated
1381
- }
1382
- );
1383
- }
1384
- function BadgeCell({ column, value }) {
1385
- const option = column.props.options?.find((o) => o.value === value);
1386
- const label = option?.label ?? asString(value);
1387
- return /* @__PURE__ */ jsx(
1388
- "span",
1389
- {
1390
- className: cn(
1391
- "inline-flex items-center px-2 py-0.5 text-xs",
1392
- column.props.pill ? "rounded-full" : "rounded-sm",
1393
- "bg-muted text-foreground"
1394
- ),
1395
- children: label
1396
- }
1397
- );
1398
- }
1399
- function BooleanCell({ column, value }) {
1400
- const truthy = Boolean(value);
1401
- return /* @__PURE__ */ jsx("span", { role: "img", "aria-label": truthy ? "true" : "false", children: truthy ? column.props.trueIcon ?? "\u2713" : column.props.falseIcon ?? "\u2014" });
1402
- }
1403
- function DateCell({ column, value }) {
1404
- if (!value) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
1405
- const raw = String(value);
1406
- const date = new Date(raw);
1407
- if (Number.isNaN(date.getTime())) return /* @__PURE__ */ jsx("span", { children: raw });
1408
- if (column.props.mode === "since") {
1409
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: formatRelative(date) });
1410
- }
1411
- if (column.props.mode === "datetime") {
1412
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: date.toLocaleString() });
1413
- }
1414
- return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), children: date.toLocaleDateString() });
1415
- }
1416
- function formatRelative(date) {
1417
- const seconds = Math.round((Date.now() - date.getTime()) / 1e3);
1418
- if (seconds < 60) return `${seconds}s ago`;
1419
- const minutes = Math.round(seconds / 60);
1420
- if (minutes < 60) return `${minutes}m ago`;
1421
- const hours = Math.round(minutes / 60);
1422
- if (hours < 24) return `${hours}h ago`;
1423
- const days = Math.round(hours / 24);
1424
- return `${days}d ago`;
1425
- }
1426
- function NumberCell({ column, value }) {
1427
- if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", { children: "\u2014" });
1428
- const num = typeof value === "number" ? value : Number(value);
1429
- if (Number.isNaN(num)) return /* @__PURE__ */ jsx("span", { children: asString(value) });
1430
- const formatted = num.toFixed(column.props.decimals ?? 0);
1431
- const [whole, dec] = formatted.split(".");
1432
- const safeWhole = whole ?? "0";
1433
- const ts = column.props.thousandsSeparator;
1434
- const finalWhole = ts ? safeWhole.replace(/\B(?=(\d{3})+(?!\d))/g, ts) : safeWhole;
1435
- const ds = column.props.decimalSeparator ?? ".";
1436
- return /* @__PURE__ */ jsxs("span", { children: [
1437
- column.props.prefix ?? "",
1438
- finalWhole,
1439
- dec ? `${ds}${dec}` : "",
1440
- column.props.suffix ?? ""
1441
- ] });
1442
- }
1443
- function IconCell({ column }) {
1444
- return /* @__PURE__ */ jsx("span", { role: "img", "aria-label": column.props.icon, title: column.props.icon, children: column.props.icon });
1445
- }
1446
- function ImageCell({ column, value }) {
1447
- const src = asString(value);
1448
- if (!src) return null;
1449
- const size = column.props.size ?? 32;
1450
- return /* @__PURE__ */ jsx(
1451
- "img",
1452
- {
1453
- src,
1454
- alt: "",
1455
- style: { width: size, height: size },
1456
- className: cn(column.props.shape === "circular" ? "rounded-full" : "rounded")
1457
- }
1458
- );
1459
- }
1460
- function RelationshipCell({ column, value }) {
1461
- if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", { children: "\u2014" });
1462
- if (typeof value === "object") {
1463
- const attr = value[column.props.attribute];
1464
- return /* @__PURE__ */ jsx("span", { children: asString(attr) });
1465
- }
1466
- return /* @__PURE__ */ jsx("span", { children: asString(value) });
1467
- }
1468
- function ComputedCell({ column, value }) {
1469
- if (value === null || value === void 0) {
1470
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: column.props.placeholder ?? "\u2014" });
1471
- }
1472
- return /* @__PURE__ */ jsx("span", { children: asString(value) });
1473
- }
1474
- var init_cells = __esm({
1475
- "src/table/cells.tsx"() {
1476
- init_cn();
1477
- }
1478
- });
1479
- function DataTable({
1480
- columns,
1481
- records,
1482
- enableSelection = false,
1483
- selectedIds = [],
1484
- onSelectionChange,
1485
- sort = null,
1486
- onSortChange,
1487
- rowActions,
1488
- loading = false,
1489
- emptyState,
1490
- className
1491
- }) {
1492
- const lastClickedIndexRef = useRef(null);
1493
- const visibleColumns = useMemo(() => columns.filter((col) => !col.hidden), [columns]);
1494
- const tableColumns = useMemo(
1495
- () => visibleColumns.map((col) => ({
1496
- id: col.name,
1497
- accessorFn: (row) => row[col.name],
1498
- header: () => col.label ?? col.name,
1499
- cell: (info) => /* @__PURE__ */ jsx(TableCell, { column: col, value: info.getValue() }),
1500
- enableSorting: col.sortable
1501
- })),
1502
- [visibleColumns]
1503
- );
1504
- const table = useReactTable({
1505
- data: records,
1506
- columns: tableColumns,
1507
- getCoreRowModel: getCoreRowModel()
1508
- });
1509
- const allSelected = enableSelection && records.length > 0 && selectedIds.length === records.length;
1510
- const someSelected = enableSelection && selectedIds.length > 0 && selectedIds.length < records.length;
1511
- const toggleAll = useCallback(() => {
1512
- if (!onSelectionChange) return;
1513
- if (allSelected) onSelectionChange([]);
1514
- else onSelectionChange(records.map((r) => r.id));
1515
- }, [allSelected, records, onSelectionChange]);
1516
- const toggleRow = useCallback(
1517
- (id, index, shiftKey) => {
1518
- if (!onSelectionChange) return;
1519
- const set = new Set(selectedIds);
1520
- const lastIndex = lastClickedIndexRef.current;
1521
- if (shiftKey && lastIndex !== null) {
1522
- const [from, to] = lastIndex < index ? [lastIndex, index] : [index, lastIndex];
1523
- const shouldSelect = !set.has(id);
1524
- for (let i = from; i <= to; i++) {
1525
- const rowId = records[i]?.id;
1526
- if (rowId === void 0) continue;
1527
- if (shouldSelect) set.add(rowId);
1528
- else set.delete(rowId);
1529
- }
1530
- } else if (set.has(id)) {
1531
- set.delete(id);
1532
- } else {
1533
- set.add(id);
1534
- }
1535
- lastClickedIndexRef.current = index;
1536
- onSelectionChange(Array.from(set));
1537
- },
1538
- [records, selectedIds, onSelectionChange]
1539
- );
1540
- const headerSortDirection = (columnName) => {
1541
- if (!sort || sort.column !== columnName) return null;
1542
- return sort.direction === "desc" ? "desc" : "asc";
1543
- };
1544
- return /* @__PURE__ */ jsx("div", { className: cn("w-full overflow-x-auto", className), children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
1545
- /* @__PURE__ */ jsx("thead", { className: "sticky top-0 bg-background", children: table.getHeaderGroups().map((group) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border", children: [
1546
- enableSelection && /* @__PURE__ */ jsx("th", { scope: "col", className: "w-10 px-3 py-2 text-left", children: /* @__PURE__ */ jsx(
1547
- "input",
1548
- {
1549
- type: "checkbox",
1550
- "aria-label": "Select all rows",
1551
- checked: allSelected,
1552
- ref: (el) => {
1553
- if (el) el.indeterminate = someSelected;
1554
- },
1555
- onChange: toggleAll
1556
- }
1557
- ) }),
1558
- group.headers.map((header) => {
1559
- const col = visibleColumns.find((c) => c.name === header.column.id);
1560
- const sortable = col?.sortable && onSortChange;
1561
- const direction = headerSortDirection(header.column.id);
1562
- return /* @__PURE__ */ jsx(
1563
- "th",
1564
- {
1565
- scope: "col",
1566
- className: cn(
1567
- "px-3 py-2 text-left font-medium text-muted-foreground",
1568
- col?.align === "center" && "text-center",
1569
- col?.align === "end" && "text-right"
1570
- ),
1571
- "aria-sort": direction === "asc" ? "ascending" : direction === "desc" ? "descending" : "none",
1572
- children: sortable ? /* @__PURE__ */ jsxs(
1573
- "button",
1574
- {
1575
- type: "button",
1576
- className: "inline-flex items-center gap-1 hover:text-foreground",
1577
- onClick: () => onSortChange(header.column.id, direction === "asc" ? "desc" : "asc"),
1578
- children: [
1579
- flexRender(header.column.columnDef.header, header.getContext()),
1580
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: direction === "asc" ? "\u25B2" : direction === "desc" ? "\u25BC" : "\u2195" })
1581
- ]
1582
- }
1583
- ) : flexRender(header.column.columnDef.header, header.getContext())
1584
- },
1585
- header.id
1586
- );
1587
- }),
1588
- rowActions && /* @__PURE__ */ jsx("th", { scope: "col", className: "w-24 px-3 py-2 text-right", "aria-label": "Actions" })
1589
- ] }, group.id)) }),
1590
- /* @__PURE__ */ jsxs("tbody", { children: [
1591
- loading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1592
- "td",
1593
- {
1594
- colSpan: visibleColumns.length + (enableSelection ? 1 : 0) + (rowActions ? 1 : 0),
1595
- className: "px-3 py-4 text-center text-muted-foreground",
1596
- children: "Loading\u2026"
1597
- }
1598
- ) }),
1599
- !loading && records.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1600
- "td",
1601
- {
1602
- colSpan: visibleColumns.length + (enableSelection ? 1 : 0) + (rowActions ? 1 : 0),
1603
- className: "px-3 py-8 text-center text-muted-foreground",
1604
- children: emptyState ?? "No records found."
1605
- }
1606
- ) }),
1607
- !loading && table.getRowModel().rows.map((row, index) => {
1608
- const record = row.original;
1609
- const checked = selectedIds.includes(record.id);
1610
- return /* @__PURE__ */ jsxs(
1611
- "tr",
1612
- {
1613
- "data-selected": checked || void 0,
1614
- className: cn("border-b border-border", checked && "bg-muted"),
1615
- children: [
1616
- enableSelection && /* @__PURE__ */ jsx("td", { className: "w-10 px-3 py-2", children: /* @__PURE__ */ jsx(
1617
- "input",
1618
- {
1619
- type: "checkbox",
1620
- "aria-label": `Select row ${record.id}`,
1621
- checked,
1622
- onChange: (event) => {
1623
- const native = event.nativeEvent;
1624
- toggleRow(record.id, index, native.shiftKey === true);
1625
- }
1626
- }
1627
- ) }),
1628
- row.getVisibleCells().map((cell) => {
1629
- const col = visibleColumns.find((c) => c.name === cell.column.id);
1630
- return /* @__PURE__ */ jsx(
1631
- "td",
1632
- {
1633
- className: cn(
1634
- "px-3 py-2",
1635
- col?.align === "center" && "text-center",
1636
- col?.align === "end" && "text-right",
1637
- col?.hiddenOnMobile && "hidden md:table-cell"
1638
- ),
1639
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
1640
- },
1641
- cell.id
1642
- );
1643
- }),
1644
- rowActions && /* @__PURE__ */ jsx("td", { className: "w-24 px-3 py-2 text-right", children: rowActions(record) })
1645
- ]
1646
- },
1647
- row.id
1648
- );
1649
- })
1650
- ] })
1651
- ] }) });
1652
- }
1653
- var init_DataTable = __esm({
1654
- "src/table/DataTable.tsx"() {
1655
- init_cn();
1656
- init_cells();
1657
- }
1658
- });
1659
- function TableFilters({
1660
- filters,
1661
- values,
1662
- onChange,
1663
- onClearAll,
1664
- className
1665
- }) {
1666
- if (filters.length === 0) return null;
1667
- const activeCount = filters.reduce((acc, f) => {
1668
- const v = values[f.name];
1669
- return v !== void 0 && v !== null && v !== "" ? acc + 1 : acc;
1670
- }, 0);
1671
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("flex flex-wrap items-end gap-2 border-0 p-0", className), children: [
1672
- /* @__PURE__ */ jsx("legend", { className: "sr-only", children: "Filters" }),
1673
- filters.map((filter) => /* @__PURE__ */ jsx(
1674
- FilterControl,
1675
- {
1676
- filter,
1677
- value: values[filter.name],
1678
- onChange: (v) => onChange(filter.name, v)
1679
- },
1680
- filter.name
1681
- )),
1682
- activeCount > 0 && onClearAll && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: [
1683
- "Clear filters (",
1684
- activeCount,
1685
- ")"
1686
- ] })
1687
- ] });
1688
- }
1689
- function FilterControl({
1690
- filter,
1691
- value,
1692
- onChange
1693
- }) {
1694
- switch (filter.type) {
1695
- case "select":
1696
- return /* @__PURE__ */ jsx(SelectFilter, { filter, value, onChange });
1697
- case "multiSelect":
1698
- return /* @__PURE__ */ jsx(MultiSelectFilter, { filter, value, onChange });
1699
- case "text":
1700
- return /* @__PURE__ */ jsx(TextFilter, { filter, value, onChange });
1701
- case "ternary":
1702
- return /* @__PURE__ */ jsx(TernaryFilter, { filter, value, onChange });
1703
- case "dateRange":
1704
- case "scope":
1705
- return null;
1706
- }
1707
- }
1708
- function controlClasses() {
1709
- return cn(
1710
- "h-9 rounded-sm border border-[var(--input)]",
1711
- "bg-background px-3 text-sm",
1712
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1713
- );
1714
- }
1715
- function SelectFilter({
1716
- filter,
1717
- value,
1718
- onChange
1719
- }) {
1720
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1721
- filter.label ?? filter.name,
1722
- /* @__PURE__ */ jsxs(
1723
- "select",
1724
- {
1725
- className: controlClasses(),
1726
- value: value === void 0 || value === null ? "" : String(value),
1727
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
1728
- children: [
1729
- /* @__PURE__ */ jsx("option", { value: "", children: "All" }),
1730
- filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
1731
- ]
1732
- }
1733
- )
1734
- ] });
1735
- }
1736
- function MultiSelectFilter({
1737
- filter,
1738
- value,
1739
- onChange
1740
- }) {
1741
- const arr = Array.isArray(value) ? value : [];
1742
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1743
- filter.label ?? filter.name,
1744
- /* @__PURE__ */ jsx(
1745
- "select",
1746
- {
1747
- multiple: true,
1748
- className: cn(controlClasses(), "h-auto min-h-[2.25rem] py-1"),
1749
- value: arr.map(String),
1750
- onChange: (e) => {
1751
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
1752
- onChange(next.length === 0 ? null : next);
1753
- },
1754
- children: filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
1755
- }
1756
- )
1757
- ] });
1758
- }
1759
- function TextFilter({
1760
- filter,
1761
- value,
1762
- onChange
1763
- }) {
1764
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1765
- filter.label ?? filter.name,
1766
- /* @__PURE__ */ jsx(
1767
- "input",
1768
- {
1769
- type: "text",
1770
- className: controlClasses(),
1771
- value: typeof value === "string" ? value : "",
1772
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
1773
- }
1774
- )
1775
- ] });
1776
- }
1777
- function TernaryFilter({
1778
- filter,
1779
- value,
1780
- onChange
1781
- }) {
1782
- const current = value === void 0 || value === null ? "all" : String(value);
1783
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1784
- filter.label ?? filter.name,
1785
- /* @__PURE__ */ jsxs(
1786
- "select",
1787
- {
1788
- className: controlClasses(),
1789
- value: current,
1790
- onChange: (e) => {
1791
- const v = e.target.value;
1792
- onChange(v === "all" ? null : v);
1793
- },
1794
- children: [
1795
- /* @__PURE__ */ jsx("option", { value: "all", children: filter.props.allLabel ?? "All" }),
1796
- /* @__PURE__ */ jsx("option", { value: "true", children: filter.props.trueLabel ?? "Yes" }),
1797
- /* @__PURE__ */ jsx("option", { value: "false", children: filter.props.falseLabel ?? "No" })
1798
- ]
1799
- }
1800
- )
1801
- ] });
1802
- }
1803
- var init_TableFilters = __esm({
1804
- "src/table/TableFilters.tsx"() {
1805
- init_Button();
1806
- init_cn();
1807
- }
1808
- });
1809
- function TablePagination({
1810
- meta,
1811
- onPageChange,
1812
- onPerPageChange,
1813
- perPageOptions = [10, 25, 50, 100],
1814
- className
1815
- }) {
1816
- const from = (meta.currentPage - 1) * meta.perPage + 1;
1817
- const to = Math.min(meta.currentPage * meta.perPage, meta.total);
1818
- const isFirst = meta.currentPage <= 1;
1819
- const isLast = meta.currentPage >= meta.lastPage;
1820
- return /* @__PURE__ */ jsxs(
1821
- "nav",
1822
- {
1823
- className: cn(
1824
- "flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm",
1825
- className
1826
- ),
1827
- "aria-label": "Pagination",
1828
- children: [
1829
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: meta.total === 0 ? "No results" : `${from}\u2013${to} of ${meta.total}` }),
1830
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1831
- onPerPageChange && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
1832
- "Per page",
1833
- /* @__PURE__ */ jsx(
1834
- "select",
1835
- {
1836
- className: "h-8 rounded-sm border border-[var(--input)] bg-background px-2",
1837
- value: meta.perPage,
1838
- onChange: (e) => onPerPageChange(Number(e.target.value)),
1839
- children: perPageOptions.map((n) => /* @__PURE__ */ jsx("option", { value: n, children: n }, n))
1840
- }
1841
- )
1842
- ] }),
1843
- /* @__PURE__ */ jsx(
1844
- Button,
1845
- {
1846
- variant: "outline",
1847
- size: "sm",
1848
- disabled: isFirst,
1849
- onClick: () => onPageChange(meta.currentPage - 1),
1850
- "aria-label": "Previous page",
1851
- children: "Prev"
1852
- }
1853
- ),
1854
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
1855
- meta.currentPage,
1856
- " / ",
1857
- meta.lastPage
1858
- ] }),
1859
- /* @__PURE__ */ jsx(
1860
- Button,
1861
- {
1862
- variant: "outline",
1863
- size: "sm",
1864
- disabled: isLast,
1865
- onClick: () => onPageChange(meta.currentPage + 1),
1866
- "aria-label": "Next page",
1867
- children: "Next"
1868
- }
1869
- )
1870
- ] })
1871
- ]
1872
- }
1873
- );
1874
- }
1875
- var init_TablePagination = __esm({
1876
- "src/table/TablePagination.tsx"() {
1877
- init_Button();
1878
- init_cn();
1879
- }
1880
- });
1881
- function TableToolbar({
1882
- search,
1883
- filters,
1884
- selectedCount = 0,
1885
- bulkActions,
1886
- onClearSelection,
1887
- className
1888
- }) {
1889
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3 px-3 py-3", className), children: [
1890
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
1891
- search,
1892
- filters
1893
- ] }),
1894
- selectedCount > 0 && /* @__PURE__ */ jsxs(
1895
- "section",
1896
- {
1897
- className: "flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2",
1898
- "aria-label": "Bulk actions",
1899
- children: [
1900
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium", children: [
1901
- selectedCount,
1902
- " selected"
1903
- ] }),
1904
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
1905
- bulkActions,
1906
- onClearSelection && /* @__PURE__ */ jsx(
1907
- "button",
1908
- {
1909
- type: "button",
1910
- className: "text-sm text-muted-foreground hover:underline",
1911
- onClick: onClearSelection,
1912
- children: "Clear"
1913
- }
1914
- )
1915
- ] })
1916
- ]
1917
- }
1918
- )
1919
- ] });
1920
- }
1921
- var init_TableToolbar = __esm({
1922
- "src/table/TableToolbar.tsx"() {
1923
- init_cn();
1924
- }
1925
- });
1926
- function ResourceIndex({
1927
- resource,
1928
- records,
1929
- pagination,
1930
- columns,
1931
- filters,
1932
- search,
1933
- sort,
1934
- selectedIds = [],
1935
- filterValues = {},
1936
- onSelectionChange,
1937
- onSortChange,
1938
- onPageChange,
1939
- onPerPageChange,
1940
- onSearchChange,
1941
- onFilterChange,
1942
- onClearFilters,
1943
- searchSlot,
1944
- toolbarActions,
1945
- rowActions,
1946
- bulkActions,
1947
- emptyState,
1948
- loading = false,
1949
- className
1950
- }) {
1951
- const enableSelection = Boolean(onSelectionChange);
1952
- const renderSearch = () => {
1953
- if (searchSlot) return searchSlot;
1954
- if (!onSearchChange) return null;
1955
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
1956
- "Search",
1957
- /* @__PURE__ */ jsx(
1958
- "input",
1959
- {
1960
- type: "search",
1961
- placeholder: `Search ${resource.pluralLabel.toLowerCase()}\u2026`,
1962
- 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",
1963
- value: search ?? "",
1964
- onChange: (e) => onSearchChange(e.target.value)
1965
- }
1966
- )
1967
- ] });
1968
- };
1969
- const normalisedSort = sort?.column ? {
1970
- column: sort.column,
1971
- direction: sort.direction === "desc" ? "desc" : "asc"
1972
- } : null;
1973
- return /* @__PURE__ */ jsxs(
1974
- "section",
1975
- {
1976
- className: cn("flex flex-col rounded-md border border-border bg-background", className),
1977
- "aria-label": resource.pluralLabel,
1978
- children: [
1979
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-center gap-3 border-b border-border px-4 py-3", children: [
1980
- /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold", children: resource.pluralLabel }),
1981
- /* @__PURE__ */ jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: toolbarActions })
1982
- ] }),
1983
- /* @__PURE__ */ jsx(
1984
- TableToolbar,
1985
- {
1986
- search: renderSearch(),
1987
- filters: filters.length > 0 ? /* @__PURE__ */ jsx(
1988
- TableFilters,
1989
- {
1990
- filters,
1991
- values: filterValues,
1992
- onChange: (name, value) => onFilterChange?.(name, value),
1993
- onClearAll: onClearFilters
1994
- }
1995
- ) : null,
1996
- selectedCount: selectedIds.length,
1997
- bulkActions,
1998
- onClearSelection: onSelectionChange ? () => onSelectionChange([]) : void 0
1999
- }
2000
- ),
2001
- /* @__PURE__ */ jsx(
2002
- DataTable,
2003
- {
2004
- columns,
2005
- records,
2006
- enableSelection,
2007
- selectedIds,
2008
- onSelectionChange,
2009
- sort: normalisedSort,
2010
- onSortChange,
2011
- rowActions: rowActions ? (record) => rowActions(record) : void 0,
2012
- loading,
2013
- emptyState
2014
- }
2015
- ),
2016
- pagination && /* @__PURE__ */ jsx(
2017
- TablePagination,
2018
- {
2019
- meta: pagination,
2020
- onPageChange: (page) => onPageChange?.(page),
2021
- onPerPageChange
2022
- }
2023
- )
2024
- ]
2025
- }
2026
- );
2027
- }
2028
- var init_ResourceIndex = __esm({
2029
- "src/resource/ResourceIndex.tsx"() {
2030
- init_DataTable();
2031
- init_TableFilters();
2032
- init_TablePagination();
2033
- init_TableToolbar();
2034
- init_cn();
2035
- }
2036
- });
2037
-
2038
- // src/pages/ArqelIndexPage.tsx
2039
- var ArqelIndexPage_exports = {};
2040
- __export(ArqelIndexPage_exports, {
2041
- default: () => ArqelIndexPage
2042
- });
2043
- function pruneEmptyFilters(values) {
2044
- const out = {};
2045
- for (const [k, v] of Object.entries(values)) {
2046
- if (v === void 0 || v === null || v === "") continue;
2047
- if (Array.isArray(v) && v.length === 0) continue;
2048
- out[k] = v;
2049
- }
2050
- return out;
2051
- }
2052
- function invokeAction(action, record, formValues) {
2053
- const recordId = record?.id;
2054
- const url = action.url ? action.url.replace("{id}", String(recordId ?? "")) : `/arqel-dev/actions/${action.name}`;
2055
- const method = action.method.toLowerCase();
2056
- const data = { ...formValues ?? {} };
2057
- if (recordId !== void 0) data["record_id"] = recordId;
2058
- router.visit(url, {
2059
- method,
2060
- data,
2061
- preserveScroll: true
2062
- });
2063
- }
2064
- function buildQuery(params) {
2065
- const data = {};
2066
- const filters = pruneEmptyFilters(params.filters);
2067
- if (Object.keys(filters).length > 0) data["filter"] = filters;
2068
- if (params.search !== void 0) data["search"] = params.search;
2069
- if (params.sort?.column) {
2070
- data["sort"] = params.sort.column;
2071
- if (params.sort.direction) data["direction"] = params.sort.direction;
2072
- }
2073
- if (params.page && params.page > 1) data["page"] = params.page;
2074
- if (params.perPage) data["per_page"] = params.perPage;
2075
- return data;
2076
- }
2077
- function ArqelIndexPage() {
2078
- const page = usePage();
2079
- const props = page.props;
2080
- const initialFilters = useMemo(() => {
2081
- if (typeof window === "undefined") return {};
2082
- const sp = new URLSearchParams(window.location.search);
2083
- const fromUrl = {};
2084
- for (const [key, value] of sp.entries()) {
2085
- const m = key.match(/^filter\[([^\]]+)\]$/);
2086
- if (m?.[1]) fromUrl[m[1]] = value;
2087
- }
2088
- return fromUrl;
2089
- }, []);
2090
- const [filterValues, setFilterValues] = useState(initialFilters);
2091
- const [searchValue, setSearchValue] = useState(props.search ?? "");
2092
- const [perPage, setPerPage] = useState(props.pagination?.perPage);
2093
- const [selectedIds, setSelectedIds] = useState([]);
2094
- const searchDebounceRef = useRef(null);
2095
- const visit = (data) => {
2096
- const url = typeof window !== "undefined" ? window.location.pathname : page.url.split("?")[0];
2097
- router.get(url ?? "/", data, {
2098
- preserveState: true,
2099
- preserveScroll: true,
2100
- replace: true
2101
- });
2102
- };
2103
- const currentSort = props.sort ?? null;
2104
- const handleFilterChange = (name, value) => {
2105
- const nextFilters = { ...filterValues };
2106
- if (value === void 0 || value === null || value === "") {
2107
- delete nextFilters[name];
2108
- } else {
2109
- nextFilters[name] = value;
2110
- }
2111
- setFilterValues(nextFilters);
2112
- visit(buildQuery({ filters: nextFilters, search: searchValue, sort: currentSort, perPage }));
2113
- };
2114
- const handleClearFilters = () => {
2115
- setFilterValues({});
2116
- visit(buildQuery({ filters: {}, search: searchValue, sort: currentSort, perPage }));
2117
- };
2118
- const handleSearchChange = (value) => {
2119
- setSearchValue(value);
2120
- if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
2121
- searchDebounceRef.current = setTimeout(() => {
2122
- visit(buildQuery({ filters: filterValues, search: value, sort: currentSort, perPage }));
2123
- }, 300);
2124
- };
2125
- useEffect(
2126
- () => () => {
2127
- if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
2128
- },
2129
- []
2130
- );
2131
- const handleSortChange = (column, direction) => {
2132
- visit(
2133
- buildQuery({
2134
- filters: filterValues,
2135
- search: searchValue,
2136
- sort: { column, direction },
2137
- perPage
2138
- })
2139
- );
2140
- };
2141
- const handlePageChange = (pageNum) => {
2142
- visit(
2143
- buildQuery({
2144
- filters: filterValues,
2145
- search: searchValue,
2146
- sort: currentSort,
2147
- page: pageNum,
2148
- perPage
2149
- })
2150
- );
2151
- };
2152
- const handlePerPageChange = (newPerPage) => {
2153
- setPerPage(newPerPage);
2154
- visit(
2155
- buildQuery({
2156
- filters: filterValues,
2157
- search: searchValue,
2158
- sort: currentSort,
2159
- perPage: newPerPage
2160
- })
2161
- );
2162
- };
2163
- const bulkActionsList = props.actions?.bulk ?? [];
2164
- const bulkActions = bulkActionsList.length > 0 && selectedIds.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: bulkActionsList.map((action) => /* @__PURE__ */ jsx(
2165
- "button",
2166
- {
2167
- type: "button",
2168
- onClick: () => {
2169
- const url = action.url ?? `/arqel-dev/actions/${action.name}`;
2170
- const method = action.method.toLowerCase();
2171
- router.visit(url, {
2172
- method,
2173
- data: { record_ids: selectedIds },
2174
- preserveScroll: true,
2175
- onSuccess: () => setSelectedIds([])
2176
- });
2177
- },
2178
- 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",
2179
- children: action.label
2180
- },
2181
- action.name
2182
- )) }) : void 0;
2183
- const rowActionsList = props.actions?.row ?? [];
2184
- const rowActions = rowActionsList.length > 0 ? (record) => /* @__PURE__ */ jsx(
2185
- ActionMenu,
2186
- {
2187
- actions: rowActionsList,
2188
- onInvoke: (action, formValues) => invokeAction(action, record, formValues)
2189
- }
2190
- ) : void 0;
2191
- return /* @__PURE__ */ jsx(
2192
- ResourceIndex,
2193
- {
2194
- ...props,
2195
- filterValues,
2196
- onFilterChange: handleFilterChange,
2197
- onClearFilters: handleClearFilters,
2198
- onSearchChange: handleSearchChange,
2199
- onSortChange: handleSortChange,
2200
- onPageChange: handlePageChange,
2201
- onPerPageChange: handlePerPageChange,
2202
- search: searchValue,
2203
- selectedIds,
2204
- onSelectionChange: (ids) => setSelectedIds(ids),
2205
- ...rowActions ? { rowActions } : {},
2206
- ...bulkActions ? { bulkActions } : {}
2207
- }
2208
- );
2209
- }
2210
- var init_ArqelIndexPage = __esm({
2211
- "src/pages/ArqelIndexPage.tsx"() {
2212
- init_ActionMenu();
2213
- init_ResourceIndex();
2214
- }
2215
- });
2216
-
2217
- // src/pages/ArqelShowPage.tsx
2218
- var ArqelShowPage_exports = {};
2219
- __export(ArqelShowPage_exports, {
2220
- default: () => ArqelShowPage
2221
- });
2222
- function ArqelShowPage() {
2223
- const page = usePage();
2224
- const props = page.props;
2225
- const fields = props.fields ?? [];
2226
- const values = {};
2227
- for (const field of fields) {
2228
- const name = field.name;
2229
- if (typeof name === "string" && props.record !== null && typeof props.record === "object") {
2230
- values[name] = props.record[name];
2231
- }
2232
- }
2233
- const declaredForm = props.form;
2234
- const schema = declaredForm ? { ...declaredForm, disabled: true } : FALLBACK_SCHEMA3;
2235
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2236
- /* @__PURE__ */ jsx(
2237
- PageHeader,
2238
- {
2239
- title: props.recordTitle ?? props.resource?.label ?? "Record",
2240
- description: props.recordSubtitle ?? null
2241
- }
2242
- ),
2243
- /* @__PURE__ */ jsx(
2244
- FormRenderer,
2245
- {
2246
- schema,
2247
- fields,
2248
- values,
2249
- onChange: () => {
2250
- },
2251
- errors: {}
2252
- }
2253
- )
2254
- ] });
2255
- }
2256
- var FALLBACK_SCHEMA3;
2257
- var init_ArqelShowPage = __esm({
2258
- "src/pages/ArqelShowPage.tsx"() {
2259
- init_FormRenderer();
2260
- init_PageHeader();
2261
- FALLBACK_SCHEMA3 = {
2262
- schema: [],
2263
- columns: 1,
2264
- model: null,
2265
- inline: false,
2266
- disabled: true
2267
- };
2268
- }
2269
- });
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';
2270
18
 
2271
19
  // src/pages/index.ts
2272
- init_ArqelCreatePage();
2273
- init_ArqelEditPage();
2274
- init_ArqelIndexPage();
2275
- init_ArqelShowPage();
2276
20
  var arqelPages = {
2277
- "arqel::index": () => Promise.resolve().then(() => (init_ArqelIndexPage(), ArqelIndexPage_exports)),
2278
- "arqel::create": () => Promise.resolve().then(() => (init_ArqelCreatePage(), ArqelCreatePage_exports)),
2279
- "arqel::edit": () => Promise.resolve().then(() => (init_ArqelEditPage(), ArqelEditPage_exports)),
2280
- "arqel::show": () => Promise.resolve().then(() => (init_ArqelShowPage(), ArqelShowPage_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')
2281
26
  };
2282
27
 
2283
- export { ArqelCreatePage, ArqelEditPage, ArqelIndexPage, ArqelShowPage, arqelPages };
28
+ export { arqelPages };
2284
29
  //# sourceMappingURL=pages.js.map
2285
30
  //# sourceMappingURL=pages.js.map