@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.js CHANGED
@@ -363,7 +363,7 @@ function NotificationBell({
363
363
  "span",
364
364
  {
365
365
  "aria-hidden": "true",
366
- 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",
366
+ 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",
367
367
  children: count > 99 ? "99+" : count
368
368
  }
369
369
  )
@@ -373,7 +373,7 @@ function NotificationBell({
373
373
  }
374
374
 
375
375
  // src/notification-inbox.tsx
376
- import { cn as cn3 } from "@quanticjs/react-ui";
376
+ import { Button, Card, EmptyState, Skeleton, cn as cn3 } from "@quanticjs/react-ui";
377
377
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
378
378
  function NotificationInbox({
379
379
  basePath,
@@ -387,54 +387,80 @@ function NotificationInbox({
387
387
  appId
388
388
  });
389
389
  if (isLoading) {
390
- return /* @__PURE__ */ jsxs3(
391
- "div",
392
- {
393
- role: "status",
394
- "aria-label": "Loading notifications",
395
- className: cn3("flex flex-col gap-2 p-4", className),
396
- children: [
397
- /* @__PURE__ */ jsx5("span", { className: "sr-only", children: "Loading notifications" }),
398
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx5("div", { "aria-hidden": "true", className: "h-12 animate-pulse rounded bg-muted" }, i))
399
- ]
400
- }
401
- );
390
+ return /* @__PURE__ */ jsxs3("div", { role: "status", "aria-label": "Loading notifications", className: cn3(className), children: [
391
+ /* @__PURE__ */ jsx5("span", { className: "sr-only", children: "Loading notifications" }),
392
+ /* @__PURE__ */ jsxs3(Card, { "aria-hidden": "true", children: [
393
+ /* @__PURE__ */ jsx5(InboxHeader, {}),
394
+ /* @__PURE__ */ jsx5("ul", { className: "divide-y divide-border", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs3("li", { className: "flex items-start gap-3 px-4 py-3.5", children: [
395
+ /* @__PURE__ */ jsx5(Skeleton, { className: "mt-1 h-2 w-2 rounded-full" }),
396
+ /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
397
+ /* @__PURE__ */ jsx5(Skeleton, { className: "h-3.5 w-2/3" }),
398
+ /* @__PURE__ */ jsx5(Skeleton, { className: "h-3 w-1/2" })
399
+ ] })
400
+ ] }, i)) })
401
+ ] })
402
+ ] });
402
403
  }
403
404
  if (isError) {
404
- return /* @__PURE__ */ jsxs3("div", { className: cn3("flex flex-col items-start gap-3 p-4", className), children: [
405
- /* @__PURE__ */ jsx5("p", { className: "text-sm text-foreground", children: "Failed to load notifications" }),
406
- /* @__PURE__ */ jsx5(
407
- "button",
408
- {
409
- type: "button",
410
- onClick: () => void refetch(),
411
- 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",
412
- children: "Try again"
413
- }
414
- )
405
+ return /* @__PURE__ */ jsxs3(Card, { className, children: [
406
+ /* @__PURE__ */ jsx5(InboxHeader, {}),
407
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
408
+ /* @__PURE__ */ jsx5(
409
+ "span",
410
+ {
411
+ "aria-hidden": "true",
412
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
413
+ children: /* @__PURE__ */ jsx5(AlertIcon, {})
414
+ }
415
+ ),
416
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
417
+ /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium text-foreground", children: "Failed to load notifications" }),
418
+ /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground", children: "Something went wrong while loading your inbox." })
419
+ ] }),
420
+ /* @__PURE__ */ jsx5(Button, { type: "button", onClick: () => void refetch(), children: "Try again" })
421
+ ] })
415
422
  ] });
416
423
  }
417
424
  const items = data?.items ?? [];
418
425
  if (items.length === 0) {
419
- return /* @__PURE__ */ jsx5("div", { className: cn3("p-6 text-center text-sm text-muted-foreground", className), children: "No notifications yet" });
420
- }
421
- return /* @__PURE__ */ jsxs3("section", { "aria-label": "Notifications", className: cn3("flex flex-col", className), children: [
422
- /* @__PURE__ */ jsxs3("header", { className: "flex items-center justify-between border-b border-border px-4 py-2", children: [
423
- /* @__PURE__ */ jsx5("h2", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
426
+ return /* @__PURE__ */ jsxs3(Card, { className, children: [
427
+ /* @__PURE__ */ jsx5(InboxHeader, {}),
424
428
  /* @__PURE__ */ jsx5(
425
- "button",
429
+ EmptyState,
426
430
  {
427
- type: "button",
428
- onClick: () => markAllRead.mutate(),
429
- disabled: markAllRead.isPending,
430
- className: "text-sm text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50",
431
- children: "Mark all read"
431
+ icon: /* @__PURE__ */ jsx5(BellOffIcon, {}),
432
+ title: "No notifications yet",
433
+ description: "New alerts and updates will appear here."
432
434
  }
433
435
  )
434
- ] }),
436
+ ] });
437
+ }
438
+ return /* @__PURE__ */ jsxs3(Card, { role: "region", "aria-label": "Notifications", className, children: [
439
+ /* @__PURE__ */ jsx5(
440
+ InboxHeader,
441
+ {
442
+ action: /* @__PURE__ */ jsx5(
443
+ Button,
444
+ {
445
+ type: "button",
446
+ variant: "ghost",
447
+ size: "sm",
448
+ onClick: () => markAllRead.mutate(),
449
+ disabled: markAllRead.isPending,
450
+ children: "Mark all read"
451
+ }
452
+ )
453
+ }
454
+ ),
435
455
  /* @__PURE__ */ jsx5("ul", { className: "divide-y divide-border", children: items.map((item) => /* @__PURE__ */ jsx5(InboxRow, { item, onMarkRead: () => markRead.mutate(item.id) }, item.id)) })
436
456
  ] });
437
457
  }
458
+ function InboxHeader({ action }) {
459
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
460
+ /* @__PURE__ */ jsx5("h2", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
461
+ action
462
+ ] });
463
+ }
438
464
  function InboxRow({
439
465
  item,
440
466
  onMarkRead
@@ -445,20 +471,63 @@ function InboxRow({
445
471
  type: "button",
446
472
  onClick: onMarkRead,
447
473
  "aria-label": `${item.title}${item.isRead ? "" : " (unread)"}`,
448
- className: cn3(
449
- "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",
450
- item.isRead ? "text-muted-foreground" : "text-foreground"
451
- ),
474
+ 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",
452
475
  children: [
453
- /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
454
- !item.isRead && /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", className: "h-2 w-2 rounded-full bg-primary" }),
455
- item.title
456
- ] }),
457
- item.body && /* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: item.body })
476
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", className: "mt-1.5 flex size-2 shrink-0 items-center justify-center", children: !item.isRead && /* @__PURE__ */ jsx5("span", { className: "size-2 rounded-full bg-coral" }) }),
477
+ /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
478
+ /* @__PURE__ */ jsx5(
479
+ "span",
480
+ {
481
+ className: cn3(
482
+ "truncate text-sm",
483
+ item.isRead ? "font-normal text-muted-foreground" : "font-semibold text-foreground"
484
+ ),
485
+ children: item.title
486
+ }
487
+ ),
488
+ item.body && /* @__PURE__ */ jsx5("span", { className: "truncate text-xs text-muted-foreground", children: item.body })
489
+ ] })
458
490
  ]
459
491
  }
460
492
  ) });
461
493
  }
494
+ function AlertIcon() {
495
+ return /* @__PURE__ */ jsxs3(
496
+ "svg",
497
+ {
498
+ viewBox: "0 0 24 24",
499
+ fill: "none",
500
+ stroke: "currentColor",
501
+ strokeWidth: "2",
502
+ strokeLinecap: "round",
503
+ strokeLinejoin: "round",
504
+ children: [
505
+ /* @__PURE__ */ jsx5("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" }),
506
+ /* @__PURE__ */ jsx5("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
507
+ /* @__PURE__ */ jsx5("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
508
+ ]
509
+ }
510
+ );
511
+ }
512
+ function BellOffIcon() {
513
+ return /* @__PURE__ */ jsxs3(
514
+ "svg",
515
+ {
516
+ viewBox: "0 0 24 24",
517
+ fill: "none",
518
+ stroke: "currentColor",
519
+ strokeWidth: "2",
520
+ strokeLinecap: "round",
521
+ strokeLinejoin: "round",
522
+ children: [
523
+ /* @__PURE__ */ jsx5("path", { d: "M8.7 3A6 6 0 0 1 18 8a21.3 21.3 0 0 0 .6 5" }),
524
+ /* @__PURE__ */ jsx5("path", { d: "M17 17H3s3-2 3-9a4.67 4.67 0 0 1 .3-1.7" }),
525
+ /* @__PURE__ */ jsx5("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" }),
526
+ /* @__PURE__ */ jsx5("line", { x1: "2", x2: "22", y1: "2", y2: "22" })
527
+ ]
528
+ }
529
+ );
530
+ }
462
531
 
463
532
  // src/notification-center.tsx
464
533
  import { useCallback, useEffect as useEffect4, useRef as useRef2, useState as useState3 } from "react";
@@ -533,7 +602,7 @@ function NotificationCenter({
533
602
  "span",
534
603
  {
535
604
  "aria-hidden": true,
536
- 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",
605
+ 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",
537
606
  children: badge
538
607
  }
539
608
  )
@@ -663,14 +732,14 @@ import {
663
732
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
664
733
  function TrendChart({ data, className }) {
665
734
  return /* @__PURE__ */ jsx8("div", { className: cn6("h-72 w-full", className), role: "img", "aria-label": "Delivery trend over time", children: /* @__PURE__ */ jsx8(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs6(LineChart, { data, margin: { top: 8, right: 16, bottom: 8, left: 0 }, children: [
666
- /* @__PURE__ */ jsx8(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }),
667
- /* @__PURE__ */ jsx8(XAxis, { dataKey: "day", stroke: "hsl(var(--muted-foreground))", fontSize: 12 }),
668
- /* @__PURE__ */ jsx8(YAxis, { stroke: "hsl(var(--muted-foreground))", fontSize: 12, allowDecimals: false }),
735
+ /* @__PURE__ */ jsx8(CartesianGrid, { strokeDasharray: "3 3", stroke: "var(--border)" }),
736
+ /* @__PURE__ */ jsx8(XAxis, { dataKey: "day", stroke: "var(--muted-foreground)", fontSize: 12 }),
737
+ /* @__PURE__ */ jsx8(YAxis, { stroke: "var(--muted-foreground)", fontSize: 12, allowDecimals: false }),
669
738
  /* @__PURE__ */ jsx8(Tooltip, {}),
670
- /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "sends", stroke: "hsl(var(--primary))", dot: false }),
671
- /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "delivered", stroke: "hsl(var(--chart-2, var(--primary)))", dot: false }),
672
- /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "opened", stroke: "hsl(var(--chart-3, var(--primary)))", dot: false }),
673
- /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "clicked", stroke: "hsl(var(--chart-4, var(--primary)))", dot: false })
739
+ /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "sends", stroke: "var(--primary)", dot: false }),
740
+ /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "delivered", stroke: "var(--chart-2, var(--primary))", dot: false }),
741
+ /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "opened", stroke: "var(--chart-3, var(--primary))", dot: false }),
742
+ /* @__PURE__ */ jsx8(Line, { type: "monotone", dataKey: "clicked", stroke: "var(--chart-4, var(--primary))", dot: false })
674
743
  ] }) }) });
675
744
  }
676
745
 
@@ -820,7 +889,7 @@ function TemplateStatusBadge({
820
889
  }
821
890
 
822
891
  // src/template-list.tsx
823
- import { cn as cn10 } from "@quanticjs/react-ui";
892
+ import { Button as Button2, Card as Card2, EmptyState as EmptyState2, Skeleton as Skeleton2, cn as cn10 } from "@quanticjs/react-ui";
824
893
  import { useApiQuery as useApiQuery7 } from "@quanticjs/react-query";
825
894
  import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
826
895
  function TemplateList({ basePath = "/api/templates", className }) {
@@ -829,51 +898,134 @@ function TemplateList({ basePath = "/api/templates", className }) {
829
898
  (client) => client.get(basePath)
830
899
  );
831
900
  if (isLoading) {
832
- return /* @__PURE__ */ jsxs9(
833
- "div",
834
- {
835
- role: "status",
836
- "aria-label": "Loading templates",
837
- className: cn10("flex flex-col gap-2 p-4", className),
838
- children: [
839
- /* @__PURE__ */ jsx12("span", { className: "sr-only", children: "Loading templates" }),
840
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx12("div", { "aria-hidden": "true", className: "h-12 animate-pulse rounded bg-muted" }, i))
841
- ]
842
- }
843
- );
901
+ return /* @__PURE__ */ jsxs9("div", { role: "status", "aria-label": "Loading templates", className: cn10(className), children: [
902
+ /* @__PURE__ */ jsx12("span", { className: "sr-only", children: "Loading templates" }),
903
+ /* @__PURE__ */ jsxs9(Card2, { "aria-hidden": "true", children: [
904
+ /* @__PURE__ */ jsx12(ListHeader, {}),
905
+ /* @__PURE__ */ jsx12("ul", { className: "divide-y divide-border", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxs9("li", { className: "flex flex-col gap-2 px-4 py-3.5", children: [
906
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2.5", children: [
907
+ /* @__PURE__ */ jsx12(Skeleton2, { className: "h-3.5 w-40" }),
908
+ /* @__PURE__ */ jsx12(Skeleton2, { className: "h-5 w-16 rounded-full" })
909
+ ] }),
910
+ /* @__PURE__ */ jsx12(Skeleton2, { className: "h-3 w-56" })
911
+ ] }, i)) })
912
+ ] })
913
+ ] });
844
914
  }
845
915
  if (isError) {
846
- return /* @__PURE__ */ jsxs9("div", { className: cn10("flex flex-col items-start gap-3 p-4", className), children: [
847
- /* @__PURE__ */ jsx12("p", { className: "text-sm text-foreground", children: "Failed to load templates" }),
916
+ return /* @__PURE__ */ jsxs9(Card2, { className, children: [
917
+ /* @__PURE__ */ jsx12(ListHeader, {}),
918
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
919
+ /* @__PURE__ */ jsx12(
920
+ "span",
921
+ {
922
+ "aria-hidden": "true",
923
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
924
+ children: /* @__PURE__ */ jsx12(AlertIcon2, {})
925
+ }
926
+ ),
927
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
928
+ /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium text-foreground", children: "Failed to load templates" }),
929
+ /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: "Something went wrong while loading your templates." })
930
+ ] }),
931
+ /* @__PURE__ */ jsx12(Button2, { type: "button", onClick: () => void refetch(), children: "Try again" })
932
+ ] })
933
+ ] });
934
+ }
935
+ const items = data ?? [];
936
+ if (items.length === 0) {
937
+ return /* @__PURE__ */ jsxs9(Card2, { className, children: [
938
+ /* @__PURE__ */ jsx12(ListHeader, {}),
848
939
  /* @__PURE__ */ jsx12(
849
- "button",
940
+ EmptyState2,
850
941
  {
851
- type: "button",
852
- onClick: () => void refetch(),
853
- 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",
854
- children: "Try again"
942
+ icon: /* @__PURE__ */ jsx12(FileIcon, {}),
943
+ title: "No templates found",
944
+ description: "This workspace doesn't have any notification templates yet."
855
945
  }
856
946
  )
857
947
  ] });
858
948
  }
859
- const items = data ?? [];
860
- if (items.length === 0) {
861
- return /* @__PURE__ */ jsx12("div", { className: cn10("p-6 text-center text-sm text-muted-foreground", className), children: "No templates found" });
862
- }
863
- return /* @__PURE__ */ jsx12("section", { "aria-label": "Templates", className: cn10("flex flex-col", className), children: /* @__PURE__ */ jsxs9("table", { className: "w-full text-sm", children: [
864
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs9("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
865
- /* @__PURE__ */ jsx12("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Template" }),
866
- /* @__PURE__ */ jsx12("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
867
- /* @__PURE__ */ jsx12("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locales" }),
868
- /* @__PURE__ */ jsx12("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Versions" })
869
- ] }) }),
870
- /* @__PURE__ */ jsx12("tbody", { children: items.map((item) => /* @__PURE__ */ jsxs9("tr", { className: "border-b border-border", children: [
871
- /* @__PURE__ */ jsx12("td", { className: "py-3 pe-4 font-medium text-foreground", children: item.templateId }),
872
- /* @__PURE__ */ jsx12("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx12(TemplateStatusBadge, { status: item.latestStatus }) }),
873
- /* @__PURE__ */ jsx12("td", { className: "px-4 py-3 text-muted-foreground", children: item.locales.join(", ") }),
874
- /* @__PURE__ */ jsx12("td", { className: "px-4 py-3 text-muted-foreground", children: item.versionCount })
875
- ] }, item.templateId)) })
876
- ] }) });
949
+ return /* @__PURE__ */ jsxs9(Card2, { role: "region", "aria-label": "Templates", className, children: [
950
+ /* @__PURE__ */ jsx12(ListHeader, { count: items.length }),
951
+ /* @__PURE__ */ jsx12("ul", { className: "divide-y divide-border", children: items.map((item) => /* @__PURE__ */ jsx12(TemplateRow, { item }, item.templateId)) })
952
+ ] });
953
+ }
954
+ function ListHeader({ count }) {
955
+ return /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
956
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col", children: [
957
+ /* @__PURE__ */ jsx12("h2", { className: "text-sm font-semibold text-foreground", children: "Templates" }),
958
+ /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", children: "Read-only view of all notification templates" })
959
+ ] }),
960
+ count != null && count > 0 && /* @__PURE__ */ jsxs9("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: [
961
+ count,
962
+ " templates"
963
+ ] })
964
+ ] });
965
+ }
966
+ function TemplateRow({ item }) {
967
+ return /* @__PURE__ */ jsxs9("li", { className: "flex flex-col gap-1.5 px-4 py-3.5", children: [
968
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2.5", children: [
969
+ /* @__PURE__ */ jsx12("span", { className: "truncate text-sm font-medium text-foreground", children: item.templateId }),
970
+ /* @__PURE__ */ jsx12(TemplateStatusBadge, { status: item.latestStatus })
971
+ ] }),
972
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
973
+ /* @__PURE__ */ jsxs9("span", { className: "tabular-nums", children: [
974
+ item.versionCount,
975
+ " versions"
976
+ ] }),
977
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "text-border", children: "\xB7" }),
978
+ /* @__PURE__ */ jsxs9("span", { className: "flex flex-wrap items-center gap-1", children: [
979
+ /* @__PURE__ */ jsx12("span", { children: "Locales:" }),
980
+ item.locales.map((locale) => /* @__PURE__ */ jsx12(
981
+ "span",
982
+ {
983
+ className: "inline-flex items-center rounded bg-muted px-1.5 py-0.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground",
984
+ children: locale
985
+ },
986
+ locale
987
+ ))
988
+ ] })
989
+ ] })
990
+ ] });
991
+ }
992
+ function AlertIcon2() {
993
+ return /* @__PURE__ */ jsxs9(
994
+ "svg",
995
+ {
996
+ viewBox: "0 0 24 24",
997
+ fill: "none",
998
+ stroke: "currentColor",
999
+ strokeWidth: "2",
1000
+ strokeLinecap: "round",
1001
+ strokeLinejoin: "round",
1002
+ children: [
1003
+ /* @__PURE__ */ jsx12("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" }),
1004
+ /* @__PURE__ */ jsx12("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
1005
+ /* @__PURE__ */ jsx12("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
1006
+ ]
1007
+ }
1008
+ );
1009
+ }
1010
+ function FileIcon() {
1011
+ return /* @__PURE__ */ jsxs9(
1012
+ "svg",
1013
+ {
1014
+ viewBox: "0 0 24 24",
1015
+ fill: "none",
1016
+ stroke: "currentColor",
1017
+ strokeWidth: "2",
1018
+ strokeLinecap: "round",
1019
+ strokeLinejoin: "round",
1020
+ children: [
1021
+ /* @__PURE__ */ jsx12("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
1022
+ /* @__PURE__ */ jsx12("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
1023
+ /* @__PURE__ */ jsx12("path", { d: "M10 9H8" }),
1024
+ /* @__PURE__ */ jsx12("path", { d: "M16 13H8" }),
1025
+ /* @__PURE__ */ jsx12("path", { d: "M16 17H8" })
1026
+ ]
1027
+ }
1028
+ );
877
1029
  }
878
1030
 
879
1031
  // src/template-editor.tsx
@@ -2087,9 +2239,74 @@ function SegmentBuilder({
2087
2239
 
2088
2240
  // src/suppression-manager.tsx
2089
2241
  import { useState as useState10 } from "react";
2090
- import { cn as cn20, formatDateTime as formatDateTime3, useToast as useToast8 } from "@quanticjs/react-ui";
2242
+ import { Button as Button4, Card as Card3, EmptyState as EmptyState3, cn as cn20, formatDateTime as formatDateTime3, useToast as useToast8 } from "@quanticjs/react-ui";
2091
2243
  import { useApiMutation as useApiMutation9, useApiQuery as useApiQuery14 } from "@quanticjs/react-query";
2092
- import { Fragment, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
2244
+
2245
+ // src/admin-panel.tsx
2246
+ import { Button as Button3, Skeleton as Skeleton3 } from "@quanticjs/react-ui";
2247
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
2248
+ function PanelHeader({ title, subtitle }) {
2249
+ return /* @__PURE__ */ jsx22("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxs19("div", { className: "flex flex-col", children: [
2250
+ /* @__PURE__ */ jsx22("h2", { className: "text-sm font-semibold text-foreground", children: title }),
2251
+ subtitle && /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground tabular-nums", children: subtitle })
2252
+ ] }) });
2253
+ }
2254
+ function SkeletonRows({ label, rows = 4 }) {
2255
+ return /* @__PURE__ */ jsxs19("div", { role: "status", "aria-label": label, className: "flex flex-col gap-2 p-4", children: [
2256
+ /* @__PURE__ */ jsx22("span", { className: "sr-only", children: label }),
2257
+ Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx22(Skeleton3, { className: "h-10 w-full" }, i))
2258
+ ] });
2259
+ }
2260
+ function ErrorPanel({ message, onRetry }) {
2261
+ return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-3 px-6 py-12 text-center", children: [
2262
+ /* @__PURE__ */ jsx22(
2263
+ "span",
2264
+ {
2265
+ "aria-hidden": "true",
2266
+ className: "grid size-12 place-items-center rounded-full bg-destructive/10 text-destructive [&_svg]:size-6",
2267
+ children: /* @__PURE__ */ jsx22(AlertIcon3, {})
2268
+ }
2269
+ ),
2270
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium text-foreground", children: message }),
2271
+ /* @__PURE__ */ jsx22(Button3, { type: "button", onClick: onRetry, children: "Try again" })
2272
+ ] });
2273
+ }
2274
+ function Pager({
2275
+ label,
2276
+ page,
2277
+ totalPages,
2278
+ onPrev,
2279
+ onNext
2280
+ }) {
2281
+ return /* @__PURE__ */ jsxs19(
2282
+ "nav",
2283
+ {
2284
+ "aria-label": label,
2285
+ className: "flex items-center justify-between gap-2 border-t border-border px-4 py-3",
2286
+ children: [
2287
+ /* @__PURE__ */ jsx22(Button3, { type: "button", variant: "outline", size: "sm", onClick: onPrev, disabled: page <= 1, children: "Previous" }),
2288
+ /* @__PURE__ */ jsxs19("span", { className: "text-xs text-muted-foreground tabular-nums", children: [
2289
+ "Page ",
2290
+ page,
2291
+ " of ",
2292
+ totalPages
2293
+ ] }),
2294
+ /* @__PURE__ */ jsx22(Button3, { type: "button", variant: "outline", size: "sm", onClick: onNext, disabled: page >= totalPages, children: "Next" })
2295
+ ]
2296
+ }
2297
+ );
2298
+ }
2299
+ 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";
2300
+ function AlertIcon3() {
2301
+ return /* @__PURE__ */ jsxs19("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2302
+ /* @__PURE__ */ jsx22("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" }),
2303
+ /* @__PURE__ */ jsx22("line", { x1: "12", x2: "12", y1: "9", y2: "13" }),
2304
+ /* @__PURE__ */ jsx22("line", { x1: "12", x2: "12.01", y1: "17", y2: "17" })
2305
+ ] });
2306
+ }
2307
+
2308
+ // src/suppression-manager.tsx
2309
+ import { Fragment, jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
2093
2310
  var LIMIT3 = 20;
2094
2311
  var CHANNELS3 = ["inapp", "email", "push", "sms"];
2095
2312
  function SuppressionManager({ basePath = "/api", className }) {
@@ -2133,154 +2350,96 @@ function SuppressionManager({ basePath = "/api", className }) {
2133
2350
  event.preventDefault();
2134
2351
  add.mutate({ channel, address: address.trim(), reason: reason.trim() });
2135
2352
  };
2136
- const addForm = /* @__PURE__ */ jsxs19("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2137
- /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
2138
- /* @__PURE__ */ jsx22("label", { htmlFor: "suppression-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
2139
- /* @__PURE__ */ jsx22(
2140
- "select",
2141
- {
2142
- id: "suppression-channel",
2143
- value: channel,
2144
- onChange: (e) => setChannel(e.target.value),
2145
- 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",
2146
- children: CHANNELS3.map((c) => /* @__PURE__ */ jsx22("option", { value: c, children: c }, c))
2147
- }
2148
- )
2149
- ] }),
2150
- /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
2151
- /* @__PURE__ */ jsx22("label", { htmlFor: "suppression-address", className: "text-sm font-medium text-foreground", children: "Address" }),
2152
- /* @__PURE__ */ jsx22(
2153
- "input",
2154
- {
2155
- id: "suppression-address",
2156
- type: "text",
2157
- value: address,
2158
- onChange: (e) => setAddress(e.target.value),
2159
- 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"
2160
- }
2161
- )
2162
- ] }),
2163
- /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
2164
- /* @__PURE__ */ jsx22("label", { htmlFor: "suppression-reason", className: "text-sm font-medium text-foreground", children: "Reason" }),
2165
- /* @__PURE__ */ jsx22(
2166
- "input",
2167
- {
2168
- id: "suppression-reason",
2169
- type: "text",
2170
- value: reason,
2171
- onChange: (e) => setReason(e.target.value),
2172
- 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"
2173
- }
2174
- )
2175
- ] }),
2176
- /* @__PURE__ */ jsx22(
2177
- "button",
2178
- {
2179
- type: "submit",
2180
- disabled: add.isPending,
2181
- 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",
2182
- children: add.isPending ? "Adding\u2026" : "Add suppression"
2183
- }
2184
- )
2185
- ] });
2186
- let body;
2187
- if (isLoading) {
2188
- body = /* @__PURE__ */ jsxs19("div", { role: "status", "aria-label": "Loading suppressions", className: "flex flex-col gap-2 p-4", children: [
2189
- /* @__PURE__ */ jsx22("span", { className: "sr-only", children: "Loading suppressions" }),
2190
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx22("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2191
- ] });
2192
- } else if (isError) {
2193
- body = /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2194
- /* @__PURE__ */ jsx22("p", { className: "text-sm text-foreground", children: "Failed to load suppressions" }),
2195
- /* @__PURE__ */ jsx22(
2196
- "button",
2197
- {
2198
- type: "button",
2199
- onClick: () => void refetch(),
2200
- 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",
2201
- children: "Try again"
2202
- }
2203
- )
2204
- ] });
2205
- } else {
2206
- const rows = data?.items ?? [];
2207
- const totalPages = data?.totalPages ?? 1;
2208
- if (rows.length === 0) {
2209
- body = /* @__PURE__ */ jsx22("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No suppressions" });
2210
- } else {
2211
- body = /* @__PURE__ */ jsxs19(Fragment, { children: [
2212
- /* @__PURE__ */ jsxs19("table", { className: "w-full text-sm", children: [
2213
- /* @__PURE__ */ jsx22("thead", { children: /* @__PURE__ */ jsxs19("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2214
- /* @__PURE__ */ jsx22("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
2215
- /* @__PURE__ */ jsx22("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Address" }),
2216
- /* @__PURE__ */ jsx22("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Reason" }),
2217
- /* @__PURE__ */ jsx22("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
2218
- /* @__PURE__ */ jsx22("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx22("span", { className: "sr-only", children: "Actions" }) })
2353
+ const rows = data?.items ?? [];
2354
+ const totalPages = data?.totalPages ?? 1;
2355
+ return /* @__PURE__ */ jsxs20("section", { "aria-label": "Suppression list", className: cn20("flex flex-col gap-4", className), children: [
2356
+ /* @__PURE__ */ jsx23(Card3, { children: /* @__PURE__ */ jsxs20("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
2357
+ /* @__PURE__ */ jsxs20("label", { className: "flex flex-col gap-1 text-sm", children: [
2358
+ /* @__PURE__ */ jsx23("span", { className: "font-medium text-foreground", children: "Channel" }),
2359
+ /* @__PURE__ */ jsx23(
2360
+ "select",
2361
+ {
2362
+ value: channel,
2363
+ onChange: (e) => setChannel(e.target.value),
2364
+ className: FIELD_CLASS,
2365
+ children: CHANNELS3.map((c) => /* @__PURE__ */ jsx23("option", { value: c, children: c }, c))
2366
+ }
2367
+ )
2368
+ ] }),
2369
+ /* @__PURE__ */ jsxs20("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
2370
+ /* @__PURE__ */ jsx23("span", { className: "font-medium text-foreground", children: "Address" }),
2371
+ /* @__PURE__ */ jsx23("input", { type: "text", value: address, onChange: (e) => setAddress(e.target.value), className: FIELD_CLASS })
2372
+ ] }),
2373
+ /* @__PURE__ */ jsxs20("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
2374
+ /* @__PURE__ */ jsx23("span", { className: "font-medium text-foreground", children: "Reason" }),
2375
+ /* @__PURE__ */ jsx23("input", { type: "text", value: reason, onChange: (e) => setReason(e.target.value), className: FIELD_CLASS })
2376
+ ] }),
2377
+ /* @__PURE__ */ jsx23(Button4, { type: "submit", disabled: add.isPending, children: add.isPending ? "Adding\u2026" : "Add suppression" })
2378
+ ] }) }),
2379
+ /* @__PURE__ */ jsxs20(Card3, { children: [
2380
+ /* @__PURE__ */ jsx23(PanelHeader, { title: "Suppressions", subtitle: `${data?.total ?? rows.length} entries` }),
2381
+ isLoading ? /* @__PURE__ */ jsx23(SkeletonRows, { label: "Loading suppressions" }) : isError ? /* @__PURE__ */ jsx23(ErrorPanel, { message: "Failed to load suppressions", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ jsx23(EmptyState3, { icon: /* @__PURE__ */ jsx23(BanIcon, {}), title: "No suppressions", description: "No addresses are suppressed." }) : /* @__PURE__ */ jsxs20(Fragment, { children: [
2382
+ /* @__PURE__ */ jsxs20("table", { className: "w-full text-sm", children: [
2383
+ /* @__PURE__ */ jsx23("thead", { children: /* @__PURE__ */ jsxs20("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2384
+ /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channel" }),
2385
+ /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Address" }),
2386
+ /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Reason" }),
2387
+ /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
2388
+ /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Actions" }) })
2219
2389
  ] }) }),
2220
- /* @__PURE__ */ jsx22("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs19("tr", { className: "border-b border-border", children: [
2221
- /* @__PURE__ */ jsx22("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
2222
- /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-foreground", children: row.address }),
2223
- /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-muted-foreground", children: row.reason }),
2224
- /* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime3(row.createdAt) }),
2225
- /* @__PURE__ */ jsx22("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx22(
2226
- "button",
2390
+ /* @__PURE__ */ jsx23("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs20("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
2391
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx23("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 }) }),
2392
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-foreground", children: row.address }),
2393
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-muted-foreground", children: row.reason }),
2394
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime3(row.createdAt) }),
2395
+ /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-end", children: /* @__PURE__ */ jsx23(
2396
+ Button4,
2227
2397
  {
2228
2398
  type: "button",
2399
+ variant: "ghost",
2400
+ size: "sm",
2401
+ className: "text-destructive",
2229
2402
  disabled: remove.isPending,
2230
2403
  onClick: () => remove.mutate(row.id),
2231
- 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",
2232
2404
  children: "Remove"
2233
2405
  }
2234
2406
  ) })
2235
2407
  ] }, row.id)) })
2236
2408
  ] }),
2237
- /* @__PURE__ */ jsxs19("nav", { "aria-label": "Suppression pagination", className: "flex items-center justify-between", children: [
2238
- /* @__PURE__ */ jsx22(
2239
- "button",
2240
- {
2241
- type: "button",
2242
- onClick: () => setPage((p) => Math.max(1, p - 1)),
2243
- disabled: page <= 1,
2244
- 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",
2245
- children: "Previous"
2246
- }
2247
- ),
2248
- /* @__PURE__ */ jsxs19("span", { className: "text-xs text-muted-foreground", children: [
2249
- "Page ",
2409
+ /* @__PURE__ */ jsx23(
2410
+ Pager,
2411
+ {
2412
+ label: "Suppression pagination",
2250
2413
  page,
2251
- " of ",
2252
- totalPages
2253
- ] }),
2254
- /* @__PURE__ */ jsx22(
2255
- "button",
2256
- {
2257
- type: "button",
2258
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
2259
- disabled: page >= totalPages,
2260
- 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",
2261
- children: "Next"
2262
- }
2263
- )
2264
- ] })
2265
- ] });
2266
- }
2267
- }
2268
- return /* @__PURE__ */ jsxs19("section", { "aria-label": "Suppression list", className: cn20("flex flex-col gap-4", className), children: [
2269
- addForm,
2270
- body
2414
+ totalPages,
2415
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
2416
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
2417
+ }
2418
+ )
2419
+ ] })
2420
+ ] })
2421
+ ] });
2422
+ }
2423
+ function BanIcon() {
2424
+ return /* @__PURE__ */ jsxs20("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2425
+ /* @__PURE__ */ jsx23("circle", { cx: "12", cy: "12", r: "10" }),
2426
+ /* @__PURE__ */ jsx23("path", { d: "m4.9 4.9 14.2 14.2" })
2271
2427
  ] });
2272
2428
  }
2273
2429
 
2274
2430
  // src/dlq-console.tsx
2275
2431
  import { Fragment as Fragment2, useState as useState11 } from "react";
2276
2432
  import {
2433
+ Button as Button5,
2434
+ Card as Card4,
2435
+ EmptyState as EmptyState4,
2436
+ StatusBadge as StatusBadge4,
2277
2437
  cn as cn21,
2278
2438
  formatDateTime as formatDateTime4,
2279
- StatusBadge as StatusBadge4,
2280
2439
  useToast as useToast9
2281
2440
  } from "@quanticjs/react-ui";
2282
2441
  import { useApiMutation as useApiMutation10, useApiQuery as useApiQuery15 } from "@quanticjs/react-query";
2283
- import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
2442
+ import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
2284
2443
  var LIMIT4 = 20;
2285
2444
  var STATUS_FILTERS = ["queued", "replayed", "discarded"];
2286
2445
  function buildErrorDescription(error) {
@@ -2304,31 +2463,26 @@ function DlqMessageDetailRow({ id, basePath }) {
2304
2463
  (client) => client.get(`${basePath}/admin/dlq/${id}`)
2305
2464
  );
2306
2465
  if (isLoading) {
2307
- return /* @__PURE__ */ jsxs20("div", { role: "status", "aria-label": "Loading message detail", className: "flex flex-col gap-2 p-3", children: [
2308
- /* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Loading message detail" }),
2309
- /* @__PURE__ */ jsx23("div", { "aria-hidden": "true", className: "h-16 animate-pulse rounded bg-muted" })
2466
+ return /* @__PURE__ */ jsxs21("div", { role: "status", "aria-label": "Loading message detail", className: "p-4", children: [
2467
+ /* @__PURE__ */ jsx24("span", { className: "sr-only", children: "Loading message detail" }),
2468
+ /* @__PURE__ */ jsx24("div", { "aria-hidden": "true", className: "h-16 animate-pulse rounded bg-muted motion-reduce:animate-none" })
2310
2469
  ] });
2311
2470
  }
2312
2471
  if (isError) {
2313
- return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col items-start gap-3 p-3", children: [
2314
- /* @__PURE__ */ jsx23("p", { className: "text-sm text-foreground", children: "Failed to load message detail" }),
2315
- /* @__PURE__ */ jsx23(
2316
- "button",
2317
- {
2318
- type: "button",
2319
- onClick: () => void refetch(),
2320
- 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",
2321
- children: "Try again"
2322
- }
2323
- )
2472
+ return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2473
+ /* @__PURE__ */ jsx24("p", { className: "text-sm text-foreground", children: "Failed to load message detail" }),
2474
+ /* @__PURE__ */ jsx24(Button5, { type: "button", size: "sm", onClick: () => void refetch(), children: "Try again" })
2324
2475
  ] });
2325
2476
  }
2326
- return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-2 p-3 text-sm", children: [
2327
- /* @__PURE__ */ jsxs20("p", { className: "text-muted-foreground", children: [
2328
- "Error: ",
2329
- /* @__PURE__ */ jsx23("span", { className: "text-foreground", children: data?.errorMessage ?? "\u2014" })
2477
+ return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-3 px-5 py-4 text-sm", children: [
2478
+ /* @__PURE__ */ jsxs21("div", { children: [
2479
+ /* @__PURE__ */ jsx24("div", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Error" }),
2480
+ /* @__PURE__ */ jsx24("div", { className: "font-mono text-xs text-destructive", children: data?.errorMessage ?? "\u2014" })
2330
2481
  ] }),
2331
- /* @__PURE__ */ jsx23("pre", { className: "max-h-64 overflow-auto rounded bg-muted p-3 text-xs text-foreground", children: JSON.stringify(data?.payload ?? {}, null, 2) })
2482
+ /* @__PURE__ */ jsxs21("div", { children: [
2483
+ /* @__PURE__ */ jsx24("div", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Payload" }),
2484
+ /* @__PURE__ */ jsx24("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) })
2485
+ ] })
2332
2486
  ] });
2333
2487
  }
2334
2488
  function DlqConsole({ basePath = "/api", className }) {
@@ -2376,150 +2530,108 @@ function DlqConsole({ basePath = "/api", className }) {
2376
2530
  })
2377
2531
  }
2378
2532
  );
2379
- const filterControl = /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
2380
- /* @__PURE__ */ jsx23("label", { htmlFor: "dlq-status", className: "text-sm font-medium text-foreground", children: "Status" }),
2381
- /* @__PURE__ */ jsxs20(
2382
- "select",
2383
- {
2384
- id: "dlq-status",
2385
- value: statusFilter,
2386
- onChange: (e) => {
2387
- setStatusFilter(e.target.value);
2388
- setPage(1);
2389
- },
2390
- 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",
2391
- children: [
2392
- /* @__PURE__ */ jsx23("option", { value: "", children: "All" }),
2393
- STATUS_FILTERS.map((s) => /* @__PURE__ */ jsx23("option", { value: s, children: s }, s))
2394
- ]
2395
- }
2396
- )
2397
- ] });
2398
- let body;
2399
- if (isLoading) {
2400
- body = /* @__PURE__ */ jsxs20(
2401
- "div",
2402
- {
2403
- role: "status",
2404
- "aria-label": "Loading dead-letter messages",
2405
- className: "flex flex-col gap-2 p-4",
2406
- children: [
2407
- /* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Loading dead-letter messages" }),
2408
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx23("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2409
- ]
2410
- }
2411
- );
2412
- } else if (isError) {
2413
- body = /* @__PURE__ */ jsxs20("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2414
- /* @__PURE__ */ jsx23("p", { className: "text-sm text-foreground", children: "Failed to load dead-letter messages" }),
2415
- /* @__PURE__ */ jsx23(
2416
- "button",
2533
+ const rows = data?.items ?? [];
2534
+ const totalPages = data?.totalPages ?? 1;
2535
+ return /* @__PURE__ */ jsxs21("section", { "aria-label": "Dead-letter queue", className: cn21("flex flex-col gap-4", className), children: [
2536
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-end gap-2", children: [
2537
+ /* @__PURE__ */ jsx24("label", { htmlFor: "dlq-status", className: "text-sm font-medium text-foreground", children: "Status" }),
2538
+ /* @__PURE__ */ jsxs21(
2539
+ "select",
2417
2540
  {
2418
- type: "button",
2419
- onClick: () => void refetch(),
2420
- 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",
2421
- children: "Try again"
2541
+ id: "dlq-status",
2542
+ value: statusFilter,
2543
+ onChange: (e) => {
2544
+ setStatusFilter(e.target.value);
2545
+ setPage(1);
2546
+ },
2547
+ className: FIELD_CLASS,
2548
+ children: [
2549
+ /* @__PURE__ */ jsx24("option", { value: "", children: "All" }),
2550
+ STATUS_FILTERS.map((s) => /* @__PURE__ */ jsx24("option", { value: s, children: s }, s))
2551
+ ]
2422
2552
  }
2423
2553
  )
2424
- ] });
2425
- } else {
2426
- const rows = data?.items ?? [];
2427
- const totalPages = data?.totalPages ?? 1;
2428
- if (rows.length === 0) {
2429
- body = /* @__PURE__ */ jsx23("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No dead-letter messages" });
2430
- } else {
2431
- body = /* @__PURE__ */ jsxs20(Fragment3, { children: [
2432
- /* @__PURE__ */ jsxs20("table", { className: "w-full text-sm", children: [
2433
- /* @__PURE__ */ jsx23("thead", { children: /* @__PURE__ */ jsxs20("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2434
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Request" }),
2435
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failure reason" }),
2436
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
2437
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
2438
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: "First seen" }),
2439
- /* @__PURE__ */ jsx23("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx23("span", { className: "sr-only", children: "Actions" }) })
2440
- ] }) }),
2441
- /* @__PURE__ */ jsx23("tbody", { children: rows.map((row) => {
2442
- const isExpanded = expandedId === row.id;
2443
- return /* @__PURE__ */ jsxs20(Fragment2, { children: [
2444
- /* @__PURE__ */ jsxs20("tr", { className: "border-b border-border", children: [
2445
- /* @__PURE__ */ jsx23("td", { className: "py-3 pe-4", children: /* @__PURE__ */ jsx23(
2446
- "button",
2447
- {
2448
- type: "button",
2449
- "aria-expanded": isExpanded,
2450
- onClick: () => setExpandedId(isExpanded ? null : row.id),
2451
- className: "rounded text-start font-medium text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
2452
- children: row.requestId ?? row.id
2453
- }
2454
- ) }),
2455
- /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-muted-foreground", children: row.failureReason }),
2456
- /* @__PURE__ */ jsx23("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx23(StatusBadge4, { variant: statusVariant3(row.status), appearance: "dot", children: row.status }) }),
2457
- /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-muted-foreground", children: row.attemptCount }),
2458
- /* @__PURE__ */ jsx23("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime4(row.firstSeenAt) }),
2459
- /* @__PURE__ */ jsx23("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2", children: [
2460
- /* @__PURE__ */ jsx23(
2461
- "button",
2462
- {
2463
- type: "button",
2464
- disabled: replay.isPending,
2465
- onClick: () => replay.mutate(row.id),
2466
- 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",
2467
- children: "Replay"
2468
- }
2469
- ),
2470
- /* @__PURE__ */ jsx23(
2471
- "button",
2472
- {
2473
- type: "button",
2474
- disabled: discard.isPending,
2475
- onClick: () => discard.mutate(row.id),
2476
- 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",
2477
- children: "Discard"
2478
- }
2479
- )
2480
- ] }) })
2554
+ ] }),
2555
+ /* @__PURE__ */ jsxs21(Card4, { children: [
2556
+ /* @__PURE__ */ jsx24(PanelHeader, { title: "Messages", subtitle: !isLoading && !isError ? `${data?.total ?? rows.length} messages` : void 0 }),
2557
+ isLoading ? /* @__PURE__ */ jsx24(SkeletonRows, { label: "Loading dead-letter messages" }) : isError ? /* @__PURE__ */ jsx24(ErrorPanel, { message: "Failed to load dead-letter messages", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ jsx24(EmptyState4, { icon: /* @__PURE__ */ jsx24(SkullIcon, {}), title: "No dead-letter messages", description: "The queue is empty for the current filter." }) : /* @__PURE__ */ jsxs21(Fragment3, { children: [
2558
+ /* @__PURE__ */ jsx24("ul", { className: "divide-y divide-border", children: rows.map((row) => {
2559
+ const isExpanded = expandedId === row.id;
2560
+ return /* @__PURE__ */ jsxs21(Fragment2, { children: [
2561
+ /* @__PURE__ */ jsxs21("li", { className: "flex items-start gap-3 px-4 py-3.5", children: [
2562
+ /* @__PURE__ */ jsx24(
2563
+ "button",
2564
+ {
2565
+ type: "button",
2566
+ "aria-expanded": isExpanded,
2567
+ "aria-label": isExpanded ? "Collapse" : "Expand",
2568
+ onClick: () => setExpandedId(isExpanded ? null : row.id),
2569
+ 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",
2570
+ children: /* @__PURE__ */ jsx24(ChevronIcon, { open: isExpanded })
2571
+ }
2572
+ ),
2573
+ /* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1", children: [
2574
+ /* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2", children: [
2575
+ /* @__PURE__ */ jsx24("span", { className: "font-mono text-xs text-muted-foreground", children: row.requestId ?? row.id }),
2576
+ /* @__PURE__ */ jsx24(StatusBadge4, { variant: statusVariant3(row.status), appearance: "dot", children: row.status })
2577
+ ] }),
2578
+ /* @__PURE__ */ jsx24("div", { className: "mt-1 truncate text-sm font-medium text-foreground", children: row.failureReason }),
2579
+ /* @__PURE__ */ jsxs21("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 text-xs text-muted-foreground", children: [
2580
+ /* @__PURE__ */ jsxs21("span", { className: "tabular-nums", children: [
2581
+ row.attemptCount,
2582
+ " attempts"
2583
+ ] }),
2584
+ /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", className: "text-border", children: "\xB7" }),
2585
+ /* @__PURE__ */ jsxs21("span", { className: "tabular-nums", children: [
2586
+ "first seen ",
2587
+ formatDateTime4(row.firstSeenAt)
2588
+ ] })
2589
+ ] })
2481
2590
  ] }),
2482
- isExpanded && /* @__PURE__ */ jsx23("tr", { className: "border-b border-border", children: /* @__PURE__ */ jsx23("td", { colSpan: 6, className: "bg-muted/40", children: /* @__PURE__ */ jsx23(DlqMessageDetailRow, { id: row.id, basePath }) }) })
2483
- ] }, row.id);
2484
- }) })
2485
- ] }),
2486
- /* @__PURE__ */ jsxs20("nav", { "aria-label": "Dead-letter pagination", className: "flex items-center justify-between", children: [
2487
- /* @__PURE__ */ jsx23(
2488
- "button",
2489
- {
2490
- type: "button",
2491
- onClick: () => setPage((p) => Math.max(1, p - 1)),
2492
- disabled: page <= 1,
2493
- 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",
2494
- children: "Previous"
2495
- }
2496
- ),
2497
- /* @__PURE__ */ jsxs20("span", { className: "text-xs text-muted-foreground", children: [
2498
- "Page ",
2591
+ /* @__PURE__ */ jsxs21("div", { className: "flex shrink-0 items-center gap-1.5", children: [
2592
+ /* @__PURE__ */ jsx24(Button5, { type: "button", variant: "outline", size: "sm", disabled: replay.isPending, onClick: () => replay.mutate(row.id), children: "Replay" }),
2593
+ /* @__PURE__ */ jsx24(Button5, { type: "button", variant: "ghost", size: "sm", className: "text-destructive", disabled: discard.isPending, onClick: () => discard.mutate(row.id), children: "Discard" })
2594
+ ] })
2595
+ ] }),
2596
+ isExpanded && /* @__PURE__ */ jsx24("li", { className: "border-t border-border bg-muted/40", children: /* @__PURE__ */ jsx24(DlqMessageDetailRow, { id: row.id, basePath }) })
2597
+ ] }, row.id);
2598
+ }) }),
2599
+ /* @__PURE__ */ jsx24(
2600
+ Pager,
2601
+ {
2602
+ label: "Dead-letter pagination",
2499
2603
  page,
2500
- " of ",
2501
- totalPages
2502
- ] }),
2503
- /* @__PURE__ */ jsx23(
2504
- "button",
2505
- {
2506
- type: "button",
2507
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
2508
- disabled: page >= totalPages,
2509
- 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",
2510
- children: "Next"
2511
- }
2512
- )
2513
- ] })
2514
- ] });
2604
+ totalPages,
2605
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
2606
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
2607
+ }
2608
+ )
2609
+ ] })
2610
+ ] })
2611
+ ] });
2612
+ }
2613
+ function ChevronIcon({ open }) {
2614
+ return /* @__PURE__ */ jsx24(
2615
+ "svg",
2616
+ {
2617
+ viewBox: "0 0 24 24",
2618
+ fill: "none",
2619
+ stroke: "currentColor",
2620
+ strokeWidth: "2",
2621
+ strokeLinecap: "round",
2622
+ strokeLinejoin: "round",
2623
+ className: cn21("transition-transform motion-reduce:transition-none", open && "rotate-90"),
2624
+ children: /* @__PURE__ */ jsx24("path", { d: "m9 18 6-6-6-6" })
2515
2625
  }
2516
- }
2517
- return /* @__PURE__ */ jsxs20("section", { "aria-label": "Dead-letter queue", className: cn21("flex flex-col gap-4", className), children: [
2518
- /* @__PURE__ */ jsxs20("header", { className: "flex items-center justify-between", children: [
2519
- /* @__PURE__ */ jsx23("h2", { className: "text-sm font-semibold text-foreground", children: "Dead-letter queue" }),
2520
- filterControl
2521
- ] }),
2522
- body
2626
+ );
2627
+ }
2628
+ function SkullIcon() {
2629
+ return /* @__PURE__ */ jsxs21("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2630
+ /* @__PURE__ */ jsx24("circle", { cx: "9", cy: "12", r: "1" }),
2631
+ /* @__PURE__ */ jsx24("circle", { cx: "15", cy: "12", r: "1" }),
2632
+ /* @__PURE__ */ jsx24("path", { d: "M8 20v2h8v-2" }),
2633
+ /* @__PURE__ */ jsx24("path", { d: "m12.5 17-.5-1-.5 1h1Z" }),
2634
+ /* @__PURE__ */ jsx24("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" })
2523
2635
  ] });
2524
2636
  }
2525
2637
 
@@ -2527,7 +2639,7 @@ function DlqConsole({ basePath = "/api", className }) {
2527
2639
  import { useMemo as useMemo3, useState as useState12 } from "react";
2528
2640
  import { cn as cn22, useToast as useToast10 } from "@quanticjs/react-ui";
2529
2641
  import { useApiMutation as useApiMutation11, useApiQuery as useApiQuery16 } from "@quanticjs/react-query";
2530
- import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
2642
+ import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
2531
2643
  function normalize2(data) {
2532
2644
  if (!data) return [];
2533
2645
  if (Array.isArray(data)) return data;
@@ -2609,10 +2721,10 @@ function CatalogEditor({ basePath = "/api", className }) {
2609
2721
  setEditingId(`${entry.key}:${entry.locale}`);
2610
2722
  setEditValue(entry.value);
2611
2723
  };
2612
- const addForm = /* @__PURE__ */ jsxs21("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2613
- /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-1", children: [
2614
- /* @__PURE__ */ jsx24("label", { htmlFor: "catalog-new-key", className: "text-sm font-medium text-foreground", children: "Key" }),
2615
- /* @__PURE__ */ jsx24(
2724
+ const addForm = /* @__PURE__ */ jsxs22("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2725
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1", children: [
2726
+ /* @__PURE__ */ jsx25("label", { htmlFor: "catalog-new-key", className: "text-sm font-medium text-foreground", children: "Key" }),
2727
+ /* @__PURE__ */ jsx25(
2616
2728
  "input",
2617
2729
  {
2618
2730
  id: "catalog-new-key",
@@ -2623,9 +2735,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2623
2735
  }
2624
2736
  )
2625
2737
  ] }),
2626
- /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-1", children: [
2627
- /* @__PURE__ */ jsx24("label", { htmlFor: "catalog-new-locale", className: "text-sm font-medium text-foreground", children: "Locale" }),
2628
- /* @__PURE__ */ jsx24(
2738
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1", children: [
2739
+ /* @__PURE__ */ jsx25("label", { htmlFor: "catalog-new-locale", className: "text-sm font-medium text-foreground", children: "Locale" }),
2740
+ /* @__PURE__ */ jsx25(
2629
2741
  "input",
2630
2742
  {
2631
2743
  id: "catalog-new-locale",
@@ -2636,9 +2748,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2636
2748
  }
2637
2749
  )
2638
2750
  ] }),
2639
- /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-1", children: [
2640
- /* @__PURE__ */ jsx24("label", { htmlFor: "catalog-new-value", className: "text-sm font-medium text-foreground", children: "Value" }),
2641
- /* @__PURE__ */ jsx24(
2751
+ /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1", children: [
2752
+ /* @__PURE__ */ jsx25("label", { htmlFor: "catalog-new-value", className: "text-sm font-medium text-foreground", children: "Value" }),
2753
+ /* @__PURE__ */ jsx25(
2642
2754
  "input",
2643
2755
  {
2644
2756
  id: "catalog-new-value",
@@ -2649,7 +2761,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2649
2761
  }
2650
2762
  )
2651
2763
  ] }),
2652
- /* @__PURE__ */ jsx24(
2764
+ /* @__PURE__ */ jsx25(
2653
2765
  "button",
2654
2766
  {
2655
2767
  type: "submit",
@@ -2659,9 +2771,9 @@ function CatalogEditor({ basePath = "/api", className }) {
2659
2771
  }
2660
2772
  )
2661
2773
  ] });
2662
- const filterInput = /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-1", children: [
2663
- /* @__PURE__ */ jsx24("label", { htmlFor: "catalog-filter", className: "text-sm font-medium text-foreground", children: "Filter by key" }),
2664
- /* @__PURE__ */ jsx24(
2774
+ const filterInput = /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1", children: [
2775
+ /* @__PURE__ */ jsx25("label", { htmlFor: "catalog-filter", className: "text-sm font-medium text-foreground", children: "Filter by key" }),
2776
+ /* @__PURE__ */ jsx25(
2665
2777
  "input",
2666
2778
  {
2667
2779
  id: "catalog-filter",
@@ -2674,14 +2786,14 @@ function CatalogEditor({ basePath = "/api", className }) {
2674
2786
  ] });
2675
2787
  let body;
2676
2788
  if (isLoading) {
2677
- body = /* @__PURE__ */ jsxs21("div", { role: "status", "aria-label": "Loading catalog", className: "flex flex-col gap-2 p-4", children: [
2678
- /* @__PURE__ */ jsx24("span", { className: "sr-only", children: "Loading catalog" }),
2679
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx24("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2789
+ body = /* @__PURE__ */ jsxs22("div", { role: "status", "aria-label": "Loading catalog", className: "flex flex-col gap-2 p-4", children: [
2790
+ /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Loading catalog" }),
2791
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx25("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2680
2792
  ] });
2681
2793
  } else if (isError) {
2682
- body = /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2683
- /* @__PURE__ */ jsx24("p", { className: "text-sm text-foreground", children: "Failed to load catalog" }),
2684
- /* @__PURE__ */ jsx24(
2794
+ body = /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2795
+ /* @__PURE__ */ jsx25("p", { className: "text-sm text-foreground", children: "Failed to load catalog" }),
2796
+ /* @__PURE__ */ jsx25(
2685
2797
  "button",
2686
2798
  {
2687
2799
  type: "button",
@@ -2692,22 +2804,22 @@ function CatalogEditor({ basePath = "/api", className }) {
2692
2804
  )
2693
2805
  ] });
2694
2806
  } else if (filtered.length === 0) {
2695
- body = /* @__PURE__ */ jsx24("div", { className: "p-6 text-center text-sm text-muted-foreground", children: entries.length === 0 ? "No catalog entries" : "No entries match your filter" });
2807
+ body = /* @__PURE__ */ jsx25("div", { className: "p-6 text-center text-sm text-muted-foreground", children: entries.length === 0 ? "No catalog entries" : "No entries match your filter" });
2696
2808
  } else {
2697
- body = /* @__PURE__ */ jsxs21("table", { className: "w-full text-sm", children: [
2698
- /* @__PURE__ */ jsx24("thead", { children: /* @__PURE__ */ jsxs21("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2699
- /* @__PURE__ */ jsx24("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2700
- /* @__PURE__ */ jsx24("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2701
- /* @__PURE__ */ jsx24("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Value" }),
2702
- /* @__PURE__ */ jsx24("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx24("span", { className: "sr-only", children: "Actions" }) })
2809
+ body = /* @__PURE__ */ jsxs22("table", { className: "w-full text-sm", children: [
2810
+ /* @__PURE__ */ jsx25("thead", { children: /* @__PURE__ */ jsxs22("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2811
+ /* @__PURE__ */ jsx25("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2812
+ /* @__PURE__ */ jsx25("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2813
+ /* @__PURE__ */ jsx25("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Value" }),
2814
+ /* @__PURE__ */ jsx25("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Actions" }) })
2703
2815
  ] }) }),
2704
- /* @__PURE__ */ jsx24("tbody", { children: filtered.map((entry) => {
2816
+ /* @__PURE__ */ jsx25("tbody", { children: filtered.map((entry) => {
2705
2817
  const rowId = `${entry.key}:${entry.locale}`;
2706
2818
  const isEditing = editingId === rowId;
2707
- return /* @__PURE__ */ jsxs21("tr", { className: "border-b border-border", children: [
2708
- /* @__PURE__ */ jsx24("td", { className: "py-3 pe-4 font-mono text-foreground", children: entry.key }),
2709
- /* @__PURE__ */ jsx24("td", { className: "px-4 py-3 text-foreground", children: entry.locale }),
2710
- /* @__PURE__ */ jsx24("td", { className: "px-4 py-3 text-foreground", children: isEditing ? /* @__PURE__ */ jsx24(
2819
+ return /* @__PURE__ */ jsxs22("tr", { className: "border-b border-border", children: [
2820
+ /* @__PURE__ */ jsx25("td", { className: "py-3 pe-4 font-mono text-foreground", children: entry.key }),
2821
+ /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 text-foreground", children: entry.locale }),
2822
+ /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 text-foreground", children: isEditing ? /* @__PURE__ */ jsx25(
2711
2823
  "input",
2712
2824
  {
2713
2825
  type: "text",
@@ -2717,8 +2829,8 @@ function CatalogEditor({ basePath = "/api", className }) {
2717
2829
  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"
2718
2830
  }
2719
2831
  ) : entry.value }),
2720
- /* @__PURE__ */ jsx24("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx24("div", { className: "flex items-center gap-2", children: isEditing ? /* @__PURE__ */ jsxs21(Fragment4, { children: [
2721
- /* @__PURE__ */ jsx24(
2832
+ /* @__PURE__ */ jsx25("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx25("div", { className: "flex items-center gap-2", children: isEditing ? /* @__PURE__ */ jsxs22(Fragment4, { children: [
2833
+ /* @__PURE__ */ jsx25(
2722
2834
  "button",
2723
2835
  {
2724
2836
  type: "button",
@@ -2732,7 +2844,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2732
2844
  children: "Save"
2733
2845
  }
2734
2846
  ),
2735
- /* @__PURE__ */ jsx24(
2847
+ /* @__PURE__ */ jsx25(
2736
2848
  "button",
2737
2849
  {
2738
2850
  type: "button",
@@ -2741,8 +2853,8 @@ function CatalogEditor({ basePath = "/api", className }) {
2741
2853
  children: "Cancel"
2742
2854
  }
2743
2855
  )
2744
- ] }) : /* @__PURE__ */ jsxs21(Fragment4, { children: [
2745
- /* @__PURE__ */ jsx24(
2856
+ ] }) : /* @__PURE__ */ jsxs22(Fragment4, { children: [
2857
+ /* @__PURE__ */ jsx25(
2746
2858
  "button",
2747
2859
  {
2748
2860
  type: "button",
@@ -2751,7 +2863,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2751
2863
  children: "Edit"
2752
2864
  }
2753
2865
  ),
2754
- /* @__PURE__ */ jsx24(
2866
+ /* @__PURE__ */ jsx25(
2755
2867
  "button",
2756
2868
  {
2757
2869
  type: "button",
@@ -2766,7 +2878,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2766
2878
  }) })
2767
2879
  ] });
2768
2880
  }
2769
- return /* @__PURE__ */ jsxs21("section", { "aria-label": "Catalog editor", className: cn22("flex flex-col gap-4", className), children: [
2881
+ return /* @__PURE__ */ jsxs22("section", { "aria-label": "Catalog editor", className: cn22("flex flex-col gap-4", className), children: [
2770
2882
  addForm,
2771
2883
  filterInput,
2772
2884
  body
@@ -2776,7 +2888,7 @@ function CatalogEditor({ basePath = "/api", className }) {
2776
2888
  // src/missing-translations-panel.tsx
2777
2889
  import { cn as cn23 } from "@quanticjs/react-ui";
2778
2890
  import { useApiQuery as useApiQuery17 } from "@quanticjs/react-query";
2779
- import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
2891
+ import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
2780
2892
  function normalize3(data) {
2781
2893
  if (Array.isArray(data)) return data;
2782
2894
  return data?.missing ?? [];
@@ -2790,23 +2902,23 @@ function MissingTranslationsPanel({
2790
2902
  (client) => client.get(`${basePath}/i18n/catalog/missing`)
2791
2903
  );
2792
2904
  if (isLoading) {
2793
- return /* @__PURE__ */ jsxs22(
2905
+ return /* @__PURE__ */ jsxs23(
2794
2906
  "div",
2795
2907
  {
2796
2908
  role: "status",
2797
2909
  "aria-label": "Loading missing translations",
2798
2910
  className: cn23("flex flex-col gap-2 p-4", className),
2799
2911
  children: [
2800
- /* @__PURE__ */ jsx25("span", { className: "sr-only", children: "Loading missing translations" }),
2801
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx25("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2912
+ /* @__PURE__ */ jsx26("span", { className: "sr-only", children: "Loading missing translations" }),
2913
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx26("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2802
2914
  ]
2803
2915
  }
2804
2916
  );
2805
2917
  }
2806
2918
  if (isError) {
2807
- return /* @__PURE__ */ jsxs22("div", { className: cn23("flex flex-col items-start gap-3 p-4", className), children: [
2808
- /* @__PURE__ */ jsx25("p", { className: "text-sm text-foreground", children: "Failed to load missing translations" }),
2809
- /* @__PURE__ */ jsx25(
2919
+ return /* @__PURE__ */ jsxs23("div", { className: cn23("flex flex-col items-start gap-3 p-4", className), children: [
2920
+ /* @__PURE__ */ jsx26("p", { className: "text-sm text-foreground", children: "Failed to load missing translations" }),
2921
+ /* @__PURE__ */ jsx26(
2810
2922
  "button",
2811
2923
  {
2812
2924
  type: "button",
@@ -2819,18 +2931,18 @@ function MissingTranslationsPanel({
2819
2931
  }
2820
2932
  const rows = normalize3(data);
2821
2933
  if (rows.length === 0) {
2822
- return /* @__PURE__ */ jsx25("div", { className: cn23("p-6 text-center text-sm text-muted-foreground", className), children: "No missing translations" });
2934
+ return /* @__PURE__ */ jsx26("div", { className: cn23("p-6 text-center text-sm text-muted-foreground", className), children: "No missing translations" });
2823
2935
  }
2824
- return /* @__PURE__ */ jsx25("section", { "aria-label": "Missing translations", className: cn23("flex flex-col gap-3", className), children: /* @__PURE__ */ jsxs22("table", { className: "w-full text-sm", children: [
2825
- /* @__PURE__ */ jsx25("thead", { children: /* @__PURE__ */ jsxs22("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2826
- /* @__PURE__ */ jsx25("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2827
- /* @__PURE__ */ jsx25("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2828
- /* @__PURE__ */ jsx25("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" })
2936
+ return /* @__PURE__ */ jsx26("section", { "aria-label": "Missing translations", className: cn23("flex flex-col gap-3", className), children: /* @__PURE__ */ jsxs23("table", { className: "w-full text-sm", children: [
2937
+ /* @__PURE__ */ jsx26("thead", { children: /* @__PURE__ */ jsxs23("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2938
+ /* @__PURE__ */ jsx26("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2939
+ /* @__PURE__ */ jsx26("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
2940
+ /* @__PURE__ */ jsx26("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" })
2829
2941
  ] }) }),
2830
- /* @__PURE__ */ jsx25("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsxs22("tr", { className: "border-b border-border", children: [
2831
- /* @__PURE__ */ jsx25("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2832
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 text-foreground", children: row.locale }),
2833
- /* @__PURE__ */ jsx25("td", { className: "px-4 py-3 text-muted-foreground", children: row.type ?? "\u2014" })
2942
+ /* @__PURE__ */ jsx26("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsxs23("tr", { className: "border-b border-border", children: [
2943
+ /* @__PURE__ */ jsx26("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2944
+ /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-foreground", children: row.locale }),
2945
+ /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-muted-foreground", children: row.type ?? "\u2014" })
2834
2946
  ] }, `${row.key}:${row.locale}:${i}`)) })
2835
2947
  ] }) });
2836
2948
  }
@@ -2838,7 +2950,7 @@ function MissingTranslationsPanel({
2838
2950
  // src/fallback-report-panel.tsx
2839
2951
  import { cn as cn24 } from "@quanticjs/react-ui";
2840
2952
  import { useApiQuery as useApiQuery18 } from "@quanticjs/react-query";
2841
- import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
2953
+ import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
2842
2954
  function normalize4(data) {
2843
2955
  if (Array.isArray(data)) return data;
2844
2956
  return data?.entries ?? [];
@@ -2849,23 +2961,23 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2849
2961
  (client) => client.get(`${basePath}/i18n/catalog/fallback-report`)
2850
2962
  );
2851
2963
  if (isLoading) {
2852
- return /* @__PURE__ */ jsxs23(
2964
+ return /* @__PURE__ */ jsxs24(
2853
2965
  "div",
2854
2966
  {
2855
2967
  role: "status",
2856
2968
  "aria-label": "Loading fallback report",
2857
2969
  className: cn24("flex flex-col gap-2 p-4", className),
2858
2970
  children: [
2859
- /* @__PURE__ */ jsx26("span", { className: "sr-only", children: "Loading fallback report" }),
2860
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx26("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2971
+ /* @__PURE__ */ jsx27("span", { className: "sr-only", children: "Loading fallback report" }),
2972
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx27("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2861
2973
  ]
2862
2974
  }
2863
2975
  );
2864
2976
  }
2865
2977
  if (isError) {
2866
- return /* @__PURE__ */ jsxs23("div", { className: cn24("flex flex-col items-start gap-3 p-4", className), children: [
2867
- /* @__PURE__ */ jsx26("p", { className: "text-sm text-foreground", children: "Failed to load fallback report" }),
2868
- /* @__PURE__ */ jsx26(
2978
+ return /* @__PURE__ */ jsxs24("div", { className: cn24("flex flex-col items-start gap-3 p-4", className), children: [
2979
+ /* @__PURE__ */ jsx27("p", { className: "text-sm text-foreground", children: "Failed to load fallback report" }),
2980
+ /* @__PURE__ */ jsx27(
2869
2981
  "button",
2870
2982
  {
2871
2983
  type: "button",
@@ -2878,22 +2990,22 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2878
2990
  }
2879
2991
  const rows = normalize4(data);
2880
2992
  if (rows.length === 0) {
2881
- return /* @__PURE__ */ jsx26("div", { className: cn24("p-6 text-center text-sm text-muted-foreground", className), children: "No fallbacks reported" });
2993
+ return /* @__PURE__ */ jsx27("div", { className: cn24("p-6 text-center text-sm text-muted-foreground", className), children: "No fallbacks reported" });
2882
2994
  }
2883
- return /* @__PURE__ */ jsx26("section", { "aria-label": "Fallback report", className: cn24("flex flex-col gap-3", className), children: /* @__PURE__ */ jsxs23("table", { className: "w-full text-sm", children: [
2884
- /* @__PURE__ */ jsx26("thead", { children: /* @__PURE__ */ jsxs23("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2885
- /* @__PURE__ */ jsx26("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2886
- /* @__PURE__ */ jsx26("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Requested" }),
2887
- /* @__PURE__ */ jsx26("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Resolved" }),
2888
- /* @__PURE__ */ jsx26("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Chain" })
2995
+ return /* @__PURE__ */ jsx27("section", { "aria-label": "Fallback report", className: cn24("flex flex-col gap-3", className), children: /* @__PURE__ */ jsxs24("table", { className: "w-full text-sm", children: [
2996
+ /* @__PURE__ */ jsx27("thead", { children: /* @__PURE__ */ jsxs24("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
2997
+ /* @__PURE__ */ jsx27("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
2998
+ /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Requested" }),
2999
+ /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Resolved" }),
3000
+ /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Chain" })
2889
3001
  ] }) }),
2890
- /* @__PURE__ */ jsx26("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsxs23("tr", { className: "border-b border-border", children: [
2891
- /* @__PURE__ */ jsx26("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
2892
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-foreground", children: row.requestedLocale }),
2893
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-foreground", children: row.resolvedLocale }),
2894
- /* @__PURE__ */ jsx26("td", { className: "px-4 py-3 text-muted-foreground", children: row.steps && row.steps.length > 0 ? /* @__PURE__ */ jsx26("ol", { className: "flex flex-wrap items-center gap-1", children: row.steps.map((step, si) => /* @__PURE__ */ jsxs23("li", { className: "flex items-center gap-1", children: [
2895
- /* @__PURE__ */ jsx26("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-foreground", children: step }),
2896
- si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" })
3002
+ /* @__PURE__ */ jsx27("tbody", { children: rows.map((row, i) => /* @__PURE__ */ jsxs24("tr", { className: "border-b border-border", children: [
3003
+ /* @__PURE__ */ jsx27("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
3004
+ /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-foreground", children: row.requestedLocale }),
3005
+ /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-foreground", children: row.resolvedLocale }),
3006
+ /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-muted-foreground", children: row.steps && row.steps.length > 0 ? /* @__PURE__ */ jsx27("ol", { className: "flex flex-wrap items-center gap-1", children: row.steps.map((step, si) => /* @__PURE__ */ jsxs24("li", { className: "flex items-center gap-1", children: [
3007
+ /* @__PURE__ */ jsx27("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-foreground", children: step }),
3008
+ si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ jsx27("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" })
2897
3009
  ] }, `${step}:${si}`)) }) : "\u2014" })
2898
3010
  ] }, `${row.key}:${row.requestedLocale}:${i}`)) })
2899
3011
  ] }) });
@@ -2903,7 +3015,7 @@ function FallbackReportPanel({ basePath = "/api", className }) {
2903
3015
  import { useState as useState13 } from "react";
2904
3016
  import { cn as cn25, formatDateTime as formatDateTime5, useToast as useToast11 } from "@quanticjs/react-ui";
2905
3017
  import { useApiMutation as useApiMutation12, useApiQuery as useApiQuery19 } from "@quanticjs/react-query";
2906
- import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
3018
+ import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
2907
3019
  var LIMIT5 = 20;
2908
3020
  function RecipientAdminPanel({ basePath = "/api", className }) {
2909
3021
  const toast = useToast11();
@@ -2954,10 +3066,10 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
2954
3066
  if (row.consentSms) active.push("sms");
2955
3067
  return active.length > 0 ? active.join(", ") : "none";
2956
3068
  };
2957
- const searchForm = /* @__PURE__ */ jsxs24("form", { onSubmit: onSearch, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
2958
- /* @__PURE__ */ jsxs24("div", { className: "flex flex-col gap-1", children: [
2959
- /* @__PURE__ */ jsx27("label", { htmlFor: "recipient-search", className: "text-sm font-medium text-foreground", children: "Search recipients" }),
2960
- /* @__PURE__ */ jsx27(
3069
+ const searchForm = /* @__PURE__ */ jsxs25("form", { onSubmit: onSearch, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3070
+ /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-1", children: [
3071
+ /* @__PURE__ */ jsx28("label", { htmlFor: "recipient-search", className: "text-sm font-medium text-foreground", children: "Search recipients" }),
3072
+ /* @__PURE__ */ jsx28(
2961
3073
  "input",
2962
3074
  {
2963
3075
  id: "recipient-search",
@@ -2968,7 +3080,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
2968
3080
  }
2969
3081
  )
2970
3082
  ] }),
2971
- /* @__PURE__ */ jsx27(
3083
+ /* @__PURE__ */ jsx28(
2972
3084
  "button",
2973
3085
  {
2974
3086
  type: "submit",
@@ -2979,14 +3091,14 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
2979
3091
  ] });
2980
3092
  let body;
2981
3093
  if (isLoading) {
2982
- body = /* @__PURE__ */ jsxs24("div", { role: "status", "aria-label": "Loading recipients", className: "flex flex-col gap-2 p-4", children: [
2983
- /* @__PURE__ */ jsx27("span", { className: "sr-only", children: "Loading recipients" }),
2984
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx27("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3094
+ body = /* @__PURE__ */ jsxs25("div", { role: "status", "aria-label": "Loading recipients", className: "flex flex-col gap-2 p-4", children: [
3095
+ /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Loading recipients" }),
3096
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx28("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
2985
3097
  ] });
2986
3098
  } else if (isError) {
2987
- body = /* @__PURE__ */ jsxs24("div", { className: "flex flex-col items-start gap-3 p-4", children: [
2988
- /* @__PURE__ */ jsx27("p", { className: "text-sm text-foreground", children: "Failed to load recipients" }),
2989
- /* @__PURE__ */ jsx27(
3099
+ body = /* @__PURE__ */ jsxs25("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3100
+ /* @__PURE__ */ jsx28("p", { className: "text-sm text-foreground", children: "Failed to load recipients" }),
3101
+ /* @__PURE__ */ jsx28(
2990
3102
  "button",
2991
3103
  {
2992
3104
  type: "button",
@@ -3000,26 +3112,26 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3000
3112
  const rows = data?.items ?? [];
3001
3113
  const totalPages = data?.totalPages ?? 1;
3002
3114
  if (rows.length === 0) {
3003
- body = /* @__PURE__ */ jsx27("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No recipients" });
3115
+ body = /* @__PURE__ */ jsx28("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No recipients" });
3004
3116
  } else {
3005
- body = /* @__PURE__ */ jsxs24(Fragment5, { children: [
3006
- /* @__PURE__ */ jsxs24("table", { className: "w-full text-sm", children: [
3007
- /* @__PURE__ */ jsx27("thead", { children: /* @__PURE__ */ jsxs24("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3008
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "User" }),
3009
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Email" }),
3010
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Phone" }),
3011
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Consents" }),
3012
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3013
- /* @__PURE__ */ jsx27("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx27("span", { className: "sr-only", children: "Actions" }) })
3117
+ body = /* @__PURE__ */ jsxs25(Fragment5, { children: [
3118
+ /* @__PURE__ */ jsxs25("table", { className: "w-full text-sm", children: [
3119
+ /* @__PURE__ */ jsx28("thead", { children: /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3120
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "User" }),
3121
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Email" }),
3122
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Phone" }),
3123
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Consents" }),
3124
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3125
+ /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Actions" }) })
3014
3126
  ] }) }),
3015
- /* @__PURE__ */ jsx27("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs24("tr", { className: "border-b border-border", children: [
3016
- /* @__PURE__ */ jsx27("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.userId }),
3017
- /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-foreground", children: row.email ?? "\u2014" }),
3018
- /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-foreground", children: row.phone ?? "\u2014" }),
3019
- /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-muted-foreground", children: consents(row) }),
3020
- /* @__PURE__ */ jsx27("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime5(row.createdAt) }),
3021
- /* @__PURE__ */ jsx27("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2", children: [
3022
- /* @__PURE__ */ jsx27(
3127
+ /* @__PURE__ */ jsx28("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border", children: [
3128
+ /* @__PURE__ */ jsx28("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.userId }),
3129
+ /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-foreground", children: row.email ?? "\u2014" }),
3130
+ /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-foreground", children: row.phone ?? "\u2014" }),
3131
+ /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-muted-foreground", children: consents(row) }),
3132
+ /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime5(row.createdAt) }),
3133
+ /* @__PURE__ */ jsx28("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
3134
+ /* @__PURE__ */ jsx28(
3023
3135
  "button",
3024
3136
  {
3025
3137
  type: "button",
@@ -3029,7 +3141,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3029
3141
  children: "Export"
3030
3142
  }
3031
3143
  ),
3032
- /* @__PURE__ */ jsx27(
3144
+ /* @__PURE__ */ jsx28(
3033
3145
  "button",
3034
3146
  {
3035
3147
  type: "button",
@@ -3042,8 +3154,8 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3042
3154
  ] }) })
3043
3155
  ] }, row.userId)) })
3044
3156
  ] }),
3045
- /* @__PURE__ */ jsxs24("nav", { "aria-label": "Recipient pagination", className: "flex items-center justify-between", children: [
3046
- /* @__PURE__ */ jsx27(
3157
+ /* @__PURE__ */ jsxs25("nav", { "aria-label": "Recipient pagination", className: "flex items-center justify-between", children: [
3158
+ /* @__PURE__ */ jsx28(
3047
3159
  "button",
3048
3160
  {
3049
3161
  type: "button",
@@ -3053,13 +3165,13 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3053
3165
  children: "Previous"
3054
3166
  }
3055
3167
  ),
3056
- /* @__PURE__ */ jsxs24("span", { className: "text-xs text-muted-foreground", children: [
3168
+ /* @__PURE__ */ jsxs25("span", { className: "text-xs text-muted-foreground", children: [
3057
3169
  "Page ",
3058
3170
  page,
3059
3171
  " of ",
3060
3172
  totalPages
3061
3173
  ] }),
3062
- /* @__PURE__ */ jsx27(
3174
+ /* @__PURE__ */ jsx28(
3063
3175
  "button",
3064
3176
  {
3065
3177
  type: "button",
@@ -3073,7 +3185,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3073
3185
  ] });
3074
3186
  }
3075
3187
  }
3076
- return /* @__PURE__ */ jsxs24("section", { "aria-label": "Recipient administration", className: cn25("flex flex-col gap-4", className), children: [
3188
+ return /* @__PURE__ */ jsxs25("section", { "aria-label": "Recipient administration", className: cn25("flex flex-col gap-4", className), children: [
3077
3189
  searchForm,
3078
3190
  body
3079
3191
  ] });
@@ -3083,7 +3195,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
3083
3195
  import { Fragment as Fragment6, useState as useState14 } from "react";
3084
3196
  import { cn as cn26, formatDateTime as formatDateTime6, useToast as useToast12, StatusBadge as StatusBadge5 } from "@quanticjs/react-ui";
3085
3197
  import { useApiMutation as useApiMutation13, useApiQuery as useApiQuery20 } from "@quanticjs/react-query";
3086
- import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
3198
+ import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
3087
3199
  var EVENT_TYPES = [
3088
3200
  "notification.sent",
3089
3201
  "notification.delivered",
@@ -3109,15 +3221,15 @@ function WebhookDeliveries({ endpointId, basePath }) {
3109
3221
  (client) => client.get(`${basePath}/webhook-endpoints/${endpointId}/deliveries`)
3110
3222
  );
3111
3223
  if (isLoading) {
3112
- return /* @__PURE__ */ jsxs25("div", { role: "status", "aria-label": "Loading deliveries", className: "flex flex-col gap-2 p-3", children: [
3113
- /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Loading deliveries" }),
3114
- [0, 1].map((i) => /* @__PURE__ */ jsx28("div", { "aria-hidden": "true", className: "h-8 animate-pulse rounded bg-muted" }, i))
3224
+ return /* @__PURE__ */ jsxs26("div", { role: "status", "aria-label": "Loading deliveries", className: "flex flex-col gap-2 p-3", children: [
3225
+ /* @__PURE__ */ jsx29("span", { className: "sr-only", children: "Loading deliveries" }),
3226
+ [0, 1].map((i) => /* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: "h-8 animate-pulse rounded bg-muted" }, i))
3115
3227
  ] });
3116
3228
  }
3117
3229
  if (isError) {
3118
- return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col items-start gap-2 p-3", children: [
3119
- /* @__PURE__ */ jsx28("p", { className: "text-sm text-foreground", children: "Failed to load deliveries" }),
3120
- /* @__PURE__ */ jsx28(
3230
+ return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col items-start gap-2 p-3", children: [
3231
+ /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground", children: "Failed to load deliveries" }),
3232
+ /* @__PURE__ */ jsx29(
3121
3233
  "button",
3122
3234
  {
3123
3235
  type: "button",
@@ -3130,18 +3242,18 @@ function WebhookDeliveries({ endpointId, basePath }) {
3130
3242
  }
3131
3243
  const rows = normalizeDeliveries(data);
3132
3244
  if (rows.length === 0) {
3133
- return /* @__PURE__ */ jsx28("div", { className: "p-3 text-center text-sm text-muted-foreground", children: "No deliveries" });
3245
+ return /* @__PURE__ */ jsx29("div", { className: "p-3 text-center text-sm text-muted-foreground", children: "No deliveries" });
3134
3246
  }
3135
- return /* @__PURE__ */ jsxs25("table", { className: "w-full text-sm", children: [
3136
- /* @__PURE__ */ jsx28("thead", { children: /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3137
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivery" }),
3138
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3139
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3247
+ return /* @__PURE__ */ jsxs26("table", { className: "w-full text-sm", children: [
3248
+ /* @__PURE__ */ jsx29("thead", { children: /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3249
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivery" }),
3250
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3251
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3140
3252
  ] }) }),
3141
- /* @__PURE__ */ jsx28("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border", children: [
3142
- /* @__PURE__ */ jsx28("td", { className: "py-2 pe-4 font-mono text-foreground", children: row.id }),
3143
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-2 text-muted-foreground", children: row.status }),
3144
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-2 text-muted-foreground", children: formatDateTime6(row.createdAt) })
3253
+ /* @__PURE__ */ jsx29("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border", children: [
3254
+ /* @__PURE__ */ jsx29("td", { className: "py-2 pe-4 font-mono text-foreground", children: row.id }),
3255
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-2 text-muted-foreground", children: row.status }),
3256
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-2 text-muted-foreground", children: formatDateTime6(row.createdAt) })
3145
3257
  ] }, row.id)) })
3146
3258
  ] });
3147
3259
  }
@@ -3216,10 +3328,10 @@ function WebhookEndpointManager({
3216
3328
  remove.mutate(id);
3217
3329
  }
3218
3330
  };
3219
- const createForm = /* @__PURE__ */ jsxs25("form", { onSubmit: onCreate, className: "flex flex-col gap-3", noValidate: true, children: [
3220
- /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-1", children: [
3221
- /* @__PURE__ */ jsx28("label", { htmlFor: "webhook-url", className: "text-sm font-medium text-foreground", children: "Endpoint URL" }),
3222
- /* @__PURE__ */ jsx28(
3331
+ const createForm = /* @__PURE__ */ jsxs26("form", { onSubmit: onCreate, className: "flex flex-col gap-3", noValidate: true, children: [
3332
+ /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-1", children: [
3333
+ /* @__PURE__ */ jsx29("label", { htmlFor: "webhook-url", className: "text-sm font-medium text-foreground", children: "Endpoint URL" }),
3334
+ /* @__PURE__ */ jsx29(
3223
3335
  "input",
3224
3336
  {
3225
3337
  id: "webhook-url",
@@ -3231,19 +3343,19 @@ function WebhookEndpointManager({
3231
3343
  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"
3232
3344
  }
3233
3345
  ),
3234
- urlError && /* @__PURE__ */ jsx28("p", { id: "webhook-url-error", className: "text-xs text-destructive", children: urlError })
3346
+ urlError && /* @__PURE__ */ jsx29("p", { id: "webhook-url-error", className: "text-xs text-destructive", children: urlError })
3235
3347
  ] }),
3236
- /* @__PURE__ */ jsxs25("fieldset", { className: "flex flex-col gap-2", children: [
3237
- /* @__PURE__ */ jsx28("legend", { className: "text-sm font-medium text-foreground", children: "Events" }),
3238
- /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-3", children: EVENT_TYPES.map((evt) => {
3348
+ /* @__PURE__ */ jsxs26("fieldset", { className: "flex flex-col gap-2", children: [
3349
+ /* @__PURE__ */ jsx29("legend", { className: "text-sm font-medium text-foreground", children: "Events" }),
3350
+ /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap gap-3", children: EVENT_TYPES.map((evt) => {
3239
3351
  const id = `webhook-event-${evt}`;
3240
- return /* @__PURE__ */ jsxs25(
3352
+ return /* @__PURE__ */ jsxs26(
3241
3353
  "label",
3242
3354
  {
3243
3355
  htmlFor: id,
3244
3356
  className: "flex items-center gap-2 text-sm text-foreground",
3245
3357
  children: [
3246
- /* @__PURE__ */ jsx28(
3358
+ /* @__PURE__ */ jsx29(
3247
3359
  "input",
3248
3360
  {
3249
3361
  id,
@@ -3260,8 +3372,8 @@ function WebhookEndpointManager({
3260
3372
  );
3261
3373
  }) })
3262
3374
  ] }),
3263
- /* @__PURE__ */ jsxs25("label", { htmlFor: "webhook-active", className: "flex items-center gap-2 text-sm text-foreground", children: [
3264
- /* @__PURE__ */ jsx28(
3375
+ /* @__PURE__ */ jsxs26("label", { htmlFor: "webhook-active", className: "flex items-center gap-2 text-sm text-foreground", children: [
3376
+ /* @__PURE__ */ jsx29(
3265
3377
  "input",
3266
3378
  {
3267
3379
  id: "webhook-active",
@@ -3273,7 +3385,7 @@ function WebhookEndpointManager({
3273
3385
  ),
3274
3386
  "Active"
3275
3387
  ] }),
3276
- /* @__PURE__ */ jsx28("div", { children: /* @__PURE__ */ jsx28(
3388
+ /* @__PURE__ */ jsx29("div", { children: /* @__PURE__ */ jsx29(
3277
3389
  "button",
3278
3390
  {
3279
3391
  type: "submit",
@@ -3285,14 +3397,14 @@ function WebhookEndpointManager({
3285
3397
  ] });
3286
3398
  let body;
3287
3399
  if (isLoading) {
3288
- body = /* @__PURE__ */ jsxs25("div", { role: "status", "aria-label": "Loading webhook endpoints", className: "flex flex-col gap-2 p-4", children: [
3289
- /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Loading webhook endpoints" }),
3290
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx28("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3400
+ body = /* @__PURE__ */ jsxs26("div", { role: "status", "aria-label": "Loading webhook endpoints", className: "flex flex-col gap-2 p-4", children: [
3401
+ /* @__PURE__ */ jsx29("span", { className: "sr-only", children: "Loading webhook endpoints" }),
3402
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3291
3403
  ] });
3292
3404
  } else if (isError) {
3293
- body = /* @__PURE__ */ jsxs25("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3294
- /* @__PURE__ */ jsx28("p", { className: "text-sm text-foreground", children: "Failed to load webhook endpoints" }),
3295
- /* @__PURE__ */ jsx28(
3405
+ body = /* @__PURE__ */ jsxs26("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3406
+ /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground", children: "Failed to load webhook endpoints" }),
3407
+ /* @__PURE__ */ jsx29(
3296
3408
  "button",
3297
3409
  {
3298
3410
  type: "button",
@@ -3305,26 +3417,26 @@ function WebhookEndpointManager({
3305
3417
  } else {
3306
3418
  const rows = normalizeEndpoints(data);
3307
3419
  if (rows.length === 0) {
3308
- body = /* @__PURE__ */ jsx28("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No webhook endpoints" });
3420
+ body = /* @__PURE__ */ jsx29("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No webhook endpoints" });
3309
3421
  } else {
3310
- body = /* @__PURE__ */ jsxs25("table", { className: "w-full text-sm", children: [
3311
- /* @__PURE__ */ jsx28("thead", { children: /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3312
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "URL" }),
3313
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Events" }),
3314
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Active" }),
3315
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3316
- /* @__PURE__ */ jsx28("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Actions" }) })
3422
+ body = /* @__PURE__ */ jsxs26("table", { className: "w-full text-sm", children: [
3423
+ /* @__PURE__ */ jsx29("thead", { children: /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3424
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "URL" }),
3425
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Events" }),
3426
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Active" }),
3427
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
3428
+ /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx29("span", { className: "sr-only", children: "Actions" }) })
3317
3429
  ] }) }),
3318
- /* @__PURE__ */ jsx28("tbody", { children: rows.map((row) => {
3430
+ /* @__PURE__ */ jsx29("tbody", { children: rows.map((row) => {
3319
3431
  const isExpanded = expandedId === row.id;
3320
- return /* @__PURE__ */ jsxs25(Fragment6, { children: [
3321
- /* @__PURE__ */ jsxs25("tr", { className: "border-b border-border", children: [
3322
- /* @__PURE__ */ jsx28("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.url }),
3323
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-muted-foreground", children: row.events.length > 0 ? row.events.join(", ") : "\u2014" }),
3324
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx28(StatusBadge5, { variant: row.active ? "success" : "neutral", appearance: "dot", children: row.active ? "active" : "inactive" }) }),
3325
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime6(row.createdAt) }),
3326
- /* @__PURE__ */ jsx28("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
3327
- /* @__PURE__ */ jsx28(
3432
+ return /* @__PURE__ */ jsxs26(Fragment6, { children: [
3433
+ /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border", children: [
3434
+ /* @__PURE__ */ jsx29("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.url }),
3435
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-muted-foreground", children: row.events.length > 0 ? row.events.join(", ") : "\u2014" }),
3436
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx29(StatusBadge5, { variant: row.active ? "success" : "neutral", appearance: "dot", children: row.active ? "active" : "inactive" }) }),
3437
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime6(row.createdAt) }),
3438
+ /* @__PURE__ */ jsx29("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
3439
+ /* @__PURE__ */ jsx29(
3328
3440
  "button",
3329
3441
  {
3330
3442
  type: "button",
@@ -3334,7 +3446,7 @@ function WebhookEndpointManager({
3334
3446
  children: row.active ? "Disable" : "Enable"
3335
3447
  }
3336
3448
  ),
3337
- /* @__PURE__ */ jsx28(
3449
+ /* @__PURE__ */ jsx29(
3338
3450
  "button",
3339
3451
  {
3340
3452
  type: "button",
@@ -3344,7 +3456,7 @@ function WebhookEndpointManager({
3344
3456
  children: "Deliveries"
3345
3457
  }
3346
3458
  ),
3347
- /* @__PURE__ */ jsx28(
3459
+ /* @__PURE__ */ jsx29(
3348
3460
  "button",
3349
3461
  {
3350
3462
  type: "button",
@@ -3356,57 +3468,65 @@ function WebhookEndpointManager({
3356
3468
  )
3357
3469
  ] }) })
3358
3470
  ] }),
3359
- isExpanded && /* @__PURE__ */ jsx28("tr", { className: "border-b border-border", children: /* @__PURE__ */ jsx28("td", { colSpan: 5, className: "bg-muted px-4 py-3", children: /* @__PURE__ */ jsx28(WebhookDeliveries, { endpointId: row.id, basePath }) }) })
3471
+ isExpanded && /* @__PURE__ */ jsx29("tr", { className: "border-b border-border", children: /* @__PURE__ */ jsx29("td", { colSpan: 5, className: "bg-muted px-4 py-3", children: /* @__PURE__ */ jsx29(WebhookDeliveries, { endpointId: row.id, basePath }) }) })
3360
3472
  ] }, row.id);
3361
3473
  }) })
3362
3474
  ] });
3363
3475
  }
3364
3476
  }
3365
- return /* @__PURE__ */ jsxs25("section", { "aria-label": "Webhook endpoints", className: cn26("flex flex-col gap-4", className), children: [
3477
+ return /* @__PURE__ */ jsxs26("section", { "aria-label": "Webhook endpoints", className: cn26("flex flex-col gap-4", className), children: [
3366
3478
  createForm,
3367
3479
  body
3368
3480
  ] });
3369
3481
  }
3370
3482
 
3371
3483
  // src/operations-overview.tsx
3372
- import { cn as cn27, formatDateTime as formatDateTime7, StatusBadge as StatusBadge6 } from "@quanticjs/react-ui";
3484
+ import {
3485
+ Button as Button6,
3486
+ Card as Card5,
3487
+ CardContent,
3488
+ CardHeader,
3489
+ CardTitle,
3490
+ EmptyState as EmptyState5,
3491
+ Skeleton as Skeleton4,
3492
+ StatCard,
3493
+ StatusBadge as StatusBadge6,
3494
+ cn as cn27,
3495
+ formatDateTime as formatDateTime7
3496
+ } from "@quanticjs/react-ui";
3373
3497
  import { useApiQuery as useApiQuery21 } from "@quanticjs/react-query";
3374
- import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
3498
+ import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
3375
3499
  function OperationsOverview({ basePath = "/api", className }) {
3376
3500
  const { data, isLoading, isError, refetch } = useApiQuery21(
3377
3501
  ["operations-overview"],
3378
3502
  (client) => client.get(`${basePath}/v1/admin/overview`)
3379
3503
  );
3380
3504
  if (isLoading) {
3381
- return /* @__PURE__ */ jsxs26(
3505
+ return /* @__PURE__ */ jsxs27(
3382
3506
  "div",
3383
3507
  {
3384
3508
  role: "status",
3385
3509
  "aria-label": "Loading operations overview",
3386
- className: cn27("flex flex-col gap-2 p-4", className),
3510
+ className: cn27("flex flex-col gap-4", className),
3387
3511
  children: [
3388
- /* @__PURE__ */ jsx29("span", { className: "sr-only", children: "Loading operations overview" }),
3389
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: "h-20 animate-pulse rounded bg-muted" }, i))
3512
+ /* @__PURE__ */ jsx30("span", { className: "sr-only", children: "Loading operations overview" }),
3513
+ /* @__PURE__ */ jsx30("div", { "aria-hidden": "true", className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx30(Card5, { children: /* @__PURE__ */ jsxs27(CardContent, { className: "space-y-3 p-5", children: [
3514
+ /* @__PURE__ */ jsx30(Skeleton4, { className: "h-3 w-24" }),
3515
+ /* @__PURE__ */ jsx30(Skeleton4, { className: "h-7 w-28" })
3516
+ ] }) }, i)) }),
3517
+ /* @__PURE__ */ jsx30(Card5, { "aria-hidden": "true", children: /* @__PURE__ */ jsx30(CardContent, { className: "space-y-2 p-5", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx30(Skeleton4, { className: "h-10 w-full" }, i)) }) })
3390
3518
  ]
3391
3519
  }
3392
3520
  );
3393
3521
  }
3394
3522
  if (isError) {
3395
- return /* @__PURE__ */ jsxs26("div", { className: cn27("flex flex-col items-start gap-3 p-4", className), children: [
3396
- /* @__PURE__ */ jsx29("p", { className: "text-sm text-foreground", children: "Failed to load operations overview" }),
3397
- /* @__PURE__ */ jsx29(
3398
- "button",
3399
- {
3400
- type: "button",
3401
- onClick: () => void refetch(),
3402
- 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",
3403
- children: "Try again"
3404
- }
3405
- )
3406
- ] });
3523
+ return /* @__PURE__ */ jsx30(Card5, { className, children: /* @__PURE__ */ jsxs27(CardContent, { className: "flex flex-col items-center gap-3 p-8 text-center", children: [
3524
+ /* @__PURE__ */ jsx30("p", { className: "text-sm text-foreground", children: "Failed to load operations overview" }),
3525
+ /* @__PURE__ */ jsx30(Button6, { type: "button", onClick: () => void refetch(), children: "Try again" })
3526
+ ] }) });
3407
3527
  }
3408
3528
  if (!data) {
3409
- return /* @__PURE__ */ jsx29("div", { className: cn27("p-6 text-center text-sm text-muted-foreground", className), children: "No overview data" });
3529
+ return /* @__PURE__ */ jsx30(EmptyState5, { className, title: "No overview data" });
3410
3530
  }
3411
3531
  const windowHours = data.windowHours;
3412
3532
  const channels = data.channels ?? [];
@@ -3416,51 +3536,68 @@ function OperationsOverview({ basePath = "/api", className }) {
3416
3536
  { label: `Delivered (${windowHours}h)`, value: data.totalDelivered },
3417
3537
  { label: `Failed (${windowHours}h)`, value: data.totalFailed }
3418
3538
  ];
3419
- return /* @__PURE__ */ jsxs26("section", { "aria-label": "Operations overview", className: cn27("flex flex-col gap-4", className), children: [
3420
- /* @__PURE__ */ jsx29("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: cards.map((card) => /* @__PURE__ */ jsxs26("div", { className: "rounded-md border border-border bg-card p-4", children: [
3421
- /* @__PURE__ */ jsx29("p", { className: "text-xs font-medium text-muted-foreground", children: card.label }),
3422
- /* @__PURE__ */ jsx29("p", { className: "mt-1 text-2xl font-semibold text-foreground", children: card.value })
3423
- ] }, card.label)) }),
3424
- /* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-4", children: [
3425
- /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
3426
- /* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
3427
- /* @__PURE__ */ jsx29(StatusBadge6, { variant: dlqPending > 0 ? "destructive" : "success", children: dlqPending })
3428
- ] }),
3429
- /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
3430
- /* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
3431
- /* @__PURE__ */ jsx29("span", { className: "text-sm font-medium text-foreground", children: data.broadcastsInFlight })
3539
+ return /* @__PURE__ */ jsxs27("section", { "aria-label": "Operations overview", className: cn27("flex flex-col gap-5", className), children: [
3540
+ /* @__PURE__ */ jsxs27("div", { className: "flex flex-wrap items-baseline justify-between gap-2", children: [
3541
+ /* @__PURE__ */ jsxs27("div", { children: [
3542
+ /* @__PURE__ */ jsx30("h2", { className: "text-base font-semibold tracking-tight text-foreground", children: "Operations overview" }),
3543
+ /* @__PURE__ */ jsxs27("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
3544
+ "Last ",
3545
+ windowHours,
3546
+ "h"
3547
+ ] })
3432
3548
  ] }),
3433
- /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
3434
- /* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
3435
- /* @__PURE__ */ jsx29(StatusBadge6, { variant: data.queueHealthy ? "success" : "destructive", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
3549
+ /* @__PURE__ */ jsxs27("p", { className: "text-xs text-muted-foreground", children: [
3550
+ "Generated ",
3551
+ formatDateTime7(data.generatedAt)
3436
3552
  ] })
3437
3553
  ] }),
3438
- /* @__PURE__ */ jsxs26("table", { className: "w-full text-sm", children: [
3439
- /* @__PURE__ */ jsx29("thead", { children: /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3440
- /* @__PURE__ */ jsx29("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
3441
- /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Sends" }),
3442
- /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Delivered" }),
3443
- /* @__PURE__ */ jsx29("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failed" })
3444
- ] }) }),
3445
- /* @__PURE__ */ jsx29("tbody", { children: channels.length === 0 ? /* @__PURE__ */ jsx29("tr", { className: "border-b border-border", children: /* @__PURE__ */ jsx29("td", { colSpan: 4, className: "py-3 text-center text-muted-foreground", children: "No channel activity" }) }) : channels.map((row) => /* @__PURE__ */ jsxs26("tr", { className: "border-b border-border", children: [
3446
- /* @__PURE__ */ jsx29("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
3447
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-muted-foreground", children: row.sends }),
3448
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-muted-foreground", children: row.delivered }),
3449
- /* @__PURE__ */ jsx29("td", { className: "px-4 py-3 text-muted-foreground", children: row.failed })
3450
- ] }, row.channel)) })
3451
- ] }),
3452
- /* @__PURE__ */ jsxs26("p", { className: "text-xs text-muted-foreground", children: [
3453
- "Generated ",
3454
- formatDateTime7(data.generatedAt)
3554
+ /* @__PURE__ */ jsx30("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: cards.map((card) => /* @__PURE__ */ jsx30(StatCard, { label: card.label, value: card.value }, card.label)) }),
3555
+ /* @__PURE__ */ jsx30(Card5, { children: /* @__PURE__ */ jsxs27(CardContent, { className: "flex flex-wrap items-center gap-x-8 gap-y-3 p-5", children: [
3556
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3557
+ /* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
3558
+ /* @__PURE__ */ jsx30(StatusBadge6, { variant: dlqPending > 0 ? "destructive" : "success", appearance: "solid", children: dlqPending })
3559
+ ] }),
3560
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3561
+ /* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
3562
+ /* @__PURE__ */ jsx30("span", { className: "text-sm font-semibold tabular-nums text-foreground", children: data.broadcastsInFlight })
3563
+ ] }),
3564
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3565
+ /* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
3566
+ /* @__PURE__ */ jsx30(StatusBadge6, { variant: data.queueHealthy ? "success" : "destructive", appearance: "solid", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
3567
+ ] })
3568
+ ] }) }),
3569
+ /* @__PURE__ */ jsxs27(Card5, { children: [
3570
+ /* @__PURE__ */ jsx30(CardHeader, { children: /* @__PURE__ */ jsx30(CardTitle, { children: "Channel breakdown" }) }),
3571
+ /* @__PURE__ */ jsx30(CardContent, { className: "p-0", children: /* @__PURE__ */ jsxs27("table", { className: "w-full text-sm", children: [
3572
+ /* @__PURE__ */ jsx30("thead", { children: /* @__PURE__ */ jsxs27("tr", { className: "border-b border-border text-start text-xs uppercase tracking-wide text-muted-foreground", children: [
3573
+ /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-5 py-2.5 font-medium", children: "Channel" }),
3574
+ /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Sends" }),
3575
+ /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Delivered" }),
3576
+ /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2.5 text-end font-medium", children: "Failed" })
3577
+ ] }) }),
3578
+ /* @__PURE__ */ jsx30("tbody", { children: channels.length === 0 ? /* @__PURE__ */ jsx30("tr", { children: /* @__PURE__ */ jsx30("td", { colSpan: 4, className: "px-5 py-8 text-center text-muted-foreground", children: "No channel activity" }) }) : channels.map((row) => /* @__PURE__ */ jsxs27(
3579
+ "tr",
3580
+ {
3581
+ className: "border-b border-border last:border-0 hover:bg-muted/40",
3582
+ children: [
3583
+ /* @__PURE__ */ jsx30("td", { className: "px-5 py-3 font-medium text-foreground", children: row.channel }),
3584
+ /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.sends }),
3585
+ /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.delivered }),
3586
+ /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-end tabular-nums text-muted-foreground", children: row.failed })
3587
+ ]
3588
+ },
3589
+ row.channel
3590
+ )) })
3591
+ ] }) })
3455
3592
  ] })
3456
3593
  ] });
3457
3594
  }
3458
3595
 
3459
3596
  // src/delivery-log-explorer.tsx
3460
3597
  import { useState as useState15 } from "react";
3461
- import { cn as cn28, formatDateTime as formatDateTime8, StatusBadge as StatusBadge7 } from "@quanticjs/react-ui";
3598
+ import { Button as Button7, Card as Card6, EmptyState as EmptyState6, StatusBadge as StatusBadge7, cn as cn28, formatDateTime as formatDateTime8 } from "@quanticjs/react-ui";
3462
3599
  import { useApiQuery as useApiQuery22 } from "@quanticjs/react-query";
3463
- import { Fragment as Fragment7, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
3600
+ import { Fragment as Fragment7, jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
3464
3601
  var LIMIT6 = 20;
3465
3602
  var EMPTY_FILTERS = {
3466
3603
  channel: "",
@@ -3513,178 +3650,90 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
3513
3650
  });
3514
3651
  };
3515
3652
  const setField = (key, value) => setDraft((prev) => ({ ...prev, [key]: value }));
3516
- const filterForm = /* @__PURE__ */ jsxs27("form", { onSubmit: onApply, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3517
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3518
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
3519
- /* @__PURE__ */ jsxs27(
3520
- "select",
3521
- {
3522
- id: "dle-channel",
3523
- value: draft.channel,
3524
- onChange: (e) => setField("channel", e.target.value),
3525
- 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",
3526
- children: [
3527
- /* @__PURE__ */ jsx30("option", { value: "", children: "All" }),
3528
- /* @__PURE__ */ jsx30("option", { value: "email", children: "Email" }),
3529
- /* @__PURE__ */ jsx30("option", { value: "sms", children: "SMS" })
3530
- ]
3531
- }
3532
- )
3533
- ] }),
3534
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3535
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-status", className: "text-sm font-medium text-foreground", children: "Status" }),
3536
- /* @__PURE__ */ jsx30(
3537
- "input",
3538
- {
3539
- id: "dle-status",
3540
- type: "text",
3541
- value: draft.status,
3542
- onChange: (e) => setField("status", e.target.value),
3543
- 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"
3544
- }
3545
- )
3546
- ] }),
3547
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3548
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-recipient", className: "text-sm font-medium text-foreground", children: "Recipient" }),
3549
- /* @__PURE__ */ jsx30(
3550
- "input",
3551
- {
3552
- id: "dle-recipient",
3553
- type: "text",
3554
- value: draft.recipient,
3555
- onChange: (e) => setField("recipient", e.target.value),
3556
- 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"
3557
- }
3558
- )
3559
- ] }),
3560
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3561
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-user", className: "text-sm font-medium text-foreground", children: "User ID" }),
3562
- /* @__PURE__ */ jsx30(
3563
- "input",
3564
- {
3565
- id: "dle-user",
3566
- type: "text",
3567
- value: draft.userId,
3568
- onChange: (e) => setField("userId", e.target.value),
3569
- 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"
3570
- }
3571
- )
3572
- ] }),
3573
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3574
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-from", className: "text-sm font-medium text-foreground", children: "From" }),
3575
- /* @__PURE__ */ jsx30(
3576
- "input",
3577
- {
3578
- id: "dle-from",
3579
- type: "date",
3580
- value: draft.from,
3581
- onChange: (e) => setField("from", e.target.value),
3582
- 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"
3583
- }
3584
- )
3585
- ] }),
3586
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-1", children: [
3587
- /* @__PURE__ */ jsx30("label", { htmlFor: "dle-to", className: "text-sm font-medium text-foreground", children: "To" }),
3588
- /* @__PURE__ */ jsx30(
3589
- "input",
3590
- {
3591
- id: "dle-to",
3592
- type: "date",
3593
- value: draft.to,
3594
- onChange: (e) => setField("to", e.target.value),
3595
- 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"
3596
- }
3597
- )
3598
- ] }),
3599
- /* @__PURE__ */ jsx30(
3600
- "button",
3601
- {
3602
- type: "submit",
3603
- 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",
3604
- children: "Apply"
3605
- }
3606
- )
3607
- ] });
3608
- let body;
3609
- if (isLoading) {
3610
- body = /* @__PURE__ */ jsxs27("div", { role: "status", "aria-label": "Loading delivery logs", className: "flex flex-col gap-2 p-4", children: [
3611
- /* @__PURE__ */ jsx30("span", { className: "sr-only", children: "Loading delivery logs" }),
3612
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx30("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3613
- ] });
3614
- } else if (isError) {
3615
- body = /* @__PURE__ */ jsxs27("div", { className: "flex flex-col items-start gap-3 p-4", children: [
3616
- /* @__PURE__ */ jsx30("p", { className: "text-sm text-foreground", children: "Failed to load delivery logs" }),
3617
- /* @__PURE__ */ jsx30(
3618
- "button",
3619
- {
3620
- type: "button",
3621
- onClick: () => void refetch(),
3622
- 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",
3623
- children: "Try again"
3624
- }
3625
- )
3626
- ] });
3627
- } else {
3628
- const rows = data?.items ?? [];
3629
- const totalPages = data?.totalPages ?? 1;
3630
- if (rows.length === 0) {
3631
- body = /* @__PURE__ */ jsx30("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No delivery logs" });
3632
- } else {
3633
- body = /* @__PURE__ */ jsxs27(Fragment7, { children: [
3634
- /* @__PURE__ */ jsxs27("table", { className: "w-full text-sm", children: [
3635
- /* @__PURE__ */ jsx30("thead", { children: /* @__PURE__ */ jsxs27("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3636
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
3637
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipient" }),
3638
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "User" }),
3639
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3640
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Provider" }),
3641
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
3642
- /* @__PURE__ */ jsx30("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3653
+ const rows = data?.items ?? [];
3654
+ const totalPages = data?.totalPages ?? 1;
3655
+ return /* @__PURE__ */ jsxs28("section", { "aria-label": "Delivery log explorer", className: cn28("flex flex-col gap-4", className), children: [
3656
+ /* @__PURE__ */ jsx31(Card6, { children: /* @__PURE__ */ jsxs28("form", { onSubmit: onApply, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
3657
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-channel", className: "flex flex-col gap-1 text-sm", children: [
3658
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "Channel" }),
3659
+ /* @__PURE__ */ jsxs28(
3660
+ "select",
3661
+ {
3662
+ id: "dle-channel",
3663
+ value: draft.channel,
3664
+ onChange: (e) => setField("channel", e.target.value),
3665
+ className: FIELD_CLASS,
3666
+ children: [
3667
+ /* @__PURE__ */ jsx31("option", { value: "", children: "All" }),
3668
+ /* @__PURE__ */ jsx31("option", { value: "email", children: "Email" }),
3669
+ /* @__PURE__ */ jsx31("option", { value: "sms", children: "SMS" })
3670
+ ]
3671
+ }
3672
+ )
3673
+ ] }),
3674
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-status", className: "flex flex-col gap-1 text-sm", children: [
3675
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "Status" }),
3676
+ /* @__PURE__ */ jsx31("input", { id: "dle-status", type: "text", value: draft.status, onChange: (e) => setField("status", e.target.value), className: FIELD_CLASS })
3677
+ ] }),
3678
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-recipient", className: "flex flex-col gap-1 text-sm", children: [
3679
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "Recipient" }),
3680
+ /* @__PURE__ */ jsx31("input", { id: "dle-recipient", type: "text", value: draft.recipient, onChange: (e) => setField("recipient", e.target.value), className: FIELD_CLASS })
3681
+ ] }),
3682
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-user", className: "flex flex-col gap-1 text-sm", children: [
3683
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "User ID" }),
3684
+ /* @__PURE__ */ jsx31("input", { id: "dle-user", type: "text", value: draft.userId, onChange: (e) => setField("userId", e.target.value), className: FIELD_CLASS })
3685
+ ] }),
3686
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-from", className: "flex flex-col gap-1 text-sm", children: [
3687
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "From" }),
3688
+ /* @__PURE__ */ jsx31("input", { id: "dle-from", type: "date", value: draft.from, onChange: (e) => setField("from", e.target.value), className: FIELD_CLASS })
3689
+ ] }),
3690
+ /* @__PURE__ */ jsxs28("label", { htmlFor: "dle-to", className: "flex flex-col gap-1 text-sm", children: [
3691
+ /* @__PURE__ */ jsx31("span", { className: "font-medium text-foreground", children: "To" }),
3692
+ /* @__PURE__ */ jsx31("input", { id: "dle-to", type: "date", value: draft.to, onChange: (e) => setField("to", e.target.value), className: FIELD_CLASS })
3693
+ ] }),
3694
+ /* @__PURE__ */ jsx31(Button7, { type: "submit", className: "ms-auto", children: "Apply" })
3695
+ ] }) }),
3696
+ /* @__PURE__ */ jsxs28(Card6, { children: [
3697
+ /* @__PURE__ */ jsx31(PanelHeader, { title: "Attempts", subtitle: !isLoading && !isError ? `${data?.total ?? rows.length} matching entries` : void 0 }),
3698
+ isLoading ? /* @__PURE__ */ jsx31(SkeletonRows, { label: "Loading delivery logs" }) : isError ? /* @__PURE__ */ jsx31(ErrorPanel, { message: "Failed to load delivery logs", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ jsx31(EmptyState6, { icon: /* @__PURE__ */ jsx31(InboxIcon, {}), title: "No delivery logs", description: "No attempts match the current filters." }) : /* @__PURE__ */ jsxs28(Fragment7, { children: [
3699
+ /* @__PURE__ */ jsxs28("table", { className: "w-full text-sm", children: [
3700
+ /* @__PURE__ */ jsx31("thead", { children: /* @__PURE__ */ jsxs28("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3701
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channel" }),
3702
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipient" }),
3703
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "User" }),
3704
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
3705
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Provider" }),
3706
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
3707
+ /* @__PURE__ */ jsx31("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
3643
3708
  ] }) }),
3644
- /* @__PURE__ */ jsx30("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs27("tr", { className: "border-b border-border", children: [
3645
- /* @__PURE__ */ jsx30("td", { className: "py-3 pe-4", children: /* @__PURE__ */ jsx30(StatusBadge7, { variant: channelVariant(row.channel), appearance: "dot", children: row.channel }) }),
3646
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
3647
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
3648
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-foreground", children: row.status }),
3649
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
3650
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-muted-foreground", children: row.attempts }),
3651
- /* @__PURE__ */ jsx30("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime8(row.createdAt) })
3709
+ /* @__PURE__ */ jsx31("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs28("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
3710
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx31(StatusBadge7, { variant: channelVariant(row.channel), appearance: "dot", children: row.channel }) }),
3711
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
3712
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
3713
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 text-foreground", children: row.status }),
3714
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
3715
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 tabular-nums text-muted-foreground", children: row.attempts }),
3716
+ /* @__PURE__ */ jsx31("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime8(row.createdAt) })
3652
3717
  ] }, row.id)) })
3653
3718
  ] }),
3654
- /* @__PURE__ */ jsxs27("nav", { "aria-label": "Delivery log pagination", className: "flex items-center justify-between", children: [
3655
- /* @__PURE__ */ jsx30(
3656
- "button",
3657
- {
3658
- type: "button",
3659
- onClick: () => setPage((p) => Math.max(1, p - 1)),
3660
- disabled: page <= 1,
3661
- 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",
3662
- children: "Previous"
3663
- }
3664
- ),
3665
- /* @__PURE__ */ jsxs27("span", { className: "text-xs text-muted-foreground", children: [
3666
- "Page ",
3719
+ /* @__PURE__ */ jsx31(
3720
+ Pager,
3721
+ {
3722
+ label: "Delivery log pagination",
3667
3723
  page,
3668
- " of ",
3669
- totalPages
3670
- ] }),
3671
- /* @__PURE__ */ jsx30(
3672
- "button",
3673
- {
3674
- type: "button",
3675
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
3676
- disabled: page >= totalPages,
3677
- 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",
3678
- children: "Next"
3679
- }
3680
- )
3681
- ] })
3682
- ] });
3683
- }
3684
- }
3685
- return /* @__PURE__ */ jsxs27("section", { "aria-label": "Delivery log explorer", className: cn28("flex flex-col gap-4", className), children: [
3686
- filterForm,
3687
- body
3724
+ totalPages,
3725
+ onPrev: () => setPage((p) => Math.max(1, p - 1)),
3726
+ onNext: () => setPage((p) => Math.min(totalPages, p + 1))
3727
+ }
3728
+ )
3729
+ ] })
3730
+ ] })
3731
+ ] });
3732
+ }
3733
+ function InboxIcon() {
3734
+ return /* @__PURE__ */ jsxs28("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
3735
+ /* @__PURE__ */ jsx31("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }),
3736
+ /* @__PURE__ */ jsx31("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" })
3688
3737
  ] });
3689
3738
  }
3690
3739
 
@@ -3692,7 +3741,7 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
3692
3741
  import { useEffect as useEffect6, useState as useState16 } from "react";
3693
3742
  import { cn as cn29, useToast as useToast13 } from "@quanticjs/react-ui";
3694
3743
  import { useApiMutation as useApiMutation14, useApiQuery as useApiQuery23 } from "@quanticjs/react-query";
3695
- import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
3744
+ import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
3696
3745
  var DEFAULTS = {
3697
3746
  enabled: false,
3698
3747
  start: "22:00",
@@ -3733,23 +3782,23 @@ function QuietHoursForm({ basePath = "/api", className }) {
3733
3782
  save.mutate(form);
3734
3783
  };
3735
3784
  if (isLoading) {
3736
- return /* @__PURE__ */ jsxs28(
3785
+ return /* @__PURE__ */ jsxs29(
3737
3786
  "div",
3738
3787
  {
3739
3788
  role: "status",
3740
3789
  "aria-label": "Loading quiet hours",
3741
3790
  className: cn29("flex flex-col gap-2 p-4", className),
3742
3791
  children: [
3743
- /* @__PURE__ */ jsx31("span", { className: "sr-only", children: "Loading quiet hours" }),
3744
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx31("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3792
+ /* @__PURE__ */ jsx32("span", { className: "sr-only", children: "Loading quiet hours" }),
3793
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx32("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3745
3794
  ]
3746
3795
  }
3747
3796
  );
3748
3797
  }
3749
3798
  if (isError) {
3750
- return /* @__PURE__ */ jsxs28("div", { className: cn29("flex flex-col items-start gap-3 p-4", className), children: [
3751
- /* @__PURE__ */ jsx31("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
3752
- /* @__PURE__ */ jsx31(
3799
+ return /* @__PURE__ */ jsxs29("div", { className: cn29("flex flex-col items-start gap-3 p-4", className), children: [
3800
+ /* @__PURE__ */ jsx32("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
3801
+ /* @__PURE__ */ jsx32(
3753
3802
  "button",
3754
3803
  {
3755
3804
  type: "button",
@@ -3760,10 +3809,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
3760
3809
  )
3761
3810
  ] });
3762
3811
  }
3763
- return /* @__PURE__ */ jsxs28("form", { onSubmit, className: cn29("flex flex-col gap-4", className), noValidate: true, children: [
3764
- /* @__PURE__ */ jsx31("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
3765
- /* @__PURE__ */ jsxs28("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
3766
- /* @__PURE__ */ jsx31(
3812
+ return /* @__PURE__ */ jsxs29("form", { onSubmit, className: cn29("flex flex-col gap-4", className), noValidate: true, children: [
3813
+ /* @__PURE__ */ jsx32("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
3814
+ /* @__PURE__ */ jsxs29("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
3815
+ /* @__PURE__ */ jsx32(
3767
3816
  "input",
3768
3817
  {
3769
3818
  type: "checkbox",
@@ -3774,10 +3823,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
3774
3823
  ),
3775
3824
  "Enable quiet hours"
3776
3825
  ] }),
3777
- /* @__PURE__ */ jsxs28("div", { className: "flex flex-wrap gap-4", children: [
3778
- /* @__PURE__ */ jsxs28("div", { className: "flex flex-col gap-1", children: [
3779
- /* @__PURE__ */ jsx31("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
3780
- /* @__PURE__ */ jsx31(
3826
+ /* @__PURE__ */ jsxs29("div", { className: "flex flex-wrap gap-4", children: [
3827
+ /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-1", children: [
3828
+ /* @__PURE__ */ jsx32("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
3829
+ /* @__PURE__ */ jsx32(
3781
3830
  "input",
3782
3831
  {
3783
3832
  id: "qh-start",
@@ -3788,9 +3837,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
3788
3837
  }
3789
3838
  )
3790
3839
  ] }),
3791
- /* @__PURE__ */ jsxs28("div", { className: "flex flex-col gap-1", children: [
3792
- /* @__PURE__ */ jsx31("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
3793
- /* @__PURE__ */ jsx31(
3840
+ /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-1", children: [
3841
+ /* @__PURE__ */ jsx32("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
3842
+ /* @__PURE__ */ jsx32(
3794
3843
  "input",
3795
3844
  {
3796
3845
  id: "qh-end",
@@ -3801,9 +3850,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
3801
3850
  }
3802
3851
  )
3803
3852
  ] }),
3804
- /* @__PURE__ */ jsxs28("div", { className: "flex flex-col gap-1", children: [
3805
- /* @__PURE__ */ jsx31("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
3806
- /* @__PURE__ */ jsx31(
3853
+ /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-1", children: [
3854
+ /* @__PURE__ */ jsx32("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
3855
+ /* @__PURE__ */ jsx32(
3807
3856
  "input",
3808
3857
  {
3809
3858
  id: "qh-tz",
@@ -3815,7 +3864,7 @@ function QuietHoursForm({ basePath = "/api", className }) {
3815
3864
  )
3816
3865
  ] })
3817
3866
  ] }),
3818
- /* @__PURE__ */ jsx31("div", { children: /* @__PURE__ */ jsx31(
3867
+ /* @__PURE__ */ jsx32("div", { children: /* @__PURE__ */ jsx32(
3819
3868
  "button",
3820
3869
  {
3821
3870
  type: "submit",
@@ -3831,7 +3880,7 @@ function QuietHoursForm({ basePath = "/api", className }) {
3831
3880
  import { useEffect as useEffect7, useState as useState17 } from "react";
3832
3881
  import { cn as cn30, useToast as useToast14 } from "@quanticjs/react-ui";
3833
3882
  import { useApiMutation as useApiMutation15, useApiQuery as useApiQuery24 } from "@quanticjs/react-query";
3834
- import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
3883
+ import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
3835
3884
  function normalize6(raw) {
3836
3885
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.caps) ? raw.caps : [];
3837
3886
  return list.map((entry) => {
@@ -3876,23 +3925,23 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3876
3925
  setNewMax(0);
3877
3926
  };
3878
3927
  if (isLoading) {
3879
- return /* @__PURE__ */ jsxs29(
3928
+ return /* @__PURE__ */ jsxs30(
3880
3929
  "div",
3881
3930
  {
3882
3931
  role: "status",
3883
3932
  "aria-label": "Loading frequency caps",
3884
3933
  className: cn30("flex flex-col gap-2 p-4", className),
3885
3934
  children: [
3886
- /* @__PURE__ */ jsx32("span", { className: "sr-only", children: "Loading frequency caps" }),
3887
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx32("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3935
+ /* @__PURE__ */ jsx33("span", { className: "sr-only", children: "Loading frequency caps" }),
3936
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx33("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
3888
3937
  ]
3889
3938
  }
3890
3939
  );
3891
3940
  }
3892
3941
  if (isError) {
3893
- return /* @__PURE__ */ jsxs29("div", { className: cn30("flex flex-col items-start gap-3 p-4", className), children: [
3894
- /* @__PURE__ */ jsx32("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
3895
- /* @__PURE__ */ jsx32(
3942
+ return /* @__PURE__ */ jsxs30("div", { className: cn30("flex flex-col items-start gap-3 p-4", className), children: [
3943
+ /* @__PURE__ */ jsx33("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
3944
+ /* @__PURE__ */ jsx33(
3896
3945
  "button",
3897
3946
  {
3898
3947
  type: "button",
@@ -3903,24 +3952,24 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3903
3952
  )
3904
3953
  ] });
3905
3954
  }
3906
- return /* @__PURE__ */ jsxs29("section", { "aria-label": "Frequency caps", className: cn30("flex flex-col gap-4", className), children: [
3907
- /* @__PURE__ */ jsx32("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
3908
- /* @__PURE__ */ jsxs29("table", { className: "w-full text-sm", children: [
3909
- /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsxs29("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3910
- /* @__PURE__ */ jsx32("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
3911
- /* @__PURE__ */ jsx32("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
3912
- /* @__PURE__ */ jsx32("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx32("span", { className: "sr-only", children: "Actions" }) })
3955
+ return /* @__PURE__ */ jsxs30("section", { "aria-label": "Frequency caps", className: cn30("flex flex-col gap-4", className), children: [
3956
+ /* @__PURE__ */ jsx33("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
3957
+ /* @__PURE__ */ jsxs30("table", { className: "w-full text-sm", children: [
3958
+ /* @__PURE__ */ jsx33("thead", { children: /* @__PURE__ */ jsxs30("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
3959
+ /* @__PURE__ */ jsx33("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
3960
+ /* @__PURE__ */ jsx33("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
3961
+ /* @__PURE__ */ jsx33("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx33("span", { className: "sr-only", children: "Actions" }) })
3913
3962
  ] }) }),
3914
- /* @__PURE__ */ jsx32("tbody", { children: caps.map((cap, index) => {
3963
+ /* @__PURE__ */ jsx33("tbody", { children: caps.map((cap, index) => {
3915
3964
  const inputId = `cap-${index}`;
3916
- return /* @__PURE__ */ jsxs29("tr", { className: "border-b border-border", children: [
3917
- /* @__PURE__ */ jsx32("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
3918
- /* @__PURE__ */ jsxs29("td", { className: "px-4 py-3", children: [
3919
- /* @__PURE__ */ jsxs29("label", { htmlFor: inputId, className: "sr-only", children: [
3965
+ return /* @__PURE__ */ jsxs30("tr", { className: "border-b border-border", children: [
3966
+ /* @__PURE__ */ jsx33("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
3967
+ /* @__PURE__ */ jsxs30("td", { className: "px-4 py-3", children: [
3968
+ /* @__PURE__ */ jsxs30("label", { htmlFor: inputId, className: "sr-only", children: [
3920
3969
  "Max per day for ",
3921
3970
  cap.type
3922
3971
  ] }),
3923
- /* @__PURE__ */ jsx32(
3972
+ /* @__PURE__ */ jsx33(
3924
3973
  "input",
3925
3974
  {
3926
3975
  id: inputId,
@@ -3932,7 +3981,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3932
3981
  }
3933
3982
  )
3934
3983
  ] }),
3935
- /* @__PURE__ */ jsx32("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx32(
3984
+ /* @__PURE__ */ jsx33("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx33(
3936
3985
  "button",
3937
3986
  {
3938
3987
  type: "button",
@@ -3944,10 +3993,10 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3944
3993
  ] }, `${cap.type}-${index}`);
3945
3994
  }) })
3946
3995
  ] }),
3947
- /* @__PURE__ */ jsxs29("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3948
- /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-1", children: [
3949
- /* @__PURE__ */ jsx32("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
3950
- /* @__PURE__ */ jsx32(
3996
+ /* @__PURE__ */ jsxs30("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
3997
+ /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
3998
+ /* @__PURE__ */ jsx33("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
3999
+ /* @__PURE__ */ jsx33(
3951
4000
  "input",
3952
4001
  {
3953
4002
  id: "cap-new-type",
@@ -3958,9 +4007,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3958
4007
  }
3959
4008
  )
3960
4009
  ] }),
3961
- /* @__PURE__ */ jsxs29("div", { className: "flex flex-col gap-1", children: [
3962
- /* @__PURE__ */ jsx32("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
3963
- /* @__PURE__ */ jsx32(
4010
+ /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
4011
+ /* @__PURE__ */ jsx33("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
4012
+ /* @__PURE__ */ jsx33(
3964
4013
  "input",
3965
4014
  {
3966
4015
  id: "cap-new-max",
@@ -3972,7 +4021,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3972
4021
  }
3973
4022
  )
3974
4023
  ] }),
3975
- /* @__PURE__ */ jsx32(
4024
+ /* @__PURE__ */ jsx33(
3976
4025
  "button",
3977
4026
  {
3978
4027
  type: "submit",
@@ -3981,7 +4030,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3981
4030
  }
3982
4031
  )
3983
4032
  ] }),
3984
- /* @__PURE__ */ jsx32("div", { children: /* @__PURE__ */ jsx32(
4033
+ /* @__PURE__ */ jsx33("div", { children: /* @__PURE__ */ jsx33(
3985
4034
  "button",
3986
4035
  {
3987
4036
  type: "button",
@@ -3998,7 +4047,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
3998
4047
  import { useEffect as useEffect8, useState as useState18 } from "react";
3999
4048
  import { cn as cn31, useToast as useToast15 } from "@quanticjs/react-ui";
4000
4049
  import { useApiMutation as useApiMutation16, useApiQuery as useApiQuery25 } from "@quanticjs/react-query";
4001
- import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
4050
+ import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
4002
4051
  function normalize7(raw) {
4003
4052
  const obj = raw ?? {};
4004
4053
  const toList = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
@@ -4016,15 +4065,15 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4016
4065
  onAdd(value);
4017
4066
  setDraft("");
4018
4067
  };
4019
- return /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-2", children: [
4020
- /* @__PURE__ */ jsx33("span", { className: "text-sm font-medium text-foreground", children: label }),
4021
- /* @__PURE__ */ jsx33("ul", { className: "flex flex-wrap gap-2", children: values.length === 0 ? /* @__PURE__ */ jsx33("li", { className: "text-sm text-muted-foreground", children: "None" }) : values.map((value) => /* @__PURE__ */ jsxs30(
4068
+ return /* @__PURE__ */ jsxs31("div", { className: "flex flex-col gap-2", children: [
4069
+ /* @__PURE__ */ jsx34("span", { className: "text-sm font-medium text-foreground", children: label }),
4070
+ /* @__PURE__ */ jsx34("ul", { className: "flex flex-wrap gap-2", children: values.length === 0 ? /* @__PURE__ */ jsx34("li", { className: "text-sm text-muted-foreground", children: "None" }) : values.map((value) => /* @__PURE__ */ jsxs31(
4022
4071
  "li",
4023
4072
  {
4024
4073
  className: "flex items-center gap-1 rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground",
4025
4074
  children: [
4026
4075
  value,
4027
- /* @__PURE__ */ jsx33(
4076
+ /* @__PURE__ */ jsx34(
4028
4077
  "button",
4029
4078
  {
4030
4079
  type: "button",
@@ -4038,13 +4087,13 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4038
4087
  },
4039
4088
  value
4040
4089
  )) }),
4041
- /* @__PURE__ */ jsxs30("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
4042
- /* @__PURE__ */ jsxs30("div", { className: "flex flex-col gap-1", children: [
4043
- /* @__PURE__ */ jsxs30("label", { htmlFor: id, className: "sr-only", children: [
4090
+ /* @__PURE__ */ jsxs31("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
4091
+ /* @__PURE__ */ jsxs31("div", { className: "flex flex-col gap-1", children: [
4092
+ /* @__PURE__ */ jsxs31("label", { htmlFor: id, className: "sr-only", children: [
4044
4093
  "Add to ",
4045
4094
  label
4046
4095
  ] }),
4047
- /* @__PURE__ */ jsx33(
4096
+ /* @__PURE__ */ jsx34(
4048
4097
  "input",
4049
4098
  {
4050
4099
  id,
@@ -4055,7 +4104,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
4055
4104
  }
4056
4105
  )
4057
4106
  ] }),
4058
- /* @__PURE__ */ jsx33(
4107
+ /* @__PURE__ */ jsx34(
4059
4108
  "button",
4060
4109
  {
4061
4110
  type: "submit",
@@ -4120,23 +4169,23 @@ function TenantConfigForm({ basePath = "/api", className }) {
4120
4169
  save.mutate(config);
4121
4170
  };
4122
4171
  if (isLoading) {
4123
- return /* @__PURE__ */ jsxs30(
4172
+ return /* @__PURE__ */ jsxs31(
4124
4173
  "div",
4125
4174
  {
4126
4175
  role: "status",
4127
4176
  "aria-label": "Loading tenant configuration",
4128
4177
  className: cn31("flex flex-col gap-2 p-4", className),
4129
4178
  children: [
4130
- /* @__PURE__ */ jsx33("span", { className: "sr-only", children: "Loading tenant configuration" }),
4131
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx33("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4179
+ /* @__PURE__ */ jsx34("span", { className: "sr-only", children: "Loading tenant configuration" }),
4180
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx34("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4132
4181
  ]
4133
4182
  }
4134
4183
  );
4135
4184
  }
4136
4185
  if (isError) {
4137
- return /* @__PURE__ */ jsxs30("div", { className: cn31("flex flex-col items-start gap-3 p-4", className), children: [
4138
- /* @__PURE__ */ jsx33("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
4139
- /* @__PURE__ */ jsx33(
4186
+ return /* @__PURE__ */ jsxs31("div", { className: cn31("flex flex-col items-start gap-3 p-4", className), children: [
4187
+ /* @__PURE__ */ jsx34("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
4188
+ /* @__PURE__ */ jsx34(
4140
4189
  "button",
4141
4190
  {
4142
4191
  type: "button",
@@ -4148,7 +4197,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
4148
4197
  ] });
4149
4198
  }
4150
4199
  const errorId = "tenant-config-subset-error";
4151
- return /* @__PURE__ */ jsxs30(
4200
+ return /* @__PURE__ */ jsxs31(
4152
4201
  "form",
4153
4202
  {
4154
4203
  onSubmit,
@@ -4157,8 +4206,8 @@ function TenantConfigForm({ basePath = "/api", className }) {
4157
4206
  "aria-invalid": subsetError ? "true" : void 0,
4158
4207
  "aria-describedby": subsetError ? errorId : void 0,
4159
4208
  children: [
4160
- /* @__PURE__ */ jsx33("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
4161
- /* @__PURE__ */ jsx33(
4209
+ /* @__PURE__ */ jsx34("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
4210
+ /* @__PURE__ */ jsx34(
4162
4211
  TagEditor,
4163
4212
  {
4164
4213
  id: "tc-notification-types",
@@ -4168,7 +4217,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
4168
4217
  onRemove: removeType
4169
4218
  }
4170
4219
  ),
4171
- /* @__PURE__ */ jsx33(
4220
+ /* @__PURE__ */ jsx34(
4172
4221
  TagEditor,
4173
4222
  {
4174
4223
  id: "tc-immediate-email-types",
@@ -4178,8 +4227,8 @@ function TenantConfigForm({ basePath = "/api", className }) {
4178
4227
  onRemove: removeImmediate
4179
4228
  }
4180
4229
  ),
4181
- subsetError && /* @__PURE__ */ jsx33("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
4182
- /* @__PURE__ */ jsx33("div", { children: /* @__PURE__ */ jsx33(
4230
+ subsetError && /* @__PURE__ */ jsx34("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
4231
+ /* @__PURE__ */ jsx34("div", { children: /* @__PURE__ */ jsx34(
4183
4232
  "button",
4184
4233
  {
4185
4234
  type: "submit",
@@ -4197,7 +4246,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
4197
4246
  import { useEffect as useEffect9, useState as useState19 } from "react";
4198
4247
  import { cn as cn32, useToast as useToast16 } from "@quanticjs/react-ui";
4199
4248
  import { useApiMutation as useApiMutation17, useApiQuery as useApiQuery26 } from "@quanticjs/react-query";
4200
- import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
4249
+ import { jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
4201
4250
  function normalize8(raw) {
4202
4251
  const obj = raw ?? {};
4203
4252
  const bool = (...keys) => {
@@ -4242,23 +4291,23 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4242
4291
  save.mutate(form);
4243
4292
  };
4244
4293
  if (isLoading) {
4245
- return /* @__PURE__ */ jsxs31(
4294
+ return /* @__PURE__ */ jsxs32(
4246
4295
  "div",
4247
4296
  {
4248
4297
  role: "status",
4249
4298
  "aria-label": "Loading tracking configuration",
4250
4299
  className: cn32("flex flex-col gap-2 p-4", className),
4251
4300
  children: [
4252
- /* @__PURE__ */ jsx34("span", { className: "sr-only", children: "Loading tracking configuration" }),
4253
- [0, 1].map((i) => /* @__PURE__ */ jsx34("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4301
+ /* @__PURE__ */ jsx35("span", { className: "sr-only", children: "Loading tracking configuration" }),
4302
+ [0, 1].map((i) => /* @__PURE__ */ jsx35("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4254
4303
  ]
4255
4304
  }
4256
4305
  );
4257
4306
  }
4258
4307
  if (isError) {
4259
- return /* @__PURE__ */ jsxs31("div", { className: cn32("flex flex-col items-start gap-3 p-4", className), children: [
4260
- /* @__PURE__ */ jsx34("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
4261
- /* @__PURE__ */ jsx34(
4308
+ return /* @__PURE__ */ jsxs32("div", { className: cn32("flex flex-col items-start gap-3 p-4", className), children: [
4309
+ /* @__PURE__ */ jsx35("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
4310
+ /* @__PURE__ */ jsx35(
4262
4311
  "button",
4263
4312
  {
4264
4313
  type: "button",
@@ -4269,10 +4318,10 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4269
4318
  )
4270
4319
  ] });
4271
4320
  }
4272
- return /* @__PURE__ */ jsxs31("form", { onSubmit, className: cn32("flex flex-col gap-4", className), noValidate: true, children: [
4273
- /* @__PURE__ */ jsx34("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
4274
- /* @__PURE__ */ jsxs31("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4275
- /* @__PURE__ */ jsx34(
4321
+ return /* @__PURE__ */ jsxs32("form", { onSubmit, className: cn32("flex flex-col gap-4", className), noValidate: true, children: [
4322
+ /* @__PURE__ */ jsx35("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
4323
+ /* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4324
+ /* @__PURE__ */ jsx35(
4276
4325
  "input",
4277
4326
  {
4278
4327
  type: "checkbox",
@@ -4283,8 +4332,8 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4283
4332
  ),
4284
4333
  "Open tracking"
4285
4334
  ] }),
4286
- /* @__PURE__ */ jsxs31("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4287
- /* @__PURE__ */ jsx34(
4335
+ /* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
4336
+ /* @__PURE__ */ jsx35(
4288
4337
  "input",
4289
4338
  {
4290
4339
  type: "checkbox",
@@ -4295,7 +4344,7 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4295
4344
  ),
4296
4345
  "Click tracking"
4297
4346
  ] }),
4298
- /* @__PURE__ */ jsx34("div", { children: /* @__PURE__ */ jsx34(
4347
+ /* @__PURE__ */ jsx35("div", { children: /* @__PURE__ */ jsx35(
4299
4348
  "button",
4300
4349
  {
4301
4350
  type: "submit",
@@ -4309,9 +4358,9 @@ function TrackingConfigForm({ basePath = "/api", className }) {
4309
4358
 
4310
4359
  // src/api-key-manager.tsx
4311
4360
  import { useState as useState20 } from "react";
4312
- import { cn as cn33, formatDateTime as formatDateTime9, StatusBadge as StatusBadge8, useToast as useToast17 } from "@quanticjs/react-ui";
4361
+ import { Button as Button8, Card as Card7, EmptyState as EmptyState7, StatusBadge as StatusBadge8, cn as cn33, formatDateTime as formatDateTime9, useToast as useToast17 } from "@quanticjs/react-ui";
4313
4362
  import { useApiMutation as useApiMutation18, useApiQuery as useApiQuery27 } from "@quanticjs/react-query";
4314
- import { jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
4363
+ import { jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
4315
4364
  function normalize9(raw) {
4316
4365
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
4317
4366
  return list;
@@ -4369,102 +4418,69 @@ function ApiKeyManager({ basePath = "/api", className }) {
4369
4418
  revoke.mutate(id);
4370
4419
  }
4371
4420
  };
4372
- const createForm = /* @__PURE__ */ jsxs32("form", { onSubmit: onCreate, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4373
- /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1", children: [
4374
- /* @__PURE__ */ jsx35("label", { htmlFor: "api-key-name", className: "text-sm font-medium text-foreground", children: "New key name" }),
4375
- /* @__PURE__ */ jsx35(
4376
- "input",
4377
- {
4378
- id: "api-key-name",
4379
- type: "text",
4380
- value: name,
4381
- onChange: (e) => setName(e.target.value),
4382
- 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"
4383
- }
4384
- )
4385
- ] }),
4386
- /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1", children: [
4387
- /* @__PURE__ */ jsx35("label", { htmlFor: "api-key-app", className: "text-sm font-medium text-foreground", children: "Application (optional)" }),
4388
- /* @__PURE__ */ jsx35(
4389
- "input",
4390
- {
4391
- id: "api-key-app",
4392
- type: "text",
4393
- value: applicationKey,
4394
- onChange: (e) => setApplicationKey(e.target.value),
4395
- placeholder: "delivery-hub",
4396
- 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"
4397
- }
4398
- )
4399
- ] }),
4400
- /* @__PURE__ */ jsx35(
4401
- "button",
4402
- {
4403
- type: "submit",
4404
- disabled: create.isPending,
4405
- 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",
4406
- children: create.isPending ? "Creating\u2026" : "Create key"
4407
- }
4408
- )
4409
- ] });
4410
- let body;
4411
- if (isLoading) {
4412
- body = /* @__PURE__ */ jsxs32("div", { role: "status", "aria-label": "Loading API keys", className: "flex flex-col gap-2 p-4", children: [
4413
- /* @__PURE__ */ jsx35("span", { className: "sr-only", children: "Loading API keys" }),
4414
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx35("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4415
- ] });
4416
- } else if (isError) {
4417
- body = /* @__PURE__ */ jsxs32("div", { className: "flex flex-col items-start gap-3 p-4", children: [
4418
- /* @__PURE__ */ jsx35("p", { className: "text-sm text-foreground", children: "Failed to load API keys" }),
4419
- /* @__PURE__ */ jsx35(
4420
- "button",
4421
- {
4422
- type: "button",
4423
- onClick: () => void refetch(),
4424
- 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",
4425
- children: "Try again"
4426
- }
4427
- )
4428
- ] });
4429
- } else {
4430
- const rows = normalize9(data);
4431
- if (rows.length === 0) {
4432
- body = /* @__PURE__ */ jsx35("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No API keys" });
4433
- } else {
4434
- body = /* @__PURE__ */ jsxs32("table", { className: "w-full text-sm", children: [
4435
- /* @__PURE__ */ jsx35("thead", { children: /* @__PURE__ */ jsxs32("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4436
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Name" }),
4437
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Prefix" }),
4438
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Application" }),
4439
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
4440
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
4441
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last used" }),
4442
- /* @__PURE__ */ jsx35("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx35("span", { className: "sr-only", children: "Actions" }) })
4421
+ const rows = normalize9(data);
4422
+ return /* @__PURE__ */ jsxs33("section", { "aria-label": "API key management", className: cn33("flex flex-col gap-4", className), children: [
4423
+ /* @__PURE__ */ jsx36(Card7, { children: /* @__PURE__ */ jsxs33("form", { onSubmit: onCreate, className: "flex flex-wrap items-end gap-3 p-4", noValidate: true, children: [
4424
+ /* @__PURE__ */ jsxs33("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
4425
+ /* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "New key name" }),
4426
+ /* @__PURE__ */ jsx36("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), className: FIELD_CLASS })
4427
+ ] }),
4428
+ /* @__PURE__ */ jsxs33("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
4429
+ /* @__PURE__ */ jsx36("span", { className: "font-medium text-foreground", children: "Application (optional)" }),
4430
+ /* @__PURE__ */ jsx36(
4431
+ "input",
4432
+ {
4433
+ type: "text",
4434
+ value: applicationKey,
4435
+ onChange: (e) => setApplicationKey(e.target.value),
4436
+ placeholder: "delivery-hub",
4437
+ className: FIELD_CLASS
4438
+ }
4439
+ )
4440
+ ] }),
4441
+ /* @__PURE__ */ jsx36(Button8, { type: "submit", disabled: create.isPending, children: create.isPending ? "Creating\u2026" : "Create key" })
4442
+ ] }) }),
4443
+ /* @__PURE__ */ jsxs33(Card7, { children: [
4444
+ /* @__PURE__ */ jsx36(PanelHeader, { title: "Keys", subtitle: !isLoading && !isError ? `${rows.length} keys` : void 0 }),
4445
+ isLoading ? /* @__PURE__ */ jsx36(SkeletonRows, { label: "Loading API keys" }) : isError ? /* @__PURE__ */ jsx36(ErrorPanel, { message: "Failed to load API keys", onRetry: () => void refetch() }) : rows.length === 0 ? /* @__PURE__ */ jsx36(EmptyState7, { icon: /* @__PURE__ */ jsx36(KeyIcon, {}), title: "No API keys", description: "Create a key to get started." }) : /* @__PURE__ */ jsxs33("table", { className: "w-full text-sm", children: [
4446
+ /* @__PURE__ */ jsx36("thead", { children: /* @__PURE__ */ jsxs33("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4447
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Name" }),
4448
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Prefix" }),
4449
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Application" }),
4450
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
4451
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
4452
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last used" }),
4453
+ /* @__PURE__ */ jsx36("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ jsx36("span", { className: "sr-only", children: "Actions" }) })
4443
4454
  ] }) }),
4444
- /* @__PURE__ */ jsx35("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs32("tr", { className: "border-b border-border", children: [
4445
- /* @__PURE__ */ jsx35("td", { className: "py-3 pe-4 text-foreground", children: row.name }),
4446
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
4447
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
4448
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx35(StatusBadge8, { variant: row.revoked ? "destructive" : "success", children: row.revoked ? "Revoked" : "Active" }) }),
4449
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime9(row.createdAt) }),
4450
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastUsedAt ? formatDateTime9(row.lastUsedAt) : "\u2014" }),
4451
- /* @__PURE__ */ jsx35("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx35(
4452
- "button",
4455
+ /* @__PURE__ */ jsx36("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs33("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [
4456
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 text-foreground", children: row.name }),
4457
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
4458
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
4459
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx36(StatusBadge8, { variant: row.revoked ? "destructive" : "success", children: row.revoked ? "Revoked" : "Active" }) }),
4460
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 text-muted-foreground", children: formatDateTime9(row.createdAt) }),
4461
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastUsedAt ? formatDateTime9(row.lastUsedAt) : "\u2014" }),
4462
+ /* @__PURE__ */ jsx36("td", { className: "px-4 py-3 text-end", children: /* @__PURE__ */ jsx36(
4463
+ Button8,
4453
4464
  {
4454
4465
  type: "button",
4466
+ variant: "ghost",
4467
+ size: "sm",
4468
+ className: "text-destructive",
4455
4469
  disabled: revoke.isPending || row.revoked,
4456
4470
  onClick: () => onRevoke(row.id),
4457
- 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",
4458
4471
  children: "Revoke"
4459
4472
  }
4460
4473
  ) })
4461
4474
  ] }, row.id)) })
4462
- ] });
4463
- }
4464
- }
4465
- return /* @__PURE__ */ jsxs32("section", { "aria-label": "API key management", className: cn33("flex flex-col gap-4", className), children: [
4466
- createForm,
4467
- body
4475
+ ] })
4476
+ ] })
4477
+ ] });
4478
+ }
4479
+ function KeyIcon() {
4480
+ return /* @__PURE__ */ jsxs33("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4481
+ /* @__PURE__ */ jsx36("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" }),
4482
+ /* @__PURE__ */ jsx36("path", { d: "m21 2-9.6 9.6" }),
4483
+ /* @__PURE__ */ jsx36("circle", { cx: "7.5", cy: "15.5", r: "5.5" })
4468
4484
  ] });
4469
4485
  }
4470
4486
 
@@ -4472,7 +4488,7 @@ function ApiKeyManager({ basePath = "/api", className }) {
4472
4488
  import { useState as useState21 } from "react";
4473
4489
  import { cn as cn34, formatDateTime as formatDateTime10, StatusBadge as StatusBadge9, useToast as useToast18 } from "@quanticjs/react-ui";
4474
4490
  import { useApiMutation as useApiMutation19, useApiQuery as useApiQuery28 } from "@quanticjs/react-query";
4475
- import { jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
4491
+ import { jsx as jsx37, jsxs as jsxs34 } from "react/jsx-runtime";
4476
4492
  function normalize10(raw) {
4477
4493
  const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
4478
4494
  return list;
@@ -4518,10 +4534,10 @@ function ApplicationRegistryPanel({
4518
4534
  if (!k || !name) return;
4519
4535
  register.mutate({ key: k, displayName: name, description: description.trim() || void 0 });
4520
4536
  };
4521
- const registerForm = /* @__PURE__ */ jsxs33("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4522
- /* @__PURE__ */ jsxs33("div", { className: "flex flex-col gap-1", children: [
4523
- /* @__PURE__ */ jsx36("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
4524
- /* @__PURE__ */ jsx36(
4537
+ const registerForm = /* @__PURE__ */ jsxs34("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
4538
+ /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1", children: [
4539
+ /* @__PURE__ */ jsx37("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
4540
+ /* @__PURE__ */ jsx37(
4525
4541
  "input",
4526
4542
  {
4527
4543
  id: "application-key",
@@ -4533,9 +4549,9 @@ function ApplicationRegistryPanel({
4533
4549
  }
4534
4550
  )
4535
4551
  ] }),
4536
- /* @__PURE__ */ jsxs33("div", { className: "flex flex-col gap-1", children: [
4537
- /* @__PURE__ */ jsx36("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
4538
- /* @__PURE__ */ jsx36(
4552
+ /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1", children: [
4553
+ /* @__PURE__ */ jsx37("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
4554
+ /* @__PURE__ */ jsx37(
4539
4555
  "input",
4540
4556
  {
4541
4557
  id: "application-name",
@@ -4547,9 +4563,9 @@ function ApplicationRegistryPanel({
4547
4563
  }
4548
4564
  )
4549
4565
  ] }),
4550
- /* @__PURE__ */ jsxs33("div", { className: "flex flex-col gap-1", children: [
4551
- /* @__PURE__ */ jsx36("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
4552
- /* @__PURE__ */ jsx36(
4566
+ /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1", children: [
4567
+ /* @__PURE__ */ jsx37("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
4568
+ /* @__PURE__ */ jsx37(
4553
4569
  "input",
4554
4570
  {
4555
4571
  id: "application-description",
@@ -4560,7 +4576,7 @@ function ApplicationRegistryPanel({
4560
4576
  }
4561
4577
  )
4562
4578
  ] }),
4563
- /* @__PURE__ */ jsx36(
4579
+ /* @__PURE__ */ jsx37(
4564
4580
  "button",
4565
4581
  {
4566
4582
  type: "submit",
@@ -4571,23 +4587,23 @@ function ApplicationRegistryPanel({
4571
4587
  )
4572
4588
  ] });
4573
4589
  if (isLoading) {
4574
- return /* @__PURE__ */ jsxs33(
4590
+ return /* @__PURE__ */ jsxs34(
4575
4591
  "div",
4576
4592
  {
4577
4593
  role: "status",
4578
4594
  "aria-label": "Loading applications",
4579
4595
  className: cn34("flex flex-col gap-2 p-4", className),
4580
4596
  children: [
4581
- /* @__PURE__ */ jsx36("span", { className: "sr-only", children: "Loading applications" }),
4582
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx36("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4597
+ /* @__PURE__ */ jsx37("span", { className: "sr-only", children: "Loading applications" }),
4598
+ [0, 1, 2].map((i) => /* @__PURE__ */ jsx37("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
4583
4599
  ]
4584
4600
  }
4585
4601
  );
4586
4602
  }
4587
4603
  if (isError) {
4588
- return /* @__PURE__ */ jsxs33("div", { className: cn34("flex flex-col items-start gap-3 p-4", className), children: [
4589
- /* @__PURE__ */ jsx36("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
4590
- /* @__PURE__ */ jsx36(
4604
+ return /* @__PURE__ */ jsxs34("div", { className: cn34("flex flex-col items-start gap-3 p-4", className), children: [
4605
+ /* @__PURE__ */ jsx37("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
4606
+ /* @__PURE__ */ jsx37(
4591
4607
  "button",
4592
4608
  {
4593
4609
  type: "button",
@@ -4599,22 +4615,22 @@ function ApplicationRegistryPanel({
4599
4615
  ] });
4600
4616
  }
4601
4617
  const apps = normalize10(data);
4602
- return /* @__PURE__ */ jsxs33("section", { "aria-label": "Applications", className: cn34("flex flex-col gap-4 p-4", className), children: [
4618
+ return /* @__PURE__ */ jsxs34("section", { "aria-label": "Applications", className: cn34("flex flex-col gap-4 p-4", className), children: [
4603
4619
  registerForm,
4604
- apps.length === 0 ? /* @__PURE__ */ jsx36("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No applications registered" }) : /* @__PURE__ */ jsxs33("table", { className: "w-full text-sm", children: [
4605
- /* @__PURE__ */ jsx36("thead", { children: /* @__PURE__ */ jsxs33("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4606
- /* @__PURE__ */ jsx36("th", { className: "py-2 font-medium", children: "Key" }),
4607
- /* @__PURE__ */ jsx36("th", { className: "py-2 font-medium", children: "Name" }),
4608
- /* @__PURE__ */ jsx36("th", { className: "py-2 font-medium", children: "Status" }),
4609
- /* @__PURE__ */ jsx36("th", { className: "py-2 font-medium", children: "Created" }),
4610
- /* @__PURE__ */ jsx36("th", { className: "py-2 font-medium", children: "Actions" })
4620
+ apps.length === 0 ? /* @__PURE__ */ jsx37("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No applications registered" }) : /* @__PURE__ */ jsxs34("table", { className: "w-full text-sm", children: [
4621
+ /* @__PURE__ */ jsx37("thead", { children: /* @__PURE__ */ jsxs34("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
4622
+ /* @__PURE__ */ jsx37("th", { className: "py-2 font-medium", children: "Key" }),
4623
+ /* @__PURE__ */ jsx37("th", { className: "py-2 font-medium", children: "Name" }),
4624
+ /* @__PURE__ */ jsx37("th", { className: "py-2 font-medium", children: "Status" }),
4625
+ /* @__PURE__ */ jsx37("th", { className: "py-2 font-medium", children: "Created" }),
4626
+ /* @__PURE__ */ jsx37("th", { className: "py-2 font-medium", children: "Actions" })
4611
4627
  ] }) }),
4612
- /* @__PURE__ */ jsx36("tbody", { children: apps.map((app) => /* @__PURE__ */ jsxs33("tr", { className: "border-b border-border", children: [
4613
- /* @__PURE__ */ jsx36("td", { className: "py-2 font-mono text-foreground", children: app.key }),
4614
- /* @__PURE__ */ jsx36("td", { className: "py-2 text-foreground", children: app.displayName }),
4615
- /* @__PURE__ */ jsx36("td", { className: "py-2", children: /* @__PURE__ */ jsx36(StatusBadge9, { variant: app.status === "active" ? "success" : "neutral", children: app.status }) }),
4616
- /* @__PURE__ */ jsx36("td", { className: "py-2 text-muted-foreground", children: formatDateTime10(app.createdAt) }),
4617
- /* @__PURE__ */ jsx36("td", { className: "py-2", children: /* @__PURE__ */ jsx36(
4628
+ /* @__PURE__ */ jsx37("tbody", { children: apps.map((app) => /* @__PURE__ */ jsxs34("tr", { className: "border-b border-border", children: [
4629
+ /* @__PURE__ */ jsx37("td", { className: "py-2 font-mono text-foreground", children: app.key }),
4630
+ /* @__PURE__ */ jsx37("td", { className: "py-2 text-foreground", children: app.displayName }),
4631
+ /* @__PURE__ */ jsx37("td", { className: "py-2", children: /* @__PURE__ */ jsx37(StatusBadge9, { variant: app.status === "active" ? "success" : "neutral", children: app.status }) }),
4632
+ /* @__PURE__ */ jsx37("td", { className: "py-2 text-muted-foreground", children: formatDateTime10(app.createdAt) }),
4633
+ /* @__PURE__ */ jsx37("td", { className: "py-2", children: /* @__PURE__ */ jsx37(
4618
4634
  "button",
4619
4635
  {
4620
4636
  type: "button",