@timbal-ai/timbal-react 1.5.0 → 1.6.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/CHANGELOG.md +32 -1
- package/README.md +33 -0
- package/dist/app.cjs +884 -642
- package/dist/app.d.cts +4 -4
- package/dist/app.d.ts +4 -4
- package/dist/app.esm.js +6 -6
- package/dist/{chart-artifact-2OTDTRwM.d.ts → chart-artifact-BYl5C-dk.d.ts} +90 -29
- package/dist/{chart-artifact-CS3qyGIY.d.cts → chart-artifact-Dpt4t5sf.d.cts} +90 -29
- package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
- package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
- package/dist/chat.cjs +26 -26
- package/dist/chat.d.cts +3 -3
- package/dist/chat.d.ts +3 -3
- package/dist/chat.esm.js +3 -3
- package/dist/{chunk-TZI3ID3C.esm.js → chunk-24B4I4XC.esm.js} +3 -3
- package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
- package/dist/{chunk-WMKPT5BV.esm.js → chunk-HSL36SJ4.esm.js} +6 -6
- package/dist/chunk-JJOO4PR5.esm.js +391 -0
- package/dist/{chunk-AZL2WANO.esm.js → chunk-MBS7XHV2.esm.js} +28 -28
- package/dist/{chunk-5ECRZ5O7.esm.js → chunk-NO5AWNWT.esm.js} +224 -57
- package/dist/{chunk-ZNYAETFD.esm.js → chunk-R4RQT2XQ.esm.js} +2 -2
- package/dist/{chunk-JYDJRGDE.esm.js → chunk-TMP7RIA7.esm.js} +2 -2
- package/dist/{chunk-SZDYIRMB.esm.js → chunk-UVPXH4MB.esm.js} +647 -532
- package/dist/{chunk-IGHBLJV3.esm.js → chunk-WQIQW7EM.esm.js} +3 -2
- package/dist/{chunk-B4XAC4G7.esm.js → chunk-YYEI6XME.esm.js} +361 -527
- package/dist/{circular-progress-CDsJwIPF.d.cts → circular-progress-B9nnwzCu.d.cts} +1 -1
- package/dist/{circular-progress-CDsJwIPF.d.ts → circular-progress-B9nnwzCu.d.ts} +1 -1
- package/dist/cli/timbal-ui-lint.mjs +503 -0
- package/dist/index.cjs +1358 -856
- package/dist/index.d.cts +9 -8
- package/dist/index.d.ts +9 -8
- package/dist/index.esm.js +40 -20
- package/dist/{kanban-U5xNe9py.d.cts → kanban-FFBeaZPS.d.cts} +4 -4
- package/dist/{kanban-U5xNe9py.d.ts → kanban-FFBeaZPS.d.ts} +4 -4
- package/dist/{layout-B8r6Jbat.d.ts → layout-CuKeSY74.d.ts} +1 -1
- package/dist/{layout-Cu7Ijn04.d.cts → layout-PzVwkJyL.d.cts} +1 -1
- package/dist/site.cjs +71 -0
- package/dist/site.d.cts +15 -1
- package/dist/site.d.ts +15 -1
- package/dist/site.esm.js +12 -311
- package/dist/studio.cjs +31 -31
- package/dist/studio.d.cts +2 -2
- package/dist/studio.d.ts +2 -2
- package/dist/studio.esm.js +7 -7
- package/dist/{timbal-v2-button-B7vPs7gg.d.ts → timbal-v2-button-DCAZNyUx.d.cts} +1 -1
- package/dist/{timbal-v2-button-B7vPs7gg.d.cts → timbal-v2-button-DCAZNyUx.d.ts} +1 -1
- package/dist/ui.cjs +77 -77
- package/dist/ui.d.cts +3 -3
- package/dist/ui.d.ts +3 -3
- package/dist/ui.esm.js +15 -15
- package/dist/{welcome-NXZlcihe.d.cts → welcome-B00oH5Io.d.cts} +1 -1
- package/dist/{welcome-DduQAC4K.d.ts → welcome-DU-4NTjZ.d.ts} +1 -1
- package/package.json +13 -3
package/dist/app.cjs
CHANGED
|
@@ -318,7 +318,11 @@ var HOUSE_RULES = [
|
|
|
318
318
|
{
|
|
319
319
|
id: "compose-from-blocks",
|
|
320
320
|
rule: "Build from premade blocks (MetricRow, MetricChartCard, DataTable, IntegrationCard). Drop to raw primitives only when no block fits.",
|
|
321
|
-
why: "Slop appears the moment generation falls below the curated block layer."
|
|
321
|
+
why: "Slop appears the moment generation falls below the curated block layer.",
|
|
322
|
+
// "Should have used a block" is a judgement about absence, not a textual
|
|
323
|
+
// pattern — no high-precision deterministic check exists, so this stays
|
|
324
|
+
// prompt-only rather than risk false-positives blocking valid UIs.
|
|
325
|
+
enforcement: "prompt-only"
|
|
322
326
|
},
|
|
323
327
|
{
|
|
324
328
|
id: "use-kit-controls",
|
|
@@ -399,7 +403,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
|
|
|
399
403
|
- Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
|
|
400
404
|
|
|
401
405
|
\`\`\`tsx
|
|
402
|
-
<AppShell contentFill topbar
|
|
406
|
+
<AppShell contentFill> {/* no global topbar / theme switch */}
|
|
403
407
|
<Page fill> {/* headerless: omit title */}
|
|
404
408
|
<TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
|
|
405
409
|
</Page>
|
|
@@ -419,7 +423,7 @@ Presentational groups \u2014 import from the package root, not from these paths:
|
|
|
419
423
|
|
|
420
424
|
| Folder | Components |
|
|
421
425
|
|--------|------------|
|
|
422
|
-
| \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
|
|
426
|
+
| \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`FilterDropdown\`, \`ChartPanel\` |
|
|
423
427
|
| \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
|
|
424
428
|
| \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
|
|
425
429
|
| \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
|
|
@@ -488,6 +492,7 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
488
492
|
| \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
|
|
489
493
|
| \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
|
|
490
494
|
| \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
|
|
495
|
+
| \`FilterDropdown\` | Single-button **multi-facet** filter popover for dense list/table views \u2014 **data-driven**: pass \`fields\` describing your **actual columns** (each \`{ id, label, type }\` where \`type\` is \`multiselect\` \\| \`text\` \\| \`daterange\` \\| \`numeric\`; \`multiselect\` takes \`options: [{ value, label, hint?, icon? }]\`). State is keyed by field \`id\` \u2014 controlled (\`value\` + \`onChange\`) or uncontrolled (\`defaultValue\`). Renders **removable active-filter pills** next to the trigger by default (\`showActiveChips\`); wire \`onChange\` to actually filter your rows. **Always derive \`fields\` from the table's columns/data; never ship the default example facets.** Use when one \`FilterBar\` row isn't enough. |
|
|
491
496
|
| \`SearchInput\` | Filter field with consistent app styling. |
|
|
492
497
|
| \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\`, \`truncate: true\` on columns with long text. **Scales:** \`pageSize\` (built-in client pager), \`selectable\` + \`onSelectionChange\` (checkbox column for bulk actions), \`loading\` (skeleton rows). \`onRowClick\` for row \u2192 detail (open a \`Sheet\`). |
|
|
493
498
|
| \`Avatar\` / \`AvatarFallback\` | User initials: \`variant="secondary"\` (or \`primary\` / \`chart\` alias) on **both** \`Avatar\` and \`AvatarFallback\` \u2014 same chrome as catalog **Action** buttons (\`Button variant="secondary"\`: elevated gradient, \`border-border\`, \`shadow-card\`, \`text-foreground\`). Never dark primary CTA fill or raw \`bg-blue-600\`. |
|
|
@@ -502,6 +507,8 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
502
507
|
|
|
503
508
|
Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipContent\` chrome (see \`src/ui/chart.tsx\`). Series colors default to \`--chart-1..6\`; override those CSS tokens to rebrand every chart.
|
|
504
509
|
|
|
510
|
+
> **React 19 requirement \u2014 do not hand-roll SVG charts to work around this.** recharts under React 19 crashes (\`Cannot assign to read only property 'lanes'\`, blank route) when \`immer\` resolves to **11.0.0**. The fix is a dependency override in the app's \`package.json\` \u2014 \`"overrides": { "immer": ">=11.0.1" }\` (Yarn: \`"resolutions"\`) \u2014 **not** a code change. Always keep using \`LineAreaChart\` / \`PieChart\` / \`ChartPanel\`; never replace them with raw SVG/CSS charts.
|
|
511
|
+
|
|
505
512
|
| Component | Use for |
|
|
506
513
|
|-----------|---------|
|
|
507
514
|
| \`LineAreaChart\` | Cartesian engine (shadcn-style chrome). Bar fills use theme gradients automatically. Props: \`data\`, \`xKey\`, \`series: [{ dataKey, label?, color? }]\`, \`variant\` (\`area\`\\|\`line\`\\|\`bar\`), \`orientation\` (\`horizontal\` for horizontal bars), \`stacked\`, \`curve\`, \`dots\`, \`gridLines\`, \`tooltipIndicator\`, \`layout\` (\`flush\` \u2014 hides axes by default; category + values on hover tooltip), \`showXAxis\` / \`showYAxis\` to opt back in, \`clipTicks\` (truncates long axis labels when axes are on), \`height\`, \`showLegend\`, \`formatX\`, \`formatValue\`, \`ariaLabel\`. |
|
|
@@ -546,6 +553,21 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
|
|
|
546
553
|
| \`Banner\` | Page-level announcement bar: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`), \`icon\`, \`title\`, body as children, right-aligned \`actions\`, \`onDismiss\` (renders the dismiss X). For in-form/field messages use \`InfoCard\` or \`Alert\` instead. |
|
|
547
554
|
| \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
|
|
548
555
|
|
|
556
|
+
#### More \`/ui\` primitives (import from \`/ui\` or the package root)
|
|
557
|
+
|
|
558
|
+
These ship in the same design system but aren't re-exported from \`/app\`. Reach for them before hand-rolling \u2014 they're all dependency-free and on the shared tokens / control surface.
|
|
559
|
+
|
|
560
|
+
| Component | Use for |
|
|
561
|
+
|-----------|---------|
|
|
562
|
+
| \`Stepper\` | Ordered step indicator for wizards / onboarding (horizontal or vertical; complete / active / upcoming states). |
|
|
563
|
+
| \`Rating\` | Star rating \u2014 interactive (keyboard + hover preview) or \`readOnly\`; controlled or uncontrolled. |
|
|
564
|
+
| \`NumberField\` | Numeric input with \u2212/+ steppers on the control surface; clamps to \`min\`/\`max\`, steps by \`step\`. |
|
|
565
|
+
| \`TagInput\` | Chips / token input; commits on Enter/comma, removes on Backspace, optional \`dedupe\`/\`max\`. |
|
|
566
|
+
| \`AvatarGroup\` | Overlapping avatar stack with an optional \`+N\` overflow chip (\`max\`, \`spacing\`). |
|
|
567
|
+
| \`CircularProgress\` | Lightweight SVG progress ring \u2014 determinate (optional center label) or indeterminate. |
|
|
568
|
+
| \`CopyButton\` | Click-to-copy with a transient check confirmation; icon-only or with a label. |
|
|
569
|
+
| \`Snippet\` | Single-line code / command on the elevated surface with a built-in copy button. |
|
|
570
|
+
|
|
549
571
|
Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
|
|
550
572
|
|
|
551
573
|
### Block recipes \u2014 compose these (don't clone wholesale)
|
|
@@ -612,6 +634,7 @@ import {
|
|
|
612
634
|
DataTable,
|
|
613
635
|
FilterBar,
|
|
614
636
|
FilterField,
|
|
637
|
+
FilterDropdown,
|
|
615
638
|
AlertCard,
|
|
616
639
|
CatalogCard,
|
|
617
640
|
} from "@timbal-ai/timbal-react/app";
|
|
@@ -666,6 +689,8 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
|
666
689
|
var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
|
|
667
690
|
var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
|
|
668
691
|
var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
|
|
692
|
+
var TREND_CONTEXT_RE = /\b(?:trend|delta|TrendingUp|TrendingDown|ArrowUp|ArrowDown|ArrowUpRight|ArrowDownRight|MoveUp|MoveDown)\b|[+\-]\d+(?:\.\d+)?\s*%/;
|
|
693
|
+
var TREND_COLOR_RE = /\b(?:text|bg|border)-(?:success|destructive|emerald|green|lime|teal|red|rose|orange|amber)(?:-\d{2,3})?(?:\/\d{1,3})?\b/;
|
|
669
694
|
var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
|
|
670
695
|
function stripVariants(util) {
|
|
671
696
|
return util.replace(/^(?:[a-z-]+:)*/, "");
|
|
@@ -709,6 +734,16 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
709
734
|
if (cardMatch) {
|
|
710
735
|
const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
|
|
711
736
|
if (!isSelfClosing) {
|
|
737
|
+
if (openCards.length > 0) {
|
|
738
|
+
const parentCard = openCards[openCards.length - 1];
|
|
739
|
+
findings.push({
|
|
740
|
+
rule: "no-card-in-card",
|
|
741
|
+
severity: "warn",
|
|
742
|
+
line: lineNo,
|
|
743
|
+
message: `Card inside card. A <${cardMatch[1]}> is nested inside the <${parentCard.type}> opened on L${parentCard.line}. Double borders/shadows add no information \u2014 group with spacing or a <Section> instead.`,
|
|
744
|
+
snippet: line.trim().slice(0, 120)
|
|
745
|
+
});
|
|
746
|
+
}
|
|
712
747
|
openCards.push({ type: cardMatch[1], line: lineNo });
|
|
713
748
|
}
|
|
714
749
|
}
|
|
@@ -781,6 +816,15 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
781
816
|
snippet: line.trim().slice(0, 120)
|
|
782
817
|
});
|
|
783
818
|
}
|
|
819
|
+
if (TREND_CONTEXT_RE.test(line) && TREND_COLOR_RE.test(line)) {
|
|
820
|
+
findings.push({
|
|
821
|
+
rule: "neutral-trend",
|
|
822
|
+
severity: "warn",
|
|
823
|
+
line: lineNo,
|
|
824
|
+
message: "Colored trend indicator. House style: don't tint deltas green/red on every metric \u2014 show a trend only when the change is the point, and keep it muted (text-muted-foreground).",
|
|
825
|
+
snippet: line.trim().slice(0, 120)
|
|
826
|
+
});
|
|
827
|
+
}
|
|
784
828
|
if (BOLD_VALUE_RE.test(line)) {
|
|
785
829
|
findings.push({
|
|
786
830
|
rule: "bold-metric",
|
|
@@ -1357,8 +1401,8 @@ var SIDEBAR_WIDTH_COLLAPSED_PX = 52;
|
|
|
1357
1401
|
var SIDEBAR_GAP_PX = 12;
|
|
1358
1402
|
var SIDEBAR_CONTENT_GAP_PX = 8;
|
|
1359
1403
|
var TOPBAR_GAP_PX = 8;
|
|
1360
|
-
var TOPBAR_HEIGHT_PX =
|
|
1361
|
-
var PILL_HEIGHT_PX =
|
|
1404
|
+
var TOPBAR_HEIGHT_PX = 44;
|
|
1405
|
+
var PILL_HEIGHT_PX = 36;
|
|
1362
1406
|
var SIDEBAR_INSET_PX_EXPANDED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_PX + SIDEBAR_CONTENT_GAP_PX;
|
|
1363
1407
|
var SIDEBAR_INSET_PX_COLLAPSED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_COLLAPSED_PX + SIDEBAR_CONTENT_GAP_PX;
|
|
1364
1408
|
var px = (n) => `${n / 16}rem`;
|
|
@@ -1728,16 +1772,16 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
|
1728
1772
|
)
|
|
1729
1773
|
},
|
|
1730
1774
|
size: {
|
|
1731
|
-
xs: "h-
|
|
1732
|
-
sm: "h-
|
|
1733
|
-
md: "h-
|
|
1734
|
-
lg: "h-
|
|
1735
|
-
xl: "h-
|
|
1736
|
-
default: "h-
|
|
1737
|
-
icon: "h-
|
|
1738
|
-
"icon-xs": "h-
|
|
1739
|
-
"icon-sm": "h-
|
|
1740
|
-
"icon-lg": "h-
|
|
1775
|
+
xs: "h-7 gap-1 rounded-md px-2 text-xs",
|
|
1776
|
+
sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
|
|
1777
|
+
md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
|
|
1778
|
+
lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
|
|
1779
|
+
xl: "h-11 gap-2 rounded-lg px-4 text-base",
|
|
1780
|
+
default: "h-9 gap-1.5 rounded-lg px-3 text-sm",
|
|
1781
|
+
icon: "h-9 w-9 rounded-lg",
|
|
1782
|
+
"icon-xs": "h-7 w-7 rounded-md",
|
|
1783
|
+
"icon-sm": "h-8 w-8 rounded-md",
|
|
1784
|
+
"icon-lg": "h-10 w-10 rounded-lg"
|
|
1741
1785
|
},
|
|
1742
1786
|
shape: {
|
|
1743
1787
|
pill: "rounded-full!",
|
|
@@ -1831,22 +1875,22 @@ var TIMBAL_V2_MODAL_SURFACE = cn(
|
|
|
1831
1875
|
);
|
|
1832
1876
|
var TIMBAL_V2_PRIMARY_GRADIENT = "bg-gradient-to-b from-primary-fill-from to-primary-fill-to";
|
|
1833
1877
|
var TIMBAL_V2_SIZE_HEIGHT = {
|
|
1834
|
-
xs: "min-h-
|
|
1835
|
-
sm: "min-h-
|
|
1836
|
-
md: "min-h-
|
|
1837
|
-
lg: "min-h-
|
|
1878
|
+
xs: "min-h-7 h-7",
|
|
1879
|
+
sm: "min-h-8 h-8",
|
|
1880
|
+
md: "min-h-9 h-9",
|
|
1881
|
+
lg: "min-h-10 h-10"
|
|
1838
1882
|
};
|
|
1839
1883
|
var TIMBAL_V2_SIZE_ICON = {
|
|
1840
|
-
xs: "min-h-
|
|
1884
|
+
xs: "min-h-7 min-w-7 size-7",
|
|
1841
1885
|
sm: "min-h-8 min-w-8 size-8",
|
|
1842
|
-
md: "min-h-
|
|
1843
|
-
lg: "min-h-
|
|
1886
|
+
md: "min-h-9 min-w-9 size-9",
|
|
1887
|
+
lg: "min-h-10 min-w-10 size-10"
|
|
1844
1888
|
};
|
|
1845
1889
|
var TIMBAL_V2_SIZE_LABEL_PX = {
|
|
1846
|
-
xs: "px-
|
|
1847
|
-
sm: "px-
|
|
1848
|
-
md: "px-5",
|
|
1849
|
-
lg: "px-
|
|
1890
|
+
xs: "px-2.5",
|
|
1891
|
+
sm: "px-3",
|
|
1892
|
+
md: "px-3.5",
|
|
1893
|
+
lg: "px-4.5"
|
|
1850
1894
|
};
|
|
1851
1895
|
var TIMBAL_V2_FILL = {
|
|
1852
1896
|
primary: [
|
|
@@ -2071,8 +2115,8 @@ var import_react = require("react");
|
|
|
2071
2115
|
|
|
2072
2116
|
// src/design/control-surface.ts
|
|
2073
2117
|
var CONTROL_SIZE = {
|
|
2074
|
-
sm: "h-
|
|
2075
|
-
default: "h-
|
|
2118
|
+
sm: "h-8 px-2.5",
|
|
2119
|
+
default: "h-9 px-3"
|
|
2076
2120
|
};
|
|
2077
2121
|
var CONTROL_SHAPE = {
|
|
2078
2122
|
field: "rounded-lg",
|
|
@@ -2100,6 +2144,7 @@ var overlayListPanelClass = cn(
|
|
|
2100
2144
|
overlaySurfaceClass,
|
|
2101
2145
|
"overflow-hidden rounded-lg p-0 outline-hidden"
|
|
2102
2146
|
);
|
|
2147
|
+
var overlayItemClass = "relative flex cursor-default items-center gap-2 rounded-md px-2 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground";
|
|
2103
2148
|
|
|
2104
2149
|
// src/design/app-classes.ts
|
|
2105
2150
|
var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
|
|
@@ -2394,30 +2439,36 @@ var Sparkline = ({
|
|
|
2394
2439
|
height = 28,
|
|
2395
2440
|
strokeWidth = 1.5,
|
|
2396
2441
|
className,
|
|
2397
|
-
ariaLabel = "Trend"
|
|
2442
|
+
ariaLabel = "Trend",
|
|
2443
|
+
interactive = false,
|
|
2444
|
+
labels,
|
|
2445
|
+
formatValue
|
|
2398
2446
|
}) => {
|
|
2399
2447
|
const uid = (0, import_react2.useId)();
|
|
2448
|
+
const containerRef = (0, import_react2.useRef)(null);
|
|
2449
|
+
const [activeIndex, setActiveIndex] = (0, import_react2.useState)(null);
|
|
2400
2450
|
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
2401
2451
|
if (values.length === 0) {
|
|
2402
2452
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: cn("inline-block", className), style: { width, height } });
|
|
2403
2453
|
}
|
|
2404
|
-
const
|
|
2454
|
+
const padX = 0;
|
|
2455
|
+
const padY = strokeWidth + 1;
|
|
2405
2456
|
const min = Math.min(...values);
|
|
2406
2457
|
const max = Math.max(...values);
|
|
2407
2458
|
const range = max - min || 1;
|
|
2408
|
-
const innerW = width -
|
|
2409
|
-
const innerH = height -
|
|
2459
|
+
const innerW = width - padX * 2;
|
|
2460
|
+
const innerH = height - padY * 2;
|
|
2410
2461
|
const points = values.map((v, i) => ({
|
|
2411
|
-
x:
|
|
2412
|
-
y:
|
|
2462
|
+
x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
2463
|
+
y: padY + innerH - (v - min) / range * innerH
|
|
2413
2464
|
}));
|
|
2414
|
-
|
|
2465
|
+
const svg = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2415
2466
|
"svg",
|
|
2416
2467
|
{
|
|
2417
2468
|
width,
|
|
2418
2469
|
height,
|
|
2419
2470
|
viewBox: `0 0 ${width} ${height}`,
|
|
2420
|
-
className: cn("block", className),
|
|
2471
|
+
className: cn("block", interactive ? "h-full w-full" : className),
|
|
2421
2472
|
role: "img",
|
|
2422
2473
|
"aria-label": ariaLabel,
|
|
2423
2474
|
preserveAspectRatio: "none",
|
|
@@ -2427,7 +2478,7 @@ var Sparkline = ({
|
|
|
2427
2478
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
2428
2479
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
2429
2480
|
] }) }),
|
|
2430
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: monotoneAreaPath(points, height -
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: monotoneAreaPath(points, height - padY), fill: `url(#${uid}-spark)` })
|
|
2431
2482
|
] }),
|
|
2432
2483
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2433
2484
|
"path",
|
|
@@ -2439,7 +2490,81 @@ var Sparkline = ({
|
|
|
2439
2490
|
strokeLinecap: "round",
|
|
2440
2491
|
strokeLinejoin: "round"
|
|
2441
2492
|
}
|
|
2442
|
-
)
|
|
2493
|
+
),
|
|
2494
|
+
interactive && activeIndex != null && points[activeIndex] ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2496
|
+
"line",
|
|
2497
|
+
{
|
|
2498
|
+
x1: points[activeIndex].x,
|
|
2499
|
+
x2: points[activeIndex].x,
|
|
2500
|
+
y1: 0,
|
|
2501
|
+
y2: height,
|
|
2502
|
+
stroke: color,
|
|
2503
|
+
strokeWidth: 1,
|
|
2504
|
+
strokeOpacity: 0.3,
|
|
2505
|
+
vectorEffect: "non-scaling-stroke"
|
|
2506
|
+
}
|
|
2507
|
+
),
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2509
|
+
"circle",
|
|
2510
|
+
{
|
|
2511
|
+
cx: points[activeIndex].x,
|
|
2512
|
+
cy: points[activeIndex].y,
|
|
2513
|
+
r: 2.75,
|
|
2514
|
+
fill: color,
|
|
2515
|
+
stroke: "var(--background, #fff)",
|
|
2516
|
+
strokeWidth: 1.5,
|
|
2517
|
+
vectorEffect: "non-scaling-stroke"
|
|
2518
|
+
}
|
|
2519
|
+
)
|
|
2520
|
+
] }) : null
|
|
2521
|
+
]
|
|
2522
|
+
}
|
|
2523
|
+
);
|
|
2524
|
+
if (!interactive) return svg;
|
|
2525
|
+
const onMove = (e) => {
|
|
2526
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2527
|
+
if (rect.width === 0) return;
|
|
2528
|
+
const fraction = (e.clientX - rect.left) / rect.width;
|
|
2529
|
+
const index = Math.max(
|
|
2530
|
+
0,
|
|
2531
|
+
Math.min(values.length - 1, Math.round(fraction * (values.length - 1)))
|
|
2532
|
+
);
|
|
2533
|
+
setActiveIndex(index);
|
|
2534
|
+
};
|
|
2535
|
+
const active = activeIndex != null ? points[activeIndex] : null;
|
|
2536
|
+
const formattedValue = activeIndex != null ? formatValue ? formatValue(values[activeIndex], activeIndex) : formatCompact(values[activeIndex]) : null;
|
|
2537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2538
|
+
"span",
|
|
2539
|
+
{
|
|
2540
|
+
ref: containerRef,
|
|
2541
|
+
className: cn("relative block touch-none", className),
|
|
2542
|
+
style: { width: "100%", height: "100%" },
|
|
2543
|
+
onPointerMove: onMove,
|
|
2544
|
+
onPointerLeave: () => setActiveIndex(null),
|
|
2545
|
+
children: [
|
|
2546
|
+
svg,
|
|
2547
|
+
active ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2548
|
+
"span",
|
|
2549
|
+
{
|
|
2550
|
+
"aria-hidden": true,
|
|
2551
|
+
className: cn(
|
|
2552
|
+
"pointer-events-none absolute z-30 -translate-x-1/2 -translate-y-full whitespace-nowrap",
|
|
2553
|
+
"rounded-xl border px-3 py-2 text-[11px] font-medium leading-none tabular-nums shadow-[0_12px_40px_-10px_rgba(0,0,0,0.55)]",
|
|
2554
|
+
"border-white/10 bg-gradient-to-b from-neutral-800 to-neutral-950 text-white",
|
|
2555
|
+
"dark:border-black/10 dark:from-white dark:to-neutral-100 dark:text-neutral-900"
|
|
2556
|
+
),
|
|
2557
|
+
style: {
|
|
2558
|
+
left: `${active.x / width * 100}%`,
|
|
2559
|
+
top: `${active.y / height * 100}%`,
|
|
2560
|
+
marginTop: -8
|
|
2561
|
+
},
|
|
2562
|
+
children: [
|
|
2563
|
+
labels?.[activeIndex] != null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mr-1.5 text-neutral-300 dark:text-neutral-500", children: labels[activeIndex] }) : null,
|
|
2564
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: formattedValue })
|
|
2565
|
+
]
|
|
2566
|
+
}
|
|
2567
|
+
) : null
|
|
2443
2568
|
]
|
|
2444
2569
|
}
|
|
2445
2570
|
);
|
|
@@ -2457,6 +2582,15 @@ var inlineTrendToneClass = {
|
|
|
2457
2582
|
down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
|
|
2458
2583
|
neutral: "text-muted-foreground/80"
|
|
2459
2584
|
};
|
|
2585
|
+
var sparklineToneColor = {
|
|
2586
|
+
up: "var(--primary, #3b82f6)",
|
|
2587
|
+
down: "var(--destructive, #f43f5e)",
|
|
2588
|
+
neutral: "var(--muted-foreground, #64748b)"
|
|
2589
|
+
};
|
|
2590
|
+
var sparklineBandBleed = {
|
|
2591
|
+
default: "-mx-4 -mb-3 h-10",
|
|
2592
|
+
compact: "-mx-3 -mb-2 h-8"
|
|
2593
|
+
};
|
|
2460
2594
|
var activeToneClass = {
|
|
2461
2595
|
default: "bg-foreground dark:bg-white",
|
|
2462
2596
|
primary: "bg-primary",
|
|
@@ -2486,8 +2620,10 @@ var MetricTile = ({
|
|
|
2486
2620
|
ariaLabel,
|
|
2487
2621
|
className
|
|
2488
2622
|
}) => {
|
|
2623
|
+
const density = useAppDensity();
|
|
2489
2624
|
const metricTileBaseClass = useAppDensityClass("metricTile");
|
|
2490
2625
|
const hasSparkline = Boolean(sparkline || sparklineData);
|
|
2626
|
+
const bandBleed = sparklineBandBleed[density === "compact" ? "compact" : "default"];
|
|
2491
2627
|
const content = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2492
2628
|
active ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2493
2629
|
"span",
|
|
@@ -2499,17 +2635,6 @@ var MetricTile = ({
|
|
|
2499
2635
|
)
|
|
2500
2636
|
}
|
|
2501
2637
|
) : null,
|
|
2502
|
-
hasSparkline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "absolute inset-x-0 bottom-0.5 h-9 w-full overflow-hidden pointer-events-none z-0 opacity-45 dark:opacity-35 select-none", children: sparkline ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2503
|
-
Sparkline,
|
|
2504
|
-
{
|
|
2505
|
-
data: sparklineData,
|
|
2506
|
-
width: 160,
|
|
2507
|
-
height: 36,
|
|
2508
|
-
className: "w-full h-full",
|
|
2509
|
-
color: trendTone === "up" ? "var(--primary, #3b82f6)" : trendTone === "down" ? "var(--destructive, #f43f5e)" : "var(--muted-foreground, #64748b)",
|
|
2510
|
-
...sparklineConfig
|
|
2511
|
-
}
|
|
2512
|
-
) }) : null,
|
|
2513
2638
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
|
|
2514
2639
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
|
|
2515
2640
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
@@ -2528,7 +2653,28 @@ var MetricTile = ({
|
|
|
2528
2653
|
}
|
|
2529
2654
|
) : null
|
|
2530
2655
|
] })
|
|
2531
|
-
] })
|
|
2656
|
+
] }),
|
|
2657
|
+
hasSparkline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2658
|
+
"div",
|
|
2659
|
+
{
|
|
2660
|
+
className: cn(
|
|
2661
|
+
"relative z-10 mt-2",
|
|
2662
|
+
bandBleed
|
|
2663
|
+
),
|
|
2664
|
+
children: sparkline ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2665
|
+
Sparkline,
|
|
2666
|
+
{
|
|
2667
|
+
data: sparklineData,
|
|
2668
|
+
width: 160,
|
|
2669
|
+
height: 40,
|
|
2670
|
+
interactive: true,
|
|
2671
|
+
className: "h-full w-full opacity-90",
|
|
2672
|
+
color: sparklineToneColor[trendTone],
|
|
2673
|
+
...sparklineConfig
|
|
2674
|
+
}
|
|
2675
|
+
)
|
|
2676
|
+
}
|
|
2677
|
+
) : null
|
|
2532
2678
|
] });
|
|
2533
2679
|
const divider = showDivider ? metricCellDividerClass : void 0;
|
|
2534
2680
|
if (onSelect) {
|
|
@@ -3277,7 +3423,7 @@ function DialogContent({
|
|
|
3277
3423
|
"data-slot": "dialog-content",
|
|
3278
3424
|
className: cn(
|
|
3279
3425
|
TIMBAL_V2_MODAL_SURFACE,
|
|
3280
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-[70] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl p-
|
|
3426
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-[70] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl p-5 duration-200 outline-none sm:max-w-lg",
|
|
3281
3427
|
className
|
|
3282
3428
|
),
|
|
3283
3429
|
...props,
|
|
@@ -3335,9 +3481,9 @@ function isBrandedVariant(variant) {
|
|
|
3335
3481
|
return variant === "secondary" || variant === "primary" || variant === "chart";
|
|
3336
3482
|
}
|
|
3337
3483
|
var AVATAR_SIZE_CLASS = {
|
|
3338
|
-
default: "size-
|
|
3339
|
-
sm: "size-
|
|
3340
|
-
lg: "size-
|
|
3484
|
+
default: "size-7",
|
|
3485
|
+
sm: "size-5",
|
|
3486
|
+
lg: "size-9"
|
|
3341
3487
|
};
|
|
3342
3488
|
function Avatar({
|
|
3343
3489
|
className,
|
|
@@ -5274,7 +5420,7 @@ var BadgeNode = ({ node }) => {
|
|
|
5274
5420
|
"span",
|
|
5275
5421
|
{
|
|
5276
5422
|
className: cn(
|
|
5277
|
-
"aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
|
|
5423
|
+
"aui-ui-badge inline-flex w-fit shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium",
|
|
5278
5424
|
BADGE_TONE[node.tone ?? "default"],
|
|
5279
5425
|
node.className
|
|
5280
5426
|
),
|
|
@@ -7935,7 +8081,7 @@ var StatusBadge = ({
|
|
|
7935
8081
|
"span",
|
|
7936
8082
|
{
|
|
7937
8083
|
className: cn(
|
|
7938
|
-
"aui-app-status-badge inline-flex items-center gap-1.5 rounded-full px-2 py-0.5",
|
|
8084
|
+
"aui-app-status-badge inline-flex w-fit shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5",
|
|
7939
8085
|
"text-xs font-medium leading-none ring-1 ring-inset",
|
|
7940
8086
|
statusBadgeToneClass[tone],
|
|
7941
8087
|
className
|
|
@@ -8394,7 +8540,7 @@ function CopyButton({
|
|
|
8394
8540
|
"inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium text-muted-foreground transition-colors",
|
|
8395
8541
|
"hover:bg-accent hover:text-foreground data-[copied=true]:text-emerald-600 dark:data-[copied=true]:text-emerald-400",
|
|
8396
8542
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
|
|
8397
|
-
children ? "h-
|
|
8543
|
+
children ? "h-7 px-1.5" : "size-7",
|
|
8398
8544
|
className
|
|
8399
8545
|
),
|
|
8400
8546
|
...props,
|
|
@@ -8925,10 +9071,10 @@ var pillSegmentedTrackFlushClass = cn(
|
|
|
8925
9071
|
"h-[var(--studio-chrome-pill-height)] items-center gap-0.5 overflow-visible p-0.5"
|
|
8926
9072
|
);
|
|
8927
9073
|
var pillSegmentedSegmentClass = cn(
|
|
8928
|
-
"relative flex items-center gap-1.5 rounded-full px-
|
|
9074
|
+
"relative flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-normal transition-colors"
|
|
8929
9075
|
);
|
|
8930
9076
|
var pillSegmentedFlushSegmentClass = cn(
|
|
8931
|
-
"relative box-border inline-flex h-
|
|
9077
|
+
"relative box-border inline-flex h-8 min-h-8 items-center justify-center gap-1.5 rounded-full px-3 py-0",
|
|
8932
9078
|
"text-sm font-normal leading-tight transition-colors"
|
|
8933
9079
|
);
|
|
8934
9080
|
var pillSegmentedActiveIndicatorClass = cn(
|
|
@@ -9328,7 +9474,7 @@ var FilterField = ({
|
|
|
9328
9474
|
|
|
9329
9475
|
// src/app/data/FilterDropdown.tsx
|
|
9330
9476
|
var import_react53 = require("react");
|
|
9331
|
-
var
|
|
9477
|
+
var import_lucide_react18 = require("lucide-react");
|
|
9332
9478
|
|
|
9333
9479
|
// src/ui/checkbox.tsx
|
|
9334
9480
|
var import_radix_ui6 = require("radix-ui");
|
|
@@ -9361,18 +9507,142 @@ function Checkbox({
|
|
|
9361
9507
|
);
|
|
9362
9508
|
}
|
|
9363
9509
|
|
|
9364
|
-
// src/ui/
|
|
9510
|
+
// src/ui/select.tsx
|
|
9365
9511
|
var import_radix_ui7 = require("radix-ui");
|
|
9512
|
+
var import_lucide_react17 = require("lucide-react");
|
|
9366
9513
|
var import_jsx_runtime86 = require("react/jsx-runtime");
|
|
9514
|
+
function Select({
|
|
9515
|
+
...props
|
|
9516
|
+
}) {
|
|
9517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Root, { "data-slot": "select", ...props });
|
|
9518
|
+
}
|
|
9519
|
+
function SelectValue({
|
|
9520
|
+
...props
|
|
9521
|
+
}) {
|
|
9522
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Value, { "data-slot": "select-value", ...props });
|
|
9523
|
+
}
|
|
9524
|
+
function SelectTrigger({
|
|
9525
|
+
className,
|
|
9526
|
+
size = "default",
|
|
9527
|
+
children,
|
|
9528
|
+
...props
|
|
9529
|
+
}) {
|
|
9530
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
|
|
9531
|
+
import_radix_ui7.Select.Trigger,
|
|
9532
|
+
{
|
|
9533
|
+
"data-slot": "select-trigger",
|
|
9534
|
+
"data-size": size,
|
|
9535
|
+
className: cn(
|
|
9536
|
+
controlClass({ size }),
|
|
9537
|
+
"flex w-fit items-center justify-between gap-2 whitespace-nowrap *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
9538
|
+
className
|
|
9539
|
+
),
|
|
9540
|
+
...props,
|
|
9541
|
+
children: [
|
|
9542
|
+
children,
|
|
9543
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
9544
|
+
]
|
|
9545
|
+
}
|
|
9546
|
+
);
|
|
9547
|
+
}
|
|
9548
|
+
function SelectContent({
|
|
9549
|
+
className,
|
|
9550
|
+
children,
|
|
9551
|
+
position = "popper",
|
|
9552
|
+
...props
|
|
9553
|
+
}) {
|
|
9554
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
|
|
9555
|
+
import_radix_ui7.Select.Content,
|
|
9556
|
+
{
|
|
9557
|
+
"data-slot": "select-content",
|
|
9558
|
+
className: cn(
|
|
9559
|
+
overlayListPanelClass,
|
|
9560
|
+
"relative max-h-[var(--radix-select-content-available-height)] min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-x-hidden overflow-y-auto",
|
|
9561
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
9562
|
+
className
|
|
9563
|
+
),
|
|
9564
|
+
position,
|
|
9565
|
+
...props,
|
|
9566
|
+
children: [
|
|
9567
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SelectScrollUpButton, {}),
|
|
9568
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
9569
|
+
import_radix_ui7.Select.Viewport,
|
|
9570
|
+
{
|
|
9571
|
+
className: cn(
|
|
9572
|
+
"p-1",
|
|
9573
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
9574
|
+
),
|
|
9575
|
+
children
|
|
9576
|
+
}
|
|
9577
|
+
),
|
|
9578
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SelectScrollDownButton, {})
|
|
9579
|
+
]
|
|
9580
|
+
}
|
|
9581
|
+
) });
|
|
9582
|
+
}
|
|
9583
|
+
function SelectItem({
|
|
9584
|
+
className,
|
|
9585
|
+
children,
|
|
9586
|
+
...props
|
|
9587
|
+
}) {
|
|
9588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
|
|
9589
|
+
import_radix_ui7.Select.Item,
|
|
9590
|
+
{
|
|
9591
|
+
"data-slot": "select-item",
|
|
9592
|
+
className: cn(
|
|
9593
|
+
overlayItemClass,
|
|
9594
|
+
"w-full py-1 pr-8 pl-2 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
9595
|
+
className
|
|
9596
|
+
),
|
|
9597
|
+
...props,
|
|
9598
|
+
children: [
|
|
9599
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.CheckIcon, { className: "size-4" }) }) }),
|
|
9600
|
+
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.ItemText, { children })
|
|
9601
|
+
]
|
|
9602
|
+
}
|
|
9603
|
+
);
|
|
9604
|
+
}
|
|
9605
|
+
function SelectScrollUpButton({
|
|
9606
|
+
className,
|
|
9607
|
+
...props
|
|
9608
|
+
}) {
|
|
9609
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
9610
|
+
import_radix_ui7.Select.ScrollUpButton,
|
|
9611
|
+
{
|
|
9612
|
+
"data-slot": "select-scroll-up-button",
|
|
9613
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
9614
|
+
...props,
|
|
9615
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.ChevronUpIcon, { className: "size-4" })
|
|
9616
|
+
}
|
|
9617
|
+
);
|
|
9618
|
+
}
|
|
9619
|
+
function SelectScrollDownButton({
|
|
9620
|
+
className,
|
|
9621
|
+
...props
|
|
9622
|
+
}) {
|
|
9623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
|
|
9624
|
+
import_radix_ui7.Select.ScrollDownButton,
|
|
9625
|
+
{
|
|
9626
|
+
"data-slot": "select-scroll-down-button",
|
|
9627
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
9628
|
+
...props,
|
|
9629
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.ChevronDownIcon, { className: "size-4" })
|
|
9630
|
+
}
|
|
9631
|
+
);
|
|
9632
|
+
}
|
|
9633
|
+
|
|
9634
|
+
// src/ui/popover.tsx
|
|
9635
|
+
var import_radix_ui8 = require("radix-ui");
|
|
9636
|
+
var import_jsx_runtime87 = require("react/jsx-runtime");
|
|
9367
9637
|
function Popover({
|
|
9368
9638
|
...props
|
|
9369
9639
|
}) {
|
|
9370
|
-
return /* @__PURE__ */ (0,
|
|
9640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Root, { "data-slot": "popover", ...props });
|
|
9371
9641
|
}
|
|
9372
9642
|
function PopoverTrigger({
|
|
9373
9643
|
...props
|
|
9374
9644
|
}) {
|
|
9375
|
-
return /* @__PURE__ */ (0,
|
|
9645
|
+
return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
9376
9646
|
}
|
|
9377
9647
|
function PopoverContent({
|
|
9378
9648
|
className,
|
|
@@ -9381,8 +9651,8 @@ function PopoverContent({
|
|
|
9381
9651
|
variant = "default",
|
|
9382
9652
|
...props
|
|
9383
9653
|
}) {
|
|
9384
|
-
return /* @__PURE__ */ (0,
|
|
9385
|
-
|
|
9654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9655
|
+
import_radix_ui8.Popover.Content,
|
|
9386
9656
|
{
|
|
9387
9657
|
"data-slot": "popover-content",
|
|
9388
9658
|
"data-variant": variant,
|
|
@@ -9394,7 +9664,7 @@ function PopoverContent({
|
|
|
9394
9664
|
"min-w-[8rem] origin-[var(--radix-popover-content-transform-origin)]"
|
|
9395
9665
|
) : cn(
|
|
9396
9666
|
overlaySurfaceClass,
|
|
9397
|
-
"w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-
|
|
9667
|
+
"w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-3 outline-hidden"
|
|
9398
9668
|
),
|
|
9399
9669
|
className
|
|
9400
9670
|
),
|
|
@@ -9404,499 +9674,471 @@ function PopoverContent({
|
|
|
9404
9674
|
}
|
|
9405
9675
|
|
|
9406
9676
|
// src/app/data/FilterDropdown.tsx
|
|
9407
|
-
var
|
|
9408
|
-
var
|
|
9409
|
-
{ id: "
|
|
9410
|
-
{ id: "
|
|
9411
|
-
{ id: "
|
|
9677
|
+
var import_jsx_runtime88 = require("react/jsx-runtime");
|
|
9678
|
+
var DEFAULT_PRESETS = [
|
|
9679
|
+
{ id: "last_7_days", label: "Last 7 days" },
|
|
9680
|
+
{ id: "last_30_days", label: "Last 30 days" },
|
|
9681
|
+
{ id: "last_90_days", label: "Last 90 days" },
|
|
9682
|
+
{ id: "this_month", label: "This month" },
|
|
9683
|
+
{ id: "this_year", label: "This year" },
|
|
9684
|
+
{ id: "custom", label: "Custom range" }
|
|
9412
9685
|
];
|
|
9413
|
-
var
|
|
9414
|
-
{ id: "
|
|
9415
|
-
{ id: "
|
|
9416
|
-
{ id: "
|
|
9686
|
+
var DEFAULT_OPERATORS = [
|
|
9687
|
+
{ id: "gt", label: "Greater than" },
|
|
9688
|
+
{ id: "lt", label: "Less than" },
|
|
9689
|
+
{ id: "eq", label: "Equals" }
|
|
9417
9690
|
];
|
|
9691
|
+
function asArray(v) {
|
|
9692
|
+
return Array.isArray(v) ? v : [];
|
|
9693
|
+
}
|
|
9694
|
+
function asText(v) {
|
|
9695
|
+
return typeof v === "string" ? v : "";
|
|
9696
|
+
}
|
|
9697
|
+
function asDate(v) {
|
|
9698
|
+
return v && !Array.isArray(v) && typeof v === "object" && "preset" in v ? v : { preset: null };
|
|
9699
|
+
}
|
|
9700
|
+
function asNumeric(v) {
|
|
9701
|
+
return v && !Array.isArray(v) && typeof v === "object" && "operator" in v ? v : { operator: "gt", value: "" };
|
|
9702
|
+
}
|
|
9703
|
+
var OPERATOR_SYMBOL = {
|
|
9704
|
+
gt: ">",
|
|
9705
|
+
lt: "<",
|
|
9706
|
+
eq: "="
|
|
9707
|
+
};
|
|
9418
9708
|
function FilterDropdown({
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9709
|
+
fields,
|
|
9710
|
+
value,
|
|
9711
|
+
defaultValue,
|
|
9712
|
+
onChange,
|
|
9713
|
+
label = "Filter",
|
|
9714
|
+
align = "start",
|
|
9715
|
+
showActiveChips = true,
|
|
9423
9716
|
className
|
|
9424
9717
|
}) {
|
|
9425
9718
|
const [isOpen, setIsOpen] = (0, import_react53.useState)(false);
|
|
9426
|
-
const [
|
|
9719
|
+
const [activeId, setActiveId] = (0, import_react53.useState)(fields[0]?.id ?? null);
|
|
9427
9720
|
const [isMobile, setIsMobile] = (0, import_react53.useState)(false);
|
|
9721
|
+
const isControlled = value !== void 0;
|
|
9722
|
+
const [internal, setInternal] = (0, import_react53.useState)(defaultValue ?? {});
|
|
9723
|
+
const values = isControlled ? value : internal;
|
|
9428
9724
|
(0, import_react53.useEffect)(() => {
|
|
9429
9725
|
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
|
9430
9726
|
checkMobile();
|
|
9431
9727
|
window.addEventListener("resize", checkMobile);
|
|
9432
9728
|
return () => window.removeEventListener("resize", checkMobile);
|
|
9433
9729
|
}, []);
|
|
9434
|
-
const [selectedContacts, setSelectedContacts] = (0, import_react53.useState)(
|
|
9435
|
-
filters?.contacts ?? initialFilters?.contacts ?? []
|
|
9436
|
-
);
|
|
9437
|
-
const [walletInput, setWalletInput] = (0, import_react53.useState)(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
|
|
9438
|
-
const [appliedWallet, setAppliedWallet] = (0, import_react53.useState)(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
|
|
9439
|
-
const [selectedDatePreset, setSelectedDatePreset] = (0, import_react53.useState)(
|
|
9440
|
-
filters?.lastInvoiceDate ?? initialFilters?.lastInvoiceDate ?? null
|
|
9441
|
-
);
|
|
9442
|
-
const [customDateFrom, setCustomDateFrom] = (0, import_react53.useState)(
|
|
9443
|
-
filters?.customDateRange?.from ?? initialFilters?.customDateRange?.from ?? ""
|
|
9444
|
-
);
|
|
9445
|
-
const [customDateTo, setCustomDateTo] = (0, import_react53.useState)(
|
|
9446
|
-
filters?.customDateRange?.to ?? initialFilters?.customDateRange?.to ?? ""
|
|
9447
|
-
);
|
|
9448
|
-
const [ltvOperator, setLtvOperator] = (0, import_react53.useState)(
|
|
9449
|
-
filters?.lifetimeValue?.operator ?? initialFilters?.lifetimeValue?.operator ?? "greater_than"
|
|
9450
|
-
);
|
|
9451
|
-
const [ltvValue, setLtvValue] = (0, import_react53.useState)(filters?.lifetimeValue?.value ?? initialFilters?.lifetimeValue?.value ?? "");
|
|
9452
|
-
const [isLtvOperatorOpen, setLtvOperatorOpen] = (0, import_react53.useState)(false);
|
|
9453
|
-
const [outstandingOperator, setOutstandingOperator] = (0, import_react53.useState)(
|
|
9454
|
-
filters?.outstanding?.operator ?? initialFilters?.outstanding?.operator ?? "greater_than"
|
|
9455
|
-
);
|
|
9456
|
-
const [outstandingValue, setOutstandingValue] = (0, import_react53.useState)(filters?.outstanding?.value ?? initialFilters?.outstanding?.value ?? "");
|
|
9457
|
-
const [isOutstandingOperatorOpen, setOutstandingOperatorOpen] = (0, import_react53.useState)(false);
|
|
9458
9730
|
(0, import_react53.useEffect)(() => {
|
|
9459
|
-
if (
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
setLtvValue(filters.lifetimeValue?.value ?? "");
|
|
9468
|
-
setOutstandingOperator(filters.outstanding?.operator ?? "greater_than");
|
|
9469
|
-
setOutstandingValue(filters.outstanding?.value ?? "");
|
|
9470
|
-
}
|
|
9471
|
-
}, [filters]);
|
|
9472
|
-
const [contactSearch, setContactSearch] = (0, import_react53.useState)("");
|
|
9473
|
-
const [walletSearch, setWalletSearch] = (0, import_react53.useState)("");
|
|
9474
|
-
const refDate = (0, import_react53.useMemo)(() => new Date(2026, 5, 26), []);
|
|
9475
|
-
const presets = (0, import_react53.useMemo)(() => {
|
|
9476
|
-
const year = refDate.getFullYear();
|
|
9477
|
-
const month = refDate.getMonth();
|
|
9478
|
-
const lastMonthDate = new Date(year, month - 1, 1);
|
|
9479
|
-
const lastMonthLabel = lastMonthDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
|
|
9480
|
-
const thisMonthLabel = refDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
|
|
9481
|
-
const thisQuarter = Math.floor(month / 3) + 1;
|
|
9482
|
-
const thisQuarterLabel = `Q${thisQuarter} ${year}`;
|
|
9483
|
-
const lastQuarter = thisQuarter === 1 ? 4 : thisQuarter - 1;
|
|
9484
|
-
const lastQuarterYear = thisQuarter === 1 ? year - 1 : year;
|
|
9485
|
-
const lastQuarterLabel = `Q${lastQuarter} ${lastQuarterYear}`;
|
|
9486
|
-
const thisYearLabel = `${year}`;
|
|
9487
|
-
const last30 = new Date(refDate);
|
|
9488
|
-
last30.setDate(refDate.getDate() - 30);
|
|
9489
|
-
const formatDate = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
9490
|
-
const last30Label = `${formatDate(last30)} - ${formatDate(refDate)}`;
|
|
9491
|
-
const last90 = new Date(refDate);
|
|
9492
|
-
last90.setDate(refDate.getDate() - 90);
|
|
9493
|
-
const last90Label = `${formatDate(last90)} - ${formatDate(refDate)}`;
|
|
9494
|
-
return [
|
|
9495
|
-
{ id: "last_month", label: "Last month", date: lastMonthLabel },
|
|
9496
|
-
{ id: "this_month", label: "This month", date: thisMonthLabel },
|
|
9497
|
-
{ id: "this_quarter", label: "This quarter", date: thisQuarterLabel },
|
|
9498
|
-
{ id: "last_quarter", label: "Last quarter", date: lastQuarterLabel },
|
|
9499
|
-
{ id: "this_year", label: "This year", date: thisYearLabel },
|
|
9500
|
-
{ id: "last_30_days", label: "Last 30 days", date: last30Label },
|
|
9501
|
-
{ id: "last_90_days", label: "Last 90 days", date: last90Label },
|
|
9502
|
-
{ id: "custom", label: "Custom range", date: "" }
|
|
9503
|
-
];
|
|
9504
|
-
}, [refDate]);
|
|
9505
|
-
const filteredContacts = (0, import_react53.useMemo)(() => {
|
|
9506
|
-
if (!contactSearch) return contacts;
|
|
9507
|
-
const query = contactSearch.toLowerCase();
|
|
9508
|
-
return contacts.filter(
|
|
9509
|
-
(c) => c.name.toLowerCase().includes(query) || c.email.toLowerCase().includes(query)
|
|
9510
|
-
);
|
|
9511
|
-
}, [contacts, contactSearch]);
|
|
9512
|
-
const handleContactToggle = (contactName) => {
|
|
9513
|
-
const next = selectedContacts.includes(contactName) ? selectedContacts.filter((name) => name !== contactName) : [...selectedContacts, contactName];
|
|
9514
|
-
setSelectedContacts(next);
|
|
9515
|
-
notifyChanges({ contacts: next });
|
|
9516
|
-
setIsOpen(false);
|
|
9517
|
-
};
|
|
9518
|
-
const handleWalletApply = () => {
|
|
9519
|
-
setAppliedWallet(walletInput);
|
|
9520
|
-
notifyChanges({ walletAddress: walletInput });
|
|
9521
|
-
setIsOpen(false);
|
|
9731
|
+
if (!fields.some((f) => f.id === activeId)) {
|
|
9732
|
+
setActiveId(fields[0]?.id ?? null);
|
|
9733
|
+
}
|
|
9734
|
+
}, [fields, activeId]);
|
|
9735
|
+
const commit = (id, next) => {
|
|
9736
|
+
const merged = { ...values, [id]: next };
|
|
9737
|
+
if (!isControlled) setInternal(merged);
|
|
9738
|
+
onChange?.(merged);
|
|
9522
9739
|
};
|
|
9523
|
-
const
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
notifyChanges({ walletAddress: "" });
|
|
9527
|
-
setIsOpen(false);
|
|
9740
|
+
const clearAll = () => {
|
|
9741
|
+
if (!isControlled) setInternal({});
|
|
9742
|
+
onChange?.({});
|
|
9528
9743
|
};
|
|
9529
|
-
const
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9744
|
+
const activeIdx = fields.findIndex((f) => f.id === activeId);
|
|
9745
|
+
const activeField = activeIdx >= 0 ? fields[activeIdx] : void 0;
|
|
9746
|
+
const chips = [];
|
|
9747
|
+
for (const field of fields) {
|
|
9748
|
+
const v = values[field.id];
|
|
9749
|
+
if (field.type === "multiselect") {
|
|
9750
|
+
const selected = asArray(v);
|
|
9751
|
+
for (const optionValue of selected) {
|
|
9752
|
+
const opt = field.options?.find((o) => o.value === optionValue);
|
|
9753
|
+
chips.push({
|
|
9754
|
+
id: `${field.id}:${optionValue}`,
|
|
9755
|
+
label: `${field.label}: ${opt?.label ?? optionValue}`,
|
|
9756
|
+
remove: () => commit(field.id, selected.filter((x) => x !== optionValue))
|
|
9757
|
+
});
|
|
9758
|
+
}
|
|
9759
|
+
} else if (field.type === "text") {
|
|
9760
|
+
const text = asText(v);
|
|
9761
|
+
if (text) {
|
|
9762
|
+
chips.push({
|
|
9763
|
+
id: field.id,
|
|
9764
|
+
label: `${field.label}: ${text}`,
|
|
9765
|
+
remove: () => commit(field.id, "")
|
|
9766
|
+
});
|
|
9767
|
+
}
|
|
9768
|
+
} else if (field.type === "numeric") {
|
|
9769
|
+
const n = asNumeric(v);
|
|
9770
|
+
if (n.value) {
|
|
9771
|
+
chips.push({
|
|
9772
|
+
id: field.id,
|
|
9773
|
+
label: `${field.label} ${OPERATOR_SYMBOL[n.operator]} ${n.value}`,
|
|
9774
|
+
remove: () => commit(field.id, null)
|
|
9775
|
+
});
|
|
9776
|
+
}
|
|
9777
|
+
} else if (field.type === "daterange") {
|
|
9778
|
+
const d = asDate(v);
|
|
9779
|
+
if (d.preset) {
|
|
9780
|
+
const presetLabel = d.preset === "custom" ? `${d.from || "\u2026"} \u2013 ${d.to || "\u2026"}` : (field.presets ?? DEFAULT_PRESETS).find((p) => p.id === d.preset)?.label ?? d.preset;
|
|
9781
|
+
chips.push({
|
|
9782
|
+
id: field.id,
|
|
9783
|
+
label: `${field.label}: ${presetLabel}`,
|
|
9784
|
+
remove: () => commit(field.id, { preset: null })
|
|
9785
|
+
});
|
|
9786
|
+
}
|
|
9534
9787
|
}
|
|
9788
|
+
}
|
|
9789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
|
|
9790
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
9791
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9792
|
+
Button,
|
|
9793
|
+
{
|
|
9794
|
+
variant: "outline",
|
|
9795
|
+
size: "sm",
|
|
9796
|
+
className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
|
|
9797
|
+
iconLeading: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.ListFilterIcon, { className: "size-4" }),
|
|
9798
|
+
children: label
|
|
9799
|
+
}
|
|
9800
|
+
) }),
|
|
9801
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9802
|
+
PopoverContent,
|
|
9803
|
+
{
|
|
9804
|
+
variant: "list",
|
|
9805
|
+
align,
|
|
9806
|
+
className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
|
|
9807
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "relative flex flex-col md:flex-row items-stretch md:items-start w-[calc(100vw-32px)] max-w-[340px] md:w-auto md:max-w-none", children: [
|
|
9808
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: fields.map((field) => {
|
|
9809
|
+
const isActive = activeId === field.id;
|
|
9810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
|
|
9811
|
+
"button",
|
|
9812
|
+
{
|
|
9813
|
+
type: "button",
|
|
9814
|
+
className: cn(
|
|
9815
|
+
"flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
|
|
9816
|
+
isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
9817
|
+
),
|
|
9818
|
+
onMouseEnter: () => !isMobile && setActiveId(field.id),
|
|
9819
|
+
onClick: () => setActiveId(field.id),
|
|
9820
|
+
children: [
|
|
9821
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
9822
|
+
field.icon,
|
|
9823
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { children: field.label })
|
|
9824
|
+
] }),
|
|
9825
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
|
|
9826
|
+
]
|
|
9827
|
+
},
|
|
9828
|
+
field.id
|
|
9829
|
+
);
|
|
9830
|
+
}) }),
|
|
9831
|
+
activeField && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9832
|
+
"div",
|
|
9833
|
+
{
|
|
9834
|
+
className: "relative left-0 mt-2 w-full md:absolute md:left-[calc(100%+6px)] md:w-80 rounded-xl border border-border bg-popover p-3 shadow-lg transition-all duration-150 md:mt-0",
|
|
9835
|
+
style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
|
|
9836
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9837
|
+
FilterFieldControl,
|
|
9838
|
+
{
|
|
9839
|
+
field: activeField,
|
|
9840
|
+
value: values[activeField.id],
|
|
9841
|
+
onChange: (next) => commit(activeField.id, next),
|
|
9842
|
+
onClose: () => setIsOpen(false)
|
|
9843
|
+
}
|
|
9844
|
+
)
|
|
9845
|
+
}
|
|
9846
|
+
)
|
|
9847
|
+
] })
|
|
9848
|
+
}
|
|
9849
|
+
)
|
|
9850
|
+
] }),
|
|
9851
|
+
showActiveChips && chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(FilterChip, { label: chip.label, onRemove: chip.remove }, chip.id)),
|
|
9852
|
+
showActiveChips && chips.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9853
|
+
"button",
|
|
9854
|
+
{
|
|
9855
|
+
type: "button",
|
|
9856
|
+
onClick: clearAll,
|
|
9857
|
+
className: "rounded-full px-3 py-1 text-sm font-medium text-muted-foreground outline-none transition-colors hover:text-foreground",
|
|
9858
|
+
children: "Clear all"
|
|
9859
|
+
}
|
|
9860
|
+
)
|
|
9861
|
+
] });
|
|
9862
|
+
}
|
|
9863
|
+
function FilterChip({ label, onRemove }) {
|
|
9864
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: "inline-flex h-9 items-center gap-1.5 rounded-full border border-border bg-muted/40 pl-3 pr-1.5 text-sm font-medium text-foreground", children: [
|
|
9865
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate", children: label }),
|
|
9866
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9867
|
+
"button",
|
|
9868
|
+
{
|
|
9869
|
+
type: "button",
|
|
9870
|
+
onClick: onRemove,
|
|
9871
|
+
"aria-label": `Remove ${label}`,
|
|
9872
|
+
className: "flex size-5 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground",
|
|
9873
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.XIcon, { className: "size-3.5" })
|
|
9874
|
+
}
|
|
9875
|
+
)
|
|
9876
|
+
] });
|
|
9877
|
+
}
|
|
9878
|
+
function FilterFieldControl({
|
|
9879
|
+
field,
|
|
9880
|
+
value,
|
|
9881
|
+
onChange,
|
|
9882
|
+
onClose
|
|
9883
|
+
}) {
|
|
9884
|
+
switch (field.type) {
|
|
9885
|
+
case "multiselect":
|
|
9886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(MultiSelectControl, { field, value: asArray(value), onChange });
|
|
9887
|
+
case "text":
|
|
9888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(TextControl, { field, value: asText(value), onChange, onClose });
|
|
9889
|
+
case "daterange":
|
|
9890
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(DateRangeControl, { field, value: asDate(value), onChange, onClose });
|
|
9891
|
+
case "numeric":
|
|
9892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(NumericControl, { field, value: asNumeric(value), onChange, onClose });
|
|
9893
|
+
default:
|
|
9894
|
+
return null;
|
|
9895
|
+
}
|
|
9896
|
+
}
|
|
9897
|
+
function MultiSelectControl({
|
|
9898
|
+
field,
|
|
9899
|
+
value,
|
|
9900
|
+
onChange
|
|
9901
|
+
}) {
|
|
9902
|
+
const options = field.options ?? [];
|
|
9903
|
+
const [search, setSearch] = (0, import_react53.useState)("");
|
|
9904
|
+
const searchable = field.searchable ?? options.length > 8;
|
|
9905
|
+
const filtered = (0, import_react53.useMemo)(() => {
|
|
9906
|
+
if (!search) return options;
|
|
9907
|
+
const q = search.toLowerCase();
|
|
9908
|
+
return options.filter(
|
|
9909
|
+
(o) => o.label.toLowerCase().includes(q) || o.hint?.toLowerCase().includes(q)
|
|
9910
|
+
);
|
|
9911
|
+
}, [options, search]);
|
|
9912
|
+
const toggle = (optionValue) => {
|
|
9913
|
+
onChange(
|
|
9914
|
+
value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
|
9915
|
+
);
|
|
9535
9916
|
};
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
customDateRange: { from: customDateFrom, to: customDateTo }
|
|
9540
|
-
});
|
|
9541
|
-
setIsOpen(false);
|
|
9542
|
-
};
|
|
9543
|
-
const handleLtvApply = () => {
|
|
9544
|
-
notifyChanges({
|
|
9545
|
-
lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null
|
|
9546
|
-
});
|
|
9547
|
-
setIsOpen(false);
|
|
9548
|
-
};
|
|
9549
|
-
const handleLtvClear = () => {
|
|
9550
|
-
setLtvValue("");
|
|
9551
|
-
notifyChanges({ lifetimeValue: null });
|
|
9552
|
-
setIsOpen(false);
|
|
9553
|
-
};
|
|
9554
|
-
const handleOutstandingApply = () => {
|
|
9555
|
-
notifyChanges({
|
|
9556
|
-
outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null
|
|
9557
|
-
});
|
|
9558
|
-
setIsOpen(false);
|
|
9559
|
-
};
|
|
9560
|
-
const handleOutstandingClear = () => {
|
|
9561
|
-
setOutstandingValue("");
|
|
9562
|
-
notifyChanges({ outstanding: null });
|
|
9563
|
-
setIsOpen(false);
|
|
9564
|
-
};
|
|
9565
|
-
const notifyChanges = (overrides) => {
|
|
9566
|
-
const current = {
|
|
9567
|
-
contacts: selectedContacts,
|
|
9568
|
-
walletAddress: appliedWallet,
|
|
9569
|
-
lastInvoiceDate: selectedDatePreset,
|
|
9570
|
-
customDateRange: customDateFrom || customDateTo ? { from: customDateFrom, to: customDateTo } : void 0,
|
|
9571
|
-
lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null,
|
|
9572
|
-
outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null,
|
|
9573
|
-
...overrides
|
|
9574
|
-
};
|
|
9575
|
-
onFiltersChange?.(current);
|
|
9576
|
-
};
|
|
9577
|
-
const menuItems = [
|
|
9578
|
-
{ id: "contact", label: "Contact", icon: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.UserIcon, { className: "size-4" }) },
|
|
9579
|
-
{ id: "wallet", label: "Wallet address", icon: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.WalletIcon, { className: "size-4" }) },
|
|
9580
|
-
{ id: "date", label: "Last invoice date", icon: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.CalendarIcon, { className: "size-4" }) },
|
|
9581
|
-
{ id: "ltv", label: "Lifetime value", icon: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.TrendingUpIcon, { className: "size-4" }) },
|
|
9582
|
-
{ id: "outstanding", label: "Outstanding", icon: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.CircleDollarSignIcon, { className: "size-4" }) }
|
|
9583
|
-
];
|
|
9584
|
-
const activeIdx = menuItems.findIndex((item) => item.id === activeMenu);
|
|
9585
|
-
return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: cn("inline-block", className), children: /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
9586
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9587
|
-
Button,
|
|
9917
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9918
|
+
searchable && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9919
|
+
SearchInput,
|
|
9588
9920
|
{
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
className: "
|
|
9593
|
-
iconLeading: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.ListFilterIcon, { className: "size-4" }),
|
|
9594
|
-
children: "Filter"
|
|
9921
|
+
placeholder: field.searchPlaceholder ?? "Search\u2026",
|
|
9922
|
+
value: search,
|
|
9923
|
+
onChange: (e) => setSearch(e.target.value),
|
|
9924
|
+
className: "w-full min-w-0"
|
|
9595
9925
|
}
|
|
9596
|
-
)
|
|
9597
|
-
/* @__PURE__ */ (0,
|
|
9598
|
-
|
|
9926
|
+
),
|
|
9927
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto pr-1", children: filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No options found" }) : filtered.map((option) => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
|
|
9928
|
+
"label",
|
|
9599
9929
|
{
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: menuItems.map((item) => {
|
|
9605
|
-
const isActive = activeMenu === item.id;
|
|
9606
|
-
return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9607
|
-
"button",
|
|
9608
|
-
{
|
|
9609
|
-
type: "button",
|
|
9610
|
-
className: cn(
|
|
9611
|
-
"flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
|
|
9612
|
-
isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
9613
|
-
),
|
|
9614
|
-
onMouseEnter: () => !isMobile && setActiveMenu(item.id),
|
|
9615
|
-
onClick: () => setActiveMenu(item.id),
|
|
9616
|
-
children: [
|
|
9617
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
9618
|
-
item.icon,
|
|
9619
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: item.label })
|
|
9620
|
-
] }),
|
|
9621
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
|
|
9622
|
-
]
|
|
9623
|
-
},
|
|
9624
|
-
item.id
|
|
9625
|
-
);
|
|
9626
|
-
}) }),
|
|
9627
|
-
activeMenu && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9628
|
-
"div",
|
|
9930
|
+
className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors hover:bg-muted/50",
|
|
9931
|
+
children: [
|
|
9932
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9933
|
+
Checkbox,
|
|
9629
9934
|
{
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
children: [
|
|
9633
|
-
activeMenu === "contact" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9634
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "relative flex items-center", children: [
|
|
9635
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.SearchIcon, { className: "absolute left-2.5 size-4 text-muted-foreground/60" }),
|
|
9636
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9637
|
-
"input",
|
|
9638
|
-
{
|
|
9639
|
-
type: "text",
|
|
9640
|
-
placeholder: "Search by name or email...",
|
|
9641
|
-
value: contactSearch,
|
|
9642
|
-
onChange: (e) => setContactSearch(e.target.value),
|
|
9643
|
-
className: "w-full rounded-lg border border-border bg-background py-1.5 pl-8 pr-3 text-sm outline-none placeholder:text-muted-foreground/60 focus:border-border"
|
|
9644
|
-
}
|
|
9645
|
-
)
|
|
9646
|
-
] }),
|
|
9647
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "max-h-48 overflow-y-auto flex flex-col gap-1 pr-1", children: filteredContacts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No contacts found" }) : filteredContacts.map((contact) => {
|
|
9648
|
-
const isChecked = selectedContacts.includes(contact.name);
|
|
9649
|
-
return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9650
|
-
"label",
|
|
9651
|
-
{
|
|
9652
|
-
className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 hover:bg-muted/50 transition-colors",
|
|
9653
|
-
children: [
|
|
9654
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9655
|
-
Checkbox,
|
|
9656
|
-
{
|
|
9657
|
-
checked: isChecked,
|
|
9658
|
-
onCheckedChange: () => handleContactToggle(contact.name)
|
|
9659
|
-
}
|
|
9660
|
-
),
|
|
9661
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Avatar, { variant: "secondary", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(AvatarFallback, { children: contact.initials }) }),
|
|
9662
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "text-sm font-medium text-foreground", children: contact.name })
|
|
9663
|
-
]
|
|
9664
|
-
},
|
|
9665
|
-
contact.id
|
|
9666
|
-
);
|
|
9667
|
-
}) })
|
|
9668
|
-
] }),
|
|
9669
|
-
activeMenu === "wallet" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9670
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "relative flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9671
|
-
"input",
|
|
9672
|
-
{
|
|
9673
|
-
type: "text",
|
|
9674
|
-
placeholder: "Search by wallet...",
|
|
9675
|
-
value: walletInput,
|
|
9676
|
-
onChange: (e) => setWalletInput(e.target.value),
|
|
9677
|
-
className: "w-full rounded-lg border border-border bg-background px-3 py-1.5 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
9678
|
-
}
|
|
9679
|
-
) }),
|
|
9680
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
9681
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9682
|
-
Button,
|
|
9683
|
-
{
|
|
9684
|
-
variant: "ghost",
|
|
9685
|
-
size: "sm",
|
|
9686
|
-
onClick: handleWalletClear,
|
|
9687
|
-
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
9688
|
-
children: "Clear"
|
|
9689
|
-
}
|
|
9690
|
-
),
|
|
9691
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9692
|
-
Button,
|
|
9693
|
-
{
|
|
9694
|
-
color: "primary",
|
|
9695
|
-
size: "sm",
|
|
9696
|
-
onClick: handleWalletApply,
|
|
9697
|
-
className: "h-8 px-3",
|
|
9698
|
-
children: "Apply"
|
|
9699
|
-
}
|
|
9700
|
-
)
|
|
9701
|
-
] })
|
|
9702
|
-
] }),
|
|
9703
|
-
activeMenu === "date" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
9704
|
-
presets.map((preset) => {
|
|
9705
|
-
const isSelected = selectedDatePreset === preset.id;
|
|
9706
|
-
return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9707
|
-
"button",
|
|
9708
|
-
{
|
|
9709
|
-
type: "button",
|
|
9710
|
-
onClick: () => handleDatePresetSelect(preset.id),
|
|
9711
|
-
className: cn(
|
|
9712
|
-
"flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-sm text-left transition-colors outline-none",
|
|
9713
|
-
isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
9714
|
-
),
|
|
9715
|
-
children: [
|
|
9716
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: preset.label }),
|
|
9717
|
-
preset.date && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "text-xs text-muted-foreground/70", children: preset.date })
|
|
9718
|
-
]
|
|
9719
|
-
},
|
|
9720
|
-
preset.id
|
|
9721
|
-
);
|
|
9722
|
-
}),
|
|
9723
|
-
selectedDatePreset === "custom" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2 mt-2 pt-2 border-t border-border/40", children: [
|
|
9724
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
9725
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9726
|
-
"input",
|
|
9727
|
-
{
|
|
9728
|
-
type: "date",
|
|
9729
|
-
value: customDateFrom,
|
|
9730
|
-
onChange: (e) => setCustomDateFrom(e.target.value),
|
|
9731
|
-
className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
|
|
9732
|
-
}
|
|
9733
|
-
),
|
|
9734
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
9735
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9736
|
-
"input",
|
|
9737
|
-
{
|
|
9738
|
-
type: "date",
|
|
9739
|
-
value: customDateTo,
|
|
9740
|
-
onChange: (e) => setCustomDateTo(e.target.value),
|
|
9741
|
-
className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
|
|
9742
|
-
}
|
|
9743
|
-
)
|
|
9744
|
-
] }),
|
|
9745
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "flex justify-end gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9746
|
-
Button,
|
|
9747
|
-
{
|
|
9748
|
-
color: "primary",
|
|
9749
|
-
size: "sm",
|
|
9750
|
-
onClick: handleCustomDateApply,
|
|
9751
|
-
className: "h-7 text-xs px-2.5",
|
|
9752
|
-
children: "Apply"
|
|
9753
|
-
}
|
|
9754
|
-
) })
|
|
9755
|
-
] })
|
|
9756
|
-
] }),
|
|
9757
|
-
activeMenu === "ltv" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9758
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
9759
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "relative shrink-0", children: [
|
|
9760
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9761
|
-
"button",
|
|
9762
|
-
{
|
|
9763
|
-
type: "button",
|
|
9764
|
-
onClick: () => setLtvOperatorOpen(!isLtvOperatorOpen),
|
|
9765
|
-
className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
|
|
9766
|
-
children: [
|
|
9767
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: OPERATORS.find((op) => op.id === ltvOperator)?.label.replace("...", "") }),
|
|
9768
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.ChevronDownIcon, { className: "size-3" })
|
|
9769
|
-
]
|
|
9770
|
-
}
|
|
9771
|
-
),
|
|
9772
|
-
isLtvOperatorOpen && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9773
|
-
"button",
|
|
9774
|
-
{
|
|
9775
|
-
type: "button",
|
|
9776
|
-
onClick: () => {
|
|
9777
|
-
setLtvOperator(op.id);
|
|
9778
|
-
setLtvOperatorOpen(false);
|
|
9779
|
-
},
|
|
9780
|
-
className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
|
|
9781
|
-
children: op.label
|
|
9782
|
-
},
|
|
9783
|
-
op.id
|
|
9784
|
-
)) })
|
|
9785
|
-
] }),
|
|
9786
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9787
|
-
"input",
|
|
9788
|
-
{
|
|
9789
|
-
type: "text",
|
|
9790
|
-
placeholder: "0.00",
|
|
9791
|
-
value: ltvValue,
|
|
9792
|
-
onChange: (e) => setLtvValue(e.target.value),
|
|
9793
|
-
className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
9794
|
-
}
|
|
9795
|
-
)
|
|
9796
|
-
] }),
|
|
9797
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
9798
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9799
|
-
Button,
|
|
9800
|
-
{
|
|
9801
|
-
variant: "ghost",
|
|
9802
|
-
size: "sm",
|
|
9803
|
-
onClick: handleLtvClear,
|
|
9804
|
-
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
9805
|
-
children: "Clear"
|
|
9806
|
-
}
|
|
9807
|
-
),
|
|
9808
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9809
|
-
Button,
|
|
9810
|
-
{
|
|
9811
|
-
color: "primary",
|
|
9812
|
-
size: "sm",
|
|
9813
|
-
onClick: handleLtvApply,
|
|
9814
|
-
className: "h-8 px-3",
|
|
9815
|
-
children: "Apply"
|
|
9816
|
-
}
|
|
9817
|
-
)
|
|
9818
|
-
] })
|
|
9819
|
-
] }),
|
|
9820
|
-
activeMenu === "outstanding" && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9821
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
9822
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "relative shrink-0", children: [
|
|
9823
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
|
|
9824
|
-
"button",
|
|
9825
|
-
{
|
|
9826
|
-
type: "button",
|
|
9827
|
-
onClick: () => setOutstandingOperatorOpen(!isOutstandingOperatorOpen),
|
|
9828
|
-
className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
|
|
9829
|
-
children: [
|
|
9830
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: OPERATORS.find((op) => op.id === outstandingOperator)?.label.replace("...", "") }),
|
|
9831
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react17.ChevronDownIcon, { className: "size-3" })
|
|
9832
|
-
]
|
|
9833
|
-
}
|
|
9834
|
-
),
|
|
9835
|
-
isOutstandingOperatorOpen && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9836
|
-
"button",
|
|
9837
|
-
{
|
|
9838
|
-
type: "button",
|
|
9839
|
-
onClick: () => {
|
|
9840
|
-
setOutstandingOperator(op.id);
|
|
9841
|
-
setOutstandingOperatorOpen(false);
|
|
9842
|
-
},
|
|
9843
|
-
className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
|
|
9844
|
-
children: op.label
|
|
9845
|
-
},
|
|
9846
|
-
op.id
|
|
9847
|
-
)) })
|
|
9848
|
-
] }),
|
|
9849
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9850
|
-
"input",
|
|
9851
|
-
{
|
|
9852
|
-
type: "text",
|
|
9853
|
-
placeholder: "0.00",
|
|
9854
|
-
value: outstandingValue,
|
|
9855
|
-
onChange: (e) => setOutstandingValue(e.target.value),
|
|
9856
|
-
className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
9857
|
-
}
|
|
9858
|
-
)
|
|
9859
|
-
] }),
|
|
9860
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
9861
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9862
|
-
Button,
|
|
9863
|
-
{
|
|
9864
|
-
variant: "ghost",
|
|
9865
|
-
size: "sm",
|
|
9866
|
-
onClick: handleOutstandingClear,
|
|
9867
|
-
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
9868
|
-
children: "Clear"
|
|
9869
|
-
}
|
|
9870
|
-
),
|
|
9871
|
-
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
|
|
9872
|
-
Button,
|
|
9873
|
-
{
|
|
9874
|
-
color: "primary",
|
|
9875
|
-
size: "sm",
|
|
9876
|
-
onClick: handleOutstandingApply,
|
|
9877
|
-
className: "h-8 px-3",
|
|
9878
|
-
children: "Apply"
|
|
9879
|
-
}
|
|
9880
|
-
)
|
|
9881
|
-
] })
|
|
9882
|
-
] })
|
|
9883
|
-
]
|
|
9935
|
+
checked: value.includes(option.value),
|
|
9936
|
+
onCheckedChange: () => toggle(option.value)
|
|
9884
9937
|
}
|
|
9885
|
-
)
|
|
9886
|
-
|
|
9938
|
+
),
|
|
9939
|
+
option.icon,
|
|
9940
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: "flex min-w-0 flex-col", children: [
|
|
9941
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate text-sm font-medium text-foreground", children: option.label }),
|
|
9942
|
+
option.hint && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: option.hint })
|
|
9943
|
+
] })
|
|
9944
|
+
]
|
|
9945
|
+
},
|
|
9946
|
+
option.value
|
|
9947
|
+
)) })
|
|
9948
|
+
] });
|
|
9949
|
+
}
|
|
9950
|
+
function TextControl({
|
|
9951
|
+
field,
|
|
9952
|
+
value,
|
|
9953
|
+
onChange,
|
|
9954
|
+
onClose
|
|
9955
|
+
}) {
|
|
9956
|
+
const [draft, setDraft] = (0, import_react53.useState)(value);
|
|
9957
|
+
(0, import_react53.useEffect)(() => setDraft(value), [value]);
|
|
9958
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
9959
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9960
|
+
"input",
|
|
9961
|
+
{
|
|
9962
|
+
type: "text",
|
|
9963
|
+
placeholder: field.placeholder ?? "Type a value\u2026",
|
|
9964
|
+
value: draft,
|
|
9965
|
+
onChange: (e) => setDraft(e.target.value),
|
|
9966
|
+
onKeyDown: (e) => {
|
|
9967
|
+
if (e.key === "Enter") {
|
|
9968
|
+
onChange(draft);
|
|
9969
|
+
onClose();
|
|
9970
|
+
}
|
|
9971
|
+
},
|
|
9972
|
+
className: controlClass({ size: "sm" }, "w-full")
|
|
9973
|
+
}
|
|
9974
|
+
),
|
|
9975
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
9976
|
+
ApplyClear,
|
|
9977
|
+
{
|
|
9978
|
+
onClear: () => {
|
|
9979
|
+
setDraft("");
|
|
9980
|
+
onChange("");
|
|
9981
|
+
onClose();
|
|
9982
|
+
},
|
|
9983
|
+
onApply: () => {
|
|
9984
|
+
onChange(draft);
|
|
9985
|
+
onClose();
|
|
9986
|
+
}
|
|
9887
9987
|
}
|
|
9888
9988
|
)
|
|
9889
|
-
] })
|
|
9989
|
+
] });
|
|
9990
|
+
}
|
|
9991
|
+
function DateRangeControl({
|
|
9992
|
+
field,
|
|
9993
|
+
value,
|
|
9994
|
+
onChange,
|
|
9995
|
+
onClose
|
|
9996
|
+
}) {
|
|
9997
|
+
const presets = field.presets ?? DEFAULT_PRESETS;
|
|
9998
|
+
const [from, setFrom] = (0, import_react53.useState)(value.from ?? "");
|
|
9999
|
+
const [to, setTo] = (0, import_react53.useState)(value.to ?? "");
|
|
10000
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
10001
|
+
presets.map((preset) => {
|
|
10002
|
+
const isSelected = value.preset === preset.id;
|
|
10003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
|
|
10004
|
+
"button",
|
|
10005
|
+
{
|
|
10006
|
+
type: "button",
|
|
10007
|
+
onClick: () => {
|
|
10008
|
+
if (preset.id === "custom") {
|
|
10009
|
+
onChange({ preset: "custom", from, to });
|
|
10010
|
+
} else {
|
|
10011
|
+
onChange({ preset: preset.id });
|
|
10012
|
+
onClose();
|
|
10013
|
+
}
|
|
10014
|
+
},
|
|
10015
|
+
className: cn(
|
|
10016
|
+
"flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors outline-none",
|
|
10017
|
+
isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
10018
|
+
),
|
|
10019
|
+
children: [
|
|
10020
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { children: preset.label }),
|
|
10021
|
+
preset.hint && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "text-xs text-muted-foreground/70", children: preset.hint })
|
|
10022
|
+
]
|
|
10023
|
+
},
|
|
10024
|
+
preset.id
|
|
10025
|
+
);
|
|
10026
|
+
}),
|
|
10027
|
+
value.preset === "custom" && /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "mt-2 flex flex-col gap-2 border-t border-border/40 pt-2", children: [
|
|
10028
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
10029
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10030
|
+
"input",
|
|
10031
|
+
{
|
|
10032
|
+
type: "date",
|
|
10033
|
+
value: from,
|
|
10034
|
+
onChange: (e) => setFrom(e.target.value),
|
|
10035
|
+
className: controlClass({ size: "sm" }, "w-full text-xs")
|
|
10036
|
+
}
|
|
10037
|
+
),
|
|
10038
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
10039
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10040
|
+
"input",
|
|
10041
|
+
{
|
|
10042
|
+
type: "date",
|
|
10043
|
+
value: to,
|
|
10044
|
+
onChange: (e) => setTo(e.target.value),
|
|
10045
|
+
className: controlClass({ size: "sm" }, "w-full text-xs")
|
|
10046
|
+
}
|
|
10047
|
+
)
|
|
10048
|
+
] }),
|
|
10049
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10050
|
+
Button,
|
|
10051
|
+
{
|
|
10052
|
+
color: "primary",
|
|
10053
|
+
size: "sm",
|
|
10054
|
+
className: "h-8 px-3",
|
|
10055
|
+
onClick: () => {
|
|
10056
|
+
onChange({ preset: "custom", from, to });
|
|
10057
|
+
onClose();
|
|
10058
|
+
},
|
|
10059
|
+
children: "Apply"
|
|
10060
|
+
}
|
|
10061
|
+
) })
|
|
10062
|
+
] })
|
|
10063
|
+
] });
|
|
10064
|
+
}
|
|
10065
|
+
function NumericControl({
|
|
10066
|
+
field,
|
|
10067
|
+
value,
|
|
10068
|
+
onChange,
|
|
10069
|
+
onClose
|
|
10070
|
+
}) {
|
|
10071
|
+
const operators = field.operators ?? DEFAULT_OPERATORS;
|
|
10072
|
+
const [operator, setOperator] = (0, import_react53.useState)(value.operator);
|
|
10073
|
+
const [draft, setDraft] = (0, import_react53.useState)(value.value);
|
|
10074
|
+
(0, import_react53.useEffect)(() => {
|
|
10075
|
+
setOperator(value.operator);
|
|
10076
|
+
setDraft(value.value);
|
|
10077
|
+
}, [value.operator, value.value]);
|
|
10078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
|
|
10079
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
10080
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Select, { value: operator, onValueChange: (v) => setOperator(v), children: [
|
|
10081
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectTrigger, { size: "sm", className: "shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectValue, {}) }),
|
|
10082
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectContent, { children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectItem, { value: op.id, children: op.label }, op.id)) })
|
|
10083
|
+
] }),
|
|
10084
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10085
|
+
"input",
|
|
10086
|
+
{
|
|
10087
|
+
type: "text",
|
|
10088
|
+
inputMode: "decimal",
|
|
10089
|
+
placeholder: field.placeholder ?? "0.00",
|
|
10090
|
+
value: draft,
|
|
10091
|
+
onChange: (e) => setDraft(e.target.value),
|
|
10092
|
+
onKeyDown: (e) => {
|
|
10093
|
+
if (e.key === "Enter") {
|
|
10094
|
+
onChange(draft ? { operator, value: draft } : null);
|
|
10095
|
+
onClose();
|
|
10096
|
+
}
|
|
10097
|
+
},
|
|
10098
|
+
className: controlClass({ size: "sm" }, "min-w-0 flex-1")
|
|
10099
|
+
}
|
|
10100
|
+
)
|
|
10101
|
+
] }),
|
|
10102
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10103
|
+
ApplyClear,
|
|
10104
|
+
{
|
|
10105
|
+
onClear: () => {
|
|
10106
|
+
setDraft("");
|
|
10107
|
+
onChange(null);
|
|
10108
|
+
onClose();
|
|
10109
|
+
},
|
|
10110
|
+
onApply: () => {
|
|
10111
|
+
onChange(draft ? { operator, value: draft } : null);
|
|
10112
|
+
onClose();
|
|
10113
|
+
}
|
|
10114
|
+
}
|
|
10115
|
+
)
|
|
10116
|
+
] });
|
|
10117
|
+
}
|
|
10118
|
+
function ApplyClear({ onClear, onApply }) {
|
|
10119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center justify-end gap-2 border-t border-border/40 pt-1", children: [
|
|
10120
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
|
|
10121
|
+
Button,
|
|
10122
|
+
{
|
|
10123
|
+
variant: "ghost",
|
|
10124
|
+
size: "sm",
|
|
10125
|
+
onClick: onClear,
|
|
10126
|
+
className: "h-8 px-3 text-muted-foreground hover:text-foreground",
|
|
10127
|
+
children: "Clear"
|
|
10128
|
+
}
|
|
10129
|
+
),
|
|
10130
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Button, { color: "primary", size: "sm", onClick: onApply, className: "h-8 px-3", children: "Apply" })
|
|
10131
|
+
] });
|
|
9890
10132
|
}
|
|
9891
10133
|
|
|
9892
10134
|
// src/app/data/DataTable.tsx
|
|
9893
10135
|
var import_react54 = require("react");
|
|
9894
|
-
var
|
|
10136
|
+
var import_lucide_react19 = require("lucide-react");
|
|
9895
10137
|
|
|
9896
10138
|
// src/ui/skeleton.tsx
|
|
9897
|
-
var
|
|
10139
|
+
var import_jsx_runtime89 = require("react/jsx-runtime");
|
|
9898
10140
|
function Skeleton({ className, ...props }) {
|
|
9899
|
-
return /* @__PURE__ */ (0,
|
|
10141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
|
|
9900
10142
|
"div",
|
|
9901
10143
|
{
|
|
9902
10144
|
"data-slot": "skeleton",
|
|
@@ -9907,7 +10149,7 @@ function Skeleton({ className, ...props }) {
|
|
|
9907
10149
|
}
|
|
9908
10150
|
|
|
9909
10151
|
// src/app/data/DataTable.tsx
|
|
9910
|
-
var
|
|
10152
|
+
var import_jsx_runtime90 = require("react/jsx-runtime");
|
|
9911
10153
|
var shellClass2 = "w-full";
|
|
9912
10154
|
var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
|
|
9913
10155
|
var headRowClass = "";
|
|
@@ -9950,12 +10192,12 @@ function SortIndicator({
|
|
|
9950
10192
|
}) {
|
|
9951
10193
|
const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
|
|
9952
10194
|
if (!active) {
|
|
9953
|
-
return /* @__PURE__ */ (0,
|
|
10195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
|
|
9954
10196
|
}
|
|
9955
10197
|
if (direction === "desc") {
|
|
9956
|
-
return /* @__PURE__ */ (0,
|
|
10198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
|
|
9957
10199
|
}
|
|
9958
|
-
return /* @__PURE__ */ (0,
|
|
10200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
|
|
9959
10201
|
}
|
|
9960
10202
|
function DataTable({
|
|
9961
10203
|
columns,
|
|
@@ -10053,7 +10295,7 @@ function DataTable({
|
|
|
10053
10295
|
const headPad = dense ? "px-3 py-2" : void 0;
|
|
10054
10296
|
const colSpan = columns.length + (selectable ? 1 : 0);
|
|
10055
10297
|
if (!loading && rows.length === 0 && emptyMode === "replace") {
|
|
10056
|
-
return /* @__PURE__ */ (0,
|
|
10298
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
|
|
10057
10299
|
}
|
|
10058
10300
|
const allKeys = sortedRows.map(getRowKey);
|
|
10059
10301
|
const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
|
|
@@ -10078,10 +10320,10 @@ function DataTable({
|
|
|
10078
10320
|
const hasPager = paginated && !loading && sortedRows.length > 0;
|
|
10079
10321
|
const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
|
|
10080
10322
|
const skeletonCount = loadingRows ?? pageSize ?? 5;
|
|
10081
|
-
return /* @__PURE__ */ (0,
|
|
10082
|
-
caption ? /* @__PURE__ */ (0,
|
|
10083
|
-
/* @__PURE__ */ (0,
|
|
10084
|
-
selectable ? /* @__PURE__ */ (0,
|
|
10323
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("table", { className: tableClass, children: [
|
|
10324
|
+
caption ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("caption", { className: "sr-only", children: caption }) : null,
|
|
10325
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("tr", { children: [
|
|
10326
|
+
selectable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10085
10327
|
Checkbox,
|
|
10086
10328
|
{
|
|
10087
10329
|
checked: headerCheckedState,
|
|
@@ -10093,19 +10335,19 @@ function DataTable({
|
|
|
10093
10335
|
columns.map((col) => {
|
|
10094
10336
|
const isSorted = sort?.columnId === col.id;
|
|
10095
10337
|
const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
|
|
10096
|
-
const headerContent = col.sortable ? /* @__PURE__ */ (0,
|
|
10338
|
+
const headerContent = col.sortable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
|
|
10097
10339
|
"button",
|
|
10098
10340
|
{
|
|
10099
10341
|
type: "button",
|
|
10100
10342
|
className: sortButtonClass,
|
|
10101
10343
|
onClick: () => setSort(nextSort(sort, col.id)),
|
|
10102
10344
|
children: [
|
|
10103
|
-
/* @__PURE__ */ (0,
|
|
10104
|
-
/* @__PURE__ */ (0,
|
|
10345
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { className: "truncate", children: col.header }),
|
|
10346
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
|
|
10105
10347
|
]
|
|
10106
10348
|
}
|
|
10107
10349
|
) : col.header;
|
|
10108
|
-
return /* @__PURE__ */ (0,
|
|
10350
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10109
10351
|
"th",
|
|
10110
10352
|
{
|
|
10111
10353
|
scope: "col",
|
|
@@ -10122,9 +10364,9 @@ function DataTable({
|
|
|
10122
10364
|
);
|
|
10123
10365
|
})
|
|
10124
10366
|
] }) }),
|
|
10125
|
-
/* @__PURE__ */ (0,
|
|
10126
|
-
selectable ? /* @__PURE__ */ (0,
|
|
10127
|
-
columns.map((col) => /* @__PURE__ */ (0,
|
|
10367
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("tr", { className: rowClass, "aria-hidden": true, children: [
|
|
10368
|
+
selectable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
|
|
10369
|
+
columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10128
10370
|
"td",
|
|
10129
10371
|
{
|
|
10130
10372
|
className: cn(
|
|
@@ -10133,17 +10375,17 @@ function DataTable({
|
|
|
10133
10375
|
col.align && alignClass[col.align],
|
|
10134
10376
|
col.className
|
|
10135
10377
|
),
|
|
10136
|
-
children: /* @__PURE__ */ (0,
|
|
10378
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(Skeleton, { className: "h-4 w-[60%]" })
|
|
10137
10379
|
},
|
|
10138
10380
|
col.id
|
|
10139
10381
|
))
|
|
10140
|
-
] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ (0,
|
|
10141
|
-
/* @__PURE__ */ (0,
|
|
10142
|
-
emptyDescription ? /* @__PURE__ */ (0,
|
|
10382
|
+
] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
|
|
10383
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)("p", { className: "font-medium text-foreground", children: emptyTitle }),
|
|
10384
|
+
emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
|
|
10143
10385
|
] }) }) }) : visibleRows.map((row) => {
|
|
10144
10386
|
const key = getRowKey(row);
|
|
10145
10387
|
const isSelected = selectedSet.has(key);
|
|
10146
|
-
return /* @__PURE__ */ (0,
|
|
10388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
|
|
10147
10389
|
"tr",
|
|
10148
10390
|
{
|
|
10149
10391
|
className: rowClass,
|
|
@@ -10159,12 +10401,12 @@ function DataTable({
|
|
|
10159
10401
|
tabIndex: onRowClick ? 0 : void 0,
|
|
10160
10402
|
role: onRowClick ? "button" : void 0,
|
|
10161
10403
|
children: [
|
|
10162
|
-
selectable ? /* @__PURE__ */ (0,
|
|
10404
|
+
selectable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10163
10405
|
"td",
|
|
10164
10406
|
{
|
|
10165
10407
|
className: cn(selectCellClass, cellPad),
|
|
10166
10408
|
onClick: (event) => event.stopPropagation(),
|
|
10167
|
-
children: /* @__PURE__ */ (0,
|
|
10409
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10168
10410
|
Checkbox,
|
|
10169
10411
|
{
|
|
10170
10412
|
checked: isSelected,
|
|
@@ -10174,7 +10416,7 @@ function DataTable({
|
|
|
10174
10416
|
)
|
|
10175
10417
|
}
|
|
10176
10418
|
) : null,
|
|
10177
|
-
columns.map((col) => /* @__PURE__ */ (0,
|
|
10419
|
+
columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10178
10420
|
"td",
|
|
10179
10421
|
{
|
|
10180
10422
|
className: cn(
|
|
@@ -10184,7 +10426,7 @@ function DataTable({
|
|
|
10184
10426
|
col.align && alignClass[col.align],
|
|
10185
10427
|
col.className
|
|
10186
10428
|
),
|
|
10187
|
-
children: col.truncate ? /* @__PURE__ */ (0,
|
|
10429
|
+
children: col.truncate ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
|
|
10188
10430
|
},
|
|
10189
10431
|
col.id
|
|
10190
10432
|
))
|
|
@@ -10193,7 +10435,7 @@ function DataTable({
|
|
|
10193
10435
|
key
|
|
10194
10436
|
);
|
|
10195
10437
|
}) }),
|
|
10196
|
-
hasFoot ? /* @__PURE__ */ (0,
|
|
10438
|
+
hasFoot ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("tfoot", { children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
|
|
10197
10439
|
"div",
|
|
10198
10440
|
{
|
|
10199
10441
|
className: cn(
|
|
@@ -10201,18 +10443,18 @@ function DataTable({
|
|
|
10201
10443
|
(showRowCount || footer || hasPager) && "justify-between"
|
|
10202
10444
|
),
|
|
10203
10445
|
children: [
|
|
10204
|
-
/* @__PURE__ */ (0,
|
|
10205
|
-
showRowCount ? /* @__PURE__ */ (0,
|
|
10446
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: footInnerClass, children: [
|
|
10447
|
+
showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { children: [
|
|
10206
10448
|
rowCountText,
|
|
10207
10449
|
selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
|
|
10208
|
-
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0,
|
|
10450
|
+
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { children: [
|
|
10209
10451
|
selectedSet.size,
|
|
10210
10452
|
" selected"
|
|
10211
10453
|
] }) : null,
|
|
10212
10454
|
footer
|
|
10213
10455
|
] }),
|
|
10214
|
-
hasPager ? /* @__PURE__ */ (0,
|
|
10215
|
-
/* @__PURE__ */ (0,
|
|
10456
|
+
hasPager ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
10457
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { className: "tabular-nums", children: [
|
|
10216
10458
|
pageIndex * pageSize + 1,
|
|
10217
10459
|
"\u2013",
|
|
10218
10460
|
Math.min(
|
|
@@ -10223,8 +10465,8 @@ function DataTable({
|
|
|
10223
10465
|
"of ",
|
|
10224
10466
|
sortedRows.length
|
|
10225
10467
|
] }),
|
|
10226
|
-
/* @__PURE__ */ (0,
|
|
10227
|
-
/* @__PURE__ */ (0,
|
|
10468
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
10469
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10228
10470
|
"button",
|
|
10229
10471
|
{
|
|
10230
10472
|
type: "button",
|
|
@@ -10232,10 +10474,10 @@ function DataTable({
|
|
|
10232
10474
|
onClick: () => setPage(pageIndex - 1),
|
|
10233
10475
|
disabled: pageIndex <= 0,
|
|
10234
10476
|
"aria-label": "Previous page",
|
|
10235
|
-
children: /* @__PURE__ */ (0,
|
|
10477
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
|
|
10236
10478
|
}
|
|
10237
10479
|
),
|
|
10238
|
-
/* @__PURE__ */ (0,
|
|
10480
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
10239
10481
|
"button",
|
|
10240
10482
|
{
|
|
10241
10483
|
type: "button",
|
|
@@ -10243,7 +10485,7 @@ function DataTable({
|
|
|
10243
10485
|
onClick: () => setPage(pageIndex + 1),
|
|
10244
10486
|
disabled: pageIndex >= pageCount - 1,
|
|
10245
10487
|
"aria-label": "Next page",
|
|
10246
|
-
children: /* @__PURE__ */ (0,
|
|
10488
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
|
|
10247
10489
|
}
|
|
10248
10490
|
)
|
|
10249
10491
|
] })
|
|
@@ -10256,7 +10498,7 @@ function DataTable({
|
|
|
10256
10498
|
|
|
10257
10499
|
// src/app/data/ChartPanel.tsx
|
|
10258
10500
|
var import_react55 = require("react");
|
|
10259
|
-
var
|
|
10501
|
+
var import_jsx_runtime91 = require("react/jsx-runtime");
|
|
10260
10502
|
var ChartPanel = ({
|
|
10261
10503
|
title,
|
|
10262
10504
|
description,
|
|
@@ -10274,14 +10516,14 @@ var ChartPanel = ({
|
|
|
10274
10516
|
const titleId = (0, import_react55.useId)();
|
|
10275
10517
|
const resolvedTitle = title ?? artifact?.title;
|
|
10276
10518
|
const hasHeader = Boolean(resolvedTitle || description || actions);
|
|
10277
|
-
const body = loading ? /* @__PURE__ */ (0,
|
|
10278
|
-
return /* @__PURE__ */ (0,
|
|
10519
|
+
const body = loading ? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(ChartArtifactView, { artifact, embedded: true, height }) : null);
|
|
10520
|
+
return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
|
|
10279
10521
|
"section",
|
|
10280
10522
|
{
|
|
10281
10523
|
className: cn(metricCardShellClass, "aui-app-chart-panel", className),
|
|
10282
10524
|
"aria-labelledby": resolvedTitle ? titleId : void 0,
|
|
10283
10525
|
children: [
|
|
10284
|
-
/* @__PURE__ */ (0,
|
|
10526
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
|
|
10285
10527
|
MetricCardHeader,
|
|
10286
10528
|
{
|
|
10287
10529
|
title: resolvedTitle,
|
|
@@ -10290,14 +10532,14 @@ var ChartPanel = ({
|
|
|
10290
10532
|
actions
|
|
10291
10533
|
}
|
|
10292
10534
|
),
|
|
10293
|
-
/* @__PURE__ */ (0,
|
|
10535
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
|
|
10294
10536
|
"div",
|
|
10295
10537
|
{
|
|
10296
10538
|
className: cn(
|
|
10297
10539
|
"relative min-h-0 w-full",
|
|
10298
10540
|
hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
|
|
10299
10541
|
),
|
|
10300
|
-
children: body ?? /* @__PURE__ */ (0,
|
|
10542
|
+
children: body ?? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
|
|
10301
10543
|
"div",
|
|
10302
10544
|
{
|
|
10303
10545
|
className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -10315,7 +10557,7 @@ var ChartPanel = ({
|
|
|
10315
10557
|
|
|
10316
10558
|
// src/app/data/MetricRow.tsx
|
|
10317
10559
|
var import_react56 = require("react");
|
|
10318
|
-
var
|
|
10560
|
+
var import_jsx_runtime92 = require("react/jsx-runtime");
|
|
10319
10561
|
var MetricRow = ({
|
|
10320
10562
|
title,
|
|
10321
10563
|
titleTag,
|
|
@@ -10341,13 +10583,13 @@ var MetricRow = ({
|
|
|
10341
10583
|
onMetricChange?.(id);
|
|
10342
10584
|
};
|
|
10343
10585
|
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
10344
|
-
return /* @__PURE__ */ (0,
|
|
10586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
|
|
10345
10587
|
"section",
|
|
10346
10588
|
{
|
|
10347
10589
|
className: cn(metricCardShellClass, className),
|
|
10348
10590
|
"aria-labelledby": title ? titleId : void 0,
|
|
10349
10591
|
children: [
|
|
10350
|
-
/* @__PURE__ */ (0,
|
|
10592
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
10351
10593
|
MetricCardHeader,
|
|
10352
10594
|
{
|
|
10353
10595
|
title,
|
|
@@ -10357,7 +10599,7 @@ var MetricRow = ({
|
|
|
10357
10599
|
actions
|
|
10358
10600
|
}
|
|
10359
10601
|
),
|
|
10360
|
-
/* @__PURE__ */ (0,
|
|
10602
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
10361
10603
|
"div",
|
|
10362
10604
|
{
|
|
10363
10605
|
role: selectable ? "group" : void 0,
|
|
@@ -10368,18 +10610,18 @@ var MetricRow = ({
|
|
|
10368
10610
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
10369
10611
|
hasHeader && "mt-3.5 border-t border-border/40"
|
|
10370
10612
|
),
|
|
10371
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0,
|
|
10613
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
|
|
10372
10614
|
"div",
|
|
10373
10615
|
{
|
|
10374
10616
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
10375
10617
|
"aria-hidden": true,
|
|
10376
10618
|
children: [
|
|
10377
|
-
/* @__PURE__ */ (0,
|
|
10378
|
-
/* @__PURE__ */ (0,
|
|
10619
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Skeleton, { className: "h-3 w-20" }),
|
|
10620
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Skeleton, { className: "h-7 w-24" })
|
|
10379
10621
|
]
|
|
10380
10622
|
},
|
|
10381
10623
|
`skeleton-${index}`
|
|
10382
|
-
)) : metrics.map((m, index) => /* @__PURE__ */ (0,
|
|
10624
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
10383
10625
|
MetricTile,
|
|
10384
10626
|
{
|
|
10385
10627
|
label: m.label,
|
|
@@ -10407,7 +10649,7 @@ var MetricRow = ({
|
|
|
10407
10649
|
|
|
10408
10650
|
// src/app/data/MetricChartCard.tsx
|
|
10409
10651
|
var import_react57 = require("react");
|
|
10410
|
-
var
|
|
10652
|
+
var import_jsx_runtime93 = require("react/jsx-runtime");
|
|
10411
10653
|
var MetricChartCard = ({
|
|
10412
10654
|
title,
|
|
10413
10655
|
titleTag,
|
|
@@ -10443,13 +10685,13 @@ var MetricChartCard = ({
|
|
|
10443
10685
|
};
|
|
10444
10686
|
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
10445
10687
|
const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
|
|
10446
|
-
return /* @__PURE__ */ (0,
|
|
10688
|
+
return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
|
|
10447
10689
|
"section",
|
|
10448
10690
|
{
|
|
10449
10691
|
className: cn(metricCardShellClass, className),
|
|
10450
10692
|
"aria-labelledby": title ? titleId : void 0,
|
|
10451
10693
|
children: [
|
|
10452
|
-
/* @__PURE__ */ (0,
|
|
10694
|
+
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10453
10695
|
MetricCardHeader,
|
|
10454
10696
|
{
|
|
10455
10697
|
title,
|
|
@@ -10459,7 +10701,7 @@ var MetricChartCard = ({
|
|
|
10459
10701
|
titleId
|
|
10460
10702
|
}
|
|
10461
10703
|
),
|
|
10462
|
-
/* @__PURE__ */ (0,
|
|
10704
|
+
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10463
10705
|
"div",
|
|
10464
10706
|
{
|
|
10465
10707
|
role: "group",
|
|
@@ -10470,18 +10712,18 @@ var MetricChartCard = ({
|
|
|
10470
10712
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
10471
10713
|
hasHeader && "mt-3.5 border-t border-border/40"
|
|
10472
10714
|
),
|
|
10473
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0,
|
|
10715
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
|
|
10474
10716
|
"div",
|
|
10475
10717
|
{
|
|
10476
10718
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
10477
10719
|
"aria-hidden": true,
|
|
10478
10720
|
children: [
|
|
10479
|
-
/* @__PURE__ */ (0,
|
|
10480
|
-
/* @__PURE__ */ (0,
|
|
10721
|
+
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Skeleton, { className: "h-3 w-20" }),
|
|
10722
|
+
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Skeleton, { className: "h-7 w-24" })
|
|
10481
10723
|
]
|
|
10482
10724
|
},
|
|
10483
10725
|
`skeleton-${index}`
|
|
10484
|
-
)) : metrics.map((m, index) => /* @__PURE__ */ (0,
|
|
10726
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10485
10727
|
MetricTile,
|
|
10486
10728
|
{
|
|
10487
10729
|
label: m.label,
|
|
@@ -10502,14 +10744,14 @@ var MetricChartCard = ({
|
|
|
10502
10744
|
))
|
|
10503
10745
|
}
|
|
10504
10746
|
),
|
|
10505
|
-
/* @__PURE__ */ (0,
|
|
10747
|
+
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10506
10748
|
Skeleton,
|
|
10507
10749
|
{
|
|
10508
10750
|
className: "w-full rounded-lg",
|
|
10509
10751
|
style: { height },
|
|
10510
10752
|
"aria-hidden": true
|
|
10511
10753
|
}
|
|
10512
|
-
) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0,
|
|
10754
|
+
) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10513
10755
|
LineAreaChart,
|
|
10514
10756
|
{
|
|
10515
10757
|
data: active.data,
|
|
@@ -10529,7 +10771,7 @@ var MetricChartCard = ({
|
|
|
10529
10771
|
ariaLabel: chartAriaLabel
|
|
10530
10772
|
},
|
|
10531
10773
|
active.id
|
|
10532
|
-
) : /* @__PURE__ */ (0,
|
|
10774
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
|
|
10533
10775
|
"div",
|
|
10534
10776
|
{
|
|
10535
10777
|
className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -10635,8 +10877,8 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
10635
10877
|
|
|
10636
10878
|
// src/ui/untitled-button.tsx
|
|
10637
10879
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
10638
|
-
var
|
|
10639
|
-
var
|
|
10880
|
+
var import_radix_ui9 = require("radix-ui");
|
|
10881
|
+
var import_jsx_runtime94 = require("react/jsx-runtime");
|
|
10640
10882
|
var SOLID_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-solid";
|
|
10641
10883
|
var BORDERED_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-bordered";
|
|
10642
10884
|
var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
|
|
@@ -10719,10 +10961,10 @@ var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
|
|
|
10719
10961
|
)
|
|
10720
10962
|
},
|
|
10721
10963
|
size: {
|
|
10722
|
-
sm: "h-
|
|
10723
|
-
md: "h-
|
|
10724
|
-
lg: "h-
|
|
10725
|
-
xl: "h-
|
|
10964
|
+
sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
|
|
10965
|
+
md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
|
|
10966
|
+
lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
|
|
10967
|
+
xl: "h-11 gap-2 rounded-lg px-4 text-base"
|
|
10726
10968
|
}
|
|
10727
10969
|
},
|
|
10728
10970
|
defaultVariants: {
|
|
@@ -10747,8 +10989,8 @@ function UntitledButton({
|
|
|
10747
10989
|
const isDisabled = disabled || isLoading;
|
|
10748
10990
|
const classes = cn(untitledButtonVariants({ color, size }), className);
|
|
10749
10991
|
if (asChild) {
|
|
10750
|
-
return /* @__PURE__ */ (0,
|
|
10751
|
-
|
|
10992
|
+
return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
|
|
10993
|
+
import_radix_ui9.Slot.Root,
|
|
10752
10994
|
{
|
|
10753
10995
|
className: classes,
|
|
10754
10996
|
"aria-disabled": isDisabled ? true : void 0,
|
|
@@ -10758,7 +11000,7 @@ function UntitledButton({
|
|
|
10758
11000
|
}
|
|
10759
11001
|
);
|
|
10760
11002
|
}
|
|
10761
|
-
return /* @__PURE__ */ (0,
|
|
11003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime94.jsxs)(
|
|
10762
11004
|
"button",
|
|
10763
11005
|
{
|
|
10764
11006
|
type,
|
|
@@ -10767,7 +11009,7 @@ function UntitledButton({
|
|
|
10767
11009
|
className: classes,
|
|
10768
11010
|
...props,
|
|
10769
11011
|
children: [
|
|
10770
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
11012
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
|
|
10771
11013
|
"span",
|
|
10772
11014
|
{
|
|
10773
11015
|
"aria-hidden": true,
|
|
@@ -10782,8 +11024,8 @@ function UntitledButton({
|
|
|
10782
11024
|
}
|
|
10783
11025
|
|
|
10784
11026
|
// src/ui/banner.tsx
|
|
10785
|
-
var
|
|
10786
|
-
var
|
|
11027
|
+
var import_lucide_react20 = require("lucide-react");
|
|
11028
|
+
var import_jsx_runtime95 = require("react/jsx-runtime");
|
|
10787
11029
|
var bannerSoftClass = {
|
|
10788
11030
|
default: "border-border/50 bg-muted/30 text-foreground/90 dark:bg-muted/15",
|
|
10789
11031
|
primary: "border-primary/15 bg-primary/5 text-primary-800 dark:text-primary-200 [&_[data-banner-icon]]:text-primary",
|
|
@@ -10828,7 +11070,7 @@ function Banner({
|
|
|
10828
11070
|
}) {
|
|
10829
11071
|
const isSolid = variant === "solid";
|
|
10830
11072
|
const isSingleLine = !title;
|
|
10831
|
-
return /* @__PURE__ */ (0,
|
|
11073
|
+
return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
|
|
10832
11074
|
"div",
|
|
10833
11075
|
{
|
|
10834
11076
|
"data-slot": "banner",
|
|
@@ -10843,7 +11085,7 @@ function Banner({
|
|
|
10843
11085
|
),
|
|
10844
11086
|
...props,
|
|
10845
11087
|
children: [
|
|
10846
|
-
icon ? /* @__PURE__ */ (0,
|
|
11088
|
+
icon ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
|
|
10847
11089
|
"span",
|
|
10848
11090
|
{
|
|
10849
11091
|
"data-banner-icon": true,
|
|
@@ -10854,9 +11096,9 @@ function Banner({
|
|
|
10854
11096
|
children: icon
|
|
10855
11097
|
}
|
|
10856
11098
|
) : null,
|
|
10857
|
-
/* @__PURE__ */ (0,
|
|
10858
|
-
title ? /* @__PURE__ */ (0,
|
|
10859
|
-
children ? /* @__PURE__ */ (0,
|
|
11099
|
+
/* @__PURE__ */ (0, import_jsx_runtime95.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
11100
|
+
title ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("p", { className: "font-medium tracking-tight", children: title }) : null,
|
|
11101
|
+
children ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
|
|
10860
11102
|
"div",
|
|
10861
11103
|
{
|
|
10862
11104
|
className: cn(
|
|
@@ -10867,8 +11109,8 @@ function Banner({
|
|
|
10867
11109
|
}
|
|
10868
11110
|
) : null
|
|
10869
11111
|
] }),
|
|
10870
|
-
actions ? /* @__PURE__ */ (0,
|
|
10871
|
-
onDismiss ? /* @__PURE__ */ (0,
|
|
11112
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
|
|
11113
|
+
onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
|
|
10872
11114
|
"button",
|
|
10873
11115
|
{
|
|
10874
11116
|
type: "button",
|
|
@@ -10879,7 +11121,7 @@ function Banner({
|
|
|
10879
11121
|
isSingleLine ? "self-center" : "-mt-0.5",
|
|
10880
11122
|
isSolid ? "opacity-80 hover:bg-background/15 hover:opacity-100" : "text-muted-foreground hover:bg-foreground/10 hover:text-foreground"
|
|
10881
11123
|
),
|
|
10882
|
-
children: /* @__PURE__ */ (0,
|
|
11124
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(import_lucide_react20.XIcon, { className: "size-4", "aria-hidden": true })
|
|
10883
11125
|
}
|
|
10884
11126
|
) : null
|
|
10885
11127
|
]
|
|
@@ -10888,7 +11130,7 @@ function Banner({
|
|
|
10888
11130
|
}
|
|
10889
11131
|
|
|
10890
11132
|
// src/ui/timeline.tsx
|
|
10891
|
-
var
|
|
11133
|
+
var import_jsx_runtime96 = require("react/jsx-runtime");
|
|
10892
11134
|
var timelineRowGap = {
|
|
10893
11135
|
sm: "pb-4",
|
|
10894
11136
|
default: "pb-6"
|
|
@@ -10921,16 +11163,16 @@ var toneStyles = {
|
|
|
10921
11163
|
}
|
|
10922
11164
|
};
|
|
10923
11165
|
function Timeline({ items, size = "default", className, ...props }) {
|
|
10924
|
-
return /* @__PURE__ */ (0,
|
|
11166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
|
|
10925
11167
|
const last = index === items.length - 1;
|
|
10926
11168
|
const tone = item.tone ?? "default";
|
|
10927
11169
|
const styles = toneStyles[tone];
|
|
10928
|
-
return /* @__PURE__ */ (0,
|
|
11170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(
|
|
10929
11171
|
"li",
|
|
10930
11172
|
{
|
|
10931
11173
|
className: cn("relative flex gap-3.5 last:pb-0", timelineRowGap[size]),
|
|
10932
11174
|
children: [
|
|
10933
|
-
!last ? /* @__PURE__ */ (0,
|
|
11175
|
+
!last ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
|
|
10934
11176
|
"span",
|
|
10935
11177
|
{
|
|
10936
11178
|
"aria-hidden": true,
|
|
@@ -10940,7 +11182,7 @@ function Timeline({ items, size = "default", className, ...props }) {
|
|
|
10940
11182
|
)
|
|
10941
11183
|
}
|
|
10942
11184
|
) : null,
|
|
10943
|
-
/* @__PURE__ */ (0,
|
|
11185
|
+
/* @__PURE__ */ (0, import_jsx_runtime96.jsx)("div", { className: "relative flex w-5 shrink-0 justify-center pt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
|
|
10944
11186
|
"span",
|
|
10945
11187
|
{
|
|
10946
11188
|
className: cn(
|
|
@@ -10948,7 +11190,7 @@ function Timeline({ items, size = "default", className, ...props }) {
|
|
|
10948
11190
|
styles.border,
|
|
10949
11191
|
item.icon ? "size-6" : "size-4"
|
|
10950
11192
|
),
|
|
10951
|
-
children: item.icon ? /* @__PURE__ */ (0,
|
|
11193
|
+
children: item.icon ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("div", { className: cn("text-xs", styles.icon), children: item.icon }) : /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
|
|
10952
11194
|
"span",
|
|
10953
11195
|
{
|
|
10954
11196
|
className: cn(
|
|
@@ -10959,12 +11201,12 @@ function Timeline({ items, size = "default", className, ...props }) {
|
|
|
10959
11201
|
)
|
|
10960
11202
|
}
|
|
10961
11203
|
) }),
|
|
10962
|
-
/* @__PURE__ */ (0,
|
|
10963
|
-
/* @__PURE__ */ (0,
|
|
10964
|
-
/* @__PURE__ */ (0,
|
|
10965
|
-
item.meta ? /* @__PURE__ */ (0,
|
|
11204
|
+
/* @__PURE__ */ (0, import_jsx_runtime96.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
|
|
11205
|
+
/* @__PURE__ */ (0, import_jsx_runtime96.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
|
|
11206
|
+
/* @__PURE__ */ (0, import_jsx_runtime96.jsx)("p", { className: "text-sm font-normal text-foreground", children: item.title }),
|
|
11207
|
+
item.meta ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
|
|
10966
11208
|
] }),
|
|
10967
|
-
item.description ? /* @__PURE__ */ (0,
|
|
11209
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("p", { className: "mt-0.5 text-[13px] leading-relaxed text-muted-foreground", children: item.description }) : null
|
|
10968
11210
|
] })
|
|
10969
11211
|
]
|
|
10970
11212
|
},
|
|
@@ -10978,8 +11220,8 @@ var React5 = __toESM(require("react"), 1);
|
|
|
10978
11220
|
var import_core2 = require("@dnd-kit/core");
|
|
10979
11221
|
var import_sortable = require("@dnd-kit/sortable");
|
|
10980
11222
|
var import_utilities = require("@dnd-kit/utilities");
|
|
10981
|
-
var
|
|
10982
|
-
var
|
|
11223
|
+
var import_lucide_react21 = require("lucide-react");
|
|
11224
|
+
var import_jsx_runtime97 = require("react/jsx-runtime");
|
|
10983
11225
|
var columnTitleToneClass = {
|
|
10984
11226
|
default: "text-foreground",
|
|
10985
11227
|
primary: "text-blue-600 dark:text-blue-400",
|
|
@@ -11041,7 +11283,7 @@ function SortableCard({
|
|
|
11041
11283
|
transition
|
|
11042
11284
|
};
|
|
11043
11285
|
const dragHandleProps = disabled ? void 0 : { ...attributes, ...listeners };
|
|
11044
|
-
return /* @__PURE__ */ (0,
|
|
11286
|
+
return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
|
|
11045
11287
|
"div",
|
|
11046
11288
|
{
|
|
11047
11289
|
ref: setNodeRef,
|
|
@@ -11056,7 +11298,7 @@ function SortableCard({
|
|
|
11056
11298
|
className
|
|
11057
11299
|
),
|
|
11058
11300
|
children: [
|
|
11059
|
-
!disabled && dragHandle === "auto" ? /* @__PURE__ */ (0,
|
|
11301
|
+
!disabled && dragHandle === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11060
11302
|
"button",
|
|
11061
11303
|
{
|
|
11062
11304
|
type: "button",
|
|
@@ -11064,7 +11306,7 @@ function SortableCard({
|
|
|
11064
11306
|
className: "absolute right-1.5 top-1.5 z-10 grid size-6 cursor-grab touch-none place-items-center rounded-md text-muted-foreground/40 opacity-0 transition hover:bg-foreground/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 group-hover/kanban-card:opacity-100 active:cursor-grabbing",
|
|
11065
11307
|
...attributes,
|
|
11066
11308
|
...listeners,
|
|
11067
|
-
children: /* @__PURE__ */ (0,
|
|
11309
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_lucide_react21.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
|
|
11068
11310
|
}
|
|
11069
11311
|
) : null,
|
|
11070
11312
|
renderCard(card, { column, isDragging, isOverlay: false, dragHandleProps })
|
|
@@ -11088,7 +11330,7 @@ function KanbanColumnView({
|
|
|
11088
11330
|
}) {
|
|
11089
11331
|
const tone = column.tone ?? "default";
|
|
11090
11332
|
const { setNodeRef, isOver } = (0, import_core2.useDroppable)({ id: column.id, disabled });
|
|
11091
|
-
return /* @__PURE__ */ (0,
|
|
11333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
|
|
11092
11334
|
"div",
|
|
11093
11335
|
{
|
|
11094
11336
|
"data-slot": "kanban-column",
|
|
@@ -11098,9 +11340,9 @@ function KanbanColumnView({
|
|
|
11098
11340
|
className
|
|
11099
11341
|
),
|
|
11100
11342
|
children: [
|
|
11101
|
-
renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ (0,
|
|
11102
|
-
/* @__PURE__ */ (0,
|
|
11103
|
-
/* @__PURE__ */ (0,
|
|
11343
|
+
renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
|
|
11344
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
11345
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11104
11346
|
"h3",
|
|
11105
11347
|
{
|
|
11106
11348
|
className: cn(
|
|
@@ -11110,12 +11352,12 @@ function KanbanColumnView({
|
|
|
11110
11352
|
children: column.title
|
|
11111
11353
|
}
|
|
11112
11354
|
),
|
|
11113
|
-
/* @__PURE__ */ (0,
|
|
11114
|
-
column.action ? /* @__PURE__ */ (0,
|
|
11355
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
|
|
11356
|
+
column.action ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("div", { className: "shrink-0", children: column.action }) : null
|
|
11115
11357
|
] }),
|
|
11116
|
-
column.description ? /* @__PURE__ */ (0,
|
|
11358
|
+
column.description ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
|
|
11117
11359
|
] }),
|
|
11118
|
-
/* @__PURE__ */ (0,
|
|
11360
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11119
11361
|
"div",
|
|
11120
11362
|
{
|
|
11121
11363
|
ref: setNodeRef,
|
|
@@ -11125,9 +11367,9 @@ function KanbanColumnView({
|
|
|
11125
11367
|
densityListClass[density],
|
|
11126
11368
|
isOver && "bg-muted/50"
|
|
11127
11369
|
),
|
|
11128
|
-
children: column.cards.length === 0 ? /* @__PURE__ */ (0,
|
|
11370
|
+
children: column.cards.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("div", { className: "flex flex-1 items-center justify-center rounded-xl border border-dashed border-border/60 px-2 py-8 text-center text-xs text-muted-foreground/70", children: emptyColumnLabel ?? "Drop here" }) : column.cards.map((card) => {
|
|
11129
11371
|
const id = getCardId(card);
|
|
11130
|
-
return /* @__PURE__ */ (0,
|
|
11372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11131
11373
|
SortableCard,
|
|
11132
11374
|
{
|
|
11133
11375
|
card,
|
|
@@ -11145,7 +11387,7 @@ function KanbanColumnView({
|
|
|
11145
11387
|
})
|
|
11146
11388
|
}
|
|
11147
11389
|
) }),
|
|
11148
|
-
column.footer ? /* @__PURE__ */ (0,
|
|
11390
|
+
column.footer ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
|
|
11149
11391
|
]
|
|
11150
11392
|
}
|
|
11151
11393
|
);
|
|
@@ -11284,7 +11526,7 @@ function Kanban({
|
|
|
11284
11526
|
onColumnsChange?.(next);
|
|
11285
11527
|
}
|
|
11286
11528
|
};
|
|
11287
|
-
return /* @__PURE__ */ (0,
|
|
11529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
|
|
11288
11530
|
import_core2.DndContext,
|
|
11289
11531
|
{
|
|
11290
11532
|
sensors,
|
|
@@ -11298,7 +11540,7 @@ function Kanban({
|
|
|
11298
11540
|
if (isControlled) setInternal(cloneColumns(columnsProp));
|
|
11299
11541
|
},
|
|
11300
11542
|
children: [
|
|
11301
|
-
/* @__PURE__ */ (0,
|
|
11543
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11302
11544
|
"div",
|
|
11303
11545
|
{
|
|
11304
11546
|
"data-slot": "kanban",
|
|
@@ -11309,7 +11551,7 @@ function Kanban({
|
|
|
11309
11551
|
density === "compact" ? "gap-3" : "gap-4",
|
|
11310
11552
|
className
|
|
11311
11553
|
),
|
|
11312
|
-
children: columns.map((column) => /* @__PURE__ */ (0,
|
|
11554
|
+
children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11313
11555
|
KanbanColumnView,
|
|
11314
11556
|
{
|
|
11315
11557
|
column,
|
|
@@ -11329,7 +11571,7 @@ function Kanban({
|
|
|
11329
11571
|
))
|
|
11330
11572
|
}
|
|
11331
11573
|
),
|
|
11332
|
-
/* @__PURE__ */ (0,
|
|
11574
|
+
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
|
|
11333
11575
|
"div",
|
|
11334
11576
|
{
|
|
11335
11577
|
"data-slot": "kanban-card-overlay",
|
|
@@ -11352,7 +11594,7 @@ function Kanban({
|
|
|
11352
11594
|
}
|
|
11353
11595
|
|
|
11354
11596
|
// src/chat/chat.tsx
|
|
11355
|
-
var
|
|
11597
|
+
var import_jsx_runtime98 = require("react/jsx-runtime");
|
|
11356
11598
|
function TimbalChat({
|
|
11357
11599
|
workforceId,
|
|
11358
11600
|
baseUrl,
|
|
@@ -11363,7 +11605,7 @@ function TimbalChat({
|
|
|
11363
11605
|
debug,
|
|
11364
11606
|
...threadProps
|
|
11365
11607
|
}) {
|
|
11366
|
-
return /* @__PURE__ */ (0,
|
|
11608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(
|
|
11367
11609
|
TimbalRuntimeProvider,
|
|
11368
11610
|
{
|
|
11369
11611
|
workforceId,
|
|
@@ -11373,7 +11615,7 @@ function TimbalChat({
|
|
|
11373
11615
|
attachmentsUploadUrl,
|
|
11374
11616
|
attachmentsAccept,
|
|
11375
11617
|
debug,
|
|
11376
|
-
children: /* @__PURE__ */ (0,
|
|
11618
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Thread, { ...threadProps })
|
|
11377
11619
|
}
|
|
11378
11620
|
);
|
|
11379
11621
|
}
|