@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.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/chunk-73J5ZD4C.js +96 -0
- package/dist/chunk-73J5ZD4C.js.map +1 -0
- package/dist/chunk-ATYZ45XL.js +19 -0
- package/dist/chunk-ATYZ45XL.js.map +1 -0
- package/dist/chunk-B7LLBNLV.js +23 -0
- package/dist/chunk-B7LLBNLV.js.map +1 -0
- package/dist/chunk-ZGDU5YLH.js +753 -0
- package/dist/chunk-ZGDU5YLH.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +765 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.d.ts +45 -0
- package/dist/client/index.js +141 -0
- package/dist/client/index.js.map +1 -0
- package/dist/define-page-B6y9TOfZ.d.ts +44 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/query/index.d.ts +11 -0
- package/dist/query/index.js +4 -0
- package/dist/query/index.js.map +1 -0
- package/dist/router/index.d.ts +36 -0
- package/dist/router/index.js +27 -0
- package/dist/router/index.js.map +1 -0
- package/dist/server/index.d.ts +53 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/node.d.ts +35 -0
- package/dist/server/node.js +84 -0
- package/dist/server/node.js.map +1 -0
- package/dist/ui/index.d.ts +217 -0
- package/dist/ui/index.js +616 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/vite/index.d.ts +15 -0
- package/dist/vite/index.js +5 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +108 -0
- package/registry/blocks/sales-overview.tsx +37 -0
- package/registry/ui/data-table.tsx +115 -0
- package/theme.css +101 -0
package/dist/ui/index.js
ADDED
|
@@ -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
|