lmbchp 0.1.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.
- package/dist/index.js +2106 -0
- package/dist/index.js.map +1 -0
- package/dist/tailwind-preset.js +123 -0
- package/dist/tailwind-preset.js.map +1 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/lib/cn.ts
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { twMerge } from "tailwind-merge";
|
|
6
|
+
function cn(...inputs) {
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/lib/use-chart-colors.ts
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
var DARK_FALLBACK = {
|
|
13
|
+
grid: "#262626",
|
|
14
|
+
tick: "#787878",
|
|
15
|
+
tooltipBg: "#141414",
|
|
16
|
+
tooltipBorder: "#262626",
|
|
17
|
+
tooltipText: "#e6ddd0",
|
|
18
|
+
cursor: "#3a3a3a",
|
|
19
|
+
activeDot: "#e6ddd0",
|
|
20
|
+
barCursor: "rgba(255, 255, 255, 0.04)",
|
|
21
|
+
accent: "#fcd34d",
|
|
22
|
+
series: [
|
|
23
|
+
"#fcd34d",
|
|
24
|
+
"#d4574b",
|
|
25
|
+
"#5b9a8b",
|
|
26
|
+
"#9b7bd4",
|
|
27
|
+
"#e5903b",
|
|
28
|
+
"#4db8a4",
|
|
29
|
+
"#d47b9b",
|
|
30
|
+
"#7bc4d4",
|
|
31
|
+
"#e6ddd0"
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
function readVar(el, name) {
|
|
35
|
+
return getComputedStyle(el).getPropertyValue(name).trim();
|
|
36
|
+
}
|
|
37
|
+
function useChartColors() {
|
|
38
|
+
const ref = React.useRef(DARK_FALLBACK);
|
|
39
|
+
const [, rerender] = React.useState(0);
|
|
40
|
+
const observerRef = React.useRef(null);
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
function read() {
|
|
43
|
+
const el = document.documentElement;
|
|
44
|
+
const grid = readVar(el, "--chart-grid");
|
|
45
|
+
if (!grid) return;
|
|
46
|
+
ref.current = {
|
|
47
|
+
grid,
|
|
48
|
+
tick: readVar(el, "--chart-tick"),
|
|
49
|
+
tooltipBg: readVar(el, "--chart-tooltip-bg"),
|
|
50
|
+
tooltipBorder: readVar(el, "--chart-tooltip-border"),
|
|
51
|
+
tooltipText: readVar(el, "--chart-tooltip-text"),
|
|
52
|
+
cursor: readVar(el, "--chart-cursor"),
|
|
53
|
+
activeDot: readVar(el, "--chart-active-dot"),
|
|
54
|
+
barCursor: readVar(el, "--chart-bar-cursor"),
|
|
55
|
+
accent: readVar(el, "--accent"),
|
|
56
|
+
series: Array.from(
|
|
57
|
+
{ length: 9 },
|
|
58
|
+
(_, i) => readVar(el, `--chart-${i + 1}`)
|
|
59
|
+
)
|
|
60
|
+
};
|
|
61
|
+
rerender((n) => n + 1);
|
|
62
|
+
}
|
|
63
|
+
read();
|
|
64
|
+
observerRef.current = new MutationObserver(read);
|
|
65
|
+
observerRef.current.observe(document.documentElement, {
|
|
66
|
+
attributes: true,
|
|
67
|
+
attributeFilter: ["class"]
|
|
68
|
+
});
|
|
69
|
+
return () => observerRef.current?.disconnect();
|
|
70
|
+
}, []);
|
|
71
|
+
return ref.current;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/components/sparkline.tsx
|
|
75
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
76
|
+
function Sparkline({
|
|
77
|
+
data,
|
|
78
|
+
width = 80,
|
|
79
|
+
height = 24,
|
|
80
|
+
color,
|
|
81
|
+
fillOpacity = 0.1,
|
|
82
|
+
className
|
|
83
|
+
}) {
|
|
84
|
+
const chartColors = useChartColors();
|
|
85
|
+
const resolvedColor = color ?? chartColors.accent;
|
|
86
|
+
if (data.length === 0) {
|
|
87
|
+
return /* @__PURE__ */ jsx(
|
|
88
|
+
"svg",
|
|
89
|
+
{
|
|
90
|
+
width,
|
|
91
|
+
height,
|
|
92
|
+
className: cn("inline-block", className)
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const min = Math.min(...data);
|
|
97
|
+
const max = Math.max(...data);
|
|
98
|
+
const range = max - min || 1;
|
|
99
|
+
const padding = 1;
|
|
100
|
+
const viewWidth = width;
|
|
101
|
+
const viewHeight = height;
|
|
102
|
+
const plotWidth = viewWidth - padding * 2;
|
|
103
|
+
const plotHeight = viewHeight - padding * 2;
|
|
104
|
+
const points = data.map((value, index) => {
|
|
105
|
+
const x = padding + index / Math.max(data.length - 1, 1) * plotWidth;
|
|
106
|
+
const y = padding + plotHeight - (value - min) / range * plotHeight;
|
|
107
|
+
return { x, y };
|
|
108
|
+
});
|
|
109
|
+
const linePoints = points.map((p) => `${p.x},${p.y}`).join(" ");
|
|
110
|
+
const fillPoints = [
|
|
111
|
+
`${points[0].x},${padding + plotHeight}`,
|
|
112
|
+
...points.map((p) => `${p.x},${p.y}`),
|
|
113
|
+
`${points[points.length - 1].x},${padding + plotHeight}`
|
|
114
|
+
].join(" ");
|
|
115
|
+
return /* @__PURE__ */ jsxs(
|
|
116
|
+
"svg",
|
|
117
|
+
{
|
|
118
|
+
width,
|
|
119
|
+
height,
|
|
120
|
+
viewBox: `0 0 ${viewWidth} ${viewHeight}`,
|
|
121
|
+
className: cn("inline-block", className),
|
|
122
|
+
role: "img",
|
|
123
|
+
"aria-label": "Sparkline chart",
|
|
124
|
+
children: [
|
|
125
|
+
/* @__PURE__ */ jsx(
|
|
126
|
+
"polygon",
|
|
127
|
+
{
|
|
128
|
+
points: fillPoints,
|
|
129
|
+
fill: resolvedColor,
|
|
130
|
+
opacity: fillOpacity
|
|
131
|
+
}
|
|
132
|
+
),
|
|
133
|
+
/* @__PURE__ */ jsx(
|
|
134
|
+
"polyline",
|
|
135
|
+
{
|
|
136
|
+
points: linePoints,
|
|
137
|
+
fill: "none",
|
|
138
|
+
stroke: resolvedColor,
|
|
139
|
+
strokeWidth: 1.5,
|
|
140
|
+
strokeLinecap: "round",
|
|
141
|
+
strokeLinejoin: "round"
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/components/kpi-card.tsx
|
|
150
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
151
|
+
function KpiCard({
|
|
152
|
+
label,
|
|
153
|
+
value,
|
|
154
|
+
trend,
|
|
155
|
+
sparklineData,
|
|
156
|
+
icon,
|
|
157
|
+
className
|
|
158
|
+
}) {
|
|
159
|
+
const colors = useChartColors();
|
|
160
|
+
return /* @__PURE__ */ jsxs2(
|
|
161
|
+
"div",
|
|
162
|
+
{
|
|
163
|
+
className: cn(
|
|
164
|
+
"rounded-card border border-surface-border bg-surface p-card-p shadow-card",
|
|
165
|
+
className
|
|
166
|
+
),
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
|
|
169
|
+
icon && /* @__PURE__ */ jsx2("span", { className: "flex-shrink-0 text-text-tertiary", children: icon }),
|
|
170
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-text-secondary", children: label })
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-2 flex items-end justify-between gap-4", children: [
|
|
173
|
+
/* @__PURE__ */ jsx2("div", { className: "text-3xl font-semibold text-text", children: value }),
|
|
174
|
+
sparklineData && sparklineData.length > 0 && /* @__PURE__ */ jsx2(
|
|
175
|
+
Sparkline,
|
|
176
|
+
{
|
|
177
|
+
data: sparklineData,
|
|
178
|
+
width: 80,
|
|
179
|
+
height: 28,
|
|
180
|
+
color: trend ? trend.isPositive ? colors.series[2] : colors.series[1] : void 0
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
] }),
|
|
184
|
+
trend && /* @__PURE__ */ jsx2("div", { className: "mt-1 flex items-center gap-1", children: /* @__PURE__ */ jsxs2(
|
|
185
|
+
"span",
|
|
186
|
+
{
|
|
187
|
+
className: cn(
|
|
188
|
+
"text-sm font-medium",
|
|
189
|
+
trend.isPositive ? "text-revenue" : "text-loss"
|
|
190
|
+
),
|
|
191
|
+
"aria-label": `${trend.isPositive ? "Up" : "Down"} ${Math.abs(trend.value).toFixed(1)}%`,
|
|
192
|
+
children: [
|
|
193
|
+
trend.isPositive ? "\u2191" : "\u2193",
|
|
194
|
+
" ",
|
|
195
|
+
Math.abs(trend.value).toFixed(1),
|
|
196
|
+
"%"
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
) })
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/components/data-table.tsx
|
|
206
|
+
import * as React2 from "react";
|
|
207
|
+
import {
|
|
208
|
+
useReactTable,
|
|
209
|
+
getCoreRowModel,
|
|
210
|
+
getSortedRowModel,
|
|
211
|
+
getPaginationRowModel,
|
|
212
|
+
getFilteredRowModel,
|
|
213
|
+
flexRender
|
|
214
|
+
} from "@tanstack/react-table";
|
|
215
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
216
|
+
function DataTable({
|
|
217
|
+
columns,
|
|
218
|
+
data,
|
|
219
|
+
pageSize = 20,
|
|
220
|
+
searchable = false,
|
|
221
|
+
searchPlaceholder = "Search...",
|
|
222
|
+
className
|
|
223
|
+
}) {
|
|
224
|
+
const [sorting, setSorting] = React2.useState([]);
|
|
225
|
+
const [globalFilter, setGlobalFilter] = React2.useState("");
|
|
226
|
+
const table = useReactTable({
|
|
227
|
+
data,
|
|
228
|
+
columns,
|
|
229
|
+
state: {
|
|
230
|
+
sorting,
|
|
231
|
+
globalFilter
|
|
232
|
+
},
|
|
233
|
+
onSortingChange: setSorting,
|
|
234
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
235
|
+
getCoreRowModel: getCoreRowModel(),
|
|
236
|
+
getSortedRowModel: getSortedRowModel(),
|
|
237
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
238
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
239
|
+
initialState: {
|
|
240
|
+
pagination: {
|
|
241
|
+
pageSize
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn("flex flex-col gap-3", className), children: [
|
|
246
|
+
searchable && /* @__PURE__ */ jsx3("div", { className: "flex items-center", children: /* @__PURE__ */ jsx3(
|
|
247
|
+
"input",
|
|
248
|
+
{
|
|
249
|
+
type: "text",
|
|
250
|
+
value: globalFilter,
|
|
251
|
+
onChange: (e) => setGlobalFilter(e.target.value),
|
|
252
|
+
placeholder: searchPlaceholder,
|
|
253
|
+
className: cn(
|
|
254
|
+
"w-full max-w-sm rounded-button border border-surface-border bg-surface px-3 py-1.5",
|
|
255
|
+
"text-sm text-text placeholder:text-text-muted",
|
|
256
|
+
"outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
) }),
|
|
260
|
+
/* @__PURE__ */ jsx3("div", { className: "overflow-auto rounded-card border border-surface-border", children: /* @__PURE__ */ jsxs3("table", { className: "w-full border-collapse text-sm", children: [
|
|
261
|
+
/* @__PURE__ */ jsx3("thead", { className: "sticky top-0 z-10 bg-surface-secondary", children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx3("tr", { children: headerGroup.headers.map((header) => {
|
|
262
|
+
const canSort = header.column.getCanSort();
|
|
263
|
+
const sorted = header.column.getIsSorted();
|
|
264
|
+
return /* @__PURE__ */ jsx3(
|
|
265
|
+
"th",
|
|
266
|
+
{
|
|
267
|
+
className: cn(
|
|
268
|
+
"border-b border-surface-border px-3 py-2 text-left font-medium text-text-secondary",
|
|
269
|
+
canSort && "cursor-pointer select-none"
|
|
270
|
+
),
|
|
271
|
+
onClick: canSort ? header.column.getToggleSortingHandler() : void 0,
|
|
272
|
+
"aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : canSort ? "none" : void 0,
|
|
273
|
+
children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
|
|
274
|
+
header.isPlaceholder ? null : flexRender(
|
|
275
|
+
header.column.columnDef.header,
|
|
276
|
+
header.getContext()
|
|
277
|
+
),
|
|
278
|
+
canSort && /* @__PURE__ */ jsxs3("span", { className: "inline-flex flex-col text-2xs leading-none text-text-muted", children: [
|
|
279
|
+
/* @__PURE__ */ jsx3(
|
|
280
|
+
"span",
|
|
281
|
+
{
|
|
282
|
+
className: cn(
|
|
283
|
+
sorted === "asc" && "text-accent"
|
|
284
|
+
),
|
|
285
|
+
children: "\u2191"
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
/* @__PURE__ */ jsx3(
|
|
289
|
+
"span",
|
|
290
|
+
{
|
|
291
|
+
className: cn(
|
|
292
|
+
sorted === "desc" && "text-accent"
|
|
293
|
+
),
|
|
294
|
+
children: "\u2193"
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
] })
|
|
298
|
+
] })
|
|
299
|
+
},
|
|
300
|
+
header.id
|
|
301
|
+
);
|
|
302
|
+
}) }, headerGroup.id)) }),
|
|
303
|
+
/* @__PURE__ */ jsx3("tbody", { children: table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3(
|
|
304
|
+
"td",
|
|
305
|
+
{
|
|
306
|
+
colSpan: columns.length,
|
|
307
|
+
className: "px-3 py-8 text-center text-text-tertiary",
|
|
308
|
+
children: "No results."
|
|
309
|
+
}
|
|
310
|
+
) }) : table.getRowModel().rows.map((row, index) => /* @__PURE__ */ jsx3(
|
|
311
|
+
"tr",
|
|
312
|
+
{
|
|
313
|
+
className: cn(
|
|
314
|
+
"border-b border-surface-border transition-colors hover:bg-surface-hover",
|
|
315
|
+
index % 2 === 1 && "bg-surface-secondary"
|
|
316
|
+
),
|
|
317
|
+
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx3("td", { className: "px-3 py-2 text-text", children: flexRender(
|
|
318
|
+
cell.column.columnDef.cell,
|
|
319
|
+
cell.getContext()
|
|
320
|
+
) }, cell.id))
|
|
321
|
+
},
|
|
322
|
+
row.id
|
|
323
|
+
)) })
|
|
324
|
+
] }) }),
|
|
325
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between px-1 text-sm text-text-secondary", children: [
|
|
326
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
327
|
+
"Page ",
|
|
328
|
+
table.getState().pagination.pageIndex + 1,
|
|
329
|
+
" of",
|
|
330
|
+
" ",
|
|
331
|
+
table.getPageCount() || 1
|
|
332
|
+
] }),
|
|
333
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
334
|
+
/* @__PURE__ */ jsx3(
|
|
335
|
+
"button",
|
|
336
|
+
{
|
|
337
|
+
type: "button",
|
|
338
|
+
onClick: () => table.previousPage(),
|
|
339
|
+
disabled: !table.getCanPreviousPage(),
|
|
340
|
+
"aria-label": "Go to previous page",
|
|
341
|
+
className: cn(
|
|
342
|
+
"rounded-button border border-surface-border bg-surface px-3 py-1 text-sm",
|
|
343
|
+
"hover:bg-surface-hover",
|
|
344
|
+
"disabled:cursor-not-allowed disabled:text-text-muted disabled:border-surface-border/50"
|
|
345
|
+
),
|
|
346
|
+
children: "Previous"
|
|
347
|
+
}
|
|
348
|
+
),
|
|
349
|
+
/* @__PURE__ */ jsx3(
|
|
350
|
+
"button",
|
|
351
|
+
{
|
|
352
|
+
type: "button",
|
|
353
|
+
onClick: () => table.nextPage(),
|
|
354
|
+
disabled: !table.getCanNextPage(),
|
|
355
|
+
"aria-label": "Go to next page",
|
|
356
|
+
className: cn(
|
|
357
|
+
"rounded-button border border-surface-border bg-surface px-3 py-1 text-sm",
|
|
358
|
+
"hover:bg-surface-hover",
|
|
359
|
+
"disabled:cursor-not-allowed disabled:text-text-muted disabled:border-surface-border/50"
|
|
360
|
+
),
|
|
361
|
+
children: "Next"
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
] })
|
|
365
|
+
] })
|
|
366
|
+
] });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/components/metric-trend.tsx
|
|
370
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
371
|
+
var sizeStyles = {
|
|
372
|
+
sm: {
|
|
373
|
+
value: "text-base font-semibold",
|
|
374
|
+
delta: "text-xs",
|
|
375
|
+
label: "text-xs"
|
|
376
|
+
},
|
|
377
|
+
md: {
|
|
378
|
+
value: "text-xl font-semibold",
|
|
379
|
+
delta: "text-sm",
|
|
380
|
+
label: "text-sm"
|
|
381
|
+
},
|
|
382
|
+
lg: {
|
|
383
|
+
value: "text-3xl font-semibold",
|
|
384
|
+
delta: "text-base",
|
|
385
|
+
label: "text-base"
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
function MetricTrend({
|
|
389
|
+
value,
|
|
390
|
+
delta,
|
|
391
|
+
deltaLabel,
|
|
392
|
+
size = "md",
|
|
393
|
+
className
|
|
394
|
+
}) {
|
|
395
|
+
const styles = sizeStyles[size];
|
|
396
|
+
const isPositive = delta >= 0;
|
|
397
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("flex items-baseline gap-2", className), children: [
|
|
398
|
+
/* @__PURE__ */ jsx4("span", { className: cn(styles.value, "text-text"), children: value }),
|
|
399
|
+
/* @__PURE__ */ jsxs4(
|
|
400
|
+
"span",
|
|
401
|
+
{
|
|
402
|
+
className: cn(
|
|
403
|
+
styles.delta,
|
|
404
|
+
"font-medium",
|
|
405
|
+
isPositive ? "text-revenue" : "text-loss"
|
|
406
|
+
),
|
|
407
|
+
children: [
|
|
408
|
+
isPositive ? "\u2191" : "\u2193",
|
|
409
|
+
" ",
|
|
410
|
+
Math.abs(delta).toFixed(1),
|
|
411
|
+
"%"
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
deltaLabel && /* @__PURE__ */ jsx4("span", { className: cn(styles.label, "text-text-muted"), children: deltaLabel })
|
|
416
|
+
] });
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/components/bar-chart.tsx
|
|
420
|
+
import {
|
|
421
|
+
ResponsiveContainer,
|
|
422
|
+
BarChart as RechartsBarChart,
|
|
423
|
+
Bar,
|
|
424
|
+
XAxis,
|
|
425
|
+
YAxis,
|
|
426
|
+
CartesianGrid,
|
|
427
|
+
Tooltip,
|
|
428
|
+
Legend
|
|
429
|
+
} from "recharts";
|
|
430
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
431
|
+
function BarChart({
|
|
432
|
+
data,
|
|
433
|
+
xKey,
|
|
434
|
+
bars,
|
|
435
|
+
height = 300,
|
|
436
|
+
layout = "vertical",
|
|
437
|
+
className
|
|
438
|
+
}) {
|
|
439
|
+
const colors = useChartColors();
|
|
440
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("w-full", className), role: "img", "aria-label": "Bar chart", children: /* @__PURE__ */ jsx5(ResponsiveContainer, { width: "100%", height, children: /* @__PURE__ */ jsxs5(
|
|
441
|
+
RechartsBarChart,
|
|
442
|
+
{
|
|
443
|
+
data,
|
|
444
|
+
layout: layout === "horizontal" ? "vertical" : "horizontal",
|
|
445
|
+
margin: { top: 8, right: 16, left: 0, bottom: 0 },
|
|
446
|
+
children: [
|
|
447
|
+
/* @__PURE__ */ jsx5(
|
|
448
|
+
CartesianGrid,
|
|
449
|
+
{
|
|
450
|
+
strokeDasharray: "3 3",
|
|
451
|
+
stroke: colors.grid,
|
|
452
|
+
vertical: false
|
|
453
|
+
}
|
|
454
|
+
),
|
|
455
|
+
layout === "vertical" ? /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
456
|
+
/* @__PURE__ */ jsx5(
|
|
457
|
+
XAxis,
|
|
458
|
+
{
|
|
459
|
+
dataKey: xKey,
|
|
460
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
461
|
+
axisLine: { stroke: colors.grid },
|
|
462
|
+
tickLine: false
|
|
463
|
+
}
|
|
464
|
+
),
|
|
465
|
+
/* @__PURE__ */ jsx5(
|
|
466
|
+
YAxis,
|
|
467
|
+
{
|
|
468
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
469
|
+
axisLine: false,
|
|
470
|
+
tickLine: false
|
|
471
|
+
}
|
|
472
|
+
)
|
|
473
|
+
] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
474
|
+
/* @__PURE__ */ jsx5(
|
|
475
|
+
XAxis,
|
|
476
|
+
{
|
|
477
|
+
type: "number",
|
|
478
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
479
|
+
axisLine: false,
|
|
480
|
+
tickLine: false
|
|
481
|
+
}
|
|
482
|
+
),
|
|
483
|
+
/* @__PURE__ */ jsx5(
|
|
484
|
+
YAxis,
|
|
485
|
+
{
|
|
486
|
+
dataKey: xKey,
|
|
487
|
+
type: "category",
|
|
488
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
489
|
+
axisLine: { stroke: colors.grid },
|
|
490
|
+
tickLine: false,
|
|
491
|
+
width: 100
|
|
492
|
+
}
|
|
493
|
+
)
|
|
494
|
+
] }),
|
|
495
|
+
/* @__PURE__ */ jsx5(
|
|
496
|
+
Tooltip,
|
|
497
|
+
{
|
|
498
|
+
contentStyle: {
|
|
499
|
+
backgroundColor: colors.tooltipBg,
|
|
500
|
+
border: `1px solid ${colors.tooltipBorder}`,
|
|
501
|
+
borderRadius: "2px",
|
|
502
|
+
boxShadow: "none",
|
|
503
|
+
fontSize: 12,
|
|
504
|
+
color: colors.tooltipText
|
|
505
|
+
},
|
|
506
|
+
cursor: { fill: colors.barCursor }
|
|
507
|
+
}
|
|
508
|
+
),
|
|
509
|
+
/* @__PURE__ */ jsx5(
|
|
510
|
+
Legend,
|
|
511
|
+
{
|
|
512
|
+
wrapperStyle: { fontSize: 12, paddingTop: 8 },
|
|
513
|
+
iconType: "circle",
|
|
514
|
+
iconSize: 8
|
|
515
|
+
}
|
|
516
|
+
),
|
|
517
|
+
bars.map((bar, index) => /* @__PURE__ */ jsx5(
|
|
518
|
+
Bar,
|
|
519
|
+
{
|
|
520
|
+
dataKey: bar.key,
|
|
521
|
+
name: bar.label,
|
|
522
|
+
fill: bar.color ?? colors.series[index % colors.series.length],
|
|
523
|
+
radius: [2, 2, 0, 0],
|
|
524
|
+
maxBarSize: 48
|
|
525
|
+
},
|
|
526
|
+
bar.key
|
|
527
|
+
))
|
|
528
|
+
]
|
|
529
|
+
}
|
|
530
|
+
) }) });
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/components/line-chart.tsx
|
|
534
|
+
import {
|
|
535
|
+
ResponsiveContainer as ResponsiveContainer2,
|
|
536
|
+
LineChart as RechartsLineChart,
|
|
537
|
+
Line,
|
|
538
|
+
XAxis as XAxis2,
|
|
539
|
+
YAxis as YAxis2,
|
|
540
|
+
CartesianGrid as CartesianGrid2,
|
|
541
|
+
Tooltip as Tooltip2,
|
|
542
|
+
Legend as Legend2
|
|
543
|
+
} from "recharts";
|
|
544
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
545
|
+
function LineChart({
|
|
546
|
+
data,
|
|
547
|
+
xKey,
|
|
548
|
+
lines,
|
|
549
|
+
height = 300,
|
|
550
|
+
className
|
|
551
|
+
}) {
|
|
552
|
+
const colors = useChartColors();
|
|
553
|
+
return /* @__PURE__ */ jsx6("div", { className: cn("w-full", className), role: "img", "aria-label": "Line chart", children: /* @__PURE__ */ jsx6(ResponsiveContainer2, { width: "100%", height, children: /* @__PURE__ */ jsxs6(
|
|
554
|
+
RechartsLineChart,
|
|
555
|
+
{
|
|
556
|
+
data,
|
|
557
|
+
margin: { top: 8, right: 16, left: 0, bottom: 0 },
|
|
558
|
+
children: [
|
|
559
|
+
/* @__PURE__ */ jsx6(
|
|
560
|
+
CartesianGrid2,
|
|
561
|
+
{
|
|
562
|
+
strokeDasharray: "3 3",
|
|
563
|
+
stroke: colors.grid,
|
|
564
|
+
vertical: false
|
|
565
|
+
}
|
|
566
|
+
),
|
|
567
|
+
/* @__PURE__ */ jsx6(
|
|
568
|
+
XAxis2,
|
|
569
|
+
{
|
|
570
|
+
dataKey: xKey,
|
|
571
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
572
|
+
axisLine: { stroke: colors.grid },
|
|
573
|
+
tickLine: false
|
|
574
|
+
}
|
|
575
|
+
),
|
|
576
|
+
/* @__PURE__ */ jsx6(
|
|
577
|
+
YAxis2,
|
|
578
|
+
{
|
|
579
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
580
|
+
axisLine: false,
|
|
581
|
+
tickLine: false
|
|
582
|
+
}
|
|
583
|
+
),
|
|
584
|
+
/* @__PURE__ */ jsx6(
|
|
585
|
+
Tooltip2,
|
|
586
|
+
{
|
|
587
|
+
contentStyle: {
|
|
588
|
+
backgroundColor: colors.tooltipBg,
|
|
589
|
+
border: `1px solid ${colors.tooltipBorder}`,
|
|
590
|
+
borderRadius: "2px",
|
|
591
|
+
boxShadow: "none",
|
|
592
|
+
fontSize: 12,
|
|
593
|
+
color: colors.tooltipText
|
|
594
|
+
},
|
|
595
|
+
cursor: { stroke: colors.cursor, strokeDasharray: "3 3" }
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
/* @__PURE__ */ jsx6(
|
|
599
|
+
Legend2,
|
|
600
|
+
{
|
|
601
|
+
wrapperStyle: { fontSize: 12, paddingTop: 8 },
|
|
602
|
+
iconType: "circle",
|
|
603
|
+
iconSize: 8
|
|
604
|
+
}
|
|
605
|
+
),
|
|
606
|
+
lines.map((line, index) => {
|
|
607
|
+
const color = line.color ?? colors.series[index % colors.series.length];
|
|
608
|
+
return /* @__PURE__ */ jsx6(
|
|
609
|
+
Line,
|
|
610
|
+
{
|
|
611
|
+
dataKey: line.key,
|
|
612
|
+
name: line.label,
|
|
613
|
+
type: "monotone",
|
|
614
|
+
stroke: color,
|
|
615
|
+
strokeWidth: 2,
|
|
616
|
+
dot: false,
|
|
617
|
+
activeDot: {
|
|
618
|
+
r: 4,
|
|
619
|
+
fill: color,
|
|
620
|
+
stroke: colors.activeDot,
|
|
621
|
+
strokeWidth: 2
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
line.key
|
|
625
|
+
);
|
|
626
|
+
})
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
) }) });
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/components/area-chart.tsx
|
|
633
|
+
import * as React3 from "react";
|
|
634
|
+
import {
|
|
635
|
+
ResponsiveContainer as ResponsiveContainer3,
|
|
636
|
+
AreaChart as RechartsAreaChart,
|
|
637
|
+
Area,
|
|
638
|
+
XAxis as XAxis3,
|
|
639
|
+
YAxis as YAxis3,
|
|
640
|
+
CartesianGrid as CartesianGrid3,
|
|
641
|
+
Tooltip as Tooltip3,
|
|
642
|
+
Legend as Legend3
|
|
643
|
+
} from "recharts";
|
|
644
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
645
|
+
function AreaChart({
|
|
646
|
+
data,
|
|
647
|
+
xKey,
|
|
648
|
+
areas,
|
|
649
|
+
height = 300,
|
|
650
|
+
stacked = true,
|
|
651
|
+
className
|
|
652
|
+
}) {
|
|
653
|
+
const gradientId = React3.useId();
|
|
654
|
+
const colors = useChartColors();
|
|
655
|
+
return /* @__PURE__ */ jsx7("div", { className: cn("w-full", className), role: "img", "aria-label": "Area chart", children: /* @__PURE__ */ jsx7(ResponsiveContainer3, { width: "100%", height, children: /* @__PURE__ */ jsxs7(
|
|
656
|
+
RechartsAreaChart,
|
|
657
|
+
{
|
|
658
|
+
data,
|
|
659
|
+
margin: { top: 8, right: 16, left: 0, bottom: 0 },
|
|
660
|
+
children: [
|
|
661
|
+
/* @__PURE__ */ jsx7("defs", { children: areas.map((area, index) => {
|
|
662
|
+
const color = area.color ?? colors.series[index % colors.series.length];
|
|
663
|
+
return /* @__PURE__ */ jsxs7(
|
|
664
|
+
"linearGradient",
|
|
665
|
+
{
|
|
666
|
+
id: `${gradientId}-${area.key}`,
|
|
667
|
+
x1: "0",
|
|
668
|
+
y1: "0",
|
|
669
|
+
x2: "0",
|
|
670
|
+
y2: "1",
|
|
671
|
+
children: [
|
|
672
|
+
/* @__PURE__ */ jsx7("stop", { offset: "0%", stopColor: color, stopOpacity: 0.3 }),
|
|
673
|
+
/* @__PURE__ */ jsx7("stop", { offset: "95%", stopColor: color, stopOpacity: 0.02 })
|
|
674
|
+
]
|
|
675
|
+
},
|
|
676
|
+
area.key
|
|
677
|
+
);
|
|
678
|
+
}) }),
|
|
679
|
+
/* @__PURE__ */ jsx7(
|
|
680
|
+
CartesianGrid3,
|
|
681
|
+
{
|
|
682
|
+
strokeDasharray: "3 3",
|
|
683
|
+
stroke: colors.grid,
|
|
684
|
+
vertical: false
|
|
685
|
+
}
|
|
686
|
+
),
|
|
687
|
+
/* @__PURE__ */ jsx7(
|
|
688
|
+
XAxis3,
|
|
689
|
+
{
|
|
690
|
+
dataKey: xKey,
|
|
691
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
692
|
+
axisLine: { stroke: colors.grid },
|
|
693
|
+
tickLine: false
|
|
694
|
+
}
|
|
695
|
+
),
|
|
696
|
+
/* @__PURE__ */ jsx7(
|
|
697
|
+
YAxis3,
|
|
698
|
+
{
|
|
699
|
+
tick: { fontSize: 12, fill: colors.tick },
|
|
700
|
+
axisLine: false,
|
|
701
|
+
tickLine: false
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
/* @__PURE__ */ jsx7(
|
|
705
|
+
Tooltip3,
|
|
706
|
+
{
|
|
707
|
+
contentStyle: {
|
|
708
|
+
backgroundColor: colors.tooltipBg,
|
|
709
|
+
border: `1px solid ${colors.tooltipBorder}`,
|
|
710
|
+
borderRadius: "2px",
|
|
711
|
+
boxShadow: "none",
|
|
712
|
+
fontSize: 12,
|
|
713
|
+
color: colors.tooltipText
|
|
714
|
+
},
|
|
715
|
+
cursor: { stroke: colors.cursor, strokeDasharray: "3 3" }
|
|
716
|
+
}
|
|
717
|
+
),
|
|
718
|
+
/* @__PURE__ */ jsx7(
|
|
719
|
+
Legend3,
|
|
720
|
+
{
|
|
721
|
+
wrapperStyle: { fontSize: 12, paddingTop: 8 },
|
|
722
|
+
iconType: "circle",
|
|
723
|
+
iconSize: 8
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
areas.map((area, index) => {
|
|
727
|
+
const color = area.color ?? colors.series[index % colors.series.length];
|
|
728
|
+
return /* @__PURE__ */ jsx7(
|
|
729
|
+
Area,
|
|
730
|
+
{
|
|
731
|
+
dataKey: area.key,
|
|
732
|
+
name: area.label,
|
|
733
|
+
type: "monotone",
|
|
734
|
+
stroke: color,
|
|
735
|
+
strokeWidth: 2,
|
|
736
|
+
fill: `url(#${gradientId}-${area.key})`,
|
|
737
|
+
stackId: stacked ? "stack" : void 0,
|
|
738
|
+
dot: false,
|
|
739
|
+
activeDot: {
|
|
740
|
+
r: 4,
|
|
741
|
+
fill: color,
|
|
742
|
+
stroke: colors.activeDot,
|
|
743
|
+
strokeWidth: 2
|
|
744
|
+
}
|
|
745
|
+
},
|
|
746
|
+
area.key
|
|
747
|
+
);
|
|
748
|
+
})
|
|
749
|
+
]
|
|
750
|
+
}
|
|
751
|
+
) }) });
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/components/comparison-row.tsx
|
|
755
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
756
|
+
function ComparisonRow({
|
|
757
|
+
label,
|
|
758
|
+
current,
|
|
759
|
+
previous,
|
|
760
|
+
format: format3 = (v) => v.toLocaleString(),
|
|
761
|
+
className
|
|
762
|
+
}) {
|
|
763
|
+
const delta = previous !== 0 ? (current - previous) / Math.abs(previous) * 100 : 0;
|
|
764
|
+
const isPositive = delta >= 0;
|
|
765
|
+
return /* @__PURE__ */ jsxs8(
|
|
766
|
+
"div",
|
|
767
|
+
{
|
|
768
|
+
className: cn(
|
|
769
|
+
"flex items-center justify-between gap-4 py-2",
|
|
770
|
+
className
|
|
771
|
+
),
|
|
772
|
+
children: [
|
|
773
|
+
/* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-shrink truncate text-sm text-text-secondary", children: label }),
|
|
774
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-4", children: [
|
|
775
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm font-semibold text-text", children: format3(current) }),
|
|
776
|
+
/* @__PURE__ */ jsx8("span", { className: "text-sm text-text-muted", children: format3(previous) }),
|
|
777
|
+
/* @__PURE__ */ jsxs8(
|
|
778
|
+
"span",
|
|
779
|
+
{
|
|
780
|
+
className: cn(
|
|
781
|
+
"inline-flex items-center rounded-button px-2 py-0.5 text-xs font-medium",
|
|
782
|
+
isPositive ? "bg-revenue-bg text-revenue" : "bg-loss-bg text-loss"
|
|
783
|
+
),
|
|
784
|
+
"aria-label": `${isPositive ? "Up" : "Down"} ${Math.abs(delta).toFixed(1)}%`,
|
|
785
|
+
children: [
|
|
786
|
+
isPositive ? "\u2191" : "\u2193",
|
|
787
|
+
" ",
|
|
788
|
+
Math.abs(delta).toFixed(1),
|
|
789
|
+
"%"
|
|
790
|
+
]
|
|
791
|
+
}
|
|
792
|
+
)
|
|
793
|
+
] })
|
|
794
|
+
]
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// src/components/date-range-picker.tsx
|
|
800
|
+
import * as React4 from "react";
|
|
801
|
+
import * as Popover from "@radix-ui/react-popover";
|
|
802
|
+
import {
|
|
803
|
+
startOfMonth,
|
|
804
|
+
endOfMonth,
|
|
805
|
+
subMonths,
|
|
806
|
+
subDays,
|
|
807
|
+
startOfYear,
|
|
808
|
+
format
|
|
809
|
+
} from "date-fns";
|
|
810
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
811
|
+
var DEFAULT_PRESETS = [
|
|
812
|
+
{ label: "MTD", value: "mtd" },
|
|
813
|
+
{ label: "Last Month", value: "last-month" },
|
|
814
|
+
{ label: "Last 30 Days", value: "last-30" },
|
|
815
|
+
{ label: "Last 90 Days", value: "last-90" },
|
|
816
|
+
{ label: "YTD", value: "ytd" },
|
|
817
|
+
{ label: "Custom", value: "custom" }
|
|
818
|
+
];
|
|
819
|
+
function resolvePreset(preset) {
|
|
820
|
+
const now = /* @__PURE__ */ new Date();
|
|
821
|
+
switch (preset) {
|
|
822
|
+
case "mtd":
|
|
823
|
+
return { start: startOfMonth(now), end: now };
|
|
824
|
+
case "last-month": {
|
|
825
|
+
const prev = subMonths(now, 1);
|
|
826
|
+
return { start: startOfMonth(prev), end: endOfMonth(prev) };
|
|
827
|
+
}
|
|
828
|
+
case "last-30":
|
|
829
|
+
return { start: subDays(now, 30), end: now };
|
|
830
|
+
case "last-90":
|
|
831
|
+
return { start: subDays(now, 90), end: now };
|
|
832
|
+
case "ytd":
|
|
833
|
+
return { start: startOfYear(now), end: now };
|
|
834
|
+
default:
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function DateRangePicker({
|
|
839
|
+
value,
|
|
840
|
+
onChange,
|
|
841
|
+
onPresetChange,
|
|
842
|
+
presets = DEFAULT_PRESETS,
|
|
843
|
+
className
|
|
844
|
+
}) {
|
|
845
|
+
const [open, setOpen] = React4.useState(false);
|
|
846
|
+
const [activePreset, setActivePreset] = React4.useState(null);
|
|
847
|
+
const [showCustomForm, setShowCustomForm] = React4.useState(false);
|
|
848
|
+
const [customStart, setCustomStart] = React4.useState(
|
|
849
|
+
format(value.start, "yyyy-MM-dd")
|
|
850
|
+
);
|
|
851
|
+
const [customEnd, setCustomEnd] = React4.useState(
|
|
852
|
+
format(value.end, "yyyy-MM-dd")
|
|
853
|
+
);
|
|
854
|
+
const handlePresetClick = (preset) => {
|
|
855
|
+
setActivePreset(preset);
|
|
856
|
+
onPresetChange?.(preset);
|
|
857
|
+
if (preset === "custom") {
|
|
858
|
+
setShowCustomForm(true);
|
|
859
|
+
setCustomStart(format(value.start, "yyyy-MM-dd"));
|
|
860
|
+
setCustomEnd(format(value.end, "yyyy-MM-dd"));
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
setShowCustomForm(false);
|
|
864
|
+
const range = resolvePreset(preset);
|
|
865
|
+
if (range) {
|
|
866
|
+
onChange(range);
|
|
867
|
+
setOpen(false);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
const handleCustomApply = () => {
|
|
871
|
+
const start = /* @__PURE__ */ new Date(customStart + "T00:00:00");
|
|
872
|
+
const end = /* @__PURE__ */ new Date(customEnd + "T00:00:00");
|
|
873
|
+
if (!isNaN(start.getTime()) && !isNaN(end.getTime()) && start <= end) {
|
|
874
|
+
onChange({ start, end });
|
|
875
|
+
setOpen(false);
|
|
876
|
+
setShowCustomForm(false);
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
const displayLabel = `${format(value.start, "MMM d, yyyy")} - ${format(value.end, "MMM d, yyyy")}`;
|
|
880
|
+
return /* @__PURE__ */ jsxs9(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
881
|
+
/* @__PURE__ */ jsx9(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
|
|
882
|
+
"button",
|
|
883
|
+
{
|
|
884
|
+
type: "button",
|
|
885
|
+
className: cn(
|
|
886
|
+
"inline-flex items-center gap-2 rounded-button border border-surface-border bg-surface px-3 py-1.5",
|
|
887
|
+
"text-sm text-text hover:bg-surface-hover",
|
|
888
|
+
"outline-none focus:border-accent focus:ring-1 focus:ring-accent",
|
|
889
|
+
className
|
|
890
|
+
),
|
|
891
|
+
children: [
|
|
892
|
+
/* @__PURE__ */ jsx9(
|
|
893
|
+
"svg",
|
|
894
|
+
{
|
|
895
|
+
width: "16",
|
|
896
|
+
height: "16",
|
|
897
|
+
viewBox: "0 0 16 16",
|
|
898
|
+
fill: "none",
|
|
899
|
+
className: "text-text-tertiary",
|
|
900
|
+
children: /* @__PURE__ */ jsx9(
|
|
901
|
+
"path",
|
|
902
|
+
{
|
|
903
|
+
d: "M5 1v2M11 1v2M1 6h14M3 3h10a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",
|
|
904
|
+
stroke: "currentColor",
|
|
905
|
+
strokeWidth: "1.5",
|
|
906
|
+
strokeLinecap: "round",
|
|
907
|
+
strokeLinejoin: "round"
|
|
908
|
+
}
|
|
909
|
+
)
|
|
910
|
+
}
|
|
911
|
+
),
|
|
912
|
+
/* @__PURE__ */ jsx9("span", { children: displayLabel })
|
|
913
|
+
]
|
|
914
|
+
}
|
|
915
|
+
) }),
|
|
916
|
+
/* @__PURE__ */ jsx9(Popover.Portal, { children: /* @__PURE__ */ jsxs9(
|
|
917
|
+
Popover.Content,
|
|
918
|
+
{
|
|
919
|
+
align: "start",
|
|
920
|
+
sideOffset: 4,
|
|
921
|
+
className: cn(
|
|
922
|
+
"z-50 rounded-card border border-surface-border bg-surface p-3 shadow-card",
|
|
923
|
+
"animate-in fade-in-0 zoom-in-95",
|
|
924
|
+
"w-64"
|
|
925
|
+
),
|
|
926
|
+
children: [
|
|
927
|
+
/* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-1", children: presets.map((preset) => /* @__PURE__ */ jsx9(
|
|
928
|
+
"button",
|
|
929
|
+
{
|
|
930
|
+
type: "button",
|
|
931
|
+
onClick: () => handlePresetClick(preset.value),
|
|
932
|
+
className: cn(
|
|
933
|
+
"rounded-button px-3 py-1.5 text-left text-sm transition-colors",
|
|
934
|
+
activePreset === preset.value ? "bg-accent-light text-accent-dark font-medium" : "text-text hover:bg-surface-hover"
|
|
935
|
+
),
|
|
936
|
+
children: preset.label
|
|
937
|
+
},
|
|
938
|
+
preset.value
|
|
939
|
+
)) }),
|
|
940
|
+
showCustomForm && /* @__PURE__ */ jsxs9("div", { className: "mt-3 flex flex-col gap-2 border-t border-surface-border pt-3", children: [
|
|
941
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex flex-col gap-1", children: [
|
|
942
|
+
/* @__PURE__ */ jsx9("span", { className: "text-xs font-medium text-text-secondary", children: "Start date" }),
|
|
943
|
+
/* @__PURE__ */ jsx9(
|
|
944
|
+
"input",
|
|
945
|
+
{
|
|
946
|
+
type: "date",
|
|
947
|
+
value: customStart,
|
|
948
|
+
onChange: (e) => setCustomStart(e.target.value),
|
|
949
|
+
className: cn(
|
|
950
|
+
"rounded-button border border-surface-border bg-surface px-2 py-1",
|
|
951
|
+
"text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
|
952
|
+
)
|
|
953
|
+
}
|
|
954
|
+
)
|
|
955
|
+
] }),
|
|
956
|
+
/* @__PURE__ */ jsxs9("label", { className: "flex flex-col gap-1", children: [
|
|
957
|
+
/* @__PURE__ */ jsx9("span", { className: "text-xs font-medium text-text-secondary", children: "End date" }),
|
|
958
|
+
/* @__PURE__ */ jsx9(
|
|
959
|
+
"input",
|
|
960
|
+
{
|
|
961
|
+
type: "date",
|
|
962
|
+
value: customEnd,
|
|
963
|
+
onChange: (e) => setCustomEnd(e.target.value),
|
|
964
|
+
className: cn(
|
|
965
|
+
"rounded-button border border-surface-border bg-surface px-2 py-1",
|
|
966
|
+
"text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
] }),
|
|
971
|
+
/* @__PURE__ */ jsx9(
|
|
972
|
+
"button",
|
|
973
|
+
{
|
|
974
|
+
type: "button",
|
|
975
|
+
onClick: handleCustomApply,
|
|
976
|
+
className: cn(
|
|
977
|
+
"mt-1 rounded-button bg-accent px-3 py-1.5 text-sm font-medium text-text-inverse",
|
|
978
|
+
"hover:bg-accent-dark transition-colors"
|
|
979
|
+
),
|
|
980
|
+
children: "Apply"
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
] }),
|
|
984
|
+
/* @__PURE__ */ jsx9(Popover.Arrow, { className: "fill-surface-border" })
|
|
985
|
+
]
|
|
986
|
+
}
|
|
987
|
+
) })
|
|
988
|
+
] });
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/components/filter-bar.tsx
|
|
992
|
+
import * as React5 from "react";
|
|
993
|
+
import * as Select from "@radix-ui/react-select";
|
|
994
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
995
|
+
function FilterDropdownSingle({
|
|
996
|
+
filter,
|
|
997
|
+
value,
|
|
998
|
+
onChange
|
|
999
|
+
}) {
|
|
1000
|
+
return /* @__PURE__ */ jsxs10(Select.Root, { value: value || void 0, onValueChange: onChange, children: [
|
|
1001
|
+
/* @__PURE__ */ jsxs10(
|
|
1002
|
+
Select.Trigger,
|
|
1003
|
+
{
|
|
1004
|
+
"aria-label": filter.label,
|
|
1005
|
+
className: cn(
|
|
1006
|
+
"inline-flex items-center gap-1.5 rounded-button border border-surface-border bg-surface px-3 py-1.5",
|
|
1007
|
+
"text-sm outline-none hover:bg-surface-hover",
|
|
1008
|
+
"focus:border-accent focus:ring-1 focus:ring-accent",
|
|
1009
|
+
value ? "text-text" : "text-text-tertiary"
|
|
1010
|
+
),
|
|
1011
|
+
children: [
|
|
1012
|
+
/* @__PURE__ */ jsx10(Select.Value, { placeholder: filter.label }),
|
|
1013
|
+
/* @__PURE__ */ jsx10(Select.Icon, { children: /* @__PURE__ */ jsx10(
|
|
1014
|
+
"svg",
|
|
1015
|
+
{
|
|
1016
|
+
width: "12",
|
|
1017
|
+
height: "12",
|
|
1018
|
+
viewBox: "0 0 12 12",
|
|
1019
|
+
fill: "none",
|
|
1020
|
+
className: "text-text-muted",
|
|
1021
|
+
children: /* @__PURE__ */ jsx10(
|
|
1022
|
+
"path",
|
|
1023
|
+
{
|
|
1024
|
+
d: "M3 4.5l3 3 3-3",
|
|
1025
|
+
stroke: "currentColor",
|
|
1026
|
+
strokeWidth: "1.5",
|
|
1027
|
+
strokeLinecap: "round",
|
|
1028
|
+
strokeLinejoin: "round"
|
|
1029
|
+
}
|
|
1030
|
+
)
|
|
1031
|
+
}
|
|
1032
|
+
) })
|
|
1033
|
+
]
|
|
1034
|
+
}
|
|
1035
|
+
),
|
|
1036
|
+
/* @__PURE__ */ jsx10(Select.Portal, { children: /* @__PURE__ */ jsx10(
|
|
1037
|
+
Select.Content,
|
|
1038
|
+
{
|
|
1039
|
+
className: cn(
|
|
1040
|
+
"z-50 overflow-hidden rounded-card border border-surface-border bg-surface shadow-card",
|
|
1041
|
+
"animate-in fade-in-0 zoom-in-95"
|
|
1042
|
+
),
|
|
1043
|
+
position: "popper",
|
|
1044
|
+
sideOffset: 4,
|
|
1045
|
+
children: /* @__PURE__ */ jsx10(Select.Viewport, { className: "p-1", children: filter.options.map((option) => /* @__PURE__ */ jsx10(
|
|
1046
|
+
Select.Item,
|
|
1047
|
+
{
|
|
1048
|
+
value: option.value,
|
|
1049
|
+
className: cn(
|
|
1050
|
+
"cursor-pointer rounded-button px-3 py-1.5 text-sm text-text outline-none",
|
|
1051
|
+
"data-[highlighted]:bg-surface-hover",
|
|
1052
|
+
"data-[state=checked]:font-medium data-[state=checked]:text-accent"
|
|
1053
|
+
),
|
|
1054
|
+
children: /* @__PURE__ */ jsx10(Select.ItemText, { children: option.label })
|
|
1055
|
+
},
|
|
1056
|
+
option.value
|
|
1057
|
+
)) })
|
|
1058
|
+
}
|
|
1059
|
+
) })
|
|
1060
|
+
] });
|
|
1061
|
+
}
|
|
1062
|
+
function FilterDropdownMultiple({
|
|
1063
|
+
filter,
|
|
1064
|
+
value,
|
|
1065
|
+
onChange
|
|
1066
|
+
}) {
|
|
1067
|
+
const [open, setOpen] = React5.useState(false);
|
|
1068
|
+
const handleToggle = (optionValue) => {
|
|
1069
|
+
const next = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue];
|
|
1070
|
+
onChange(next);
|
|
1071
|
+
};
|
|
1072
|
+
const selectedLabels = filter.options.filter((o) => value.includes(o.value)).map((o) => o.label);
|
|
1073
|
+
return /* @__PURE__ */ jsxs10("div", { className: "relative", children: [
|
|
1074
|
+
/* @__PURE__ */ jsxs10(
|
|
1075
|
+
"button",
|
|
1076
|
+
{
|
|
1077
|
+
type: "button",
|
|
1078
|
+
onClick: () => setOpen(!open),
|
|
1079
|
+
className: cn(
|
|
1080
|
+
"inline-flex items-center gap-1.5 rounded-button border border-surface-border bg-surface px-3 py-1.5",
|
|
1081
|
+
"text-sm outline-none hover:bg-surface-hover",
|
|
1082
|
+
"focus:border-accent focus:ring-1 focus:ring-accent",
|
|
1083
|
+
value.length > 0 ? "text-text" : "text-text-tertiary"
|
|
1084
|
+
),
|
|
1085
|
+
children: [
|
|
1086
|
+
/* @__PURE__ */ jsx10("span", { children: value.length === 0 ? filter.label : selectedLabels.length <= 2 ? selectedLabels.join(", ") : `${selectedLabels[0]} +${selectedLabels.length - 1}` }),
|
|
1087
|
+
/* @__PURE__ */ jsx10(
|
|
1088
|
+
"svg",
|
|
1089
|
+
{
|
|
1090
|
+
width: "12",
|
|
1091
|
+
height: "12",
|
|
1092
|
+
viewBox: "0 0 12 12",
|
|
1093
|
+
fill: "none",
|
|
1094
|
+
className: "text-text-muted",
|
|
1095
|
+
children: /* @__PURE__ */ jsx10(
|
|
1096
|
+
"path",
|
|
1097
|
+
{
|
|
1098
|
+
d: "M3 4.5l3 3 3-3",
|
|
1099
|
+
stroke: "currentColor",
|
|
1100
|
+
strokeWidth: "1.5",
|
|
1101
|
+
strokeLinecap: "round",
|
|
1102
|
+
strokeLinejoin: "round"
|
|
1103
|
+
}
|
|
1104
|
+
)
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
]
|
|
1108
|
+
}
|
|
1109
|
+
),
|
|
1110
|
+
open && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
1111
|
+
/* @__PURE__ */ jsx10(
|
|
1112
|
+
"div",
|
|
1113
|
+
{
|
|
1114
|
+
className: "fixed inset-0 z-40",
|
|
1115
|
+
onClick: () => setOpen(false)
|
|
1116
|
+
}
|
|
1117
|
+
),
|
|
1118
|
+
/* @__PURE__ */ jsx10(
|
|
1119
|
+
"div",
|
|
1120
|
+
{
|
|
1121
|
+
className: cn(
|
|
1122
|
+
"absolute left-0 top-full z-50 mt-1 min-w-[12rem] overflow-hidden rounded-card border border-surface-border bg-surface p-1 shadow-card"
|
|
1123
|
+
),
|
|
1124
|
+
children: filter.options.map((option) => {
|
|
1125
|
+
const checked = value.includes(option.value);
|
|
1126
|
+
return /* @__PURE__ */ jsxs10(
|
|
1127
|
+
"button",
|
|
1128
|
+
{
|
|
1129
|
+
type: "button",
|
|
1130
|
+
onClick: () => handleToggle(option.value),
|
|
1131
|
+
className: cn(
|
|
1132
|
+
"flex w-full items-center gap-2 rounded-button px-3 py-1.5 text-left text-sm text-text outline-none",
|
|
1133
|
+
"hover:bg-surface-hover"
|
|
1134
|
+
),
|
|
1135
|
+
children: [
|
|
1136
|
+
/* @__PURE__ */ jsx10(
|
|
1137
|
+
"span",
|
|
1138
|
+
{
|
|
1139
|
+
className: cn(
|
|
1140
|
+
"flex h-4 w-4 items-center justify-center rounded border",
|
|
1141
|
+
checked ? "border-accent bg-accent text-text-inverse" : "border-surface-border bg-surface"
|
|
1142
|
+
),
|
|
1143
|
+
children: checked && /* @__PURE__ */ jsx10(
|
|
1144
|
+
"svg",
|
|
1145
|
+
{
|
|
1146
|
+
width: "10",
|
|
1147
|
+
height: "10",
|
|
1148
|
+
viewBox: "0 0 10 10",
|
|
1149
|
+
fill: "none",
|
|
1150
|
+
children: /* @__PURE__ */ jsx10(
|
|
1151
|
+
"path",
|
|
1152
|
+
{
|
|
1153
|
+
d: "M2 5l2 2 4-4",
|
|
1154
|
+
stroke: "currentColor",
|
|
1155
|
+
strokeWidth: "1.5",
|
|
1156
|
+
strokeLinecap: "round",
|
|
1157
|
+
strokeLinejoin: "round"
|
|
1158
|
+
}
|
|
1159
|
+
)
|
|
1160
|
+
}
|
|
1161
|
+
)
|
|
1162
|
+
}
|
|
1163
|
+
),
|
|
1164
|
+
/* @__PURE__ */ jsx10("span", { children: option.label })
|
|
1165
|
+
]
|
|
1166
|
+
},
|
|
1167
|
+
option.value
|
|
1168
|
+
);
|
|
1169
|
+
})
|
|
1170
|
+
}
|
|
1171
|
+
)
|
|
1172
|
+
] })
|
|
1173
|
+
] });
|
|
1174
|
+
}
|
|
1175
|
+
function ActiveFilterBadge({
|
|
1176
|
+
filter,
|
|
1177
|
+
value
|
|
1178
|
+
}) {
|
|
1179
|
+
const count = Array.isArray(value) ? value.length : value ? 1 : 0;
|
|
1180
|
+
if (count === 0) return null;
|
|
1181
|
+
return /* @__PURE__ */ jsxs10(
|
|
1182
|
+
"span",
|
|
1183
|
+
{
|
|
1184
|
+
className: cn(
|
|
1185
|
+
"inline-flex items-center gap-1 rounded-full bg-accent-light px-2 py-0.5 text-xs font-medium text-accent-dark"
|
|
1186
|
+
),
|
|
1187
|
+
children: [
|
|
1188
|
+
filter.label,
|
|
1189
|
+
/* @__PURE__ */ jsx10("span", { className: "rounded-full bg-accent px-1.5 py-0.5 text-2xs leading-none text-text-inverse", children: count })
|
|
1190
|
+
]
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
function FilterBar({
|
|
1195
|
+
filters,
|
|
1196
|
+
values,
|
|
1197
|
+
onChange,
|
|
1198
|
+
onClear,
|
|
1199
|
+
className
|
|
1200
|
+
}) {
|
|
1201
|
+
const hasActiveFilters = Object.values(values).some(
|
|
1202
|
+
(v) => Array.isArray(v) ? v.length > 0 : Boolean(v)
|
|
1203
|
+
);
|
|
1204
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col gap-2", className), children: [
|
|
1205
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1206
|
+
filters.map((filter) => {
|
|
1207
|
+
const currentValue = values[filter.id];
|
|
1208
|
+
if (filter.multiple) {
|
|
1209
|
+
return /* @__PURE__ */ jsx10(
|
|
1210
|
+
FilterDropdownMultiple,
|
|
1211
|
+
{
|
|
1212
|
+
filter,
|
|
1213
|
+
value: Array.isArray(currentValue) ? currentValue : [],
|
|
1214
|
+
onChange: (val) => onChange(filter.id, val)
|
|
1215
|
+
},
|
|
1216
|
+
filter.id
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
return /* @__PURE__ */ jsx10(
|
|
1220
|
+
FilterDropdownSingle,
|
|
1221
|
+
{
|
|
1222
|
+
filter,
|
|
1223
|
+
value: typeof currentValue === "string" ? currentValue : "",
|
|
1224
|
+
onChange: (val) => onChange(filter.id, val)
|
|
1225
|
+
},
|
|
1226
|
+
filter.id
|
|
1227
|
+
);
|
|
1228
|
+
}),
|
|
1229
|
+
hasActiveFilters && onClear && /* @__PURE__ */ jsx10(
|
|
1230
|
+
"button",
|
|
1231
|
+
{
|
|
1232
|
+
type: "button",
|
|
1233
|
+
onClick: onClear,
|
|
1234
|
+
className: cn(
|
|
1235
|
+
"rounded-button px-2 py-1.5 text-sm text-text-tertiary",
|
|
1236
|
+
"hover:text-text hover:bg-surface-hover transition-colors"
|
|
1237
|
+
),
|
|
1238
|
+
children: "Clear all"
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] }),
|
|
1242
|
+
hasActiveFilters && /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap items-center gap-1.5", children: filters.map((filter) => {
|
|
1243
|
+
const currentValue = values[filter.id];
|
|
1244
|
+
if (!currentValue) return null;
|
|
1245
|
+
return /* @__PURE__ */ jsx10(
|
|
1246
|
+
ActiveFilterBadge,
|
|
1247
|
+
{
|
|
1248
|
+
filter,
|
|
1249
|
+
value: currentValue
|
|
1250
|
+
},
|
|
1251
|
+
filter.id
|
|
1252
|
+
);
|
|
1253
|
+
}) })
|
|
1254
|
+
] });
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// src/components/sidebar-nav.tsx
|
|
1258
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1259
|
+
function SidebarNav({
|
|
1260
|
+
items,
|
|
1261
|
+
collapsed = false,
|
|
1262
|
+
onToggleCollapse,
|
|
1263
|
+
header,
|
|
1264
|
+
footer,
|
|
1265
|
+
className
|
|
1266
|
+
}) {
|
|
1267
|
+
return /* @__PURE__ */ jsxs11(
|
|
1268
|
+
"nav",
|
|
1269
|
+
{
|
|
1270
|
+
className: cn(
|
|
1271
|
+
"flex h-full flex-col border-r border-surface-border bg-surface-secondary",
|
|
1272
|
+
"transition-[width] duration-200 ease-in-out",
|
|
1273
|
+
collapsed ? "w-sidebar-collapsed" : "w-sidebar-w",
|
|
1274
|
+
className
|
|
1275
|
+
),
|
|
1276
|
+
"aria-label": "Sidebar navigation",
|
|
1277
|
+
children: [
|
|
1278
|
+
header && /* @__PURE__ */ jsx11(
|
|
1279
|
+
"div",
|
|
1280
|
+
{
|
|
1281
|
+
className: cn(
|
|
1282
|
+
"flex items-center border-b border-surface-border px-3 py-3",
|
|
1283
|
+
collapsed && "justify-center"
|
|
1284
|
+
),
|
|
1285
|
+
children: header
|
|
1286
|
+
}
|
|
1287
|
+
),
|
|
1288
|
+
/* @__PURE__ */ jsx11("ul", { className: "flex flex-1 flex-col gap-0.5 overflow-y-auto p-2", children: items.map((item) => /* @__PURE__ */ jsx11("li", { children: /* @__PURE__ */ jsxs11(
|
|
1289
|
+
"a",
|
|
1290
|
+
{
|
|
1291
|
+
href: item.href,
|
|
1292
|
+
className: cn(
|
|
1293
|
+
"flex items-center gap-3 rounded-button px-3 py-2 text-sm transition-colors",
|
|
1294
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
1295
|
+
item.active ? "bg-surface-hover text-text font-medium border-l-2 border-accent" : "text-text-secondary hover:bg-surface-hover hover:text-text border-l-2 border-transparent",
|
|
1296
|
+
collapsed && "justify-center px-2"
|
|
1297
|
+
),
|
|
1298
|
+
"aria-current": item.active ? "page" : void 0,
|
|
1299
|
+
title: collapsed ? item.label : void 0,
|
|
1300
|
+
children: [
|
|
1301
|
+
item.icon && /* @__PURE__ */ jsx11(
|
|
1302
|
+
"span",
|
|
1303
|
+
{
|
|
1304
|
+
className: cn(
|
|
1305
|
+
"flex h-5 w-5 shrink-0 items-center justify-center",
|
|
1306
|
+
item.active ? "text-accent" : "text-text-tertiary"
|
|
1307
|
+
),
|
|
1308
|
+
children: item.icon
|
|
1309
|
+
}
|
|
1310
|
+
),
|
|
1311
|
+
!collapsed && /* @__PURE__ */ jsx11("span", { className: "truncate", children: item.label })
|
|
1312
|
+
]
|
|
1313
|
+
}
|
|
1314
|
+
) }, item.href)) }),
|
|
1315
|
+
footer,
|
|
1316
|
+
onToggleCollapse && /* @__PURE__ */ jsx11("div", { className: "border-t border-surface-border p-2", children: /* @__PURE__ */ jsxs11(
|
|
1317
|
+
"button",
|
|
1318
|
+
{
|
|
1319
|
+
type: "button",
|
|
1320
|
+
onClick: onToggleCollapse,
|
|
1321
|
+
className: cn(
|
|
1322
|
+
"flex w-full items-center gap-3 rounded-button px-3 py-2 text-sm text-text-tertiary",
|
|
1323
|
+
"hover:bg-surface-hover hover:text-text transition-colors",
|
|
1324
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
1325
|
+
collapsed && "justify-center px-2"
|
|
1326
|
+
),
|
|
1327
|
+
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1328
|
+
children: [
|
|
1329
|
+
/* @__PURE__ */ jsx11(
|
|
1330
|
+
"svg",
|
|
1331
|
+
{
|
|
1332
|
+
width: "16",
|
|
1333
|
+
height: "16",
|
|
1334
|
+
viewBox: "0 0 16 16",
|
|
1335
|
+
fill: "none",
|
|
1336
|
+
className: cn(
|
|
1337
|
+
"shrink-0 transition-transform duration-200",
|
|
1338
|
+
collapsed && "rotate-180"
|
|
1339
|
+
),
|
|
1340
|
+
children: /* @__PURE__ */ jsx11(
|
|
1341
|
+
"path",
|
|
1342
|
+
{
|
|
1343
|
+
d: "M10 3L5 8l5 5",
|
|
1344
|
+
stroke: "currentColor",
|
|
1345
|
+
strokeWidth: "1.5",
|
|
1346
|
+
strokeLinecap: "round",
|
|
1347
|
+
strokeLinejoin: "round"
|
|
1348
|
+
}
|
|
1349
|
+
)
|
|
1350
|
+
}
|
|
1351
|
+
),
|
|
1352
|
+
!collapsed && /* @__PURE__ */ jsx11("span", { children: "Collapse" })
|
|
1353
|
+
]
|
|
1354
|
+
}
|
|
1355
|
+
) })
|
|
1356
|
+
]
|
|
1357
|
+
}
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/components/page-header.tsx
|
|
1362
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1363
|
+
function PageHeader({
|
|
1364
|
+
title,
|
|
1365
|
+
description,
|
|
1366
|
+
breadcrumbs,
|
|
1367
|
+
actions,
|
|
1368
|
+
className
|
|
1369
|
+
}) {
|
|
1370
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn("space-y-1", className), children: [
|
|
1371
|
+
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx12("nav", { "aria-label": "Breadcrumb", className: "mb-2", children: /* @__PURE__ */ jsx12("ol", { className: "flex items-center gap-1 text-xs text-text-tertiary", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsxs12("li", { className: "flex items-center gap-1", children: [
|
|
1372
|
+
index > 0 && /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "text-text-muted", children: "/" }),
|
|
1373
|
+
crumb.href ? /* @__PURE__ */ jsx12(
|
|
1374
|
+
"a",
|
|
1375
|
+
{
|
|
1376
|
+
href: crumb.href,
|
|
1377
|
+
className: "transition-colors hover:text-text-secondary",
|
|
1378
|
+
children: crumb.label
|
|
1379
|
+
}
|
|
1380
|
+
) : /* @__PURE__ */ jsx12("span", { className: "text-text-secondary", children: crumb.label })
|
|
1381
|
+
] }, index)) }) }),
|
|
1382
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-start justify-between gap-4", children: [
|
|
1383
|
+
/* @__PURE__ */ jsxs12("div", { className: "min-w-0", children: [
|
|
1384
|
+
/* @__PURE__ */ jsx12("h1", { className: "text-2xl font-semibold text-text", children: title }),
|
|
1385
|
+
description && /* @__PURE__ */ jsx12("p", { className: "mt-1 text-sm text-text-secondary", children: description })
|
|
1386
|
+
] }),
|
|
1387
|
+
actions && /* @__PURE__ */ jsx12("div", { className: "flex flex-shrink-0 items-center gap-2", children: actions })
|
|
1388
|
+
] })
|
|
1389
|
+
] });
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// src/components/button.tsx
|
|
1393
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
1394
|
+
import { cva } from "class-variance-authority";
|
|
1395
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1396
|
+
var buttonVariants = cva(
|
|
1397
|
+
"inline-flex items-center justify-center rounded-button font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent disabled:pointer-events-none disabled:opacity-50",
|
|
1398
|
+
{
|
|
1399
|
+
variants: {
|
|
1400
|
+
variant: {
|
|
1401
|
+
primary: "bg-primary text-primary-foreground font-display uppercase tracking-wider hover:opacity-90",
|
|
1402
|
+
secondary: "border-2 border-foreground bg-transparent text-foreground hover:bg-foreground hover:text-background",
|
|
1403
|
+
ghost: "bg-transparent text-foreground hover:bg-muted"
|
|
1404
|
+
},
|
|
1405
|
+
size: {
|
|
1406
|
+
sm: "h-8 px-3 text-xs",
|
|
1407
|
+
md: "h-10 px-5 text-sm",
|
|
1408
|
+
lg: "h-12 px-8 text-base"
|
|
1409
|
+
}
|
|
1410
|
+
},
|
|
1411
|
+
defaultVariants: {
|
|
1412
|
+
variant: "primary",
|
|
1413
|
+
size: "md"
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
);
|
|
1417
|
+
function Button({
|
|
1418
|
+
className,
|
|
1419
|
+
variant,
|
|
1420
|
+
size,
|
|
1421
|
+
asChild = false,
|
|
1422
|
+
...props
|
|
1423
|
+
}) {
|
|
1424
|
+
const Comp = asChild ? Slot : "button";
|
|
1425
|
+
return /* @__PURE__ */ jsx13(
|
|
1426
|
+
Comp,
|
|
1427
|
+
{
|
|
1428
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
1429
|
+
...props
|
|
1430
|
+
}
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// src/components/badge.tsx
|
|
1435
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
1436
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1437
|
+
var badgeVariants = cva2(
|
|
1438
|
+
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors",
|
|
1439
|
+
{
|
|
1440
|
+
variants: {
|
|
1441
|
+
variant: {
|
|
1442
|
+
default: "bg-foreground text-background",
|
|
1443
|
+
primary: "bg-primary text-primary-foreground",
|
|
1444
|
+
outline: "border-2 border-foreground text-foreground bg-transparent",
|
|
1445
|
+
muted: "bg-muted text-muted-foreground"
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
defaultVariants: {
|
|
1449
|
+
variant: "default"
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
);
|
|
1453
|
+
function Badge({ className, variant, ...props }) {
|
|
1454
|
+
return /* @__PURE__ */ jsx14("span", { className: cn(badgeVariants({ variant, className })), ...props });
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
// src/components/card.tsx
|
|
1458
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
1459
|
+
function CardHeader({ className, ...props }) {
|
|
1460
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("flex flex-col gap-1.5 p-6 pb-0", className), ...props });
|
|
1461
|
+
}
|
|
1462
|
+
function CardTitle({ className, ...props }) {
|
|
1463
|
+
return /* @__PURE__ */ jsx15(
|
|
1464
|
+
"h3",
|
|
1465
|
+
{
|
|
1466
|
+
className: cn("font-display text-xl uppercase tracking-wider text-card-foreground", className),
|
|
1467
|
+
...props
|
|
1468
|
+
}
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
function CardDescription({ className, ...props }) {
|
|
1472
|
+
return /* @__PURE__ */ jsx15("p", { className: cn("text-sm text-muted-foreground", className), ...props });
|
|
1473
|
+
}
|
|
1474
|
+
function CardContent({ className, ...props }) {
|
|
1475
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("p-6", className), ...props });
|
|
1476
|
+
}
|
|
1477
|
+
function CardFooter({ className, ...props }) {
|
|
1478
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("flex items-center p-6 pt-0", className), ...props });
|
|
1479
|
+
}
|
|
1480
|
+
function CardRoot({ className, ...props }) {
|
|
1481
|
+
return /* @__PURE__ */ jsx15(
|
|
1482
|
+
"div",
|
|
1483
|
+
{
|
|
1484
|
+
className: cn(
|
|
1485
|
+
"rounded-card border-2 border-foreground/40 bg-card text-card-foreground",
|
|
1486
|
+
className
|
|
1487
|
+
),
|
|
1488
|
+
...props
|
|
1489
|
+
}
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
var Card = Object.assign(CardRoot, {
|
|
1493
|
+
Header: CardHeader,
|
|
1494
|
+
Title: CardTitle,
|
|
1495
|
+
Description: CardDescription,
|
|
1496
|
+
Content: CardContent,
|
|
1497
|
+
Footer: CardFooter
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
// src/components/section.tsx
|
|
1501
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
1502
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1503
|
+
var sectionVariants = cva3("py-16 md:py-24", {
|
|
1504
|
+
variants: {
|
|
1505
|
+
variant: {
|
|
1506
|
+
default: "",
|
|
1507
|
+
muted: "bg-muted/30",
|
|
1508
|
+
bordered: "border-y-2 border-foreground/40"
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
defaultVariants: {
|
|
1512
|
+
variant: "default"
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
function Section({
|
|
1516
|
+
className,
|
|
1517
|
+
variant,
|
|
1518
|
+
noContainer = false,
|
|
1519
|
+
children,
|
|
1520
|
+
...props
|
|
1521
|
+
}) {
|
|
1522
|
+
return /* @__PURE__ */ jsx16("section", { className: cn(sectionVariants({ variant }), className), ...props, children: noContainer ? children : /* @__PURE__ */ jsx16("div", { className: "mx-auto max-w-7xl px-6 lg:px-8", children }) });
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// src/components/stat-card.tsx
|
|
1526
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1527
|
+
function StatCard({ value, label, notched = false, className, ...props }) {
|
|
1528
|
+
return /* @__PURE__ */ jsxs13(
|
|
1529
|
+
"div",
|
|
1530
|
+
{
|
|
1531
|
+
className: cn(
|
|
1532
|
+
"relative overflow-hidden rounded-card bg-foreground p-6 text-background",
|
|
1533
|
+
className
|
|
1534
|
+
),
|
|
1535
|
+
...props,
|
|
1536
|
+
children: [
|
|
1537
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute top-2 left-2 h-1 w-1 rounded-full bg-background/40" }),
|
|
1538
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute top-2 right-2 h-1 w-1 rounded-full bg-background/40" }),
|
|
1539
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute bottom-2 left-2 h-1 w-1 rounded-full bg-background/40" }),
|
|
1540
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute bottom-2 right-2 h-1 w-1 rounded-full bg-background/40" }),
|
|
1541
|
+
notched && /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
1542
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute left-0 top-1/2 h-5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-background" }),
|
|
1543
|
+
/* @__PURE__ */ jsx17("span", { className: "absolute right-0 top-1/2 h-5 w-2.5 translate-x-1/2 -translate-y-1/2 rounded-full bg-background" })
|
|
1544
|
+
] }),
|
|
1545
|
+
/* @__PURE__ */ jsx17("div", { className: "font-display text-4xl uppercase tracking-wider", children: value }),
|
|
1546
|
+
/* @__PURE__ */ jsx17("div", { className: "mt-1 text-sm uppercase tracking-wider text-background/70", children: label })
|
|
1547
|
+
]
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// src/components/testimonial-card.tsx
|
|
1553
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1554
|
+
function TestimonialCard({
|
|
1555
|
+
quote,
|
|
1556
|
+
author,
|
|
1557
|
+
role,
|
|
1558
|
+
avatarUrl,
|
|
1559
|
+
rating,
|
|
1560
|
+
className,
|
|
1561
|
+
...props
|
|
1562
|
+
}) {
|
|
1563
|
+
return /* @__PURE__ */ jsxs14(
|
|
1564
|
+
"div",
|
|
1565
|
+
{
|
|
1566
|
+
className: cn(
|
|
1567
|
+
"rounded-card border-2 border-foreground/40 p-6",
|
|
1568
|
+
className
|
|
1569
|
+
),
|
|
1570
|
+
...props,
|
|
1571
|
+
children: [
|
|
1572
|
+
/* @__PURE__ */ jsx18("span", { className: "font-display text-4xl leading-none text-primary", "aria-hidden": "true", children: "\u201C" }),
|
|
1573
|
+
rating && /* @__PURE__ */ jsx18("div", { className: "mb-3 flex gap-0.5", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx18(
|
|
1574
|
+
"svg",
|
|
1575
|
+
{
|
|
1576
|
+
className: cn("h-4 w-4", i < rating ? "text-primary" : "text-muted"),
|
|
1577
|
+
viewBox: "0 0 20 20",
|
|
1578
|
+
fill: "currentColor",
|
|
1579
|
+
children: /* @__PURE__ */ jsx18("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" })
|
|
1580
|
+
},
|
|
1581
|
+
i
|
|
1582
|
+
)) }),
|
|
1583
|
+
/* @__PURE__ */ jsx18("blockquote", { className: "text-sm leading-relaxed text-foreground", children: quote }),
|
|
1584
|
+
/* @__PURE__ */ jsxs14("div", { className: "mt-4 flex items-center gap-3", children: [
|
|
1585
|
+
avatarUrl && /* @__PURE__ */ jsx18(
|
|
1586
|
+
"img",
|
|
1587
|
+
{
|
|
1588
|
+
src: avatarUrl,
|
|
1589
|
+
alt: author,
|
|
1590
|
+
className: "h-10 w-10 rounded-full border-2 border-foreground/20 object-cover"
|
|
1591
|
+
}
|
|
1592
|
+
),
|
|
1593
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
1594
|
+
/* @__PURE__ */ jsx18("div", { className: "text-sm font-semibold text-foreground", children: author }),
|
|
1595
|
+
role && /* @__PURE__ */ jsx18("div", { className: "text-xs text-muted-foreground", children: role })
|
|
1596
|
+
] })
|
|
1597
|
+
] })
|
|
1598
|
+
]
|
|
1599
|
+
}
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// src/components/pricing-card.tsx
|
|
1604
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1605
|
+
function PricingCard({
|
|
1606
|
+
name,
|
|
1607
|
+
price,
|
|
1608
|
+
unit = "/mo",
|
|
1609
|
+
description,
|
|
1610
|
+
features,
|
|
1611
|
+
ctaLabel = "Get Started",
|
|
1612
|
+
ctaHref,
|
|
1613
|
+
onCtaClick,
|
|
1614
|
+
featured = false,
|
|
1615
|
+
badge,
|
|
1616
|
+
className,
|
|
1617
|
+
...props
|
|
1618
|
+
}) {
|
|
1619
|
+
return /* @__PURE__ */ jsxs15(
|
|
1620
|
+
"div",
|
|
1621
|
+
{
|
|
1622
|
+
className: cn(
|
|
1623
|
+
"relative flex flex-col rounded-card border-2 p-6",
|
|
1624
|
+
featured ? "border-primary bg-foreground text-background" : "border-foreground/40",
|
|
1625
|
+
className
|
|
1626
|
+
),
|
|
1627
|
+
...props,
|
|
1628
|
+
children: [
|
|
1629
|
+
/* @__PURE__ */ jsxs15("div", { className: "mb-4 flex items-center justify-between", children: [
|
|
1630
|
+
/* @__PURE__ */ jsx19("h3", { className: "font-display text-xl uppercase tracking-wider", children: name }),
|
|
1631
|
+
badge && /* @__PURE__ */ jsx19(Badge, { variant: featured ? "primary" : "default", children: badge })
|
|
1632
|
+
] }),
|
|
1633
|
+
/* @__PURE__ */ jsxs15("div", { className: "mb-4", children: [
|
|
1634
|
+
/* @__PURE__ */ jsx19("span", { className: "font-display text-4xl", children: price }),
|
|
1635
|
+
unit && /* @__PURE__ */ jsx19("span", { className: cn("text-sm", featured ? "text-background/70" : "text-muted-foreground"), children: unit })
|
|
1636
|
+
] }),
|
|
1637
|
+
description && /* @__PURE__ */ jsx19("div", { className: cn(
|
|
1638
|
+
"mb-6 border-y-2 py-3 text-sm",
|
|
1639
|
+
featured ? "border-background/20" : "border-foreground/20"
|
|
1640
|
+
), children: description }),
|
|
1641
|
+
/* @__PURE__ */ jsx19("ul", { className: "mb-6 flex-1 space-y-2", children: features.map((feature) => /* @__PURE__ */ jsxs15("li", { className: "flex items-start gap-2 text-sm", children: [
|
|
1642
|
+
/* @__PURE__ */ jsx19(
|
|
1643
|
+
"svg",
|
|
1644
|
+
{
|
|
1645
|
+
className: cn("mt-0.5 h-4 w-4 shrink-0", featured ? "text-primary" : "text-primary"),
|
|
1646
|
+
viewBox: "0 0 20 20",
|
|
1647
|
+
fill: "currentColor",
|
|
1648
|
+
children: /* @__PURE__ */ jsx19("path", { d: "M10 2l2.39 4.843L17.5 7.72l-3.75 3.657.885 5.163L10 14.058 5.365 16.54l.885-5.163L2.5 7.72l5.11-.877L10 2z" })
|
|
1649
|
+
}
|
|
1650
|
+
),
|
|
1651
|
+
/* @__PURE__ */ jsx19("span", { children: feature })
|
|
1652
|
+
] }, feature)) }),
|
|
1653
|
+
/* @__PURE__ */ jsx19(
|
|
1654
|
+
Button,
|
|
1655
|
+
{
|
|
1656
|
+
variant: featured ? "primary" : "secondary",
|
|
1657
|
+
className: "w-full",
|
|
1658
|
+
onClick: onCtaClick,
|
|
1659
|
+
...ctaHref ? { asChild: true } : {},
|
|
1660
|
+
children: ctaHref ? /* @__PURE__ */ jsx19("a", { href: ctaHref, children: ctaLabel }) : ctaLabel
|
|
1661
|
+
}
|
|
1662
|
+
)
|
|
1663
|
+
]
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// src/components/logo-grid.tsx
|
|
1669
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1670
|
+
var colsMap = {
|
|
1671
|
+
3: "grid-cols-3",
|
|
1672
|
+
4: "grid-cols-2 sm:grid-cols-4",
|
|
1673
|
+
5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
|
|
1674
|
+
6: "grid-cols-3 sm:grid-cols-6"
|
|
1675
|
+
};
|
|
1676
|
+
function LogoGrid({
|
|
1677
|
+
logos,
|
|
1678
|
+
columns = 4,
|
|
1679
|
+
className,
|
|
1680
|
+
...props
|
|
1681
|
+
}) {
|
|
1682
|
+
return /* @__PURE__ */ jsx20(
|
|
1683
|
+
"div",
|
|
1684
|
+
{
|
|
1685
|
+
className: cn("grid items-center gap-8", colsMap[columns], className),
|
|
1686
|
+
...props,
|
|
1687
|
+
children: logos.map((logo) => {
|
|
1688
|
+
const img = /* @__PURE__ */ jsx20(
|
|
1689
|
+
"img",
|
|
1690
|
+
{
|
|
1691
|
+
src: logo.src,
|
|
1692
|
+
alt: logo.alt,
|
|
1693
|
+
className: "h-10 w-auto object-contain grayscale transition-all duration-300 hover:grayscale-0 hover:scale-110"
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
return /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center", children: logo.href ? /* @__PURE__ */ jsx20("a", { href: logo.href, target: "_blank", rel: "noopener noreferrer", children: img }) : img }, logo.alt);
|
|
1697
|
+
})
|
|
1698
|
+
}
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// src/components/footer.tsx
|
|
1703
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1704
|
+
function Footer({
|
|
1705
|
+
columns,
|
|
1706
|
+
bottomContent,
|
|
1707
|
+
className,
|
|
1708
|
+
...props
|
|
1709
|
+
}) {
|
|
1710
|
+
return /* @__PURE__ */ jsx21("footer", { className: cn("bg-foreground text-background", className), ...props, children: /* @__PURE__ */ jsxs16("div", { className: "mx-auto max-w-7xl px-6 py-12 lg:px-8", children: [
|
|
1711
|
+
/* @__PURE__ */ jsx21("div", { className: "grid gap-8 sm:grid-cols-2 lg:grid-cols-4", children: columns.map((col) => /* @__PURE__ */ jsxs16("div", { children: [
|
|
1712
|
+
/* @__PURE__ */ jsx21("h4", { className: "font-display text-sm uppercase tracking-wider text-background/70", children: col.heading }),
|
|
1713
|
+
/* @__PURE__ */ jsx21("ul", { className: "mt-3 space-y-2", children: col.links.map((link) => /* @__PURE__ */ jsx21("li", { children: /* @__PURE__ */ jsx21(
|
|
1714
|
+
"a",
|
|
1715
|
+
{
|
|
1716
|
+
href: link.href,
|
|
1717
|
+
className: "text-sm text-background/80 transition-colors hover:text-background",
|
|
1718
|
+
children: link.label
|
|
1719
|
+
}
|
|
1720
|
+
) }, link.label)) })
|
|
1721
|
+
] }, col.heading)) }),
|
|
1722
|
+
/* @__PURE__ */ jsx21("div", { className: "mt-10 border-t-2 border-background/20 pt-6", children: bottomContent ?? /* @__PURE__ */ jsxs16("p", { className: "text-xs text-background/50", children: [
|
|
1723
|
+
"\xA9 ",
|
|
1724
|
+
(/* @__PURE__ */ new Date()).getFullYear(),
|
|
1725
|
+
" All rights reserved."
|
|
1726
|
+
] }) })
|
|
1727
|
+
] }) });
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// src/components/social-card.tsx
|
|
1731
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1732
|
+
function SocialCard({ width = 1200, height = 630 }) {
|
|
1733
|
+
return /* @__PURE__ */ jsxs17(
|
|
1734
|
+
"div",
|
|
1735
|
+
{
|
|
1736
|
+
style: {
|
|
1737
|
+
width,
|
|
1738
|
+
height,
|
|
1739
|
+
background: "#0e0e0e",
|
|
1740
|
+
color: "#e6ddd0",
|
|
1741
|
+
fontFamily: "'Bebas Neue', Impact, sans-serif",
|
|
1742
|
+
display: "flex",
|
|
1743
|
+
flexDirection: "column",
|
|
1744
|
+
justifyContent: "space-between",
|
|
1745
|
+
padding: 64,
|
|
1746
|
+
position: "relative",
|
|
1747
|
+
overflow: "hidden"
|
|
1748
|
+
},
|
|
1749
|
+
children: [
|
|
1750
|
+
[
|
|
1751
|
+
{ top: 24, left: 24 },
|
|
1752
|
+
{ top: 24, right: 24 },
|
|
1753
|
+
{ bottom: 24, left: 24 },
|
|
1754
|
+
{ bottom: 24, right: 24 }
|
|
1755
|
+
].map((pos, i) => /* @__PURE__ */ jsx22(
|
|
1756
|
+
"span",
|
|
1757
|
+
{
|
|
1758
|
+
style: {
|
|
1759
|
+
position: "absolute",
|
|
1760
|
+
...pos,
|
|
1761
|
+
width: 6,
|
|
1762
|
+
height: 6,
|
|
1763
|
+
borderRadius: "50%",
|
|
1764
|
+
background: "rgba(230,221,208,0.3)"
|
|
1765
|
+
}
|
|
1766
|
+
},
|
|
1767
|
+
i
|
|
1768
|
+
)),
|
|
1769
|
+
/* @__PURE__ */ jsx22(
|
|
1770
|
+
"div",
|
|
1771
|
+
{
|
|
1772
|
+
style: {
|
|
1773
|
+
position: "absolute",
|
|
1774
|
+
inset: 16,
|
|
1775
|
+
border: "2px solid rgba(230,221,208,0.15)",
|
|
1776
|
+
borderRadius: 2,
|
|
1777
|
+
pointerEvents: "none"
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
),
|
|
1781
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
1782
|
+
/* @__PURE__ */ jsxs17("div", { style: { display: "flex", alignItems: "center", gap: 16, marginBottom: 48 }, children: [
|
|
1783
|
+
/* @__PURE__ */ jsx22(
|
|
1784
|
+
"div",
|
|
1785
|
+
{
|
|
1786
|
+
style: {
|
|
1787
|
+
width: 48,
|
|
1788
|
+
height: 48,
|
|
1789
|
+
background: "#fcd34d",
|
|
1790
|
+
borderRadius: 4,
|
|
1791
|
+
display: "flex",
|
|
1792
|
+
alignItems: "center",
|
|
1793
|
+
justifyContent: "center",
|
|
1794
|
+
fontSize: 28,
|
|
1795
|
+
lineHeight: 1
|
|
1796
|
+
},
|
|
1797
|
+
children: "\u{1F969}"
|
|
1798
|
+
}
|
|
1799
|
+
),
|
|
1800
|
+
/* @__PURE__ */ jsx22(
|
|
1801
|
+
"span",
|
|
1802
|
+
{
|
|
1803
|
+
style: {
|
|
1804
|
+
fontSize: 24,
|
|
1805
|
+
letterSpacing: "0.1em",
|
|
1806
|
+
textTransform: "uppercase",
|
|
1807
|
+
color: "rgba(230,221,208,0.5)",
|
|
1808
|
+
fontFamily: "'Bebas Neue', Impact, sans-serif"
|
|
1809
|
+
},
|
|
1810
|
+
children: "Lambchop"
|
|
1811
|
+
}
|
|
1812
|
+
)
|
|
1813
|
+
] }),
|
|
1814
|
+
/* @__PURE__ */ jsxs17(
|
|
1815
|
+
"h1",
|
|
1816
|
+
{
|
|
1817
|
+
style: {
|
|
1818
|
+
fontSize: 96,
|
|
1819
|
+
lineHeight: 1,
|
|
1820
|
+
textTransform: "uppercase",
|
|
1821
|
+
letterSpacing: "0.05em",
|
|
1822
|
+
margin: 0,
|
|
1823
|
+
fontWeight: 400
|
|
1824
|
+
},
|
|
1825
|
+
children: [
|
|
1826
|
+
"Design",
|
|
1827
|
+
/* @__PURE__ */ jsx22("br", {}),
|
|
1828
|
+
"System"
|
|
1829
|
+
]
|
|
1830
|
+
}
|
|
1831
|
+
)
|
|
1832
|
+
] }),
|
|
1833
|
+
/* @__PURE__ */ jsxs17("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end" }, children: [
|
|
1834
|
+
/* @__PURE__ */ jsx22("div", { style: { display: "flex", gap: 32 }, children: [
|
|
1835
|
+
{ value: "24", label: "Components" },
|
|
1836
|
+
{ value: "3", label: "Font Families" },
|
|
1837
|
+
{ value: "9", label: "Chart Colors" }
|
|
1838
|
+
].map((stat) => /* @__PURE__ */ jsxs17("div", { children: [
|
|
1839
|
+
/* @__PURE__ */ jsx22(
|
|
1840
|
+
"div",
|
|
1841
|
+
{
|
|
1842
|
+
style: {
|
|
1843
|
+
fontSize: 40,
|
|
1844
|
+
letterSpacing: "0.05em",
|
|
1845
|
+
color: "#fcd34d",
|
|
1846
|
+
fontFamily: "'Bebas Neue', Impact, sans-serif"
|
|
1847
|
+
},
|
|
1848
|
+
children: stat.value
|
|
1849
|
+
}
|
|
1850
|
+
),
|
|
1851
|
+
/* @__PURE__ */ jsx22(
|
|
1852
|
+
"div",
|
|
1853
|
+
{
|
|
1854
|
+
style: {
|
|
1855
|
+
fontSize: 11,
|
|
1856
|
+
letterSpacing: "0.15em",
|
|
1857
|
+
textTransform: "uppercase",
|
|
1858
|
+
color: "rgba(230,221,208,0.5)",
|
|
1859
|
+
fontFamily: "'DM Sans', sans-serif"
|
|
1860
|
+
},
|
|
1861
|
+
children: stat.label
|
|
1862
|
+
}
|
|
1863
|
+
)
|
|
1864
|
+
] }, stat.label)) }),
|
|
1865
|
+
/* @__PURE__ */ jsx22(
|
|
1866
|
+
"div",
|
|
1867
|
+
{
|
|
1868
|
+
style: {
|
|
1869
|
+
width: 80,
|
|
1870
|
+
height: 4,
|
|
1871
|
+
background: "#fcd34d",
|
|
1872
|
+
borderRadius: 2
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
)
|
|
1876
|
+
] })
|
|
1877
|
+
]
|
|
1878
|
+
}
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// src/hooks/use-intersection-observer.ts
|
|
1883
|
+
import { useRef as useRef2, useState as useState5, useEffect as useEffect2, useCallback } from "react";
|
|
1884
|
+
function useIntersectionObserver({
|
|
1885
|
+
threshold = 0.1,
|
|
1886
|
+
rootMargin = "0px",
|
|
1887
|
+
triggerOnce = true
|
|
1888
|
+
} = {}) {
|
|
1889
|
+
const [isIntersecting, setIsIntersecting] = useState5(false);
|
|
1890
|
+
const elementRef = useRef2(null);
|
|
1891
|
+
const observerRef = useRef2(null);
|
|
1892
|
+
const ref = useCallback(
|
|
1893
|
+
(node) => {
|
|
1894
|
+
if (observerRef.current) {
|
|
1895
|
+
observerRef.current.disconnect();
|
|
1896
|
+
observerRef.current = null;
|
|
1897
|
+
}
|
|
1898
|
+
elementRef.current = node;
|
|
1899
|
+
if (!node) return;
|
|
1900
|
+
observerRef.current = new IntersectionObserver(
|
|
1901
|
+
([entry]) => {
|
|
1902
|
+
if (entry.isIntersecting) {
|
|
1903
|
+
setIsIntersecting(true);
|
|
1904
|
+
if (triggerOnce && observerRef.current) {
|
|
1905
|
+
observerRef.current.disconnect();
|
|
1906
|
+
}
|
|
1907
|
+
} else if (!triggerOnce) {
|
|
1908
|
+
setIsIntersecting(false);
|
|
1909
|
+
}
|
|
1910
|
+
},
|
|
1911
|
+
{ threshold, rootMargin }
|
|
1912
|
+
);
|
|
1913
|
+
observerRef.current.observe(node);
|
|
1914
|
+
},
|
|
1915
|
+
[threshold, rootMargin, triggerOnce]
|
|
1916
|
+
);
|
|
1917
|
+
useEffect2(() => {
|
|
1918
|
+
return () => {
|
|
1919
|
+
observerRef.current?.disconnect();
|
|
1920
|
+
};
|
|
1921
|
+
}, []);
|
|
1922
|
+
return { ref, isIntersecting };
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
// src/components/fade-in.tsx
|
|
1926
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1927
|
+
var directionMap = {
|
|
1928
|
+
up: "animate-fade-in-up",
|
|
1929
|
+
down: "animate-fade-in-down",
|
|
1930
|
+
left: "animate-fade-in-left",
|
|
1931
|
+
right: "animate-fade-in-right"
|
|
1932
|
+
};
|
|
1933
|
+
function FadeIn({
|
|
1934
|
+
direction = "up",
|
|
1935
|
+
delay = 0,
|
|
1936
|
+
className,
|
|
1937
|
+
children,
|
|
1938
|
+
...props
|
|
1939
|
+
}) {
|
|
1940
|
+
const { ref, isIntersecting } = useIntersectionObserver({ triggerOnce: true });
|
|
1941
|
+
return /* @__PURE__ */ jsx23(
|
|
1942
|
+
"div",
|
|
1943
|
+
{
|
|
1944
|
+
ref,
|
|
1945
|
+
className: cn(
|
|
1946
|
+
isIntersecting ? directionMap[direction] : "opacity-0",
|
|
1947
|
+
className
|
|
1948
|
+
),
|
|
1949
|
+
style: delay ? { animationDelay: `${delay}ms` } : void 0,
|
|
1950
|
+
...props,
|
|
1951
|
+
children
|
|
1952
|
+
}
|
|
1953
|
+
);
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// src/components/scale-in.tsx
|
|
1957
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1958
|
+
function ScaleIn({
|
|
1959
|
+
delay = 0,
|
|
1960
|
+
className,
|
|
1961
|
+
children,
|
|
1962
|
+
...props
|
|
1963
|
+
}) {
|
|
1964
|
+
const { ref, isIntersecting } = useIntersectionObserver({ triggerOnce: true });
|
|
1965
|
+
return /* @__PURE__ */ jsx24(
|
|
1966
|
+
"div",
|
|
1967
|
+
{
|
|
1968
|
+
ref,
|
|
1969
|
+
className: cn(
|
|
1970
|
+
isIntersecting ? "animate-scale-in" : "opacity-0",
|
|
1971
|
+
className
|
|
1972
|
+
),
|
|
1973
|
+
style: delay ? { animationDelay: `${delay}ms` } : void 0,
|
|
1974
|
+
...props,
|
|
1975
|
+
children
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// src/components/stagger-container.tsx
|
|
1981
|
+
import * as React6 from "react";
|
|
1982
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1983
|
+
function StaggerContainer({
|
|
1984
|
+
stagger = 100,
|
|
1985
|
+
className,
|
|
1986
|
+
children,
|
|
1987
|
+
...props
|
|
1988
|
+
}) {
|
|
1989
|
+
const { ref, isIntersecting } = useIntersectionObserver({ triggerOnce: true });
|
|
1990
|
+
return /* @__PURE__ */ jsx25("div", { ref, className: cn(className), ...props, children: React6.Children.map(children, (child, index) => {
|
|
1991
|
+
if (!React6.isValidElement(child)) return child;
|
|
1992
|
+
return React6.cloneElement(child, {
|
|
1993
|
+
style: {
|
|
1994
|
+
...child.props.style,
|
|
1995
|
+
transitionDelay: `${index * stagger}ms`,
|
|
1996
|
+
opacity: isIntersecting ? 1 : 0,
|
|
1997
|
+
transform: isIntersecting ? "translateY(0)" : "translateY(1rem)",
|
|
1998
|
+
transition: `opacity 0.5s ease-out, transform 0.5s ease-out`
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
}) });
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// src/hooks/use-time-range.ts
|
|
2005
|
+
import { useState as useState6, useMemo, useCallback as useCallback2 } from "react";
|
|
2006
|
+
import {
|
|
2007
|
+
startOfMonth as startOfMonth2,
|
|
2008
|
+
endOfMonth as endOfMonth2,
|
|
2009
|
+
subMonths as subMonths2,
|
|
2010
|
+
subDays as subDays2,
|
|
2011
|
+
startOfYear as startOfYear2,
|
|
2012
|
+
format as format2
|
|
2013
|
+
} from "date-fns";
|
|
2014
|
+
function resolvePreset2(preset) {
|
|
2015
|
+
const now = /* @__PURE__ */ new Date();
|
|
2016
|
+
switch (preset) {
|
|
2017
|
+
case "mtd":
|
|
2018
|
+
return { start: startOfMonth2(now), end: now };
|
|
2019
|
+
case "last-month": {
|
|
2020
|
+
const prev = subMonths2(now, 1);
|
|
2021
|
+
return { start: startOfMonth2(prev), end: endOfMonth2(prev) };
|
|
2022
|
+
}
|
|
2023
|
+
case "last-30":
|
|
2024
|
+
return { start: subDays2(now, 30), end: now };
|
|
2025
|
+
case "last-90":
|
|
2026
|
+
return { start: subDays2(now, 90), end: now };
|
|
2027
|
+
case "ytd":
|
|
2028
|
+
return { start: startOfYear2(now), end: now };
|
|
2029
|
+
case "custom":
|
|
2030
|
+
return { start: subDays2(now, 30), end: now };
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function useTimeRange(initialPreset = "mtd") {
|
|
2034
|
+
const [preset, setPresetState] = useState6(initialPreset);
|
|
2035
|
+
const [customRange, setCustomRange] = useState6(null);
|
|
2036
|
+
const range = useMemo(() => {
|
|
2037
|
+
if (preset === "custom" && customRange) {
|
|
2038
|
+
return { ...customRange, preset };
|
|
2039
|
+
}
|
|
2040
|
+
return { ...resolvePreset2(preset), preset };
|
|
2041
|
+
}, [preset, customRange]);
|
|
2042
|
+
const setPreset = useCallback2((p) => {
|
|
2043
|
+
setPresetState(p);
|
|
2044
|
+
if (p !== "custom") setCustomRange(null);
|
|
2045
|
+
}, []);
|
|
2046
|
+
const setCustom = useCallback2((start, end) => {
|
|
2047
|
+
setPresetState("custom");
|
|
2048
|
+
setCustomRange({ start, end });
|
|
2049
|
+
}, []);
|
|
2050
|
+
const formatted = useMemo(
|
|
2051
|
+
() => ({
|
|
2052
|
+
start: format2(range.start, "yyyy-MM-dd"),
|
|
2053
|
+
end: format2(range.end, "yyyy-MM-dd")
|
|
2054
|
+
}),
|
|
2055
|
+
[range]
|
|
2056
|
+
);
|
|
2057
|
+
return { range, preset, setPreset, setCustom, formatted };
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
// src/hooks/use-table-sort.ts
|
|
2061
|
+
import { useState as useState7, useCallback as useCallback3 } from "react";
|
|
2062
|
+
function useTableSort(initialSort = []) {
|
|
2063
|
+
const [sorting, setSorting] = useState7(initialSort);
|
|
2064
|
+
const toggleSort = useCallback3((columnId) => {
|
|
2065
|
+
setSorting((prev) => {
|
|
2066
|
+
const existing = prev.find((s) => s.id === columnId);
|
|
2067
|
+
if (!existing) return [{ id: columnId, desc: true }];
|
|
2068
|
+
if (existing.desc) return [{ id: columnId, desc: false }];
|
|
2069
|
+
return [];
|
|
2070
|
+
});
|
|
2071
|
+
}, []);
|
|
2072
|
+
return { sorting, setSorting, toggleSort };
|
|
2073
|
+
}
|
|
2074
|
+
export {
|
|
2075
|
+
AreaChart,
|
|
2076
|
+
Badge,
|
|
2077
|
+
BarChart,
|
|
2078
|
+
Button,
|
|
2079
|
+
Card,
|
|
2080
|
+
ComparisonRow,
|
|
2081
|
+
DataTable,
|
|
2082
|
+
DateRangePicker,
|
|
2083
|
+
FadeIn,
|
|
2084
|
+
FilterBar,
|
|
2085
|
+
Footer,
|
|
2086
|
+
KpiCard,
|
|
2087
|
+
LineChart,
|
|
2088
|
+
LogoGrid,
|
|
2089
|
+
MetricTrend,
|
|
2090
|
+
PageHeader,
|
|
2091
|
+
PricingCard,
|
|
2092
|
+
ScaleIn,
|
|
2093
|
+
Section,
|
|
2094
|
+
SidebarNav,
|
|
2095
|
+
SocialCard,
|
|
2096
|
+
Sparkline,
|
|
2097
|
+
StaggerContainer,
|
|
2098
|
+
StatCard,
|
|
2099
|
+
TestimonialCard,
|
|
2100
|
+
cn,
|
|
2101
|
+
useChartColors,
|
|
2102
|
+
useIntersectionObserver,
|
|
2103
|
+
useTableSort,
|
|
2104
|
+
useTimeRange
|
|
2105
|
+
};
|
|
2106
|
+
//# sourceMappingURL=index.js.map
|