@squadbase/vantage 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/dist/chunk-73J5ZD4C.js +96 -0
  4. package/dist/chunk-73J5ZD4C.js.map +1 -0
  5. package/dist/chunk-ATYZ45XL.js +19 -0
  6. package/dist/chunk-ATYZ45XL.js.map +1 -0
  7. package/dist/chunk-B7LLBNLV.js +23 -0
  8. package/dist/chunk-B7LLBNLV.js.map +1 -0
  9. package/dist/chunk-ZGDU5YLH.js +753 -0
  10. package/dist/chunk-ZGDU5YLH.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +765 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/client/index.d.ts +45 -0
  15. package/dist/client/index.js +141 -0
  16. package/dist/client/index.js.map +1 -0
  17. package/dist/define-page-B6y9TOfZ.d.ts +44 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.js +13 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/query/index.d.ts +11 -0
  22. package/dist/query/index.js +4 -0
  23. package/dist/query/index.js.map +1 -0
  24. package/dist/router/index.d.ts +36 -0
  25. package/dist/router/index.js +27 -0
  26. package/dist/router/index.js.map +1 -0
  27. package/dist/server/index.d.ts +53 -0
  28. package/dist/server/index.js +3 -0
  29. package/dist/server/index.js.map +1 -0
  30. package/dist/server/node.d.ts +35 -0
  31. package/dist/server/node.js +84 -0
  32. package/dist/server/node.js.map +1 -0
  33. package/dist/ui/index.d.ts +217 -0
  34. package/dist/ui/index.js +616 -0
  35. package/dist/ui/index.js.map +1 -0
  36. package/dist/vite/index.d.ts +15 -0
  37. package/dist/vite/index.js +5 -0
  38. package/dist/vite/index.js.map +1 -0
  39. package/package.json +108 -0
  40. package/registry/blocks/sales-overview.tsx +37 -0
  41. package/registry/ui/data-table.tsx +115 -0
  42. package/theme.css +101 -0
@@ -0,0 +1,616 @@
1
+ import { forwardRef, createContext, useState, useContext, useMemo, useEffect, useRef } from 'react';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+
4
+ // @squadbase/vantage — generated build. Do not edit.
5
+
6
+ // src/ui/cn.ts
7
+ function cn(...classes) {
8
+ return classes.filter(Boolean).join(" ");
9
+ }
10
+ var VARIANTS = {
11
+ default: "bg-primary text-primary-foreground hover:opacity-90",
12
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
13
+ outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground",
14
+ ghost: "hover:bg-accent hover:text-accent-foreground",
15
+ destructive: "bg-destructive text-destructive-foreground hover:opacity-90"
16
+ };
17
+ var SIZES = {
18
+ sm: "h-8 px-3 text-xs",
19
+ default: "h-9 px-4 text-sm",
20
+ lg: "h-10 px-6 text-sm",
21
+ icon: "h-9 w-9"
22
+ };
23
+ var Button = forwardRef(
24
+ ({ className, variant = "default", size = "default", type = "button", ...props }, ref) => /* @__PURE__ */ jsx(
25
+ "button",
26
+ {
27
+ ref,
28
+ type,
29
+ className: cn(
30
+ "inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors",
31
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
32
+ "disabled:pointer-events-none disabled:opacity-50",
33
+ VARIANTS[variant],
34
+ SIZES[size],
35
+ className
36
+ ),
37
+ ...props
38
+ }
39
+ )
40
+ );
41
+ Button.displayName = "Button";
42
+ var FIELD = "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
43
+ var Input = forwardRef(
44
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("input", { ref, className: cn(FIELD, className), ...props })
45
+ );
46
+ Input.displayName = "Input";
47
+ var Select = forwardRef(
48
+ ({ className, options, children, ...props }, ref) => /* @__PURE__ */ jsx("select", { ref, className: cn(FIELD, "pr-8", className), ...props, children: options ? options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value)) : children })
49
+ );
50
+ Select.displayName = "Select";
51
+ var Checkbox = forwardRef(
52
+ ({ className, label, id, ...props }, ref) => /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm", children: [
53
+ /* @__PURE__ */ jsx(
54
+ "input",
55
+ {
56
+ ref,
57
+ id,
58
+ type: "checkbox",
59
+ className: cn("h-4 w-4 rounded border-input text-primary focus-visible:ring-2 focus-visible:ring-ring", className),
60
+ ...props
61
+ }
62
+ ),
63
+ label ? /* @__PURE__ */ jsx("span", { children: label }) : null
64
+ ] })
65
+ );
66
+ Checkbox.displayName = "Checkbox";
67
+ function Card({ className, ...props }) {
68
+ return /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: cn("rounded-xl border border-border bg-card text-card-foreground shadow-sm", className),
72
+ ...props
73
+ }
74
+ );
75
+ }
76
+ function CardHeader({ className, ...props }) {
77
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-1.5 p-6", className), ...props });
78
+ }
79
+ function CardTitle({ className, ...props }) {
80
+ return /* @__PURE__ */ jsx("h3", { className: cn("text-base font-semibold leading-none tracking-tight", className), ...props });
81
+ }
82
+ function CardDescription({ className, ...props }) {
83
+ return /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground", className), ...props });
84
+ }
85
+ function CardContent({ className, ...props }) {
86
+ return /* @__PURE__ */ jsx("div", { className: cn("p-6 pt-0", className), ...props });
87
+ }
88
+ function CardFooter({ className, ...props }) {
89
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center p-6 pt-0", className), ...props });
90
+ }
91
+ var Ctx = createContext(null);
92
+ function Tabs({ defaultValue, value, onValueChange, className, children }) {
93
+ const [internal, setInternal] = useState(defaultValue);
94
+ const current = value ?? internal;
95
+ const setValue = (v) => {
96
+ if (value === void 0) setInternal(v);
97
+ onValueChange?.(v);
98
+ };
99
+ return /* @__PURE__ */ jsx(Ctx.Provider, { value: { value: current, setValue }, children: /* @__PURE__ */ jsx("div", { className, children }) });
100
+ }
101
+ function TabsList({ className, children }) {
102
+ return /* @__PURE__ */ jsx("div", { className: cn("inline-flex h-9 items-center gap-1 rounded-lg bg-muted p-1 text-muted-foreground", className), children });
103
+ }
104
+ function TabsTrigger({ value, children }) {
105
+ const ctx = useContext(Ctx);
106
+ if (!ctx) throw new Error("TabsTrigger must be used within Tabs");
107
+ const active = ctx.value === value;
108
+ return /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ onClick: () => ctx.setValue(value),
113
+ className: cn(
114
+ "inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium transition-all",
115
+ active ? "bg-background text-foreground shadow-sm" : "hover:text-foreground"
116
+ ),
117
+ children
118
+ }
119
+ );
120
+ }
121
+ function TabsContent({ value, className, children }) {
122
+ const ctx = useContext(Ctx);
123
+ if (!ctx) throw new Error("TabsContent must be used within Tabs");
124
+ if (ctx.value !== value) return null;
125
+ return /* @__PURE__ */ jsx("div", { className: cn("mt-4", className), children });
126
+ }
127
+ function Table({ className, ...props }) {
128
+ return /* @__PURE__ */ jsx("div", { className: "w-full overflow-x-auto", children: /* @__PURE__ */ jsx("table", { className: cn("w-full caption-bottom text-sm", className), ...props }) });
129
+ }
130
+ function TableHeader(props) {
131
+ return /* @__PURE__ */ jsx("thead", { className: "[&_tr]:border-b border-border", ...props });
132
+ }
133
+ function TableBody(props) {
134
+ return /* @__PURE__ */ jsx("tbody", { className: "[&_tr:last-child]:border-0", ...props });
135
+ }
136
+ function TableRow({ className, ...props }) {
137
+ return /* @__PURE__ */ jsx("tr", { className: cn("border-b border-border transition-colors hover:bg-muted/50", className), ...props });
138
+ }
139
+ function TableHead({ className, ...props }) {
140
+ return /* @__PURE__ */ jsx(
141
+ "th",
142
+ {
143
+ className: cn("h-10 px-3 text-left align-middle text-xs font-medium text-muted-foreground", className),
144
+ ...props
145
+ }
146
+ );
147
+ }
148
+ function TableCell({ className, ...props }) {
149
+ return /* @__PURE__ */ jsx("td", { className: cn("px-3 py-2.5 align-middle", className), ...props });
150
+ }
151
+ function DataTable({
152
+ columns,
153
+ data,
154
+ pageSize = 10,
155
+ getRowId,
156
+ empty,
157
+ className
158
+ }) {
159
+ const [sort, setSort] = useState(null);
160
+ const [page, setPage] = useState(0);
161
+ const valueOf = (row, col) => {
162
+ if (col.accessor) return col.accessor(row);
163
+ const v = row[col.key];
164
+ return typeof v === "number" ? v : String(v ?? "");
165
+ };
166
+ const sorted = useMemo(() => {
167
+ if (!sort) return data;
168
+ const col = columns.find((c) => c.key === sort.key);
169
+ if (!col) return data;
170
+ const copy = [...data];
171
+ const rank = (v) => typeof v === "number" ? 0 : 1;
172
+ copy.sort((a, b) => {
173
+ const av = valueOf(a, col);
174
+ const bv = valueOf(b, col);
175
+ let cmp = rank(av) - rank(bv);
176
+ if (cmp === 0) {
177
+ cmp = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv));
178
+ }
179
+ return sort.dir === "asc" ? cmp : -cmp;
180
+ });
181
+ return copy;
182
+ }, [data, sort, columns]);
183
+ const pageCount = Math.max(1, Math.ceil(sorted.length / pageSize));
184
+ const clampedPage = Math.min(page, pageCount - 1);
185
+ const rows = sorted.slice(clampedPage * pageSize, clampedPage * pageSize + pageSize);
186
+ const toggleSort = (key) => {
187
+ setPage(0);
188
+ setSort(
189
+ (prev) => prev?.key === key ? prev.dir === "asc" ? { key, dir: "desc" } : null : { key, dir: "asc" }
190
+ );
191
+ };
192
+ const alignClass = (a) => a === "right" ? "text-right" : a === "center" ? "text-center" : "text-left";
193
+ if (data.length === 0 && empty) {
194
+ return /* @__PURE__ */ jsx("div", { className: cn("rounded-xl border border-border bg-card", className), children: empty });
195
+ }
196
+ return /* @__PURE__ */ jsxs("div", { className: cn("rounded-xl border border-border bg-card", className), children: [
197
+ /* @__PURE__ */ jsxs(Table, { children: [
198
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(TableHead, { className: alignClass(col.align), children: col.sortable ? /* @__PURE__ */ jsxs(
199
+ "button",
200
+ {
201
+ type: "button",
202
+ onClick: () => toggleSort(col.key),
203
+ className: "inline-flex items-center gap-1 hover:text-foreground",
204
+ children: [
205
+ col.header,
206
+ /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: sort?.key === col.key ? sort.dir === "asc" ? "\u25B2" : "\u25BC" : "\u2195" })
207
+ ]
208
+ }
209
+ ) : col.header }, col.key)) }) }),
210
+ /* @__PURE__ */ jsx(TableBody, { children: rows.map((row, i) => /* @__PURE__ */ jsx(TableRow, { children: columns.map((col) => /* @__PURE__ */ jsx(TableCell, { className: alignClass(col.align), children: col.render ? col.render(row) : String(row[col.key] ?? "") }, col.key)) }, getRowId ? getRowId(row, i) : i)) })
211
+ ] }),
212
+ pageCount > 1 ? /* @__PURE__ */ jsx(
213
+ Pagination,
214
+ {
215
+ page: clampedPage,
216
+ pageCount,
217
+ total: sorted.length,
218
+ onPageChange: setPage
219
+ }
220
+ ) : null
221
+ ] });
222
+ }
223
+ function Pagination({ page, pageCount, total, onPageChange }) {
224
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm", children: [
225
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
226
+ total != null ? `${total} rows \xB7 ` : "",
227
+ "Page ",
228
+ page + 1,
229
+ " of ",
230
+ pageCount
231
+ ] }),
232
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
233
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", disabled: page === 0, onClick: () => onPageChange(page - 1), children: "Previous" }),
234
+ /* @__PURE__ */ jsx(
235
+ Button,
236
+ {
237
+ size: "sm",
238
+ variant: "outline",
239
+ disabled: page >= pageCount - 1,
240
+ onClick: () => onPageChange(page + 1),
241
+ children: "Next"
242
+ }
243
+ )
244
+ ] })
245
+ ] });
246
+ }
247
+ function MetricCard({ label, value, delta, deltaLabel, icon, className }) {
248
+ const positive = (delta ?? 0) >= 0;
249
+ return /* @__PURE__ */ jsxs(Card, { className: cn("p-5", className), children: [
250
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
251
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: label }),
252
+ icon ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: icon }) : null
253
+ ] }),
254
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-3xl font-semibold tracking-tight", children: value }),
255
+ delta != null ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs", children: [
256
+ /* @__PURE__ */ jsxs("span", { className: cn("font-medium", positive ? "text-[var(--chart-2)]" : "text-destructive"), children: [
257
+ positive ? "\u25B2" : "\u25BC",
258
+ " ",
259
+ Math.abs(delta),
260
+ "%"
261
+ ] }),
262
+ " ",
263
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: deltaLabel ?? "vs. previous" })
264
+ ] }) : null
265
+ ] });
266
+ }
267
+ function FilterBar({ className, children, ...props }) {
268
+ return /* @__PURE__ */ jsx(
269
+ "div",
270
+ {
271
+ className: cn(
272
+ "flex flex-wrap items-end gap-3 rounded-xl border border-border bg-card p-4",
273
+ className
274
+ ),
275
+ ...props,
276
+ children
277
+ }
278
+ );
279
+ }
280
+ function FilterField({ label, children }) {
281
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
282
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label }),
283
+ children
284
+ ] });
285
+ }
286
+ function DashboardShell({ title, description, actions, nav, children, className }) {
287
+ return /* @__PURE__ */ jsxs("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
288
+ /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 border-b border-border bg-background/80 backdrop-blur", children: [
289
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-3 px-6 py-4", children: [
290
+ /* @__PURE__ */ jsxs("div", { children: [
291
+ title ? /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold tracking-tight", children: title }) : null,
292
+ description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
293
+ ] }),
294
+ actions ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: actions }) : null
295
+ ] }),
296
+ nav ? /* @__PURE__ */ jsx("nav", { className: "mx-auto max-w-7xl px-6 pb-2", children: nav }) : null
297
+ ] }),
298
+ /* @__PURE__ */ jsx("main", { className: "mx-auto max-w-7xl px-6 py-6", children })
299
+ ] });
300
+ }
301
+ function Loading({ label = "Loading\u2026", className }) {
302
+ return /* @__PURE__ */ jsx("div", { className: cn("flex min-h-[30vh] items-center justify-center", className), role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground", children: [
303
+ /* @__PURE__ */ jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }),
304
+ label
305
+ ] }) });
306
+ }
307
+ function Empty({
308
+ title = "Nothing here yet",
309
+ description,
310
+ icon,
311
+ action,
312
+ className
313
+ }) {
314
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center justify-center gap-2 p-10 text-center", className), children: [
315
+ icon ? /* @__PURE__ */ jsx("div", { className: "text-3xl text-muted-foreground", children: icon }) : null,
316
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: title }),
317
+ description ? /* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
318
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-2", children: action }) : null
319
+ ] });
320
+ }
321
+ function ErrorState({
322
+ title = "Something went wrong",
323
+ message,
324
+ action,
325
+ className
326
+ }) {
327
+ return /* @__PURE__ */ jsxs(
328
+ "div",
329
+ {
330
+ className: cn("rounded-xl border border-destructive/30 bg-destructive/5 p-6", className),
331
+ role: "alert",
332
+ children: [
333
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-destructive", children: title }),
334
+ message ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: message }) : null,
335
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-3", children: action }) : null
336
+ ]
337
+ }
338
+ );
339
+ }
340
+ var FIELD2 = "h-9 rounded-md border border-input bg-background px-3 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
341
+ function DatePicker({ value, onChange, min, max, className, id }) {
342
+ return /* @__PURE__ */ jsx(
343
+ "input",
344
+ {
345
+ id,
346
+ type: "date",
347
+ value: value ?? "",
348
+ min,
349
+ max,
350
+ onChange: (e) => onChange?.(e.target.value),
351
+ className: cn(FIELD2, className)
352
+ }
353
+ );
354
+ }
355
+ function DateRangePicker({ value, onChange, className }) {
356
+ const from = value?.from ?? "";
357
+ const to = value?.to ?? "";
358
+ return /* @__PURE__ */ jsxs("div", { className: cn("inline-flex items-center gap-2", className), children: [
359
+ /* @__PURE__ */ jsx(
360
+ "input",
361
+ {
362
+ type: "date",
363
+ value: from,
364
+ max: to || void 0,
365
+ onChange: (e) => onChange?.({ from: e.target.value, to }),
366
+ className: FIELD2,
367
+ "aria-label": "From date"
368
+ }
369
+ ),
370
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
371
+ /* @__PURE__ */ jsx(
372
+ "input",
373
+ {
374
+ type: "date",
375
+ value: to,
376
+ min: from || void 0,
377
+ onChange: (e) => onChange?.({ from, to: e.target.value }),
378
+ className: FIELD2,
379
+ "aria-label": "To date"
380
+ }
381
+ )
382
+ ] });
383
+ }
384
+ var CHART_COLORS = [
385
+ "var(--chart-1)",
386
+ "var(--chart-2)",
387
+ "var(--chart-3)",
388
+ "var(--chart-4)",
389
+ "var(--chart-5)"
390
+ ];
391
+ function ChartContainer({
392
+ children,
393
+ className,
394
+ height = 260
395
+ }) {
396
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full", className), style: { height }, children });
397
+ }
398
+ function ChartLegend({ series }) {
399
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-4 pt-3 text-xs text-muted-foreground", children: series.map((s, i) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
400
+ /* @__PURE__ */ jsx(
401
+ "span",
402
+ {
403
+ className: "h-2.5 w-2.5 rounded-[3px]",
404
+ style: { backgroundColor: s.color ?? CHART_COLORS[i % CHART_COLORS.length] }
405
+ }
406
+ ),
407
+ s.label ?? s.key
408
+ ] }, s.key)) });
409
+ }
410
+ function ChartTooltip({ children, className }) {
411
+ return /* @__PURE__ */ jsx(
412
+ "div",
413
+ {
414
+ className: cn(
415
+ "pointer-events-none rounded-md border border-border bg-popover px-2.5 py-1.5 text-xs shadow-md",
416
+ className
417
+ ),
418
+ children
419
+ }
420
+ );
421
+ }
422
+ var PAD = { top: 8, right: 8, bottom: 22, left: 32 };
423
+ function useGeometry(data, series) {
424
+ let max = 0;
425
+ for (const row of data) {
426
+ for (const s of series) {
427
+ const v = Number(row[s.key] ?? 0);
428
+ if (v > max) max = v;
429
+ }
430
+ }
431
+ return { max: max === 0 ? 1 : max };
432
+ }
433
+ function BarChart({
434
+ data,
435
+ categoryKey,
436
+ series,
437
+ height = 260,
438
+ className
439
+ }) {
440
+ const width = 640;
441
+ const { max } = useGeometry(data, series);
442
+ const chartW = width - PAD.left - PAD.right;
443
+ const chartH = height - PAD.top - PAD.bottom;
444
+ const groupW = chartW / Math.max(1, data.length);
445
+ const barW = Math.max(2, groupW * 0.7 / series.length);
446
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
447
+ /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "w-full", style: { height }, role: "img", children: [
448
+ [0, 0.25, 0.5, 0.75, 1].map((t) => {
449
+ const y = PAD.top + chartH * (1 - t);
450
+ return /* @__PURE__ */ jsxs("g", { children: [
451
+ /* @__PURE__ */ jsx("line", { x1: PAD.left, x2: width - PAD.right, y1: y, y2: y, stroke: "var(--border)", strokeWidth: 1 }),
452
+ /* @__PURE__ */ jsx("text", { x: PAD.left - 6, y: y + 3, textAnchor: "end", fontSize: 9, fill: "var(--muted-foreground)", children: Math.round(max * t) })
453
+ ] }, t);
454
+ }),
455
+ data.map((row, i) => {
456
+ const gx = PAD.left + groupW * i + (groupW - barW * series.length) / 2;
457
+ return /* @__PURE__ */ jsxs("g", { children: [
458
+ series.map((s, si) => {
459
+ const v = Number(row[s.key] ?? 0);
460
+ const h = v / max * chartH;
461
+ return /* @__PURE__ */ jsx(
462
+ "rect",
463
+ {
464
+ x: gx + si * barW,
465
+ y: PAD.top + chartH - h,
466
+ width: barW - 1,
467
+ height: Math.max(0, h),
468
+ rx: 2,
469
+ fill: s.color ?? CHART_COLORS[si % CHART_COLORS.length],
470
+ children: /* @__PURE__ */ jsx("title", { children: `${String(row[categoryKey])} \xB7 ${s.label ?? s.key}: ${v}` })
471
+ },
472
+ s.key
473
+ );
474
+ }),
475
+ /* @__PURE__ */ jsx(
476
+ "text",
477
+ {
478
+ x: PAD.left + groupW * i + groupW / 2,
479
+ y: height - 6,
480
+ textAnchor: "middle",
481
+ fontSize: 9,
482
+ fill: "var(--muted-foreground)",
483
+ children: String(row[categoryKey])
484
+ }
485
+ )
486
+ ] }, i);
487
+ })
488
+ ] }),
489
+ /* @__PURE__ */ jsx(ChartLegend, { series })
490
+ ] });
491
+ }
492
+ function LineChart({
493
+ data,
494
+ categoryKey,
495
+ series,
496
+ height = 260,
497
+ className
498
+ }) {
499
+ const width = 640;
500
+ const { max } = useGeometry(data, series);
501
+ const chartW = width - PAD.left - PAD.right;
502
+ const chartH = height - PAD.top - PAD.bottom;
503
+ const stepX = chartW / Math.max(1, data.length - 1);
504
+ const pointFor = (v, i) => ({
505
+ x: PAD.left + stepX * i,
506
+ y: PAD.top + chartH - v / max * chartH
507
+ });
508
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
509
+ /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "w-full", style: { height }, role: "img", children: [
510
+ [0, 0.5, 1].map((t) => {
511
+ const y = PAD.top + chartH * (1 - t);
512
+ return /* @__PURE__ */ jsx("line", { x1: PAD.left, x2: width - PAD.right, y1: y, y2: y, stroke: "var(--border)", strokeWidth: 1 }, t);
513
+ }),
514
+ series.map((s, si) => {
515
+ const pts = data.map((row, i) => pointFor(Number(row[s.key] ?? 0), i));
516
+ const d = pts.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
517
+ const color = s.color ?? CHART_COLORS[si % CHART_COLORS.length];
518
+ return /* @__PURE__ */ jsxs("g", { children: [
519
+ /* @__PURE__ */ jsx("path", { d, fill: "none", stroke: color, strokeWidth: 2 }),
520
+ pts.map((p, i) => /* @__PURE__ */ jsx("circle", { cx: p.x, cy: p.y, r: 2.5, fill: color }, i))
521
+ ] }, s.key);
522
+ }),
523
+ data.map((row, i) => /* @__PURE__ */ jsx(
524
+ "text",
525
+ {
526
+ x: PAD.left + stepX * i,
527
+ y: height - 6,
528
+ textAnchor: "middle",
529
+ fontSize: 9,
530
+ fill: "var(--muted-foreground)",
531
+ children: String(row[categoryKey])
532
+ },
533
+ i
534
+ ))
535
+ ] }),
536
+ /* @__PURE__ */ jsx(ChartLegend, { series })
537
+ ] });
538
+ }
539
+ function Dialog({ open, onClose, title, description, children, footer, className }) {
540
+ useEffect(() => {
541
+ if (!open) return;
542
+ const onKey = (e) => {
543
+ if (e.key === "Escape") onClose();
544
+ };
545
+ document.addEventListener("keydown", onKey);
546
+ return () => document.removeEventListener("keydown", onKey);
547
+ }, [open, onClose]);
548
+ if (!open) return null;
549
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
550
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose, "aria-hidden": true }),
551
+ /* @__PURE__ */ jsxs(
552
+ "div",
553
+ {
554
+ role: "dialog",
555
+ "aria-modal": "true",
556
+ className: cn(
557
+ "relative z-10 w-full max-w-lg rounded-xl border border-border bg-card p-6 shadow-lg",
558
+ className
559
+ ),
560
+ children: [
561
+ title ? /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: title }) : null,
562
+ description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
563
+ children ? /* @__PURE__ */ jsx("div", { className: "mt-4", children }) : null,
564
+ footer ? /* @__PURE__ */ jsx("div", { className: "mt-6 flex justify-end gap-2", children: footer }) : null
565
+ ]
566
+ }
567
+ )
568
+ ] });
569
+ }
570
+ function DropdownMenu({ trigger, items, align = "start", className }) {
571
+ const [open, setOpen] = useState(false);
572
+ const ref = useRef(null);
573
+ useEffect(() => {
574
+ if (!open) return;
575
+ const onDoc = (e) => {
576
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
577
+ };
578
+ document.addEventListener("mousedown", onDoc);
579
+ return () => document.removeEventListener("mousedown", onDoc);
580
+ }, [open]);
581
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("relative inline-block", className), children: [
582
+ /* @__PURE__ */ jsx("span", { onClick: () => setOpen((v) => !v), children: trigger }),
583
+ open ? /* @__PURE__ */ jsx(
584
+ "div",
585
+ {
586
+ role: "menu",
587
+ className: cn(
588
+ "absolute z-50 mt-1 min-w-[10rem] rounded-md border border-border bg-popover p-1 shadow-md",
589
+ align === "end" ? "right-0" : "left-0"
590
+ ),
591
+ children: items.map((item, i) => /* @__PURE__ */ jsx(
592
+ "button",
593
+ {
594
+ type: "button",
595
+ role: "menuitem",
596
+ disabled: item.disabled,
597
+ onClick: () => {
598
+ item.onSelect?.();
599
+ setOpen(false);
600
+ },
601
+ className: cn(
602
+ "flex w-full items-center rounded-sm px-2 py-1.5 text-left text-sm",
603
+ "hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50"
604
+ ),
605
+ children: item.label
606
+ },
607
+ i
608
+ ))
609
+ }
610
+ ) : null
611
+ ] });
612
+ }
613
+
614
+ export { BarChart, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChartContainer, ChartLegend, ChartTooltip, Checkbox, DashboardShell, DataTable, DatePicker, DateRangePicker, Dialog, DropdownMenu, Empty, ErrorState, FilterBar, FilterField, Input, LineChart, Loading, MetricCard, Pagination, Select, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, cn };
615
+ //# sourceMappingURL=index.js.map
616
+ //# sourceMappingURL=index.js.map