@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/widgets.js CHANGED
@@ -1,677 +1,5 @@
1
- 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';
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { lazy, Suspense, useState, useRef, useEffect } from 'react';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
-
7
- var __defProp = Object.defineProperty;
8
- var __getOwnPropNames = Object.getOwnPropertyNames;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
-
17
- // src/widgets/types.ts
18
- function toRowShape(data) {
19
- return data.labels.map((label, i) => {
20
- const row = { name: label };
21
- data.datasets.forEach((ds, j) => {
22
- row[`dataset${j}`] = ds.data[i] ?? 0;
23
- });
24
- return row;
25
- });
26
- }
27
- function colorFor(dataset, index) {
28
- return dataset.color ?? PALETTE[index % PALETTE.length] ?? "#2563eb";
29
- }
30
- var PALETTE;
31
- var init_types = __esm({
32
- "src/widgets/types.ts"() {
33
- PALETTE = [
34
- "#2563eb",
35
- "#10b981",
36
- "#f59e0b",
37
- "#ef4444",
38
- "#8b5cf6",
39
- "#ec4899",
40
- "#14b8a6",
41
- "#f97316"
42
- ];
43
- }
44
- });
45
-
46
- // src/widgets/charts/LineChart.tsx
47
- var LineChart_exports = {};
48
- __export(LineChart_exports, {
49
- LineChart: () => LineChart
50
- });
51
- function LineChart({ chartData, height, showLegend, showGrid }) {
52
- const rows = toRowShape(chartData);
53
- 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: [
54
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
55
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
56
- /* @__PURE__ */ jsx(YAxis, {}),
57
- /* @__PURE__ */ jsx(Tooltip, {}),
58
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
59
- chartData.datasets.map((ds, i) => /* @__PURE__ */ jsx(
60
- Line,
61
- {
62
- type: "monotone",
63
- dataKey: `dataset${i}`,
64
- name: ds.label,
65
- stroke: colorFor(ds, i),
66
- strokeWidth: 2,
67
- dot: false
68
- },
69
- ds.label
70
- ))
71
- ] }) }) });
72
- }
73
- var init_LineChart = __esm({
74
- "src/widgets/charts/LineChart.tsx"() {
75
- init_types();
76
- }
77
- });
78
-
79
- // src/widgets/charts/BarChart.tsx
80
- var BarChart_exports = {};
81
- __export(BarChart_exports, {
82
- BarChart: () => BarChart
83
- });
84
- function BarChart({ chartData, height, showLegend, showGrid }) {
85
- const rows = toRowShape(chartData);
86
- 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: [
87
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
88
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
89
- /* @__PURE__ */ jsx(YAxis, {}),
90
- /* @__PURE__ */ jsx(Tooltip, {}),
91
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
92
- chartData.datasets.map((ds, i) => /* @__PURE__ */ jsx(Bar, { dataKey: `dataset${i}`, name: ds.label, fill: colorFor(ds, i) }, ds.label))
93
- ] }) }) });
94
- }
95
- var init_BarChart = __esm({
96
- "src/widgets/charts/BarChart.tsx"() {
97
- init_types();
98
- }
99
- });
100
-
101
- // src/widgets/charts/AreaChart.tsx
102
- var AreaChart_exports = {};
103
- __export(AreaChart_exports, {
104
- AreaChart: () => AreaChart
105
- });
106
- function AreaChart({ chartData, height, showLegend, showGrid }) {
107
- const rows = toRowShape(chartData);
108
- 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: [
109
- showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3" }) : null,
110
- /* @__PURE__ */ jsx(XAxis, { dataKey: "name" }),
111
- /* @__PURE__ */ jsx(YAxis, {}),
112
- /* @__PURE__ */ jsx(Tooltip, {}),
113
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null,
114
- chartData.datasets.map((ds, i) => {
115
- const color = colorFor(ds, i);
116
- return /* @__PURE__ */ jsx(
117
- Area,
118
- {
119
- type: "monotone",
120
- dataKey: `dataset${i}`,
121
- name: ds.label,
122
- stroke: color,
123
- fill: color,
124
- fillOpacity: 0.3
125
- },
126
- ds.label
127
- );
128
- })
129
- ] }) }) });
130
- }
131
- var init_AreaChart = __esm({
132
- "src/widgets/charts/AreaChart.tsx"() {
133
- init_types();
134
- }
135
- });
136
-
137
- // src/widgets/charts/PieChart.tsx
138
- var PieChart_exports = {};
139
- __export(PieChart_exports, {
140
- PieChart: () => PieChart
141
- });
142
- function PieChart({
143
- chartData,
144
- height,
145
- showLegend,
146
- innerRadius = 0,
147
- testId = "chart-pie"
148
- }) {
149
- const first = chartData.datasets[0];
150
- const slices = (first?.data ?? []).map((value, i) => ({
151
- name: chartData.labels[i] ?? `Slice ${i + 1}`,
152
- value
153
- }));
154
- 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: [
155
- /* @__PURE__ */ jsx(
156
- Pie,
157
- {
158
- data: slices,
159
- dataKey: "value",
160
- nameKey: "name",
161
- innerRadius,
162
- outerRadius: "80%",
163
- label: true,
164
- children: slices.map((slice, i) => /* @__PURE__ */ jsx(Cell, { fill: colorFor(first ?? { }, i) }, slice.name))
165
- }
166
- ),
167
- /* @__PURE__ */ jsx(Tooltip, {}),
168
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
169
- ] }) }) });
170
- }
171
- var init_PieChart = __esm({
172
- "src/widgets/charts/PieChart.tsx"() {
173
- init_types();
174
- }
175
- });
176
-
177
- // src/widgets/charts/DonutChart.tsx
178
- var DonutChart_exports = {};
179
- __export(DonutChart_exports, {
180
- DonutChart: () => DonutChart
181
- });
182
- function DonutChart(props) {
183
- return /* @__PURE__ */ jsx(PieChart, { ...props, innerRadius: 60, testId: "chart-donut" });
184
- }
185
- var init_DonutChart = __esm({
186
- "src/widgets/charts/DonutChart.tsx"() {
187
- init_PieChart();
188
- }
189
- });
190
-
191
- // src/widgets/charts/RadarChart.tsx
192
- var RadarChart_exports = {};
193
- __export(RadarChart_exports, {
194
- RadarChart: () => RadarChart
195
- });
196
- function RadarChart({ chartData, height, showLegend, showGrid }) {
197
- const rows = toRowShape(chartData);
198
- 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: [
199
- showGrid ? /* @__PURE__ */ jsx(PolarGrid, {}) : null,
200
- /* @__PURE__ */ jsx(PolarAngleAxis, { dataKey: "name" }),
201
- /* @__PURE__ */ jsx(PolarRadiusAxis, {}),
202
- chartData.datasets.map((ds, i) => {
203
- const color = colorFor(ds, i);
204
- return /* @__PURE__ */ jsx(
205
- Radar,
206
- {
207
- dataKey: `dataset${i}`,
208
- name: ds.label,
209
- stroke: color,
210
- fill: color,
211
- fillOpacity: 0.3
212
- },
213
- ds.label
214
- );
215
- }),
216
- /* @__PURE__ */ jsx(Tooltip, {}),
217
- showLegend ? /* @__PURE__ */ jsx(Legend, {}) : null
218
- ] }) }) });
219
- }
220
- var init_RadarChart = __esm({
221
- "src/widgets/charts/RadarChart.tsx"() {
222
- init_types();
223
- }
224
- });
225
- function cn(...inputs) {
226
- return twMerge(clsx(inputs));
227
- }
228
- function resolveColumnSpan(columnSpan) {
229
- if (columnSpan === void 0) return void 0;
230
- if (typeof columnSpan === "number") return `col-span-${columnSpan}`;
231
- return columnSpan;
232
- }
233
- function WidgetWrapper({
234
- heading,
235
- description,
236
- loading = false,
237
- error = null,
238
- onRetry,
239
- columnSpan,
240
- className,
241
- children
242
- }) {
243
- const baseClass = cn(
244
- "rounded border bg-background p-4",
245
- resolveColumnSpan(columnSpan),
246
- className
247
- );
248
- if (loading) {
249
- return /* @__PURE__ */ jsx(
250
- "section",
251
- {
252
- "aria-label": heading,
253
- "aria-busy": "true",
254
- className: baseClass,
255
- "data-widget-state": "loading",
256
- children: /* @__PURE__ */ jsx("div", { className: "animate-pulse h-32 bg-muted rounded" })
257
- }
258
- );
259
- }
260
- if (error) {
261
- 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: [
262
- /* @__PURE__ */ jsx("p", { children: error.message }),
263
- onRetry && /* @__PURE__ */ jsx(
264
- "button",
265
- {
266
- type: "button",
267
- onClick: onRetry,
268
- className: "self-start rounded border px-2 py-1 text-xs text-foreground hover:bg-muted",
269
- children: "Retry"
270
- }
271
- )
272
- ] }) });
273
- }
274
- return /* @__PURE__ */ jsxs("section", { "aria-label": heading, className: baseClass, "data-widget-state": "ready", children: [
275
- heading && /* @__PURE__ */ jsx("h2", { className: "text-sm font-medium text-muted-foreground", children: heading }),
276
- description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: description }),
277
- /* @__PURE__ */ jsx("div", { className: cn(heading || description ? "mt-3" : void 0), children })
278
- ] });
279
- }
280
- var LineChart2 = lazy(() => Promise.resolve().then(() => (init_LineChart(), LineChart_exports)).then((m) => ({ default: m.LineChart })));
281
- var BarChart2 = lazy(() => Promise.resolve().then(() => (init_BarChart(), BarChart_exports)).then((m) => ({ default: m.BarChart })));
282
- var AreaChart2 = lazy(() => Promise.resolve().then(() => (init_AreaChart(), AreaChart_exports)).then((m) => ({ default: m.AreaChart })));
283
- var PieChart2 = lazy(() => Promise.resolve().then(() => (init_PieChart(), PieChart_exports)).then((m) => ({ default: m.PieChart })));
284
- var DonutChart2 = lazy(
285
- () => Promise.resolve().then(() => (init_DonutChart(), DonutChart_exports)).then((m) => ({ default: m.DonutChart }))
286
- );
287
- var RadarChart2 = lazy(
288
- () => Promise.resolve().then(() => (init_RadarChart(), RadarChart_exports)).then((m) => ({ default: m.RadarChart }))
289
- );
290
- var REGISTRY = {
291
- line: LineChart2,
292
- bar: BarChart2,
293
- area: AreaChart2,
294
- pie: PieChart2,
295
- donut: DonutChart2,
296
- radar: RadarChart2
297
- };
298
- function ChartCard({ widget }) {
299
- const Chart = REGISTRY[widget.chartType] ?? LineChart2;
300
- return /* @__PURE__ */ jsx(WidgetWrapper, { heading: widget.heading, description: widget.description, children: /* @__PURE__ */ jsx(
301
- Suspense,
302
- {
303
- fallback: /* @__PURE__ */ jsx(
304
- "div",
305
- {
306
- role: "status",
307
- "aria-label": "Loading chart",
308
- "data-testid": "chart-fallback",
309
- className: "animate-pulse rounded bg-muted",
310
- style: { height: widget.height }
311
- }
312
- ),
313
- children: /* @__PURE__ */ jsx(
314
- Chart,
315
- {
316
- chartData: widget.chartData,
317
- chartOptions: widget.chartOptions,
318
- height: widget.height,
319
- showLegend: widget.showLegend,
320
- showGrid: widget.showGrid
321
- }
322
- )
323
- }
324
- ) });
325
- }
326
- function DashboardFilters({ filters, values, onChange, className }) {
327
- if (!filters || filters.length === 0) return null;
328
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-2", className), "data-testid": "dashboard-filters", children: filters.map((filter) => /* @__PURE__ */ jsx(
329
- FilterControl,
330
- {
331
- filter,
332
- value: values[filter.name],
333
- onChange: (v) => onChange(filter.name, v)
334
- },
335
- filter.name
336
- )) });
337
- }
338
- function FilterControl({
339
- filter,
340
- value,
341
- onChange
342
- }) {
343
- if (filter.type === "select") {
344
- return /* @__PURE__ */ jsx(SelectControl, { filter, value, onChange });
345
- }
346
- if (filter.type === "date_range") {
347
- return /* @__PURE__ */ jsx(
348
- DateRangeControl,
349
- {
350
- filter,
351
- value,
352
- onChange
353
- }
354
- );
355
- }
356
- return null;
357
- }
358
- function controlClasses() {
359
- return cn(
360
- "h-9 rounded-sm border border-[var(--input)]",
361
- "bg-background px-2 text-sm",
362
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
363
- );
364
- }
365
- function normaliseOptions(raw) {
366
- if (!raw) return [];
367
- if (Array.isArray(raw)) {
368
- return raw.map((o) => ({ value: String(o.value), label: String(o.label ?? o.value) }));
369
- }
370
- return Object.entries(raw).map(([k, v]) => ({ value: String(k), label: String(v) }));
371
- }
372
- function SelectControl({
373
- filter,
374
- value,
375
- onChange
376
- }) {
377
- const options = normaliseOptions(filter.options);
378
- return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
379
- filter.label ?? filter.name,
380
- /* @__PURE__ */ jsxs(
381
- "select",
382
- {
383
- "aria-label": filter.label ?? filter.name,
384
- className: controlClasses(),
385
- value: value === void 0 || value === null ? "" : String(value),
386
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
387
- children: [
388
- /* @__PURE__ */ jsx("option", { value: "", children: "All" }),
389
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
390
- ]
391
- }
392
- )
393
- ] });
394
- }
395
- function DateRangeControl({
396
- filter,
397
- value,
398
- onChange
399
- }) {
400
- const range = value && typeof value === "object" ? value : {};
401
- const fromValue = typeof range.from === "string" ? range.from : "";
402
- const toValue = typeof range.to === "string" ? range.to : "";
403
- return /* @__PURE__ */ jsxs("fieldset", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
404
- /* @__PURE__ */ jsx("legend", { children: filter.label ?? filter.name }),
405
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
406
- /* @__PURE__ */ jsx(
407
- "input",
408
- {
409
- type: "date",
410
- "aria-label": `${filter.label ?? filter.name} from`,
411
- className: controlClasses(),
412
- value: fromValue,
413
- onChange: (e) => onChange({ from: e.target.value || null, to: range.to ?? null })
414
- }
415
- ),
416
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2013" }),
417
- /* @__PURE__ */ jsx(
418
- "input",
419
- {
420
- type: "date",
421
- "aria-label": `${filter.label ?? filter.name} to`,
422
- className: controlClasses(),
423
- value: toValue,
424
- onChange: (e) => onChange({ from: range.from ?? null, to: e.target.value || null })
425
- }
426
- )
427
- ] })
428
- ] });
429
- }
430
- var COLOR_TEXT = {
431
- primary: "text-primary",
432
- secondary: "text-secondary-foreground",
433
- success: "text-green-600",
434
- warning: "text-amber-600",
435
- danger: "text-red-600",
436
- info: "text-sky-600"
437
- };
438
- var COLOR_STROKE = {
439
- primary: "stroke-primary",
440
- secondary: "stroke-secondary-foreground",
441
- success: "stroke-green-500",
442
- warning: "stroke-amber-500",
443
- danger: "stroke-red-500",
444
- info: "stroke-sky-500"
445
- };
446
- function Sparkline({ points, color }) {
447
- if (points.length < 2) return null;
448
- const min = Math.min(...points);
449
- const max = Math.max(...points);
450
- const range = max - min || 1;
451
- const stepX = 100 / (points.length - 1);
452
- const coords = points.map((value, index) => {
453
- const x = index * stepX;
454
- const y = 30 - (value - min) / range * 30;
455
- return `${x.toFixed(2)},${y.toFixed(2)}`;
456
- }).join(" ");
457
- return /* @__PURE__ */ jsx(
458
- "svg",
459
- {
460
- viewBox: "0 0 100 30",
461
- preserveAspectRatio: "none",
462
- className: cn("mt-3 h-8 w-full", COLOR_STROKE[color]),
463
- role: "img",
464
- "aria-label": "Trend sparkline",
465
- "data-testid": "stat-card-sparkline",
466
- children: /* @__PURE__ */ jsx(
467
- "polyline",
468
- {
469
- fill: "none",
470
- stroke: "currentColor",
471
- strokeWidth: "1.5",
472
- strokeLinecap: "round",
473
- strokeLinejoin: "round",
474
- points: coords
475
- }
476
- )
477
- }
478
- );
479
- }
480
- function StatCard({ widget, className, columnSpan }) {
481
- const { heading, description, value, statDescription, descriptionIcon, color, chart, url } = widget;
482
- const hasChart = Array.isArray(chart) && chart.length >= 2;
483
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
484
- /* @__PURE__ */ jsx("div", { className: cn("text-4xl font-bold tabular-nums", COLOR_TEXT[color]), children: value ?? "\u2014" }),
485
- statDescription && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-1 text-xs text-muted-foreground", children: [
486
- descriptionIcon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-testid": "stat-card-icon", children: descriptionIcon }),
487
- /* @__PURE__ */ jsx("span", { children: statDescription })
488
- ] }),
489
- hasChart && /* @__PURE__ */ jsx(Sparkline, { points: chart, color })
490
- ] });
491
- const inner = url ? /* @__PURE__ */ jsx(
492
- "a",
493
- {
494
- href: url,
495
- className: "block rounded outline-none focus-visible:ring-2 focus-visible:ring-ring",
496
- "data-testid": "stat-card-link",
497
- children: body
498
- }
499
- ) : body;
500
- return /* @__PURE__ */ jsx(
501
- WidgetWrapper,
502
- {
503
- heading,
504
- description,
505
- columnSpan,
506
- className,
507
- children: inner
508
- }
509
- );
510
- }
511
- function TableCard({ widget, className }) {
512
- return /* @__PURE__ */ jsx(
513
- WidgetWrapper,
514
- {
515
- heading: widget.heading ?? void 0,
516
- description: widget.description ?? void 0,
517
- className,
518
- children: widget.loadError ? /* @__PURE__ */ jsx("div", { role: "alert", className: "text-sm text-red-600", children: widget.loadError }) : /* @__PURE__ */ jsxs(Fragment, { children: [
519
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: cn("w-full border-collapse text-sm", "text-foreground"), children: [
520
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border text-left", children: widget.columns.map((column) => /* @__PURE__ */ jsx(
521
- "th",
522
- {
523
- scope: "col",
524
- className: "px-2 py-1.5 font-medium text-muted-foreground",
525
- children: column.label ?? column.name
526
- },
527
- column.name
528
- )) }) }),
529
- /* @__PURE__ */ jsx("tbody", { children: widget.records.map((record, rowIndex) => /* @__PURE__ */ jsx(
530
- "tr",
531
- {
532
- className: "border-b border-border last:border-0",
533
- children: widget.columns.map((column) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5", children: formatCell(record[column.name]) }, column.name))
534
- },
535
- rowIndex
536
- )) })
537
- ] }) }),
538
- 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" }) })
539
- ] })
540
- }
541
- );
542
- }
543
- function formatCell(value) {
544
- if (value === null || value === void 0) return "";
545
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
546
- return String(value);
547
- }
548
- try {
549
- return JSON.stringify(value);
550
- } catch {
551
- return "";
552
- }
553
- }
554
- function WidgetRenderer({ widget, filterValues, fetcher }) {
555
- const [data, setData] = useState(widget.data ?? null);
556
- const filterRef = useRef(filterValues);
557
- filterRef.current = filterValues;
558
- const dashboardId = widget.dashboardId;
559
- const widgetId = widget.widgetId ?? widget.name;
560
- const poll = typeof widget.poll === "number" ? widget.poll : 0;
561
- const deferred = widget.deferred === true;
562
- const hasInlineDataRef = useRef(widget.data !== null && widget.data !== void 0);
563
- useEffect(() => {
564
- if (!dashboardId || !widgetId) return void 0;
565
- const url = `/admin/dashboards/${dashboardId}/widgets/${widgetId}/data`;
566
- const doFetch = async () => {
567
- try {
568
- const result = fetcher ? await fetcher(url) : await fetch(url, { headers: { Accept: "application/json" } }).then((r) => r.json());
569
- setData(result);
570
- } catch {
571
- }
572
- };
573
- let interval;
574
- if (deferred && !hasInlineDataRef.current) {
575
- void doFetch();
576
- }
577
- if (poll > 0) {
578
- interval = setInterval(() => {
579
- void doFetch();
580
- }, poll * 1e3);
581
- }
582
- return () => {
583
- if (interval) clearInterval(interval);
584
- };
585
- }, [dashboardId, widgetId, poll, deferred, fetcher]);
586
- const merged = mergeData(widget, data);
587
- switch (widget.type) {
588
- case "stat":
589
- return /* @__PURE__ */ jsx(StatCard, { widget: merged });
590
- case "chart":
591
- return /* @__PURE__ */ jsx(ChartCard, { widget: merged });
592
- case "table":
593
- return /* @__PURE__ */ jsx(TableCard, { widget: merged });
594
- default:
595
- return /* @__PURE__ */ jsxs("div", { role: "alert", className: "text-sm text-red-600", children: [
596
- "Widget type ",
597
- widget.type,
598
- " not registered"
599
- ] });
600
- }
601
- }
602
- function mergeData(widget, data) {
603
- if (data && typeof data === "object" && !Array.isArray(data)) {
604
- return { ...widget, ...data };
605
- }
606
- return { ...widget, data };
607
- }
608
- var BREAKPOINT_PREFIX = {
609
- sm: "sm:",
610
- md: "md:",
611
- lg: "lg:",
612
- xl: "xl:",
613
- "2xl": "2xl:"
614
- };
615
- function DashboardGrid({
616
- dashboard,
617
- filterValues,
618
- onFilterChange,
619
- className
620
- }) {
621
- const [internalValues, setInternalValues] = useState(filterValues ?? {});
622
- const values = filterValues ?? internalValues;
623
- const handleChange = (name, value) => {
624
- if (onFilterChange) {
625
- onFilterChange(name, value);
626
- } else {
627
- setInternalValues((prev) => ({ ...prev, [name]: value }));
628
- }
629
- };
630
- const filterPayload = normaliseFilters(dashboard.filters);
631
- const heading = dashboard.heading ?? dashboard.label;
632
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
633
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [
634
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
635
- heading && /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: heading }),
636
- dashboard.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: dashboard.description })
637
- ] }),
638
- filterPayload.length > 0 && /* @__PURE__ */ jsx(DashboardFilters, { filters: filterPayload, values, onChange: handleChange })
639
- ] }),
640
- /* @__PURE__ */ jsx(
641
- "div",
642
- {
643
- className: cn("grid gap-4", gridColsClass(dashboard.columns)),
644
- "data-testid": "dashboard-grid",
645
- children: dashboard.widgets.map((widget) => {
646
- const rawSpan = widget.columnSpan;
647
- const span = typeof rawSpan === "number" ? rawSpan : 1;
648
- const style = { gridColumn: `span ${span}` };
649
- return /* @__PURE__ */ jsx("div", { style, "data-widget-slot": widget.name, children: /* @__PURE__ */ jsx(WidgetRenderer, { widget, filterValues: values }) }, widget.name);
650
- })
651
- }
652
- )
653
- ] });
654
- }
655
- function gridColsClass(columns) {
656
- if (typeof columns === "number") {
657
- return `grid-cols-${Math.max(1, Math.min(12, columns))}`;
658
- }
659
- const parts = [];
660
- parts.push("grid-cols-1");
661
- for (const bp of ["sm", "md", "lg", "xl", "2xl"]) {
662
- const value = columns[bp];
663
- if (typeof value === "number") {
664
- parts.push(`${BREAKPOINT_PREFIX[bp]}grid-cols-${Math.max(1, Math.min(12, value))}`);
665
- }
666
- }
667
- return parts.join(" ");
668
- }
669
- function normaliseFilters(raw) {
670
- if (!raw) return [];
671
- if (Array.isArray(raw)) return raw;
672
- return [];
673
- }
674
-
675
- export { ChartCard, DashboardFilters, DashboardGrid, StatCard, TableCard, WidgetRenderer, WidgetWrapper };
1
+ export { ChartCard, DashboardFilters, DashboardGrid, TableCard, WidgetRenderer } from './chunk-47HD7ALI.js';
2
+ export { StatCard, WidgetWrapper } from './chunk-WKWV36PG.js';
3
+ import './chunk-B7ULUJVO.js';
676
4
  //# sourceMappingURL=widgets.js.map
677
5
  //# sourceMappingURL=widgets.js.map