@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +33 -0
  3. package/dist/app.cjs +884 -642
  4. package/dist/app.d.cts +4 -4
  5. package/dist/app.d.ts +4 -4
  6. package/dist/app.esm.js +6 -6
  7. package/dist/{chart-artifact-2OTDTRwM.d.ts → chart-artifact-BYl5C-dk.d.ts} +90 -29
  8. package/dist/{chart-artifact-CS3qyGIY.d.cts → chart-artifact-Dpt4t5sf.d.cts} +90 -29
  9. package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
  10. package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
  11. package/dist/chat.cjs +26 -26
  12. package/dist/chat.d.cts +3 -3
  13. package/dist/chat.d.ts +3 -3
  14. package/dist/chat.esm.js +3 -3
  15. package/dist/{chunk-TZI3ID3C.esm.js → chunk-24B4I4XC.esm.js} +3 -3
  16. package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
  17. package/dist/{chunk-WMKPT5BV.esm.js → chunk-HSL36SJ4.esm.js} +6 -6
  18. package/dist/chunk-JJOO4PR5.esm.js +391 -0
  19. package/dist/{chunk-AZL2WANO.esm.js → chunk-MBS7XHV2.esm.js} +28 -28
  20. package/dist/{chunk-5ECRZ5O7.esm.js → chunk-NO5AWNWT.esm.js} +224 -57
  21. package/dist/{chunk-ZNYAETFD.esm.js → chunk-R4RQT2XQ.esm.js} +2 -2
  22. package/dist/{chunk-JYDJRGDE.esm.js → chunk-TMP7RIA7.esm.js} +2 -2
  23. package/dist/{chunk-SZDYIRMB.esm.js → chunk-UVPXH4MB.esm.js} +647 -532
  24. package/dist/{chunk-IGHBLJV3.esm.js → chunk-WQIQW7EM.esm.js} +3 -2
  25. package/dist/{chunk-B4XAC4G7.esm.js → chunk-YYEI6XME.esm.js} +361 -527
  26. package/dist/{circular-progress-CDsJwIPF.d.cts → circular-progress-B9nnwzCu.d.cts} +1 -1
  27. package/dist/{circular-progress-CDsJwIPF.d.ts → circular-progress-B9nnwzCu.d.ts} +1 -1
  28. package/dist/cli/timbal-ui-lint.mjs +503 -0
  29. package/dist/index.cjs +1358 -856
  30. package/dist/index.d.cts +9 -8
  31. package/dist/index.d.ts +9 -8
  32. package/dist/index.esm.js +40 -20
  33. package/dist/{kanban-U5xNe9py.d.cts → kanban-FFBeaZPS.d.cts} +4 -4
  34. package/dist/{kanban-U5xNe9py.d.ts → kanban-FFBeaZPS.d.ts} +4 -4
  35. package/dist/{layout-B8r6Jbat.d.ts → layout-CuKeSY74.d.ts} +1 -1
  36. package/dist/{layout-Cu7Ijn04.d.cts → layout-PzVwkJyL.d.cts} +1 -1
  37. package/dist/site.cjs +71 -0
  38. package/dist/site.d.cts +15 -1
  39. package/dist/site.d.ts +15 -1
  40. package/dist/site.esm.js +12 -311
  41. package/dist/studio.cjs +31 -31
  42. package/dist/studio.d.cts +2 -2
  43. package/dist/studio.d.ts +2 -2
  44. package/dist/studio.esm.js +7 -7
  45. package/dist/{timbal-v2-button-B7vPs7gg.d.ts → timbal-v2-button-DCAZNyUx.d.cts} +1 -1
  46. package/dist/{timbal-v2-button-B7vPs7gg.d.cts → timbal-v2-button-DCAZNyUx.d.ts} +1 -1
  47. package/dist/ui.cjs +77 -77
  48. package/dist/ui.d.cts +3 -3
  49. package/dist/ui.d.ts +3 -3
  50. package/dist/ui.esm.js +15 -15
  51. package/dist/{welcome-NXZlcihe.d.cts → welcome-B00oH5Io.d.cts} +1 -1
  52. package/dist/{welcome-DduQAC4K.d.ts → welcome-DU-4NTjZ.d.ts} +1 -1
  53. 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={<div className="flex justify-end p-4"><ModeToggle /></div>}>
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 = 48;
1361
- var PILL_HEIGHT_PX = 40;
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-8 gap-1 rounded-md px-2.5 text-xs",
1732
- sm: "h-9 gap-1.5 rounded-lg px-3 text-sm",
1733
- md: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
1734
- lg: "h-11 gap-2 rounded-lg px-4 text-base",
1735
- xl: "h-12 gap-2 rounded-lg px-5 text-base",
1736
- default: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
1737
- icon: "h-10 w-10 rounded-lg",
1738
- "icon-xs": "h-8 w-8 rounded-md",
1739
- "icon-sm": "h-9 w-9 rounded-lg",
1740
- "icon-lg": "h-11 w-11 rounded-lg"
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-8 h-8",
1835
- sm: "min-h-9 h-9",
1836
- md: "min-h-10 h-10",
1837
- lg: "min-h-11 h-11"
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-8 min-w-8 size-8",
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-10 min-w-10 size-10",
1843
- lg: "min-h-11 min-w-11 size-11"
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-3",
1847
- sm: "px-4",
1848
- md: "px-5",
1849
- lg: "px-6"
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-9 px-3",
2075
- default: "h-10 px-3"
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 pad = strokeWidth + 1;
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 - pad * 2;
2409
- const innerH = height - pad * 2;
2459
+ const innerW = width - padX * 2;
2460
+ const innerH = height - padY * 2;
2410
2461
  const points = values.map((v, i) => ({
2411
- x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
2412
- y: pad + innerH - (v - min) / range * innerH
2462
+ x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
2463
+ y: padY + innerH - (v - min) / range * innerH
2413
2464
  }));
2414
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
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 - pad), fill: `url(#${uid}-spark)` })
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-6 duration-200 outline-none sm:max-w-lg",
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-8",
3339
- sm: "size-6",
3340
- lg: "size-10"
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-8 px-2" : "size-8",
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-4 py-1.5 text-xs font-normal transition-colors"
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-9 min-h-9 items-center justify-center gap-1.5 rounded-full px-3.5 py-0",
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 import_lucide_react17 = require("lucide-react");
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/popover.tsx
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, import_jsx_runtime86.jsx)(import_radix_ui7.Popover.Root, { "data-slot": "popover", ...props });
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, import_jsx_runtime86.jsx)(import_radix_ui7.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
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, import_jsx_runtime86.jsx)(import_radix_ui7.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
9385
- import_radix_ui7.Popover.Content,
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-4 outline-hidden"
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 import_jsx_runtime87 = require("react/jsx-runtime");
9408
- var DEFAULT_CONTACTS = [
9409
- { id: "1", name: "Pedro Olivares Sanchez", email: "polivares@timbal.ai", initials: "PE" },
9410
- { id: "2", name: "John Doe", email: "john@example.com", initials: "JD" },
9411
- { id: "3", name: "Sarah Smith", email: "sarah@example.com", initials: "SS" }
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 OPERATORS = [
9414
- { id: "greater_than", label: "Greater than..." },
9415
- { id: "less_than", label: "Less than..." },
9416
- { id: "equals", label: "Equals..." }
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
- filters,
9420
- onFiltersChange,
9421
- initialFilters,
9422
- contacts = DEFAULT_CONTACTS,
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 [activeMenu, setActiveMenu] = (0, import_react53.useState)("contact");
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 (filters) {
9460
- setSelectedContacts(filters.contacts ?? []);
9461
- setWalletInput(filters.walletAddress ?? "");
9462
- setAppliedWallet(filters.walletAddress ?? "");
9463
- setSelectedDatePreset(filters.lastInvoiceDate ?? null);
9464
- setCustomDateFrom(filters.customDateRange?.from ?? "");
9465
- setCustomDateTo(filters.customDateRange?.to ?? "");
9466
- setLtvOperator(filters.lifetimeValue?.operator ?? "greater_than");
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 handleWalletClear = () => {
9524
- setWalletInput("");
9525
- setAppliedWallet("");
9526
- notifyChanges({ walletAddress: "" });
9527
- setIsOpen(false);
9740
+ const clearAll = () => {
9741
+ if (!isControlled) setInternal({});
9742
+ onChange?.({});
9528
9743
  };
9529
- const handleDatePresetSelect = (presetId) => {
9530
- setSelectedDatePreset(presetId);
9531
- if (presetId !== "custom") {
9532
- notifyChanges({ lastInvoiceDate: presetId, customDateRange: void 0 });
9533
- setIsOpen(false);
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
- const handleCustomDateApply = () => {
9537
- notifyChanges({
9538
- lastInvoiceDate: "custom",
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
- variant: "outline",
9590
- size: "sm",
9591
- shape: "pill",
9592
- className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
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, import_jsx_runtime87.jsx)(
9598
- PopoverContent,
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
- variant: "list",
9601
- align: "start",
9602
- className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
9603
- children: /* @__PURE__ */ (0, import_jsx_runtime87.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: [
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
- 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",
9631
- style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
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 import_lucide_react18 = require("lucide-react");
10136
+ var import_lucide_react19 = require("lucide-react");
9895
10137
 
9896
10138
  // src/ui/skeleton.tsx
9897
- var import_jsx_runtime88 = require("react/jsx-runtime");
10139
+ var import_jsx_runtime89 = require("react/jsx-runtime");
9898
10140
  function Skeleton({ className, ...props }) {
9899
- return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
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 import_jsx_runtime89 = require("react/jsx-runtime");
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, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
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, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
10198
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
9957
10199
  }
9958
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
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, import_jsx_runtime89.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
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, import_jsx_runtime89.jsx)("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("table", { className: tableClass, children: [
10082
- caption ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("caption", { className: "sr-only", children: caption }) : null,
10083
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("tr", { children: [
10084
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsxs)(
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, import_jsx_runtime89.jsx)("span", { className: "truncate", children: col.header }),
10104
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
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, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("tr", { className: rowClass, "aria-hidden": true, children: [
10126
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
10127
- columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)(Skeleton, { className: "h-4 w-[60%]" })
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, import_jsx_runtime89.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
10141
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "font-medium text-foreground", children: emptyTitle }),
10142
- emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
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, import_jsx_runtime89.jsxs)(
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, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
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, import_jsx_runtime89.jsx)("tfoot", { children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(
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, import_jsx_runtime89.jsxs)("div", { className: footInnerClass, children: [
10205
- showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { children: [
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, import_jsx_runtime89.jsxs)("span", { children: [
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, import_jsx_runtime89.jsxs)("div", { className: "flex items-center gap-2", children: [
10215
- /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "tabular-nums", children: [
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, import_jsx_runtime89.jsxs)("div", { className: "flex items-center gap-1", children: [
10227
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)(import_lucide_react18.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
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, import_jsx_runtime89.jsx)(
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, import_jsx_runtime89.jsx)(import_lucide_react18.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
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 import_jsx_runtime90 = require("react/jsx-runtime");
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, import_jsx_runtime90.jsx)(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(ChartArtifactView, { artifact, embedded: true, height }) : null);
10278
- return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
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, import_jsx_runtime90.jsx)(
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, import_jsx_runtime90.jsx)(
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, import_jsx_runtime90.jsx)(
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 import_jsx_runtime91 = require("react/jsx-runtime");
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, import_jsx_runtime91.jsxs)(
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, import_jsx_runtime91.jsx)(
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, import_jsx_runtime91.jsx)(
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, import_jsx_runtime91.jsxs)(
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, import_jsx_runtime91.jsx)(Skeleton, { className: "h-3 w-20" }),
10378
- /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(Skeleton, { className: "h-7 w-24" })
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, import_jsx_runtime91.jsx)(
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 import_jsx_runtime92 = require("react/jsx-runtime");
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, import_jsx_runtime92.jsxs)(
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, import_jsx_runtime92.jsx)(
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, import_jsx_runtime92.jsx)(
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, import_jsx_runtime92.jsxs)(
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, import_jsx_runtime92.jsx)(Skeleton, { className: "h-3 w-20" }),
10480
- /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Skeleton, { className: "h-7 w-24" })
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, import_jsx_runtime92.jsx)(
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, import_jsx_runtime92.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
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, import_jsx_runtime92.jsx)(
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, import_jsx_runtime92.jsx)(
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 import_radix_ui8 = require("radix-ui");
10639
- var import_jsx_runtime93 = require("react/jsx-runtime");
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-9 gap-1.5 rounded-lg px-3 text-sm",
10723
- md: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
10724
- lg: "h-11 gap-2 rounded-lg px-4 text-base",
10725
- xl: "h-12 gap-2 rounded-lg px-5 text-base"
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, import_jsx_runtime93.jsx)(
10751
- import_radix_ui8.Slot.Root,
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, import_jsx_runtime93.jsxs)(
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, import_jsx_runtime93.jsx)(
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 import_lucide_react19 = require("lucide-react");
10786
- var import_jsx_runtime94 = require("react/jsx-runtime");
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, import_jsx_runtime94.jsxs)(
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, import_jsx_runtime94.jsx)(
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, import_jsx_runtime94.jsxs)("div", { className: "min-w-0 flex-1", children: [
10858
- title ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("p", { className: "font-medium tracking-tight", children: title }) : null,
10859
- children ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
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, import_jsx_runtime94.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
10871
- onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
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, import_jsx_runtime94.jsx)(import_lucide_react19.XIcon, { className: "size-4", "aria-hidden": true })
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 import_jsx_runtime95 = require("react/jsx-runtime");
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, import_jsx_runtime95.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
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, import_jsx_runtime95.jsxs)(
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, import_jsx_runtime95.jsx)(
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, import_jsx_runtime95.jsx)("div", { className: "relative flex w-5 shrink-0 justify-center pt-0.5", children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
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, import_jsx_runtime95.jsx)("div", { className: cn("text-xs", styles.icon), children: item.icon }) : /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
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, import_jsx_runtime95.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
10963
- /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
10964
- /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("p", { className: "text-sm font-normal text-foreground", children: item.title }),
10965
- item.meta ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
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, import_jsx_runtime95.jsx)("p", { className: "mt-0.5 text-[13px] leading-relaxed text-muted-foreground", children: item.description }) : null
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 import_lucide_react20 = require("lucide-react");
10982
- var import_jsx_runtime96 = require("react/jsx-runtime");
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, import_jsx_runtime96.jsxs)(
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, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.jsx)(import_lucide_react20.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
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, import_jsx_runtime96.jsxs)(
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, import_jsx_runtime96.jsxs)("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
11102
- /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)("div", { className: "flex items-center gap-2", children: [
11103
- /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.jsx)("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
11114
- column.action ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("div", { className: "shrink-0", children: column.action }) : null
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, import_jsx_runtime96.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
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, import_jsx_runtime96.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.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) => {
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, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
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, import_jsx_runtime96.jsxs)(
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, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.jsx)(
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, import_jsx_runtime96.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
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 import_jsx_runtime97 = require("react/jsx-runtime");
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, import_jsx_runtime97.jsx)(
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, import_jsx_runtime97.jsx)(Thread, { ...threadProps })
11618
+ children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Thread, { ...threadProps })
11377
11619
  }
11378
11620
  );
11379
11621
  }