@quanticjs/notification-ui 8.1.0 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -432,7 +432,7 @@ function NotificationBell({
432
432
  "span",
433
433
  {
434
434
  "aria-hidden": "true",
435
- className: "absolute -end-0.5 -top-0.5 inline-flex min-w-5 items-center justify-center rounded-full bg-destructive px-1.5 text-xs font-semibold text-destructive-foreground",
435
+ className: "absolute -end-0.5 -top-0.5 inline-flex min-w-5 items-center justify-center rounded-full bg-coral px-1.5 text-xs font-semibold text-coral-foreground ring-2 ring-background",
436
436
  children: count > 99 ? "99+" : count
437
437
  }
438
438
  )
@@ -456,54 +456,80 @@ function NotificationInbox({
456
456
  appId
457
457
  });
458
458
  if (isLoading) {
459
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
460
- "div",
461
- {
462
- role: "status",
463
- "aria-label": "Loading notifications",
464
- className: (0, import_react_ui4.cn)("flex flex-col gap-2 p-4", className),
465
- children: [
466
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "sr-only", children: "Loading notifications" }),
467
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { "aria-hidden": "true", className: "h-12 animate-pulse rounded bg-muted" }, i))
468
- ]
469
- }
470
- );
459
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { role: "status", "aria-label": "Loading notifications", className: (0, import_react_ui4.cn)(className), children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "sr-only", children: "Loading notifications" }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_ui4.Card, { "aria-hidden": "true", children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxHeader, {}),
463
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "divide-y divide-border", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: "flex items-start gap-3 px-4 py-3.5", children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_ui4.Skeleton, { className: "mt-1 h-2 w-2 rounded-full" }),
465
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_ui4.Skeleton, { className: "h-3.5 w-2/3" }),
467
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_ui4.Skeleton, { className: "h-3 w-1/2" })
468
+ ] })
469
+ ] }, i)) })
470
+ ] })
471
+ ] });
471
472
  }
472
473
  if (isError) {
473
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: (0, import_react_ui4.cn)("flex flex-col items-start gap-3 p-4", className), children: [
474
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load notifications" }),
475
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
476
- "button",
477
- {
478
- type: "button",
479
- onClick: () => void refetch(),
480
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
481
- children: "Try again"
482
- }
483
- )
474
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_ui4.Card, { className, children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxHeader, {}),
476
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
478
+ "span",
479
+ {
480
+ "aria-hidden": "true",
481
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
482
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AlertIcon, {})
483
+ }
484
+ ),
485
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1", children: [
486
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Failed to load notifications" }),
487
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm text-muted-foreground", children: "Something went wrong while loading your inbox." })
488
+ ] }),
489
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_ui4.Button, { type: "button", onClick: () => void refetch(), children: "Try again" })
490
+ ] })
484
491
  ] });
485
492
  }
486
493
  const items = data?.items ?? [];
487
494
  if (items.length === 0) {
488
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: (0, import_react_ui4.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No notifications yet" });
489
- }
490
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { "aria-label": "Notifications", className: (0, import_react_ui4.cn)("flex flex-col", className), children: [
491
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "flex items-center justify-between border-b border-border px-4 py-2", children: [
492
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
495
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_ui4.Card, { className, children: [
496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxHeader, {}),
493
497
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
494
- "button",
498
+ import_react_ui4.EmptyState,
495
499
  {
496
- type: "button",
497
- onClick: () => markAllRead.mutate(),
498
- disabled: markAllRead.isPending,
499
- className: "text-sm text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
500
- children: "Mark all read"
500
+ icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BellOffIcon, {}),
501
+ title: "No notifications yet",
502
+ description: "New alerts and updates will appear here."
501
503
  }
502
504
  )
503
- ] }),
505
+ ] });
506
+ }
507
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_ui4.Card, { role: "region", "aria-label": "Notifications", className, children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
509
+ InboxHeader,
510
+ {
511
+ action: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
512
+ import_react_ui4.Button,
513
+ {
514
+ type: "button",
515
+ variant: "ghost",
516
+ size: "sm",
517
+ onClick: () => markAllRead.mutate(),
518
+ disabled: markAllRead.isPending,
519
+ children: "Mark all read"
520
+ }
521
+ )
522
+ }
523
+ ),
504
524
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "divide-y divide-border", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxRow, { item, onMarkRead: () => markRead.mutate(item.id) }, item.id)) })
505
525
  ] });
506
526
  }
527
+ function InboxHeader({ action }) {
528
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
529
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
530
+ action
531
+ ] });
532
+ }
507
533
  function InboxRow({
508
534
  item,
509
535
  onMarkRead
@@ -514,20 +540,63 @@ function InboxRow({
514
540
  type: "button",
515
541
  onClick: onMarkRead,
516
542
  "aria-label": `${item.title}${item.isRead ? "" : " (unread)"}`,
517
- className: (0, import_react_ui4.cn)(
518
- "flex w-full flex-col items-start gap-0.5 px-4 py-3 text-start hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
519
- item.isRead ? "text-muted-foreground" : "text-foreground"
520
- ),
543
+ className: "flex w-full items-start gap-3 px-4 py-3.5 text-start transition-colors hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
521
544
  children: [
522
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
523
- !item.isRead && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", className: "h-2 w-2 rounded-full bg-primary" }),
524
- item.title
525
- ] }),
526
- item.body && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs text-muted-foreground", children: item.body })
545
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", className: "mt-1.5 flex size-2 shrink-0 items-center justify-center", children: !item.isRead && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "size-2 rounded-full bg-coral" }) }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
548
+ "span",
549
+ {
550
+ className: (0, import_react_ui4.cn)(
551
+ "truncate text-sm",
552
+ item.isRead ? "font-normal text-muted-foreground" : "font-semibold text-foreground"
553
+ ),
554
+ children: item.title
555
+ }
556
+ ),
557
+ item.body && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "truncate text-xs text-muted-foreground", children: item.body })
558
+ ] })
527
559
  ]
528
560
  }
529
561
  ) });
530
562
  }
563
+ function AlertIcon() {
564
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
565
+ "svg",
566
+ {
567
+ viewBox: "0 0 24 24",
568
+ fill: "none",
569
+ stroke: "currentColor",
570
+ strokeWidth: "2",
571
+ strokeLinecap: "round",
572
+ strokeLinejoin: "round",
573
+ children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }),
575
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
576
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
577
+ ]
578
+ }
579
+ );
580
+ }
581
+ function BellOffIcon() {
582
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
583
+ "svg",
584
+ {
585
+ viewBox: "0 0 24 24",
586
+ fill: "none",
587
+ stroke: "currentColor",
588
+ strokeWidth: "2",
589
+ strokeLinecap: "round",
590
+ strokeLinejoin: "round",
591
+ children: [
592
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M8.7 3A6 6 0 0 1 18 8a21.3 21.3 0 0 0 .6 5" }),
593
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M17 17H3s3-2 3-9a4.67 4.67 0 0 1 .3-1.7" }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" }),
595
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "2", x2: "22", y1: "2", y2: "22" })
596
+ ]
597
+ }
598
+ );
599
+ }
531
600
 
532
601
  // src/notification-center.tsx
533
602
  var import_react5 = require("react");
@@ -602,7 +671,7 @@ function NotificationCenter({
602
671
  "span",
603
672
  {
604
673
  "aria-hidden": true,
605
- className: "absolute -end-0.5 -top-0.5 inline-flex min-w-5 items-center justify-center rounded-full bg-destructive px-1.5 text-xs font-semibold text-destructive-foreground",
674
+ className: "absolute -end-0.5 -top-0.5 inline-flex min-w-5 items-center justify-center rounded-full bg-coral px-1.5 text-xs font-semibold text-coral-foreground ring-2 ring-background",
606
675
  children: badge
607
676
  }
608
677
  )
@@ -724,14 +793,14 @@ var import_recharts = require("recharts");
724
793
  var import_jsx_runtime8 = require("react/jsx-runtime");
725
794
  function TrendChart({ data, className }) {
726
795
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: (0, import_react_ui7.cn)("h-72 w-full", className), role: "img", "aria-label": "Delivery trend over time", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_recharts.LineChart, { data, margin: { top: 8, right: 16, bottom: 8, left: 0 }, children: [
727
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }),
728
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.XAxis, { dataKey: "day", stroke: "hsl(var(--muted-foreground))", fontSize: 12 }),
729
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.YAxis, { stroke: "hsl(var(--muted-foreground))", fontSize: 12, allowDecimals: false }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--border)" }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.XAxis, { dataKey: "day", stroke: "var(--muted-foreground)", fontSize: 12 }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.YAxis, { stroke: "var(--muted-foreground)", fontSize: 12, allowDecimals: false }),
730
799
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Tooltip, {}),
731
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "sends", stroke: "hsl(var(--primary))", dot: false }),
732
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "delivered", stroke: "hsl(var(--chart-2, var(--primary)))", dot: false }),
733
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "opened", stroke: "hsl(var(--chart-3, var(--primary)))", dot: false }),
734
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "clicked", stroke: "hsl(var(--chart-4, var(--primary)))", dot: false })
800
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "sends", stroke: "var(--primary)", dot: false }),
801
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "delivered", stroke: "var(--chart-2, var(--primary))", dot: false }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "opened", stroke: "var(--chart-3, var(--primary))", dot: false }),
803
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "clicked", stroke: "var(--chart-4, var(--primary))", dot: false })
735
804
  ] }) }) });
736
805
  }
737
806
 
@@ -890,51 +959,134 @@ function TemplateList({ basePath = "/api/templates", className }) {
890
959
  (client) => client.get(basePath)
891
960
  );
892
961
  if (isLoading) {
893
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
894
- "div",
895
- {
896
- role: "status",
897
- "aria-label": "Loading templates",
898
- className: (0, import_react_ui11.cn)("flex flex-col gap-2 p-4", className),
899
- children: [
900
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sr-only", children: "Loading templates" }),
901
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { "aria-hidden": "true", className: "h-12 animate-pulse rounded bg-muted" }, i))
902
- ]
903
- }
904
- );
962
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { role: "status", "aria-label": "Loading templates", className: (0, import_react_ui11.cn)(className), children: [
963
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sr-only", children: "Loading templates" }),
964
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_ui11.Card, { "aria-hidden": "true", children: [
965
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ListHeader, {}),
966
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "divide-y divide-border", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { className: "flex flex-col gap-2 px-4 py-3.5", children: [
967
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2.5", children: [
968
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_ui11.Skeleton, { className: "h-3.5 w-40" }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_ui11.Skeleton, { className: "h-5 w-16 rounded-full" })
970
+ ] }),
971
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_ui11.Skeleton, { className: "h-3 w-56" })
972
+ ] }, i)) })
973
+ ] })
974
+ ] });
905
975
  }
906
976
  if (isError) {
907
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: (0, import_react_ui11.cn)("flex flex-col items-start gap-3 p-4", className), children: [
908
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load templates" }),
977
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_ui11.Card, { className, children: [
978
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ListHeader, {}),
979
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
980
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
981
+ "span",
982
+ {
983
+ "aria-hidden": "true",
984
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
985
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AlertIcon2, {})
986
+ }
987
+ ),
988
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-1", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Failed to load templates" }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-muted-foreground", children: "Something went wrong while loading your templates." })
991
+ ] }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_ui11.Button, { type: "button", onClick: () => void refetch(), children: "Try again" })
993
+ ] })
994
+ ] });
995
+ }
996
+ const items = data ?? [];
997
+ if (items.length === 0) {
998
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_ui11.Card, { className, children: [
999
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ListHeader, {}),
909
1000
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
910
- "button",
1001
+ import_react_ui11.EmptyState,
911
1002
  {
912
- type: "button",
913
- onClick: () => void refetch(),
914
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
915
- children: "Try again"
1003
+ icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FileIcon, {}),
1004
+ title: "No templates found",
1005
+ description: "This workspace doesn't have any notification templates yet."
916
1006
  }
917
1007
  )
918
1008
  ] });
919
1009
  }
920
- const items = data ?? [];
921
- if (items.length === 0) {
922
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: (0, import_react_ui11.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No templates found" });
923
- }
924
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { "aria-label": "Templates", className: (0, import_react_ui11.cn)("flex flex-col", className), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full text-sm", children: [
925
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
926
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Template" }),
927
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
928
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locales" }),
929
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Versions" })
930
- ] }) }),
931
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b border-border", children: [
932
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-3 pe-4 font-medium text-foreground", children: item.templateId }),
933
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TemplateStatusBadge, { status: item.latestStatus }) }),
934
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: item.locales.join(", ") }),
935
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: item.versionCount })
936
- ] }, item.templateId)) })
937
- ] }) });
1010
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_ui11.Card, { role: "region", "aria-label": "Templates", className, children: [
1011
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ListHeader, { count: items.length }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "divide-y divide-border", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TemplateRow, { item }, item.templateId)) })
1013
+ ] });
1014
+ }
1015
+ function ListHeader({ count }) {
1016
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
1017
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col", children: [
1018
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Templates" }),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-muted-foreground", children: "Read-only view of all notification templates" })
1020
+ ] }),
1021
+ count != null && count > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-xs font-medium tabular-nums text-muted-foreground", children: [
1022
+ count,
1023
+ " templates"
1024
+ ] })
1025
+ ] });
1026
+ }
1027
+ function TemplateRow({ item }) {
1028
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { className: "flex flex-col gap-1.5 px-4 py-3.5", children: [
1029
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2.5", children: [
1030
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate text-sm font-medium text-foreground", children: item.templateId }),
1031
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TemplateStatusBadge, { status: item.latestStatus })
1032
+ ] }),
1033
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
1034
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "tabular-nums", children: [
1035
+ item.versionCount,
1036
+ " versions"
1037
+ ] }),
1038
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { "aria-hidden": "true", className: "text-border", children: "\xB7" }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex flex-wrap items-center gap-1", children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Locales:" }),
1041
+ item.locales.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1042
+ "span",
1043
+ {
1044
+ className: "inline-flex items-center rounded bg-muted px-1.5 py-0.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground",
1045
+ children: locale
1046
+ },
1047
+ locale
1048
+ ))
1049
+ ] })
1050
+ ] })
1051
+ ] });
1052
+ }
1053
+ function AlertIcon2() {
1054
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1055
+ "svg",
1056
+ {
1057
+ viewBox: "0 0 24 24",
1058
+ fill: "none",
1059
+ stroke: "currentColor",
1060
+ strokeWidth: "2",
1061
+ strokeLinecap: "round",
1062
+ strokeLinejoin: "round",
1063
+ children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }),
1065
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
1067
+ ]
1068
+ }
1069
+ );
1070
+ }
1071
+ function FileIcon() {
1072
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1073
+ "svg",
1074
+ {
1075
+ viewBox: "0 0 24 24",
1076
+ fill: "none",
1077
+ stroke: "currentColor",
1078
+ strokeWidth: "2",
1079
+ strokeLinecap: "round",
1080
+ strokeLinejoin: "round",
1081
+ children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
1083
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M10 9H8" }),
1085
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M16 13H8" }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M16 17H8" })
1087
+ ]
1088
+ }
1089
+ );
938
1090
  }
939
1091
 
940
1092
  // src/template-editor.tsx
@@ -2148,13 +2300,78 @@ function SegmentBuilder({
2148
2300
 
2149
2301
  // src/suppression-manager.tsx
2150
2302
  var import_react13 = require("react");
2151
- var import_react_ui21 = require("@quanticjs/react-ui");
2303
+ var import_react_ui22 = require("@quanticjs/react-ui");
2152
2304
  var import_react_query18 = require("@quanticjs/react-query");
2305
+
2306
+ // src/admin-panel.tsx
2307
+ var import_react_ui21 = require("@quanticjs/react-ui");
2153
2308
  var import_jsx_runtime22 = require("react/jsx-runtime");
2309
+ function PanelHeader({ title, subtitle }) {
2310
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col", children: [
2311
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: title }),
2312
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-xs text-muted-foreground tabular-nums", children: subtitle })
2313
+ ] }) });
2314
+ }
2315
+ function SkeletonRows({ label, rows = 4 }) {
2316
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { role: "status", "aria-label": label, className: "flex flex-col gap-2 p-4", children: [
2317
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: label }),
2318
+ Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_ui21.Skeleton, { className: "h-10 w-full" }, i))
2319
+ ] });
2320
+ }
2321
+ function ErrorPanel({ message, onRetry }) {
2322
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
2323
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2324
+ "span",
2325
+ {
2326
+ "aria-hidden": "true",
2327
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
2328
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AlertIcon3, {})
2329
+ }
2330
+ ),
2331
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm font-medium text-foreground", children: message }),
2332
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_ui21.Button, { type: "button", onClick: onRetry, children: "Try again" })
2333
+ ] });
2334
+ }
2335
+ function Pager({
2336
+ label,
2337
+ page,
2338
+ totalPages,
2339
+ onPrev,
2340
+ onNext
2341
+ }) {
2342
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2343
+ "nav",
2344
+ {
2345
+ "aria-label": label,
2346
+ className: "flex items-center justify-between gap-2 border-t border-border px-4 py-3",
2347
+ children: [
2348
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_ui21.Button, { type: "button", variant: "outline", size: "sm", onClick: onPrev, disabled: page <= 1, children: "Previous" }),
2349
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "text-xs text-muted-foreground tabular-nums", children: [
2350
+ "Page ",
2351
+ page,
2352
+ " of ",
2353
+ totalPages
2354
+ ] }),
2355
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_ui21.Button, { type: "button", variant: "outline", size: "sm", onClick: onNext, disabled: page >= totalPages, children: "Next" })
2356
+ ]
2357
+ }
2358
+ );
2359
+ }
2360
+ var FIELD_CLASS = "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring";
2361
+ function AlertIcon3() {
2362
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2363
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }),
2364
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
2365
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
2366
+ ] });
2367
+ }
2368
+
2369
+ // src/suppression-manager.tsx
2370
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2154
2371
  var LIMIT3 = 20;
2155
2372
  var CHANNELS3 = ["inapp", "email", "push", "sms"];
2156
2373
  function SuppressionManager({ basePath = "/api", className }) {
2157
- const toast = (0, import_react_ui21.useToast)();
2374
+ const toast = (0, import_react_ui22.useToast)();
2158
2375
  const [page, setPage] = (0, import_react13.useState)(1);
2159
2376
  const [channel, setChannel] = (0, import_react13.useState)("email");
2160
2377
  const [address, setAddress] = (0, import_react13.useState)("");
@@ -2194,149 +2411,88 @@ function SuppressionManager({ basePath = "/api", className }) {
2194
2411
  event.preventDefault();
2195
2412
  add.mutate({ channel, address: address.trim(), reason: reason.trim() });
2196
2413
  };
2197
- const addForm = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2198
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
2199
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
2200
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2201
- "select",
2202
- {
2203
- id: "suppression-channel",
2204
- value: channel,
2205
- onChange: (e) => setChannel(e.target.value),
2206
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2207
- children: CHANNELS3.map((c) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: c, children: c }, c))
2208
- }
2209
- )
2210
- ] }),
2211
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
2212
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-address", className: "text-sm font-medium text-foreground", children: "Address" }),
2213
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2214
- "input",
2215
- {
2216
- id: "suppression-address",
2217
- type: "text",
2218
- value: address,
2219
- onChange: (e) => setAddress(e.target.value),
2220
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
2221
- }
2222
- )
2223
- ] }),
2224
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
2225
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-reason", className: "text-sm font-medium text-foreground", children: "Reason" }),
2226
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2227
- "input",
2228
- {
2229
- id: "suppression-reason",
2230
- type: "text",
2231
- value: reason,
2232
- onChange: (e) => setReason(e.target.value),
2233
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
2234
- }
2235
- )
2236
- ] }),
2237
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2238
- "button",
2239
- {
2240
- type: "submit",
2241
- disabled: add.isPending,
2242
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2243
- children: add.isPending ? "Adding\u2026" : "Add suppression"
2244
- }
2245
- )
2246
- ] });
2247
- let body;
2248
- if (isLoading) {
2249
- body = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { role: "status", "aria-label": "Loading suppressions", className: "flex flex-col gap-2 p-4", children: [
2250
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Loading suppressions" }),
2251
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2252
- ] });
2253
- } else if (isError) {
2254
- body = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2255
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load suppressions" }),
2256
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2257
- "button",
2258
- {
2259
- type: "button",
2260
- onClick: () => void refetch(),
2261
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2262
- children: "Try again"
2263
- }
2264
- )
2265
- ] });
2266
- } else {
2267
- const rows = data?.items ?? [];
2268
- const totalPages = data?.totalPages ?? 1;
2269
- if (rows.length === 0) {
2270
- body = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No suppressions" });
2271
- } else {
2272
- body = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2273
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("table", { className: "w-full text-sm", children: [
2274
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2275
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
2276
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Address" }),
2277
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Reason" }),
2278
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
2279
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Actions" }) })
2414
+ const rows = data?.items ?? [];
2415
+ const totalPages = data?.totalPages ?? 1;
2416
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("section", { "aria-label": "Suppression list", className: (0, import_react_ui22.cn)("flex flex-col gap-4", className), children: [
2417
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_ui22.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
2418
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex flex-col gap-1 text-sm", children: [
2419
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-medium text-foreground", children: "Channel" }),
2420
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2421
+ "select",
2422
+ {
2423
+ value: channel,
2424
+ onChange: (e) => setChannel(e.target.value),
2425
+ className: FIELD_CLASS,
2426
+ children: CHANNELS3.map((c) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: c, children: c }, c))
2427
+ }
2428
+ )
2429
+ ] }),
2430
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
2431
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-medium text-foreground", children: "Address" }),
2432
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { type: "text", value: address, onChange: (e) => setAddress(e.target.value), className: FIELD_CLASS })
2433
+ ] }),
2434
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
2435
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-medium text-foreground", children: "Reason" }),
2436
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { type: "text", value: reason, onChange: (e) => setReason(e.target.value), className: FIELD_CLASS })
2437
+ ] }),
2438
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_ui22.Button, { type: "submit", disabled: add.isPending, children: add.isPending ? "Adding\u2026" : "Add suppression" })
2439
+ ] }) }),
2440
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react_ui22.Card, { children: [
2441
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(PanelHeader, { title: "Suppressions", subtitle: `${data?.total ?? rows.length} entries` }),
2442
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SkeletonRows, { label: "Loading suppressions" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ErrorPanel, { message: "Failed to load suppressions", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_ui22.EmptyState, { icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(BanIcon, {}), title: "No suppressions", description: "No addresses are suppressed." }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
2443
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("table", { className: "w-full text-sm", children: [
2444
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2445
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channel" }),
2446
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Address" }),
2447
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Reason" }),
2448
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
2449
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Actions" }) })
2280
2450
  ] }) }),
2281
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("tr", { className: "border-b border-border", children: [
2282
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
2283
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.address }),
2284
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.reason }),
2285
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui21.formatDateTime)(row.createdAt) }),
2286
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2287
- "button",
2451
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
2452
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "inline-flex items-center rounded bg-muted px-1.5 py-0.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: row.channel }) }),
2453
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.address }),
2454
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.reason }),
2455
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui22.formatDateTime)(row.createdAt) }),
2456
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-end", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2457
+ import_react_ui22.Button,
2288
2458
  {
2289
2459
  type: "button",
2460
+ variant: "ghost",
2461
+ size: "sm",
2462
+ className: "text-destructive",
2290
2463
  disabled: remove.isPending,
2291
2464
  onClick: () => remove.mutate(row.id),
2292
- className: "rounded-md border border-border px-3 py-1 text-sm font-medium text-destructive hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2293
2465
  children: "Remove"
2294
2466
  }
2295
2467
  ) })
2296
2468
  ] }, row.id)) })
2297
2469
  ] }),
2298
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("nav", { "aria-label": "Suppression pagination", className: "flex items-center justify-between", children: [
2299
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2300
- "button",
2301
- {
2302
- type: "button",
2303
- onClick: () => setPage((p) => Math.max(1, p - 1)),
2304
- disabled: page <= 1,
2305
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2306
- children: "Previous"
2307
- }
2308
- ),
2309
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
2310
- "Page ",
2470
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2471
+ Pager,
2472
+ {
2473
+ label: "Suppression pagination",
2311
2474
  page,
2312
- " of ",
2313
- totalPages
2314
- ] }),
2315
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2316
- "button",
2317
- {
2318
- type: "button",
2319
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
2320
- disabled: page >= totalPages,
2321
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2322
- children: "Next"
2323
- }
2324
- )
2325
- ] })
2326
- ] });
2327
- }
2328
- }
2329
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("section", { "aria-label": "Suppression list", className: (0, import_react_ui21.cn)("flex flex-col gap-4", className), children: [
2330
- addForm,
2331
- body
2475
+ totalPages,
2476
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
2477
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
2478
+ }
2479
+ )
2480
+ ] })
2481
+ ] })
2482
+ ] });
2483
+ }
2484
+ function BanIcon() {
2485
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2486
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
2487
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "m4.9 4.9 14.2 14.2" })
2332
2488
  ] });
2333
2489
  }
2334
2490
 
2335
2491
  // src/dlq-console.tsx
2336
2492
  var import_react14 = require("react");
2337
- var import_react_ui22 = require("@quanticjs/react-ui");
2493
+ var import_react_ui23 = require("@quanticjs/react-ui");
2338
2494
  var import_react_query19 = require("@quanticjs/react-query");
2339
- var import_jsx_runtime23 = require("react/jsx-runtime");
2495
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2340
2496
  var LIMIT4 = 20;
2341
2497
  var STATUS_FILTERS = ["queued", "replayed", "discarded"];
2342
2498
  function buildErrorDescription(error) {
@@ -2360,35 +2516,30 @@ function DlqMessageDetailRow({ id, basePath }) {
2360
2516
  (client) => client.get(`${basePath}/admin/dlq/${id}`)
2361
2517
  );
2362
2518
  if (isLoading) {
2363
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { role: "status", "aria-label": "Loading message detail", className: "flex flex-col gap-2 p-3", children: [
2364
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Loading message detail" }),
2365
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { "aria-hidden": "true", className: "h-16 animate-pulse rounded bg-muted" })
2519
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { role: "status", "aria-label": "Loading message detail", className: "p-4", children: [
2520
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: "Loading message detail" }),
2521
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "aria-hidden": "true", className: "h-16 animate-pulse rounded bg-muted motion-reduce:animate-none" })
2366
2522
  ] });
2367
2523
  }
2368
2524
  if (isError) {
2369
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col items-start gap-3 p-3", children: [
2370
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load message detail" }),
2371
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2372
- "button",
2373
- {
2374
- type: "button",
2375
- onClick: () => void refetch(),
2376
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2377
- children: "Try again"
2378
- }
2379
- )
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load message detail" }),
2527
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_ui23.Button, { type: "button", size: "sm", onClick: () => void refetch(), children: "Try again" })
2380
2528
  ] });
2381
2529
  }
2382
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-2 p-3 text-sm", children: [
2383
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { className: "text-muted-foreground", children: [
2384
- "Error: ",
2385
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-foreground", children: data?.errorMessage ?? "\u2014" })
2530
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-3 px-5 py-4 text-sm", children: [
2531
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { children: [
2532
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Error" }),
2533
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "font-mono text-xs text-destructive", children: data?.errorMessage ?? "\u2014" })
2386
2534
  ] }),
2387
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("pre", { className: "max-h-64 overflow-auto rounded bg-muted p-3 text-xs text-foreground", children: JSON.stringify(data?.payload ?? {}, null, 2) })
2535
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { children: [
2536
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Payload" }),
2537
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("pre", { className: "max-h-64 overflow-auto rounded-md border border-border bg-card p-3 font-mono text-xs leading-relaxed text-foreground", children: JSON.stringify(data?.payload ?? {}, null, 2) })
2538
+ ] })
2388
2539
  ] });
2389
2540
  }
2390
2541
  function DlqConsole({ basePath = "/api", className }) {
2391
- const toast = (0, import_react_ui22.useToast)();
2542
+ const toast = (0, import_react_ui23.useToast)();
2392
2543
  const [page, setPage] = (0, import_react14.useState)(1);
2393
2544
  const [statusFilter, setStatusFilter] = (0, import_react14.useState)("");
2394
2545
  const [expandedId, setExpandedId] = (0, import_react14.useState)(null);
@@ -2432,158 +2583,116 @@ function DlqConsole({ basePath = "/api", className }) {
2432
2583
  })
2433
2584
  }
2434
2585
  );
2435
- const filterControl = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
2436
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("label", { htmlFor: "dlq-status", className: "text-sm font-medium text-foreground", children: "Status" }),
2437
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2438
- "select",
2439
- {
2440
- id: "dlq-status",
2441
- value: statusFilter,
2442
- onChange: (e) => {
2443
- setStatusFilter(e.target.value);
2444
- setPage(1);
2445
- },
2446
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2447
- children: [
2448
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "", children: "All" }),
2449
- STATUS_FILTERS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: s, children: s }, s))
2450
- ]
2451
- }
2452
- )
2453
- ] });
2454
- let body;
2455
- if (isLoading) {
2456
- body = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2457
- "div",
2458
- {
2459
- role: "status",
2460
- "aria-label": "Loading dead-letter messages",
2461
- className: "flex flex-col gap-2 p-4",
2462
- children: [
2463
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Loading dead-letter messages" }),
2464
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2465
- ]
2466
- }
2467
- );
2468
- } else if (isError) {
2469
- body = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2470
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load dead-letter messages" }),
2471
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2472
- "button",
2586
+ const rows = data?.items ?? [];
2587
+ const totalPages = data?.totalPages ?? 1;
2588
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("section", { "aria-label": "Dead-letter queue", className: (0, import_react_ui23.cn)("flex flex-col gap-4", className), children: [
2589
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center justify-end gap-2", children: [
2590
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "dlq-status", className: "text-sm font-medium text-foreground", children: "Status" }),
2591
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2592
+ "select",
2473
2593
  {
2474
- type: "button",
2475
- onClick: () => void refetch(),
2476
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2477
- children: "Try again"
2594
+ id: "dlq-status",
2595
+ value: statusFilter,
2596
+ onChange: (e) => {
2597
+ setStatusFilter(e.target.value);
2598
+ setPage(1);
2599
+ },
2600
+ className: FIELD_CLASS,
2601
+ children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: "", children: "All" }),
2603
+ STATUS_FILTERS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("option", { value: s, children: s }, s))
2604
+ ]
2478
2605
  }
2479
2606
  )
2480
- ] });
2481
- } else {
2482
- const rows = data?.items ?? [];
2483
- const totalPages = data?.totalPages ?? 1;
2484
- if (rows.length === 0) {
2485
- body = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No dead-letter messages" });
2486
- } else {
2487
- body = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
2488
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("table", { className: "w-full text-sm", children: [
2489
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2490
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Request" }),
2491
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failure reason" }),
2492
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
2493
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
2494
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "First seen" }),
2495
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Actions" }) })
2496
- ] }) }),
2497
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tbody", { children: rows.map((row) => {
2498
- const isExpanded = expandedId === row.id;
2499
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react14.Fragment, { children: [
2500
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border", children: [
2501
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "py-3 pe-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2502
- "button",
2503
- {
2504
- type: "button",
2505
- "aria-expanded": isExpanded,
2506
- onClick: () => setExpandedId(isExpanded ? null : row.id),
2507
- className: "rounded text-start font-medium text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2508
- children: row.requestId ?? row.id
2509
- }
2510
- ) }),
2511
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.failureReason }),
2512
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_ui22.StatusBadge, { variant: statusVariant3(row.status), appearance: "dot", children: row.status }) }),
2513
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.attemptCount }),
2514
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui22.formatDateTime)(row.firstSeenAt) }),
2515
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
2516
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2517
- "button",
2518
- {
2519
- type: "button",
2520
- disabled: replay.isPending,
2521
- onClick: () => replay.mutate(row.id),
2522
- className: "rounded-md border border-border px-3 py-1 text-sm font-medium text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2523
- children: "Replay"
2524
- }
2525
- ),
2526
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2527
- "button",
2528
- {
2529
- type: "button",
2530
- disabled: discard.isPending,
2531
- onClick: () => discard.mutate(row.id),
2532
- className: "rounded-md border border-border px-3 py-1 text-sm font-medium text-destructive hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2533
- children: "Discard"
2534
- }
2535
- )
2536
- ] }) })
2607
+ ] }),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react_ui23.Card, { children: [
2609
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PanelHeader, { title: "Messages", subtitle: !isLoading && !isError ? `${data?.total ?? rows.length} messages` : void 0 }),
2610
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SkeletonRows, { label: "Loading dead-letter messages" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ErrorPanel, { message: "Failed to load dead-letter messages", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_ui23.EmptyState, { icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SkullIcon, {}), title: "No dead-letter messages", description: "The queue is empty for the current filter." }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
2611
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "divide-y divide-border", children: rows.map((row) => {
2612
+ const isExpanded = expandedId === row.id;
2613
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react14.Fragment, { children: [
2614
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("li", { className: "flex items-start gap-3 px-4 py-3.5", children: [
2615
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2616
+ "button",
2617
+ {
2618
+ type: "button",
2619
+ "aria-expanded": isExpanded,
2620
+ "aria-label": isExpanded ? "Collapse" : "Expand",
2621
+ onClick: () => setExpandedId(isExpanded ? null : row.id),
2622
+ className: "mt-0.5 grid size-5 shrink-0 place-items-center rounded text-muted-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring [&_svg]:size-4",
2623
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ChevronIcon, { open: isExpanded })
2624
+ }
2625
+ ),
2626
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "min-w-0 flex-1", children: [
2627
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2628
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "font-mono text-xs text-muted-foreground", children: row.requestId ?? row.id }),
2629
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_ui23.StatusBadge, { variant: statusVariant3(row.status), appearance: "dot", children: row.status })
2630
+ ] }),
2631
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mt-1 truncate text-sm font-medium text-foreground", children: row.failureReason }),
2632
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 text-xs text-muted-foreground", children: [
2633
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "tabular-nums", children: [
2634
+ row.attemptCount,
2635
+ " attempts"
2636
+ ] }),
2637
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": "true", className: "text-border", children: "\xB7" }),
2638
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "tabular-nums", children: [
2639
+ "first seen ",
2640
+ (0, import_react_ui23.formatDateTime)(row.firstSeenAt)
2641
+ ] })
2642
+ ] })
2537
2643
  ] }),
2538
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { colSpan: 6, className: "bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DlqMessageDetailRow, { id: row.id, basePath }) }) })
2539
- ] }, row.id);
2540
- }) })
2541
- ] }),
2542
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("nav", { "aria-label": "Dead-letter pagination", className: "flex items-center justify-between", children: [
2543
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2544
- "button",
2545
- {
2546
- type: "button",
2547
- onClick: () => setPage((p) => Math.max(1, p - 1)),
2548
- disabled: page <= 1,
2549
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2550
- children: "Previous"
2551
- }
2552
- ),
2553
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
2554
- "Page ",
2644
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex shrink-0 items-center gap-1.5", children: [
2645
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_ui23.Button, { type: "button", variant: "outline", size: "sm", disabled: replay.isPending, onClick: () => replay.mutate(row.id), children: "Replay" }),
2646
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_ui23.Button, { type: "button", variant: "ghost", size: "sm", className: "text-destructive", disabled: discard.isPending, onClick: () => discard.mutate(row.id), children: "Discard" })
2647
+ ] })
2648
+ ] }),
2649
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "border-t border-border bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DlqMessageDetailRow, { id: row.id, basePath }) })
2650
+ ] }, row.id);
2651
+ }) }),
2652
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2653
+ Pager,
2654
+ {
2655
+ label: "Dead-letter pagination",
2555
2656
  page,
2556
- " of ",
2557
- totalPages
2558
- ] }),
2559
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2560
- "button",
2561
- {
2562
- type: "button",
2563
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
2564
- disabled: page >= totalPages,
2565
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
2566
- children: "Next"
2567
- }
2568
- )
2569
- ] })
2570
- ] });
2657
+ totalPages,
2658
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
2659
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
2660
+ }
2661
+ )
2662
+ ] })
2663
+ ] })
2664
+ ] });
2665
+ }
2666
+ function ChevronIcon({ open }) {
2667
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2668
+ "svg",
2669
+ {
2670
+ viewBox: "0 0 24 24",
2671
+ fill: "none",
2672
+ stroke: "currentColor",
2673
+ strokeWidth: "2",
2674
+ strokeLinecap: "round",
2675
+ strokeLinejoin: "round",
2676
+ className: (0, import_react_ui23.cn)("transition-transform motion-reduce:transition-none", open && "rotate-90"),
2677
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "m9 18 6-6-6-6" })
2571
2678
  }
2572
- }
2573
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("section", { "aria-label": "Dead-letter queue", className: (0, import_react_ui22.cn)("flex flex-col gap-4", className), children: [
2574
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("header", { className: "flex items-center justify-between", children: [
2575
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Dead-letter queue" }),
2576
- filterControl
2577
- ] }),
2578
- body
2679
+ );
2680
+ }
2681
+ function SkullIcon() {
2682
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2683
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("circle", { cx: "9", cy: "12", r: "1" }),
2684
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("circle", { cx: "15", cy: "12", r: "1" }),
2685
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M8 20v2h8v-2" }),
2686
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "m12.5 17-.5-1-.5 1h1Z" }),
2687
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { d: "M16 20a2 2 0 0 0 1.56-3.25 8 8 0 1 0-11.12 0A2 2 0 0 0 8 20" })
2579
2688
  ] });
2580
2689
  }
2581
2690
 
2582
2691
  // src/catalog-editor.tsx
2583
2692
  var import_react15 = require("react");
2584
- var import_react_ui23 = require("@quanticjs/react-ui");
2693
+ var import_react_ui24 = require("@quanticjs/react-ui");
2585
2694
  var import_react_query20 = require("@quanticjs/react-query");
2586
- var import_jsx_runtime24 = require("react/jsx-runtime");
2695
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2587
2696
  function normalize2(data) {
2588
2697
  if (!data) return [];
2589
2698
  if (Array.isArray(data)) return data;
@@ -2595,7 +2704,7 @@ function normalize2(data) {
2595
2704
  );
2596
2705
  }
2597
2706
  function CatalogEditor({ basePath = "/api", className }) {
2598
- const toast = (0, import_react_ui23.useToast)();
2707
+ const toast = (0, import_react_ui24.useToast)();
2599
2708
  const [filter, setFilter] = (0, import_react15.useState)("");
2600
2709
  const [editingId, setEditingId] = (0, import_react15.useState)(null);
2601
2710
  const [editValue, setEditValue] = (0, import_react15.useState)("");
@@ -2665,10 +2774,10 @@ function CatalogEditor({ basePath = "/api", className }) {
2665
2774
  setEditingId(`${entry.key}:${entry.locale}`);
2666
2775
  setEditValue(entry.value);
2667
2776
  };
2668
- const addForm = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2669
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
2670
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-key", className: "text-sm font-medium text-foreground", children: "Key" }),
2671
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2777
+ const addForm = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2778
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
2779
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { htmlFor: "catalog-new-key", className: "text-sm font-medium text-foreground", children: "Key" }),
2780
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2672
2781
  "input",
2673
2782
  {
2674
2783
  id: "catalog-new-key",
@@ -2679,9 +2788,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2679
2788
  }
2680
2789
  )
2681
2790
  ] }),
2682
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
2683
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-locale", className: "text-sm font-medium text-foreground", children: "Locale" }),
2684
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2791
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
2792
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { htmlFor: "catalog-new-locale", className: "text-sm font-medium text-foreground", children: "Locale" }),
2793
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2685
2794
  "input",
2686
2795
  {
2687
2796
  id: "catalog-new-locale",
@@ -2692,9 +2801,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2692
2801
  }
2693
2802
  )
2694
2803
  ] }),
2695
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
2696
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-value", className: "text-sm font-medium text-foreground", children: "Value" }),
2697
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2804
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
2805
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { htmlFor: "catalog-new-value", className: "text-sm font-medium text-foreground", children: "Value" }),
2806
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2698
2807
  "input",
2699
2808
  {
2700
2809
  id: "catalog-new-value",
@@ -2705,7 +2814,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2705
2814
  }
2706
2815
  )
2707
2816
  ] }),
2708
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2817
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2709
2818
  "button",
2710
2819
  {
2711
2820
  type: "submit",
@@ -2715,9 +2824,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2715
2824
  }
2716
2825
  )
2717
2826
  ] });
2718
- const filterInput = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
2719
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-filter", className: "text-sm font-medium text-foreground", children: "Filter by key" }),
2720
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2827
+ const filterInput = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-1", children: [
2828
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("label", { htmlFor: "catalog-filter", className: "text-sm font-medium text-foreground", children: "Filter by key" }),
2829
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2721
2830
  "input",
2722
2831
  {
2723
2832
  id: "catalog-filter",
@@ -2730,14 +2839,14 @@ function CatalogEditor({ basePath = "/api", className }) {
2730
2839
  ] });
2731
2840
  let body;
2732
2841
  if (isLoading) {
2733
- body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { role: "status", "aria-label": "Loading catalog", className: "flex flex-col gap-2 p-4", children: [
2734
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: "Loading catalog" }),
2735
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2842
+ body = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { role: "status", "aria-label": "Loading catalog", className: "flex flex-col gap-2 p-4", children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Loading catalog" }),
2844
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2736
2845
  ] });
2737
2846
  } else if (isError) {
2738
- body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2739
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load catalog" }),
2740
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2847
+ body = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2848
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load catalog" }),
2849
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2741
2850
  "button",
2742
2851
  {
2743
2852
  type: "button",
@@ -2748,22 +2857,22 @@ function CatalogEditor({ basePath = "/api", className }) {
2748
2857
  )
2749
2858
  ] });
2750
2859
  } else if (filtered.length === 0) {
2751
- body = /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: entries.length === 0 ? "No catalog entries" : "No entries match your filter" });
2860
+ body = /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: entries.length === 0 ? "No catalog entries" : "No entries match your filter" });
2752
2861
  } else {
2753
- body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("table", { className: "w-full text-sm", children: [
2754
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2755
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2756
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2757
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Value" }),
2758
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: "Actions" }) })
2862
+ body = /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("table", { className: "w-full text-sm", children: [
2863
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2864
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2865
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2866
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Value" }),
2867
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Actions" }) })
2759
2868
  ] }) }),
2760
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("tbody", { children: filtered.map((entry) => {
2869
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("tbody", { children: filtered.map((entry) => {
2761
2870
  const rowId = `${entry.key}:${entry.locale}`;
2762
2871
  const isEditing = editingId === rowId;
2763
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("tr", { className: "border-b border-border", children: [
2764
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: entry.key }),
2765
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3 text-foreground", children: entry.locale }),
2766
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3 text-foreground", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2872
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border", children: [
2873
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: entry.key }),
2874
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-foreground", children: entry.locale }),
2875
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-foreground", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2767
2876
  "input",
2768
2877
  {
2769
2878
  type: "text",
@@ -2773,8 +2882,8 @@ function CatalogEditor({ basePath = "/api", className }) {
2773
2882
  className: "w-full rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
2774
2883
  }
2775
2884
  ) : entry.value }),
2776
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex items-center gap-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
2777
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2885
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex items-center gap-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
2886
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2778
2887
  "button",
2779
2888
  {
2780
2889
  type: "button",
@@ -2788,7 +2897,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2788
2897
  children: "Save"
2789
2898
  }
2790
2899
  ),
2791
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2900
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2792
2901
  "button",
2793
2902
  {
2794
2903
  type: "button",
@@ -2797,8 +2906,8 @@ function CatalogEditor({ basePath = "/api", className }) {
2797
2906
  children: "Cancel"
2798
2907
  }
2799
2908
  )
2800
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
2801
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2909
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
2910
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2802
2911
  "button",
2803
2912
  {
2804
2913
  type: "button",
@@ -2807,7 +2916,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2807
2916
  children: "Edit"
2808
2917
  }
2809
2918
  ),
2810
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2919
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2811
2920
  "button",
2812
2921
  {
2813
2922
  type: "button",
@@ -2822,7 +2931,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2822
2931
  }) })
2823
2932
  ] });
2824
2933
  }
2825
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("section", { "aria-label": "Catalog editor", className: (0, import_react_ui23.cn)("flex flex-col gap-4", className), children: [
2934
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("section", { "aria-label": "Catalog editor", className: (0, import_react_ui24.cn)("flex flex-col gap-4", className), children: [
2826
2935
  addForm,
2827
2936
  filterInput,
2828
2937
  body
@@ -2830,9 +2939,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2830
2939
  }
2831
2940
 
2832
2941
  // src/missing-translations-panel.tsx
2833
- var import_react_ui24 = require("@quanticjs/react-ui");
2942
+ var import_react_ui25 = require("@quanticjs/react-ui");
2834
2943
  var import_react_query21 = require("@quanticjs/react-query");
2835
- var import_jsx_runtime25 = require("react/jsx-runtime");
2944
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2836
2945
  function normalize3(data) {
2837
2946
  if (Array.isArray(data)) return data;
2838
2947
  return data?.missing ?? [];
@@ -2846,23 +2955,23 @@ function MissingTranslationsPanel({
2846
2955
  (client) => client.get(`${basePath}/i18n/catalog/missing`)
2847
2956
  );
2848
2957
  if (isLoading) {
2849
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2850
2959
  "div",
2851
2960
  {
2852
2961
  role: "status",
2853
2962
  "aria-label": "Loading missing translations",
2854
- className: (0, import_react_ui24.cn)("flex flex-col gap-2 p-4", className),
2963
+ className: (0, import_react_ui25.cn)("flex flex-col gap-2 p-4", className),
2855
2964
  children: [
2856
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Loading missing translations" }),
2857
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2965
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: "Loading missing translations" }),
2966
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2858
2967
  ]
2859
2968
  }
2860
2969
  );
2861
2970
  }
2862
2971
  if (isError) {
2863
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: (0, import_react_ui24.cn)("flex flex-col items-start gap-3 p-4", className), children: [
2864
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load missing translations" }),
2865
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2972
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: (0, import_react_ui25.cn)("flex flex-col items-start gap-3 p-4", className), children: [
2973
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load missing translations" }),
2974
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2866
2975
  "button",
2867
2976
  {
2868
2977
  type: "button",
@@ -2875,26 +2984,26 @@ function MissingTranslationsPanel({
2875
2984
  }
2876
2985
  const rows = normalize3(data);
2877
2986
  if (rows.length === 0) {
2878
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: (0, import_react_ui24.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No missing translations" });
2987
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: (0, import_react_ui25.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No missing translations" });
2879
2988
  }
2880
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("section", { "aria-label": "Missing translations", className: (0, import_react_ui24.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("table", { className: "w-full text-sm", children: [
2881
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2882
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2883
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2884
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" })
2989
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("section", { "aria-label": "Missing translations", className: (0, import_react_ui25.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("table", { className: "w-full text-sm", children: [
2990
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2991
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2992
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2993
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" })
2885
2994
  ] }) }),
2886
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border", children: [
2887
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2888
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.locale }),
2889
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.type ?? "\u2014" })
2995
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border", children: [
2996
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2997
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.locale }),
2998
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.type ?? "\u2014" })
2890
2999
  ] }, `${row.key}:${row.locale}:${i}`)) })
2891
3000
  ] }) });
2892
3001
  }
2893
3002
 
2894
3003
  // src/fallback-report-panel.tsx
2895
- var import_react_ui25 = require("@quanticjs/react-ui");
3004
+ var import_react_ui26 = require("@quanticjs/react-ui");
2896
3005
  var import_react_query22 = require("@quanticjs/react-query");
2897
- var import_jsx_runtime26 = require("react/jsx-runtime");
3006
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2898
3007
  function normalize4(data) {
2899
3008
  if (Array.isArray(data)) return data;
2900
3009
  return data?.entries ?? [];
@@ -2905,23 +3014,23 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2905
3014
  (client) => client.get(`${basePath}/i18n/catalog/fallback-report`)
2906
3015
  );
2907
3016
  if (isLoading) {
2908
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3017
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
2909
3018
  "div",
2910
3019
  {
2911
3020
  role: "status",
2912
3021
  "aria-label": "Loading fallback report",
2913
- className: (0, import_react_ui25.cn)("flex flex-col gap-2 p-4", className),
3022
+ className: (0, import_react_ui26.cn)("flex flex-col gap-2 p-4", className),
2914
3023
  children: [
2915
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: "Loading fallback report" }),
2916
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3024
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: "Loading fallback report" }),
3025
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2917
3026
  ]
2918
3027
  }
2919
3028
  );
2920
3029
  }
2921
3030
  if (isError) {
2922
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: (0, import_react_ui25.cn)("flex flex-col items-start gap-3 p-4", className), children: [
2923
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load fallback report" }),
2924
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3031
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: (0, import_react_ui26.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3032
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load fallback report" }),
3033
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2925
3034
  "button",
2926
3035
  {
2927
3036
  type: "button",
@@ -2934,22 +3043,22 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2934
3043
  }
2935
3044
  const rows = normalize4(data);
2936
3045
  if (rows.length === 0) {
2937
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: (0, import_react_ui25.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No fallbacks reported" });
3046
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: (0, import_react_ui26.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No fallbacks reported" });
2938
3047
  }
2939
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("section", { "aria-label": "Fallback report", className: (0, import_react_ui25.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("table", { className: "w-full text-sm", children: [
2940
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2941
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2942
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Requested" }),
2943
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Resolved" }),
2944
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Chain" })
3048
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("section", { "aria-label": "Fallback report", className: (0, import_react_ui26.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("table", { className: "w-full text-sm", children: [
3049
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3050
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
3051
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Requested" }),
3052
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Resolved" }),
3053
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Chain" })
2945
3054
  ] }) }),
2946
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border", children: [
2947
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2948
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.requestedLocale }),
2949
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.resolvedLocale }),
2950
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.steps && row.steps.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ol", { className: "flex flex-wrap items-center gap-1", children: row.steps.map((step, si) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("li", { className: "flex items-center gap-1", children: [
2951
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-foreground", children: step }),
2952
- si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" })
3055
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border", children: [
3056
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
3057
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.requestedLocale }),
3058
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.resolvedLocale }),
3059
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.steps && row.steps.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ol", { className: "flex flex-wrap items-center gap-1", children: row.steps.map((step, si) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { className: "flex items-center gap-1", children: [
3060
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-foreground", children: step }),
3061
+ si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" })
2953
3062
  ] }, `${step}:${si}`)) }) : "\u2014" })
2954
3063
  ] }, `${row.key}:${row.requestedLocale}:${i}`)) })
2955
3064
  ] }) });
@@ -2957,12 +3066,12 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2957
3066
 
2958
3067
  // src/recipient-admin-panel.tsx
2959
3068
  var import_react16 = require("react");
2960
- var import_react_ui26 = require("@quanticjs/react-ui");
3069
+ var import_react_ui27 = require("@quanticjs/react-ui");
2961
3070
  var import_react_query23 = require("@quanticjs/react-query");
2962
- var import_jsx_runtime27 = require("react/jsx-runtime");
3071
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2963
3072
  var LIMIT5 = 20;
2964
3073
  function RecipientAdminPanel({ basePath = "/api", className }) {
2965
- const toast = (0, import_react_ui26.useToast)();
3074
+ const toast = (0, import_react_ui27.useToast)();
2966
3075
  const [page, setPage] = (0, import_react16.useState)(1);
2967
3076
  const [search, setSearch] = (0, import_react16.useState)("");
2968
3077
  const [query, setQuery] = (0, import_react16.useState)("");
@@ -3010,10 +3119,10 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3010
3119
  if (row.consentSms) active.push("sms");
3011
3120
  return active.length > 0 ? active.join(", ") : "none";
3012
3121
  };
3013
- const searchForm = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit: onSearch, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3014
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-1", children: [
3015
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("label", { htmlFor: "recipient-search", className: "text-sm font-medium text-foreground", children: "Search recipients" }),
3016
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3122
+ const searchForm = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("form", { onSubmit: onSearch, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3123
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-1", children: [
3124
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("label", { htmlFor: "recipient-search", className: "text-sm font-medium text-foreground", children: "Search recipients" }),
3125
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3017
3126
  "input",
3018
3127
  {
3019
3128
  id: "recipient-search",
@@ -3024,7 +3133,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3024
3133
  }
3025
3134
  )
3026
3135
  ] }),
3027
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3136
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3028
3137
  "button",
3029
3138
  {
3030
3139
  type: "submit",
@@ -3035,14 +3144,14 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3035
3144
  ] });
3036
3145
  let body;
3037
3146
  if (isLoading) {
3038
- body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "status", "aria-label": "Loading recipients", className: "flex flex-col gap-2 p-4", children: [
3039
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: "Loading recipients" }),
3040
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3147
+ body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { role: "status", "aria-label": "Loading recipients", className: "flex flex-col gap-2 p-4", children: [
3148
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Loading recipients" }),
3149
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3041
3150
  ] });
3042
3151
  } else if (isError) {
3043
- body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3044
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load recipients" }),
3045
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3152
+ body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3153
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load recipients" }),
3154
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3046
3155
  "button",
3047
3156
  {
3048
3157
  type: "button",
@@ -3056,26 +3165,26 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3056
3165
  const rows = data?.items ?? [];
3057
3166
  const totalPages = data?.totalPages ?? 1;
3058
3167
  if (rows.length === 0) {
3059
- body = /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No recipients" });
3168
+ body = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No recipients" });
3060
3169
  } else {
3061
- body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3062
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("table", { className: "w-full text-sm", children: [
3063
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3064
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "User" }),
3065
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Email" }),
3066
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Phone" }),
3067
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Consents" }),
3068
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3069
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: "Actions" }) })
3170
+ body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
3171
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full text-sm", children: [
3172
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3173
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "User" }),
3174
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Email" }),
3175
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Phone" }),
3176
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Consents" }),
3177
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Actions" }) })
3070
3179
  ] }) }),
3071
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border", children: [
3072
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.userId }),
3073
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.email ?? "\u2014" }),
3074
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.phone ?? "\u2014" }),
3075
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: consents(row) }),
3076
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui26.formatDateTime)(row.createdAt) }),
3077
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2", children: [
3078
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3180
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border", children: [
3181
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.userId }),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.email ?? "\u2014" }),
3183
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.phone ?? "\u2014" }),
3184
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: consents(row) }),
3185
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui27.formatDateTime)(row.createdAt) }),
3186
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2", children: [
3187
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3079
3188
  "button",
3080
3189
  {
3081
3190
  type: "button",
@@ -3085,7 +3194,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3085
3194
  children: "Export"
3086
3195
  }
3087
3196
  ),
3088
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3197
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3089
3198
  "button",
3090
3199
  {
3091
3200
  type: "button",
@@ -3098,8 +3207,8 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3098
3207
  ] }) })
3099
3208
  ] }, row.userId)) })
3100
3209
  ] }),
3101
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("nav", { "aria-label": "Recipient pagination", className: "flex items-center justify-between", children: [
3102
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3210
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("nav", { "aria-label": "Recipient pagination", className: "flex items-center justify-between", children: [
3211
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3103
3212
  "button",
3104
3213
  {
3105
3214
  type: "button",
@@ -3109,13 +3218,13 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3109
3218
  children: "Previous"
3110
3219
  }
3111
3220
  ),
3112
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
3221
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
3113
3222
  "Page ",
3114
3223
  page,
3115
3224
  " of ",
3116
3225
  totalPages
3117
3226
  ] }),
3118
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3227
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3119
3228
  "button",
3120
3229
  {
3121
3230
  type: "button",
@@ -3129,7 +3238,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3129
3238
  ] });
3130
3239
  }
3131
3240
  }
3132
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { "aria-label": "Recipient administration", className: (0, import_react_ui26.cn)("flex flex-col gap-4", className), children: [
3241
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": "Recipient administration", className: (0, import_react_ui27.cn)("flex flex-col gap-4", className), children: [
3133
3242
  searchForm,
3134
3243
  body
3135
3244
  ] });
@@ -3137,9 +3246,9 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3137
3246
 
3138
3247
  // src/webhook-endpoint-manager.tsx
3139
3248
  var import_react17 = require("react");
3140
- var import_react_ui27 = require("@quanticjs/react-ui");
3249
+ var import_react_ui28 = require("@quanticjs/react-ui");
3141
3250
  var import_react_query24 = require("@quanticjs/react-query");
3142
- var import_jsx_runtime28 = require("react/jsx-runtime");
3251
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3143
3252
  var EVENT_TYPES = [
3144
3253
  "notification.sent",
3145
3254
  "notification.delivered",
@@ -3165,15 +3274,15 @@ function WebhookDeliveries({ endpointId, basePath }) {
3165
3274
  (client) => client.get(`${basePath}/webhook-endpoints/${endpointId}/deliveries`)
3166
3275
  );
3167
3276
  if (isLoading) {
3168
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { role: "status", "aria-label": "Loading deliveries", className: "flex flex-col gap-2 p-3", children: [
3169
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Loading deliveries" }),
3170
- [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "h-8 animate-pulse rounded bg-muted" }, i))
3277
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { role: "status", "aria-label": "Loading deliveries", className: "flex flex-col gap-2 p-3", children: [
3278
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "sr-only", children: "Loading deliveries" }),
3279
+ [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-hidden": "true", className: "h-8 animate-pulse rounded bg-muted" }, i))
3171
3280
  ] });
3172
3281
  }
3173
3282
  if (isError) {
3174
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-start gap-2 p-3", children: [
3175
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load deliveries" }),
3176
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3283
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-start gap-2 p-3", children: [
3284
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load deliveries" }),
3285
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3177
3286
  "button",
3178
3287
  {
3179
3288
  type: "button",
@@ -3186,18 +3295,18 @@ function WebhookDeliveries({ endpointId, basePath }) {
3186
3295
  }
3187
3296
  const rows = normalizeDeliveries(data);
3188
3297
  if (rows.length === 0) {
3189
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-3 text-center text-sm text-muted-foreground", children: "No deliveries" });
3298
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "p-3 text-center text-sm text-muted-foreground", children: "No deliveries" });
3190
3299
  }
3191
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full text-sm", children: [
3192
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3193
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivery" }),
3194
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3195
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3300
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("table", { className: "w-full text-sm", children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3302
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivery" }),
3303
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3304
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3196
3305
  ] }) }),
3197
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border", children: [
3198
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "py-2 pe-4 font-mono text-foreground", children: row.id }),
3199
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: row.status }),
3200
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: (0, import_react_ui27.formatDateTime)(row.createdAt) })
3306
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border", children: [
3307
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "py-2 pe-4 font-mono text-foreground", children: row.id }),
3308
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: row.status }),
3309
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: (0, import_react_ui28.formatDateTime)(row.createdAt) })
3201
3310
  ] }, row.id)) })
3202
3311
  ] });
3203
3312
  }
@@ -3205,7 +3314,7 @@ function WebhookEndpointManager({
3205
3314
  basePath = "/api",
3206
3315
  className
3207
3316
  }) {
3208
- const toast = (0, import_react_ui27.useToast)();
3317
+ const toast = (0, import_react_ui28.useToast)();
3209
3318
  const [url, setUrl] = (0, import_react17.useState)("");
3210
3319
  const [events, setEvents] = (0, import_react17.useState)([]);
3211
3320
  const [active, setActive] = (0, import_react17.useState)(true);
@@ -3272,10 +3381,10 @@ function WebhookEndpointManager({
3272
3381
  remove.mutate(id);
3273
3382
  }
3274
3383
  };
3275
- const createForm = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("form", { onSubmit: onCreate, className: "flex flex-col gap-3", noValidate: true, children: [
3276
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-1", children: [
3277
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("label", { htmlFor: "webhook-url", className: "text-sm font-medium text-foreground", children: "Endpoint URL" }),
3278
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3384
+ const createForm = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("form", { onSubmit: onCreate, className: "flex flex-col gap-3", noValidate: true, children: [
3385
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-1", children: [
3386
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("label", { htmlFor: "webhook-url", className: "text-sm font-medium text-foreground", children: "Endpoint URL" }),
3387
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3279
3388
  "input",
3280
3389
  {
3281
3390
  id: "webhook-url",
@@ -3287,19 +3396,19 @@ function WebhookEndpointManager({
3287
3396
  className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3288
3397
  }
3289
3398
  ),
3290
- urlError && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { id: "webhook-url-error", className: "text-xs text-destructive", children: urlError })
3399
+ urlError && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { id: "webhook-url-error", className: "text-xs text-destructive", children: urlError })
3291
3400
  ] }),
3292
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("fieldset", { className: "flex flex-col gap-2", children: [
3293
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("legend", { className: "text-sm font-medium text-foreground", children: "Events" }),
3294
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-wrap gap-3", children: EVENT_TYPES.map((evt) => {
3401
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("fieldset", { className: "flex flex-col gap-2", children: [
3402
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("legend", { className: "text-sm font-medium text-foreground", children: "Events" }),
3403
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex flex-wrap gap-3", children: EVENT_TYPES.map((evt) => {
3295
3404
  const id = `webhook-event-${evt}`;
3296
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3405
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
3297
3406
  "label",
3298
3407
  {
3299
3408
  htmlFor: id,
3300
3409
  className: "flex items-center gap-2 text-sm text-foreground",
3301
3410
  children: [
3302
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3411
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3303
3412
  "input",
3304
3413
  {
3305
3414
  id,
@@ -3316,8 +3425,8 @@ function WebhookEndpointManager({
3316
3425
  );
3317
3426
  }) })
3318
3427
  ] }),
3319
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("label", { htmlFor: "webhook-active", className: "flex items-center gap-2 text-sm text-foreground", children: [
3320
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3428
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("label", { htmlFor: "webhook-active", className: "flex items-center gap-2 text-sm text-foreground", children: [
3429
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3321
3430
  "input",
3322
3431
  {
3323
3432
  id: "webhook-active",
@@ -3329,7 +3438,7 @@ function WebhookEndpointManager({
3329
3438
  ),
3330
3439
  "Active"
3331
3440
  ] }),
3332
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3441
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3333
3442
  "button",
3334
3443
  {
3335
3444
  type: "submit",
@@ -3341,14 +3450,14 @@ function WebhookEndpointManager({
3341
3450
  ] });
3342
3451
  let body;
3343
3452
  if (isLoading) {
3344
- body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { role: "status", "aria-label": "Loading webhook endpoints", className: "flex flex-col gap-2 p-4", children: [
3345
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Loading webhook endpoints" }),
3346
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3453
+ body = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { role: "status", "aria-label": "Loading webhook endpoints", className: "flex flex-col gap-2 p-4", children: [
3454
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "sr-only", children: "Loading webhook endpoints" }),
3455
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3347
3456
  ] });
3348
3457
  } else if (isError) {
3349
- body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3350
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load webhook endpoints" }),
3351
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3458
+ body = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3459
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load webhook endpoints" }),
3460
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3352
3461
  "button",
3353
3462
  {
3354
3463
  type: "button",
@@ -3361,26 +3470,26 @@ function WebhookEndpointManager({
3361
3470
  } else {
3362
3471
  const rows = normalizeEndpoints(data);
3363
3472
  if (rows.length === 0) {
3364
- body = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No webhook endpoints" });
3473
+ body = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No webhook endpoints" });
3365
3474
  } else {
3366
- body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full text-sm", children: [
3367
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3368
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "URL" }),
3369
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Events" }),
3370
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Active" }),
3371
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3372
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Actions" }) })
3475
+ body = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("table", { className: "w-full text-sm", children: [
3476
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3477
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "URL" }),
3478
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Events" }),
3479
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Active" }),
3480
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3481
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "sr-only", children: "Actions" }) })
3373
3482
  ] }) }),
3374
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: rows.map((row) => {
3483
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tbody", { children: rows.map((row) => {
3375
3484
  const isExpanded = expandedId === row.id;
3376
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react17.Fragment, { children: [
3377
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border", children: [
3378
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.url }),
3379
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.events.length > 0 ? row.events.join(", ") : "\u2014" }),
3380
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_ui27.StatusBadge, { variant: row.active ? "success" : "neutral", appearance: "dot", children: row.active ? "active" : "inactive" }) }),
3381
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui27.formatDateTime)(row.createdAt) }),
3382
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2", children: [
3383
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3485
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react17.Fragment, { children: [
3486
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border", children: [
3487
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.url }),
3488
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.events.length > 0 ? row.events.join(", ") : "\u2014" }),
3489
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: row.active ? "success" : "neutral", appearance: "dot", children: row.active ? "active" : "inactive" }) }),
3490
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui28.formatDateTime)(row.createdAt) }),
3491
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
3492
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3384
3493
  "button",
3385
3494
  {
3386
3495
  type: "button",
@@ -3390,7 +3499,7 @@ function WebhookEndpointManager({
3390
3499
  children: row.active ? "Disable" : "Enable"
3391
3500
  }
3392
3501
  ),
3393
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3502
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3394
3503
  "button",
3395
3504
  {
3396
3505
  type: "button",
@@ -3400,7 +3509,7 @@ function WebhookEndpointManager({
3400
3509
  children: "Deliveries"
3401
3510
  }
3402
3511
  ),
3403
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3512
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3404
3513
  "button",
3405
3514
  {
3406
3515
  type: "button",
@@ -3412,57 +3521,53 @@ function WebhookEndpointManager({
3412
3521
  )
3413
3522
  ] }) })
3414
3523
  ] }),
3415
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { colSpan: 5, className: "bg-muted px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WebhookDeliveries, { endpointId: row.id, basePath }) }) })
3524
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { colSpan: 5, className: "bg-muted px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(WebhookDeliveries, { endpointId: row.id, basePath }) }) })
3416
3525
  ] }, row.id);
3417
3526
  }) })
3418
3527
  ] });
3419
3528
  }
3420
3529
  }
3421
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": "Webhook endpoints", className: (0, import_react_ui27.cn)("flex flex-col gap-4", className), children: [
3530
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("section", { "aria-label": "Webhook endpoints", className: (0, import_react_ui28.cn)("flex flex-col gap-4", className), children: [
3422
3531
  createForm,
3423
3532
  body
3424
3533
  ] });
3425
3534
  }
3426
3535
 
3427
3536
  // src/operations-overview.tsx
3428
- var import_react_ui28 = require("@quanticjs/react-ui");
3537
+ var import_react_ui29 = require("@quanticjs/react-ui");
3429
3538
  var import_react_query25 = require("@quanticjs/react-query");
3430
- var import_jsx_runtime29 = require("react/jsx-runtime");
3539
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3431
3540
  function OperationsOverview({ basePath = "/api", className }) {
3432
3541
  const { data, isLoading, isError, refetch } = (0, import_react_query25.useApiQuery)(
3433
3542
  ["operations-overview"],
3434
3543
  (client) => client.get(`${basePath}/v1/admin/overview`)
3435
3544
  );
3436
3545
  if (isLoading) {
3437
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
3546
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3438
3547
  "div",
3439
3548
  {
3440
3549
  role: "status",
3441
3550
  "aria-label": "Loading operations overview",
3442
- className: (0, import_react_ui28.cn)("flex flex-col gap-2 p-4", className),
3551
+ className: (0, import_react_ui29.cn)("flex flex-col gap-4", className),
3443
3552
  children: [
3444
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "sr-only", children: "Loading operations overview" }),
3445
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-hidden": "true", className: "h-20 animate-pulse rounded bg-muted" }, i))
3553
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "sr-only", children: "Loading operations overview" }),
3554
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-hidden": "true", className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.CardContent, { className: "space-y-3 p-5", children: [
3555
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Skeleton, { className: "h-3 w-24" }),
3556
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Skeleton, { className: "h-7 w-28" })
3557
+ ] }) }, i)) }),
3558
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Card, { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardContent, { className: "space-y-2 p-5", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Skeleton, { className: "h-10 w-full" }, i)) }) })
3446
3559
  ]
3447
3560
  }
3448
3561
  );
3449
3562
  }
3450
3563
  if (isError) {
3451
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: (0, import_react_ui28.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3452
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load operations overview" }),
3453
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3454
- "button",
3455
- {
3456
- type: "button",
3457
- onClick: () => void refetch(),
3458
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
3459
- children: "Try again"
3460
- }
3461
- )
3462
- ] });
3564
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Card, { className, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.CardContent, { className: "flex flex-col items-center gap-3 p-8 text-center", children: [
3565
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load operations overview" }),
3566
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Button, { type: "button", onClick: () => void refetch(), children: "Try again" })
3567
+ ] }) });
3463
3568
  }
3464
3569
  if (!data) {
3465
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: (0, import_react_ui28.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No overview data" });
3570
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.EmptyState, { className, title: "No overview data" });
3466
3571
  }
3467
3572
  const windowHours = data.windowHours;
3468
3573
  const channels = data.channels ?? [];
@@ -3472,51 +3577,68 @@ function OperationsOverview({ basePath = "/api", className }) {
3472
3577
  { label: `Delivered (${windowHours}h)`, value: data.totalDelivered },
3473
3578
  { label: `Failed (${windowHours}h)`, value: data.totalFailed }
3474
3579
  ];
3475
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("section", { "aria-label": "Operations overview", className: (0, import_react_ui28.cn)("flex flex-col gap-4", className), children: [
3476
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "rounded-md border border-border bg-card p-4", children: [
3477
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children: card.label }),
3478
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mt-1 text-2xl font-semibold text-foreground", children: card.value })
3479
- ] }, card.label)) }),
3480
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap items-center gap-4", children: [
3481
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
3482
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
3483
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: dlqPending > 0 ? "destructive" : "success", children: dlqPending })
3484
- ] }),
3485
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
3486
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
3487
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm font-medium text-foreground", children: data.broadcastsInFlight })
3580
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { "aria-label": "Operations overview", className: (0, import_react_ui29.cn)("flex flex-col gap-5", className), children: [
3581
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2", children: [
3582
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
3583
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { className: "text-base font-semibold tracking-tight text-foreground", children: "Operations overview" }),
3584
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
3585
+ "Last ",
3586
+ windowHours,
3587
+ "h"
3588
+ ] })
3488
3589
  ] }),
3489
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
3490
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
3491
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: data.queueHealthy ? "success" : "destructive", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
3590
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
3591
+ "Generated ",
3592
+ (0, import_react_ui29.formatDateTime)(data.generatedAt)
3492
3593
  ] })
3493
3594
  ] }),
3494
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("table", { className: "w-full text-sm", children: [
3495
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3496
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
3497
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Sends" }),
3498
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Delivered" }),
3499
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failed" })
3500
- ] }) }),
3501
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tbody", { children: channels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { colSpan: 4, className: "py-3 text-center text-muted-foreground", children: "No channel activity" }) }) : channels.map((row) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border", children: [
3502
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
3503
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.sends }),
3504
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.delivered }),
3505
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.failed })
3506
- ] }, row.channel)) })
3507
- ] }),
3508
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
3509
- "Generated ",
3510
- (0, import_react_ui28.formatDateTime)(data.generatedAt)
3595
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatCard, { label: card.label, value: card.value }, card.label)) }),
3596
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.CardContent, { className: "flex flex-wrap items-center gap-x-8 gap-y-3 p-5", children: [
3597
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
3598
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
3599
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: dlqPending > 0 ? "destructive" : "success", appearance: "solid", children: dlqPending })
3600
+ ] }),
3601
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
3602
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
3603
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm font-semibold tabular-nums text-foreground", children: data.broadcastsInFlight })
3604
+ ] }),
3605
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
3606
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
3607
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: data.queueHealthy ? "success" : "destructive", appearance: "solid", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
3608
+ ] })
3609
+ ] }) }),
3610
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.Card, { children: [
3611
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardTitle, { children: "Channel breakdown" }) }),
3612
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardContent, { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("table", { className: "w-full text-sm", children: [
3613
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { className: "border-b border-border text-start text-xs uppercase tracking-wide text-muted-foreground", children: [
3614
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-5 py-2.5 font-medium", children: "Channel" }),
3615
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Sends" }),
3616
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Delivered" }),
3617
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Failed" })
3618
+ ] }) }),
3619
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tbody", { children: channels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { colSpan: 4, className: "px-5 py-8 text-center text-muted-foreground", children: "No channel activity" }) }) : channels.map((row) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3620
+ "tr",
3621
+ {
3622
+ className: "border-b border-border last:border-0 hover:bg-muted/40",
3623
+ children: [
3624
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-5 py-3 font-medium text-foreground", children: row.channel }),
3625
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.sends }),
3626
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.delivered }),
3627
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.failed })
3628
+ ]
3629
+ },
3630
+ row.channel
3631
+ )) })
3632
+ ] }) })
3511
3633
  ] })
3512
3634
  ] });
3513
3635
  }
3514
3636
 
3515
3637
  // src/delivery-log-explorer.tsx
3516
3638
  var import_react18 = require("react");
3517
- var import_react_ui29 = require("@quanticjs/react-ui");
3639
+ var import_react_ui30 = require("@quanticjs/react-ui");
3518
3640
  var import_react_query26 = require("@quanticjs/react-query");
3519
- var import_jsx_runtime30 = require("react/jsx-runtime");
3641
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3520
3642
  var LIMIT6 = 20;
3521
3643
  var EMPTY_FILTERS = {
3522
3644
  channel: "",
@@ -3569,186 +3691,98 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
3569
3691
  });
3570
3692
  };
3571
3693
  const setField = (key, value) => setDraft((prev) => ({ ...prev, [key]: value }));
3572
- const filterForm = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("form", { onSubmit: onApply, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3573
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3574
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
3575
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
3576
- "select",
3577
- {
3578
- id: "dle-channel",
3579
- value: draft.channel,
3580
- onChange: (e) => setField("channel", e.target.value),
3581
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
3582
- children: [
3583
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "", children: "All" }),
3584
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "email", children: "Email" }),
3585
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "sms", children: "SMS" })
3586
- ]
3587
- }
3588
- )
3589
- ] }),
3590
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3591
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-status", className: "text-sm font-medium text-foreground", children: "Status" }),
3592
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3593
- "input",
3594
- {
3595
- id: "dle-status",
3596
- type: "text",
3597
- value: draft.status,
3598
- onChange: (e) => setField("status", e.target.value),
3599
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3600
- }
3601
- )
3602
- ] }),
3603
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3604
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-recipient", className: "text-sm font-medium text-foreground", children: "Recipient" }),
3605
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3606
- "input",
3607
- {
3608
- id: "dle-recipient",
3609
- type: "text",
3610
- value: draft.recipient,
3611
- onChange: (e) => setField("recipient", e.target.value),
3612
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3613
- }
3614
- )
3615
- ] }),
3616
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3617
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-user", className: "text-sm font-medium text-foreground", children: "User ID" }),
3618
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3619
- "input",
3620
- {
3621
- id: "dle-user",
3622
- type: "text",
3623
- value: draft.userId,
3624
- onChange: (e) => setField("userId", e.target.value),
3625
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3626
- }
3627
- )
3628
- ] }),
3629
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3630
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-from", className: "text-sm font-medium text-foreground", children: "From" }),
3631
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3632
- "input",
3633
- {
3634
- id: "dle-from",
3635
- type: "date",
3636
- value: draft.from,
3637
- onChange: (e) => setField("from", e.target.value),
3638
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3639
- }
3640
- )
3641
- ] }),
3642
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
3643
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-to", className: "text-sm font-medium text-foreground", children: "To" }),
3644
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3645
- "input",
3646
- {
3647
- id: "dle-to",
3648
- type: "date",
3649
- value: draft.to,
3650
- onChange: (e) => setField("to", e.target.value),
3651
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
3652
- }
3653
- )
3654
- ] }),
3655
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3656
- "button",
3657
- {
3658
- type: "submit",
3659
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
3660
- children: "Apply"
3661
- }
3662
- )
3663
- ] });
3664
- let body;
3665
- if (isLoading) {
3666
- body = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { role: "status", "aria-label": "Loading delivery logs", className: "flex flex-col gap-2 p-4", children: [
3667
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "sr-only", children: "Loading delivery logs" }),
3668
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3669
- ] });
3670
- } else if (isError) {
3671
- body = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3672
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load delivery logs" }),
3673
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3674
- "button",
3675
- {
3676
- type: "button",
3677
- onClick: () => void refetch(),
3678
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
3679
- children: "Try again"
3680
- }
3681
- )
3682
- ] });
3683
- } else {
3684
- const rows = data?.items ?? [];
3685
- const totalPages = data?.totalPages ?? 1;
3686
- if (rows.length === 0) {
3687
- body = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No delivery logs" });
3688
- } else {
3689
- body = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
3690
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("table", { className: "w-full text-sm", children: [
3691
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3692
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
3693
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipient" }),
3694
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "User" }),
3695
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3696
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Provider" }),
3697
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
3698
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3694
+ const rows = data?.items ?? [];
3695
+ const totalPages = data?.totalPages ?? 1;
3696
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("section", { "aria-label": "Delivery log explorer", className: (0, import_react_ui30.cn)("flex flex-col gap-4", className), children: [
3697
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_ui30.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("form", { onSubmit: onApply, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
3698
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-channel", className: "flex flex-col gap-1 text-sm", children: [
3699
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Channel" }),
3700
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
3701
+ "select",
3702
+ {
3703
+ id: "dle-channel",
3704
+ value: draft.channel,
3705
+ onChange: (e) => setField("channel", e.target.value),
3706
+ className: FIELD_CLASS,
3707
+ children: [
3708
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "", children: "All" }),
3709
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "email", children: "Email" }),
3710
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "sms", children: "SMS" })
3711
+ ]
3712
+ }
3713
+ )
3714
+ ] }),
3715
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-status", className: "flex flex-col gap-1 text-sm", children: [
3716
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Status" }),
3717
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { id: "dle-status", type: "text", value: draft.status, onChange: (e) => setField("status", e.target.value), className: FIELD_CLASS })
3718
+ ] }),
3719
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-recipient", className: "flex flex-col gap-1 text-sm", children: [
3720
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Recipient" }),
3721
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { id: "dle-recipient", type: "text", value: draft.recipient, onChange: (e) => setField("recipient", e.target.value), className: FIELD_CLASS })
3722
+ ] }),
3723
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-user", className: "flex flex-col gap-1 text-sm", children: [
3724
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "User ID" }),
3725
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { id: "dle-user", type: "text", value: draft.userId, onChange: (e) => setField("userId", e.target.value), className: FIELD_CLASS })
3726
+ ] }),
3727
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-from", className: "flex flex-col gap-1 text-sm", children: [
3728
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "From" }),
3729
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { id: "dle-from", type: "date", value: draft.from, onChange: (e) => setField("from", e.target.value), className: FIELD_CLASS })
3730
+ ] }),
3731
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-to", className: "flex flex-col gap-1 text-sm", children: [
3732
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "To" }),
3733
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("input", { id: "dle-to", type: "date", value: draft.to, onChange: (e) => setField("to", e.target.value), className: FIELD_CLASS })
3734
+ ] }),
3735
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_ui30.Button, { type: "submit", className: "ms-auto", children: "Apply" })
3736
+ ] }) }),
3737
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_ui30.Card, { children: [
3738
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PanelHeader, { title: "Attempts", subtitle: !isLoading && !isError ? `${data?.total ?? rows.length} matching entries` : void 0 }),
3739
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SkeletonRows, { label: "Loading delivery logs" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ErrorPanel, { message: "Failed to load delivery logs", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_ui30.EmptyState, { icon: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(InboxIcon, {}), title: "No delivery logs", description: "No attempts match the current filters." }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
3740
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("table", { className: "w-full text-sm", children: [
3741
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3742
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channel" }),
3743
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipient" }),
3744
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "User" }),
3745
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3746
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Provider" }),
3747
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
3748
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3699
3749
  ] }) }),
3700
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { className: "border-b border-border", children: [
3701
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "py-3 pe-4", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: channelVariant(row.channel), appearance: "dot", children: row.channel }) }),
3702
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
3703
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
3704
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.status }),
3705
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
3706
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.attempts }),
3707
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui29.formatDateTime)(row.createdAt) })
3750
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
3751
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_ui30.StatusBadge, { variant: channelVariant(row.channel), appearance: "dot", children: row.channel }) }),
3752
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
3753
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
3754
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.status }),
3755
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
3756
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 tabular-nums text-muted-foreground", children: row.attempts }),
3757
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui30.formatDateTime)(row.createdAt) })
3708
3758
  ] }, row.id)) })
3709
3759
  ] }),
3710
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("nav", { "aria-label": "Delivery log pagination", className: "flex items-center justify-between", children: [
3711
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3712
- "button",
3713
- {
3714
- type: "button",
3715
- onClick: () => setPage((p) => Math.max(1, p - 1)),
3716
- disabled: page <= 1,
3717
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
3718
- children: "Previous"
3719
- }
3720
- ),
3721
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
3722
- "Page ",
3760
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3761
+ Pager,
3762
+ {
3763
+ label: "Delivery log pagination",
3723
3764
  page,
3724
- " of ",
3725
- totalPages
3726
- ] }),
3727
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3728
- "button",
3729
- {
3730
- type: "button",
3731
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
3732
- disabled: page >= totalPages,
3733
- className: "rounded-md border border-border px-3 py-1 text-sm text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
3734
- children: "Next"
3735
- }
3736
- )
3737
- ] })
3738
- ] });
3739
- }
3740
- }
3741
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { "aria-label": "Delivery log explorer", className: (0, import_react_ui29.cn)("flex flex-col gap-4", className), children: [
3742
- filterForm,
3743
- body
3765
+ totalPages,
3766
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
3767
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
3768
+ }
3769
+ )
3770
+ ] })
3771
+ ] })
3772
+ ] });
3773
+ }
3774
+ function InboxIcon() {
3775
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
3776
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }),
3777
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11Z" })
3744
3778
  ] });
3745
3779
  }
3746
3780
 
3747
3781
  // src/quiet-hours-form.tsx
3748
3782
  var import_react19 = require("react");
3749
- var import_react_ui30 = require("@quanticjs/react-ui");
3783
+ var import_react_ui31 = require("@quanticjs/react-ui");
3750
3784
  var import_react_query27 = require("@quanticjs/react-query");
3751
- var import_jsx_runtime31 = require("react/jsx-runtime");
3785
+ var import_jsx_runtime32 = require("react/jsx-runtime");
3752
3786
  var DEFAULTS = {
3753
3787
  enabled: false,
3754
3788
  start: "22:00",
@@ -3765,7 +3799,7 @@ function normalize5(raw) {
3765
3799
  };
3766
3800
  }
3767
3801
  function QuietHoursForm({ basePath = "/api", className }) {
3768
- const toast = (0, import_react_ui30.useToast)();
3802
+ const toast = (0, import_react_ui31.useToast)();
3769
3803
  const url = `${basePath}/notifications/config/quiet-hours`;
3770
3804
  const { data, isLoading, isError, refetch } = (0, import_react_query27.useApiQuery)(
3771
3805
  ["quiet-hours"],
@@ -3789,23 +3823,23 @@ function QuietHoursForm({ basePath = "/api", className }) {
3789
3823
  save.mutate(form);
3790
3824
  };
3791
3825
  if (isLoading) {
3792
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
3826
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
3793
3827
  "div",
3794
3828
  {
3795
3829
  role: "status",
3796
3830
  "aria-label": "Loading quiet hours",
3797
- className: (0, import_react_ui30.cn)("flex flex-col gap-2 p-4", className),
3831
+ className: (0, import_react_ui31.cn)("flex flex-col gap-2 p-4", className),
3798
3832
  children: [
3799
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "sr-only", children: "Loading quiet hours" }),
3800
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3833
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Loading quiet hours" }),
3834
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3801
3835
  ]
3802
3836
  }
3803
3837
  );
3804
3838
  }
3805
3839
  if (isError) {
3806
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: (0, import_react_ui30.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3807
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
3808
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3840
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: (0, import_react_ui31.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3841
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
3842
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3809
3843
  "button",
3810
3844
  {
3811
3845
  type: "button",
@@ -3816,10 +3850,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
3816
3850
  )
3817
3851
  ] });
3818
3852
  }
3819
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("form", { onSubmit, className: (0, import_react_ui30.cn)("flex flex-col gap-4", className), noValidate: true, children: [
3820
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
3821
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
3822
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3853
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("form", { onSubmit, className: (0, import_react_ui31.cn)("flex flex-col gap-4", className), noValidate: true, children: [
3854
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
3855
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
3856
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3823
3857
  "input",
3824
3858
  {
3825
3859
  type: "checkbox",
@@ -3830,10 +3864,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
3830
3864
  ),
3831
3865
  "Enable quiet hours"
3832
3866
  ] }),
3833
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
3834
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
3835
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
3836
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3867
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
3868
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
3869
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
3870
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3837
3871
  "input",
3838
3872
  {
3839
3873
  id: "qh-start",
@@ -3844,9 +3878,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
3844
3878
  }
3845
3879
  )
3846
3880
  ] }),
3847
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
3848
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
3849
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3881
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
3882
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
3883
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3850
3884
  "input",
3851
3885
  {
3852
3886
  id: "qh-end",
@@ -3857,9 +3891,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
3857
3891
  }
3858
3892
  )
3859
3893
  ] }),
3860
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
3861
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
3862
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3894
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
3895
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
3896
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3863
3897
  "input",
3864
3898
  {
3865
3899
  id: "qh-tz",
@@ -3871,7 +3905,7 @@ function QuietHoursForm({ basePath = "/api", className }) {
3871
3905
  )
3872
3906
  ] })
3873
3907
  ] }),
3874
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3908
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3875
3909
  "button",
3876
3910
  {
3877
3911
  type: "submit",
@@ -3885,9 +3919,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
3885
3919
 
3886
3920
  // src/frequency-cap-table.tsx
3887
3921
  var import_react20 = require("react");
3888
- var import_react_ui31 = require("@quanticjs/react-ui");
3922
+ var import_react_ui32 = require("@quanticjs/react-ui");
3889
3923
  var import_react_query28 = require("@quanticjs/react-query");
3890
- var import_jsx_runtime32 = require("react/jsx-runtime");
3924
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3891
3925
  function normalize6(raw) {
3892
3926
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.caps) ? raw.caps : [];
3893
3927
  return list.map((entry) => {
@@ -3897,7 +3931,7 @@ function normalize6(raw) {
3897
3931
  });
3898
3932
  }
3899
3933
  function FrequencyCapTable({ basePath = "/api", className }) {
3900
- const toast = (0, import_react_ui31.useToast)();
3934
+ const toast = (0, import_react_ui32.useToast)();
3901
3935
  const url = `${basePath}/notifications/config/frequency-cap`;
3902
3936
  const { data, isLoading, isError, refetch } = (0, import_react_query28.useApiQuery)(
3903
3937
  ["frequency-cap"],
@@ -3932,23 +3966,23 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3932
3966
  setNewMax(0);
3933
3967
  };
3934
3968
  if (isLoading) {
3935
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
3969
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
3936
3970
  "div",
3937
3971
  {
3938
3972
  role: "status",
3939
3973
  "aria-label": "Loading frequency caps",
3940
- className: (0, import_react_ui31.cn)("flex flex-col gap-2 p-4", className),
3974
+ className: (0, import_react_ui32.cn)("flex flex-col gap-2 p-4", className),
3941
3975
  children: [
3942
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Loading frequency caps" }),
3943
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3976
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "sr-only", children: "Loading frequency caps" }),
3977
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3944
3978
  ]
3945
3979
  }
3946
3980
  );
3947
3981
  }
3948
3982
  if (isError) {
3949
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: (0, import_react_ui31.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3950
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
3951
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3983
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: (0, import_react_ui32.cn)("flex flex-col items-start gap-3 p-4", className), children: [
3984
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
3985
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3952
3986
  "button",
3953
3987
  {
3954
3988
  type: "button",
@@ -3959,24 +3993,24 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3959
3993
  )
3960
3994
  ] });
3961
3995
  }
3962
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("section", { "aria-label": "Frequency caps", className: (0, import_react_ui31.cn)("flex flex-col gap-4", className), children: [
3963
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
3964
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("table", { className: "w-full text-sm", children: [
3965
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3966
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
3967
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
3968
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Actions" }) })
3996
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { "aria-label": "Frequency caps", className: (0, import_react_ui32.cn)("flex flex-col gap-4", className), children: [
3997
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
3998
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("table", { className: "w-full text-sm", children: [
3999
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4000
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
4001
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
4002
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "sr-only", children: "Actions" }) })
3969
4003
  ] }) }),
3970
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("tbody", { children: caps.map((cap, index) => {
4004
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("tbody", { children: caps.map((cap, index) => {
3971
4005
  const inputId = `cap-${index}`;
3972
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: "border-b border-border", children: [
3973
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
3974
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("td", { className: "px-4 py-3", children: [
3975
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { htmlFor: inputId, className: "sr-only", children: [
4006
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("tr", { className: "border-b border-border", children: [
4007
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
4008
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("td", { className: "px-4 py-3", children: [
4009
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("label", { htmlFor: inputId, className: "sr-only", children: [
3976
4010
  "Max per day for ",
3977
4011
  cap.type
3978
4012
  ] }),
3979
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4013
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3980
4014
  "input",
3981
4015
  {
3982
4016
  id: inputId,
@@ -3988,7 +4022,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3988
4022
  }
3989
4023
  )
3990
4024
  ] }),
3991
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4025
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3992
4026
  "button",
3993
4027
  {
3994
4028
  type: "button",
@@ -4000,10 +4034,10 @@ function FrequencyCapTable({ basePath = "/api", className }) {
4000
4034
  ] }, `${cap.type}-${index}`);
4001
4035
  }) })
4002
4036
  ] }),
4003
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4004
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
4005
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
4006
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4037
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4038
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
4039
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
4040
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4007
4041
  "input",
4008
4042
  {
4009
4043
  id: "cap-new-type",
@@ -4014,9 +4048,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
4014
4048
  }
4015
4049
  )
4016
4050
  ] }),
4017
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
4018
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
4019
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4051
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
4052
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
4053
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4020
4054
  "input",
4021
4055
  {
4022
4056
  id: "cap-new-max",
@@ -4028,7 +4062,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
4028
4062
  }
4029
4063
  )
4030
4064
  ] }),
4031
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4065
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4032
4066
  "button",
4033
4067
  {
4034
4068
  type: "submit",
@@ -4037,7 +4071,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
4037
4071
  }
4038
4072
  )
4039
4073
  ] }),
4040
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4074
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4041
4075
  "button",
4042
4076
  {
4043
4077
  type: "button",
@@ -4052,9 +4086,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
4052
4086
 
4053
4087
  // src/tenant-config-form.tsx
4054
4088
  var import_react21 = require("react");
4055
- var import_react_ui32 = require("@quanticjs/react-ui");
4089
+ var import_react_ui33 = require("@quanticjs/react-ui");
4056
4090
  var import_react_query29 = require("@quanticjs/react-query");
4057
- var import_jsx_runtime33 = require("react/jsx-runtime");
4091
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4058
4092
  function normalize7(raw) {
4059
4093
  const obj = raw ?? {};
4060
4094
  const toList = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
@@ -4072,15 +4106,15 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4072
4106
  onAdd(value);
4073
4107
  setDraft("");
4074
4108
  };
4075
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-2", children: [
4076
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-sm font-medium text-foreground", children: label }),
4077
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "flex flex-wrap gap-2", children: values.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: "text-sm text-muted-foreground", children: "None" }) : values.map((value) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4109
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col gap-2", children: [
4110
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-sm font-medium text-foreground", children: label }),
4111
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-2", children: values.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { className: "text-sm text-muted-foreground", children: "None" }) : values.map((value) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
4078
4112
  "li",
4079
4113
  {
4080
4114
  className: "flex items-center gap-1 rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground",
4081
4115
  children: [
4082
4116
  value,
4083
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4117
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4084
4118
  "button",
4085
4119
  {
4086
4120
  type: "button",
@@ -4094,13 +4128,13 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4094
4128
  },
4095
4129
  value
4096
4130
  )) }),
4097
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
4098
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
4099
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("label", { htmlFor: id, className: "sr-only", children: [
4131
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
4132
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col gap-1", children: [
4133
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { htmlFor: id, className: "sr-only", children: [
4100
4134
  "Add to ",
4101
4135
  label
4102
4136
  ] }),
4103
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4137
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4104
4138
  "input",
4105
4139
  {
4106
4140
  id,
@@ -4111,7 +4145,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4111
4145
  }
4112
4146
  )
4113
4147
  ] }),
4114
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4148
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4115
4149
  "button",
4116
4150
  {
4117
4151
  type: "submit",
@@ -4123,7 +4157,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4123
4157
  ] });
4124
4158
  }
4125
4159
  function TenantConfigForm({ basePath = "/api", className }) {
4126
- const toast = (0, import_react_ui32.useToast)();
4160
+ const toast = (0, import_react_ui33.useToast)();
4127
4161
  const url = `${basePath}/admin/notification-config`;
4128
4162
  const { data, isLoading, isError, refetch } = (0, import_react_query29.useApiQuery)(
4129
4163
  ["tenant-config"],
@@ -4176,23 +4210,23 @@ function TenantConfigForm({ basePath = "/api", className }) {
4176
4210
  save.mutate(config);
4177
4211
  };
4178
4212
  if (isLoading) {
4179
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4213
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
4180
4214
  "div",
4181
4215
  {
4182
4216
  role: "status",
4183
4217
  "aria-label": "Loading tenant configuration",
4184
- className: (0, import_react_ui32.cn)("flex flex-col gap-2 p-4", className),
4218
+ className: (0, import_react_ui33.cn)("flex flex-col gap-2 p-4", className),
4185
4219
  children: [
4186
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "sr-only", children: "Loading tenant configuration" }),
4187
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4220
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "sr-only", children: "Loading tenant configuration" }),
4221
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4188
4222
  ]
4189
4223
  }
4190
4224
  );
4191
4225
  }
4192
4226
  if (isError) {
4193
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: (0, import_react_ui32.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4194
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
4195
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4227
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: (0, import_react_ui33.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4228
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
4229
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4196
4230
  "button",
4197
4231
  {
4198
4232
  type: "button",
@@ -4204,17 +4238,17 @@ function TenantConfigForm({ basePath = "/api", className }) {
4204
4238
  ] });
4205
4239
  }
4206
4240
  const errorId = "tenant-config-subset-error";
4207
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4241
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
4208
4242
  "form",
4209
4243
  {
4210
4244
  onSubmit,
4211
- className: (0, import_react_ui32.cn)("flex flex-col gap-5", className),
4245
+ className: (0, import_react_ui33.cn)("flex flex-col gap-5", className),
4212
4246
  noValidate: true,
4213
4247
  "aria-invalid": subsetError ? "true" : void 0,
4214
4248
  "aria-describedby": subsetError ? errorId : void 0,
4215
4249
  children: [
4216
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
4217
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4250
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
4251
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4218
4252
  TagEditor,
4219
4253
  {
4220
4254
  id: "tc-notification-types",
@@ -4224,7 +4258,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
4224
4258
  onRemove: removeType
4225
4259
  }
4226
4260
  ),
4227
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4261
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4228
4262
  TagEditor,
4229
4263
  {
4230
4264
  id: "tc-immediate-email-types",
@@ -4234,8 +4268,8 @@ function TenantConfigForm({ basePath = "/api", className }) {
4234
4268
  onRemove: removeImmediate
4235
4269
  }
4236
4270
  ),
4237
- subsetError && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
4238
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4271
+ subsetError && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
4272
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4239
4273
  "button",
4240
4274
  {
4241
4275
  type: "submit",
@@ -4251,9 +4285,9 @@ function TenantConfigForm({ basePath = "/api", className }) {
4251
4285
 
4252
4286
  // src/tracking-config-form.tsx
4253
4287
  var import_react22 = require("react");
4254
- var import_react_ui33 = require("@quanticjs/react-ui");
4288
+ var import_react_ui34 = require("@quanticjs/react-ui");
4255
4289
  var import_react_query30 = require("@quanticjs/react-query");
4256
- var import_jsx_runtime34 = require("react/jsx-runtime");
4290
+ var import_jsx_runtime35 = require("react/jsx-runtime");
4257
4291
  function normalize8(raw) {
4258
4292
  const obj = raw ?? {};
4259
4293
  const bool = (...keys) => {
@@ -4268,7 +4302,7 @@ function normalize8(raw) {
4268
4302
  };
4269
4303
  }
4270
4304
  function TrackingConfigForm({ basePath = "/api", className }) {
4271
- const toast = (0, import_react_ui33.useToast)();
4305
+ const toast = (0, import_react_ui34.useToast)();
4272
4306
  const url = `${basePath}/analytics/notifications/tracking-config`;
4273
4307
  const { data, isLoading, isError, refetch } = (0, import_react_query30.useApiQuery)(
4274
4308
  ["tracking-config"],
@@ -4298,23 +4332,23 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4298
4332
  save.mutate(form);
4299
4333
  };
4300
4334
  if (isLoading) {
4301
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
4335
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
4302
4336
  "div",
4303
4337
  {
4304
4338
  role: "status",
4305
4339
  "aria-label": "Loading tracking configuration",
4306
- className: (0, import_react_ui33.cn)("flex flex-col gap-2 p-4", className),
4340
+ className: (0, import_react_ui34.cn)("flex flex-col gap-2 p-4", className),
4307
4341
  children: [
4308
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "sr-only", children: "Loading tracking configuration" }),
4309
- [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4342
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "sr-only", children: "Loading tracking configuration" }),
4343
+ [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4310
4344
  ]
4311
4345
  }
4312
4346
  );
4313
4347
  }
4314
4348
  if (isError) {
4315
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: (0, import_react_ui33.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4316
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
4317
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4349
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: (0, import_react_ui34.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4350
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
4351
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4318
4352
  "button",
4319
4353
  {
4320
4354
  type: "button",
@@ -4325,10 +4359,10 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4325
4359
  )
4326
4360
  ] });
4327
4361
  }
4328
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("form", { onSubmit, className: (0, import_react_ui33.cn)("flex flex-col gap-4", className), noValidate: true, children: [
4329
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
4330
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4331
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4362
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("form", { onSubmit, className: (0, import_react_ui34.cn)("flex flex-col gap-4", className), noValidate: true, children: [
4363
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
4364
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4365
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4332
4366
  "input",
4333
4367
  {
4334
4368
  type: "checkbox",
@@ -4339,8 +4373,8 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4339
4373
  ),
4340
4374
  "Open tracking"
4341
4375
  ] }),
4342
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4343
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4376
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4377
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4344
4378
  "input",
4345
4379
  {
4346
4380
  type: "checkbox",
@@ -4351,7 +4385,7 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4351
4385
  ),
4352
4386
  "Click tracking"
4353
4387
  ] }),
4354
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4388
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4355
4389
  "button",
4356
4390
  {
4357
4391
  type: "submit",
@@ -4365,15 +4399,15 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4365
4399
 
4366
4400
  // src/api-key-manager.tsx
4367
4401
  var import_react23 = require("react");
4368
- var import_react_ui34 = require("@quanticjs/react-ui");
4402
+ var import_react_ui35 = require("@quanticjs/react-ui");
4369
4403
  var import_react_query31 = require("@quanticjs/react-query");
4370
- var import_jsx_runtime35 = require("react/jsx-runtime");
4404
+ var import_jsx_runtime36 = require("react/jsx-runtime");
4371
4405
  function normalize9(raw) {
4372
4406
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
4373
4407
  return list;
4374
4408
  }
4375
4409
  function ApiKeyManager({ basePath = "/api", className }) {
4376
- const toast = (0, import_react_ui34.useToast)();
4410
+ const toast = (0, import_react_ui35.useToast)();
4377
4411
  const url = `${basePath}/v1/admin/api-keys`;
4378
4412
  const { data, isLoading, isError, refetch } = (0, import_react_query31.useApiQuery)(
4379
4413
  ["api-keys"],
@@ -4425,110 +4459,77 @@ function ApiKeyManager({ basePath = "/api", className }) {
4425
4459
  revoke.mutate(id);
4426
4460
  }
4427
4461
  };
4428
- const createForm = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("form", { onSubmit: onCreate, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4429
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-1", children: [
4430
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("label", { htmlFor: "api-key-name", className: "text-sm font-medium text-foreground", children: "New key name" }),
4431
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4432
- "input",
4433
- {
4434
- id: "api-key-name",
4435
- type: "text",
4436
- value: name,
4437
- onChange: (e) => setName(e.target.value),
4438
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
4439
- }
4440
- )
4441
- ] }),
4442
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-1", children: [
4443
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("label", { htmlFor: "api-key-app", className: "text-sm font-medium text-foreground", children: "Application (optional)" }),
4444
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4445
- "input",
4446
- {
4447
- id: "api-key-app",
4448
- type: "text",
4449
- value: applicationKey,
4450
- onChange: (e) => setApplicationKey(e.target.value),
4451
- placeholder: "delivery-hub",
4452
- className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
4453
- }
4454
- )
4455
- ] }),
4456
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4457
- "button",
4458
- {
4459
- type: "submit",
4460
- disabled: create.isPending,
4461
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
4462
- children: create.isPending ? "Creating\u2026" : "Create key"
4463
- }
4464
- )
4465
- ] });
4466
- let body;
4467
- if (isLoading) {
4468
- body = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { role: "status", "aria-label": "Loading API keys", className: "flex flex-col gap-2 p-4", children: [
4469
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "sr-only", children: "Loading API keys" }),
4470
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4471
- ] });
4472
- } else if (isError) {
4473
- body = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
4474
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load API keys" }),
4475
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4476
- "button",
4477
- {
4478
- type: "button",
4479
- onClick: () => void refetch(),
4480
- className: "rounded bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
4481
- children: "Try again"
4482
- }
4483
- )
4484
- ] });
4485
- } else {
4486
- const rows = normalize9(data);
4487
- if (rows.length === 0) {
4488
- body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No API keys" });
4489
- } else {
4490
- body = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("table", { className: "w-full text-sm", children: [
4491
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4492
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Name" }),
4493
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Prefix" }),
4494
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Application" }),
4495
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
4496
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
4497
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last used" }),
4498
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "sr-only", children: "Actions" }) })
4462
+ const rows = normalize9(data);
4463
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { "aria-label": "API key management", className: (0, import_react_ui35.cn)("flex flex-col gap-4", className), children: [
4464
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("form", { onSubmit: onCreate, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
4465
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
4466
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-foreground", children: "New key name" }),
4467
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), className: FIELD_CLASS })
4468
+ ] }),
4469
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
4470
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-foreground", children: "Application (optional)" }),
4471
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4472
+ "input",
4473
+ {
4474
+ type: "text",
4475
+ value: applicationKey,
4476
+ onChange: (e) => setApplicationKey(e.target.value),
4477
+ placeholder: "delivery-hub",
4478
+ className: FIELD_CLASS
4479
+ }
4480
+ )
4481
+ ] }),
4482
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.Button, { type: "submit", disabled: create.isPending, children: create.isPending ? "Creating\u2026" : "Create key" })
4483
+ ] }) }),
4484
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_ui35.Card, { children: [
4485
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(PanelHeader, { title: "Keys", subtitle: !isLoading && !isError ? `${rows.length} keys` : void 0 }),
4486
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SkeletonRows, { label: "Loading API keys" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ErrorPanel, { message: "Failed to load API keys", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.EmptyState, { icon: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(KeyIcon, {}), title: "No API keys", description: "Create a key to get started." }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "w-full text-sm", children: [
4487
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4488
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Name" }),
4489
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Prefix" }),
4490
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Application" }),
4491
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
4492
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
4493
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last used" }),
4494
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "sr-only", children: "Actions" }) })
4499
4495
  ] }) }),
4500
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("tr", { className: "border-b border-border", children: [
4501
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.name }),
4502
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
4503
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
4504
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_ui34.StatusBadge, { variant: row.revoked ? "destructive" : "success", children: row.revoked ? "Revoked" : "Active" }) }),
4505
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui34.formatDateTime)(row.createdAt) }),
4506
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastUsedAt ? (0, import_react_ui34.formatDateTime)(row.lastUsedAt) : "\u2014" }),
4507
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4508
- "button",
4496
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
4497
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.name }),
4498
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
4499
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
4500
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.StatusBadge, { variant: row.revoked ? "destructive" : "success", children: row.revoked ? "Revoked" : "Active" }) }),
4501
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui35.formatDateTime)(row.createdAt) }),
4502
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastUsedAt ? (0, import_react_ui35.formatDateTime)(row.lastUsedAt) : "\u2014" }),
4503
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-end", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4504
+ import_react_ui35.Button,
4509
4505
  {
4510
4506
  type: "button",
4507
+ variant: "ghost",
4508
+ size: "sm",
4509
+ className: "text-destructive",
4511
4510
  disabled: revoke.isPending || row.revoked,
4512
4511
  onClick: () => onRevoke(row.id),
4513
- className: "rounded-md border border-border px-3 py-1 text-sm font-medium text-destructive hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
4514
4512
  children: "Revoke"
4515
4513
  }
4516
4514
  ) })
4517
4515
  ] }, row.id)) })
4518
- ] });
4519
- }
4520
- }
4521
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { "aria-label": "API key management", className: (0, import_react_ui34.cn)("flex flex-col gap-4", className), children: [
4522
- createForm,
4523
- body
4516
+ ] })
4517
+ ] })
4518
+ ] });
4519
+ }
4520
+ function KeyIcon() {
4521
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4522
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4" }),
4523
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "m21 2-9.6 9.6" }),
4524
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: "7.5", cy: "15.5", r: "5.5" })
4524
4525
  ] });
4525
4526
  }
4526
4527
 
4527
4528
  // src/application-registry-panel.tsx
4528
4529
  var import_react24 = require("react");
4529
- var import_react_ui35 = require("@quanticjs/react-ui");
4530
+ var import_react_ui36 = require("@quanticjs/react-ui");
4530
4531
  var import_react_query32 = require("@quanticjs/react-query");
4531
- var import_jsx_runtime36 = require("react/jsx-runtime");
4532
+ var import_jsx_runtime37 = require("react/jsx-runtime");
4532
4533
  function normalize10(raw) {
4533
4534
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
4534
4535
  return list;
@@ -4537,7 +4538,7 @@ function ApplicationRegistryPanel({
4537
4538
  basePath = "/api",
4538
4539
  className
4539
4540
  }) {
4540
- const toast = (0, import_react_ui35.useToast)();
4541
+ const toast = (0, import_react_ui36.useToast)();
4541
4542
  const url = `${basePath}/admin/applications`;
4542
4543
  const { data, isLoading, isError, refetch } = (0, import_react_query32.useApiQuery)(
4543
4544
  ["applications"],
@@ -4574,10 +4575,10 @@ function ApplicationRegistryPanel({
4574
4575
  if (!k || !name) return;
4575
4576
  register.mutate({ key: k, displayName: name, description: description.trim() || void 0 });
4576
4577
  };
4577
- const registerForm = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4578
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
4579
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
4580
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4578
+ const registerForm = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4579
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
4580
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
4581
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4581
4582
  "input",
4582
4583
  {
4583
4584
  id: "application-key",
@@ -4589,9 +4590,9 @@ function ApplicationRegistryPanel({
4589
4590
  }
4590
4591
  )
4591
4592
  ] }),
4592
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
4593
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
4594
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4593
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
4594
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
4595
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4595
4596
  "input",
4596
4597
  {
4597
4598
  id: "application-name",
@@ -4603,9 +4604,9 @@ function ApplicationRegistryPanel({
4603
4604
  }
4604
4605
  )
4605
4606
  ] }),
4606
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
4607
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
4608
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4607
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
4608
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
4609
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4609
4610
  "input",
4610
4611
  {
4611
4612
  id: "application-description",
@@ -4616,7 +4617,7 @@ function ApplicationRegistryPanel({
4616
4617
  }
4617
4618
  )
4618
4619
  ] }),
4619
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4620
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4620
4621
  "button",
4621
4622
  {
4622
4623
  type: "submit",
@@ -4627,23 +4628,23 @@ function ApplicationRegistryPanel({
4627
4628
  )
4628
4629
  ] });
4629
4630
  if (isLoading) {
4630
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
4631
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
4631
4632
  "div",
4632
4633
  {
4633
4634
  role: "status",
4634
4635
  "aria-label": "Loading applications",
4635
- className: (0, import_react_ui35.cn)("flex flex-col gap-2 p-4", className),
4636
+ className: (0, import_react_ui36.cn)("flex flex-col gap-2 p-4", className),
4636
4637
  children: [
4637
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "sr-only", children: "Loading applications" }),
4638
- [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4638
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sr-only", children: "Loading applications" }),
4639
+ [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4639
4640
  ]
4640
4641
  }
4641
4642
  );
4642
4643
  }
4643
4644
  if (isError) {
4644
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: (0, import_react_ui35.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4645
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
4646
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4645
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: (0, import_react_ui36.cn)("flex flex-col items-start gap-3 p-4", className), children: [
4646
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
4647
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4647
4648
  "button",
4648
4649
  {
4649
4650
  type: "button",
@@ -4655,22 +4656,22 @@ function ApplicationRegistryPanel({
4655
4656
  ] });
4656
4657
  }
4657
4658
  const apps = normalize10(data);
4658
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { "aria-label": "Applications", className: (0, import_react_ui35.cn)("flex flex-col gap-4 p-4", className), children: [
4659
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("section", { "aria-label": "Applications", className: (0, import_react_ui36.cn)("flex flex-col gap-4 p-4", className), children: [
4659
4660
  registerForm,
4660
- apps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No applications registered" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "w-full text-sm", children: [
4661
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4662
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Key" }),
4663
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Name" }),
4664
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Status" }),
4665
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Created" }),
4666
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Actions" })
4661
+ apps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No applications registered" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("table", { className: "w-full text-sm", children: [
4662
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4663
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Key" }),
4664
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Name" }),
4665
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Status" }),
4666
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Created" }),
4667
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Actions" })
4667
4668
  ] }) }),
4668
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border", children: [
4669
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 font-mono text-foreground", children: app.key }),
4670
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 text-foreground", children: app.displayName }),
4671
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.StatusBadge, { variant: app.status === "active" ? "success" : "neutral", children: app.status }) }),
4672
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 text-muted-foreground", children: (0, import_react_ui35.formatDateTime)(app.createdAt) }),
4673
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4669
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("tbody", { children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("tr", { className: "border-b border-border", children: [
4670
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 font-mono text-foreground", children: app.key }),
4671
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 text-foreground", children: app.displayName }),
4672
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_ui36.StatusBadge, { variant: app.status === "active" ? "success" : "neutral", children: app.status }) }),
4673
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 text-muted-foreground", children: (0, import_react_ui36.formatDateTime)(app.createdAt) }),
4674
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4674
4675
  "button",
4675
4676
  {
4676
4677
  type: "button",