@quanticjs/notification-ui 9.0.0 → 9.0.2
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 +1106 -1013
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1082 -974
- 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,120 +3521,225 @@ 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 ?? [];
|
|
3469
3574
|
const dlqPending = data.dlqPending;
|
|
3470
|
-
const
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3482
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
|
|
3483
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: dlqPending > 0 ? "destructive" : "success", children: dlqPending })
|
|
3484
|
-
] }),
|
|
3485
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3486
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
|
|
3487
|
-
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm font-medium text-foreground", children: data.broadcastsInFlight })
|
|
3575
|
+
const deliveryRate = ratePct(data.totalDelivered, data.totalSends);
|
|
3576
|
+
const failureRate = ratePct(data.totalFailed, data.totalSends);
|
|
3577
|
+
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: [
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-wrap items-baseline justify-between gap-2", children: [
|
|
3579
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
|
|
3580
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { className: "text-base font-semibold tracking-tight text-foreground", children: "Operations overview" }),
|
|
3581
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
3582
|
+
"Last ",
|
|
3583
|
+
windowHours,
|
|
3584
|
+
"h"
|
|
3585
|
+
] })
|
|
3488
3586
|
] }),
|
|
3489
|
-
/* @__PURE__ */ (0,
|
|
3490
|
-
|
|
3491
|
-
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3588
|
+
"Generated ",
|
|
3589
|
+
(0, import_react_ui29.formatDateTime)(data.generatedAt)
|
|
3492
3590
|
] })
|
|
3493
3591
|
] }),
|
|
3494
|
-
/* @__PURE__ */ (0,
|
|
3495
|
-
/* @__PURE__ */ (0,
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3592
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-3", children: [
|
|
3593
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatCard, { label: `Sent (${windowHours}h)`, value: data.totalSends, icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(SendIcon, {}) }),
|
|
3594
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3595
|
+
import_react_ui29.StatCard,
|
|
3596
|
+
{
|
|
3597
|
+
label: "Delivery rate",
|
|
3598
|
+
value: deliveryRate,
|
|
3599
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CheckIcon, {}),
|
|
3600
|
+
delta: `${data.totalDelivered} delivered`,
|
|
3601
|
+
trend: "up"
|
|
3602
|
+
}
|
|
3603
|
+
),
|
|
3604
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3605
|
+
import_react_ui29.StatCard,
|
|
3606
|
+
{
|
|
3607
|
+
label: "Failure rate",
|
|
3608
|
+
value: failureRate,
|
|
3609
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(AlertIcon4, {}),
|
|
3610
|
+
delta: `${data.totalFailed} failed`,
|
|
3611
|
+
trend: data.totalFailed > 0 ? "down" : "flat"
|
|
3612
|
+
}
|
|
3613
|
+
)
|
|
3507
3614
|
] }),
|
|
3508
|
-
/* @__PURE__ */ (0,
|
|
3509
|
-
"
|
|
3510
|
-
|
|
3615
|
+
/* @__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: [
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3617
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
|
|
3618
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: dlqPending > 0 ? "destructive" : "success", appearance: "solid", children: dlqPending })
|
|
3619
|
+
] }),
|
|
3620
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3621
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "Broadcasts in flight" }),
|
|
3622
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm font-semibold tabular-nums text-foreground", children: data.broadcastsInFlight })
|
|
3623
|
+
] }),
|
|
3624
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3625
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
|
|
3626
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: data.queueHealthy ? "success" : "destructive", appearance: "solid", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
|
|
3627
|
+
] })
|
|
3628
|
+
] }) }),
|
|
3629
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.Card, { children: [
|
|
3630
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.CardHeader, { children: [
|
|
3631
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardTitle, { children: "Channel health" }),
|
|
3632
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-sm text-muted-foreground", children: "Delivery success by channel" })
|
|
3633
|
+
] }),
|
|
3634
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.CardContent, { className: "p-0", children: channels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "px-5 py-8 text-center text-sm text-muted-foreground", children: "No channel activity" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { className: "divide-y divide-border", children: channels.map((row) => {
|
|
3635
|
+
const success = ratePct(row.delivered, row.sends);
|
|
3636
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("li", { className: "flex items-center gap-3 px-5 py-3.5", children: [
|
|
3637
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3638
|
+
"span",
|
|
3639
|
+
{
|
|
3640
|
+
"aria-hidden": "true",
|
|
3641
|
+
className: "grid size-9 shrink-0 place-items-center rounded-md bg-primary/10 text-primary [&_svg]:size-4",
|
|
3642
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ChannelIcon, { channel: row.channel })
|
|
3643
|
+
}
|
|
3644
|
+
),
|
|
3645
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
3646
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3647
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "flex items-center gap-2 text-sm font-medium capitalize text-foreground", children: [
|
|
3648
|
+
row.channel,
|
|
3649
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_ui29.StatusBadge, { variant: row.failed > 0 ? "warning" : "success", appearance: "dot", children: [
|
|
3650
|
+
success,
|
|
3651
|
+
" success"
|
|
3652
|
+
] })
|
|
3653
|
+
] }),
|
|
3654
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: [
|
|
3655
|
+
row.sends,
|
|
3656
|
+
" sent"
|
|
3657
|
+
] })
|
|
3658
|
+
] }),
|
|
3659
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "mt-2 flex h-1.5 overflow-hidden rounded-full bg-muted", children: [
|
|
3660
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "bg-success", style: { width: `${barPct(row.delivered, row.sends)}%` } }),
|
|
3661
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "bg-destructive", style: { width: `${barPct(row.failed, row.sends)}%` } })
|
|
3662
|
+
] }),
|
|
3663
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "mt-1.5 text-xs tabular-nums text-muted-foreground", children: [
|
|
3664
|
+
row.delivered,
|
|
3665
|
+
" delivered \xB7 ",
|
|
3666
|
+
row.failed,
|
|
3667
|
+
" failed"
|
|
3668
|
+
] })
|
|
3669
|
+
] })
|
|
3670
|
+
] }, row.channel);
|
|
3671
|
+
}) }) })
|
|
3511
3672
|
] })
|
|
3512
3673
|
] });
|
|
3513
3674
|
}
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3675
|
+
function ratePct(n, total) {
|
|
3676
|
+
if (total <= 0) return "\u2014";
|
|
3677
|
+
return `${(n / total * 100).toFixed(2)}%`;
|
|
3678
|
+
}
|
|
3679
|
+
function barPct(n, total) {
|
|
3680
|
+
if (total <= 0) return 0;
|
|
3681
|
+
return Math.min(100, n / total * 100);
|
|
3682
|
+
}
|
|
3683
|
+
function SendIcon() {
|
|
3684
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3685
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z" }),
|
|
3686
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "m21.854 2.147-10.94 10.939" })
|
|
3687
|
+
] });
|
|
3688
|
+
}
|
|
3689
|
+
function CheckIcon() {
|
|
3690
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3691
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M21.801 10A10 10 0 1 1 17 3.335" }),
|
|
3692
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "m9 11 3 3L22 4" })
|
|
3693
|
+
] });
|
|
3694
|
+
}
|
|
3695
|
+
function AlertIcon4() {
|
|
3696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3697
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
3698
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "m15 9-6 6" }),
|
|
3699
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "m9 9 6 6" })
|
|
3700
|
+
] });
|
|
3701
|
+
}
|
|
3702
|
+
function ChannelIcon({ channel }) {
|
|
3703
|
+
switch (channel) {
|
|
3704
|
+
case "email":
|
|
3705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3706
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }),
|
|
3707
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" })
|
|
3708
|
+
] });
|
|
3709
|
+
case "sms":
|
|
3710
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" }) });
|
|
3711
|
+
case "push":
|
|
3712
|
+
case "inapp":
|
|
3713
|
+
case "in-app":
|
|
3714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3715
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M10.268 21a2 2 0 0 0 3.464 0" }),
|
|
3716
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" })
|
|
3717
|
+
] });
|
|
3718
|
+
case "webhook":
|
|
3719
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3720
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M10 6.5a3.5 3.5 0 1 1 6 2.45" }),
|
|
3721
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M6 10.5a3.5 3.5 0 1 0 5 3.15" }),
|
|
3722
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { d: "M14 17.5a3.5 3.5 0 1 0 1-6.85" })
|
|
3723
|
+
] });
|
|
3724
|
+
default:
|
|
3725
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("circle", { cx: "12", cy: "12", r: "4" }) });
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3729
|
+
// src/delivery-log-explorer.tsx
|
|
3730
|
+
var import_react18 = require("react");
|
|
3731
|
+
var import_react_ui30 = require("@quanticjs/react-ui");
|
|
3732
|
+
var import_react_query26 = require("@quanticjs/react-query");
|
|
3733
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3734
|
+
var LIMIT6 = 20;
|
|
3735
|
+
var EMPTY_FILTERS = {
|
|
3736
|
+
channel: "",
|
|
3737
|
+
status: "",
|
|
3738
|
+
recipient: "",
|
|
3739
|
+
userId: "",
|
|
3740
|
+
from: "",
|
|
3741
|
+
to: ""
|
|
3742
|
+
};
|
|
3529
3743
|
function channelVariant(channel) {
|
|
3530
3744
|
switch (channel) {
|
|
3531
3745
|
case "email":
|
|
@@ -3569,186 +3783,98 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3569
3783
|
});
|
|
3570
3784
|
};
|
|
3571
3785
|
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" })
|
|
3786
|
+
const rows = data?.items ?? [];
|
|
3787
|
+
const totalPages = data?.totalPages ?? 1;
|
|
3788
|
+
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: [
|
|
3789
|
+
/* @__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: [
|
|
3790
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-channel", className: "flex flex-col gap-1 text-sm", children: [
|
|
3791
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Channel" }),
|
|
3792
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
3793
|
+
"select",
|
|
3794
|
+
{
|
|
3795
|
+
id: "dle-channel",
|
|
3796
|
+
value: draft.channel,
|
|
3797
|
+
onChange: (e) => setField("channel", e.target.value),
|
|
3798
|
+
className: FIELD_CLASS,
|
|
3799
|
+
children: [
|
|
3800
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "", children: "All" }),
|
|
3801
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "email", children: "Email" }),
|
|
3802
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("option", { value: "sms", children: "SMS" })
|
|
3803
|
+
]
|
|
3804
|
+
}
|
|
3805
|
+
)
|
|
3806
|
+
] }),
|
|
3807
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-status", className: "flex flex-col gap-1 text-sm", children: [
|
|
3808
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Status" }),
|
|
3809
|
+
/* @__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 })
|
|
3810
|
+
] }),
|
|
3811
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-recipient", className: "flex flex-col gap-1 text-sm", children: [
|
|
3812
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "Recipient" }),
|
|
3813
|
+
/* @__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 })
|
|
3814
|
+
] }),
|
|
3815
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-user", className: "flex flex-col gap-1 text-sm", children: [
|
|
3816
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "User ID" }),
|
|
3817
|
+
/* @__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 })
|
|
3818
|
+
] }),
|
|
3819
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-from", className: "flex flex-col gap-1 text-sm", children: [
|
|
3820
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "From" }),
|
|
3821
|
+
/* @__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 })
|
|
3822
|
+
] }),
|
|
3823
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { htmlFor: "dle-to", className: "flex flex-col gap-1 text-sm", children: [
|
|
3824
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-foreground", children: "To" }),
|
|
3825
|
+
/* @__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 })
|
|
3826
|
+
] }),
|
|
3827
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_ui30.Button, { type: "submit", className: "ms-auto", children: "Apply" })
|
|
3828
|
+
] }) }),
|
|
3829
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_ui30.Card, { children: [
|
|
3830
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PanelHeader, { title: "Attempts", subtitle: !isLoading && !isError ? `${data?.total ?? rows.length} matching entries` : void 0 }),
|
|
3831
|
+
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: [
|
|
3832
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3833
|
+
/* @__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: [
|
|
3834
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channel" }),
|
|
3835
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipient" }),
|
|
3836
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "User" }),
|
|
3837
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
3838
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Provider" }),
|
|
3839
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
|
|
3840
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
|
|
3699
3841
|
] }) }),
|
|
3700
|
-
/* @__PURE__ */ (0,
|
|
3701
|
-
/* @__PURE__ */ (0,
|
|
3702
|
-
/* @__PURE__ */ (0,
|
|
3703
|
-
/* @__PURE__ */ (0,
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3706
|
-
/* @__PURE__ */ (0,
|
|
3707
|
-
/* @__PURE__ */ (0,
|
|
3842
|
+
/* @__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: [
|
|
3843
|
+
/* @__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 }) }),
|
|
3844
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
|
|
3845
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
|
|
3846
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.status }),
|
|
3847
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
|
|
3848
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 tabular-nums text-muted-foreground", children: row.attempts }),
|
|
3849
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui30.formatDateTime)(row.createdAt) })
|
|
3708
3850
|
] }, row.id)) })
|
|
3709
3851
|
] }),
|
|
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 ",
|
|
3852
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3853
|
+
Pager,
|
|
3854
|
+
{
|
|
3855
|
+
label: "Delivery log pagination",
|
|
3723
3856
|
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
|
|
3857
|
+
totalPages,
|
|
3858
|
+
onPrev: () => setPage((p) => Math.max(1, p - 1)),
|
|
3859
|
+
onNext: () => setPage((p) => Math.min(totalPages, p + 1))
|
|
3860
|
+
}
|
|
3861
|
+
)
|
|
3862
|
+
] })
|
|
3863
|
+
] })
|
|
3864
|
+
] });
|
|
3865
|
+
}
|
|
3866
|
+
function InboxIcon() {
|
|
3867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
3868
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }),
|
|
3869
|
+
/* @__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
3870
|
] });
|
|
3745
3871
|
}
|
|
3746
3872
|
|
|
3747
3873
|
// src/quiet-hours-form.tsx
|
|
3748
3874
|
var import_react19 = require("react");
|
|
3749
|
-
var
|
|
3875
|
+
var import_react_ui31 = require("@quanticjs/react-ui");
|
|
3750
3876
|
var import_react_query27 = require("@quanticjs/react-query");
|
|
3751
|
-
var
|
|
3877
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3752
3878
|
var DEFAULTS = {
|
|
3753
3879
|
enabled: false,
|
|
3754
3880
|
start: "22:00",
|
|
@@ -3765,7 +3891,7 @@ function normalize5(raw) {
|
|
|
3765
3891
|
};
|
|
3766
3892
|
}
|
|
3767
3893
|
function QuietHoursForm({ basePath = "/api", className }) {
|
|
3768
|
-
const toast = (0,
|
|
3894
|
+
const toast = (0, import_react_ui31.useToast)();
|
|
3769
3895
|
const url = `${basePath}/notifications/config/quiet-hours`;
|
|
3770
3896
|
const { data, isLoading, isError, refetch } = (0, import_react_query27.useApiQuery)(
|
|
3771
3897
|
["quiet-hours"],
|
|
@@ -3789,23 +3915,23 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3789
3915
|
save.mutate(form);
|
|
3790
3916
|
};
|
|
3791
3917
|
if (isLoading) {
|
|
3792
|
-
return /* @__PURE__ */ (0,
|
|
3918
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
3793
3919
|
"div",
|
|
3794
3920
|
{
|
|
3795
3921
|
role: "status",
|
|
3796
3922
|
"aria-label": "Loading quiet hours",
|
|
3797
|
-
className: (0,
|
|
3923
|
+
className: (0, import_react_ui31.cn)("flex flex-col gap-2 p-4", className),
|
|
3798
3924
|
children: [
|
|
3799
|
-
/* @__PURE__ */ (0,
|
|
3800
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3925
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Loading quiet hours" }),
|
|
3926
|
+
[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
3927
|
]
|
|
3802
3928
|
}
|
|
3803
3929
|
);
|
|
3804
3930
|
}
|
|
3805
3931
|
if (isError) {
|
|
3806
|
-
return /* @__PURE__ */ (0,
|
|
3807
|
-
/* @__PURE__ */ (0,
|
|
3808
|
-
/* @__PURE__ */ (0,
|
|
3932
|
+
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: [
|
|
3933
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
|
|
3934
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3809
3935
|
"button",
|
|
3810
3936
|
{
|
|
3811
3937
|
type: "button",
|
|
@@ -3816,10 +3942,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3816
3942
|
)
|
|
3817
3943
|
] });
|
|
3818
3944
|
}
|
|
3819
|
-
return /* @__PURE__ */ (0,
|
|
3820
|
-
/* @__PURE__ */ (0,
|
|
3821
|
-
/* @__PURE__ */ (0,
|
|
3822
|
-
/* @__PURE__ */ (0,
|
|
3945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("form", { onSubmit, className: (0, import_react_ui31.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
3946
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
|
|
3947
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
3948
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3823
3949
|
"input",
|
|
3824
3950
|
{
|
|
3825
3951
|
type: "checkbox",
|
|
@@ -3830,10 +3956,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3830
3956
|
),
|
|
3831
3957
|
"Enable quiet hours"
|
|
3832
3958
|
] }),
|
|
3833
|
-
/* @__PURE__ */ (0,
|
|
3834
|
-
/* @__PURE__ */ (0,
|
|
3835
|
-
/* @__PURE__ */ (0,
|
|
3836
|
-
/* @__PURE__ */ (0,
|
|
3959
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
|
|
3960
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3961
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
|
|
3962
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3837
3963
|
"input",
|
|
3838
3964
|
{
|
|
3839
3965
|
id: "qh-start",
|
|
@@ -3844,9 +3970,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3844
3970
|
}
|
|
3845
3971
|
)
|
|
3846
3972
|
] }),
|
|
3847
|
-
/* @__PURE__ */ (0,
|
|
3848
|
-
/* @__PURE__ */ (0,
|
|
3849
|
-
/* @__PURE__ */ (0,
|
|
3973
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3974
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3850
3976
|
"input",
|
|
3851
3977
|
{
|
|
3852
3978
|
id: "qh-end",
|
|
@@ -3857,9 +3983,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3857
3983
|
}
|
|
3858
3984
|
)
|
|
3859
3985
|
] }),
|
|
3860
|
-
/* @__PURE__ */ (0,
|
|
3861
|
-
/* @__PURE__ */ (0,
|
|
3862
|
-
/* @__PURE__ */ (0,
|
|
3986
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3987
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
|
|
3988
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3863
3989
|
"input",
|
|
3864
3990
|
{
|
|
3865
3991
|
id: "qh-tz",
|
|
@@ -3871,7 +3997,7 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3871
3997
|
)
|
|
3872
3998
|
] })
|
|
3873
3999
|
] }),
|
|
3874
|
-
/* @__PURE__ */ (0,
|
|
4000
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3875
4001
|
"button",
|
|
3876
4002
|
{
|
|
3877
4003
|
type: "submit",
|
|
@@ -3885,9 +4011,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3885
4011
|
|
|
3886
4012
|
// src/frequency-cap-table.tsx
|
|
3887
4013
|
var import_react20 = require("react");
|
|
3888
|
-
var
|
|
4014
|
+
var import_react_ui32 = require("@quanticjs/react-ui");
|
|
3889
4015
|
var import_react_query28 = require("@quanticjs/react-query");
|
|
3890
|
-
var
|
|
4016
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3891
4017
|
function normalize6(raw) {
|
|
3892
4018
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.caps) ? raw.caps : [];
|
|
3893
4019
|
return list.map((entry) => {
|
|
@@ -3897,7 +4023,7 @@ function normalize6(raw) {
|
|
|
3897
4023
|
});
|
|
3898
4024
|
}
|
|
3899
4025
|
function FrequencyCapTable({ basePath = "/api", className }) {
|
|
3900
|
-
const toast = (0,
|
|
4026
|
+
const toast = (0, import_react_ui32.useToast)();
|
|
3901
4027
|
const url = `${basePath}/notifications/config/frequency-cap`;
|
|
3902
4028
|
const { data, isLoading, isError, refetch } = (0, import_react_query28.useApiQuery)(
|
|
3903
4029
|
["frequency-cap"],
|
|
@@ -3932,23 +4058,23 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3932
4058
|
setNewMax(0);
|
|
3933
4059
|
};
|
|
3934
4060
|
if (isLoading) {
|
|
3935
|
-
return /* @__PURE__ */ (0,
|
|
4061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
3936
4062
|
"div",
|
|
3937
4063
|
{
|
|
3938
4064
|
role: "status",
|
|
3939
4065
|
"aria-label": "Loading frequency caps",
|
|
3940
|
-
className: (0,
|
|
4066
|
+
className: (0, import_react_ui32.cn)("flex flex-col gap-2 p-4", className),
|
|
3941
4067
|
children: [
|
|
3942
|
-
/* @__PURE__ */ (0,
|
|
3943
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
4068
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "sr-only", children: "Loading frequency caps" }),
|
|
4069
|
+
[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
4070
|
]
|
|
3945
4071
|
}
|
|
3946
4072
|
);
|
|
3947
4073
|
}
|
|
3948
4074
|
if (isError) {
|
|
3949
|
-
return /* @__PURE__ */ (0,
|
|
3950
|
-
/* @__PURE__ */ (0,
|
|
3951
|
-
/* @__PURE__ */ (0,
|
|
4075
|
+
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: [
|
|
4076
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
|
|
4077
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3952
4078
|
"button",
|
|
3953
4079
|
{
|
|
3954
4080
|
type: "button",
|
|
@@ -3959,24 +4085,24 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3959
4085
|
)
|
|
3960
4086
|
] });
|
|
3961
4087
|
}
|
|
3962
|
-
return /* @__PURE__ */ (0,
|
|
3963
|
-
/* @__PURE__ */ (0,
|
|
3964
|
-
/* @__PURE__ */ (0,
|
|
3965
|
-
/* @__PURE__ */ (0,
|
|
3966
|
-
/* @__PURE__ */ (0,
|
|
3967
|
-
/* @__PURE__ */ (0,
|
|
3968
|
-
/* @__PURE__ */ (0,
|
|
4088
|
+
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: [
|
|
4089
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
|
|
4090
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("table", { className: "w-full text-sm", children: [
|
|
4091
|
+
/* @__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: [
|
|
4092
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
|
|
4093
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
|
|
4094
|
+
/* @__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
4095
|
] }) }),
|
|
3970
|
-
/* @__PURE__ */ (0,
|
|
4096
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("tbody", { children: caps.map((cap, index) => {
|
|
3971
4097
|
const inputId = `cap-${index}`;
|
|
3972
|
-
return /* @__PURE__ */ (0,
|
|
3973
|
-
/* @__PURE__ */ (0,
|
|
3974
|
-
/* @__PURE__ */ (0,
|
|
3975
|
-
/* @__PURE__ */ (0,
|
|
4098
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("tr", { className: "border-b border-border", children: [
|
|
4099
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
|
|
4100
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("td", { className: "px-4 py-3", children: [
|
|
4101
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("label", { htmlFor: inputId, className: "sr-only", children: [
|
|
3976
4102
|
"Max per day for ",
|
|
3977
4103
|
cap.type
|
|
3978
4104
|
] }),
|
|
3979
|
-
/* @__PURE__ */ (0,
|
|
4105
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3980
4106
|
"input",
|
|
3981
4107
|
{
|
|
3982
4108
|
id: inputId,
|
|
@@ -3988,7 +4114,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3988
4114
|
}
|
|
3989
4115
|
)
|
|
3990
4116
|
] }),
|
|
3991
|
-
/* @__PURE__ */ (0,
|
|
4117
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3992
4118
|
"button",
|
|
3993
4119
|
{
|
|
3994
4120
|
type: "button",
|
|
@@ -4000,10 +4126,10 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
4000
4126
|
] }, `${cap.type}-${index}`);
|
|
4001
4127
|
}) })
|
|
4002
4128
|
] }),
|
|
4003
|
-
/* @__PURE__ */ (0,
|
|
4004
|
-
/* @__PURE__ */ (0,
|
|
4005
|
-
/* @__PURE__ */ (0,
|
|
4006
|
-
/* @__PURE__ */ (0,
|
|
4129
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
4130
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4131
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
|
|
4132
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4007
4133
|
"input",
|
|
4008
4134
|
{
|
|
4009
4135
|
id: "cap-new-type",
|
|
@@ -4014,9 +4140,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
4014
4140
|
}
|
|
4015
4141
|
)
|
|
4016
4142
|
] }),
|
|
4017
|
-
/* @__PURE__ */ (0,
|
|
4018
|
-
/* @__PURE__ */ (0,
|
|
4019
|
-
/* @__PURE__ */ (0,
|
|
4143
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4144
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
|
|
4145
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4020
4146
|
"input",
|
|
4021
4147
|
{
|
|
4022
4148
|
id: "cap-new-max",
|
|
@@ -4028,7 +4154,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
4028
4154
|
}
|
|
4029
4155
|
)
|
|
4030
4156
|
] }),
|
|
4031
|
-
/* @__PURE__ */ (0,
|
|
4157
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4032
4158
|
"button",
|
|
4033
4159
|
{
|
|
4034
4160
|
type: "submit",
|
|
@@ -4037,7 +4163,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
4037
4163
|
}
|
|
4038
4164
|
)
|
|
4039
4165
|
] }),
|
|
4040
|
-
/* @__PURE__ */ (0,
|
|
4166
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4041
4167
|
"button",
|
|
4042
4168
|
{
|
|
4043
4169
|
type: "button",
|
|
@@ -4052,9 +4178,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
4052
4178
|
|
|
4053
4179
|
// src/tenant-config-form.tsx
|
|
4054
4180
|
var import_react21 = require("react");
|
|
4055
|
-
var
|
|
4181
|
+
var import_react_ui33 = require("@quanticjs/react-ui");
|
|
4056
4182
|
var import_react_query29 = require("@quanticjs/react-query");
|
|
4057
|
-
var
|
|
4183
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
4058
4184
|
function normalize7(raw) {
|
|
4059
4185
|
const obj = raw ?? {};
|
|
4060
4186
|
const toList = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
@@ -4072,15 +4198,15 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4072
4198
|
onAdd(value);
|
|
4073
4199
|
setDraft("");
|
|
4074
4200
|
};
|
|
4075
|
-
return /* @__PURE__ */ (0,
|
|
4076
|
-
/* @__PURE__ */ (0,
|
|
4077
|
-
/* @__PURE__ */ (0,
|
|
4201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
4202
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-sm font-medium text-foreground", children: label }),
|
|
4203
|
+
/* @__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
4204
|
"li",
|
|
4079
4205
|
{
|
|
4080
4206
|
className: "flex items-center gap-1 rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground",
|
|
4081
4207
|
children: [
|
|
4082
4208
|
value,
|
|
4083
|
-
/* @__PURE__ */ (0,
|
|
4209
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4084
4210
|
"button",
|
|
4085
4211
|
{
|
|
4086
4212
|
type: "button",
|
|
@@ -4094,13 +4220,13 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4094
4220
|
},
|
|
4095
4221
|
value
|
|
4096
4222
|
)) }),
|
|
4097
|
-
/* @__PURE__ */ (0,
|
|
4098
|
-
/* @__PURE__ */ (0,
|
|
4099
|
-
/* @__PURE__ */ (0,
|
|
4223
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
|
|
4224
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4225
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { htmlFor: id, className: "sr-only", children: [
|
|
4100
4226
|
"Add to ",
|
|
4101
4227
|
label
|
|
4102
4228
|
] }),
|
|
4103
|
-
/* @__PURE__ */ (0,
|
|
4229
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4104
4230
|
"input",
|
|
4105
4231
|
{
|
|
4106
4232
|
id,
|
|
@@ -4111,7 +4237,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4111
4237
|
}
|
|
4112
4238
|
)
|
|
4113
4239
|
] }),
|
|
4114
|
-
/* @__PURE__ */ (0,
|
|
4240
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4115
4241
|
"button",
|
|
4116
4242
|
{
|
|
4117
4243
|
type: "submit",
|
|
@@ -4123,7 +4249,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4123
4249
|
] });
|
|
4124
4250
|
}
|
|
4125
4251
|
function TenantConfigForm({ basePath = "/api", className }) {
|
|
4126
|
-
const toast = (0,
|
|
4252
|
+
const toast = (0, import_react_ui33.useToast)();
|
|
4127
4253
|
const url = `${basePath}/admin/notification-config`;
|
|
4128
4254
|
const { data, isLoading, isError, refetch } = (0, import_react_query29.useApiQuery)(
|
|
4129
4255
|
["tenant-config"],
|
|
@@ -4176,23 +4302,23 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4176
4302
|
save.mutate(config);
|
|
4177
4303
|
};
|
|
4178
4304
|
if (isLoading) {
|
|
4179
|
-
return /* @__PURE__ */ (0,
|
|
4305
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
4180
4306
|
"div",
|
|
4181
4307
|
{
|
|
4182
4308
|
role: "status",
|
|
4183
4309
|
"aria-label": "Loading tenant configuration",
|
|
4184
|
-
className: (0,
|
|
4310
|
+
className: (0, import_react_ui33.cn)("flex flex-col gap-2 p-4", className),
|
|
4185
4311
|
children: [
|
|
4186
|
-
/* @__PURE__ */ (0,
|
|
4187
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
4312
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "sr-only", children: "Loading tenant configuration" }),
|
|
4313
|
+
[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
4314
|
]
|
|
4189
4315
|
}
|
|
4190
4316
|
);
|
|
4191
4317
|
}
|
|
4192
4318
|
if (isError) {
|
|
4193
|
-
return /* @__PURE__ */ (0,
|
|
4194
|
-
/* @__PURE__ */ (0,
|
|
4195
|
-
/* @__PURE__ */ (0,
|
|
4319
|
+
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: [
|
|
4320
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
|
|
4321
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4196
4322
|
"button",
|
|
4197
4323
|
{
|
|
4198
4324
|
type: "button",
|
|
@@ -4204,17 +4330,17 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4204
4330
|
] });
|
|
4205
4331
|
}
|
|
4206
4332
|
const errorId = "tenant-config-subset-error";
|
|
4207
|
-
return /* @__PURE__ */ (0,
|
|
4333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
4208
4334
|
"form",
|
|
4209
4335
|
{
|
|
4210
4336
|
onSubmit,
|
|
4211
|
-
className: (0,
|
|
4337
|
+
className: (0, import_react_ui33.cn)("flex flex-col gap-5", className),
|
|
4212
4338
|
noValidate: true,
|
|
4213
4339
|
"aria-invalid": subsetError ? "true" : void 0,
|
|
4214
4340
|
"aria-describedby": subsetError ? errorId : void 0,
|
|
4215
4341
|
children: [
|
|
4216
|
-
/* @__PURE__ */ (0,
|
|
4217
|
-
/* @__PURE__ */ (0,
|
|
4342
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
|
|
4343
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4218
4344
|
TagEditor,
|
|
4219
4345
|
{
|
|
4220
4346
|
id: "tc-notification-types",
|
|
@@ -4224,7 +4350,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4224
4350
|
onRemove: removeType
|
|
4225
4351
|
}
|
|
4226
4352
|
),
|
|
4227
|
-
/* @__PURE__ */ (0,
|
|
4353
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4228
4354
|
TagEditor,
|
|
4229
4355
|
{
|
|
4230
4356
|
id: "tc-immediate-email-types",
|
|
@@ -4234,8 +4360,8 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4234
4360
|
onRemove: removeImmediate
|
|
4235
4361
|
}
|
|
4236
4362
|
),
|
|
4237
|
-
subsetError && /* @__PURE__ */ (0,
|
|
4238
|
-
/* @__PURE__ */ (0,
|
|
4363
|
+
subsetError && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
|
|
4364
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4239
4365
|
"button",
|
|
4240
4366
|
{
|
|
4241
4367
|
type: "submit",
|
|
@@ -4251,9 +4377,9 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4251
4377
|
|
|
4252
4378
|
// src/tracking-config-form.tsx
|
|
4253
4379
|
var import_react22 = require("react");
|
|
4254
|
-
var
|
|
4380
|
+
var import_react_ui34 = require("@quanticjs/react-ui");
|
|
4255
4381
|
var import_react_query30 = require("@quanticjs/react-query");
|
|
4256
|
-
var
|
|
4382
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
4257
4383
|
function normalize8(raw) {
|
|
4258
4384
|
const obj = raw ?? {};
|
|
4259
4385
|
const bool = (...keys) => {
|
|
@@ -4268,7 +4394,7 @@ function normalize8(raw) {
|
|
|
4268
4394
|
};
|
|
4269
4395
|
}
|
|
4270
4396
|
function TrackingConfigForm({ basePath = "/api", className }) {
|
|
4271
|
-
const toast = (0,
|
|
4397
|
+
const toast = (0, import_react_ui34.useToast)();
|
|
4272
4398
|
const url = `${basePath}/analytics/notifications/tracking-config`;
|
|
4273
4399
|
const { data, isLoading, isError, refetch } = (0, import_react_query30.useApiQuery)(
|
|
4274
4400
|
["tracking-config"],
|
|
@@ -4298,23 +4424,23 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4298
4424
|
save.mutate(form);
|
|
4299
4425
|
};
|
|
4300
4426
|
if (isLoading) {
|
|
4301
|
-
return /* @__PURE__ */ (0,
|
|
4427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
4302
4428
|
"div",
|
|
4303
4429
|
{
|
|
4304
4430
|
role: "status",
|
|
4305
4431
|
"aria-label": "Loading tracking configuration",
|
|
4306
|
-
className: (0,
|
|
4432
|
+
className: (0, import_react_ui34.cn)("flex flex-col gap-2 p-4", className),
|
|
4307
4433
|
children: [
|
|
4308
|
-
/* @__PURE__ */ (0,
|
|
4309
|
-
[0, 1].map((i) => /* @__PURE__ */ (0,
|
|
4434
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "sr-only", children: "Loading tracking configuration" }),
|
|
4435
|
+
[0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
4310
4436
|
]
|
|
4311
4437
|
}
|
|
4312
4438
|
);
|
|
4313
4439
|
}
|
|
4314
4440
|
if (isError) {
|
|
4315
|
-
return /* @__PURE__ */ (0,
|
|
4316
|
-
/* @__PURE__ */ (0,
|
|
4317
|
-
/* @__PURE__ */ (0,
|
|
4441
|
+
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: [
|
|
4442
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
|
|
4443
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4318
4444
|
"button",
|
|
4319
4445
|
{
|
|
4320
4446
|
type: "button",
|
|
@@ -4325,10 +4451,10 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4325
4451
|
)
|
|
4326
4452
|
] });
|
|
4327
4453
|
}
|
|
4328
|
-
return /* @__PURE__ */ (0,
|
|
4329
|
-
/* @__PURE__ */ (0,
|
|
4330
|
-
/* @__PURE__ */ (0,
|
|
4331
|
-
/* @__PURE__ */ (0,
|
|
4454
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("form", { onSubmit, className: (0, import_react_ui34.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
4455
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
|
|
4456
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
4457
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4332
4458
|
"input",
|
|
4333
4459
|
{
|
|
4334
4460
|
type: "checkbox",
|
|
@@ -4339,8 +4465,8 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4339
4465
|
),
|
|
4340
4466
|
"Open tracking"
|
|
4341
4467
|
] }),
|
|
4342
|
-
/* @__PURE__ */ (0,
|
|
4343
|
-
/* @__PURE__ */ (0,
|
|
4468
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
4469
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4344
4470
|
"input",
|
|
4345
4471
|
{
|
|
4346
4472
|
type: "checkbox",
|
|
@@ -4351,7 +4477,7 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4351
4477
|
),
|
|
4352
4478
|
"Click tracking"
|
|
4353
4479
|
] }),
|
|
4354
|
-
/* @__PURE__ */ (0,
|
|
4480
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4355
4481
|
"button",
|
|
4356
4482
|
{
|
|
4357
4483
|
type: "submit",
|
|
@@ -4365,15 +4491,15 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4365
4491
|
|
|
4366
4492
|
// src/api-key-manager.tsx
|
|
4367
4493
|
var import_react23 = require("react");
|
|
4368
|
-
var
|
|
4494
|
+
var import_react_ui35 = require("@quanticjs/react-ui");
|
|
4369
4495
|
var import_react_query31 = require("@quanticjs/react-query");
|
|
4370
|
-
var
|
|
4496
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
4371
4497
|
function normalize9(raw) {
|
|
4372
4498
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
|
|
4373
4499
|
return list;
|
|
4374
4500
|
}
|
|
4375
4501
|
function ApiKeyManager({ basePath = "/api", className }) {
|
|
4376
|
-
const toast = (0,
|
|
4502
|
+
const toast = (0, import_react_ui35.useToast)();
|
|
4377
4503
|
const url = `${basePath}/v1/admin/api-keys`;
|
|
4378
4504
|
const { data, isLoading, isError, refetch } = (0, import_react_query31.useApiQuery)(
|
|
4379
4505
|
["api-keys"],
|
|
@@ -4425,110 +4551,77 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4425
4551
|
revoke.mutate(id);
|
|
4426
4552
|
}
|
|
4427
4553
|
};
|
|
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" }) })
|
|
4554
|
+
const rows = normalize9(data);
|
|
4555
|
+
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: [
|
|
4556
|
+
/* @__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: [
|
|
4557
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
|
|
4558
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-foreground", children: "New key name" }),
|
|
4559
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), className: FIELD_CLASS })
|
|
4560
|
+
] }),
|
|
4561
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("label", { className: "flex flex-1 flex-col gap-1 text-sm", children: [
|
|
4562
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-foreground", children: "Application (optional)" }),
|
|
4563
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4564
|
+
"input",
|
|
4565
|
+
{
|
|
4566
|
+
type: "text",
|
|
4567
|
+
value: applicationKey,
|
|
4568
|
+
onChange: (e) => setApplicationKey(e.target.value),
|
|
4569
|
+
placeholder: "delivery-hub",
|
|
4570
|
+
className: FIELD_CLASS
|
|
4571
|
+
}
|
|
4572
|
+
)
|
|
4573
|
+
] }),
|
|
4574
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.Button, { type: "submit", disabled: create.isPending, children: create.isPending ? "Creating\u2026" : "Create key" })
|
|
4575
|
+
] }) }),
|
|
4576
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_ui35.Card, { children: [
|
|
4577
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(PanelHeader, { title: "Keys", subtitle: !isLoading && !isError ? `${rows.length} keys` : void 0 }),
|
|
4578
|
+
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: [
|
|
4579
|
+
/* @__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: [
|
|
4580
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Name" }),
|
|
4581
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Prefix" }),
|
|
4582
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Application" }),
|
|
4583
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
4584
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
|
|
4585
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last used" }),
|
|
4586
|
+
/* @__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
4587
|
] }) }),
|
|
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
|
-
|
|
4588
|
+
/* @__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: [
|
|
4589
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.name }),
|
|
4590
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
|
|
4591
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
|
|
4592
|
+
/* @__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" }) }),
|
|
4593
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui35.formatDateTime)(row.createdAt) }),
|
|
4594
|
+
/* @__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" }),
|
|
4595
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "px-4 py-3 text-end", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4596
|
+
import_react_ui35.Button,
|
|
4509
4597
|
{
|
|
4510
4598
|
type: "button",
|
|
4599
|
+
variant: "ghost",
|
|
4600
|
+
size: "sm",
|
|
4601
|
+
className: "text-destructive",
|
|
4511
4602
|
disabled: revoke.isPending || row.revoked,
|
|
4512
4603
|
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
4604
|
children: "Revoke"
|
|
4515
4605
|
}
|
|
4516
4606
|
) })
|
|
4517
4607
|
] }, row.id)) })
|
|
4518
|
-
] })
|
|
4519
|
-
}
|
|
4520
|
-
}
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4608
|
+
] })
|
|
4609
|
+
] })
|
|
4610
|
+
] });
|
|
4611
|
+
}
|
|
4612
|
+
function KeyIcon() {
|
|
4613
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
4614
|
+
/* @__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" }),
|
|
4615
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "m21 2-9.6 9.6" }),
|
|
4616
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: "7.5", cy: "15.5", r: "5.5" })
|
|
4524
4617
|
] });
|
|
4525
4618
|
}
|
|
4526
4619
|
|
|
4527
4620
|
// src/application-registry-panel.tsx
|
|
4528
4621
|
var import_react24 = require("react");
|
|
4529
|
-
var
|
|
4622
|
+
var import_react_ui36 = require("@quanticjs/react-ui");
|
|
4530
4623
|
var import_react_query32 = require("@quanticjs/react-query");
|
|
4531
|
-
var
|
|
4624
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
4532
4625
|
function normalize10(raw) {
|
|
4533
4626
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
|
|
4534
4627
|
return list;
|
|
@@ -4537,7 +4630,7 @@ function ApplicationRegistryPanel({
|
|
|
4537
4630
|
basePath = "/api",
|
|
4538
4631
|
className
|
|
4539
4632
|
}) {
|
|
4540
|
-
const toast = (0,
|
|
4633
|
+
const toast = (0, import_react_ui36.useToast)();
|
|
4541
4634
|
const url = `${basePath}/admin/applications`;
|
|
4542
4635
|
const { data, isLoading, isError, refetch } = (0, import_react_query32.useApiQuery)(
|
|
4543
4636
|
["applications"],
|
|
@@ -4574,10 +4667,10 @@ function ApplicationRegistryPanel({
|
|
|
4574
4667
|
if (!k || !name) return;
|
|
4575
4668
|
register.mutate({ key: k, displayName: name, description: description.trim() || void 0 });
|
|
4576
4669
|
};
|
|
4577
|
-
const registerForm = /* @__PURE__ */ (0,
|
|
4578
|
-
/* @__PURE__ */ (0,
|
|
4579
|
-
/* @__PURE__ */ (0,
|
|
4580
|
-
/* @__PURE__ */ (0,
|
|
4670
|
+
const registerForm = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
4671
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4672
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
|
|
4673
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4581
4674
|
"input",
|
|
4582
4675
|
{
|
|
4583
4676
|
id: "application-key",
|
|
@@ -4589,9 +4682,9 @@ function ApplicationRegistryPanel({
|
|
|
4589
4682
|
}
|
|
4590
4683
|
)
|
|
4591
4684
|
] }),
|
|
4592
|
-
/* @__PURE__ */ (0,
|
|
4593
|
-
/* @__PURE__ */ (0,
|
|
4594
|
-
/* @__PURE__ */ (0,
|
|
4685
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4686
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
|
|
4687
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4595
4688
|
"input",
|
|
4596
4689
|
{
|
|
4597
4690
|
id: "application-name",
|
|
@@ -4603,9 +4696,9 @@ function ApplicationRegistryPanel({
|
|
|
4603
4696
|
}
|
|
4604
4697
|
)
|
|
4605
4698
|
] }),
|
|
4606
|
-
/* @__PURE__ */ (0,
|
|
4607
|
-
/* @__PURE__ */ (0,
|
|
4608
|
-
/* @__PURE__ */ (0,
|
|
4699
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4700
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
|
|
4701
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4609
4702
|
"input",
|
|
4610
4703
|
{
|
|
4611
4704
|
id: "application-description",
|
|
@@ -4616,7 +4709,7 @@ function ApplicationRegistryPanel({
|
|
|
4616
4709
|
}
|
|
4617
4710
|
)
|
|
4618
4711
|
] }),
|
|
4619
|
-
/* @__PURE__ */ (0,
|
|
4712
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4620
4713
|
"button",
|
|
4621
4714
|
{
|
|
4622
4715
|
type: "submit",
|
|
@@ -4627,23 +4720,23 @@ function ApplicationRegistryPanel({
|
|
|
4627
4720
|
)
|
|
4628
4721
|
] });
|
|
4629
4722
|
if (isLoading) {
|
|
4630
|
-
return /* @__PURE__ */ (0,
|
|
4723
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
4631
4724
|
"div",
|
|
4632
4725
|
{
|
|
4633
4726
|
role: "status",
|
|
4634
4727
|
"aria-label": "Loading applications",
|
|
4635
|
-
className: (0,
|
|
4728
|
+
className: (0, import_react_ui36.cn)("flex flex-col gap-2 p-4", className),
|
|
4636
4729
|
children: [
|
|
4637
|
-
/* @__PURE__ */ (0,
|
|
4638
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
4730
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sr-only", children: "Loading applications" }),
|
|
4731
|
+
[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
4732
|
]
|
|
4640
4733
|
}
|
|
4641
4734
|
);
|
|
4642
4735
|
}
|
|
4643
4736
|
if (isError) {
|
|
4644
|
-
return /* @__PURE__ */ (0,
|
|
4645
|
-
/* @__PURE__ */ (0,
|
|
4646
|
-
/* @__PURE__ */ (0,
|
|
4737
|
+
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: [
|
|
4738
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
|
|
4739
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4647
4740
|
"button",
|
|
4648
4741
|
{
|
|
4649
4742
|
type: "button",
|
|
@@ -4655,22 +4748,22 @@ function ApplicationRegistryPanel({
|
|
|
4655
4748
|
] });
|
|
4656
4749
|
}
|
|
4657
4750
|
const apps = normalize10(data);
|
|
4658
|
-
return /* @__PURE__ */ (0,
|
|
4751
|
+
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
4752
|
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,
|
|
4753
|
+
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: [
|
|
4754
|
+
/* @__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: [
|
|
4755
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Key" }),
|
|
4756
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Name" }),
|
|
4757
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Status" }),
|
|
4758
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Created" }),
|
|
4759
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("th", { className: "py-2 font-medium", children: "Actions" })
|
|
4667
4760
|
] }) }),
|
|
4668
|
-
/* @__PURE__ */ (0,
|
|
4669
|
-
/* @__PURE__ */ (0,
|
|
4670
|
-
/* @__PURE__ */ (0,
|
|
4671
|
-
/* @__PURE__ */ (0,
|
|
4672
|
-
/* @__PURE__ */ (0,
|
|
4673
|
-
/* @__PURE__ */ (0,
|
|
4761
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("tbody", { children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("tr", { className: "border-b border-border", children: [
|
|
4762
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 font-mono text-foreground", children: app.key }),
|
|
4763
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 text-foreground", children: app.displayName }),
|
|
4764
|
+
/* @__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 }) }),
|
|
4765
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2 text-muted-foreground", children: (0, import_react_ui36.formatDateTime)(app.createdAt) }),
|
|
4766
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
4674
4767
|
"button",
|
|
4675
4768
|
{
|
|
4676
4769
|
type: "button",
|