@quanticjs/notification-ui 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.cjs +1060 -950
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +1040 -931
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
FunnelStats: () => FunnelStats,
|
|
36
36
|
MissingTranslationsPanel: () => MissingTranslationsPanel,
|
|
37
37
|
NotificationBell: () => NotificationBell,
|
|
38
|
+
NotificationCenter: () => NotificationCenter,
|
|
38
39
|
NotificationInbox: () => NotificationInbox,
|
|
39
40
|
NotificationPreferences: () => NotificationPreferences,
|
|
40
41
|
NotificationProvider: () => NotificationProvider,
|
|
@@ -528,9 +529,117 @@ function InboxRow({
|
|
|
528
529
|
) });
|
|
529
530
|
}
|
|
530
531
|
|
|
531
|
-
// src/
|
|
532
|
+
// src/notification-center.tsx
|
|
532
533
|
var import_react5 = require("react");
|
|
533
|
-
var
|
|
534
|
+
var import_react_ui5 = require("@quanticjs/react-ui");
|
|
535
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
536
|
+
function BellIcon() {
|
|
537
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
538
|
+
"svg",
|
|
539
|
+
{
|
|
540
|
+
"aria-hidden": true,
|
|
541
|
+
viewBox: "0 0 24 24",
|
|
542
|
+
fill: "none",
|
|
543
|
+
stroke: "currentColor",
|
|
544
|
+
strokeWidth: "2",
|
|
545
|
+
strokeLinecap: "round",
|
|
546
|
+
strokeLinejoin: "round",
|
|
547
|
+
className: "h-5 w-5",
|
|
548
|
+
children: [
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
551
|
+
]
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
function NotificationCenter({
|
|
556
|
+
appId,
|
|
557
|
+
basePath,
|
|
558
|
+
pollIntervalMs,
|
|
559
|
+
className,
|
|
560
|
+
panelClassName
|
|
561
|
+
}) {
|
|
562
|
+
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
563
|
+
const containerRef = (0, import_react5.useRef)(null);
|
|
564
|
+
const panelRef = (0, import_react5.useRef)(null);
|
|
565
|
+
const { data } = useUnreadCount({ appId, basePath, pollIntervalMs });
|
|
566
|
+
const unreadCount = data?.count ?? 0;
|
|
567
|
+
const badge = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
568
|
+
const label = unreadCount === 0 ? "No unread notifications" : `${unreadCount} unread notifications`;
|
|
569
|
+
const close = (0, import_react5.useCallback)(() => setOpen(false), []);
|
|
570
|
+
(0, import_react5.useEffect)(() => {
|
|
571
|
+
if (!open) return;
|
|
572
|
+
const onKey = (e) => {
|
|
573
|
+
if (e.key === "Escape") close();
|
|
574
|
+
};
|
|
575
|
+
const onPointer = (e) => {
|
|
576
|
+
const c = containerRef.current;
|
|
577
|
+
if (c && e.target instanceof Node && !c.contains(e.target)) close();
|
|
578
|
+
};
|
|
579
|
+
document.addEventListener("keydown", onKey);
|
|
580
|
+
document.addEventListener("mousedown", onPointer);
|
|
581
|
+
return () => {
|
|
582
|
+
document.removeEventListener("keydown", onKey);
|
|
583
|
+
document.removeEventListener("mousedown", onPointer);
|
|
584
|
+
};
|
|
585
|
+
}, [open, close]);
|
|
586
|
+
(0, import_react_ui5.useFocusTrap)(panelRef, open);
|
|
587
|
+
const panelExit = (0, import_react_ui5.useExitAnimation)(open);
|
|
588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: containerRef, className: (0, import_react_ui5.cn)("relative inline-block", className), children: [
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
590
|
+
"button",
|
|
591
|
+
{
|
|
592
|
+
type: "button",
|
|
593
|
+
"aria-label": label,
|
|
594
|
+
"aria-haspopup": "dialog",
|
|
595
|
+
"aria-expanded": open,
|
|
596
|
+
onClick: () => setOpen((p) => !p),
|
|
597
|
+
className: "relative inline-flex h-10 w-10 items-center justify-center rounded-full text-foreground hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
|
|
598
|
+
children: [
|
|
599
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BellIcon, {}),
|
|
600
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "status", className: "sr-only", children: label }),
|
|
601
|
+
unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
602
|
+
"span",
|
|
603
|
+
{
|
|
604
|
+
"aria-hidden": true,
|
|
605
|
+
className: "absolute -end-0.5 -top-0.5 inline-flex min-w-5 items-center justify-center rounded-full bg-destructive px-1.5 text-xs font-semibold text-destructive-foreground",
|
|
606
|
+
children: badge
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
]
|
|
610
|
+
}
|
|
611
|
+
),
|
|
612
|
+
panelExit.mounted && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
613
|
+
"div",
|
|
614
|
+
{
|
|
615
|
+
ref: panelRef,
|
|
616
|
+
role: "dialog",
|
|
617
|
+
"aria-label": "Notifications",
|
|
618
|
+
tabIndex: -1,
|
|
619
|
+
"data-state": panelExit.state,
|
|
620
|
+
onAnimationEnd: panelExit.onAnimationEnd,
|
|
621
|
+
className: (0, import_react_ui5.cn)(
|
|
622
|
+
"absolute end-0 z-(--z-popover) mt-2 w-96 overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-raised",
|
|
623
|
+
panelExit.state === "open" ? "animate-pop-in" : "animate-pop-out [animation-fill-mode:forwards]",
|
|
624
|
+
panelClassName
|
|
625
|
+
),
|
|
626
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
627
|
+
NotificationInbox,
|
|
628
|
+
{
|
|
629
|
+
appId,
|
|
630
|
+
basePath,
|
|
631
|
+
pollIntervalMs,
|
|
632
|
+
className: "max-h-[28rem] overflow-y-auto"
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
] });
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/delivery-analytics-page.tsx
|
|
641
|
+
var import_react6 = require("react");
|
|
642
|
+
var import_react_ui9 = require("@quanticjs/react-ui");
|
|
534
643
|
|
|
535
644
|
// src/use-delivery-analytics.ts
|
|
536
645
|
var import_react_query5 = require("@quanticjs/react-query");
|
|
@@ -582,8 +691,8 @@ function useDeliveryTypes({
|
|
|
582
691
|
}
|
|
583
692
|
|
|
584
693
|
// src/funnel-stats.tsx
|
|
585
|
-
var
|
|
586
|
-
var
|
|
694
|
+
var import_react_ui6 = require("@quanticjs/react-ui");
|
|
695
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
587
696
|
var pct = (n) => `${(n * 100).toFixed(1)}%`;
|
|
588
697
|
function FunnelStats({ funnel, className }) {
|
|
589
698
|
const cards = [
|
|
@@ -596,13 +705,13 @@ function FunnelStats({ funnel, className }) {
|
|
|
596
705
|
{ label: "Bounce rate", value: pct(funnel.bounceRate) },
|
|
597
706
|
{ label: "Delivery rate", value: pct(funnel.deliveryRate) }
|
|
598
707
|
];
|
|
599
|
-
return /* @__PURE__ */ (0,
|
|
708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: (0, import_react_ui6.cn)("grid grid-cols-2 gap-3 sm:grid-cols-4", className), "aria-label": "Delivery funnel", children: cards.map((c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
600
709
|
"li",
|
|
601
710
|
{
|
|
602
711
|
className: "flex flex-col gap-1 rounded-lg border border-border bg-card p-4",
|
|
603
712
|
children: [
|
|
604
|
-
/* @__PURE__ */ (0,
|
|
605
|
-
/* @__PURE__ */ (0,
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: c.label }),
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-2xl font-semibold text-foreground", children: c.value })
|
|
606
715
|
]
|
|
607
716
|
},
|
|
608
717
|
c.label
|
|
@@ -610,48 +719,48 @@ function FunnelStats({ funnel, className }) {
|
|
|
610
719
|
}
|
|
611
720
|
|
|
612
721
|
// src/trend-chart.tsx
|
|
613
|
-
var
|
|
722
|
+
var import_react_ui7 = require("@quanticjs/react-ui");
|
|
614
723
|
var import_recharts = require("recharts");
|
|
615
|
-
var
|
|
724
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
616
725
|
function TrendChart({ data, className }) {
|
|
617
|
-
return /* @__PURE__ */ (0,
|
|
618
|
-
/* @__PURE__ */ (0,
|
|
619
|
-
/* @__PURE__ */ (0,
|
|
620
|
-
/* @__PURE__ */ (0,
|
|
621
|
-
/* @__PURE__ */ (0,
|
|
622
|
-
/* @__PURE__ */ (0,
|
|
623
|
-
/* @__PURE__ */ (0,
|
|
624
|
-
/* @__PURE__ */ (0,
|
|
625
|
-
/* @__PURE__ */ (0,
|
|
726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: (0, import_react_ui7.cn)("h-72 w-full", className), role: "img", "aria-label": "Delivery trend over time", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_recharts.LineChart, { data, margin: { top: 8, right: 16, bottom: 8, left: 0 }, children: [
|
|
727
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }),
|
|
728
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.XAxis, { dataKey: "day", stroke: "hsl(var(--muted-foreground))", fontSize: 12 }),
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.YAxis, { stroke: "hsl(var(--muted-foreground))", fontSize: 12, allowDecimals: false }),
|
|
730
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Tooltip, {}),
|
|
731
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "sends", stroke: "hsl(var(--primary))", dot: false }),
|
|
732
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "delivered", stroke: "hsl(var(--chart-2, var(--primary)))", dot: false }),
|
|
733
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "opened", stroke: "hsl(var(--chart-3, var(--primary)))", dot: false }),
|
|
734
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_recharts.Line, { type: "monotone", dataKey: "clicked", stroke: "hsl(var(--chart-4, var(--primary)))", dot: false })
|
|
626
735
|
] }) }) });
|
|
627
736
|
}
|
|
628
737
|
|
|
629
738
|
// src/type-table.tsx
|
|
630
|
-
var
|
|
631
|
-
var
|
|
739
|
+
var import_react_ui8 = require("@quanticjs/react-ui");
|
|
740
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
632
741
|
var pct2 = (n) => `${(n * 100).toFixed(1)}%`;
|
|
633
742
|
function TypeTable({ rows, className }) {
|
|
634
|
-
return /* @__PURE__ */ (0,
|
|
635
|
-
/* @__PURE__ */ (0,
|
|
636
|
-
/* @__PURE__ */ (0,
|
|
637
|
-
/* @__PURE__ */ (0,
|
|
638
|
-
/* @__PURE__ */ (0,
|
|
639
|
-
/* @__PURE__ */ (0,
|
|
640
|
-
/* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("table", { className: (0, import_react_ui8.cn)("w-full border-collapse text-sm", className), children: [
|
|
744
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("caption", { className: "sr-only", children: "Delivery breakdown by notification type" }),
|
|
745
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
746
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
|
|
747
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Sends" }),
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivered" }),
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Open rate" }),
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("th", { scope: "col", className: "py-2 font-medium", children: "Click rate" })
|
|
642
751
|
] }) }),
|
|
643
|
-
/* @__PURE__ */ (0,
|
|
644
|
-
/* @__PURE__ */ (0,
|
|
645
|
-
/* @__PURE__ */ (0,
|
|
646
|
-
/* @__PURE__ */ (0,
|
|
647
|
-
/* @__PURE__ */ (0,
|
|
648
|
-
/* @__PURE__ */ (0,
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("tbody", { children: rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("tr", { className: "border-b border-border/50", children: [
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "py-2 pe-4 font-medium text-foreground", children: r.type }),
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "py-2 pe-4 text-foreground", children: r.sends }),
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "py-2 pe-4 text-foreground", children: r.delivered }),
|
|
756
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "py-2 pe-4 text-foreground", children: pct2(r.openRate) }),
|
|
757
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("td", { className: "py-2 text-foreground", children: pct2(r.clickRate) })
|
|
649
758
|
] }, r.type)) })
|
|
650
759
|
] });
|
|
651
760
|
}
|
|
652
761
|
|
|
653
762
|
// src/delivery-analytics-page.tsx
|
|
654
|
-
var
|
|
763
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
655
764
|
var DEFAULT_FROM = "2026-06-01";
|
|
656
765
|
var DEFAULT_TO = "2026-06-30";
|
|
657
766
|
var CHANNELS = ["", "email", "inapp", "push", "sms"];
|
|
@@ -660,36 +769,36 @@ function DeliveryAnalyticsPage({
|
|
|
660
769
|
organizationId,
|
|
661
770
|
className
|
|
662
771
|
}) {
|
|
663
|
-
const [channel, setChannel] = (0,
|
|
664
|
-
const [from, setFrom] = (0,
|
|
665
|
-
const [to, setTo] = (0,
|
|
772
|
+
const [channel, setChannel] = (0, import_react6.useState)("");
|
|
773
|
+
const [from, setFrom] = (0, import_react6.useState)(DEFAULT_FROM);
|
|
774
|
+
const [to, setTo] = (0, import_react6.useState)(DEFAULT_TO);
|
|
666
775
|
const filters = { from, to, channel: channel || void 0, organizationId, basePath };
|
|
667
776
|
const summary = useDeliveryAnalytics(filters);
|
|
668
777
|
const funnel = useFunnelStats(filters);
|
|
669
778
|
const types = useDeliveryTypes({ from, to, organizationId, basePath });
|
|
670
779
|
const isLoading = summary.isLoading || funnel.isLoading || types.isLoading;
|
|
671
780
|
const isError = summary.isError || funnel.isError || types.isError;
|
|
672
|
-
return /* @__PURE__ */ (0,
|
|
673
|
-
/* @__PURE__ */ (0,
|
|
674
|
-
/* @__PURE__ */ (0,
|
|
675
|
-
/* @__PURE__ */ (0,
|
|
781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("main", { className: (0, import_react_ui9.cn)("flex flex-col gap-6 p-4 sm:p-6", className), children: [
|
|
782
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("header", { className: "flex flex-col gap-1", children: [
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-xl font-semibold text-foreground", children: "Delivery Analytics" }),
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-muted-foreground", children: "Cross-channel send, open, and click performance." })
|
|
676
785
|
] }),
|
|
677
|
-
/* @__PURE__ */ (0,
|
|
678
|
-
/* @__PURE__ */ (0,
|
|
679
|
-
/* @__PURE__ */ (0,
|
|
680
|
-
/* @__PURE__ */ (0,
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-wrap items-end gap-3", children: [
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex flex-col gap-1 text-sm", children: [
|
|
788
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-medium text-foreground", children: "Channel" }),
|
|
789
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
681
790
|
"select",
|
|
682
791
|
{
|
|
683
792
|
value: channel,
|
|
684
793
|
onChange: (e) => setChannel(e.target.value),
|
|
685
794
|
className: "rounded border border-border bg-background px-2 py-1.5 text-foreground",
|
|
686
|
-
children: CHANNELS.map((c) => /* @__PURE__ */ (0,
|
|
795
|
+
children: CHANNELS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: c, children: c === "" ? "All channels" : c }, c || "all"))
|
|
687
796
|
}
|
|
688
797
|
)
|
|
689
798
|
] }),
|
|
690
|
-
/* @__PURE__ */ (0,
|
|
691
|
-
/* @__PURE__ */ (0,
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
799
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex flex-col gap-1 text-sm", children: [
|
|
800
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-medium text-foreground", children: "From" }),
|
|
801
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
693
802
|
"input",
|
|
694
803
|
{
|
|
695
804
|
type: "date",
|
|
@@ -699,9 +808,9 @@ function DeliveryAnalyticsPage({
|
|
|
699
808
|
}
|
|
700
809
|
)
|
|
701
810
|
] }),
|
|
702
|
-
/* @__PURE__ */ (0,
|
|
703
|
-
/* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "flex flex-col gap-1 text-sm", children: [
|
|
812
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "font-medium text-foreground", children: "To" }),
|
|
813
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
705
814
|
"input",
|
|
706
815
|
{
|
|
707
816
|
type: "date",
|
|
@@ -712,13 +821,13 @@ function DeliveryAnalyticsPage({
|
|
|
712
821
|
)
|
|
713
822
|
] })
|
|
714
823
|
] }),
|
|
715
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
716
|
-
/* @__PURE__ */ (0,
|
|
717
|
-
/* @__PURE__ */ (0,
|
|
718
|
-
/* @__PURE__ */ (0,
|
|
719
|
-
] }) : isError ? /* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
721
|
-
/* @__PURE__ */ (0,
|
|
824
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { role: "status", "aria-label": "Loading delivery analytics", className: "flex flex-col gap-3", children: [
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "sr-only", children: "Loading delivery analytics" }),
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "aria-hidden": "true", className: "h-24 animate-pulse rounded bg-muted" }),
|
|
827
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "aria-hidden": "true", className: "h-72 animate-pulse rounded bg-muted" })
|
|
828
|
+
] }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-start gap-3", children: [
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load delivery analytics" }),
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
722
831
|
"button",
|
|
723
832
|
{
|
|
724
833
|
type: "button",
|
|
@@ -731,23 +840,23 @@ function DeliveryAnalyticsPage({
|
|
|
731
840
|
children: "Try again"
|
|
732
841
|
}
|
|
733
842
|
)
|
|
734
|
-
] }) : (summary.data?.length ?? 0) === 0 ? /* @__PURE__ */ (0,
|
|
735
|
-
funnel.data ? /* @__PURE__ */ (0,
|
|
736
|
-
/* @__PURE__ */ (0,
|
|
737
|
-
/* @__PURE__ */ (0,
|
|
738
|
-
/* @__PURE__ */ (0,
|
|
843
|
+
] }) : (summary.data?.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "rounded border border-border p-6 text-center text-sm text-muted-foreground", children: "No delivery data for the selected range" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-6", children: [
|
|
844
|
+
funnel.data ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FunnelStats, { funnel: funnel.data }) : null,
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { "aria-label": "Delivery trend", className: "rounded-lg border border-border p-4", children: [
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "mb-3 text-sm font-semibold text-foreground", children: "Trend" }),
|
|
847
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrendChart, { data: summary.data ?? [] })
|
|
739
848
|
] }),
|
|
740
|
-
/* @__PURE__ */ (0,
|
|
741
|
-
/* @__PURE__ */ (0,
|
|
742
|
-
/* @__PURE__ */ (0,
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { "aria-label": "Delivery by type", className: "rounded-lg border border-border p-4", children: [
|
|
850
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "mb-3 text-sm font-semibold text-foreground", children: "By type" }),
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TypeTable, { rows: types.data ?? [] })
|
|
743
852
|
] })
|
|
744
853
|
] })
|
|
745
854
|
] });
|
|
746
855
|
}
|
|
747
856
|
|
|
748
857
|
// src/template-status-badge.tsx
|
|
749
|
-
var
|
|
750
|
-
var
|
|
858
|
+
var import_react_ui10 = require("@quanticjs/react-ui");
|
|
859
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
751
860
|
var STATUS_STYLES = {
|
|
752
861
|
draft: "bg-muted text-muted-foreground",
|
|
753
862
|
published: "bg-primary text-primary-foreground",
|
|
@@ -758,10 +867,10 @@ function TemplateStatusBadge({
|
|
|
758
867
|
className
|
|
759
868
|
}) {
|
|
760
869
|
const style = STATUS_STYLES[status] ?? "bg-muted text-muted-foreground";
|
|
761
|
-
return /* @__PURE__ */ (0,
|
|
870
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
762
871
|
"span",
|
|
763
872
|
{
|
|
764
|
-
className: (0,
|
|
873
|
+
className: (0, import_react_ui10.cn)(
|
|
765
874
|
"inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize",
|
|
766
875
|
style,
|
|
767
876
|
className
|
|
@@ -772,32 +881,32 @@ function TemplateStatusBadge({
|
|
|
772
881
|
}
|
|
773
882
|
|
|
774
883
|
// src/template-list.tsx
|
|
775
|
-
var
|
|
884
|
+
var import_react_ui11 = require("@quanticjs/react-ui");
|
|
776
885
|
var import_react_query8 = require("@quanticjs/react-query");
|
|
777
|
-
var
|
|
886
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
778
887
|
function TemplateList({ basePath = "/api/templates", className }) {
|
|
779
888
|
const { data, isLoading, isError, refetch } = (0, import_react_query8.useApiQuery)(
|
|
780
889
|
["templates", basePath],
|
|
781
890
|
(client) => client.get(basePath)
|
|
782
891
|
);
|
|
783
892
|
if (isLoading) {
|
|
784
|
-
return /* @__PURE__ */ (0,
|
|
893
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
785
894
|
"div",
|
|
786
895
|
{
|
|
787
896
|
role: "status",
|
|
788
897
|
"aria-label": "Loading templates",
|
|
789
|
-
className: (0,
|
|
898
|
+
className: (0, import_react_ui11.cn)("flex flex-col gap-2 p-4", className),
|
|
790
899
|
children: [
|
|
791
|
-
/* @__PURE__ */ (0,
|
|
792
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
900
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "sr-only", children: "Loading templates" }),
|
|
901
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { "aria-hidden": "true", className: "h-12 animate-pulse rounded bg-muted" }, i))
|
|
793
902
|
]
|
|
794
903
|
}
|
|
795
904
|
);
|
|
796
905
|
}
|
|
797
906
|
if (isError) {
|
|
798
|
-
return /* @__PURE__ */ (0,
|
|
799
|
-
/* @__PURE__ */ (0,
|
|
800
|
-
/* @__PURE__ */ (0,
|
|
907
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: (0, import_react_ui11.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
908
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load templates" }),
|
|
909
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
801
910
|
"button",
|
|
802
911
|
{
|
|
803
912
|
type: "button",
|
|
@@ -810,29 +919,29 @@ function TemplateList({ basePath = "/api/templates", className }) {
|
|
|
810
919
|
}
|
|
811
920
|
const items = data ?? [];
|
|
812
921
|
if (items.length === 0) {
|
|
813
|
-
return /* @__PURE__ */ (0,
|
|
922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: (0, import_react_ui11.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No templates found" });
|
|
814
923
|
}
|
|
815
|
-
return /* @__PURE__ */ (0,
|
|
816
|
-
/* @__PURE__ */ (0,
|
|
817
|
-
/* @__PURE__ */ (0,
|
|
818
|
-
/* @__PURE__ */ (0,
|
|
819
|
-
/* @__PURE__ */ (0,
|
|
820
|
-
/* @__PURE__ */ (0,
|
|
924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { "aria-label": "Templates", className: (0, import_react_ui11.cn)("flex flex-col", className), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full text-sm", children: [
|
|
925
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
926
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Template" }),
|
|
927
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
928
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locales" }),
|
|
929
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Versions" })
|
|
821
930
|
] }) }),
|
|
822
|
-
/* @__PURE__ */ (0,
|
|
823
|
-
/* @__PURE__ */ (0,
|
|
824
|
-
/* @__PURE__ */ (0,
|
|
825
|
-
/* @__PURE__ */ (0,
|
|
826
|
-
/* @__PURE__ */ (0,
|
|
931
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tbody", { children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tr", { className: "border-b border-border", children: [
|
|
932
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "py-3 pe-4 font-medium text-foreground", children: item.templateId }),
|
|
933
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TemplateStatusBadge, { status: item.latestStatus }) }),
|
|
934
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: item.locales.join(", ") }),
|
|
935
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: item.versionCount })
|
|
827
936
|
] }, item.templateId)) })
|
|
828
937
|
] }) });
|
|
829
938
|
}
|
|
830
939
|
|
|
831
940
|
// src/template-editor.tsx
|
|
832
|
-
var
|
|
833
|
-
var
|
|
941
|
+
var import_react7 = require("react");
|
|
942
|
+
var import_react_ui12 = require("@quanticjs/react-ui");
|
|
834
943
|
var import_react_query9 = require("@quanticjs/react-query");
|
|
835
|
-
var
|
|
944
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
836
945
|
var FIELD_DEFS = [
|
|
837
946
|
{ key: "subject", label: "Subject", multiline: false },
|
|
838
947
|
{ key: "heading", label: "Heading", multiline: false },
|
|
@@ -853,17 +962,17 @@ function TemplateEditor({
|
|
|
853
962
|
basePath = "/api/templates",
|
|
854
963
|
className
|
|
855
964
|
}) {
|
|
856
|
-
const toast = (0,
|
|
857
|
-
const [fields, setFields] = (0,
|
|
965
|
+
const toast = (0, import_react_ui12.useToast)();
|
|
966
|
+
const [fields, setFields] = (0, import_react7.useState)({
|
|
858
967
|
subject: initialFields?.subject ?? "",
|
|
859
968
|
heading: initialFields?.heading ?? "",
|
|
860
969
|
body: initialFields?.body ?? "",
|
|
861
970
|
cta: initialFields?.cta ?? "",
|
|
862
971
|
html: initialFields?.html ?? ""
|
|
863
972
|
});
|
|
864
|
-
const [errors, setErrors] = (0,
|
|
865
|
-
const [status, setStatus] = (0,
|
|
866
|
-
const [activeVersionId, setActiveVersionId] = (0,
|
|
973
|
+
const [errors, setErrors] = (0, import_react7.useState)({});
|
|
974
|
+
const [status, setStatus] = (0, import_react7.useState)(initialStatus);
|
|
975
|
+
const [activeVersionId, setActiveVersionId] = (0, import_react7.useState)(versionId);
|
|
867
976
|
const save = (0, import_react_query9.useApiMutation)(
|
|
868
977
|
(client, payload) => client.put(`${basePath}/${templateId}`, payload),
|
|
869
978
|
{
|
|
@@ -907,21 +1016,21 @@ function TemplateEditor({
|
|
|
907
1016
|
if (!validate()) return;
|
|
908
1017
|
save.mutate(fields);
|
|
909
1018
|
};
|
|
910
|
-
return /* @__PURE__ */ (0,
|
|
911
|
-
/* @__PURE__ */ (0,
|
|
912
|
-
/* @__PURE__ */ (0,
|
|
1019
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit, className: (0, import_react_ui12.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
1020
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { className: "flex items-center justify-between", children: [
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("h2", { className: "text-sm font-semibold text-foreground", children: [
|
|
913
1022
|
"Edit template: ",
|
|
914
1023
|
templateId
|
|
915
1024
|
] }),
|
|
916
|
-
/* @__PURE__ */ (0,
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TemplateStatusBadge, { status })
|
|
917
1026
|
] }),
|
|
918
1027
|
FIELD_DEFS.map(({ key, label, multiline }) => {
|
|
919
1028
|
const fieldId = `template-${key}`;
|
|
920
1029
|
const errorId = `${fieldId}-error`;
|
|
921
1030
|
const error = errors[key];
|
|
922
|
-
return /* @__PURE__ */ (0,
|
|
923
|
-
/* @__PURE__ */ (0,
|
|
924
|
-
multiline ? /* @__PURE__ */ (0,
|
|
1031
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1032
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: fieldId, className: "text-sm font-medium text-foreground", children: label }),
|
|
1033
|
+
multiline ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
925
1034
|
"textarea",
|
|
926
1035
|
{
|
|
927
1036
|
id: fieldId,
|
|
@@ -932,7 +1041,7 @@ function TemplateEditor({
|
|
|
932
1041
|
onChange: (e) => setFields((prev) => ({ ...prev, [key]: e.target.value })),
|
|
933
1042
|
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"
|
|
934
1043
|
}
|
|
935
|
-
) : /* @__PURE__ */ (0,
|
|
1044
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
936
1045
|
"input",
|
|
937
1046
|
{
|
|
938
1047
|
id: fieldId,
|
|
@@ -944,11 +1053,11 @@ function TemplateEditor({
|
|
|
944
1053
|
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"
|
|
945
1054
|
}
|
|
946
1055
|
),
|
|
947
|
-
error && /* @__PURE__ */ (0,
|
|
1056
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { id: errorId, className: "text-xs text-destructive", children: error })
|
|
948
1057
|
] }, key);
|
|
949
1058
|
}),
|
|
950
|
-
/* @__PURE__ */ (0,
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
952
1061
|
"button",
|
|
953
1062
|
{
|
|
954
1063
|
type: "submit",
|
|
@@ -957,7 +1066,7 @@ function TemplateEditor({
|
|
|
957
1066
|
children: save.isPending ? "Saving\u2026" : "Save draft"
|
|
958
1067
|
}
|
|
959
1068
|
),
|
|
960
|
-
/* @__PURE__ */ (0,
|
|
1069
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
961
1070
|
"button",
|
|
962
1071
|
{
|
|
963
1072
|
type: "button",
|
|
@@ -972,10 +1081,10 @@ function TemplateEditor({
|
|
|
972
1081
|
}
|
|
973
1082
|
|
|
974
1083
|
// src/template-preview-pane.tsx
|
|
975
|
-
var
|
|
976
|
-
var
|
|
1084
|
+
var import_react8 = require("react");
|
|
1085
|
+
var import_react_ui13 = require("@quanticjs/react-ui");
|
|
977
1086
|
var import_react_query10 = require("@quanticjs/react-query");
|
|
978
|
-
var
|
|
1087
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
979
1088
|
function TemplatePreviewPane({
|
|
980
1089
|
templateId,
|
|
981
1090
|
versionId,
|
|
@@ -987,28 +1096,28 @@ function TemplatePreviewPane({
|
|
|
987
1096
|
(client, payload) => client.post(`${basePath}/${templateId}/preview`, payload)
|
|
988
1097
|
);
|
|
989
1098
|
const { mutate, data, isPending, isError } = preview;
|
|
990
|
-
(0,
|
|
1099
|
+
(0, import_react8.useEffect)(() => {
|
|
991
1100
|
mutate({ versionId, vars });
|
|
992
1101
|
}, [templateId, versionId]);
|
|
993
1102
|
if (isPending) {
|
|
994
|
-
return /* @__PURE__ */ (0,
|
|
1103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
995
1104
|
"div",
|
|
996
1105
|
{
|
|
997
1106
|
role: "status",
|
|
998
1107
|
"aria-label": "Loading preview",
|
|
999
|
-
className: (0,
|
|
1108
|
+
className: (0, import_react_ui13.cn)("flex flex-col gap-2 p-4", className),
|
|
1000
1109
|
children: [
|
|
1001
|
-
/* @__PURE__ */ (0,
|
|
1002
|
-
/* @__PURE__ */ (0,
|
|
1003
|
-
/* @__PURE__ */ (0,
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "sr-only", children: "Loading preview" }),
|
|
1111
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { "aria-hidden": "true", className: "h-6 w-1/2 animate-pulse rounded bg-muted" }),
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { "aria-hidden": "true", className: "h-40 animate-pulse rounded bg-muted" })
|
|
1004
1113
|
]
|
|
1005
1114
|
}
|
|
1006
1115
|
);
|
|
1007
1116
|
}
|
|
1008
1117
|
if (isError || !data) {
|
|
1009
|
-
return /* @__PURE__ */ (0,
|
|
1010
|
-
/* @__PURE__ */ (0,
|
|
1011
|
-
/* @__PURE__ */ (0,
|
|
1118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: (0, import_react_ui13.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1119
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load preview" }),
|
|
1120
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1012
1121
|
"button",
|
|
1013
1122
|
{
|
|
1014
1123
|
type: "button",
|
|
@@ -1019,9 +1128,9 @@ function TemplatePreviewPane({
|
|
|
1019
1128
|
)
|
|
1020
1129
|
] });
|
|
1021
1130
|
}
|
|
1022
|
-
return /* @__PURE__ */ (0,
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1024
|
-
/* @__PURE__ */ (0,
|
|
1131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("section", { "aria-label": "Template preview", className: (0, import_react_ui13.cn)("flex flex-col gap-4", className), children: [
|
|
1132
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: data.subject }),
|
|
1133
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1025
1134
|
"iframe",
|
|
1026
1135
|
{
|
|
1027
1136
|
title: "Email preview",
|
|
@@ -1030,24 +1139,24 @@ function TemplatePreviewPane({
|
|
|
1030
1139
|
className: "h-96 w-full rounded-md border border-border bg-background"
|
|
1031
1140
|
}
|
|
1032
1141
|
),
|
|
1033
|
-
/* @__PURE__ */ (0,
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1035
|
-
/* @__PURE__ */ (0,
|
|
1142
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1143
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "text-xs font-medium text-muted-foreground", children: "Plain text" }),
|
|
1144
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { className: "overflow-auto whitespace-pre-wrap rounded-md border border-border bg-muted p-3 text-xs text-foreground", children: data.text })
|
|
1036
1145
|
] })
|
|
1037
1146
|
] });
|
|
1038
1147
|
}
|
|
1039
1148
|
|
|
1040
1149
|
// src/template-version-history.tsx
|
|
1041
|
-
var
|
|
1150
|
+
var import_react_ui14 = require("@quanticjs/react-ui");
|
|
1042
1151
|
var import_react_query11 = require("@quanticjs/react-query");
|
|
1043
|
-
var
|
|
1152
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1044
1153
|
function TemplateVersionHistory({
|
|
1045
1154
|
templateId,
|
|
1046
1155
|
basePath = "/api/templates",
|
|
1047
1156
|
className,
|
|
1048
1157
|
canRollback = true
|
|
1049
1158
|
}) {
|
|
1050
|
-
const toast = (0,
|
|
1159
|
+
const toast = (0, import_react_ui14.useToast)();
|
|
1051
1160
|
const versionsKey = ["templates", templateId, "versions"];
|
|
1052
1161
|
const { data, isLoading, isError, refetch } = (0, import_react_query11.useApiQuery)(
|
|
1053
1162
|
versionsKey,
|
|
@@ -1065,23 +1174,23 @@ function TemplateVersionHistory({
|
|
|
1065
1174
|
}
|
|
1066
1175
|
);
|
|
1067
1176
|
if (isLoading) {
|
|
1068
|
-
return /* @__PURE__ */ (0,
|
|
1177
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
1069
1178
|
"div",
|
|
1070
1179
|
{
|
|
1071
1180
|
role: "status",
|
|
1072
1181
|
"aria-label": "Loading versions",
|
|
1073
|
-
className: (0,
|
|
1182
|
+
className: (0, import_react_ui14.cn)("flex flex-col gap-2 p-4", className),
|
|
1074
1183
|
children: [
|
|
1075
|
-
/* @__PURE__ */ (0,
|
|
1076
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sr-only", children: "Loading versions" }),
|
|
1185
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1077
1186
|
]
|
|
1078
1187
|
}
|
|
1079
1188
|
);
|
|
1080
1189
|
}
|
|
1081
1190
|
if (isError) {
|
|
1082
|
-
return /* @__PURE__ */ (0,
|
|
1083
|
-
/* @__PURE__ */ (0,
|
|
1084
|
-
/* @__PURE__ */ (0,
|
|
1191
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: (0, import_react_ui14.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load versions" }),
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1085
1194
|
"button",
|
|
1086
1195
|
{
|
|
1087
1196
|
type: "button",
|
|
@@ -1094,25 +1203,25 @@ function TemplateVersionHistory({
|
|
|
1094
1203
|
}
|
|
1095
1204
|
const versions = data ?? [];
|
|
1096
1205
|
if (versions.length === 0) {
|
|
1097
|
-
return /* @__PURE__ */ (0,
|
|
1206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: (0, import_react_ui14.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No versions" });
|
|
1098
1207
|
}
|
|
1099
|
-
return /* @__PURE__ */ (0,
|
|
1100
|
-
/* @__PURE__ */ (0,
|
|
1101
|
-
/* @__PURE__ */ (0,
|
|
1102
|
-
/* @__PURE__ */ (0,
|
|
1103
|
-
/* @__PURE__ */ (0,
|
|
1104
|
-
/* @__PURE__ */ (0,
|
|
1105
|
-
/* @__PURE__ */ (0,
|
|
1208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("section", { "aria-label": "Version history", className: (0, import_react_ui14.cn)("flex flex-col", className), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("table", { className: "w-full text-sm", children: [
|
|
1209
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
1210
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Version" }),
|
|
1211
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
1212
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Author" }),
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Date" }),
|
|
1214
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
1106
1215
|
] }) }),
|
|
1107
|
-
/* @__PURE__ */ (0,
|
|
1108
|
-
/* @__PURE__ */ (0,
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("tbody", { children: versions.map((version) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("tr", { className: "border-b border-border", children: [
|
|
1217
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("td", { className: "py-3 pe-4 font-medium text-foreground", children: [
|
|
1109
1218
|
"v",
|
|
1110
1219
|
version.versionNumber
|
|
1111
1220
|
] }),
|
|
1112
|
-
/* @__PURE__ */ (0,
|
|
1113
|
-
/* @__PURE__ */ (0,
|
|
1114
|
-
/* @__PURE__ */ (0,
|
|
1115
|
-
/* @__PURE__ */ (0,
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TemplateStatusBadge, { status: version.status }) }),
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: version.createdBy }),
|
|
1223
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: version.createdAt }),
|
|
1224
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("td", { className: "px-4 py-3 text-end", children: canRollback && version.status === "archived" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1116
1225
|
"button",
|
|
1117
1226
|
{
|
|
1118
1227
|
type: "button",
|
|
@@ -1127,39 +1236,39 @@ function TemplateVersionHistory({
|
|
|
1127
1236
|
}
|
|
1128
1237
|
|
|
1129
1238
|
// src/delivery-log-viewer.tsx
|
|
1130
|
-
var
|
|
1131
|
-
var
|
|
1239
|
+
var import_react9 = require("react");
|
|
1240
|
+
var import_react_ui15 = require("@quanticjs/react-ui");
|
|
1132
1241
|
var import_react_query12 = require("@quanticjs/react-query");
|
|
1133
|
-
var
|
|
1242
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1134
1243
|
var LIMIT = 20;
|
|
1135
1244
|
function DeliveryLogViewer({
|
|
1136
1245
|
templateId,
|
|
1137
1246
|
basePath = "/api/templates",
|
|
1138
1247
|
className
|
|
1139
1248
|
}) {
|
|
1140
|
-
const [page, setPage] = (0,
|
|
1249
|
+
const [page, setPage] = (0, import_react9.useState)(1);
|
|
1141
1250
|
const { data, isLoading, isError, refetch } = (0, import_react_query12.useApiQuery)(
|
|
1142
1251
|
["templates", templateId, "delivery-logs", page],
|
|
1143
1252
|
(client) => client.get(`${basePath}/${templateId}/delivery-logs?page=${page}&limit=${LIMIT}`)
|
|
1144
1253
|
);
|
|
1145
1254
|
if (isLoading) {
|
|
1146
|
-
return /* @__PURE__ */ (0,
|
|
1255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
1147
1256
|
"div",
|
|
1148
1257
|
{
|
|
1149
1258
|
role: "status",
|
|
1150
1259
|
"aria-label": "Loading delivery logs",
|
|
1151
|
-
className: (0,
|
|
1260
|
+
className: (0, import_react_ui15.cn)("flex flex-col gap-2 p-4", className),
|
|
1152
1261
|
children: [
|
|
1153
|
-
/* @__PURE__ */ (0,
|
|
1154
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "sr-only", children: "Loading delivery logs" }),
|
|
1263
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1155
1264
|
]
|
|
1156
1265
|
}
|
|
1157
1266
|
);
|
|
1158
1267
|
}
|
|
1159
1268
|
if (isError) {
|
|
1160
|
-
return /* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1162
|
-
/* @__PURE__ */ (0,
|
|
1269
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: (0, import_react_ui15.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load delivery logs" }),
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1163
1272
|
"button",
|
|
1164
1273
|
{
|
|
1165
1274
|
type: "button",
|
|
@@ -1173,25 +1282,25 @@ function DeliveryLogViewer({
|
|
|
1173
1282
|
const rows = data?.items ?? [];
|
|
1174
1283
|
const totalPages = data?.totalPages ?? 1;
|
|
1175
1284
|
if (rows.length === 0) {
|
|
1176
|
-
return /* @__PURE__ */ (0,
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: (0, import_react_ui15.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No delivery logs" });
|
|
1177
1286
|
}
|
|
1178
|
-
return /* @__PURE__ */ (0,
|
|
1179
|
-
/* @__PURE__ */ (0,
|
|
1180
|
-
/* @__PURE__ */ (0,
|
|
1181
|
-
/* @__PURE__ */ (0,
|
|
1182
|
-
/* @__PURE__ */ (0,
|
|
1183
|
-
/* @__PURE__ */ (0,
|
|
1184
|
-
/* @__PURE__ */ (0,
|
|
1287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("section", { "aria-label": "Delivery logs", className: (0, import_react_ui15.cn)("flex flex-col gap-3", className), children: [
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("table", { className: "w-full text-sm", children: [
|
|
1289
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Recipient" }),
|
|
1291
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
1292
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Last error" })
|
|
1185
1294
|
] }) }),
|
|
1186
|
-
/* @__PURE__ */ (0,
|
|
1187
|
-
/* @__PURE__ */ (0,
|
|
1188
|
-
/* @__PURE__ */ (0,
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1190
|
-
/* @__PURE__ */ (0,
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("tr", { className: "border-b border-border", children: [
|
|
1296
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.recipientEmail }),
|
|
1297
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TemplateStatusBadge, { status: row.status }) }),
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.attempts }),
|
|
1299
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastError ?? "\u2014" })
|
|
1191
1300
|
] }, row.id)) })
|
|
1192
1301
|
] }),
|
|
1193
|
-
/* @__PURE__ */ (0,
|
|
1194
|
-
/* @__PURE__ */ (0,
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("nav", { "aria-label": "Delivery log pagination", className: "flex items-center justify-between", children: [
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1195
1304
|
"button",
|
|
1196
1305
|
{
|
|
1197
1306
|
type: "button",
|
|
@@ -1201,13 +1310,13 @@ function DeliveryLogViewer({
|
|
|
1201
1310
|
children: "Previous"
|
|
1202
1311
|
}
|
|
1203
1312
|
),
|
|
1204
|
-
/* @__PURE__ */ (0,
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
1205
1314
|
"Page ",
|
|
1206
1315
|
page,
|
|
1207
1316
|
" of ",
|
|
1208
1317
|
totalPages
|
|
1209
1318
|
] }),
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1211
1320
|
"button",
|
|
1212
1321
|
{
|
|
1213
1322
|
type: "button",
|
|
@@ -1233,9 +1342,9 @@ function useBroadcasts({ page = 1, status, basePath = "/api" } = {}) {
|
|
|
1233
1342
|
}
|
|
1234
1343
|
|
|
1235
1344
|
// src/broadcast-list.tsx
|
|
1236
|
-
var
|
|
1237
|
-
var
|
|
1238
|
-
var
|
|
1345
|
+
var import_react10 = require("react");
|
|
1346
|
+
var import_react_ui16 = require("@quanticjs/react-ui");
|
|
1347
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1239
1348
|
function statusVariant(status) {
|
|
1240
1349
|
switch (status) {
|
|
1241
1350
|
case "completed":
|
|
@@ -1260,26 +1369,26 @@ function BroadcastList({
|
|
|
1260
1369
|
onSelect,
|
|
1261
1370
|
className
|
|
1262
1371
|
}) {
|
|
1263
|
-
const [page, setPage] = (0,
|
|
1372
|
+
const [page, setPage] = (0, import_react10.useState)(1);
|
|
1264
1373
|
const { data, isLoading, isError, refetch } = useBroadcasts({ page, status, basePath });
|
|
1265
1374
|
if (isLoading) {
|
|
1266
|
-
return /* @__PURE__ */ (0,
|
|
1375
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
1267
1376
|
"div",
|
|
1268
1377
|
{
|
|
1269
1378
|
role: "status",
|
|
1270
1379
|
"aria-label": "Loading broadcasts",
|
|
1271
|
-
className: (0,
|
|
1380
|
+
className: (0, import_react_ui16.cn)("flex flex-col gap-2 p-4", className),
|
|
1272
1381
|
children: [
|
|
1273
|
-
/* @__PURE__ */ (0,
|
|
1274
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
1382
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "sr-only", children: "Loading broadcasts" }),
|
|
1383
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1275
1384
|
]
|
|
1276
1385
|
}
|
|
1277
1386
|
);
|
|
1278
1387
|
}
|
|
1279
1388
|
if (isError) {
|
|
1280
|
-
return /* @__PURE__ */ (0,
|
|
1281
|
-
/* @__PURE__ */ (0,
|
|
1282
|
-
/* @__PURE__ */ (0,
|
|
1389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: (0, import_react_ui16.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load broadcasts" }),
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1283
1392
|
"button",
|
|
1284
1393
|
{
|
|
1285
1394
|
type: "button",
|
|
@@ -1293,9 +1402,9 @@ function BroadcastList({
|
|
|
1293
1402
|
const rows = data?.items ?? [];
|
|
1294
1403
|
const totalPages = data?.totalPages ?? 1;
|
|
1295
1404
|
if (rows.length === 0) {
|
|
1296
|
-
return /* @__PURE__ */ (0,
|
|
1405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: (0, import_react_ui16.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No broadcasts" });
|
|
1297
1406
|
}
|
|
1298
|
-
const renderTemplateCell = (row) => onSelect ? /* @__PURE__ */ (0,
|
|
1407
|
+
const renderTemplateCell = (row) => onSelect ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1299
1408
|
"button",
|
|
1300
1409
|
{
|
|
1301
1410
|
type: "button",
|
|
@@ -1303,34 +1412,34 @@ function BroadcastList({
|
|
|
1303
1412
|
className: "rounded text-start font-medium text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
|
|
1304
1413
|
children: row.templateId
|
|
1305
1414
|
}
|
|
1306
|
-
) : /* @__PURE__ */ (0,
|
|
1307
|
-
return /* @__PURE__ */ (0,
|
|
1308
|
-
/* @__PURE__ */ (0,
|
|
1309
|
-
/* @__PURE__ */ (0,
|
|
1310
|
-
/* @__PURE__ */ (0,
|
|
1311
|
-
/* @__PURE__ */ (0,
|
|
1312
|
-
/* @__PURE__ */ (0,
|
|
1313
|
-
/* @__PURE__ */ (0,
|
|
1314
|
-
/* @__PURE__ */ (0,
|
|
1315
|
-
/* @__PURE__ */ (0,
|
|
1415
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-foreground", children: row.templateId });
|
|
1416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("section", { "aria-label": "Broadcasts", className: (0, import_react_ui16.cn)("flex flex-col gap-3", className), children: [
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("table", { className: "w-full text-sm", children: [
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Template" }),
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Channels" }),
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Sent / Failed / Skipped" }),
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created by" }),
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
|
|
1316
1425
|
] }) }),
|
|
1317
|
-
/* @__PURE__ */ (0,
|
|
1318
|
-
/* @__PURE__ */ (0,
|
|
1319
|
-
/* @__PURE__ */ (0,
|
|
1320
|
-
/* @__PURE__ */ (0,
|
|
1321
|
-
/* @__PURE__ */ (0,
|
|
1426
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("tr", { className: "border-b border-border", children: [
|
|
1427
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "py-3 pe-4", children: renderTemplateCell(row) }),
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_ui16.StatusBadge, { variant: statusVariant(row.status), children: row.status }) }),
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.channels.length > 0 ? row.channels.join(", ") : "\u2014" }),
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("td", { className: "px-4 py-3 text-muted-foreground", children: [
|
|
1322
1431
|
row.sentCount,
|
|
1323
1432
|
" / ",
|
|
1324
1433
|
row.failedCount,
|
|
1325
1434
|
" / ",
|
|
1326
1435
|
row.skippedCount
|
|
1327
1436
|
] }),
|
|
1328
|
-
/* @__PURE__ */ (0,
|
|
1329
|
-
/* @__PURE__ */ (0,
|
|
1437
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.createdBy }),
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui16.formatDateTime)(row.createdAt) })
|
|
1330
1439
|
] }, row.id)) })
|
|
1331
1440
|
] }),
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("nav", { "aria-label": "Broadcast pagination", className: "flex items-center justify-between", children: [
|
|
1442
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1334
1443
|
"button",
|
|
1335
1444
|
{
|
|
1336
1445
|
type: "button",
|
|
@@ -1340,13 +1449,13 @@ function BroadcastList({
|
|
|
1340
1449
|
children: "Previous"
|
|
1341
1450
|
}
|
|
1342
1451
|
),
|
|
1343
|
-
/* @__PURE__ */ (0,
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
1344
1453
|
"Page ",
|
|
1345
1454
|
page,
|
|
1346
1455
|
" of ",
|
|
1347
1456
|
totalPages
|
|
1348
1457
|
] }),
|
|
1349
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1350
1459
|
"button",
|
|
1351
1460
|
{
|
|
1352
1461
|
type: "button",
|
|
@@ -1361,9 +1470,9 @@ function BroadcastList({
|
|
|
1361
1470
|
}
|
|
1362
1471
|
|
|
1363
1472
|
// src/broadcast-progress.tsx
|
|
1364
|
-
var
|
|
1473
|
+
var import_react_ui17 = require("@quanticjs/react-ui");
|
|
1365
1474
|
var import_react_query14 = require("@quanticjs/react-query");
|
|
1366
|
-
var
|
|
1475
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1367
1476
|
var TERMINAL_STATUSES = ["completed", "completed_with_errors", "failed", "cancelled"];
|
|
1368
1477
|
function isTerminalStatus(status) {
|
|
1369
1478
|
return status !== void 0 && TERMINAL_STATUSES.includes(status);
|
|
@@ -1388,7 +1497,7 @@ function BroadcastProgress({
|
|
|
1388
1497
|
onCancelled,
|
|
1389
1498
|
className
|
|
1390
1499
|
}) {
|
|
1391
|
-
const toast = (0,
|
|
1500
|
+
const toast = (0, import_react_ui17.useToast)();
|
|
1392
1501
|
const { data, isLoading, isError, refetch } = (0, import_react_query14.useApiQuery)(
|
|
1393
1502
|
["broadcasts", broadcastId],
|
|
1394
1503
|
(client) => client.get(`${basePath}/v1/broadcasts/${broadcastId}`),
|
|
@@ -1410,23 +1519,23 @@ function BroadcastProgress({
|
|
|
1410
1519
|
}
|
|
1411
1520
|
);
|
|
1412
1521
|
if (isLoading) {
|
|
1413
|
-
return /* @__PURE__ */ (0,
|
|
1522
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1414
1523
|
"div",
|
|
1415
1524
|
{
|
|
1416
1525
|
role: "status",
|
|
1417
1526
|
"aria-label": "Loading broadcast",
|
|
1418
|
-
className: (0,
|
|
1527
|
+
className: (0, import_react_ui17.cn)("flex flex-col gap-2 p-4", className),
|
|
1419
1528
|
children: [
|
|
1420
|
-
/* @__PURE__ */ (0,
|
|
1421
|
-
[0, 1].map((i) => /* @__PURE__ */ (0,
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sr-only", children: "Loading broadcast" }),
|
|
1530
|
+
[0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1422
1531
|
]
|
|
1423
1532
|
}
|
|
1424
1533
|
);
|
|
1425
1534
|
}
|
|
1426
1535
|
if (isError) {
|
|
1427
|
-
return /* @__PURE__ */ (0,
|
|
1428
|
-
/* @__PURE__ */ (0,
|
|
1429
|
-
/* @__PURE__ */ (0,
|
|
1536
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: (0, import_react_ui17.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load broadcast" }),
|
|
1538
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1430
1539
|
"button",
|
|
1431
1540
|
{
|
|
1432
1541
|
type: "button",
|
|
@@ -1438,21 +1547,21 @@ function BroadcastProgress({
|
|
|
1438
1547
|
] });
|
|
1439
1548
|
}
|
|
1440
1549
|
if (!data) {
|
|
1441
|
-
return /* @__PURE__ */ (0,
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: (0, import_react_ui17.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No broadcast" });
|
|
1442
1551
|
}
|
|
1443
1552
|
const total = data.totalRecipients;
|
|
1444
1553
|
const processed = data.sentCount + data.failedCount + data.skippedCount;
|
|
1445
1554
|
const pct3 = total > 0 ? Math.min(100, Math.round(processed / total * 100)) : 0;
|
|
1446
1555
|
const terminal = isTerminalStatus(data.status);
|
|
1447
|
-
return /* @__PURE__ */ (0,
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1449
|
-
/* @__PURE__ */ (0,
|
|
1556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { "aria-label": "Broadcast progress", className: (0, import_react_ui17.cn)("flex flex-col gap-4 p-4", className), children: [
|
|
1557
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("header", { className: "flex items-center justify-between", children: [
|
|
1558
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("h2", { className: "text-sm font-semibold text-foreground", children: [
|
|
1450
1559
|
"Broadcast ",
|
|
1451
1560
|
data.templateId
|
|
1452
1561
|
] }),
|
|
1453
|
-
/* @__PURE__ */ (0,
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_ui17.StatusBadge, { variant: statusVariant2(data.status), appearance: "solid", children: data.status })
|
|
1454
1563
|
] }),
|
|
1455
|
-
/* @__PURE__ */ (0,
|
|
1564
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1456
1565
|
"div",
|
|
1457
1566
|
{
|
|
1458
1567
|
role: "progressbar",
|
|
@@ -1461,10 +1570,10 @@ function BroadcastProgress({
|
|
|
1461
1570
|
"aria-valuemax": 100,
|
|
1462
1571
|
"aria-label": "Broadcast completion",
|
|
1463
1572
|
className: "h-2 w-full overflow-hidden rounded-full bg-muted",
|
|
1464
|
-
children: /* @__PURE__ */ (0,
|
|
1573
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "h-full rounded-full bg-primary", style: { width: `${pct3}%` } })
|
|
1465
1574
|
}
|
|
1466
1575
|
),
|
|
1467
|
-
/* @__PURE__ */ (0,
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
1468
1577
|
processed,
|
|
1469
1578
|
" of ",
|
|
1470
1579
|
total,
|
|
@@ -1472,25 +1581,25 @@ function BroadcastProgress({
|
|
|
1472
1581
|
pct3,
|
|
1473
1582
|
"%)"
|
|
1474
1583
|
] }),
|
|
1475
|
-
/* @__PURE__ */ (0,
|
|
1476
|
-
/* @__PURE__ */ (0,
|
|
1477
|
-
/* @__PURE__ */ (0,
|
|
1478
|
-
/* @__PURE__ */ (0,
|
|
1584
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("dl", { className: "grid grid-cols-2 gap-3 text-sm sm:grid-cols-4", children: [
|
|
1585
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col rounded-md border border-border bg-card p-3", children: [
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dt", { className: "text-xs text-muted-foreground", children: "Sent" }),
|
|
1587
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dd", { className: "text-foreground", children: data.sentCount })
|
|
1479
1588
|
] }),
|
|
1480
|
-
/* @__PURE__ */ (0,
|
|
1481
|
-
/* @__PURE__ */ (0,
|
|
1482
|
-
/* @__PURE__ */ (0,
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col rounded-md border border-border bg-card p-3", children: [
|
|
1590
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dt", { className: "text-xs text-muted-foreground", children: "Failed" }),
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dd", { className: "text-foreground", children: data.failedCount })
|
|
1483
1592
|
] }),
|
|
1484
|
-
/* @__PURE__ */ (0,
|
|
1485
|
-
/* @__PURE__ */ (0,
|
|
1486
|
-
/* @__PURE__ */ (0,
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col rounded-md border border-border bg-card p-3", children: [
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dt", { className: "text-xs text-muted-foreground", children: "Skipped" }),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dd", { className: "text-foreground", children: data.skippedCount })
|
|
1487
1596
|
] }),
|
|
1488
|
-
/* @__PURE__ */ (0,
|
|
1489
|
-
/* @__PURE__ */ (0,
|
|
1490
|
-
/* @__PURE__ */ (0,
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col rounded-md border border-border bg-card p-3", children: [
|
|
1598
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dt", { className: "text-xs text-muted-foreground", children: "Total" }),
|
|
1599
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("dd", { className: "text-foreground", children: total })
|
|
1491
1600
|
] })
|
|
1492
1601
|
] }),
|
|
1493
|
-
/* @__PURE__ */ (0,
|
|
1602
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1494
1603
|
"button",
|
|
1495
1604
|
{
|
|
1496
1605
|
type: "button",
|
|
@@ -1504,10 +1613,10 @@ function BroadcastProgress({
|
|
|
1504
1613
|
}
|
|
1505
1614
|
|
|
1506
1615
|
// src/broadcast-composer.tsx
|
|
1507
|
-
var
|
|
1508
|
-
var
|
|
1616
|
+
var import_react11 = require("react");
|
|
1617
|
+
var import_react_ui18 = require("@quanticjs/react-ui");
|
|
1509
1618
|
var import_react_query15 = require("@quanticjs/react-query");
|
|
1510
|
-
var
|
|
1619
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1511
1620
|
var CHANNELS2 = ["inapp", "email", "push", "sms", "webhook", "slack", "teams"];
|
|
1512
1621
|
function normalizeList(data) {
|
|
1513
1622
|
if (!data) return [];
|
|
@@ -1522,15 +1631,15 @@ function BroadcastComposer({
|
|
|
1522
1631
|
onCreated,
|
|
1523
1632
|
className
|
|
1524
1633
|
}) {
|
|
1525
|
-
const toast = (0,
|
|
1526
|
-
const [idempotencyKey] = (0,
|
|
1527
|
-
const [templateId, setTemplateId] = (0,
|
|
1528
|
-
const [channels, setChannels] = (0,
|
|
1529
|
-
const [audienceMode, setAudienceMode] = (0,
|
|
1530
|
-
const [segmentId, setSegmentId] = (0,
|
|
1531
|
-
const [recipientsRaw, setRecipientsRaw] = (0,
|
|
1532
|
-
const [type, setType] = (0,
|
|
1533
|
-
const [errors, setErrors] = (0,
|
|
1634
|
+
const toast = (0, import_react_ui18.useToast)();
|
|
1635
|
+
const [idempotencyKey] = (0, import_react11.useState)(() => crypto.randomUUID());
|
|
1636
|
+
const [templateId, setTemplateId] = (0, import_react11.useState)("");
|
|
1637
|
+
const [channels, setChannels] = (0, import_react11.useState)([]);
|
|
1638
|
+
const [audienceMode, setAudienceMode] = (0, import_react11.useState)("segment");
|
|
1639
|
+
const [segmentId, setSegmentId] = (0, import_react11.useState)("");
|
|
1640
|
+
const [recipientsRaw, setRecipientsRaw] = (0, import_react11.useState)("");
|
|
1641
|
+
const [type, setType] = (0, import_react11.useState)("");
|
|
1642
|
+
const [errors, setErrors] = (0, import_react11.useState)({});
|
|
1534
1643
|
const templatesQuery = (0, import_react_query15.useApiQuery)(
|
|
1535
1644
|
["templates"],
|
|
1536
1645
|
(client) => client.get(`${basePath}/templates`)
|
|
@@ -1555,23 +1664,23 @@ function BroadcastComposer({
|
|
|
1555
1664
|
const isLoading = templatesQuery.isLoading || segmentsQuery.isLoading;
|
|
1556
1665
|
const isError = templatesQuery.isError || segmentsQuery.isError;
|
|
1557
1666
|
if (isLoading) {
|
|
1558
|
-
return /* @__PURE__ */ (0,
|
|
1667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1559
1668
|
"div",
|
|
1560
1669
|
{
|
|
1561
1670
|
role: "status",
|
|
1562
1671
|
"aria-label": "Loading broadcast composer",
|
|
1563
|
-
className: (0,
|
|
1672
|
+
className: (0, import_react_ui18.cn)("flex flex-col gap-2 p-4", className),
|
|
1564
1673
|
children: [
|
|
1565
|
-
/* @__PURE__ */ (0,
|
|
1566
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
1674
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "sr-only", children: "Loading broadcast composer" }),
|
|
1675
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1567
1676
|
]
|
|
1568
1677
|
}
|
|
1569
1678
|
);
|
|
1570
1679
|
}
|
|
1571
1680
|
if (isError) {
|
|
1572
|
-
return /* @__PURE__ */ (0,
|
|
1573
|
-
/* @__PURE__ */ (0,
|
|
1574
|
-
/* @__PURE__ */ (0,
|
|
1681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: (0, import_react_ui18.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1682
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load broadcast composer" }),
|
|
1683
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1575
1684
|
"button",
|
|
1576
1685
|
{
|
|
1577
1686
|
type: "button",
|
|
@@ -1618,11 +1727,11 @@ function BroadcastComposer({
|
|
|
1618
1727
|
idempotencyKey
|
|
1619
1728
|
});
|
|
1620
1729
|
};
|
|
1621
|
-
return /* @__PURE__ */ (0,
|
|
1622
|
-
/* @__PURE__ */ (0,
|
|
1623
|
-
/* @__PURE__ */ (0,
|
|
1624
|
-
/* @__PURE__ */ (0,
|
|
1625
|
-
/* @__PURE__ */ (0,
|
|
1730
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("form", { onSubmit, className: (0, import_react_ui18.cn)("flex flex-col gap-4 p-4", className), noValidate: true, children: [
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "New broadcast" }),
|
|
1732
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { htmlFor: "composer-template", className: "text-sm font-medium text-foreground", children: "Template" }),
|
|
1734
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1626
1735
|
"select",
|
|
1627
1736
|
{
|
|
1628
1737
|
id: "composer-template",
|
|
@@ -1632,30 +1741,30 @@ function BroadcastComposer({
|
|
|
1632
1741
|
onChange: (e) => setTemplateId(e.target.value),
|
|
1633
1742
|
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",
|
|
1634
1743
|
children: [
|
|
1635
|
-
/* @__PURE__ */ (0,
|
|
1636
|
-
templates.map((t) => /* @__PURE__ */ (0,
|
|
1744
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "Select a template\u2026" }),
|
|
1745
|
+
templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: t.id, children: t.name ?? t.id }, t.id))
|
|
1637
1746
|
]
|
|
1638
1747
|
}
|
|
1639
1748
|
),
|
|
1640
|
-
errors.templateId && /* @__PURE__ */ (0,
|
|
1749
|
+
errors.templateId && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { id: "composer-template-error", className: "text-xs text-destructive", children: errors.templateId })
|
|
1641
1750
|
] }),
|
|
1642
|
-
/* @__PURE__ */ (0,
|
|
1751
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1643
1752
|
"fieldset",
|
|
1644
1753
|
{
|
|
1645
1754
|
className: "flex flex-col gap-2",
|
|
1646
1755
|
"aria-invalid": errors.channels ? "true" : void 0,
|
|
1647
1756
|
"aria-describedby": errors.channels ? "composer-channels-error" : void 0,
|
|
1648
1757
|
children: [
|
|
1649
|
-
/* @__PURE__ */ (0,
|
|
1650
|
-
/* @__PURE__ */ (0,
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("legend", { className: "text-sm font-medium text-foreground", children: "Channels" }),
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-wrap gap-3", children: CHANNELS2.map((channel) => {
|
|
1651
1760
|
const checkboxId = `composer-channel-${channel}`;
|
|
1652
|
-
return /* @__PURE__ */ (0,
|
|
1761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1653
1762
|
"label",
|
|
1654
1763
|
{
|
|
1655
1764
|
htmlFor: checkboxId,
|
|
1656
1765
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
1657
1766
|
children: [
|
|
1658
|
-
/* @__PURE__ */ (0,
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1659
1768
|
"input",
|
|
1660
1769
|
{
|
|
1661
1770
|
id: checkboxId,
|
|
@@ -1671,20 +1780,20 @@ function BroadcastComposer({
|
|
|
1671
1780
|
channel
|
|
1672
1781
|
);
|
|
1673
1782
|
}) }),
|
|
1674
|
-
errors.channels && /* @__PURE__ */ (0,
|
|
1783
|
+
errors.channels && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { id: "composer-channels-error", className: "text-xs text-destructive", children: errors.channels })
|
|
1675
1784
|
]
|
|
1676
1785
|
}
|
|
1677
1786
|
),
|
|
1678
|
-
/* @__PURE__ */ (0,
|
|
1679
|
-
/* @__PURE__ */ (0,
|
|
1680
|
-
/* @__PURE__ */ (0,
|
|
1681
|
-
/* @__PURE__ */ (0,
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("fieldset", { className: "flex flex-col gap-2", children: [
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("legend", { className: "text-sm font-medium text-foreground", children: "Audience" }),
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-4", children: [
|
|
1790
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1682
1791
|
"label",
|
|
1683
1792
|
{
|
|
1684
1793
|
htmlFor: "composer-audience-segment",
|
|
1685
1794
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
1686
1795
|
children: [
|
|
1687
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1688
1797
|
"input",
|
|
1689
1798
|
{
|
|
1690
1799
|
id: "composer-audience-segment",
|
|
@@ -1699,13 +1808,13 @@ function BroadcastComposer({
|
|
|
1699
1808
|
]
|
|
1700
1809
|
}
|
|
1701
1810
|
),
|
|
1702
|
-
/* @__PURE__ */ (0,
|
|
1811
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1703
1812
|
"label",
|
|
1704
1813
|
{
|
|
1705
1814
|
htmlFor: "composer-audience-recipients",
|
|
1706
1815
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
1707
1816
|
children: [
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1817
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1709
1818
|
"input",
|
|
1710
1819
|
{
|
|
1711
1820
|
id: "composer-audience-recipients",
|
|
@@ -1721,7 +1830,7 @@ function BroadcastComposer({
|
|
|
1721
1830
|
}
|
|
1722
1831
|
)
|
|
1723
1832
|
] }),
|
|
1724
|
-
audienceMode === "segment" ? /* @__PURE__ */ (0,
|
|
1833
|
+
audienceMode === "segment" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1725
1834
|
"select",
|
|
1726
1835
|
{
|
|
1727
1836
|
id: "composer-segment",
|
|
@@ -1732,11 +1841,11 @@ function BroadcastComposer({
|
|
|
1732
1841
|
onChange: (e) => setSegmentId(e.target.value),
|
|
1733
1842
|
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",
|
|
1734
1843
|
children: [
|
|
1735
|
-
/* @__PURE__ */ (0,
|
|
1736
|
-
segments.map((s) => /* @__PURE__ */ (0,
|
|
1844
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "Select a segment\u2026" }),
|
|
1845
|
+
segments.map((s) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: s.id, children: s.name }, s.id))
|
|
1737
1846
|
]
|
|
1738
1847
|
}
|
|
1739
|
-
) : /* @__PURE__ */ (0,
|
|
1848
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1740
1849
|
"textarea",
|
|
1741
1850
|
{
|
|
1742
1851
|
id: "composer-recipients",
|
|
@@ -1750,11 +1859,11 @@ function BroadcastComposer({
|
|
|
1750
1859
|
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"
|
|
1751
1860
|
}
|
|
1752
1861
|
),
|
|
1753
|
-
errors.audience && /* @__PURE__ */ (0,
|
|
1862
|
+
errors.audience && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { id: "composer-audience-error", className: "text-xs text-destructive", children: errors.audience })
|
|
1754
1863
|
] }),
|
|
1755
|
-
/* @__PURE__ */ (0,
|
|
1756
|
-
/* @__PURE__ */ (0,
|
|
1757
|
-
/* @__PURE__ */ (0,
|
|
1864
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("label", { htmlFor: "composer-type", className: "text-sm font-medium text-foreground", children: "Type (optional)" }),
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1758
1867
|
"input",
|
|
1759
1868
|
{
|
|
1760
1869
|
id: "composer-type",
|
|
@@ -1765,7 +1874,7 @@ function BroadcastComposer({
|
|
|
1765
1874
|
}
|
|
1766
1875
|
)
|
|
1767
1876
|
] }),
|
|
1768
|
-
/* @__PURE__ */ (0,
|
|
1877
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1769
1878
|
"button",
|
|
1770
1879
|
{
|
|
1771
1880
|
type: "submit",
|
|
@@ -1778,9 +1887,9 @@ function BroadcastComposer({
|
|
|
1778
1887
|
}
|
|
1779
1888
|
|
|
1780
1889
|
// src/segment-list.tsx
|
|
1781
|
-
var
|
|
1890
|
+
var import_react_ui19 = require("@quanticjs/react-ui");
|
|
1782
1891
|
var import_react_query16 = require("@quanticjs/react-query");
|
|
1783
|
-
var
|
|
1892
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1784
1893
|
function normalize(data) {
|
|
1785
1894
|
if (Array.isArray(data)) return data;
|
|
1786
1895
|
return data?.items ?? [];
|
|
@@ -1806,23 +1915,23 @@ function SegmentList({
|
|
|
1806
1915
|
(client) => client.get(`${basePath}/v1/segments`)
|
|
1807
1916
|
);
|
|
1808
1917
|
if (isLoading) {
|
|
1809
|
-
return /* @__PURE__ */ (0,
|
|
1918
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1810
1919
|
"div",
|
|
1811
1920
|
{
|
|
1812
1921
|
role: "status",
|
|
1813
1922
|
"aria-label": "Loading segments",
|
|
1814
|
-
className: (0,
|
|
1923
|
+
className: (0, import_react_ui19.cn)("flex flex-col gap-2 p-4", className),
|
|
1815
1924
|
children: [
|
|
1816
|
-
/* @__PURE__ */ (0,
|
|
1817
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
1925
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "sr-only", children: "Loading segments" }),
|
|
1926
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
1818
1927
|
]
|
|
1819
1928
|
}
|
|
1820
1929
|
);
|
|
1821
1930
|
}
|
|
1822
1931
|
if (isError) {
|
|
1823
|
-
return /* @__PURE__ */ (0,
|
|
1824
|
-
/* @__PURE__ */ (0,
|
|
1825
|
-
/* @__PURE__ */ (0,
|
|
1932
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: (0, import_react_ui19.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
1933
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load segments" }),
|
|
1934
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1826
1935
|
"button",
|
|
1827
1936
|
{
|
|
1828
1937
|
type: "button",
|
|
@@ -1834,7 +1943,7 @@ function SegmentList({
|
|
|
1834
1943
|
] });
|
|
1835
1944
|
}
|
|
1836
1945
|
const rows = normalize(data);
|
|
1837
|
-
const header = onCreate ? /* @__PURE__ */ (0,
|
|
1946
|
+
const header = onCreate ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("header", { className: "flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1838
1947
|
"button",
|
|
1839
1948
|
{
|
|
1840
1949
|
type: "button",
|
|
@@ -1844,12 +1953,12 @@ function SegmentList({
|
|
|
1844
1953
|
}
|
|
1845
1954
|
) }) : null;
|
|
1846
1955
|
if (rows.length === 0) {
|
|
1847
|
-
return /* @__PURE__ */ (0,
|
|
1956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "aria-label": "Segments", className: (0, import_react_ui19.cn)("flex flex-col gap-3", className), children: [
|
|
1848
1957
|
header,
|
|
1849
|
-
/* @__PURE__ */ (0,
|
|
1958
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No segments" })
|
|
1850
1959
|
] });
|
|
1851
1960
|
}
|
|
1852
|
-
const renderNameCell = (row) => onSelect ? /* @__PURE__ */ (0,
|
|
1961
|
+
const renderNameCell = (row) => onSelect ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1853
1962
|
"button",
|
|
1854
1963
|
{
|
|
1855
1964
|
type: "button",
|
|
@@ -1857,33 +1966,33 @@ function SegmentList({
|
|
|
1857
1966
|
className: "rounded text-start font-medium text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
|
|
1858
1967
|
children: row.name
|
|
1859
1968
|
}
|
|
1860
|
-
) : /* @__PURE__ */ (0,
|
|
1861
|
-
return /* @__PURE__ */ (0,
|
|
1969
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-foreground", children: row.name });
|
|
1970
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "aria-label": "Segments", className: (0, import_react_ui19.cn)("flex flex-col gap-3", className), children: [
|
|
1862
1971
|
header,
|
|
1863
|
-
/* @__PURE__ */ (0,
|
|
1864
|
-
/* @__PURE__ */ (0,
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1866
|
-
/* @__PURE__ */ (0,
|
|
1867
|
-
/* @__PURE__ */ (0,
|
|
1868
|
-
/* @__PURE__ */ (0,
|
|
1869
|
-
/* @__PURE__ */ (0,
|
|
1972
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("table", { className: "w-full text-sm", children: [
|
|
1973
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
1974
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Name" }),
|
|
1975
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" }),
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Description" }),
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Recipients" }),
|
|
1978
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
|
|
1870
1979
|
] }) }),
|
|
1871
|
-
/* @__PURE__ */ (0,
|
|
1872
|
-
/* @__PURE__ */ (0,
|
|
1873
|
-
/* @__PURE__ */ (0,
|
|
1874
|
-
/* @__PURE__ */ (0,
|
|
1875
|
-
/* @__PURE__ */ (0,
|
|
1876
|
-
/* @__PURE__ */ (0,
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("tr", { className: "border-b border-border", children: [
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("td", { className: "py-3 pe-4", children: renderNameCell(row) }),
|
|
1982
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_ui19.StatusBadge, { variant: typeVariant(row.type), appearance: "dot", children: row.type }) }),
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.description ?? "\u2014" }),
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.recipientIds?.length ?? "\u2014" }),
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui19.formatDateTime)(row.createdAt) })
|
|
1877
1986
|
] }, row.id)) })
|
|
1878
1987
|
] })
|
|
1879
1988
|
] });
|
|
1880
1989
|
}
|
|
1881
1990
|
|
|
1882
1991
|
// src/segment-builder.tsx
|
|
1883
|
-
var
|
|
1884
|
-
var
|
|
1992
|
+
var import_react12 = require("react");
|
|
1993
|
+
var import_react_ui20 = require("@quanticjs/react-ui");
|
|
1885
1994
|
var import_react_query17 = require("@quanticjs/react-query");
|
|
1886
|
-
var
|
|
1995
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1887
1996
|
function parseRecipientIds(value) {
|
|
1888
1997
|
return value.split(/[\n,]/).map((id) => id.trim()).filter((id) => id.length > 0);
|
|
1889
1998
|
}
|
|
@@ -1895,12 +2004,12 @@ function SegmentBuilder({
|
|
|
1895
2004
|
onDeleted,
|
|
1896
2005
|
className
|
|
1897
2006
|
}) {
|
|
1898
|
-
const toast = (0,
|
|
1899
|
-
const [name, setName] = (0,
|
|
1900
|
-
const [type, setType] = (0,
|
|
1901
|
-
const [description, setDescription] = (0,
|
|
1902
|
-
const [recipientText, setRecipientText] = (0,
|
|
1903
|
-
const [errors, setErrors] = (0,
|
|
2007
|
+
const toast = (0, import_react_ui20.useToast)();
|
|
2008
|
+
const [name, setName] = (0, import_react12.useState)(initial?.name ?? "");
|
|
2009
|
+
const [type, setType] = (0, import_react12.useState)(initial?.type ?? "static");
|
|
2010
|
+
const [description, setDescription] = (0, import_react12.useState)(initial?.description ?? "");
|
|
2011
|
+
const [recipientText, setRecipientText] = (0, import_react12.useState)((initial?.recipientIds ?? []).join("\n"));
|
|
2012
|
+
const [errors, setErrors] = (0, import_react12.useState)({});
|
|
1904
2013
|
const isEdit = Boolean(segmentId);
|
|
1905
2014
|
const onMutationError = (error) => toast.error(error.isServerError ? "Something went wrong" : error.title, {
|
|
1906
2015
|
description: error.isServerError ? `Please try again. (ref: ${error.correlationId ?? "unknown"})` : `${error.detail ?? ""} (ref: ${error.correlationId ?? "unknown"})`
|
|
@@ -1949,11 +2058,11 @@ function SegmentBuilder({
|
|
|
1949
2058
|
if (!window.confirm("Delete this segment? This cannot be undone.")) return;
|
|
1950
2059
|
remove.mutate();
|
|
1951
2060
|
};
|
|
1952
|
-
return /* @__PURE__ */ (0,
|
|
1953
|
-
/* @__PURE__ */ (0,
|
|
1954
|
-
/* @__PURE__ */ (0,
|
|
1955
|
-
/* @__PURE__ */ (0,
|
|
1956
|
-
/* @__PURE__ */ (0,
|
|
2061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("form", { onSubmit, className: (0, import_react_ui20.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("header", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: isEdit ? `Edit segment: ${segmentId}` : "New segment" }) }),
|
|
2063
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: "segment-name", className: "text-sm font-medium text-foreground", children: "Name" }),
|
|
2065
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1957
2066
|
"input",
|
|
1958
2067
|
{
|
|
1959
2068
|
id: "segment-name",
|
|
@@ -1965,11 +2074,11 @@ function SegmentBuilder({
|
|
|
1965
2074
|
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"
|
|
1966
2075
|
}
|
|
1967
2076
|
),
|
|
1968
|
-
errors.name && /* @__PURE__ */ (0,
|
|
2077
|
+
errors.name && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { id: "segment-name-error", className: "text-xs text-destructive", children: errors.name })
|
|
1969
2078
|
] }),
|
|
1970
|
-
/* @__PURE__ */ (0,
|
|
1971
|
-
/* @__PURE__ */ (0,
|
|
1972
|
-
/* @__PURE__ */ (0,
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: "segment-type", className: "text-sm font-medium text-foreground", children: "Type" }),
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
1973
2082
|
"select",
|
|
1974
2083
|
{
|
|
1975
2084
|
id: "segment-type",
|
|
@@ -1977,15 +2086,15 @@ function SegmentBuilder({
|
|
|
1977
2086
|
onChange: (e) => setType(e.target.value),
|
|
1978
2087
|
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",
|
|
1979
2088
|
children: [
|
|
1980
|
-
/* @__PURE__ */ (0,
|
|
1981
|
-
/* @__PURE__ */ (0,
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "static", children: "static" }),
|
|
2090
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("option", { value: "dynamic", children: "dynamic" })
|
|
1982
2091
|
]
|
|
1983
2092
|
}
|
|
1984
2093
|
)
|
|
1985
2094
|
] }),
|
|
1986
|
-
/* @__PURE__ */ (0,
|
|
1987
|
-
/* @__PURE__ */ (0,
|
|
1988
|
-
/* @__PURE__ */ (0,
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2096
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: "segment-description", className: "text-sm font-medium text-foreground", children: "Description" }),
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1989
2098
|
"textarea",
|
|
1990
2099
|
{
|
|
1991
2100
|
id: "segment-description",
|
|
@@ -1996,9 +2105,9 @@ function SegmentBuilder({
|
|
|
1996
2105
|
}
|
|
1997
2106
|
)
|
|
1998
2107
|
] }),
|
|
1999
|
-
type === "static" && /* @__PURE__ */ (0,
|
|
2000
|
-
/* @__PURE__ */ (0,
|
|
2001
|
-
/* @__PURE__ */ (0,
|
|
2108
|
+
type === "static" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("label", { htmlFor: "segment-recipients", className: "text-sm font-medium text-foreground", children: "Recipient ids" }),
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2002
2111
|
"textarea",
|
|
2003
2112
|
{
|
|
2004
2113
|
id: "segment-recipients",
|
|
@@ -2011,10 +2120,10 @@ function SegmentBuilder({
|
|
|
2011
2120
|
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"
|
|
2012
2121
|
}
|
|
2013
2122
|
),
|
|
2014
|
-
errors.recipientIds && /* @__PURE__ */ (0,
|
|
2123
|
+
errors.recipientIds && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { id: "segment-recipients-error", className: "text-xs text-destructive", children: errors.recipientIds })
|
|
2015
2124
|
] }),
|
|
2016
|
-
/* @__PURE__ */ (0,
|
|
2017
|
-
/* @__PURE__ */ (0,
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2018
2127
|
"button",
|
|
2019
2128
|
{
|
|
2020
2129
|
type: "submit",
|
|
@@ -2023,7 +2132,7 @@ function SegmentBuilder({
|
|
|
2023
2132
|
children: save.isPending ? "Saving\u2026" : isEdit ? "Save changes" : "Create segment"
|
|
2024
2133
|
}
|
|
2025
2134
|
),
|
|
2026
|
-
isEdit && /* @__PURE__ */ (0,
|
|
2135
|
+
isEdit && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2027
2136
|
"button",
|
|
2028
2137
|
{
|
|
2029
2138
|
type: "button",
|
|
@@ -2038,18 +2147,18 @@ function SegmentBuilder({
|
|
|
2038
2147
|
}
|
|
2039
2148
|
|
|
2040
2149
|
// src/suppression-manager.tsx
|
|
2041
|
-
var
|
|
2042
|
-
var
|
|
2150
|
+
var import_react13 = require("react");
|
|
2151
|
+
var import_react_ui21 = require("@quanticjs/react-ui");
|
|
2043
2152
|
var import_react_query18 = require("@quanticjs/react-query");
|
|
2044
|
-
var
|
|
2153
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2045
2154
|
var LIMIT3 = 20;
|
|
2046
2155
|
var CHANNELS3 = ["inapp", "email", "push", "sms"];
|
|
2047
2156
|
function SuppressionManager({ basePath = "/api", className }) {
|
|
2048
|
-
const toast = (0,
|
|
2049
|
-
const [page, setPage] = (0,
|
|
2050
|
-
const [channel, setChannel] = (0,
|
|
2051
|
-
const [address, setAddress] = (0,
|
|
2052
|
-
const [reason, setReason] = (0,
|
|
2157
|
+
const toast = (0, import_react_ui21.useToast)();
|
|
2158
|
+
const [page, setPage] = (0, import_react13.useState)(1);
|
|
2159
|
+
const [channel, setChannel] = (0, import_react13.useState)("email");
|
|
2160
|
+
const [address, setAddress] = (0, import_react13.useState)("");
|
|
2161
|
+
const [reason, setReason] = (0, import_react13.useState)("");
|
|
2053
2162
|
const { data, isLoading, isError, refetch } = (0, import_react_query18.useApiQuery)(
|
|
2054
2163
|
["suppression", page],
|
|
2055
2164
|
(client) => client.get(`${basePath}/v1/admin/suppression?page=${page}&limit=${LIMIT3}`)
|
|
@@ -2085,23 +2194,23 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2085
2194
|
event.preventDefault();
|
|
2086
2195
|
add.mutate({ channel, address: address.trim(), reason: reason.trim() });
|
|
2087
2196
|
};
|
|
2088
|
-
const addForm = /* @__PURE__ */ (0,
|
|
2089
|
-
/* @__PURE__ */ (0,
|
|
2090
|
-
/* @__PURE__ */ (0,
|
|
2091
|
-
/* @__PURE__ */ (0,
|
|
2197
|
+
const addForm = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
2198
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
|
|
2200
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2092
2201
|
"select",
|
|
2093
2202
|
{
|
|
2094
2203
|
id: "suppression-channel",
|
|
2095
2204
|
value: channel,
|
|
2096
2205
|
onChange: (e) => setChannel(e.target.value),
|
|
2097
2206
|
className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
|
|
2098
|
-
children: CHANNELS3.map((c) => /* @__PURE__ */ (0,
|
|
2207
|
+
children: CHANNELS3.map((c) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: c, children: c }, c))
|
|
2099
2208
|
}
|
|
2100
2209
|
)
|
|
2101
2210
|
] }),
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2103
|
-
/* @__PURE__ */ (0,
|
|
2104
|
-
/* @__PURE__ */ (0,
|
|
2211
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-address", className: "text-sm font-medium text-foreground", children: "Address" }),
|
|
2213
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2105
2214
|
"input",
|
|
2106
2215
|
{
|
|
2107
2216
|
id: "suppression-address",
|
|
@@ -2112,9 +2221,9 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2112
2221
|
}
|
|
2113
2222
|
)
|
|
2114
2223
|
] }),
|
|
2115
|
-
/* @__PURE__ */ (0,
|
|
2116
|
-
/* @__PURE__ */ (0,
|
|
2117
|
-
/* @__PURE__ */ (0,
|
|
2224
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("label", { htmlFor: "suppression-reason", className: "text-sm font-medium text-foreground", children: "Reason" }),
|
|
2226
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2118
2227
|
"input",
|
|
2119
2228
|
{
|
|
2120
2229
|
id: "suppression-reason",
|
|
@@ -2125,7 +2234,7 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2125
2234
|
}
|
|
2126
2235
|
)
|
|
2127
2236
|
] }),
|
|
2128
|
-
/* @__PURE__ */ (0,
|
|
2237
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2129
2238
|
"button",
|
|
2130
2239
|
{
|
|
2131
2240
|
type: "submit",
|
|
@@ -2137,14 +2246,14 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2137
2246
|
] });
|
|
2138
2247
|
let body;
|
|
2139
2248
|
if (isLoading) {
|
|
2140
|
-
body = /* @__PURE__ */ (0,
|
|
2141
|
-
/* @__PURE__ */ (0,
|
|
2142
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
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))
|
|
2143
2252
|
] });
|
|
2144
2253
|
} else if (isError) {
|
|
2145
|
-
body = /* @__PURE__ */ (0,
|
|
2146
|
-
/* @__PURE__ */ (0,
|
|
2147
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
2148
2257
|
"button",
|
|
2149
2258
|
{
|
|
2150
2259
|
type: "button",
|
|
@@ -2158,23 +2267,23 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2158
2267
|
const rows = data?.items ?? [];
|
|
2159
2268
|
const totalPages = data?.totalPages ?? 1;
|
|
2160
2269
|
if (rows.length === 0) {
|
|
2161
|
-
body = /* @__PURE__ */ (0,
|
|
2270
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No suppressions" });
|
|
2162
2271
|
} else {
|
|
2163
|
-
body = /* @__PURE__ */ (0,
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
/* @__PURE__ */ (0,
|
|
2166
|
-
/* @__PURE__ */ (0,
|
|
2167
|
-
/* @__PURE__ */ (0,
|
|
2168
|
-
/* @__PURE__ */ (0,
|
|
2169
|
-
/* @__PURE__ */ (0,
|
|
2170
|
-
/* @__PURE__ */ (0,
|
|
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" }) })
|
|
2171
2280
|
] }) }),
|
|
2172
|
-
/* @__PURE__ */ (0,
|
|
2173
|
-
/* @__PURE__ */ (0,
|
|
2174
|
-
/* @__PURE__ */ (0,
|
|
2175
|
-
/* @__PURE__ */ (0,
|
|
2176
|
-
/* @__PURE__ */ (0,
|
|
2177
|
-
/* @__PURE__ */ (0,
|
|
2281
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("tr", { className: "border-b border-border", children: [
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.address }),
|
|
2284
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.reason }),
|
|
2285
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui21.formatDateTime)(row.createdAt) }),
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2178
2287
|
"button",
|
|
2179
2288
|
{
|
|
2180
2289
|
type: "button",
|
|
@@ -2186,8 +2295,8 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2186
2295
|
) })
|
|
2187
2296
|
] }, row.id)) })
|
|
2188
2297
|
] }),
|
|
2189
|
-
/* @__PURE__ */ (0,
|
|
2190
|
-
/* @__PURE__ */ (0,
|
|
2298
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("nav", { "aria-label": "Suppression pagination", className: "flex items-center justify-between", children: [
|
|
2299
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2191
2300
|
"button",
|
|
2192
2301
|
{
|
|
2193
2302
|
type: "button",
|
|
@@ -2197,13 +2306,13 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2197
2306
|
children: "Previous"
|
|
2198
2307
|
}
|
|
2199
2308
|
),
|
|
2200
|
-
/* @__PURE__ */ (0,
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
2201
2310
|
"Page ",
|
|
2202
2311
|
page,
|
|
2203
2312
|
" of ",
|
|
2204
2313
|
totalPages
|
|
2205
2314
|
] }),
|
|
2206
|
-
/* @__PURE__ */ (0,
|
|
2315
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2207
2316
|
"button",
|
|
2208
2317
|
{
|
|
2209
2318
|
type: "button",
|
|
@@ -2217,17 +2326,17 @@ function SuppressionManager({ basePath = "/api", className }) {
|
|
|
2217
2326
|
] });
|
|
2218
2327
|
}
|
|
2219
2328
|
}
|
|
2220
|
-
return /* @__PURE__ */ (0,
|
|
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: [
|
|
2221
2330
|
addForm,
|
|
2222
2331
|
body
|
|
2223
2332
|
] });
|
|
2224
2333
|
}
|
|
2225
2334
|
|
|
2226
2335
|
// src/dlq-console.tsx
|
|
2227
|
-
var
|
|
2228
|
-
var
|
|
2336
|
+
var import_react14 = require("react");
|
|
2337
|
+
var import_react_ui22 = require("@quanticjs/react-ui");
|
|
2229
2338
|
var import_react_query19 = require("@quanticjs/react-query");
|
|
2230
|
-
var
|
|
2339
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2231
2340
|
var LIMIT4 = 20;
|
|
2232
2341
|
var STATUS_FILTERS = ["queued", "replayed", "discarded"];
|
|
2233
2342
|
function buildErrorDescription(error) {
|
|
@@ -2251,15 +2360,15 @@ function DlqMessageDetailRow({ id, basePath }) {
|
|
|
2251
2360
|
(client) => client.get(`${basePath}/admin/dlq/${id}`)
|
|
2252
2361
|
);
|
|
2253
2362
|
if (isLoading) {
|
|
2254
|
-
return /* @__PURE__ */ (0,
|
|
2255
|
-
/* @__PURE__ */ (0,
|
|
2256
|
-
/* @__PURE__ */ (0,
|
|
2363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { role: "status", "aria-label": "Loading message detail", className: "flex flex-col gap-2 p-3", children: [
|
|
2364
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Loading message detail" }),
|
|
2365
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { "aria-hidden": "true", className: "h-16 animate-pulse rounded bg-muted" })
|
|
2257
2366
|
] });
|
|
2258
2367
|
}
|
|
2259
2368
|
if (isError) {
|
|
2260
|
-
return /* @__PURE__ */ (0,
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2369
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col items-start gap-3 p-3", children: [
|
|
2370
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load message detail" }),
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2263
2372
|
"button",
|
|
2264
2373
|
{
|
|
2265
2374
|
type: "button",
|
|
@@ -2270,19 +2379,19 @@ function DlqMessageDetailRow({ id, basePath }) {
|
|
|
2270
2379
|
)
|
|
2271
2380
|
] });
|
|
2272
2381
|
}
|
|
2273
|
-
return /* @__PURE__ */ (0,
|
|
2274
|
-
/* @__PURE__ */ (0,
|
|
2382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-2 p-3 text-sm", children: [
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { className: "text-muted-foreground", children: [
|
|
2275
2384
|
"Error: ",
|
|
2276
|
-
/* @__PURE__ */ (0,
|
|
2385
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-foreground", children: data?.errorMessage ?? "\u2014" })
|
|
2277
2386
|
] }),
|
|
2278
|
-
/* @__PURE__ */ (0,
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("pre", { className: "max-h-64 overflow-auto rounded bg-muted p-3 text-xs text-foreground", children: JSON.stringify(data?.payload ?? {}, null, 2) })
|
|
2279
2388
|
] });
|
|
2280
2389
|
}
|
|
2281
2390
|
function DlqConsole({ basePath = "/api", className }) {
|
|
2282
|
-
const toast = (0,
|
|
2283
|
-
const [page, setPage] = (0,
|
|
2284
|
-
const [statusFilter, setStatusFilter] = (0,
|
|
2285
|
-
const [expandedId, setExpandedId] = (0,
|
|
2391
|
+
const toast = (0, import_react_ui22.useToast)();
|
|
2392
|
+
const [page, setPage] = (0, import_react14.useState)(1);
|
|
2393
|
+
const [statusFilter, setStatusFilter] = (0, import_react14.useState)("");
|
|
2394
|
+
const [expandedId, setExpandedId] = (0, import_react14.useState)(null);
|
|
2286
2395
|
const statusQuery = statusFilter ? `&status=${statusFilter}` : "";
|
|
2287
2396
|
const { data, isLoading, isError, refetch } = (0, import_react_query19.useApiQuery)(
|
|
2288
2397
|
["dlq", page, statusFilter],
|
|
@@ -2323,9 +2432,9 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2323
2432
|
})
|
|
2324
2433
|
}
|
|
2325
2434
|
);
|
|
2326
|
-
const filterControl = /* @__PURE__ */ (0,
|
|
2327
|
-
/* @__PURE__ */ (0,
|
|
2328
|
-
/* @__PURE__ */ (0,
|
|
2435
|
+
const filterControl = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("label", { htmlFor: "dlq-status", className: "text-sm font-medium text-foreground", children: "Status" }),
|
|
2437
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
2329
2438
|
"select",
|
|
2330
2439
|
{
|
|
2331
2440
|
id: "dlq-status",
|
|
@@ -2336,30 +2445,30 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2336
2445
|
},
|
|
2337
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",
|
|
2338
2447
|
children: [
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2340
|
-
STATUS_FILTERS.map((s) => /* @__PURE__ */ (0,
|
|
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))
|
|
2341
2450
|
]
|
|
2342
2451
|
}
|
|
2343
2452
|
)
|
|
2344
2453
|
] });
|
|
2345
2454
|
let body;
|
|
2346
2455
|
if (isLoading) {
|
|
2347
|
-
body = /* @__PURE__ */ (0,
|
|
2456
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
2348
2457
|
"div",
|
|
2349
2458
|
{
|
|
2350
2459
|
role: "status",
|
|
2351
2460
|
"aria-label": "Loading dead-letter messages",
|
|
2352
2461
|
className: "flex flex-col gap-2 p-4",
|
|
2353
2462
|
children: [
|
|
2354
|
-
/* @__PURE__ */ (0,
|
|
2355
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
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))
|
|
2356
2465
|
]
|
|
2357
2466
|
}
|
|
2358
2467
|
);
|
|
2359
2468
|
} else if (isError) {
|
|
2360
|
-
body = /* @__PURE__ */ (0,
|
|
2361
|
-
/* @__PURE__ */ (0,
|
|
2362
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
2363
2472
|
"button",
|
|
2364
2473
|
{
|
|
2365
2474
|
type: "button",
|
|
@@ -2373,23 +2482,23 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2373
2482
|
const rows = data?.items ?? [];
|
|
2374
2483
|
const totalPages = data?.totalPages ?? 1;
|
|
2375
2484
|
if (rows.length === 0) {
|
|
2376
|
-
body = /* @__PURE__ */ (0,
|
|
2485
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No dead-letter messages" });
|
|
2377
2486
|
} else {
|
|
2378
|
-
body = /* @__PURE__ */ (0,
|
|
2379
|
-
/* @__PURE__ */ (0,
|
|
2380
|
-
/* @__PURE__ */ (0,
|
|
2381
|
-
/* @__PURE__ */ (0,
|
|
2382
|
-
/* @__PURE__ */ (0,
|
|
2383
|
-
/* @__PURE__ */ (0,
|
|
2384
|
-
/* @__PURE__ */ (0,
|
|
2385
|
-
/* @__PURE__ */ (0,
|
|
2386
|
-
/* @__PURE__ */ (0,
|
|
2487
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("table", { className: "w-full text-sm", children: [
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
2490
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Request" }),
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failure reason" }),
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Attempts" }),
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "First seen" }),
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
2387
2496
|
] }) }),
|
|
2388
|
-
/* @__PURE__ */ (0,
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tbody", { children: rows.map((row) => {
|
|
2389
2498
|
const isExpanded = expandedId === row.id;
|
|
2390
|
-
return /* @__PURE__ */ (0,
|
|
2391
|
-
/* @__PURE__ */ (0,
|
|
2392
|
-
/* @__PURE__ */ (0,
|
|
2499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react14.Fragment, { children: [
|
|
2500
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("tr", { className: "border-b border-border", children: [
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "py-3 pe-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2393
2502
|
"button",
|
|
2394
2503
|
{
|
|
2395
2504
|
type: "button",
|
|
@@ -2399,12 +2508,12 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2399
2508
|
children: row.requestId ?? row.id
|
|
2400
2509
|
}
|
|
2401
2510
|
) }),
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2404
|
-
/* @__PURE__ */ (0,
|
|
2405
|
-
/* @__PURE__ */ (0,
|
|
2406
|
-
/* @__PURE__ */ (0,
|
|
2407
|
-
/* @__PURE__ */ (0,
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.failureReason }),
|
|
2512
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_ui22.StatusBadge, { variant: statusVariant3(row.status), appearance: "dot", children: row.status }) }),
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.attemptCount }),
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui22.formatDateTime)(row.firstSeenAt) }),
|
|
2515
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2516
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2408
2517
|
"button",
|
|
2409
2518
|
{
|
|
2410
2519
|
type: "button",
|
|
@@ -2414,7 +2523,7 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2414
2523
|
children: "Replay"
|
|
2415
2524
|
}
|
|
2416
2525
|
),
|
|
2417
|
-
/* @__PURE__ */ (0,
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2418
2527
|
"button",
|
|
2419
2528
|
{
|
|
2420
2529
|
type: "button",
|
|
@@ -2426,12 +2535,12 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2426
2535
|
)
|
|
2427
2536
|
] }) })
|
|
2428
2537
|
] }),
|
|
2429
|
-
isExpanded && /* @__PURE__ */ (0,
|
|
2538
|
+
isExpanded && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("td", { colSpan: 6, className: "bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DlqMessageDetailRow, { id: row.id, basePath }) }) })
|
|
2430
2539
|
] }, row.id);
|
|
2431
2540
|
}) })
|
|
2432
2541
|
] }),
|
|
2433
|
-
/* @__PURE__ */ (0,
|
|
2434
|
-
/* @__PURE__ */ (0,
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("nav", { "aria-label": "Dead-letter pagination", className: "flex items-center justify-between", children: [
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2435
2544
|
"button",
|
|
2436
2545
|
{
|
|
2437
2546
|
type: "button",
|
|
@@ -2441,13 +2550,13 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2441
2550
|
children: "Previous"
|
|
2442
2551
|
}
|
|
2443
2552
|
),
|
|
2444
|
-
/* @__PURE__ */ (0,
|
|
2553
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
2445
2554
|
"Page ",
|
|
2446
2555
|
page,
|
|
2447
2556
|
" of ",
|
|
2448
2557
|
totalPages
|
|
2449
2558
|
] }),
|
|
2450
|
-
/* @__PURE__ */ (0,
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
2451
2560
|
"button",
|
|
2452
2561
|
{
|
|
2453
2562
|
type: "button",
|
|
@@ -2461,9 +2570,9 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2461
2570
|
] });
|
|
2462
2571
|
}
|
|
2463
2572
|
}
|
|
2464
|
-
return /* @__PURE__ */ (0,
|
|
2465
|
-
/* @__PURE__ */ (0,
|
|
2466
|
-
/* @__PURE__ */ (0,
|
|
2573
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("section", { "aria-label": "Dead-letter queue", className: (0, import_react_ui22.cn)("flex flex-col gap-4", className), children: [
|
|
2574
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("header", { className: "flex items-center justify-between", children: [
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Dead-letter queue" }),
|
|
2467
2576
|
filterControl
|
|
2468
2577
|
] }),
|
|
2469
2578
|
body
|
|
@@ -2471,10 +2580,10 @@ function DlqConsole({ basePath = "/api", className }) {
|
|
|
2471
2580
|
}
|
|
2472
2581
|
|
|
2473
2582
|
// src/catalog-editor.tsx
|
|
2474
|
-
var
|
|
2475
|
-
var
|
|
2583
|
+
var import_react15 = require("react");
|
|
2584
|
+
var import_react_ui23 = require("@quanticjs/react-ui");
|
|
2476
2585
|
var import_react_query20 = require("@quanticjs/react-query");
|
|
2477
|
-
var
|
|
2586
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2478
2587
|
function normalize2(data) {
|
|
2479
2588
|
if (!data) return [];
|
|
2480
2589
|
if (Array.isArray(data)) return data;
|
|
@@ -2486,13 +2595,13 @@ function normalize2(data) {
|
|
|
2486
2595
|
);
|
|
2487
2596
|
}
|
|
2488
2597
|
function CatalogEditor({ basePath = "/api", className }) {
|
|
2489
|
-
const toast = (0,
|
|
2490
|
-
const [filter, setFilter] = (0,
|
|
2491
|
-
const [editingId, setEditingId] = (0,
|
|
2492
|
-
const [editValue, setEditValue] = (0,
|
|
2493
|
-
const [newKey, setNewKey] = (0,
|
|
2494
|
-
const [newLocale, setNewLocale] = (0,
|
|
2495
|
-
const [newValue, setNewValue] = (0,
|
|
2598
|
+
const toast = (0, import_react_ui23.useToast)();
|
|
2599
|
+
const [filter, setFilter] = (0, import_react15.useState)("");
|
|
2600
|
+
const [editingId, setEditingId] = (0, import_react15.useState)(null);
|
|
2601
|
+
const [editValue, setEditValue] = (0, import_react15.useState)("");
|
|
2602
|
+
const [newKey, setNewKey] = (0, import_react15.useState)("");
|
|
2603
|
+
const [newLocale, setNewLocale] = (0, import_react15.useState)("");
|
|
2604
|
+
const [newValue, setNewValue] = (0, import_react15.useState)("");
|
|
2496
2605
|
const { data, isLoading, isError, refetch } = (0, import_react_query20.useApiQuery)(["i18n-catalog"], (client) => client.get(`${basePath}/i18n/catalog/export`));
|
|
2497
2606
|
const onMutationError = (error) => toast.error(error.isServerError ? "Something went wrong" : error.title, {
|
|
2498
2607
|
description: error.isServerError ? `Please try again. (ref: ${error.correlationId ?? "unknown"})` : `${error.detail ?? ""} (ref: ${error.correlationId ?? "unknown"})`
|
|
@@ -2542,8 +2651,8 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2542
2651
|
onError: onMutationError
|
|
2543
2652
|
}
|
|
2544
2653
|
);
|
|
2545
|
-
const entries = (0,
|
|
2546
|
-
const filtered = (0,
|
|
2654
|
+
const entries = (0, import_react15.useMemo)(() => normalize2(data), [data]);
|
|
2655
|
+
const filtered = (0, import_react15.useMemo)(() => {
|
|
2547
2656
|
const q = filter.trim().toLowerCase();
|
|
2548
2657
|
if (!q) return entries;
|
|
2549
2658
|
return entries.filter((e) => e.key.toLowerCase().includes(q));
|
|
@@ -2556,10 +2665,10 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2556
2665
|
setEditingId(`${entry.key}:${entry.locale}`);
|
|
2557
2666
|
setEditValue(entry.value);
|
|
2558
2667
|
};
|
|
2559
|
-
const addForm = /* @__PURE__ */ (0,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
/* @__PURE__ */ (0,
|
|
2562
|
-
/* @__PURE__ */ (0,
|
|
2668
|
+
const addForm = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("form", { onSubmit: onAdd, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
2669
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2670
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-key", className: "text-sm font-medium text-foreground", children: "Key" }),
|
|
2671
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2563
2672
|
"input",
|
|
2564
2673
|
{
|
|
2565
2674
|
id: "catalog-new-key",
|
|
@@ -2570,9 +2679,9 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2570
2679
|
}
|
|
2571
2680
|
)
|
|
2572
2681
|
] }),
|
|
2573
|
-
/* @__PURE__ */ (0,
|
|
2574
|
-
/* @__PURE__ */ (0,
|
|
2575
|
-
/* @__PURE__ */ (0,
|
|
2682
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2683
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-locale", className: "text-sm font-medium text-foreground", children: "Locale" }),
|
|
2684
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2576
2685
|
"input",
|
|
2577
2686
|
{
|
|
2578
2687
|
id: "catalog-new-locale",
|
|
@@ -2583,9 +2692,9 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2583
2692
|
}
|
|
2584
2693
|
)
|
|
2585
2694
|
] }),
|
|
2586
|
-
/* @__PURE__ */ (0,
|
|
2587
|
-
/* @__PURE__ */ (0,
|
|
2588
|
-
/* @__PURE__ */ (0,
|
|
2695
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2696
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-new-value", className: "text-sm font-medium text-foreground", children: "Value" }),
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2589
2698
|
"input",
|
|
2590
2699
|
{
|
|
2591
2700
|
id: "catalog-new-value",
|
|
@@ -2596,7 +2705,7 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2596
2705
|
}
|
|
2597
2706
|
)
|
|
2598
2707
|
] }),
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
2708
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2600
2709
|
"button",
|
|
2601
2710
|
{
|
|
2602
2711
|
type: "submit",
|
|
@@ -2606,9 +2715,9 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2606
2715
|
}
|
|
2607
2716
|
)
|
|
2608
2717
|
] });
|
|
2609
|
-
const filterInput = /* @__PURE__ */ (0,
|
|
2610
|
-
/* @__PURE__ */ (0,
|
|
2611
|
-
/* @__PURE__ */ (0,
|
|
2718
|
+
const filterInput = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { htmlFor: "catalog-filter", className: "text-sm font-medium text-foreground", children: "Filter by key" }),
|
|
2720
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2612
2721
|
"input",
|
|
2613
2722
|
{
|
|
2614
2723
|
id: "catalog-filter",
|
|
@@ -2621,14 +2730,14 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2621
2730
|
] });
|
|
2622
2731
|
let body;
|
|
2623
2732
|
if (isLoading) {
|
|
2624
|
-
body = /* @__PURE__ */ (0,
|
|
2625
|
-
/* @__PURE__ */ (0,
|
|
2626
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
2733
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { role: "status", "aria-label": "Loading catalog", className: "flex flex-col gap-2 p-4", children: [
|
|
2734
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: "Loading catalog" }),
|
|
2735
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
2627
2736
|
] });
|
|
2628
2737
|
} else if (isError) {
|
|
2629
|
-
body = /* @__PURE__ */ (0,
|
|
2630
|
-
/* @__PURE__ */ (0,
|
|
2631
|
-
/* @__PURE__ */ (0,
|
|
2738
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
|
|
2739
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load catalog" }),
|
|
2740
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2632
2741
|
"button",
|
|
2633
2742
|
{
|
|
2634
2743
|
type: "button",
|
|
@@ -2639,22 +2748,22 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2639
2748
|
)
|
|
2640
2749
|
] });
|
|
2641
2750
|
} else if (filtered.length === 0) {
|
|
2642
|
-
body = /* @__PURE__ */ (0,
|
|
2751
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: entries.length === 0 ? "No catalog entries" : "No entries match your filter" });
|
|
2643
2752
|
} else {
|
|
2644
|
-
body = /* @__PURE__ */ (0,
|
|
2645
|
-
/* @__PURE__ */ (0,
|
|
2646
|
-
/* @__PURE__ */ (0,
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
2648
|
-
/* @__PURE__ */ (0,
|
|
2649
|
-
/* @__PURE__ */ (0,
|
|
2753
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("table", { className: "w-full text-sm", children: [
|
|
2754
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
2755
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
|
|
2756
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Value" }),
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
2650
2759
|
] }) }),
|
|
2651
|
-
/* @__PURE__ */ (0,
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("tbody", { children: filtered.map((entry) => {
|
|
2652
2761
|
const rowId = `${entry.key}:${entry.locale}`;
|
|
2653
2762
|
const isEditing = editingId === rowId;
|
|
2654
|
-
return /* @__PURE__ */ (0,
|
|
2655
|
-
/* @__PURE__ */ (0,
|
|
2656
|
-
/* @__PURE__ */ (0,
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("tr", { className: "border-b border-border", children: [
|
|
2764
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: entry.key }),
|
|
2765
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3 text-foreground", children: entry.locale }),
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3 text-foreground", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2658
2767
|
"input",
|
|
2659
2768
|
{
|
|
2660
2769
|
type: "text",
|
|
@@ -2664,8 +2773,8 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2664
2773
|
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"
|
|
2665
2774
|
}
|
|
2666
2775
|
) : entry.value }),
|
|
2667
|
-
/* @__PURE__ */ (0,
|
|
2668
|
-
/* @__PURE__ */ (0,
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex items-center gap-2", children: isEditing ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
2777
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2669
2778
|
"button",
|
|
2670
2779
|
{
|
|
2671
2780
|
type: "button",
|
|
@@ -2679,7 +2788,7 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2679
2788
|
children: "Save"
|
|
2680
2789
|
}
|
|
2681
2790
|
),
|
|
2682
|
-
/* @__PURE__ */ (0,
|
|
2791
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2683
2792
|
"button",
|
|
2684
2793
|
{
|
|
2685
2794
|
type: "button",
|
|
@@ -2688,8 +2797,8 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2688
2797
|
children: "Cancel"
|
|
2689
2798
|
}
|
|
2690
2799
|
)
|
|
2691
|
-
] }) : /* @__PURE__ */ (0,
|
|
2692
|
-
/* @__PURE__ */ (0,
|
|
2800
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2693
2802
|
"button",
|
|
2694
2803
|
{
|
|
2695
2804
|
type: "button",
|
|
@@ -2698,7 +2807,7 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2698
2807
|
children: "Edit"
|
|
2699
2808
|
}
|
|
2700
2809
|
),
|
|
2701
|
-
/* @__PURE__ */ (0,
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
2702
2811
|
"button",
|
|
2703
2812
|
{
|
|
2704
2813
|
type: "button",
|
|
@@ -2713,7 +2822,7 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2713
2822
|
}) })
|
|
2714
2823
|
] });
|
|
2715
2824
|
}
|
|
2716
|
-
return /* @__PURE__ */ (0,
|
|
2825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("section", { "aria-label": "Catalog editor", className: (0, import_react_ui23.cn)("flex flex-col gap-4", className), children: [
|
|
2717
2826
|
addForm,
|
|
2718
2827
|
filterInput,
|
|
2719
2828
|
body
|
|
@@ -2721,9 +2830,9 @@ function CatalogEditor({ basePath = "/api", className }) {
|
|
|
2721
2830
|
}
|
|
2722
2831
|
|
|
2723
2832
|
// src/missing-translations-panel.tsx
|
|
2724
|
-
var
|
|
2833
|
+
var import_react_ui24 = require("@quanticjs/react-ui");
|
|
2725
2834
|
var import_react_query21 = require("@quanticjs/react-query");
|
|
2726
|
-
var
|
|
2835
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2727
2836
|
function normalize3(data) {
|
|
2728
2837
|
if (Array.isArray(data)) return data;
|
|
2729
2838
|
return data?.missing ?? [];
|
|
@@ -2737,23 +2846,23 @@ function MissingTranslationsPanel({
|
|
|
2737
2846
|
(client) => client.get(`${basePath}/i18n/catalog/missing`)
|
|
2738
2847
|
);
|
|
2739
2848
|
if (isLoading) {
|
|
2740
|
-
return /* @__PURE__ */ (0,
|
|
2849
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
2741
2850
|
"div",
|
|
2742
2851
|
{
|
|
2743
2852
|
role: "status",
|
|
2744
2853
|
"aria-label": "Loading missing translations",
|
|
2745
|
-
className: (0,
|
|
2854
|
+
className: (0, import_react_ui24.cn)("flex flex-col gap-2 p-4", className),
|
|
2746
2855
|
children: [
|
|
2747
|
-
/* @__PURE__ */ (0,
|
|
2748
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
2856
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Loading missing translations" }),
|
|
2857
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
2749
2858
|
]
|
|
2750
2859
|
}
|
|
2751
2860
|
);
|
|
2752
2861
|
}
|
|
2753
2862
|
if (isError) {
|
|
2754
|
-
return /* @__PURE__ */ (0,
|
|
2755
|
-
/* @__PURE__ */ (0,
|
|
2756
|
-
/* @__PURE__ */ (0,
|
|
2863
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: (0, import_react_ui24.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load missing translations" }),
|
|
2865
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2757
2866
|
"button",
|
|
2758
2867
|
{
|
|
2759
2868
|
type: "button",
|
|
@@ -2766,26 +2875,26 @@ function MissingTranslationsPanel({
|
|
|
2766
2875
|
}
|
|
2767
2876
|
const rows = normalize3(data);
|
|
2768
2877
|
if (rows.length === 0) {
|
|
2769
|
-
return /* @__PURE__ */ (0,
|
|
2878
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: (0, import_react_ui24.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No missing translations" });
|
|
2770
2879
|
}
|
|
2771
|
-
return /* @__PURE__ */ (0,
|
|
2772
|
-
/* @__PURE__ */ (0,
|
|
2773
|
-
/* @__PURE__ */ (0,
|
|
2774
|
-
/* @__PURE__ */ (0,
|
|
2775
|
-
/* @__PURE__ */ (0,
|
|
2880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("section", { "aria-label": "Missing translations", className: (0, import_react_ui24.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("table", { className: "w-full text-sm", children: [
|
|
2881
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
2882
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
|
|
2883
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Locale" }),
|
|
2884
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Type" })
|
|
2776
2885
|
] }) }),
|
|
2777
|
-
/* @__PURE__ */ (0,
|
|
2778
|
-
/* @__PURE__ */ (0,
|
|
2779
|
-
/* @__PURE__ */ (0,
|
|
2780
|
-
/* @__PURE__ */ (0,
|
|
2886
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("tr", { className: "border-b border-border", children: [
|
|
2887
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
|
|
2888
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.locale }),
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.type ?? "\u2014" })
|
|
2781
2890
|
] }, `${row.key}:${row.locale}:${i}`)) })
|
|
2782
2891
|
] }) });
|
|
2783
2892
|
}
|
|
2784
2893
|
|
|
2785
2894
|
// src/fallback-report-panel.tsx
|
|
2786
|
-
var
|
|
2895
|
+
var import_react_ui25 = require("@quanticjs/react-ui");
|
|
2787
2896
|
var import_react_query22 = require("@quanticjs/react-query");
|
|
2788
|
-
var
|
|
2897
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2789
2898
|
function normalize4(data) {
|
|
2790
2899
|
if (Array.isArray(data)) return data;
|
|
2791
2900
|
return data?.entries ?? [];
|
|
@@ -2796,23 +2905,23 @@ function FallbackReportPanel({ basePath = "/api", className }) {
|
|
|
2796
2905
|
(client) => client.get(`${basePath}/i18n/catalog/fallback-report`)
|
|
2797
2906
|
);
|
|
2798
2907
|
if (isLoading) {
|
|
2799
|
-
return /* @__PURE__ */ (0,
|
|
2908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
2800
2909
|
"div",
|
|
2801
2910
|
{
|
|
2802
2911
|
role: "status",
|
|
2803
2912
|
"aria-label": "Loading fallback report",
|
|
2804
|
-
className: (0,
|
|
2913
|
+
className: (0, import_react_ui25.cn)("flex flex-col gap-2 p-4", className),
|
|
2805
2914
|
children: [
|
|
2806
|
-
/* @__PURE__ */ (0,
|
|
2807
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
2915
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: "Loading fallback report" }),
|
|
2916
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
2808
2917
|
]
|
|
2809
2918
|
}
|
|
2810
2919
|
);
|
|
2811
2920
|
}
|
|
2812
2921
|
if (isError) {
|
|
2813
|
-
return /* @__PURE__ */ (0,
|
|
2814
|
-
/* @__PURE__ */ (0,
|
|
2815
|
-
/* @__PURE__ */ (0,
|
|
2922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: (0, import_react_ui25.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load fallback report" }),
|
|
2924
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2816
2925
|
"button",
|
|
2817
2926
|
{
|
|
2818
2927
|
type: "button",
|
|
@@ -2825,38 +2934,38 @@ function FallbackReportPanel({ basePath = "/api", className }) {
|
|
|
2825
2934
|
}
|
|
2826
2935
|
const rows = normalize4(data);
|
|
2827
2936
|
if (rows.length === 0) {
|
|
2828
|
-
return /* @__PURE__ */ (0,
|
|
2937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: (0, import_react_ui25.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No fallbacks reported" });
|
|
2829
2938
|
}
|
|
2830
|
-
return /* @__PURE__ */ (0,
|
|
2831
|
-
/* @__PURE__ */ (0,
|
|
2832
|
-
/* @__PURE__ */ (0,
|
|
2833
|
-
/* @__PURE__ */ (0,
|
|
2834
|
-
/* @__PURE__ */ (0,
|
|
2835
|
-
/* @__PURE__ */ (0,
|
|
2939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("section", { "aria-label": "Fallback report", className: (0, import_react_ui25.cn)("flex flex-col gap-3", className), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("table", { className: "w-full text-sm", children: [
|
|
2940
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
2941
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Key" }),
|
|
2942
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Requested" }),
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Resolved" }),
|
|
2944
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Chain" })
|
|
2836
2945
|
] }) }),
|
|
2837
|
-
/* @__PURE__ */ (0,
|
|
2838
|
-
/* @__PURE__ */ (0,
|
|
2839
|
-
/* @__PURE__ */ (0,
|
|
2840
|
-
/* @__PURE__ */ (0,
|
|
2841
|
-
/* @__PURE__ */ (0,
|
|
2842
|
-
/* @__PURE__ */ (0,
|
|
2843
|
-
si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ (0,
|
|
2946
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("tr", { className: "border-b border-border", children: [
|
|
2947
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.key }),
|
|
2948
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.requestedLocale }),
|
|
2949
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.resolvedLocale }),
|
|
2950
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.steps && row.steps.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ol", { className: "flex flex-wrap items-center gap-1", children: row.steps.map((step, si) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("li", { className: "flex items-center gap-1", children: [
|
|
2951
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs text-foreground", children: step }),
|
|
2952
|
+
si < (row.steps?.length ?? 0) - 1 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" })
|
|
2844
2953
|
] }, `${step}:${si}`)) }) : "\u2014" })
|
|
2845
2954
|
] }, `${row.key}:${row.requestedLocale}:${i}`)) })
|
|
2846
2955
|
] }) });
|
|
2847
2956
|
}
|
|
2848
2957
|
|
|
2849
2958
|
// src/recipient-admin-panel.tsx
|
|
2850
|
-
var
|
|
2851
|
-
var
|
|
2959
|
+
var import_react16 = require("react");
|
|
2960
|
+
var import_react_ui26 = require("@quanticjs/react-ui");
|
|
2852
2961
|
var import_react_query23 = require("@quanticjs/react-query");
|
|
2853
|
-
var
|
|
2962
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
2854
2963
|
var LIMIT5 = 20;
|
|
2855
2964
|
function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
2856
|
-
const toast = (0,
|
|
2857
|
-
const [page, setPage] = (0,
|
|
2858
|
-
const [search, setSearch] = (0,
|
|
2859
|
-
const [query, setQuery] = (0,
|
|
2965
|
+
const toast = (0, import_react_ui26.useToast)();
|
|
2966
|
+
const [page, setPage] = (0, import_react16.useState)(1);
|
|
2967
|
+
const [search, setSearch] = (0, import_react16.useState)("");
|
|
2968
|
+
const [query, setQuery] = (0, import_react16.useState)("");
|
|
2860
2969
|
const { data, isLoading, isError, refetch } = (0, import_react_query23.useApiQuery)(
|
|
2861
2970
|
["recipients", page, query],
|
|
2862
2971
|
(client) => client.get(
|
|
@@ -2901,10 +3010,10 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2901
3010
|
if (row.consentSms) active.push("sms");
|
|
2902
3011
|
return active.length > 0 ? active.join(", ") : "none";
|
|
2903
3012
|
};
|
|
2904
|
-
const searchForm = /* @__PURE__ */ (0,
|
|
2905
|
-
/* @__PURE__ */ (0,
|
|
2906
|
-
/* @__PURE__ */ (0,
|
|
2907
|
-
/* @__PURE__ */ (0,
|
|
3013
|
+
const searchForm = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit: onSearch, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
3014
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3015
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("label", { htmlFor: "recipient-search", className: "text-sm font-medium text-foreground", children: "Search recipients" }),
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2908
3017
|
"input",
|
|
2909
3018
|
{
|
|
2910
3019
|
id: "recipient-search",
|
|
@@ -2915,7 +3024,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2915
3024
|
}
|
|
2916
3025
|
)
|
|
2917
3026
|
] }),
|
|
2918
|
-
/* @__PURE__ */ (0,
|
|
3027
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2919
3028
|
"button",
|
|
2920
3029
|
{
|
|
2921
3030
|
type: "submit",
|
|
@@ -2926,14 +3035,14 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2926
3035
|
] });
|
|
2927
3036
|
let body;
|
|
2928
3037
|
if (isLoading) {
|
|
2929
|
-
body = /* @__PURE__ */ (0,
|
|
2930
|
-
/* @__PURE__ */ (0,
|
|
2931
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3038
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "status", "aria-label": "Loading recipients", className: "flex flex-col gap-2 p-4", children: [
|
|
3039
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: "Loading recipients" }),
|
|
3040
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
2932
3041
|
] });
|
|
2933
3042
|
} else if (isError) {
|
|
2934
|
-
body = /* @__PURE__ */ (0,
|
|
2935
|
-
/* @__PURE__ */ (0,
|
|
2936
|
-
/* @__PURE__ */ (0,
|
|
3043
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
|
|
3044
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load recipients" }),
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2937
3046
|
"button",
|
|
2938
3047
|
{
|
|
2939
3048
|
type: "button",
|
|
@@ -2947,26 +3056,26 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2947
3056
|
const rows = data?.items ?? [];
|
|
2948
3057
|
const totalPages = data?.totalPages ?? 1;
|
|
2949
3058
|
if (rows.length === 0) {
|
|
2950
|
-
body = /* @__PURE__ */ (0,
|
|
3059
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No recipients" });
|
|
2951
3060
|
} else {
|
|
2952
|
-
body = /* @__PURE__ */ (0,
|
|
2953
|
-
/* @__PURE__ */ (0,
|
|
2954
|
-
/* @__PURE__ */ (0,
|
|
2955
|
-
/* @__PURE__ */ (0,
|
|
2956
|
-
/* @__PURE__ */ (0,
|
|
2957
|
-
/* @__PURE__ */ (0,
|
|
2958
|
-
/* @__PURE__ */ (0,
|
|
2959
|
-
/* @__PURE__ */ (0,
|
|
2960
|
-
/* @__PURE__ */ (0,
|
|
3061
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3063
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
3064
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "User" }),
|
|
3065
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Email" }),
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Phone" }),
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Consents" }),
|
|
3068
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
|
|
3069
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
2961
3070
|
] }) }),
|
|
2962
|
-
/* @__PURE__ */ (0,
|
|
2963
|
-
/* @__PURE__ */ (0,
|
|
2964
|
-
/* @__PURE__ */ (0,
|
|
2965
|
-
/* @__PURE__ */ (0,
|
|
2966
|
-
/* @__PURE__ */ (0,
|
|
2967
|
-
/* @__PURE__ */ (0,
|
|
2968
|
-
/* @__PURE__ */ (0,
|
|
2969
|
-
/* @__PURE__ */ (0,
|
|
3071
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3072
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.userId }),
|
|
3073
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.email ?? "\u2014" }),
|
|
3074
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.phone ?? "\u2014" }),
|
|
3075
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: consents(row) }),
|
|
3076
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui26.formatDateTime)(row.createdAt) }),
|
|
3077
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3078
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2970
3079
|
"button",
|
|
2971
3080
|
{
|
|
2972
3081
|
type: "button",
|
|
@@ -2976,7 +3085,7 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2976
3085
|
children: "Export"
|
|
2977
3086
|
}
|
|
2978
3087
|
),
|
|
2979
|
-
/* @__PURE__ */ (0,
|
|
3088
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2980
3089
|
"button",
|
|
2981
3090
|
{
|
|
2982
3091
|
type: "button",
|
|
@@ -2989,8 +3098,8 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
2989
3098
|
] }) })
|
|
2990
3099
|
] }, row.userId)) })
|
|
2991
3100
|
] }),
|
|
2992
|
-
/* @__PURE__ */ (0,
|
|
2993
|
-
/* @__PURE__ */ (0,
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("nav", { "aria-label": "Recipient pagination", className: "flex items-center justify-between", children: [
|
|
3102
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2994
3103
|
"button",
|
|
2995
3104
|
{
|
|
2996
3105
|
type: "button",
|
|
@@ -3000,13 +3109,13 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
3000
3109
|
children: "Previous"
|
|
3001
3110
|
}
|
|
3002
3111
|
),
|
|
3003
|
-
/* @__PURE__ */ (0,
|
|
3112
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
3004
3113
|
"Page ",
|
|
3005
3114
|
page,
|
|
3006
3115
|
" of ",
|
|
3007
3116
|
totalPages
|
|
3008
3117
|
] }),
|
|
3009
|
-
/* @__PURE__ */ (0,
|
|
3118
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3010
3119
|
"button",
|
|
3011
3120
|
{
|
|
3012
3121
|
type: "button",
|
|
@@ -3020,17 +3129,17 @@ function RecipientAdminPanel({ basePath = "/api", className }) {
|
|
|
3020
3129
|
] });
|
|
3021
3130
|
}
|
|
3022
3131
|
}
|
|
3023
|
-
return /* @__PURE__ */ (0,
|
|
3132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { "aria-label": "Recipient administration", className: (0, import_react_ui26.cn)("flex flex-col gap-4", className), children: [
|
|
3024
3133
|
searchForm,
|
|
3025
3134
|
body
|
|
3026
3135
|
] });
|
|
3027
3136
|
}
|
|
3028
3137
|
|
|
3029
3138
|
// src/webhook-endpoint-manager.tsx
|
|
3030
|
-
var
|
|
3031
|
-
var
|
|
3139
|
+
var import_react17 = require("react");
|
|
3140
|
+
var import_react_ui27 = require("@quanticjs/react-ui");
|
|
3032
3141
|
var import_react_query24 = require("@quanticjs/react-query");
|
|
3033
|
-
var
|
|
3142
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
3034
3143
|
var EVENT_TYPES = [
|
|
3035
3144
|
"notification.sent",
|
|
3036
3145
|
"notification.delivered",
|
|
@@ -3056,15 +3165,15 @@ function WebhookDeliveries({ endpointId, basePath }) {
|
|
|
3056
3165
|
(client) => client.get(`${basePath}/webhook-endpoints/${endpointId}/deliveries`)
|
|
3057
3166
|
);
|
|
3058
3167
|
if (isLoading) {
|
|
3059
|
-
return /* @__PURE__ */ (0,
|
|
3060
|
-
/* @__PURE__ */ (0,
|
|
3061
|
-
[0, 1].map((i) => /* @__PURE__ */ (0,
|
|
3168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { role: "status", "aria-label": "Loading deliveries", className: "flex flex-col gap-2 p-3", children: [
|
|
3169
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Loading deliveries" }),
|
|
3170
|
+
[0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "h-8 animate-pulse rounded bg-muted" }, i))
|
|
3062
3171
|
] });
|
|
3063
3172
|
}
|
|
3064
3173
|
if (isError) {
|
|
3065
|
-
return /* @__PURE__ */ (0,
|
|
3066
|
-
/* @__PURE__ */ (0,
|
|
3067
|
-
/* @__PURE__ */ (0,
|
|
3174
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-start gap-2 p-3", children: [
|
|
3175
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load deliveries" }),
|
|
3176
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3068
3177
|
"button",
|
|
3069
3178
|
{
|
|
3070
3179
|
type: "button",
|
|
@@ -3077,18 +3186,18 @@ function WebhookDeliveries({ endpointId, basePath }) {
|
|
|
3077
3186
|
}
|
|
3078
3187
|
const rows = normalizeDeliveries(data);
|
|
3079
3188
|
if (rows.length === 0) {
|
|
3080
|
-
return /* @__PURE__ */ (0,
|
|
3189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-3 text-center text-sm text-muted-foreground", children: "No deliveries" });
|
|
3081
3190
|
}
|
|
3082
|
-
return /* @__PURE__ */ (0,
|
|
3083
|
-
/* @__PURE__ */ (0,
|
|
3084
|
-
/* @__PURE__ */ (0,
|
|
3085
|
-
/* @__PURE__ */ (0,
|
|
3086
|
-
/* @__PURE__ */ (0,
|
|
3191
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3192
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
3193
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Delivery" }),
|
|
3194
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Status" }),
|
|
3195
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" })
|
|
3087
3196
|
] }) }),
|
|
3088
|
-
/* @__PURE__ */ (0,
|
|
3089
|
-
/* @__PURE__ */ (0,
|
|
3090
|
-
/* @__PURE__ */ (0,
|
|
3091
|
-
/* @__PURE__ */ (0,
|
|
3197
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3198
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "py-2 pe-4 font-mono text-foreground", children: row.id }),
|
|
3199
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: row.status }),
|
|
3200
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-2 text-muted-foreground", children: (0, import_react_ui27.formatDateTime)(row.createdAt) })
|
|
3092
3201
|
] }, row.id)) })
|
|
3093
3202
|
] });
|
|
3094
3203
|
}
|
|
@@ -3096,12 +3205,12 @@ function WebhookEndpointManager({
|
|
|
3096
3205
|
basePath = "/api",
|
|
3097
3206
|
className
|
|
3098
3207
|
}) {
|
|
3099
|
-
const toast = (0,
|
|
3100
|
-
const [url, setUrl] = (0,
|
|
3101
|
-
const [events, setEvents] = (0,
|
|
3102
|
-
const [active, setActive] = (0,
|
|
3103
|
-
const [urlError, setUrlError] = (0,
|
|
3104
|
-
const [expandedId, setExpandedId] = (0,
|
|
3208
|
+
const toast = (0, import_react_ui27.useToast)();
|
|
3209
|
+
const [url, setUrl] = (0, import_react17.useState)("");
|
|
3210
|
+
const [events, setEvents] = (0, import_react17.useState)([]);
|
|
3211
|
+
const [active, setActive] = (0, import_react17.useState)(true);
|
|
3212
|
+
const [urlError, setUrlError] = (0, import_react17.useState)();
|
|
3213
|
+
const [expandedId, setExpandedId] = (0, import_react17.useState)(null);
|
|
3105
3214
|
const { data, isLoading, isError, refetch } = (0, import_react_query24.useApiQuery)(["webhook-endpoints"], (client) => client.get(`${basePath}/webhook-endpoints`));
|
|
3106
3215
|
const create = (0, import_react_query24.useApiMutation)(
|
|
3107
3216
|
(client, payload) => client.post(`${basePath}/webhook-endpoints`, payload),
|
|
@@ -3163,10 +3272,10 @@ function WebhookEndpointManager({
|
|
|
3163
3272
|
remove.mutate(id);
|
|
3164
3273
|
}
|
|
3165
3274
|
};
|
|
3166
|
-
const createForm = /* @__PURE__ */ (0,
|
|
3167
|
-
/* @__PURE__ */ (0,
|
|
3168
|
-
/* @__PURE__ */ (0,
|
|
3169
|
-
/* @__PURE__ */ (0,
|
|
3275
|
+
const createForm = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("form", { onSubmit: onCreate, className: "flex flex-col gap-3", noValidate: true, children: [
|
|
3276
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3277
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("label", { htmlFor: "webhook-url", className: "text-sm font-medium text-foreground", children: "Endpoint URL" }),
|
|
3278
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3170
3279
|
"input",
|
|
3171
3280
|
{
|
|
3172
3281
|
id: "webhook-url",
|
|
@@ -3178,19 +3287,19 @@ function WebhookEndpointManager({
|
|
|
3178
3287
|
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"
|
|
3179
3288
|
}
|
|
3180
3289
|
),
|
|
3181
|
-
urlError && /* @__PURE__ */ (0,
|
|
3290
|
+
urlError && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { id: "webhook-url-error", className: "text-xs text-destructive", children: urlError })
|
|
3182
3291
|
] }),
|
|
3183
|
-
/* @__PURE__ */ (0,
|
|
3184
|
-
/* @__PURE__ */ (0,
|
|
3185
|
-
/* @__PURE__ */ (0,
|
|
3292
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("fieldset", { className: "flex flex-col gap-2", children: [
|
|
3293
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("legend", { className: "text-sm font-medium text-foreground", children: "Events" }),
|
|
3294
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-wrap gap-3", children: EVENT_TYPES.map((evt) => {
|
|
3186
3295
|
const id = `webhook-event-${evt}`;
|
|
3187
|
-
return /* @__PURE__ */ (0,
|
|
3296
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
3188
3297
|
"label",
|
|
3189
3298
|
{
|
|
3190
3299
|
htmlFor: id,
|
|
3191
3300
|
className: "flex items-center gap-2 text-sm text-foreground",
|
|
3192
3301
|
children: [
|
|
3193
|
-
/* @__PURE__ */ (0,
|
|
3302
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3194
3303
|
"input",
|
|
3195
3304
|
{
|
|
3196
3305
|
id,
|
|
@@ -3207,8 +3316,8 @@ function WebhookEndpointManager({
|
|
|
3207
3316
|
);
|
|
3208
3317
|
}) })
|
|
3209
3318
|
] }),
|
|
3210
|
-
/* @__PURE__ */ (0,
|
|
3211
|
-
/* @__PURE__ */ (0,
|
|
3319
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("label", { htmlFor: "webhook-active", className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
3320
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3212
3321
|
"input",
|
|
3213
3322
|
{
|
|
3214
3323
|
id: "webhook-active",
|
|
@@ -3220,7 +3329,7 @@ function WebhookEndpointManager({
|
|
|
3220
3329
|
),
|
|
3221
3330
|
"Active"
|
|
3222
3331
|
] }),
|
|
3223
|
-
/* @__PURE__ */ (0,
|
|
3332
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3224
3333
|
"button",
|
|
3225
3334
|
{
|
|
3226
3335
|
type: "submit",
|
|
@@ -3232,14 +3341,14 @@ function WebhookEndpointManager({
|
|
|
3232
3341
|
] });
|
|
3233
3342
|
let body;
|
|
3234
3343
|
if (isLoading) {
|
|
3235
|
-
body = /* @__PURE__ */ (0,
|
|
3236
|
-
/* @__PURE__ */ (0,
|
|
3237
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3344
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { role: "status", "aria-label": "Loading webhook endpoints", className: "flex flex-col gap-2 p-4", children: [
|
|
3345
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Loading webhook endpoints" }),
|
|
3346
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
3238
3347
|
] });
|
|
3239
3348
|
} else if (isError) {
|
|
3240
|
-
body = /* @__PURE__ */ (0,
|
|
3241
|
-
/* @__PURE__ */ (0,
|
|
3242
|
-
/* @__PURE__ */ (0,
|
|
3349
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col items-start gap-3 p-4", children: [
|
|
3350
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load webhook endpoints" }),
|
|
3351
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3243
3352
|
"button",
|
|
3244
3353
|
{
|
|
3245
3354
|
type: "button",
|
|
@@ -3252,26 +3361,26 @@ function WebhookEndpointManager({
|
|
|
3252
3361
|
} else {
|
|
3253
3362
|
const rows = normalizeEndpoints(data);
|
|
3254
3363
|
if (rows.length === 0) {
|
|
3255
|
-
body = /* @__PURE__ */ (0,
|
|
3364
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No webhook endpoints" });
|
|
3256
3365
|
} else {
|
|
3257
|
-
body = /* @__PURE__ */ (0,
|
|
3258
|
-
/* @__PURE__ */ (0,
|
|
3259
|
-
/* @__PURE__ */ (0,
|
|
3260
|
-
/* @__PURE__ */ (0,
|
|
3261
|
-
/* @__PURE__ */ (0,
|
|
3262
|
-
/* @__PURE__ */ (0,
|
|
3263
|
-
/* @__PURE__ */ (0,
|
|
3366
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3367
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "URL" }),
|
|
3369
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Events" }),
|
|
3370
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Active" }),
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Created" }),
|
|
3372
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
3264
3373
|
] }) }),
|
|
3265
|
-
/* @__PURE__ */ (0,
|
|
3374
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tbody", { children: rows.map((row) => {
|
|
3266
3375
|
const isExpanded = expandedId === row.id;
|
|
3267
|
-
return /* @__PURE__ */ (0,
|
|
3268
|
-
/* @__PURE__ */ (0,
|
|
3269
|
-
/* @__PURE__ */ (0,
|
|
3270
|
-
/* @__PURE__ */ (0,
|
|
3271
|
-
/* @__PURE__ */ (0,
|
|
3272
|
-
/* @__PURE__ */ (0,
|
|
3273
|
-
/* @__PURE__ */ (0,
|
|
3274
|
-
/* @__PURE__ */ (0,
|
|
3376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react17.Fragment, { children: [
|
|
3377
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3378
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "py-3 pe-4 font-mono text-foreground", children: row.url }),
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.events.length > 0 ? row.events.join(", ") : "\u2014" }),
|
|
3380
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_ui27.StatusBadge, { variant: row.active ? "success" : "neutral", appearance: "dot", children: row.active ? "active" : "inactive" }) }),
|
|
3381
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui27.formatDateTime)(row.createdAt) }),
|
|
3382
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3383
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3275
3384
|
"button",
|
|
3276
3385
|
{
|
|
3277
3386
|
type: "button",
|
|
@@ -3281,7 +3390,7 @@ function WebhookEndpointManager({
|
|
|
3281
3390
|
children: row.active ? "Disable" : "Enable"
|
|
3282
3391
|
}
|
|
3283
3392
|
),
|
|
3284
|
-
/* @__PURE__ */ (0,
|
|
3393
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3285
3394
|
"button",
|
|
3286
3395
|
{
|
|
3287
3396
|
type: "button",
|
|
@@ -3291,7 +3400,7 @@ function WebhookEndpointManager({
|
|
|
3291
3400
|
children: "Deliveries"
|
|
3292
3401
|
}
|
|
3293
3402
|
),
|
|
3294
|
-
/* @__PURE__ */ (0,
|
|
3403
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3295
3404
|
"button",
|
|
3296
3405
|
{
|
|
3297
3406
|
type: "button",
|
|
@@ -3303,45 +3412,45 @@ function WebhookEndpointManager({
|
|
|
3303
3412
|
)
|
|
3304
3413
|
] }) })
|
|
3305
3414
|
] }),
|
|
3306
|
-
isExpanded && /* @__PURE__ */ (0,
|
|
3415
|
+
isExpanded && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { colSpan: 5, className: "bg-muted px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WebhookDeliveries, { endpointId: row.id, basePath }) }) })
|
|
3307
3416
|
] }, row.id);
|
|
3308
3417
|
}) })
|
|
3309
3418
|
] });
|
|
3310
3419
|
}
|
|
3311
3420
|
}
|
|
3312
|
-
return /* @__PURE__ */ (0,
|
|
3421
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": "Webhook endpoints", className: (0, import_react_ui27.cn)("flex flex-col gap-4", className), children: [
|
|
3313
3422
|
createForm,
|
|
3314
3423
|
body
|
|
3315
3424
|
] });
|
|
3316
3425
|
}
|
|
3317
3426
|
|
|
3318
3427
|
// src/operations-overview.tsx
|
|
3319
|
-
var
|
|
3428
|
+
var import_react_ui28 = require("@quanticjs/react-ui");
|
|
3320
3429
|
var import_react_query25 = require("@quanticjs/react-query");
|
|
3321
|
-
var
|
|
3430
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3322
3431
|
function OperationsOverview({ basePath = "/api", className }) {
|
|
3323
3432
|
const { data, isLoading, isError, refetch } = (0, import_react_query25.useApiQuery)(
|
|
3324
3433
|
["operations-overview"],
|
|
3325
3434
|
(client) => client.get(`${basePath}/v1/admin/overview`)
|
|
3326
3435
|
);
|
|
3327
3436
|
if (isLoading) {
|
|
3328
|
-
return /* @__PURE__ */ (0,
|
|
3437
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
3329
3438
|
"div",
|
|
3330
3439
|
{
|
|
3331
3440
|
role: "status",
|
|
3332
3441
|
"aria-label": "Loading operations overview",
|
|
3333
|
-
className: (0,
|
|
3442
|
+
className: (0, import_react_ui28.cn)("flex flex-col gap-2 p-4", className),
|
|
3334
3443
|
children: [
|
|
3335
|
-
/* @__PURE__ */ (0,
|
|
3336
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3444
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "sr-only", children: "Loading operations overview" }),
|
|
3445
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-hidden": "true", className: "h-20 animate-pulse rounded bg-muted" }, i))
|
|
3337
3446
|
]
|
|
3338
3447
|
}
|
|
3339
3448
|
);
|
|
3340
3449
|
}
|
|
3341
3450
|
if (isError) {
|
|
3342
|
-
return /* @__PURE__ */ (0,
|
|
3343
|
-
/* @__PURE__ */ (0,
|
|
3344
|
-
/* @__PURE__ */ (0,
|
|
3451
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: (0, import_react_ui28.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
3452
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load operations overview" }),
|
|
3453
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
3345
3454
|
"button",
|
|
3346
3455
|
{
|
|
3347
3456
|
type: "button",
|
|
@@ -3353,7 +3462,7 @@ function OperationsOverview({ basePath = "/api", className }) {
|
|
|
3353
3462
|
] });
|
|
3354
3463
|
}
|
|
3355
3464
|
if (!data) {
|
|
3356
|
-
return /* @__PURE__ */ (0,
|
|
3465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: (0, import_react_ui28.cn)("p-6 text-center text-sm text-muted-foreground", className), children: "No overview data" });
|
|
3357
3466
|
}
|
|
3358
3467
|
const windowHours = data.windowHours;
|
|
3359
3468
|
const channels = data.channels ?? [];
|
|
@@ -3363,51 +3472,51 @@ function OperationsOverview({ basePath = "/api", className }) {
|
|
|
3363
3472
|
{ label: `Delivered (${windowHours}h)`, value: data.totalDelivered },
|
|
3364
3473
|
{ label: `Failed (${windowHours}h)`, value: data.totalFailed }
|
|
3365
3474
|
];
|
|
3366
|
-
return /* @__PURE__ */ (0,
|
|
3367
|
-
/* @__PURE__ */ (0,
|
|
3368
|
-
/* @__PURE__ */ (0,
|
|
3369
|
-
/* @__PURE__ */ (0,
|
|
3475
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("section", { "aria-label": "Operations overview", className: (0, import_react_ui28.cn)("flex flex-col gap-4", className), children: [
|
|
3476
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "rounded-md border border-border bg-card p-4", children: [
|
|
3477
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-xs font-medium text-muted-foreground", children: card.label }),
|
|
3478
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "mt-1 text-2xl font-semibold text-foreground", children: card.value })
|
|
3370
3479
|
] }, card.label)) }),
|
|
3371
|
-
/* @__PURE__ */ (0,
|
|
3372
|
-
/* @__PURE__ */ (0,
|
|
3373
|
-
/* @__PURE__ */ (0,
|
|
3374
|
-
/* @__PURE__ */ (0,
|
|
3480
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap items-center gap-4", children: [
|
|
3481
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3482
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "DLQ pending" }),
|
|
3483
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: dlqPending > 0 ? "destructive" : "success", children: dlqPending })
|
|
3375
3484
|
] }),
|
|
3376
|
-
/* @__PURE__ */ (0,
|
|
3377
|
-
/* @__PURE__ */ (0,
|
|
3378
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
3379
3488
|
] }),
|
|
3380
|
-
/* @__PURE__ */ (0,
|
|
3381
|
-
/* @__PURE__ */ (0,
|
|
3382
|
-
/* @__PURE__ */ (0,
|
|
3489
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-sm text-muted-foreground", children: "Queue" }),
|
|
3491
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_ui28.StatusBadge, { variant: data.queueHealthy ? "success" : "destructive", children: data.queueHealthy ? "Healthy" : "Unhealthy" })
|
|
3383
3492
|
] })
|
|
3384
3493
|
] }),
|
|
3385
|
-
/* @__PURE__ */ (0,
|
|
3386
|
-
/* @__PURE__ */ (0,
|
|
3387
|
-
/* @__PURE__ */ (0,
|
|
3388
|
-
/* @__PURE__ */ (0,
|
|
3389
|
-
/* @__PURE__ */ (0,
|
|
3390
|
-
/* @__PURE__ */ (0,
|
|
3494
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3495
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
3496
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Channel" }),
|
|
3497
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Sends" }),
|
|
3498
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Delivered" }),
|
|
3499
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Failed" })
|
|
3391
3500
|
] }) }),
|
|
3392
|
-
/* @__PURE__ */ (0,
|
|
3393
|
-
/* @__PURE__ */ (0,
|
|
3394
|
-
/* @__PURE__ */ (0,
|
|
3395
|
-
/* @__PURE__ */ (0,
|
|
3396
|
-
/* @__PURE__ */ (0,
|
|
3501
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tbody", { children: channels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("tr", { className: "border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { colSpan: 4, className: "py-3 text-center text-muted-foreground", children: "No channel activity" }) }) : channels.map((row) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3502
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.channel }),
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.sends }),
|
|
3504
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.delivered }),
|
|
3505
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.failed })
|
|
3397
3506
|
] }, row.channel)) })
|
|
3398
3507
|
] }),
|
|
3399
|
-
/* @__PURE__ */ (0,
|
|
3508
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
3400
3509
|
"Generated ",
|
|
3401
|
-
(0,
|
|
3510
|
+
(0, import_react_ui28.formatDateTime)(data.generatedAt)
|
|
3402
3511
|
] })
|
|
3403
3512
|
] });
|
|
3404
3513
|
}
|
|
3405
3514
|
|
|
3406
3515
|
// src/delivery-log-explorer.tsx
|
|
3407
|
-
var
|
|
3408
|
-
var
|
|
3516
|
+
var import_react18 = require("react");
|
|
3517
|
+
var import_react_ui29 = require("@quanticjs/react-ui");
|
|
3409
3518
|
var import_react_query26 = require("@quanticjs/react-query");
|
|
3410
|
-
var
|
|
3519
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3411
3520
|
var LIMIT6 = 20;
|
|
3412
3521
|
var EMPTY_FILTERS = {
|
|
3413
3522
|
channel: "",
|
|
@@ -3428,9 +3537,9 @@ function channelVariant(channel) {
|
|
|
3428
3537
|
}
|
|
3429
3538
|
}
|
|
3430
3539
|
function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
3431
|
-
const [page, setPage] = (0,
|
|
3432
|
-
const [draft, setDraft] = (0,
|
|
3433
|
-
const [applied, setApplied] = (0,
|
|
3540
|
+
const [page, setPage] = (0, import_react18.useState)(1);
|
|
3541
|
+
const [draft, setDraft] = (0, import_react18.useState)(EMPTY_FILTERS);
|
|
3542
|
+
const [applied, setApplied] = (0, import_react18.useState)(EMPTY_FILTERS);
|
|
3434
3543
|
const queryString = (() => {
|
|
3435
3544
|
const params = new URLSearchParams();
|
|
3436
3545
|
params.set("page", String(page));
|
|
@@ -3460,10 +3569,10 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3460
3569
|
});
|
|
3461
3570
|
};
|
|
3462
3571
|
const setField = (key, value) => setDraft((prev) => ({ ...prev, [key]: value }));
|
|
3463
|
-
const filterForm = /* @__PURE__ */ (0,
|
|
3464
|
-
/* @__PURE__ */ (0,
|
|
3465
|
-
/* @__PURE__ */ (0,
|
|
3466
|
-
/* @__PURE__ */ (0,
|
|
3572
|
+
const filterForm = /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("form", { onSubmit: onApply, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3574
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-channel", className: "text-sm font-medium text-foreground", children: "Channel" }),
|
|
3575
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
3467
3576
|
"select",
|
|
3468
3577
|
{
|
|
3469
3578
|
id: "dle-channel",
|
|
@@ -3471,16 +3580,16 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3471
3580
|
onChange: (e) => setField("channel", e.target.value),
|
|
3472
3581
|
className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring",
|
|
3473
3582
|
children: [
|
|
3474
|
-
/* @__PURE__ */ (0,
|
|
3475
|
-
/* @__PURE__ */ (0,
|
|
3476
|
-
/* @__PURE__ */ (0,
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "", children: "All" }),
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "email", children: "Email" }),
|
|
3585
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("option", { value: "sms", children: "SMS" })
|
|
3477
3586
|
]
|
|
3478
3587
|
}
|
|
3479
3588
|
)
|
|
3480
3589
|
] }),
|
|
3481
|
-
/* @__PURE__ */ (0,
|
|
3482
|
-
/* @__PURE__ */ (0,
|
|
3483
|
-
/* @__PURE__ */ (0,
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3591
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-status", className: "text-sm font-medium text-foreground", children: "Status" }),
|
|
3592
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3484
3593
|
"input",
|
|
3485
3594
|
{
|
|
3486
3595
|
id: "dle-status",
|
|
@@ -3491,9 +3600,9 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3491
3600
|
}
|
|
3492
3601
|
)
|
|
3493
3602
|
] }),
|
|
3494
|
-
/* @__PURE__ */ (0,
|
|
3495
|
-
/* @__PURE__ */ (0,
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3604
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-recipient", className: "text-sm font-medium text-foreground", children: "Recipient" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3497
3606
|
"input",
|
|
3498
3607
|
{
|
|
3499
3608
|
id: "dle-recipient",
|
|
@@ -3504,9 +3613,9 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3504
3613
|
}
|
|
3505
3614
|
)
|
|
3506
3615
|
] }),
|
|
3507
|
-
/* @__PURE__ */ (0,
|
|
3508
|
-
/* @__PURE__ */ (0,
|
|
3509
|
-
/* @__PURE__ */ (0,
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3617
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("label", { htmlFor: "dle-user", className: "text-sm font-medium text-foreground", children: "User ID" }),
|
|
3618
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3510
3619
|
"input",
|
|
3511
3620
|
{
|
|
3512
3621
|
id: "dle-user",
|
|
@@ -3517,9 +3626,9 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3517
3626
|
}
|
|
3518
3627
|
)
|
|
3519
3628
|
] }),
|
|
3520
|
-
/* @__PURE__ */ (0,
|
|
3521
|
-
/* @__PURE__ */ (0,
|
|
3522
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
3523
3632
|
"input",
|
|
3524
3633
|
{
|
|
3525
3634
|
id: "dle-from",
|
|
@@ -3530,9 +3639,9 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3530
3639
|
}
|
|
3531
3640
|
)
|
|
3532
3641
|
] }),
|
|
3533
|
-
/* @__PURE__ */ (0,
|
|
3534
|
-
/* @__PURE__ */ (0,
|
|
3535
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
3536
3645
|
"input",
|
|
3537
3646
|
{
|
|
3538
3647
|
id: "dle-to",
|
|
@@ -3543,7 +3652,7 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3543
3652
|
}
|
|
3544
3653
|
)
|
|
3545
3654
|
] }),
|
|
3546
|
-
/* @__PURE__ */ (0,
|
|
3655
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3547
3656
|
"button",
|
|
3548
3657
|
{
|
|
3549
3658
|
type: "submit",
|
|
@@ -3554,14 +3663,14 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3554
3663
|
] });
|
|
3555
3664
|
let body;
|
|
3556
3665
|
if (isLoading) {
|
|
3557
|
-
body = /* @__PURE__ */ (0,
|
|
3558
|
-
/* @__PURE__ */ (0,
|
|
3559
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
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))
|
|
3560
3669
|
] });
|
|
3561
3670
|
} else if (isError) {
|
|
3562
|
-
body = /* @__PURE__ */ (0,
|
|
3563
|
-
/* @__PURE__ */ (0,
|
|
3564
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
3565
3674
|
"button",
|
|
3566
3675
|
{
|
|
3567
3676
|
type: "button",
|
|
@@ -3575,31 +3684,31 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3575
3684
|
const rows = data?.items ?? [];
|
|
3576
3685
|
const totalPages = data?.totalPages ?? 1;
|
|
3577
3686
|
if (rows.length === 0) {
|
|
3578
|
-
body = /* @__PURE__ */ (0,
|
|
3687
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No delivery logs" });
|
|
3579
3688
|
} else {
|
|
3580
|
-
body = /* @__PURE__ */ (0,
|
|
3581
|
-
/* @__PURE__ */ (0,
|
|
3582
|
-
/* @__PURE__ */ (0,
|
|
3583
|
-
/* @__PURE__ */ (0,
|
|
3584
|
-
/* @__PURE__ */ (0,
|
|
3585
|
-
/* @__PURE__ */ (0,
|
|
3586
|
-
/* @__PURE__ */ (0,
|
|
3587
|
-
/* @__PURE__ */ (0,
|
|
3588
|
-
/* @__PURE__ */ (0,
|
|
3589
|
-
/* @__PURE__ */ (0,
|
|
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" })
|
|
3590
3699
|
] }) }),
|
|
3591
|
-
/* @__PURE__ */ (0,
|
|
3592
|
-
/* @__PURE__ */ (0,
|
|
3593
|
-
/* @__PURE__ */ (0,
|
|
3594
|
-
/* @__PURE__ */ (0,
|
|
3595
|
-
/* @__PURE__ */ (0,
|
|
3596
|
-
/* @__PURE__ */ (0,
|
|
3597
|
-
/* @__PURE__ */ (0,
|
|
3598
|
-
/* @__PURE__ */ (0,
|
|
3700
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3701
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "py-3 pe-4", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_ui29.StatusBadge, { variant: channelVariant(row.channel), appearance: "dot", children: row.channel }) }),
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.recipient ?? "\u2014" }),
|
|
3703
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 font-mono text-foreground", children: row.userId }),
|
|
3704
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-foreground", children: row.status }),
|
|
3705
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.provider ?? "\u2014" }),
|
|
3706
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.attempts }),
|
|
3707
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui29.formatDateTime)(row.createdAt) })
|
|
3599
3708
|
] }, row.id)) })
|
|
3600
3709
|
] }),
|
|
3601
|
-
/* @__PURE__ */ (0,
|
|
3602
|
-
/* @__PURE__ */ (0,
|
|
3710
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("nav", { "aria-label": "Delivery log pagination", className: "flex items-center justify-between", children: [
|
|
3711
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3603
3712
|
"button",
|
|
3604
3713
|
{
|
|
3605
3714
|
type: "button",
|
|
@@ -3609,13 +3718,13 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3609
3718
|
children: "Previous"
|
|
3610
3719
|
}
|
|
3611
3720
|
),
|
|
3612
|
-
/* @__PURE__ */ (0,
|
|
3721
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
|
|
3613
3722
|
"Page ",
|
|
3614
3723
|
page,
|
|
3615
3724
|
" of ",
|
|
3616
3725
|
totalPages
|
|
3617
3726
|
] }),
|
|
3618
|
-
/* @__PURE__ */ (0,
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3619
3728
|
"button",
|
|
3620
3729
|
{
|
|
3621
3730
|
type: "button",
|
|
@@ -3629,17 +3738,17 @@ function DeliveryLogExplorer({ basePath = "/api", className }) {
|
|
|
3629
3738
|
] });
|
|
3630
3739
|
}
|
|
3631
3740
|
}
|
|
3632
|
-
return /* @__PURE__ */ (0,
|
|
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: [
|
|
3633
3742
|
filterForm,
|
|
3634
3743
|
body
|
|
3635
3744
|
] });
|
|
3636
3745
|
}
|
|
3637
3746
|
|
|
3638
3747
|
// src/quiet-hours-form.tsx
|
|
3639
|
-
var
|
|
3640
|
-
var
|
|
3748
|
+
var import_react19 = require("react");
|
|
3749
|
+
var import_react_ui30 = require("@quanticjs/react-ui");
|
|
3641
3750
|
var import_react_query27 = require("@quanticjs/react-query");
|
|
3642
|
-
var
|
|
3751
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3643
3752
|
var DEFAULTS = {
|
|
3644
3753
|
enabled: false,
|
|
3645
3754
|
start: "22:00",
|
|
@@ -3656,14 +3765,14 @@ function normalize5(raw) {
|
|
|
3656
3765
|
};
|
|
3657
3766
|
}
|
|
3658
3767
|
function QuietHoursForm({ basePath = "/api", className }) {
|
|
3659
|
-
const toast = (0,
|
|
3768
|
+
const toast = (0, import_react_ui30.useToast)();
|
|
3660
3769
|
const url = `${basePath}/notifications/config/quiet-hours`;
|
|
3661
3770
|
const { data, isLoading, isError, refetch } = (0, import_react_query27.useApiQuery)(
|
|
3662
3771
|
["quiet-hours"],
|
|
3663
3772
|
(client) => client.get(url)
|
|
3664
3773
|
);
|
|
3665
|
-
const [form, setForm] = (0,
|
|
3666
|
-
(0,
|
|
3774
|
+
const [form, setForm] = (0, import_react19.useState)(DEFAULTS);
|
|
3775
|
+
(0, import_react19.useEffect)(() => {
|
|
3667
3776
|
if (data !== void 0) {
|
|
3668
3777
|
setForm(normalize5(data));
|
|
3669
3778
|
}
|
|
@@ -3680,23 +3789,23 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3680
3789
|
save.mutate(form);
|
|
3681
3790
|
};
|
|
3682
3791
|
if (isLoading) {
|
|
3683
|
-
return /* @__PURE__ */ (0,
|
|
3792
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
3684
3793
|
"div",
|
|
3685
3794
|
{
|
|
3686
3795
|
role: "status",
|
|
3687
3796
|
"aria-label": "Loading quiet hours",
|
|
3688
|
-
className: (0,
|
|
3797
|
+
className: (0, import_react_ui30.cn)("flex flex-col gap-2 p-4", className),
|
|
3689
3798
|
children: [
|
|
3690
|
-
/* @__PURE__ */ (0,
|
|
3691
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3799
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "sr-only", children: "Loading quiet hours" }),
|
|
3800
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
3692
3801
|
]
|
|
3693
3802
|
}
|
|
3694
3803
|
);
|
|
3695
3804
|
}
|
|
3696
3805
|
if (isError) {
|
|
3697
|
-
return /* @__PURE__ */ (0,
|
|
3698
|
-
/* @__PURE__ */ (0,
|
|
3699
|
-
/* @__PURE__ */ (0,
|
|
3806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: (0, import_react_ui30.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
3807
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load quiet hours" }),
|
|
3808
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3700
3809
|
"button",
|
|
3701
3810
|
{
|
|
3702
3811
|
type: "button",
|
|
@@ -3707,10 +3816,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3707
3816
|
)
|
|
3708
3817
|
] });
|
|
3709
3818
|
}
|
|
3710
|
-
return /* @__PURE__ */ (0,
|
|
3711
|
-
/* @__PURE__ */ (0,
|
|
3712
|
-
/* @__PURE__ */ (0,
|
|
3713
|
-
/* @__PURE__ */ (0,
|
|
3819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("form", { onSubmit, className: (0, import_react_ui30.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
3820
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Quiet hours" }),
|
|
3821
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
3822
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3714
3823
|
"input",
|
|
3715
3824
|
{
|
|
3716
3825
|
type: "checkbox",
|
|
@@ -3721,10 +3830,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3721
3830
|
),
|
|
3722
3831
|
"Enable quiet hours"
|
|
3723
3832
|
] }),
|
|
3724
|
-
/* @__PURE__ */ (0,
|
|
3725
|
-
/* @__PURE__ */ (0,
|
|
3726
|
-
/* @__PURE__ */ (0,
|
|
3727
|
-
/* @__PURE__ */ (0,
|
|
3833
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-wrap gap-4", children: [
|
|
3834
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3835
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-start", className: "text-sm font-medium text-foreground", children: "Start" }),
|
|
3836
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3728
3837
|
"input",
|
|
3729
3838
|
{
|
|
3730
3839
|
id: "qh-start",
|
|
@@ -3735,9 +3844,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3735
3844
|
}
|
|
3736
3845
|
)
|
|
3737
3846
|
] }),
|
|
3738
|
-
/* @__PURE__ */ (0,
|
|
3739
|
-
/* @__PURE__ */ (0,
|
|
3740
|
-
/* @__PURE__ */ (0,
|
|
3847
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3848
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-end", className: "text-sm font-medium text-foreground", children: "End" }),
|
|
3849
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3741
3850
|
"input",
|
|
3742
3851
|
{
|
|
3743
3852
|
id: "qh-end",
|
|
@@ -3748,9 +3857,9 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3748
3857
|
}
|
|
3749
3858
|
)
|
|
3750
3859
|
] }),
|
|
3751
|
-
/* @__PURE__ */ (0,
|
|
3752
|
-
/* @__PURE__ */ (0,
|
|
3753
|
-
/* @__PURE__ */ (0,
|
|
3860
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
3861
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("label", { htmlFor: "qh-tz", className: "text-sm font-medium text-foreground", children: "Timezone" }),
|
|
3862
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3754
3863
|
"input",
|
|
3755
3864
|
{
|
|
3756
3865
|
id: "qh-tz",
|
|
@@ -3762,7 +3871,7 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3762
3871
|
)
|
|
3763
3872
|
] })
|
|
3764
3873
|
] }),
|
|
3765
|
-
/* @__PURE__ */ (0,
|
|
3874
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3766
3875
|
"button",
|
|
3767
3876
|
{
|
|
3768
3877
|
type: "submit",
|
|
@@ -3775,10 +3884,10 @@ function QuietHoursForm({ basePath = "/api", className }) {
|
|
|
3775
3884
|
}
|
|
3776
3885
|
|
|
3777
3886
|
// src/frequency-cap-table.tsx
|
|
3778
|
-
var
|
|
3779
|
-
var
|
|
3887
|
+
var import_react20 = require("react");
|
|
3888
|
+
var import_react_ui31 = require("@quanticjs/react-ui");
|
|
3780
3889
|
var import_react_query28 = require("@quanticjs/react-query");
|
|
3781
|
-
var
|
|
3890
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3782
3891
|
function normalize6(raw) {
|
|
3783
3892
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.caps) ? raw.caps : [];
|
|
3784
3893
|
return list.map((entry) => {
|
|
@@ -3788,16 +3897,16 @@ function normalize6(raw) {
|
|
|
3788
3897
|
});
|
|
3789
3898
|
}
|
|
3790
3899
|
function FrequencyCapTable({ basePath = "/api", className }) {
|
|
3791
|
-
const toast = (0,
|
|
3900
|
+
const toast = (0, import_react_ui31.useToast)();
|
|
3792
3901
|
const url = `${basePath}/notifications/config/frequency-cap`;
|
|
3793
3902
|
const { data, isLoading, isError, refetch } = (0, import_react_query28.useApiQuery)(
|
|
3794
3903
|
["frequency-cap"],
|
|
3795
3904
|
(client) => client.get(url)
|
|
3796
3905
|
);
|
|
3797
|
-
const [caps, setCaps] = (0,
|
|
3798
|
-
const [newType, setNewType] = (0,
|
|
3799
|
-
const [newMax, setNewMax] = (0,
|
|
3800
|
-
(0,
|
|
3906
|
+
const [caps, setCaps] = (0, import_react20.useState)([]);
|
|
3907
|
+
const [newType, setNewType] = (0, import_react20.useState)("");
|
|
3908
|
+
const [newMax, setNewMax] = (0, import_react20.useState)(0);
|
|
3909
|
+
(0, import_react20.useEffect)(() => {
|
|
3801
3910
|
if (data !== void 0) {
|
|
3802
3911
|
setCaps(normalize6(data));
|
|
3803
3912
|
}
|
|
@@ -3823,23 +3932,23 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3823
3932
|
setNewMax(0);
|
|
3824
3933
|
};
|
|
3825
3934
|
if (isLoading) {
|
|
3826
|
-
return /* @__PURE__ */ (0,
|
|
3935
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
3827
3936
|
"div",
|
|
3828
3937
|
{
|
|
3829
3938
|
role: "status",
|
|
3830
3939
|
"aria-label": "Loading frequency caps",
|
|
3831
|
-
className: (0,
|
|
3940
|
+
className: (0, import_react_ui31.cn)("flex flex-col gap-2 p-4", className),
|
|
3832
3941
|
children: [
|
|
3833
|
-
/* @__PURE__ */ (0,
|
|
3834
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
3942
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Loading frequency caps" }),
|
|
3943
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
3835
3944
|
]
|
|
3836
3945
|
}
|
|
3837
3946
|
);
|
|
3838
3947
|
}
|
|
3839
3948
|
if (isError) {
|
|
3840
|
-
return /* @__PURE__ */ (0,
|
|
3841
|
-
/* @__PURE__ */ (0,
|
|
3842
|
-
/* @__PURE__ */ (0,
|
|
3949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: (0, import_react_ui31.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
3950
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load frequency caps" }),
|
|
3951
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3843
3952
|
"button",
|
|
3844
3953
|
{
|
|
3845
3954
|
type: "button",
|
|
@@ -3850,24 +3959,24 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3850
3959
|
)
|
|
3851
3960
|
] });
|
|
3852
3961
|
}
|
|
3853
|
-
return /* @__PURE__ */ (0,
|
|
3854
|
-
/* @__PURE__ */ (0,
|
|
3855
|
-
/* @__PURE__ */ (0,
|
|
3856
|
-
/* @__PURE__ */ (0,
|
|
3857
|
-
/* @__PURE__ */ (0,
|
|
3858
|
-
/* @__PURE__ */ (0,
|
|
3859
|
-
/* @__PURE__ */ (0,
|
|
3962
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("section", { "aria-label": "Frequency caps", className: (0, import_react_ui31.cn)("flex flex-col gap-4", className), children: [
|
|
3963
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Frequency caps" }),
|
|
3964
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("table", { className: "w-full text-sm", children: [
|
|
3965
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
3966
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "py-2 pe-4 font-medium", children: "Type" }),
|
|
3967
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: "Max per day" }),
|
|
3968
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("th", { scope: "col", className: "px-4 py-2 font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "sr-only", children: "Actions" }) })
|
|
3860
3969
|
] }) }),
|
|
3861
|
-
/* @__PURE__ */ (0,
|
|
3970
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("tbody", { children: caps.map((cap, index) => {
|
|
3862
3971
|
const inputId = `cap-${index}`;
|
|
3863
|
-
return /* @__PURE__ */ (0,
|
|
3864
|
-
/* @__PURE__ */ (0,
|
|
3865
|
-
/* @__PURE__ */ (0,
|
|
3866
|
-
/* @__PURE__ */ (0,
|
|
3972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("tr", { className: "border-b border-border", children: [
|
|
3973
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "py-3 pe-4 text-foreground", children: cap.type }),
|
|
3974
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("td", { className: "px-4 py-3", children: [
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { htmlFor: inputId, className: "sr-only", children: [
|
|
3867
3976
|
"Max per day for ",
|
|
3868
3977
|
cap.type
|
|
3869
3978
|
] }),
|
|
3870
|
-
/* @__PURE__ */ (0,
|
|
3979
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3871
3980
|
"input",
|
|
3872
3981
|
{
|
|
3873
3982
|
id: inputId,
|
|
@@ -3879,7 +3988,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3879
3988
|
}
|
|
3880
3989
|
)
|
|
3881
3990
|
] }),
|
|
3882
|
-
/* @__PURE__ */ (0,
|
|
3991
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3883
3992
|
"button",
|
|
3884
3993
|
{
|
|
3885
3994
|
type: "button",
|
|
@@ -3891,10 +4000,10 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3891
4000
|
] }, `${cap.type}-${index}`);
|
|
3892
4001
|
}) })
|
|
3893
4002
|
] }),
|
|
3894
|
-
/* @__PURE__ */ (0,
|
|
3895
|
-
/* @__PURE__ */ (0,
|
|
3896
|
-
/* @__PURE__ */ (0,
|
|
3897
|
-
/* @__PURE__ */ (0,
|
|
4003
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("form", { onSubmit: addRow, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
4004
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4005
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "cap-new-type", className: "text-sm font-medium text-foreground", children: "New type" }),
|
|
4006
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3898
4007
|
"input",
|
|
3899
4008
|
{
|
|
3900
4009
|
id: "cap-new-type",
|
|
@@ -3905,9 +4014,9 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3905
4014
|
}
|
|
3906
4015
|
)
|
|
3907
4016
|
] }),
|
|
3908
|
-
/* @__PURE__ */ (0,
|
|
3909
|
-
/* @__PURE__ */ (0,
|
|
3910
|
-
/* @__PURE__ */ (0,
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4018
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: "cap-new-max", className: "text-sm font-medium text-foreground", children: "Max per day" }),
|
|
4019
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3911
4020
|
"input",
|
|
3912
4021
|
{
|
|
3913
4022
|
id: "cap-new-max",
|
|
@@ -3919,7 +4028,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3919
4028
|
}
|
|
3920
4029
|
)
|
|
3921
4030
|
] }),
|
|
3922
|
-
/* @__PURE__ */ (0,
|
|
4031
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3923
4032
|
"button",
|
|
3924
4033
|
{
|
|
3925
4034
|
type: "submit",
|
|
@@ -3928,7 +4037,7 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3928
4037
|
}
|
|
3929
4038
|
)
|
|
3930
4039
|
] }),
|
|
3931
|
-
/* @__PURE__ */ (0,
|
|
4040
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3932
4041
|
"button",
|
|
3933
4042
|
{
|
|
3934
4043
|
type: "button",
|
|
@@ -3942,10 +4051,10 @@ function FrequencyCapTable({ basePath = "/api", className }) {
|
|
|
3942
4051
|
}
|
|
3943
4052
|
|
|
3944
4053
|
// src/tenant-config-form.tsx
|
|
3945
|
-
var
|
|
3946
|
-
var
|
|
4054
|
+
var import_react21 = require("react");
|
|
4055
|
+
var import_react_ui32 = require("@quanticjs/react-ui");
|
|
3947
4056
|
var import_react_query29 = require("@quanticjs/react-query");
|
|
3948
|
-
var
|
|
4057
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3949
4058
|
function normalize7(raw) {
|
|
3950
4059
|
const obj = raw ?? {};
|
|
3951
4060
|
const toList = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
@@ -3955,7 +4064,7 @@ function normalize7(raw) {
|
|
|
3955
4064
|
};
|
|
3956
4065
|
}
|
|
3957
4066
|
function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
3958
|
-
const [draft, setDraft] = (0,
|
|
4067
|
+
const [draft, setDraft] = (0, import_react21.useState)("");
|
|
3959
4068
|
const add = (event) => {
|
|
3960
4069
|
event.preventDefault();
|
|
3961
4070
|
const value = draft.trim();
|
|
@@ -3963,15 +4072,15 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
3963
4072
|
onAdd(value);
|
|
3964
4073
|
setDraft("");
|
|
3965
4074
|
};
|
|
3966
|
-
return /* @__PURE__ */ (0,
|
|
3967
|
-
/* @__PURE__ */ (0,
|
|
3968
|
-
/* @__PURE__ */ (0,
|
|
4075
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
4076
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-sm font-medium text-foreground", children: label }),
|
|
4077
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "flex flex-wrap gap-2", children: values.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: "text-sm text-muted-foreground", children: "None" }) : values.map((value) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
3969
4078
|
"li",
|
|
3970
4079
|
{
|
|
3971
4080
|
className: "flex items-center gap-1 rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground",
|
|
3972
4081
|
children: [
|
|
3973
4082
|
value,
|
|
3974
|
-
/* @__PURE__ */ (0,
|
|
4083
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3975
4084
|
"button",
|
|
3976
4085
|
{
|
|
3977
4086
|
type: "button",
|
|
@@ -3985,13 +4094,13 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
3985
4094
|
},
|
|
3986
4095
|
value
|
|
3987
4096
|
)) }),
|
|
3988
|
-
/* @__PURE__ */ (0,
|
|
3989
|
-
/* @__PURE__ */ (0,
|
|
3990
|
-
/* @__PURE__ */ (0,
|
|
4097
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("form", { onSubmit: add, className: "flex items-end gap-2", noValidate: true, children: [
|
|
4098
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4099
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("label", { htmlFor: id, className: "sr-only", children: [
|
|
3991
4100
|
"Add to ",
|
|
3992
4101
|
label
|
|
3993
4102
|
] }),
|
|
3994
|
-
/* @__PURE__ */ (0,
|
|
4103
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3995
4104
|
"input",
|
|
3996
4105
|
{
|
|
3997
4106
|
id,
|
|
@@ -4002,7 +4111,7 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4002
4111
|
}
|
|
4003
4112
|
)
|
|
4004
4113
|
] }),
|
|
4005
|
-
/* @__PURE__ */ (0,
|
|
4114
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4006
4115
|
"button",
|
|
4007
4116
|
{
|
|
4008
4117
|
type: "submit",
|
|
@@ -4014,18 +4123,18 @@ function TagEditor({ id, label, values, onAdd, onRemove }) {
|
|
|
4014
4123
|
] });
|
|
4015
4124
|
}
|
|
4016
4125
|
function TenantConfigForm({ basePath = "/api", className }) {
|
|
4017
|
-
const toast = (0,
|
|
4126
|
+
const toast = (0, import_react_ui32.useToast)();
|
|
4018
4127
|
const url = `${basePath}/admin/notification-config`;
|
|
4019
4128
|
const { data, isLoading, isError, refetch } = (0, import_react_query29.useApiQuery)(
|
|
4020
4129
|
["tenant-config"],
|
|
4021
4130
|
(client) => client.get(url)
|
|
4022
4131
|
);
|
|
4023
|
-
const [config, setConfig] = (0,
|
|
4132
|
+
const [config, setConfig] = (0, import_react21.useState)({
|
|
4024
4133
|
notificationTypes: [],
|
|
4025
4134
|
immediateEmailTypes: []
|
|
4026
4135
|
});
|
|
4027
|
-
const [subsetError, setSubsetError] = (0,
|
|
4028
|
-
(0,
|
|
4136
|
+
const [subsetError, setSubsetError] = (0, import_react21.useState)(void 0);
|
|
4137
|
+
(0, import_react21.useEffect)(() => {
|
|
4029
4138
|
if (data !== void 0) {
|
|
4030
4139
|
setConfig(normalize7(data));
|
|
4031
4140
|
}
|
|
@@ -4067,23 +4176,23 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4067
4176
|
save.mutate(config);
|
|
4068
4177
|
};
|
|
4069
4178
|
if (isLoading) {
|
|
4070
|
-
return /* @__PURE__ */ (0,
|
|
4179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
4071
4180
|
"div",
|
|
4072
4181
|
{
|
|
4073
4182
|
role: "status",
|
|
4074
4183
|
"aria-label": "Loading tenant configuration",
|
|
4075
|
-
className: (0,
|
|
4184
|
+
className: (0, import_react_ui32.cn)("flex flex-col gap-2 p-4", className),
|
|
4076
4185
|
children: [
|
|
4077
|
-
/* @__PURE__ */ (0,
|
|
4078
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
4186
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "sr-only", children: "Loading tenant configuration" }),
|
|
4187
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
4079
4188
|
]
|
|
4080
4189
|
}
|
|
4081
4190
|
);
|
|
4082
4191
|
}
|
|
4083
4192
|
if (isError) {
|
|
4084
|
-
return /* @__PURE__ */ (0,
|
|
4085
|
-
/* @__PURE__ */ (0,
|
|
4086
|
-
/* @__PURE__ */ (0,
|
|
4193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: (0, import_react_ui32.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
4194
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tenant configuration" }),
|
|
4195
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4087
4196
|
"button",
|
|
4088
4197
|
{
|
|
4089
4198
|
type: "button",
|
|
@@ -4095,17 +4204,17 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4095
4204
|
] });
|
|
4096
4205
|
}
|
|
4097
4206
|
const errorId = "tenant-config-subset-error";
|
|
4098
|
-
return /* @__PURE__ */ (0,
|
|
4207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
4099
4208
|
"form",
|
|
4100
4209
|
{
|
|
4101
4210
|
onSubmit,
|
|
4102
|
-
className: (0,
|
|
4211
|
+
className: (0, import_react_ui32.cn)("flex flex-col gap-5", className),
|
|
4103
4212
|
noValidate: true,
|
|
4104
4213
|
"aria-invalid": subsetError ? "true" : void 0,
|
|
4105
4214
|
"aria-describedby": subsetError ? errorId : void 0,
|
|
4106
4215
|
children: [
|
|
4107
|
-
/* @__PURE__ */ (0,
|
|
4108
|
-
/* @__PURE__ */ (0,
|
|
4216
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Notification configuration" }),
|
|
4217
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4109
4218
|
TagEditor,
|
|
4110
4219
|
{
|
|
4111
4220
|
id: "tc-notification-types",
|
|
@@ -4115,7 +4224,7 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4115
4224
|
onRemove: removeType
|
|
4116
4225
|
}
|
|
4117
4226
|
),
|
|
4118
|
-
/* @__PURE__ */ (0,
|
|
4227
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4119
4228
|
TagEditor,
|
|
4120
4229
|
{
|
|
4121
4230
|
id: "tc-immediate-email-types",
|
|
@@ -4125,8 +4234,8 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4125
4234
|
onRemove: removeImmediate
|
|
4126
4235
|
}
|
|
4127
4236
|
),
|
|
4128
|
-
subsetError && /* @__PURE__ */ (0,
|
|
4129
|
-
/* @__PURE__ */ (0,
|
|
4237
|
+
subsetError && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { id: errorId, className: "text-xs text-destructive", children: subsetError }),
|
|
4238
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
4130
4239
|
"button",
|
|
4131
4240
|
{
|
|
4132
4241
|
type: "submit",
|
|
@@ -4141,10 +4250,10 @@ function TenantConfigForm({ basePath = "/api", className }) {
|
|
|
4141
4250
|
}
|
|
4142
4251
|
|
|
4143
4252
|
// src/tracking-config-form.tsx
|
|
4144
|
-
var
|
|
4145
|
-
var
|
|
4253
|
+
var import_react22 = require("react");
|
|
4254
|
+
var import_react_ui33 = require("@quanticjs/react-ui");
|
|
4146
4255
|
var import_react_query30 = require("@quanticjs/react-query");
|
|
4147
|
-
var
|
|
4256
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
4148
4257
|
function normalize8(raw) {
|
|
4149
4258
|
const obj = raw ?? {};
|
|
4150
4259
|
const bool = (...keys) => {
|
|
@@ -4159,17 +4268,17 @@ function normalize8(raw) {
|
|
|
4159
4268
|
};
|
|
4160
4269
|
}
|
|
4161
4270
|
function TrackingConfigForm({ basePath = "/api", className }) {
|
|
4162
|
-
const toast = (0,
|
|
4271
|
+
const toast = (0, import_react_ui33.useToast)();
|
|
4163
4272
|
const url = `${basePath}/analytics/notifications/tracking-config`;
|
|
4164
4273
|
const { data, isLoading, isError, refetch } = (0, import_react_query30.useApiQuery)(
|
|
4165
4274
|
["tracking-config"],
|
|
4166
4275
|
(client) => client.get(url)
|
|
4167
4276
|
);
|
|
4168
|
-
const [form, setForm] = (0,
|
|
4277
|
+
const [form, setForm] = (0, import_react22.useState)({
|
|
4169
4278
|
openTrackingEnabled: false,
|
|
4170
4279
|
clickTrackingEnabled: false
|
|
4171
4280
|
});
|
|
4172
|
-
(0,
|
|
4281
|
+
(0, import_react22.useEffect)(() => {
|
|
4173
4282
|
if (data !== void 0) {
|
|
4174
4283
|
setForm(normalize8(data));
|
|
4175
4284
|
}
|
|
@@ -4189,23 +4298,23 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4189
4298
|
save.mutate(form);
|
|
4190
4299
|
};
|
|
4191
4300
|
if (isLoading) {
|
|
4192
|
-
return /* @__PURE__ */ (0,
|
|
4301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
4193
4302
|
"div",
|
|
4194
4303
|
{
|
|
4195
4304
|
role: "status",
|
|
4196
4305
|
"aria-label": "Loading tracking configuration",
|
|
4197
|
-
className: (0,
|
|
4306
|
+
className: (0, import_react_ui33.cn)("flex flex-col gap-2 p-4", className),
|
|
4198
4307
|
children: [
|
|
4199
|
-
/* @__PURE__ */ (0,
|
|
4200
|
-
[0, 1].map((i) => /* @__PURE__ */ (0,
|
|
4308
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "sr-only", children: "Loading tracking configuration" }),
|
|
4309
|
+
[0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
4201
4310
|
]
|
|
4202
4311
|
}
|
|
4203
4312
|
);
|
|
4204
4313
|
}
|
|
4205
4314
|
if (isError) {
|
|
4206
|
-
return /* @__PURE__ */ (0,
|
|
4207
|
-
/* @__PURE__ */ (0,
|
|
4208
|
-
/* @__PURE__ */ (0,
|
|
4315
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: (0, import_react_ui33.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
4316
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load tracking configuration" }),
|
|
4317
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4209
4318
|
"button",
|
|
4210
4319
|
{
|
|
4211
4320
|
type: "button",
|
|
@@ -4216,10 +4325,10 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4216
4325
|
)
|
|
4217
4326
|
] });
|
|
4218
4327
|
}
|
|
4219
|
-
return /* @__PURE__ */ (0,
|
|
4220
|
-
/* @__PURE__ */ (0,
|
|
4221
|
-
/* @__PURE__ */ (0,
|
|
4222
|
-
/* @__PURE__ */ (0,
|
|
4328
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("form", { onSubmit, className: (0, import_react_ui33.cn)("flex flex-col gap-4", className), noValidate: true, children: [
|
|
4329
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-sm font-semibold text-foreground", children: "Tracking configuration" }),
|
|
4330
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
4331
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4223
4332
|
"input",
|
|
4224
4333
|
{
|
|
4225
4334
|
type: "checkbox",
|
|
@@ -4230,8 +4339,8 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4230
4339
|
),
|
|
4231
4340
|
"Open tracking"
|
|
4232
4341
|
] }),
|
|
4233
|
-
/* @__PURE__ */ (0,
|
|
4234
|
-
/* @__PURE__ */ (0,
|
|
4342
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [
|
|
4343
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4235
4344
|
"input",
|
|
4236
4345
|
{
|
|
4237
4346
|
type: "checkbox",
|
|
@@ -4242,7 +4351,7 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4242
4351
|
),
|
|
4243
4352
|
"Click tracking"
|
|
4244
4353
|
] }),
|
|
4245
|
-
/* @__PURE__ */ (0,
|
|
4354
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
4246
4355
|
"button",
|
|
4247
4356
|
{
|
|
4248
4357
|
type: "submit",
|
|
@@ -4255,23 +4364,23 @@ function TrackingConfigForm({ basePath = "/api", className }) {
|
|
|
4255
4364
|
}
|
|
4256
4365
|
|
|
4257
4366
|
// src/api-key-manager.tsx
|
|
4258
|
-
var
|
|
4259
|
-
var
|
|
4367
|
+
var import_react23 = require("react");
|
|
4368
|
+
var import_react_ui34 = require("@quanticjs/react-ui");
|
|
4260
4369
|
var import_react_query31 = require("@quanticjs/react-query");
|
|
4261
|
-
var
|
|
4370
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
4262
4371
|
function normalize9(raw) {
|
|
4263
4372
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
|
|
4264
4373
|
return list;
|
|
4265
4374
|
}
|
|
4266
4375
|
function ApiKeyManager({ basePath = "/api", className }) {
|
|
4267
|
-
const toast = (0,
|
|
4376
|
+
const toast = (0, import_react_ui34.useToast)();
|
|
4268
4377
|
const url = `${basePath}/v1/admin/api-keys`;
|
|
4269
4378
|
const { data, isLoading, isError, refetch } = (0, import_react_query31.useApiQuery)(
|
|
4270
4379
|
["api-keys"],
|
|
4271
4380
|
(client) => client.get(url)
|
|
4272
4381
|
);
|
|
4273
|
-
const [name, setName] = (0,
|
|
4274
|
-
const [applicationKey, setApplicationKey] = (0,
|
|
4382
|
+
const [name, setName] = (0, import_react23.useState)("");
|
|
4383
|
+
const [applicationKey, setApplicationKey] = (0, import_react23.useState)("");
|
|
4275
4384
|
const onMutationError = (error) => toast.error(error.isServerError ? "Something went wrong" : error.title, {
|
|
4276
4385
|
description: error.isServerError ? `Please try again. (ref: ${error.correlationId ?? "unknown"})` : `${error.detail ?? ""} (ref: ${error.correlationId ?? "unknown"})`
|
|
4277
4386
|
});
|
|
@@ -4316,10 +4425,10 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4316
4425
|
revoke.mutate(id);
|
|
4317
4426
|
}
|
|
4318
4427
|
};
|
|
4319
|
-
const createForm = /* @__PURE__ */ (0,
|
|
4320
|
-
/* @__PURE__ */ (0,
|
|
4321
|
-
/* @__PURE__ */ (0,
|
|
4322
|
-
/* @__PURE__ */ (0,
|
|
4428
|
+
const createForm = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("form", { onSubmit: onCreate, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
4429
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4430
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("label", { htmlFor: "api-key-name", className: "text-sm font-medium text-foreground", children: "New key name" }),
|
|
4431
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4323
4432
|
"input",
|
|
4324
4433
|
{
|
|
4325
4434
|
id: "api-key-name",
|
|
@@ -4330,9 +4439,9 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4330
4439
|
}
|
|
4331
4440
|
)
|
|
4332
4441
|
] }),
|
|
4333
|
-
/* @__PURE__ */ (0,
|
|
4334
|
-
/* @__PURE__ */ (0,
|
|
4335
|
-
/* @__PURE__ */ (0,
|
|
4442
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4443
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("label", { htmlFor: "api-key-app", className: "text-sm font-medium text-foreground", children: "Application (optional)" }),
|
|
4444
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4336
4445
|
"input",
|
|
4337
4446
|
{
|
|
4338
4447
|
id: "api-key-app",
|
|
@@ -4344,7 +4453,7 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4344
4453
|
}
|
|
4345
4454
|
)
|
|
4346
4455
|
] }),
|
|
4347
|
-
/* @__PURE__ */ (0,
|
|
4456
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4348
4457
|
"button",
|
|
4349
4458
|
{
|
|
4350
4459
|
type: "submit",
|
|
@@ -4356,14 +4465,14 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4356
4465
|
] });
|
|
4357
4466
|
let body;
|
|
4358
4467
|
if (isLoading) {
|
|
4359
|
-
body = /* @__PURE__ */ (0,
|
|
4360
|
-
/* @__PURE__ */ (0,
|
|
4361
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
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))
|
|
4362
4471
|
] });
|
|
4363
4472
|
} else if (isError) {
|
|
4364
|
-
body = /* @__PURE__ */ (0,
|
|
4365
|
-
/* @__PURE__ */ (0,
|
|
4366
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
4367
4476
|
"button",
|
|
4368
4477
|
{
|
|
4369
4478
|
type: "button",
|
|
@@ -4376,26 +4485,26 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4376
4485
|
} else {
|
|
4377
4486
|
const rows = normalize9(data);
|
|
4378
4487
|
if (rows.length === 0) {
|
|
4379
|
-
body = /* @__PURE__ */ (0,
|
|
4488
|
+
body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "No API keys" });
|
|
4380
4489
|
} else {
|
|
4381
|
-
body = /* @__PURE__ */ (0,
|
|
4382
|
-
/* @__PURE__ */ (0,
|
|
4383
|
-
/* @__PURE__ */ (0,
|
|
4384
|
-
/* @__PURE__ */ (0,
|
|
4385
|
-
/* @__PURE__ */ (0,
|
|
4386
|
-
/* @__PURE__ */ (0,
|
|
4387
|
-
/* @__PURE__ */ (0,
|
|
4388
|
-
/* @__PURE__ */ (0,
|
|
4389
|
-
/* @__PURE__ */ (0,
|
|
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" }) })
|
|
4390
4499
|
] }) }),
|
|
4391
|
-
/* @__PURE__ */ (0,
|
|
4392
|
-
/* @__PURE__ */ (0,
|
|
4393
|
-
/* @__PURE__ */ (0,
|
|
4394
|
-
/* @__PURE__ */ (0,
|
|
4395
|
-
/* @__PURE__ */ (0,
|
|
4396
|
-
/* @__PURE__ */ (0,
|
|
4397
|
-
/* @__PURE__ */ (0,
|
|
4398
|
-
/* @__PURE__ */ (0,
|
|
4500
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("tbody", { children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("tr", { className: "border-b border-border", children: [
|
|
4501
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "py-3 pe-4 text-foreground", children: row.name }),
|
|
4502
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.prefix ?? "\u2014" }),
|
|
4503
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 font-mono text-muted-foreground", children: row.applicationKey ?? "\u2014" }),
|
|
4504
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_ui34.StatusBadge, { variant: row.revoked ? "destructive" : "success", children: row.revoked ? "Revoked" : "Active" }) }),
|
|
4505
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: (0, import_react_ui34.formatDateTime)(row.createdAt) }),
|
|
4506
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3 text-muted-foreground", children: row.lastUsedAt ? (0, import_react_ui34.formatDateTime)(row.lastUsedAt) : "\u2014" }),
|
|
4507
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("td", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
4399
4508
|
"button",
|
|
4400
4509
|
{
|
|
4401
4510
|
type: "button",
|
|
@@ -4409,17 +4518,17 @@ function ApiKeyManager({ basePath = "/api", className }) {
|
|
|
4409
4518
|
] });
|
|
4410
4519
|
}
|
|
4411
4520
|
}
|
|
4412
|
-
return /* @__PURE__ */ (0,
|
|
4521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { "aria-label": "API key management", className: (0, import_react_ui34.cn)("flex flex-col gap-4", className), children: [
|
|
4413
4522
|
createForm,
|
|
4414
4523
|
body
|
|
4415
4524
|
] });
|
|
4416
4525
|
}
|
|
4417
4526
|
|
|
4418
4527
|
// src/application-registry-panel.tsx
|
|
4419
|
-
var
|
|
4420
|
-
var
|
|
4528
|
+
var import_react24 = require("react");
|
|
4529
|
+
var import_react_ui35 = require("@quanticjs/react-ui");
|
|
4421
4530
|
var import_react_query32 = require("@quanticjs/react-query");
|
|
4422
|
-
var
|
|
4531
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
4423
4532
|
function normalize10(raw) {
|
|
4424
4533
|
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.items) ? raw.items : [];
|
|
4425
4534
|
return list;
|
|
@@ -4428,15 +4537,15 @@ function ApplicationRegistryPanel({
|
|
|
4428
4537
|
basePath = "/api",
|
|
4429
4538
|
className
|
|
4430
4539
|
}) {
|
|
4431
|
-
const toast = (0,
|
|
4540
|
+
const toast = (0, import_react_ui35.useToast)();
|
|
4432
4541
|
const url = `${basePath}/admin/applications`;
|
|
4433
4542
|
const { data, isLoading, isError, refetch } = (0, import_react_query32.useApiQuery)(
|
|
4434
4543
|
["applications"],
|
|
4435
4544
|
(client) => client.get(url)
|
|
4436
4545
|
);
|
|
4437
|
-
const [key, setKey] = (0,
|
|
4438
|
-
const [displayName, setDisplayName] = (0,
|
|
4439
|
-
const [description, setDescription] = (0,
|
|
4546
|
+
const [key, setKey] = (0, import_react24.useState)("");
|
|
4547
|
+
const [displayName, setDisplayName] = (0, import_react24.useState)("");
|
|
4548
|
+
const [description, setDescription] = (0, import_react24.useState)("");
|
|
4440
4549
|
const onMutationError = (error) => toast.error(error.isServerError ? "Something went wrong" : error.title, {
|
|
4441
4550
|
description: error.isServerError ? `Please try again. (ref: ${error.correlationId ?? "unknown"})` : `${error.detail ?? ""} (ref: ${error.correlationId ?? "unknown"})`
|
|
4442
4551
|
});
|
|
@@ -4465,10 +4574,10 @@ function ApplicationRegistryPanel({
|
|
|
4465
4574
|
if (!k || !name) return;
|
|
4466
4575
|
register.mutate({ key: k, displayName: name, description: description.trim() || void 0 });
|
|
4467
4576
|
};
|
|
4468
|
-
const registerForm = /* @__PURE__ */ (0,
|
|
4469
|
-
/* @__PURE__ */ (0,
|
|
4470
|
-
/* @__PURE__ */ (0,
|
|
4471
|
-
/* @__PURE__ */ (0,
|
|
4577
|
+
const registerForm = /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("form", { onSubmit: onRegister, className: "flex flex-wrap items-end gap-3", noValidate: true, children: [
|
|
4578
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4579
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-key", className: "text-sm font-medium text-foreground", children: "Key (slug)" }),
|
|
4580
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4472
4581
|
"input",
|
|
4473
4582
|
{
|
|
4474
4583
|
id: "application-key",
|
|
@@ -4480,9 +4589,9 @@ function ApplicationRegistryPanel({
|
|
|
4480
4589
|
}
|
|
4481
4590
|
)
|
|
4482
4591
|
] }),
|
|
4483
|
-
/* @__PURE__ */ (0,
|
|
4484
|
-
/* @__PURE__ */ (0,
|
|
4485
|
-
/* @__PURE__ */ (0,
|
|
4592
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4593
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-name", className: "text-sm font-medium text-foreground", children: "Display name" }),
|
|
4594
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4486
4595
|
"input",
|
|
4487
4596
|
{
|
|
4488
4597
|
id: "application-name",
|
|
@@ -4494,9 +4603,9 @@ function ApplicationRegistryPanel({
|
|
|
4494
4603
|
}
|
|
4495
4604
|
)
|
|
4496
4605
|
] }),
|
|
4497
|
-
/* @__PURE__ */ (0,
|
|
4498
|
-
/* @__PURE__ */ (0,
|
|
4499
|
-
/* @__PURE__ */ (0,
|
|
4606
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
4607
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: "application-description", className: "text-sm font-medium text-foreground", children: "Description (optional)" }),
|
|
4608
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4500
4609
|
"input",
|
|
4501
4610
|
{
|
|
4502
4611
|
id: "application-description",
|
|
@@ -4507,7 +4616,7 @@ function ApplicationRegistryPanel({
|
|
|
4507
4616
|
}
|
|
4508
4617
|
)
|
|
4509
4618
|
] }),
|
|
4510
|
-
/* @__PURE__ */ (0,
|
|
4619
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4511
4620
|
"button",
|
|
4512
4621
|
{
|
|
4513
4622
|
type: "submit",
|
|
@@ -4518,23 +4627,23 @@ function ApplicationRegistryPanel({
|
|
|
4518
4627
|
)
|
|
4519
4628
|
] });
|
|
4520
4629
|
if (isLoading) {
|
|
4521
|
-
return /* @__PURE__ */ (0,
|
|
4630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
4522
4631
|
"div",
|
|
4523
4632
|
{
|
|
4524
4633
|
role: "status",
|
|
4525
4634
|
"aria-label": "Loading applications",
|
|
4526
|
-
className: (0,
|
|
4635
|
+
className: (0, import_react_ui35.cn)("flex flex-col gap-2 p-4", className),
|
|
4527
4636
|
children: [
|
|
4528
|
-
/* @__PURE__ */ (0,
|
|
4529
|
-
[0, 1, 2].map((i) => /* @__PURE__ */ (0,
|
|
4637
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "sr-only", children: "Loading applications" }),
|
|
4638
|
+
[0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { "aria-hidden": "true", className: "h-10 animate-pulse rounded bg-muted" }, i))
|
|
4530
4639
|
]
|
|
4531
4640
|
}
|
|
4532
4641
|
);
|
|
4533
4642
|
}
|
|
4534
4643
|
if (isError) {
|
|
4535
|
-
return /* @__PURE__ */ (0,
|
|
4536
|
-
/* @__PURE__ */ (0,
|
|
4537
|
-
/* @__PURE__ */ (0,
|
|
4644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: (0, import_react_ui35.cn)("flex flex-col items-start gap-3 p-4", className), children: [
|
|
4645
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-sm text-foreground", children: "Failed to load applications" }),
|
|
4646
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4538
4647
|
"button",
|
|
4539
4648
|
{
|
|
4540
4649
|
type: "button",
|
|
@@ -4546,22 +4655,22 @@ function ApplicationRegistryPanel({
|
|
|
4546
4655
|
] });
|
|
4547
4656
|
}
|
|
4548
4657
|
const apps = normalize10(data);
|
|
4549
|
-
return /* @__PURE__ */ (0,
|
|
4658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { "aria-label": "Applications", className: (0, import_react_ui35.cn)("flex flex-col gap-4 p-4", className), children: [
|
|
4550
4659
|
registerForm,
|
|
4551
|
-
apps.length === 0 ? /* @__PURE__ */ (0,
|
|
4552
|
-
/* @__PURE__ */ (0,
|
|
4553
|
-
/* @__PURE__ */ (0,
|
|
4554
|
-
/* @__PURE__ */ (0,
|
|
4555
|
-
/* @__PURE__ */ (0,
|
|
4556
|
-
/* @__PURE__ */ (0,
|
|
4557
|
-
/* @__PURE__ */ (0,
|
|
4660
|
+
apps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "p-6 text-center text-sm text-muted-foreground", children: "No applications registered" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "w-full text-sm", children: [
|
|
4661
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border text-start text-muted-foreground", children: [
|
|
4662
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Key" }),
|
|
4663
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Name" }),
|
|
4664
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Status" }),
|
|
4665
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Created" }),
|
|
4666
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { className: "py-2 font-medium", children: "Actions" })
|
|
4558
4667
|
] }) }),
|
|
4559
|
-
/* @__PURE__ */ (0,
|
|
4560
|
-
/* @__PURE__ */ (0,
|
|
4561
|
-
/* @__PURE__ */ (0,
|
|
4562
|
-
/* @__PURE__ */ (0,
|
|
4563
|
-
/* @__PURE__ */ (0,
|
|
4564
|
-
/* @__PURE__ */ (0,
|
|
4668
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: apps.map((app) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-b border-border", children: [
|
|
4669
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 font-mono text-foreground", children: app.key }),
|
|
4670
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 text-foreground", children: app.displayName }),
|
|
4671
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_ui35.StatusBadge, { variant: app.status === "active" ? "success" : "neutral", children: app.status }) }),
|
|
4672
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2 text-muted-foreground", children: (0, import_react_ui35.formatDateTime)(app.createdAt) }),
|
|
4673
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-2", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
4565
4674
|
"button",
|
|
4566
4675
|
{
|
|
4567
4676
|
type: "button",
|
|
@@ -4595,6 +4704,7 @@ function ApplicationRegistryPanel({
|
|
|
4595
4704
|
FunnelStats,
|
|
4596
4705
|
MissingTranslationsPanel,
|
|
4597
4706
|
NotificationBell,
|
|
4707
|
+
NotificationCenter,
|
|
4598
4708
|
NotificationInbox,
|
|
4599
4709
|
NotificationPreferences,
|
|
4600
4710
|
NotificationProvider,
|