@timbal-ai/timbal-react 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +25 -0
  3. package/dist/app.cjs +855 -640
  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-C2pZQsaP.d.ts} +81 -29
  8. package/dist/{chart-artifact-CS3qyGIY.d.cts → chart-artifact-VAqgH-My.d.cts} +81 -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-SZDYIRMB.esm.js → chunk-6SQMTBPL.esm.js} +618 -530
  17. package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
  18. package/dist/{chunk-WMKPT5BV.esm.js → chunk-HSL36SJ4.esm.js} +6 -6
  19. package/dist/chunk-JJOO4PR5.esm.js +391 -0
  20. package/dist/{chunk-AZL2WANO.esm.js → chunk-MBS7XHV2.esm.js} +28 -28
  21. package/dist/{chunk-5ECRZ5O7.esm.js → chunk-NO5AWNWT.esm.js} +224 -57
  22. package/dist/{chunk-ZNYAETFD.esm.js → chunk-R4RQT2XQ.esm.js} +2 -2
  23. package/dist/{chunk-JYDJRGDE.esm.js → chunk-TMP7RIA7.esm.js} +2 -2
  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/index.cjs +1327 -852
  29. package/dist/index.d.cts +9 -8
  30. package/dist/index.d.ts +9 -8
  31. package/dist/index.esm.js +40 -20
  32. package/dist/{kanban-U5xNe9py.d.cts → kanban-FFBeaZPS.d.cts} +4 -4
  33. package/dist/{kanban-U5xNe9py.d.ts → kanban-FFBeaZPS.d.ts} +4 -4
  34. package/dist/{layout-B8r6Jbat.d.ts → layout-CuKeSY74.d.ts} +1 -1
  35. package/dist/{layout-Cu7Ijn04.d.cts → layout-PzVwkJyL.d.cts} +1 -1
  36. package/dist/site.cjs +71 -0
  37. package/dist/site.d.cts +15 -1
  38. package/dist/site.d.ts +15 -1
  39. package/dist/site.esm.js +12 -311
  40. package/dist/studio.cjs +31 -31
  41. package/dist/studio.d.cts +2 -2
  42. package/dist/studio.d.ts +2 -2
  43. package/dist/studio.esm.js +7 -7
  44. package/dist/{timbal-v2-button-B7vPs7gg.d.ts → timbal-v2-button-DCAZNyUx.d.cts} +1 -1
  45. package/dist/{timbal-v2-button-B7vPs7gg.d.cts → timbal-v2-button-DCAZNyUx.d.ts} +1 -1
  46. package/dist/ui.cjs +77 -77
  47. package/dist/ui.d.cts +3 -3
  48. package/dist/ui.d.ts +3 -3
  49. package/dist/ui.esm.js +15 -15
  50. package/dist/{welcome-NXZlcihe.d.cts → welcome-B00oH5Io.d.cts} +1 -1
  51. package/dist/{welcome-DduQAC4K.d.ts → welcome-DU-4NTjZ.d.ts} +1 -1
  52. package/package.json +1 -1
package/dist/app.cjs CHANGED
@@ -419,7 +419,7 @@ Presentational groups \u2014 import from the package root, not from these paths:
419
419
 
420
420
  | Folder | Components |
421
421
  |--------|------------|
422
- | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
422
+ | \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`FilterDropdown\`, \`ChartPanel\` |
423
423
  | \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
424
424
  | \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
425
425
  | \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
@@ -488,6 +488,7 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
488
488
  | \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
489
489
  | \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
490
490
  | \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
491
+ | \`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
492
  | \`SearchInput\` | Filter field with consistent app styling. |
492
493
  | \`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
494
  | \`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\`. |
@@ -546,6 +547,21 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
546
547
  | \`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
548
  | \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
548
549
 
550
+ #### More \`/ui\` primitives (import from \`/ui\` or the package root)
551
+
552
+ 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.
553
+
554
+ | Component | Use for |
555
+ |-----------|---------|
556
+ | \`Stepper\` | Ordered step indicator for wizards / onboarding (horizontal or vertical; complete / active / upcoming states). |
557
+ | \`Rating\` | Star rating \u2014 interactive (keyboard + hover preview) or \`readOnly\`; controlled or uncontrolled. |
558
+ | \`NumberField\` | Numeric input with \u2212/+ steppers on the control surface; clamps to \`min\`/\`max\`, steps by \`step\`. |
559
+ | \`TagInput\` | Chips / token input; commits on Enter/comma, removes on Backspace, optional \`dedupe\`/\`max\`. |
560
+ | \`AvatarGroup\` | Overlapping avatar stack with an optional \`+N\` overflow chip (\`max\`, \`spacing\`). |
561
+ | \`CircularProgress\` | Lightweight SVG progress ring \u2014 determinate (optional center label) or indeterminate. |
562
+ | \`CopyButton\` | Click-to-copy with a transient check confirmation; icon-only or with a label. |
563
+ | \`Snippet\` | Single-line code / command on the elevated surface with a built-in copy button. |
564
+
549
565
  Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
550
566
 
551
567
  ### Block recipes \u2014 compose these (don't clone wholesale)
@@ -612,6 +628,7 @@ import {
612
628
  DataTable,
613
629
  FilterBar,
614
630
  FilterField,
631
+ FilterDropdown,
615
632
  AlertCard,
616
633
  CatalogCard,
617
634
  } from "@timbal-ai/timbal-react/app";
@@ -1357,8 +1374,8 @@ var SIDEBAR_WIDTH_COLLAPSED_PX = 52;
1357
1374
  var SIDEBAR_GAP_PX = 12;
1358
1375
  var SIDEBAR_CONTENT_GAP_PX = 8;
1359
1376
  var TOPBAR_GAP_PX = 8;
1360
- var TOPBAR_HEIGHT_PX = 48;
1361
- var PILL_HEIGHT_PX = 40;
1377
+ var TOPBAR_HEIGHT_PX = 44;
1378
+ var PILL_HEIGHT_PX = 36;
1362
1379
  var SIDEBAR_INSET_PX_EXPANDED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_PX + SIDEBAR_CONTENT_GAP_PX;
1363
1380
  var SIDEBAR_INSET_PX_COLLAPSED = SIDEBAR_GAP_PX + SIDEBAR_WIDTH_COLLAPSED_PX + SIDEBAR_CONTENT_GAP_PX;
1364
1381
  var px = (n) => `${n / 16}rem`;
@@ -1728,16 +1745,16 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
1728
1745
  )
1729
1746
  },
1730
1747
  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"
1748
+ xs: "h-7 gap-1 rounded-md px-2 text-xs",
1749
+ sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
1750
+ md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
1751
+ lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
1752
+ xl: "h-11 gap-2 rounded-lg px-4 text-base",
1753
+ default: "h-9 gap-1.5 rounded-lg px-3 text-sm",
1754
+ icon: "h-9 w-9 rounded-lg",
1755
+ "icon-xs": "h-7 w-7 rounded-md",
1756
+ "icon-sm": "h-8 w-8 rounded-md",
1757
+ "icon-lg": "h-10 w-10 rounded-lg"
1741
1758
  },
1742
1759
  shape: {
1743
1760
  pill: "rounded-full!",
@@ -1831,22 +1848,22 @@ var TIMBAL_V2_MODAL_SURFACE = cn(
1831
1848
  );
1832
1849
  var TIMBAL_V2_PRIMARY_GRADIENT = "bg-gradient-to-b from-primary-fill-from to-primary-fill-to";
1833
1850
  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"
1851
+ xs: "min-h-7 h-7",
1852
+ sm: "min-h-8 h-8",
1853
+ md: "min-h-9 h-9",
1854
+ lg: "min-h-10 h-10"
1838
1855
  };
1839
1856
  var TIMBAL_V2_SIZE_ICON = {
1840
- xs: "min-h-8 min-w-8 size-8",
1857
+ xs: "min-h-7 min-w-7 size-7",
1841
1858
  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"
1859
+ md: "min-h-9 min-w-9 size-9",
1860
+ lg: "min-h-10 min-w-10 size-10"
1844
1861
  };
1845
1862
  var TIMBAL_V2_SIZE_LABEL_PX = {
1846
- xs: "px-3",
1847
- sm: "px-4",
1848
- md: "px-5",
1849
- lg: "px-6"
1863
+ xs: "px-2.5",
1864
+ sm: "px-3",
1865
+ md: "px-3.5",
1866
+ lg: "px-4.5"
1850
1867
  };
1851
1868
  var TIMBAL_V2_FILL = {
1852
1869
  primary: [
@@ -2071,8 +2088,8 @@ var import_react = require("react");
2071
2088
 
2072
2089
  // src/design/control-surface.ts
2073
2090
  var CONTROL_SIZE = {
2074
- sm: "h-9 px-3",
2075
- default: "h-10 px-3"
2091
+ sm: "h-8 px-2.5",
2092
+ default: "h-9 px-3"
2076
2093
  };
2077
2094
  var CONTROL_SHAPE = {
2078
2095
  field: "rounded-lg",
@@ -2100,6 +2117,7 @@ var overlayListPanelClass = cn(
2100
2117
  overlaySurfaceClass,
2101
2118
  "overflow-hidden rounded-lg p-0 outline-hidden"
2102
2119
  );
2120
+ 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
2121
 
2104
2122
  // src/design/app-classes.ts
2105
2123
  var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
@@ -2394,30 +2412,36 @@ var Sparkline = ({
2394
2412
  height = 28,
2395
2413
  strokeWidth = 1.5,
2396
2414
  className,
2397
- ariaLabel = "Trend"
2415
+ ariaLabel = "Trend",
2416
+ interactive = false,
2417
+ labels,
2418
+ formatValue
2398
2419
  }) => {
2399
2420
  const uid = (0, import_react2.useId)();
2421
+ const containerRef = (0, import_react2.useRef)(null);
2422
+ const [activeIndex, setActiveIndex] = (0, import_react2.useState)(null);
2400
2423
  const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
2401
2424
  if (values.length === 0) {
2402
2425
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: cn("inline-block", className), style: { width, height } });
2403
2426
  }
2404
- const pad = strokeWidth + 1;
2427
+ const padX = 0;
2428
+ const padY = strokeWidth + 1;
2405
2429
  const min = Math.min(...values);
2406
2430
  const max = Math.max(...values);
2407
2431
  const range = max - min || 1;
2408
- const innerW = width - pad * 2;
2409
- const innerH = height - pad * 2;
2432
+ const innerW = width - padX * 2;
2433
+ const innerH = height - padY * 2;
2410
2434
  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
2435
+ x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
2436
+ y: padY + innerH - (v - min) / range * innerH
2413
2437
  }));
2414
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2438
+ const svg = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2415
2439
  "svg",
2416
2440
  {
2417
2441
  width,
2418
2442
  height,
2419
2443
  viewBox: `0 0 ${width} ${height}`,
2420
- className: cn("block", className),
2444
+ className: cn("block", interactive ? "h-full w-full" : className),
2421
2445
  role: "img",
2422
2446
  "aria-label": ariaLabel,
2423
2447
  preserveAspectRatio: "none",
@@ -2427,7 +2451,7 @@ var Sparkline = ({
2427
2451
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
2428
2452
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
2429
2453
  ] }) }),
2430
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
2454
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: monotoneAreaPath(points, height - padY), fill: `url(#${uid}-spark)` })
2431
2455
  ] }),
2432
2456
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2433
2457
  "path",
@@ -2439,7 +2463,81 @@ var Sparkline = ({
2439
2463
  strokeLinecap: "round",
2440
2464
  strokeLinejoin: "round"
2441
2465
  }
2442
- )
2466
+ ),
2467
+ interactive && activeIndex != null && points[activeIndex] ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2468
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2469
+ "line",
2470
+ {
2471
+ x1: points[activeIndex].x,
2472
+ x2: points[activeIndex].x,
2473
+ y1: 0,
2474
+ y2: height,
2475
+ stroke: color,
2476
+ strokeWidth: 1,
2477
+ strokeOpacity: 0.3,
2478
+ vectorEffect: "non-scaling-stroke"
2479
+ }
2480
+ ),
2481
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2482
+ "circle",
2483
+ {
2484
+ cx: points[activeIndex].x,
2485
+ cy: points[activeIndex].y,
2486
+ r: 2.75,
2487
+ fill: color,
2488
+ stroke: "var(--background, #fff)",
2489
+ strokeWidth: 1.5,
2490
+ vectorEffect: "non-scaling-stroke"
2491
+ }
2492
+ )
2493
+ ] }) : null
2494
+ ]
2495
+ }
2496
+ );
2497
+ if (!interactive) return svg;
2498
+ const onMove = (e) => {
2499
+ const rect = e.currentTarget.getBoundingClientRect();
2500
+ if (rect.width === 0) return;
2501
+ const fraction = (e.clientX - rect.left) / rect.width;
2502
+ const index = Math.max(
2503
+ 0,
2504
+ Math.min(values.length - 1, Math.round(fraction * (values.length - 1)))
2505
+ );
2506
+ setActiveIndex(index);
2507
+ };
2508
+ const active = activeIndex != null ? points[activeIndex] : null;
2509
+ const formattedValue = activeIndex != null ? formatValue ? formatValue(values[activeIndex], activeIndex) : formatCompact(values[activeIndex]) : null;
2510
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2511
+ "span",
2512
+ {
2513
+ ref: containerRef,
2514
+ className: cn("relative block touch-none", className),
2515
+ style: { width: "100%", height: "100%" },
2516
+ onPointerMove: onMove,
2517
+ onPointerLeave: () => setActiveIndex(null),
2518
+ children: [
2519
+ svg,
2520
+ active ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2521
+ "span",
2522
+ {
2523
+ "aria-hidden": true,
2524
+ className: cn(
2525
+ "pointer-events-none absolute z-30 -translate-x-1/2 -translate-y-full whitespace-nowrap",
2526
+ "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)]",
2527
+ "border-white/10 bg-gradient-to-b from-neutral-800 to-neutral-950 text-white",
2528
+ "dark:border-black/10 dark:from-white dark:to-neutral-100 dark:text-neutral-900"
2529
+ ),
2530
+ style: {
2531
+ left: `${active.x / width * 100}%`,
2532
+ top: `${active.y / height * 100}%`,
2533
+ marginTop: -8
2534
+ },
2535
+ children: [
2536
+ 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,
2537
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: formattedValue })
2538
+ ]
2539
+ }
2540
+ ) : null
2443
2541
  ]
2444
2542
  }
2445
2543
  );
@@ -2457,6 +2555,15 @@ var inlineTrendToneClass = {
2457
2555
  down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
2458
2556
  neutral: "text-muted-foreground/80"
2459
2557
  };
2558
+ var sparklineToneColor = {
2559
+ up: "var(--primary, #3b82f6)",
2560
+ down: "var(--destructive, #f43f5e)",
2561
+ neutral: "var(--muted-foreground, #64748b)"
2562
+ };
2563
+ var sparklineBandBleed = {
2564
+ default: "-mx-4 -mb-3 h-10",
2565
+ compact: "-mx-3 -mb-2 h-8"
2566
+ };
2460
2567
  var activeToneClass = {
2461
2568
  default: "bg-foreground dark:bg-white",
2462
2569
  primary: "bg-primary",
@@ -2486,8 +2593,10 @@ var MetricTile = ({
2486
2593
  ariaLabel,
2487
2594
  className
2488
2595
  }) => {
2596
+ const density = useAppDensity();
2489
2597
  const metricTileBaseClass = useAppDensityClass("metricTile");
2490
2598
  const hasSparkline = Boolean(sparkline || sparklineData);
2599
+ const bandBleed = sparklineBandBleed[density === "compact" ? "compact" : "default"];
2491
2600
  const content = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2492
2601
  active ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2493
2602
  "span",
@@ -2499,17 +2608,6 @@ var MetricTile = ({
2499
2608
  )
2500
2609
  }
2501
2610
  ) : 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
2611
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
2514
2612
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
2515
2613
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex items-center gap-2", children: [
@@ -2528,7 +2626,28 @@ var MetricTile = ({
2528
2626
  }
2529
2627
  ) : null
2530
2628
  ] })
2531
- ] })
2629
+ ] }),
2630
+ hasSparkline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2631
+ "div",
2632
+ {
2633
+ className: cn(
2634
+ "relative z-10 mt-2",
2635
+ bandBleed
2636
+ ),
2637
+ children: sparkline ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2638
+ Sparkline,
2639
+ {
2640
+ data: sparklineData,
2641
+ width: 160,
2642
+ height: 40,
2643
+ interactive: true,
2644
+ className: "h-full w-full opacity-90",
2645
+ color: sparklineToneColor[trendTone],
2646
+ ...sparklineConfig
2647
+ }
2648
+ )
2649
+ }
2650
+ ) : null
2532
2651
  ] });
2533
2652
  const divider = showDivider ? metricCellDividerClass : void 0;
2534
2653
  if (onSelect) {
@@ -3277,7 +3396,7 @@ function DialogContent({
3277
3396
  "data-slot": "dialog-content",
3278
3397
  className: cn(
3279
3398
  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",
3399
+ "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
3400
  className
3282
3401
  ),
3283
3402
  ...props,
@@ -3335,9 +3454,9 @@ function isBrandedVariant(variant) {
3335
3454
  return variant === "secondary" || variant === "primary" || variant === "chart";
3336
3455
  }
3337
3456
  var AVATAR_SIZE_CLASS = {
3338
- default: "size-8",
3339
- sm: "size-6",
3340
- lg: "size-10"
3457
+ default: "size-7",
3458
+ sm: "size-5",
3459
+ lg: "size-9"
3341
3460
  };
3342
3461
  function Avatar({
3343
3462
  className,
@@ -5274,7 +5393,7 @@ var BadgeNode = ({ node }) => {
5274
5393
  "span",
5275
5394
  {
5276
5395
  className: cn(
5277
- "aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
5396
+ "aui-ui-badge inline-flex w-fit shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium",
5278
5397
  BADGE_TONE[node.tone ?? "default"],
5279
5398
  node.className
5280
5399
  ),
@@ -7935,7 +8054,7 @@ var StatusBadge = ({
7935
8054
  "span",
7936
8055
  {
7937
8056
  className: cn(
7938
- "aui-app-status-badge inline-flex items-center gap-1.5 rounded-full px-2 py-0.5",
8057
+ "aui-app-status-badge inline-flex w-fit shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5",
7939
8058
  "text-xs font-medium leading-none ring-1 ring-inset",
7940
8059
  statusBadgeToneClass[tone],
7941
8060
  className
@@ -8394,7 +8513,7 @@ function CopyButton({
8394
8513
  "inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium text-muted-foreground transition-colors",
8395
8514
  "hover:bg-accent hover:text-foreground data-[copied=true]:text-emerald-600 dark:data-[copied=true]:text-emerald-400",
8396
8515
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15",
8397
- children ? "h-8 px-2" : "size-8",
8516
+ children ? "h-7 px-1.5" : "size-7",
8398
8517
  className
8399
8518
  ),
8400
8519
  ...props,
@@ -8925,10 +9044,10 @@ var pillSegmentedTrackFlushClass = cn(
8925
9044
  "h-[var(--studio-chrome-pill-height)] items-center gap-0.5 overflow-visible p-0.5"
8926
9045
  );
8927
9046
  var pillSegmentedSegmentClass = cn(
8928
- "relative flex items-center gap-1.5 rounded-full px-4 py-1.5 text-xs font-normal transition-colors"
9047
+ "relative flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-normal transition-colors"
8929
9048
  );
8930
9049
  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",
9050
+ "relative box-border inline-flex h-8 min-h-8 items-center justify-center gap-1.5 rounded-full px-3 py-0",
8932
9051
  "text-sm font-normal leading-tight transition-colors"
8933
9052
  );
8934
9053
  var pillSegmentedActiveIndicatorClass = cn(
@@ -9328,7 +9447,7 @@ var FilterField = ({
9328
9447
 
9329
9448
  // src/app/data/FilterDropdown.tsx
9330
9449
  var import_react53 = require("react");
9331
- var import_lucide_react17 = require("lucide-react");
9450
+ var import_lucide_react18 = require("lucide-react");
9332
9451
 
9333
9452
  // src/ui/checkbox.tsx
9334
9453
  var import_radix_ui6 = require("radix-ui");
@@ -9361,18 +9480,142 @@ function Checkbox({
9361
9480
  );
9362
9481
  }
9363
9482
 
9364
- // src/ui/popover.tsx
9483
+ // src/ui/select.tsx
9365
9484
  var import_radix_ui7 = require("radix-ui");
9485
+ var import_lucide_react17 = require("lucide-react");
9366
9486
  var import_jsx_runtime86 = require("react/jsx-runtime");
9487
+ function Select({
9488
+ ...props
9489
+ }) {
9490
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Root, { "data-slot": "select", ...props });
9491
+ }
9492
+ function SelectValue({
9493
+ ...props
9494
+ }) {
9495
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Value, { "data-slot": "select-value", ...props });
9496
+ }
9497
+ function SelectTrigger({
9498
+ className,
9499
+ size = "default",
9500
+ children,
9501
+ ...props
9502
+ }) {
9503
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
9504
+ import_radix_ui7.Select.Trigger,
9505
+ {
9506
+ "data-slot": "select-trigger",
9507
+ "data-size": size,
9508
+ className: cn(
9509
+ controlClass({ size }),
9510
+ "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",
9511
+ className
9512
+ ),
9513
+ ...props,
9514
+ children: [
9515
+ children,
9516
+ /* @__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" }) })
9517
+ ]
9518
+ }
9519
+ );
9520
+ }
9521
+ function SelectContent({
9522
+ className,
9523
+ children,
9524
+ position = "popper",
9525
+ ...props
9526
+ }) {
9527
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
9528
+ import_radix_ui7.Select.Content,
9529
+ {
9530
+ "data-slot": "select-content",
9531
+ className: cn(
9532
+ overlayListPanelClass,
9533
+ "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",
9534
+ 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",
9535
+ className
9536
+ ),
9537
+ position,
9538
+ ...props,
9539
+ children: [
9540
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SelectScrollUpButton, {}),
9541
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
9542
+ import_radix_ui7.Select.Viewport,
9543
+ {
9544
+ className: cn(
9545
+ "p-1",
9546
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
9547
+ ),
9548
+ children
9549
+ }
9550
+ ),
9551
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(SelectScrollDownButton, {})
9552
+ ]
9553
+ }
9554
+ ) });
9555
+ }
9556
+ function SelectItem({
9557
+ className,
9558
+ children,
9559
+ ...props
9560
+ }) {
9561
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(
9562
+ import_radix_ui7.Select.Item,
9563
+ {
9564
+ "data-slot": "select-item",
9565
+ className: cn(
9566
+ overlayItemClass,
9567
+ "w-full py-1 pr-8 pl-2 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
9568
+ className
9569
+ ),
9570
+ ...props,
9571
+ children: [
9572
+ /* @__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" }) }) }),
9573
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Select.ItemText, { children })
9574
+ ]
9575
+ }
9576
+ );
9577
+ }
9578
+ function SelectScrollUpButton({
9579
+ className,
9580
+ ...props
9581
+ }) {
9582
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
9583
+ import_radix_ui7.Select.ScrollUpButton,
9584
+ {
9585
+ "data-slot": "select-scroll-up-button",
9586
+ className: cn("flex cursor-default items-center justify-center py-1", className),
9587
+ ...props,
9588
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.ChevronUpIcon, { className: "size-4" })
9589
+ }
9590
+ );
9591
+ }
9592
+ function SelectScrollDownButton({
9593
+ className,
9594
+ ...props
9595
+ }) {
9596
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
9597
+ import_radix_ui7.Select.ScrollDownButton,
9598
+ {
9599
+ "data-slot": "select-scroll-down-button",
9600
+ className: cn("flex cursor-default items-center justify-center py-1", className),
9601
+ ...props,
9602
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react17.ChevronDownIcon, { className: "size-4" })
9603
+ }
9604
+ );
9605
+ }
9606
+
9607
+ // src/ui/popover.tsx
9608
+ var import_radix_ui8 = require("radix-ui");
9609
+ var import_jsx_runtime87 = require("react/jsx-runtime");
9367
9610
  function Popover({
9368
9611
  ...props
9369
9612
  }) {
9370
- return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Popover.Root, { "data-slot": "popover", ...props });
9613
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Root, { "data-slot": "popover", ...props });
9371
9614
  }
9372
9615
  function PopoverTrigger({
9373
9616
  ...props
9374
9617
  }) {
9375
- return /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_radix_ui7.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
9618
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
9376
9619
  }
9377
9620
  function PopoverContent({
9378
9621
  className,
@@ -9381,8 +9624,8 @@ function PopoverContent({
9381
9624
  variant = "default",
9382
9625
  ...props
9383
9626
  }) {
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,
9627
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_radix_ui8.Popover.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
9628
+ import_radix_ui8.Popover.Content,
9386
9629
  {
9387
9630
  "data-slot": "popover-content",
9388
9631
  "data-variant": variant,
@@ -9394,7 +9637,7 @@ function PopoverContent({
9394
9637
  "min-w-[8rem] origin-[var(--radix-popover-content-transform-origin)]"
9395
9638
  ) : cn(
9396
9639
  overlaySurfaceClass,
9397
- "w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-4 outline-hidden"
9640
+ "w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-xl p-3 outline-hidden"
9398
9641
  ),
9399
9642
  className
9400
9643
  ),
@@ -9404,499 +9647,471 @@ function PopoverContent({
9404
9647
  }
9405
9648
 
9406
9649
  // 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" }
9650
+ var import_jsx_runtime88 = require("react/jsx-runtime");
9651
+ var DEFAULT_PRESETS = [
9652
+ { id: "last_7_days", label: "Last 7 days" },
9653
+ { id: "last_30_days", label: "Last 30 days" },
9654
+ { id: "last_90_days", label: "Last 90 days" },
9655
+ { id: "this_month", label: "This month" },
9656
+ { id: "this_year", label: "This year" },
9657
+ { id: "custom", label: "Custom range" }
9412
9658
  ];
9413
- var OPERATORS = [
9414
- { id: "greater_than", label: "Greater than..." },
9415
- { id: "less_than", label: "Less than..." },
9416
- { id: "equals", label: "Equals..." }
9659
+ var DEFAULT_OPERATORS = [
9660
+ { id: "gt", label: "Greater than" },
9661
+ { id: "lt", label: "Less than" },
9662
+ { id: "eq", label: "Equals" }
9417
9663
  ];
9664
+ function asArray(v) {
9665
+ return Array.isArray(v) ? v : [];
9666
+ }
9667
+ function asText(v) {
9668
+ return typeof v === "string" ? v : "";
9669
+ }
9670
+ function asDate(v) {
9671
+ return v && !Array.isArray(v) && typeof v === "object" && "preset" in v ? v : { preset: null };
9672
+ }
9673
+ function asNumeric(v) {
9674
+ return v && !Array.isArray(v) && typeof v === "object" && "operator" in v ? v : { operator: "gt", value: "" };
9675
+ }
9676
+ var OPERATOR_SYMBOL = {
9677
+ gt: ">",
9678
+ lt: "<",
9679
+ eq: "="
9680
+ };
9418
9681
  function FilterDropdown({
9419
- filters,
9420
- onFiltersChange,
9421
- initialFilters,
9422
- contacts = DEFAULT_CONTACTS,
9682
+ fields,
9683
+ value,
9684
+ defaultValue,
9685
+ onChange,
9686
+ label = "Filter",
9687
+ align = "start",
9688
+ showActiveChips = true,
9423
9689
  className
9424
9690
  }) {
9425
9691
  const [isOpen, setIsOpen] = (0, import_react53.useState)(false);
9426
- const [activeMenu, setActiveMenu] = (0, import_react53.useState)("contact");
9692
+ const [activeId, setActiveId] = (0, import_react53.useState)(fields[0]?.id ?? null);
9427
9693
  const [isMobile, setIsMobile] = (0, import_react53.useState)(false);
9694
+ const isControlled = value !== void 0;
9695
+ const [internal, setInternal] = (0, import_react53.useState)(defaultValue ?? {});
9696
+ const values = isControlled ? value : internal;
9428
9697
  (0, import_react53.useEffect)(() => {
9429
9698
  const checkMobile = () => setIsMobile(window.innerWidth < 768);
9430
9699
  checkMobile();
9431
9700
  window.addEventListener("resize", checkMobile);
9432
9701
  return () => window.removeEventListener("resize", checkMobile);
9433
9702
  }, []);
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
9703
  (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);
9704
+ if (!fields.some((f) => f.id === activeId)) {
9705
+ setActiveId(fields[0]?.id ?? null);
9706
+ }
9707
+ }, [fields, activeId]);
9708
+ const commit = (id, next) => {
9709
+ const merged = { ...values, [id]: next };
9710
+ if (!isControlled) setInternal(merged);
9711
+ onChange?.(merged);
9522
9712
  };
9523
- const handleWalletClear = () => {
9524
- setWalletInput("");
9525
- setAppliedWallet("");
9526
- notifyChanges({ walletAddress: "" });
9527
- setIsOpen(false);
9713
+ const clearAll = () => {
9714
+ if (!isControlled) setInternal({});
9715
+ onChange?.({});
9528
9716
  };
9529
- const handleDatePresetSelect = (presetId) => {
9530
- setSelectedDatePreset(presetId);
9531
- if (presetId !== "custom") {
9532
- notifyChanges({ lastInvoiceDate: presetId, customDateRange: void 0 });
9533
- setIsOpen(false);
9717
+ const activeIdx = fields.findIndex((f) => f.id === activeId);
9718
+ const activeField = activeIdx >= 0 ? fields[activeIdx] : void 0;
9719
+ const chips = [];
9720
+ for (const field of fields) {
9721
+ const v = values[field.id];
9722
+ if (field.type === "multiselect") {
9723
+ const selected = asArray(v);
9724
+ for (const optionValue of selected) {
9725
+ const opt = field.options?.find((o) => o.value === optionValue);
9726
+ chips.push({
9727
+ id: `${field.id}:${optionValue}`,
9728
+ label: `${field.label}: ${opt?.label ?? optionValue}`,
9729
+ remove: () => commit(field.id, selected.filter((x) => x !== optionValue))
9730
+ });
9731
+ }
9732
+ } else if (field.type === "text") {
9733
+ const text = asText(v);
9734
+ if (text) {
9735
+ chips.push({
9736
+ id: field.id,
9737
+ label: `${field.label}: ${text}`,
9738
+ remove: () => commit(field.id, "")
9739
+ });
9740
+ }
9741
+ } else if (field.type === "numeric") {
9742
+ const n = asNumeric(v);
9743
+ if (n.value) {
9744
+ chips.push({
9745
+ id: field.id,
9746
+ label: `${field.label} ${OPERATOR_SYMBOL[n.operator]} ${n.value}`,
9747
+ remove: () => commit(field.id, null)
9748
+ });
9749
+ }
9750
+ } else if (field.type === "daterange") {
9751
+ const d = asDate(v);
9752
+ if (d.preset) {
9753
+ 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;
9754
+ chips.push({
9755
+ id: field.id,
9756
+ label: `${field.label}: ${presetLabel}`,
9757
+ remove: () => commit(field.id, { preset: null })
9758
+ });
9759
+ }
9534
9760
  }
9761
+ }
9762
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
9763
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
9764
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9765
+ Button,
9766
+ {
9767
+ variant: "outline",
9768
+ size: "sm",
9769
+ className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
9770
+ iconLeading: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.ListFilterIcon, { className: "size-4" }),
9771
+ children: label
9772
+ }
9773
+ ) }),
9774
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9775
+ PopoverContent,
9776
+ {
9777
+ variant: "list",
9778
+ align,
9779
+ className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
9780
+ 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: [
9781
+ /* @__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) => {
9782
+ const isActive = activeId === field.id;
9783
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
9784
+ "button",
9785
+ {
9786
+ type: "button",
9787
+ className: cn(
9788
+ "flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
9789
+ isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
9790
+ ),
9791
+ onMouseEnter: () => !isMobile && setActiveId(field.id),
9792
+ onClick: () => setActiveId(field.id),
9793
+ children: [
9794
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: "flex items-center gap-2", children: [
9795
+ field.icon,
9796
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { children: field.label })
9797
+ ] }),
9798
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
9799
+ ]
9800
+ },
9801
+ field.id
9802
+ );
9803
+ }) }),
9804
+ activeField && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9805
+ "div",
9806
+ {
9807
+ 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",
9808
+ style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
9809
+ children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9810
+ FilterFieldControl,
9811
+ {
9812
+ field: activeField,
9813
+ value: values[activeField.id],
9814
+ onChange: (next) => commit(activeField.id, next),
9815
+ onClose: () => setIsOpen(false)
9816
+ }
9817
+ )
9818
+ }
9819
+ )
9820
+ ] })
9821
+ }
9822
+ )
9823
+ ] }),
9824
+ showActiveChips && chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(FilterChip, { label: chip.label, onRemove: chip.remove }, chip.id)),
9825
+ showActiveChips && chips.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9826
+ "button",
9827
+ {
9828
+ type: "button",
9829
+ onClick: clearAll,
9830
+ className: "rounded-full px-3 py-1 text-sm font-medium text-muted-foreground outline-none transition-colors hover:text-foreground",
9831
+ children: "Clear all"
9832
+ }
9833
+ )
9834
+ ] });
9835
+ }
9836
+ function FilterChip({ label, onRemove }) {
9837
+ 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: [
9838
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate", children: label }),
9839
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9840
+ "button",
9841
+ {
9842
+ type: "button",
9843
+ onClick: onRemove,
9844
+ "aria-label": `Remove ${label}`,
9845
+ className: "flex size-5 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground",
9846
+ children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(import_lucide_react18.XIcon, { className: "size-3.5" })
9847
+ }
9848
+ )
9849
+ ] });
9850
+ }
9851
+ function FilterFieldControl({
9852
+ field,
9853
+ value,
9854
+ onChange,
9855
+ onClose
9856
+ }) {
9857
+ switch (field.type) {
9858
+ case "multiselect":
9859
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(MultiSelectControl, { field, value: asArray(value), onChange });
9860
+ case "text":
9861
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(TextControl, { field, value: asText(value), onChange, onClose });
9862
+ case "daterange":
9863
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(DateRangeControl, { field, value: asDate(value), onChange, onClose });
9864
+ case "numeric":
9865
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(NumericControl, { field, value: asNumeric(value), onChange, onClose });
9866
+ default:
9867
+ return null;
9868
+ }
9869
+ }
9870
+ function MultiSelectControl({
9871
+ field,
9872
+ value,
9873
+ onChange
9874
+ }) {
9875
+ const options = field.options ?? [];
9876
+ const [search, setSearch] = (0, import_react53.useState)("");
9877
+ const searchable = field.searchable ?? options.length > 8;
9878
+ const filtered = (0, import_react53.useMemo)(() => {
9879
+ if (!search) return options;
9880
+ const q = search.toLowerCase();
9881
+ return options.filter(
9882
+ (o) => o.label.toLowerCase().includes(q) || o.hint?.toLowerCase().includes(q)
9883
+ );
9884
+ }, [options, search]);
9885
+ const toggle = (optionValue) => {
9886
+ onChange(
9887
+ value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
9888
+ );
9535
9889
  };
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,
9890
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
9891
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9892
+ SearchInput,
9588
9893
  {
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"
9894
+ placeholder: field.searchPlaceholder ?? "Search\u2026",
9895
+ value: search,
9896
+ onChange: (e) => setSearch(e.target.value),
9897
+ className: "w-full min-w-0"
9595
9898
  }
9596
- ) }),
9597
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
9598
- PopoverContent,
9899
+ ),
9900
+ /* @__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)(
9901
+ "label",
9599
9902
  {
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",
9903
+ className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors hover:bg-muted/50",
9904
+ children: [
9905
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9906
+ Checkbox,
9629
9907
  {
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
- ]
9908
+ checked: value.includes(option.value),
9909
+ onCheckedChange: () => toggle(option.value)
9884
9910
  }
9885
- )
9886
- ] })
9911
+ ),
9912
+ option.icon,
9913
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("span", { className: "flex min-w-0 flex-col", children: [
9914
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate text-sm font-medium text-foreground", children: option.label }),
9915
+ option.hint && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: option.hint })
9916
+ ] })
9917
+ ]
9918
+ },
9919
+ option.value
9920
+ )) })
9921
+ ] });
9922
+ }
9923
+ function TextControl({
9924
+ field,
9925
+ value,
9926
+ onChange,
9927
+ onClose
9928
+ }) {
9929
+ const [draft, setDraft] = (0, import_react53.useState)(value);
9930
+ (0, import_react53.useEffect)(() => setDraft(value), [value]);
9931
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
9932
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9933
+ "input",
9934
+ {
9935
+ type: "text",
9936
+ placeholder: field.placeholder ?? "Type a value\u2026",
9937
+ value: draft,
9938
+ onChange: (e) => setDraft(e.target.value),
9939
+ onKeyDown: (e) => {
9940
+ if (e.key === "Enter") {
9941
+ onChange(draft);
9942
+ onClose();
9943
+ }
9944
+ },
9945
+ className: controlClass({ size: "sm" }, "w-full")
9946
+ }
9947
+ ),
9948
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
9949
+ ApplyClear,
9950
+ {
9951
+ onClear: () => {
9952
+ setDraft("");
9953
+ onChange("");
9954
+ onClose();
9955
+ },
9956
+ onApply: () => {
9957
+ onChange(draft);
9958
+ onClose();
9959
+ }
9887
9960
  }
9888
9961
  )
9889
- ] }) });
9962
+ ] });
9963
+ }
9964
+ function DateRangeControl({
9965
+ field,
9966
+ value,
9967
+ onChange,
9968
+ onClose
9969
+ }) {
9970
+ const presets = field.presets ?? DEFAULT_PRESETS;
9971
+ const [from, setFrom] = (0, import_react53.useState)(value.from ?? "");
9972
+ const [to, setTo] = (0, import_react53.useState)(value.to ?? "");
9973
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-1", children: [
9974
+ presets.map((preset) => {
9975
+ const isSelected = value.preset === preset.id;
9976
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
9977
+ "button",
9978
+ {
9979
+ type: "button",
9980
+ onClick: () => {
9981
+ if (preset.id === "custom") {
9982
+ onChange({ preset: "custom", from, to });
9983
+ } else {
9984
+ onChange({ preset: preset.id });
9985
+ onClose();
9986
+ }
9987
+ },
9988
+ className: cn(
9989
+ "flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors outline-none",
9990
+ isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
9991
+ ),
9992
+ children: [
9993
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { children: preset.label }),
9994
+ preset.hint && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "text-xs text-muted-foreground/70", children: preset.hint })
9995
+ ]
9996
+ },
9997
+ preset.id
9998
+ );
9999
+ }),
10000
+ 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: [
10001
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center gap-2", children: [
10002
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10003
+ "input",
10004
+ {
10005
+ type: "date",
10006
+ value: from,
10007
+ onChange: (e) => setFrom(e.target.value),
10008
+ className: controlClass({ size: "sm" }, "w-full text-xs")
10009
+ }
10010
+ ),
10011
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "text-xs text-muted-foreground", children: "to" }),
10012
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10013
+ "input",
10014
+ {
10015
+ type: "date",
10016
+ value: to,
10017
+ onChange: (e) => setTo(e.target.value),
10018
+ className: controlClass({ size: "sm" }, "w-full text-xs")
10019
+ }
10020
+ )
10021
+ ] }),
10022
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10023
+ Button,
10024
+ {
10025
+ color: "primary",
10026
+ size: "sm",
10027
+ className: "h-8 px-3",
10028
+ onClick: () => {
10029
+ onChange({ preset: "custom", from, to });
10030
+ onClose();
10031
+ },
10032
+ children: "Apply"
10033
+ }
10034
+ ) })
10035
+ ] })
10036
+ ] });
10037
+ }
10038
+ function NumericControl({
10039
+ field,
10040
+ value,
10041
+ onChange,
10042
+ onClose
10043
+ }) {
10044
+ const operators = field.operators ?? DEFAULT_OPERATORS;
10045
+ const [operator, setOperator] = (0, import_react53.useState)(value.operator);
10046
+ const [draft, setDraft] = (0, import_react53.useState)(value.value);
10047
+ (0, import_react53.useEffect)(() => {
10048
+ setOperator(value.operator);
10049
+ setDraft(value.value);
10050
+ }, [value.operator, value.value]);
10051
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
10052
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center gap-2", children: [
10053
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Select, { value: operator, onValueChange: (v) => setOperator(v), children: [
10054
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectTrigger, { size: "sm", className: "shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SelectValue, {}) }),
10055
+ /* @__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)) })
10056
+ ] }),
10057
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10058
+ "input",
10059
+ {
10060
+ type: "text",
10061
+ inputMode: "decimal",
10062
+ placeholder: field.placeholder ?? "0.00",
10063
+ value: draft,
10064
+ onChange: (e) => setDraft(e.target.value),
10065
+ onKeyDown: (e) => {
10066
+ if (e.key === "Enter") {
10067
+ onChange(draft ? { operator, value: draft } : null);
10068
+ onClose();
10069
+ }
10070
+ },
10071
+ className: controlClass({ size: "sm" }, "min-w-0 flex-1")
10072
+ }
10073
+ )
10074
+ ] }),
10075
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10076
+ ApplyClear,
10077
+ {
10078
+ onClear: () => {
10079
+ setDraft("");
10080
+ onChange(null);
10081
+ onClose();
10082
+ },
10083
+ onApply: () => {
10084
+ onChange(draft ? { operator, value: draft } : null);
10085
+ onClose();
10086
+ }
10087
+ }
10088
+ )
10089
+ ] });
10090
+ }
10091
+ function ApplyClear({ onClear, onApply }) {
10092
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "flex items-center justify-end gap-2 border-t border-border/40 pt-1", children: [
10093
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10094
+ Button,
10095
+ {
10096
+ variant: "ghost",
10097
+ size: "sm",
10098
+ onClick: onClear,
10099
+ className: "h-8 px-3 text-muted-foreground hover:text-foreground",
10100
+ children: "Clear"
10101
+ }
10102
+ ),
10103
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Button, { color: "primary", size: "sm", onClick: onApply, className: "h-8 px-3", children: "Apply" })
10104
+ ] });
9890
10105
  }
9891
10106
 
9892
10107
  // src/app/data/DataTable.tsx
9893
10108
  var import_react54 = require("react");
9894
- var import_lucide_react18 = require("lucide-react");
10109
+ var import_lucide_react19 = require("lucide-react");
9895
10110
 
9896
10111
  // src/ui/skeleton.tsx
9897
- var import_jsx_runtime88 = require("react/jsx-runtime");
10112
+ var import_jsx_runtime89 = require("react/jsx-runtime");
9898
10113
  function Skeleton({ className, ...props }) {
9899
- return /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
10114
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
9900
10115
  "div",
9901
10116
  {
9902
10117
  "data-slot": "skeleton",
@@ -9907,7 +10122,7 @@ function Skeleton({ className, ...props }) {
9907
10122
  }
9908
10123
 
9909
10124
  // src/app/data/DataTable.tsx
9910
- var import_jsx_runtime89 = require("react/jsx-runtime");
10125
+ var import_jsx_runtime90 = require("react/jsx-runtime");
9911
10126
  var shellClass2 = "w-full";
9912
10127
  var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
9913
10128
  var headRowClass = "";
@@ -9950,12 +10165,12 @@ function SortIndicator({
9950
10165
  }) {
9951
10166
  const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
9952
10167
  if (!active) {
9953
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
10168
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
9954
10169
  }
9955
10170
  if (direction === "desc") {
9956
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
10171
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowDownIcon, { className: iconClass, "aria-hidden": true });
9957
10172
  }
9958
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
10173
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ArrowUpIcon, { className: iconClass, "aria-hidden": true });
9959
10174
  }
9960
10175
  function DataTable({
9961
10176
  columns,
@@ -10053,7 +10268,7 @@ function DataTable({
10053
10268
  const headPad = dense ? "px-3 py-2" : void 0;
10054
10269
  const colSpan = columns.length + (selectable ? 1 : 0);
10055
10270
  if (!loading && rows.length === 0 && emptyMode === "replace") {
10056
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
10271
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, className });
10057
10272
  }
10058
10273
  const allKeys = sortedRows.map(getRowKey);
10059
10274
  const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
@@ -10078,10 +10293,10 @@ function DataTable({
10078
10293
  const hasPager = paginated && !loading && sortedRows.length > 0;
10079
10294
  const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
10080
10295
  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)(
10296
+ 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: [
10297
+ caption ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("caption", { className: "sr-only", children: caption }) : null,
10298
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("tr", { children: [
10299
+ selectable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10085
10300
  Checkbox,
10086
10301
  {
10087
10302
  checked: headerCheckedState,
@@ -10093,19 +10308,19 @@ function DataTable({
10093
10308
  columns.map((col) => {
10094
10309
  const isSorted = sort?.columnId === col.id;
10095
10310
  const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
10096
- const headerContent = col.sortable ? /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(
10311
+ const headerContent = col.sortable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
10097
10312
  "button",
10098
10313
  {
10099
10314
  type: "button",
10100
10315
  className: sortButtonClass,
10101
10316
  onClick: () => setSort(nextSort(sort, col.id)),
10102
10317
  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 })
10318
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("span", { className: "truncate", children: col.header }),
10319
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
10105
10320
  ]
10106
10321
  }
10107
10322
  ) : col.header;
10108
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10323
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10109
10324
  "th",
10110
10325
  {
10111
10326
  scope: "col",
@@ -10122,9 +10337,9 @@ function DataTable({
10122
10337
  );
10123
10338
  })
10124
10339
  ] }) }),
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)(
10340
+ /* @__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: [
10341
+ 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,
10342
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10128
10343
  "td",
10129
10344
  {
10130
10345
  className: cn(
@@ -10133,17 +10348,17 @@ function DataTable({
10133
10348
  col.align && alignClass[col.align],
10134
10349
  col.className
10135
10350
  ),
10136
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Skeleton, { className: "h-4 w-[60%]" })
10351
+ children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(Skeleton, { className: "h-4 w-[60%]" })
10137
10352
  },
10138
10353
  col.id
10139
10354
  ))
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
10355
+ ] }, `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: [
10356
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("p", { className: "font-medium text-foreground", children: emptyTitle }),
10357
+ emptyDescription ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
10143
10358
  ] }) }) }) : visibleRows.map((row) => {
10144
10359
  const key = getRowKey(row);
10145
10360
  const isSelected = selectedSet.has(key);
10146
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(
10361
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)(
10147
10362
  "tr",
10148
10363
  {
10149
10364
  className: rowClass,
@@ -10159,12 +10374,12 @@ function DataTable({
10159
10374
  tabIndex: onRowClick ? 0 : void 0,
10160
10375
  role: onRowClick ? "button" : void 0,
10161
10376
  children: [
10162
- selectable ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10377
+ selectable ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10163
10378
  "td",
10164
10379
  {
10165
10380
  className: cn(selectCellClass, cellPad),
10166
10381
  onClick: (event) => event.stopPropagation(),
10167
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10382
+ children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10168
10383
  Checkbox,
10169
10384
  {
10170
10385
  checked: isSelected,
@@ -10174,7 +10389,7 @@ function DataTable({
10174
10389
  )
10175
10390
  }
10176
10391
  ) : null,
10177
- columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10392
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10178
10393
  "td",
10179
10394
  {
10180
10395
  className: cn(
@@ -10184,7 +10399,7 @@ function DataTable({
10184
10399
  col.align && alignClass[col.align],
10185
10400
  col.className
10186
10401
  ),
10187
- children: col.truncate ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
10402
+ children: col.truncate ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
10188
10403
  },
10189
10404
  col.id
10190
10405
  ))
@@ -10193,7 +10408,7 @@ function DataTable({
10193
10408
  key
10194
10409
  );
10195
10410
  }) }),
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)(
10411
+ 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
10412
  "div",
10198
10413
  {
10199
10414
  className: cn(
@@ -10201,18 +10416,18 @@ function DataTable({
10201
10416
  (showRowCount || footer || hasPager) && "justify-between"
10202
10417
  ),
10203
10418
  children: [
10204
- /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: footInnerClass, children: [
10205
- showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { children: [
10419
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: footInnerClass, children: [
10420
+ showRowCount ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { children: [
10206
10421
  rowCountText,
10207
10422
  selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
10208
- ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { children: [
10423
+ ] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { children: [
10209
10424
  selectedSet.size,
10210
10425
  " selected"
10211
10426
  ] }) : null,
10212
10427
  footer
10213
10428
  ] }),
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: [
10429
+ hasPager ? /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "flex items-center gap-2", children: [
10430
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("span", { className: "tabular-nums", children: [
10216
10431
  pageIndex * pageSize + 1,
10217
10432
  "\u2013",
10218
10433
  Math.min(
@@ -10223,8 +10438,8 @@ function DataTable({
10223
10438
  "of ",
10224
10439
  sortedRows.length
10225
10440
  ] }),
10226
- /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "flex items-center gap-1", children: [
10227
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10441
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "flex items-center gap-1", children: [
10442
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10228
10443
  "button",
10229
10444
  {
10230
10445
  type: "button",
@@ -10232,10 +10447,10 @@ function DataTable({
10232
10447
  onClick: () => setPage(pageIndex - 1),
10233
10448
  disabled: pageIndex <= 0,
10234
10449
  "aria-label": "Previous page",
10235
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
10450
+ children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
10236
10451
  }
10237
10452
  ),
10238
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(
10453
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10239
10454
  "button",
10240
10455
  {
10241
10456
  type: "button",
@@ -10243,7 +10458,7 @@ function DataTable({
10243
10458
  onClick: () => setPage(pageIndex + 1),
10244
10459
  disabled: pageIndex >= pageCount - 1,
10245
10460
  "aria-label": "Next page",
10246
- children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_lucide_react18.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
10461
+ children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(import_lucide_react19.ChevronRightIcon, { className: "size-4", "aria-hidden": true })
10247
10462
  }
10248
10463
  )
10249
10464
  ] })
@@ -10256,7 +10471,7 @@ function DataTable({
10256
10471
 
10257
10472
  // src/app/data/ChartPanel.tsx
10258
10473
  var import_react55 = require("react");
10259
- var import_jsx_runtime90 = require("react/jsx-runtime");
10474
+ var import_jsx_runtime91 = require("react/jsx-runtime");
10260
10475
  var ChartPanel = ({
10261
10476
  title,
10262
10477
  description,
@@ -10274,14 +10489,14 @@ var ChartPanel = ({
10274
10489
  const titleId = (0, import_react55.useId)();
10275
10490
  const resolvedTitle = title ?? artifact?.title;
10276
10491
  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)(
10492
+ 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);
10493
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
10279
10494
  "section",
10280
10495
  {
10281
10496
  className: cn(metricCardShellClass, "aui-app-chart-panel", className),
10282
10497
  "aria-labelledby": resolvedTitle ? titleId : void 0,
10283
10498
  children: [
10284
- /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10499
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10285
10500
  MetricCardHeader,
10286
10501
  {
10287
10502
  title: resolvedTitle,
@@ -10290,14 +10505,14 @@ var ChartPanel = ({
10290
10505
  actions
10291
10506
  }
10292
10507
  ),
10293
- /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10508
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10294
10509
  "div",
10295
10510
  {
10296
10511
  className: cn(
10297
10512
  "relative min-h-0 w-full",
10298
10513
  hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
10299
10514
  ),
10300
- children: body ?? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
10515
+ children: body ?? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10301
10516
  "div",
10302
10517
  {
10303
10518
  className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
@@ -10315,7 +10530,7 @@ var ChartPanel = ({
10315
10530
 
10316
10531
  // src/app/data/MetricRow.tsx
10317
10532
  var import_react56 = require("react");
10318
- var import_jsx_runtime91 = require("react/jsx-runtime");
10533
+ var import_jsx_runtime92 = require("react/jsx-runtime");
10319
10534
  var MetricRow = ({
10320
10535
  title,
10321
10536
  titleTag,
@@ -10341,13 +10556,13 @@ var MetricRow = ({
10341
10556
  onMetricChange?.(id);
10342
10557
  };
10343
10558
  const hasHeader = Boolean(title || titleTag || description || actions);
10344
- return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
10559
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
10345
10560
  "section",
10346
10561
  {
10347
10562
  className: cn(metricCardShellClass, className),
10348
10563
  "aria-labelledby": title ? titleId : void 0,
10349
10564
  children: [
10350
- /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10565
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10351
10566
  MetricCardHeader,
10352
10567
  {
10353
10568
  title,
@@ -10357,7 +10572,7 @@ var MetricRow = ({
10357
10572
  actions
10358
10573
  }
10359
10574
  ),
10360
- /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10575
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10361
10576
  "div",
10362
10577
  {
10363
10578
  role: selectable ? "group" : void 0,
@@ -10368,18 +10583,18 @@ var MetricRow = ({
10368
10583
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
10369
10584
  hasHeader && "mt-3.5 border-t border-border/40"
10370
10585
  ),
10371
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
10586
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
10372
10587
  "div",
10373
10588
  {
10374
10589
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
10375
10590
  "aria-hidden": true,
10376
10591
  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" })
10592
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Skeleton, { className: "h-3 w-20" }),
10593
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Skeleton, { className: "h-7 w-24" })
10379
10594
  ]
10380
10595
  },
10381
10596
  `skeleton-${index}`
10382
- )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
10597
+ )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10383
10598
  MetricTile,
10384
10599
  {
10385
10600
  label: m.label,
@@ -10407,7 +10622,7 @@ var MetricRow = ({
10407
10622
 
10408
10623
  // src/app/data/MetricChartCard.tsx
10409
10624
  var import_react57 = require("react");
10410
- var import_jsx_runtime92 = require("react/jsx-runtime");
10625
+ var import_jsx_runtime93 = require("react/jsx-runtime");
10411
10626
  var MetricChartCard = ({
10412
10627
  title,
10413
10628
  titleTag,
@@ -10443,13 +10658,13 @@ var MetricChartCard = ({
10443
10658
  };
10444
10659
  const hasHeader = Boolean(title || titleTag || description || actions);
10445
10660
  const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
10446
- return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
10661
+ return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
10447
10662
  "section",
10448
10663
  {
10449
10664
  className: cn(metricCardShellClass, className),
10450
10665
  "aria-labelledby": title ? titleId : void 0,
10451
10666
  children: [
10452
- /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10667
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10453
10668
  MetricCardHeader,
10454
10669
  {
10455
10670
  title,
@@ -10459,7 +10674,7 @@ var MetricChartCard = ({
10459
10674
  titleId
10460
10675
  }
10461
10676
  ),
10462
- /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10677
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10463
10678
  "div",
10464
10679
  {
10465
10680
  role: "group",
@@ -10470,18 +10685,18 @@ var MetricChartCard = ({
10470
10685
  metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
10471
10686
  hasHeader && "mt-3.5 border-t border-border/40"
10472
10687
  ),
10473
- children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
10688
+ children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
10474
10689
  "div",
10475
10690
  {
10476
10691
  className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
10477
10692
  "aria-hidden": true,
10478
10693
  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" })
10694
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Skeleton, { className: "h-3 w-20" }),
10695
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Skeleton, { className: "h-7 w-24" })
10481
10696
  ]
10482
10697
  },
10483
10698
  `skeleton-${index}`
10484
- )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10699
+ )) : metrics.map((m, index) => /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10485
10700
  MetricTile,
10486
10701
  {
10487
10702
  label: m.label,
@@ -10502,14 +10717,14 @@ var MetricChartCard = ({
10502
10717
  ))
10503
10718
  }
10504
10719
  ),
10505
- /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10720
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10506
10721
  Skeleton,
10507
10722
  {
10508
10723
  className: "w-full rounded-lg",
10509
10724
  style: { height },
10510
10725
  "aria-hidden": true
10511
10726
  }
10512
- ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10727
+ ) : active?.data && active.data.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10513
10728
  LineAreaChart,
10514
10729
  {
10515
10730
  data: active.data,
@@ -10529,7 +10744,7 @@ var MetricChartCard = ({
10529
10744
  ariaLabel: chartAriaLabel
10530
10745
  },
10531
10746
  active.id
10532
- ) : /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10747
+ ) : /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10533
10748
  "div",
10534
10749
  {
10535
10750
  className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
@@ -10635,8 +10850,8 @@ function useLiveQuery(fetcher, options = {}) {
10635
10850
 
10636
10851
  // src/ui/untitled-button.tsx
10637
10852
  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");
10853
+ var import_radix_ui9 = require("radix-ui");
10854
+ var import_jsx_runtime94 = require("react/jsx-runtime");
10640
10855
  var SOLID_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-solid";
10641
10856
  var BORDERED_SKEUOMORPHIC_SHADOW2 = "shadow-skeuomorphic-bordered";
10642
10857
  var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
@@ -10719,10 +10934,10 @@ var untitledButtonVariants = (0, import_class_variance_authority2.cva)(
10719
10934
  )
10720
10935
  },
10721
10936
  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"
10937
+ sm: "h-8 gap-1 rounded-md px-2.5 text-xs",
10938
+ md: "h-9 gap-1.5 rounded-lg px-3 text-sm",
10939
+ lg: "h-10 gap-1.5 rounded-lg px-3.5 text-sm",
10940
+ xl: "h-11 gap-2 rounded-lg px-4 text-base"
10726
10941
  }
10727
10942
  },
10728
10943
  defaultVariants: {
@@ -10747,8 +10962,8 @@ function UntitledButton({
10747
10962
  const isDisabled = disabled || isLoading;
10748
10963
  const classes = cn(untitledButtonVariants({ color, size }), className);
10749
10964
  if (asChild) {
10750
- return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10751
- import_radix_ui8.Slot.Root,
10965
+ return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
10966
+ import_radix_ui9.Slot.Root,
10752
10967
  {
10753
10968
  className: classes,
10754
10969
  "aria-disabled": isDisabled ? true : void 0,
@@ -10758,7 +10973,7 @@ function UntitledButton({
10758
10973
  }
10759
10974
  );
10760
10975
  }
10761
- return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
10976
+ return /* @__PURE__ */ (0, import_jsx_runtime94.jsxs)(
10762
10977
  "button",
10763
10978
  {
10764
10979
  type,
@@ -10767,7 +10982,7 @@ function UntitledButton({
10767
10982
  className: classes,
10768
10983
  ...props,
10769
10984
  children: [
10770
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
10985
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
10771
10986
  "span",
10772
10987
  {
10773
10988
  "aria-hidden": true,
@@ -10782,8 +10997,8 @@ function UntitledButton({
10782
10997
  }
10783
10998
 
10784
10999
  // src/ui/banner.tsx
10785
- var import_lucide_react19 = require("lucide-react");
10786
- var import_jsx_runtime94 = require("react/jsx-runtime");
11000
+ var import_lucide_react20 = require("lucide-react");
11001
+ var import_jsx_runtime95 = require("react/jsx-runtime");
10787
11002
  var bannerSoftClass = {
10788
11003
  default: "border-border/50 bg-muted/30 text-foreground/90 dark:bg-muted/15",
10789
11004
  primary: "border-primary/15 bg-primary/5 text-primary-800 dark:text-primary-200 [&_[data-banner-icon]]:text-primary",
@@ -10828,7 +11043,7 @@ function Banner({
10828
11043
  }) {
10829
11044
  const isSolid = variant === "solid";
10830
11045
  const isSingleLine = !title;
10831
- return /* @__PURE__ */ (0, import_jsx_runtime94.jsxs)(
11046
+ return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
10832
11047
  "div",
10833
11048
  {
10834
11049
  "data-slot": "banner",
@@ -10843,7 +11058,7 @@ function Banner({
10843
11058
  ),
10844
11059
  ...props,
10845
11060
  children: [
10846
- icon ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
11061
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
10847
11062
  "span",
10848
11063
  {
10849
11064
  "data-banner-icon": true,
@@ -10854,9 +11069,9 @@ function Banner({
10854
11069
  children: icon
10855
11070
  }
10856
11071
  ) : 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)(
11072
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)("div", { className: "min-w-0 flex-1", children: [
11073
+ title ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("p", { className: "font-medium tracking-tight", children: title }) : null,
11074
+ children ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
10860
11075
  "div",
10861
11076
  {
10862
11077
  className: cn(
@@ -10867,8 +11082,8 @@ function Banner({
10867
11082
  }
10868
11083
  ) : null
10869
11084
  ] }),
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)(
11085
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions }) : null,
11086
+ onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
10872
11087
  "button",
10873
11088
  {
10874
11089
  type: "button",
@@ -10879,7 +11094,7 @@ function Banner({
10879
11094
  isSingleLine ? "self-center" : "-mt-0.5",
10880
11095
  isSolid ? "opacity-80 hover:bg-background/15 hover:opacity-100" : "text-muted-foreground hover:bg-foreground/10 hover:text-foreground"
10881
11096
  ),
10882
- children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(import_lucide_react19.XIcon, { className: "size-4", "aria-hidden": true })
11097
+ children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(import_lucide_react20.XIcon, { className: "size-4", "aria-hidden": true })
10883
11098
  }
10884
11099
  ) : null
10885
11100
  ]
@@ -10888,7 +11103,7 @@ function Banner({
10888
11103
  }
10889
11104
 
10890
11105
  // src/ui/timeline.tsx
10891
- var import_jsx_runtime95 = require("react/jsx-runtime");
11106
+ var import_jsx_runtime96 = require("react/jsx-runtime");
10892
11107
  var timelineRowGap = {
10893
11108
  sm: "pb-4",
10894
11109
  default: "pb-6"
@@ -10921,16 +11136,16 @@ var toneStyles = {
10921
11136
  }
10922
11137
  };
10923
11138
  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) => {
11139
+ return /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("ol", { "data-slot": "timeline", className: cn("flex flex-col", className), ...props, children: items.map((item, index) => {
10925
11140
  const last = index === items.length - 1;
10926
11141
  const tone = item.tone ?? "default";
10927
11142
  const styles = toneStyles[tone];
10928
- return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
11143
+ return /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(
10929
11144
  "li",
10930
11145
  {
10931
11146
  className: cn("relative flex gap-3.5 last:pb-0", timelineRowGap[size]),
10932
11147
  children: [
10933
- !last ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
11148
+ !last ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
10934
11149
  "span",
10935
11150
  {
10936
11151
  "aria-hidden": true,
@@ -10940,7 +11155,7 @@ function Timeline({ items, size = "default", className, ...props }) {
10940
11155
  )
10941
11156
  }
10942
11157
  ) : 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)(
11158
+ /* @__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
11159
  "span",
10945
11160
  {
10946
11161
  className: cn(
@@ -10948,7 +11163,7 @@ function Timeline({ items, size = "default", className, ...props }) {
10948
11163
  styles.border,
10949
11164
  item.icon ? "size-6" : "size-4"
10950
11165
  ),
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)(
11166
+ 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
11167
  "span",
10953
11168
  {
10954
11169
  className: cn(
@@ -10959,12 +11174,12 @@ function Timeline({ items, size = "default", className, ...props }) {
10959
11174
  )
10960
11175
  }
10961
11176
  ) }),
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
11177
+ /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)("div", { className: "min-w-0 flex-1 pb-0.5", children: [
11178
+ /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
11179
+ /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("p", { className: "text-sm font-normal text-foreground", children: item.title }),
11180
+ item.meta ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground tabular-nums", children: item.meta }) : null
10966
11181
  ] }),
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
11182
+ 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
11183
  ] })
10969
11184
  ]
10970
11185
  },
@@ -10978,8 +11193,8 @@ var React5 = __toESM(require("react"), 1);
10978
11193
  var import_core2 = require("@dnd-kit/core");
10979
11194
  var import_sortable = require("@dnd-kit/sortable");
10980
11195
  var import_utilities = require("@dnd-kit/utilities");
10981
- var import_lucide_react20 = require("lucide-react");
10982
- var import_jsx_runtime96 = require("react/jsx-runtime");
11196
+ var import_lucide_react21 = require("lucide-react");
11197
+ var import_jsx_runtime97 = require("react/jsx-runtime");
10983
11198
  var columnTitleToneClass = {
10984
11199
  default: "text-foreground",
10985
11200
  primary: "text-blue-600 dark:text-blue-400",
@@ -11041,7 +11256,7 @@ function SortableCard({
11041
11256
  transition
11042
11257
  };
11043
11258
  const dragHandleProps = disabled ? void 0 : { ...attributes, ...listeners };
11044
- return /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(
11259
+ return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
11045
11260
  "div",
11046
11261
  {
11047
11262
  ref: setNodeRef,
@@ -11056,7 +11271,7 @@ function SortableCard({
11056
11271
  className
11057
11272
  ),
11058
11273
  children: [
11059
- !disabled && dragHandle === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11274
+ !disabled && dragHandle === "auto" ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11060
11275
  "button",
11061
11276
  {
11062
11277
  type: "button",
@@ -11064,7 +11279,7 @@ function SortableCard({
11064
11279
  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
11280
  ...attributes,
11066
11281
  ...listeners,
11067
- children: /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(import_lucide_react20.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
11282
+ children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_lucide_react21.GripVerticalIcon, { className: "size-4", "aria-hidden": true })
11068
11283
  }
11069
11284
  ) : null,
11070
11285
  renderCard(card, { column, isDragging, isOverlay: false, dragHandleProps })
@@ -11088,7 +11303,7 @@ function KanbanColumnView({
11088
11303
  }) {
11089
11304
  const tone = column.tone ?? "default";
11090
11305
  const { setNodeRef, isOver } = (0, import_core2.useDroppable)({ id: column.id, disabled });
11091
- return /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(
11306
+ return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
11092
11307
  "div",
11093
11308
  {
11094
11309
  "data-slot": "kanban-column",
@@ -11098,9 +11313,9 @@ function KanbanColumnView({
11098
11313
  className
11099
11314
  ),
11100
11315
  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)(
11316
+ renderColumnHeader ? renderColumnHeader(column) : /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)("div", { className: "flex flex-col gap-0.5 px-1 pb-0.5", children: [
11317
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)("div", { className: "flex items-center gap-2", children: [
11318
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11104
11319
  "h3",
11105
11320
  {
11106
11321
  className: cn(
@@ -11110,12 +11325,12 @@ function KanbanColumnView({
11110
11325
  children: column.title
11111
11326
  }
11112
11327
  ),
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
11328
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("span", { className: "shrink-0 text-xs font-normal tabular-nums text-muted-foreground/60", children: column.cards.length }),
11329
+ column.action ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("div", { className: "shrink-0", children: column.action }) : null
11115
11330
  ] }),
11116
- column.description ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
11331
+ column.description ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: column.description }) : null
11117
11332
  ] }),
11118
- /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11333
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_sortable.SortableContext, { items: cardIds, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11119
11334
  "div",
11120
11335
  {
11121
11336
  ref: setNodeRef,
@@ -11125,9 +11340,9 @@ function KanbanColumnView({
11125
11340
  densityListClass[density],
11126
11341
  isOver && "bg-muted/50"
11127
11342
  ),
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) => {
11343
+ 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
11344
  const id = getCardId(card);
11130
- return /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11345
+ return /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11131
11346
  SortableCard,
11132
11347
  {
11133
11348
  card,
@@ -11145,7 +11360,7 @@ function KanbanColumnView({
11145
11360
  })
11146
11361
  }
11147
11362
  ) }),
11148
- column.footer ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
11363
+ column.footer ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("div", { className: "px-0.5 pt-0.5", children: column.footer }) : null
11149
11364
  ]
11150
11365
  }
11151
11366
  );
@@ -11284,7 +11499,7 @@ function Kanban({
11284
11499
  onColumnsChange?.(next);
11285
11500
  }
11286
11501
  };
11287
- return /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(
11502
+ return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
11288
11503
  import_core2.DndContext,
11289
11504
  {
11290
11505
  sensors,
@@ -11298,7 +11513,7 @@ function Kanban({
11298
11513
  if (isControlled) setInternal(cloneColumns(columnsProp));
11299
11514
  },
11300
11515
  children: [
11301
- /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11516
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11302
11517
  "div",
11303
11518
  {
11304
11519
  "data-slot": "kanban",
@@ -11309,7 +11524,7 @@ function Kanban({
11309
11524
  density === "compact" ? "gap-3" : "gap-4",
11310
11525
  className
11311
11526
  ),
11312
- children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11527
+ children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11313
11528
  KanbanColumnView,
11314
11529
  {
11315
11530
  column,
@@ -11329,7 +11544,7 @@ function Kanban({
11329
11544
  ))
11330
11545
  }
11331
11546
  ),
11332
- /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
11547
+ /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_core2.DragOverlay, { children: activeCard ? /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11333
11548
  "div",
11334
11549
  {
11335
11550
  "data-slot": "kanban-card-overlay",
@@ -11352,7 +11567,7 @@ function Kanban({
11352
11567
  }
11353
11568
 
11354
11569
  // src/chat/chat.tsx
11355
- var import_jsx_runtime97 = require("react/jsx-runtime");
11570
+ var import_jsx_runtime98 = require("react/jsx-runtime");
11356
11571
  function TimbalChat({
11357
11572
  workforceId,
11358
11573
  baseUrl,
@@ -11363,7 +11578,7 @@ function TimbalChat({
11363
11578
  debug,
11364
11579
  ...threadProps
11365
11580
  }) {
11366
- return /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
11581
+ return /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(
11367
11582
  TimbalRuntimeProvider,
11368
11583
  {
11369
11584
  workforceId,
@@ -11373,7 +11588,7 @@ function TimbalChat({
11373
11588
  attachmentsUploadUrl,
11374
11589
  attachmentsAccept,
11375
11590
  debug,
11376
- children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Thread, { ...threadProps })
11591
+ children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Thread, { ...threadProps })
11377
11592
  }
11378
11593
  );
11379
11594
  }