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