@odla-ai/chapter 0.48.2 → 0.49.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/dist/{chunk-KZ2AXEZW.js → chunk-KHDO2QFA.js} +749 -543
- package/dist/chunk-KHDO2QFA.js.map +1 -0
- package/dist/{chunk-MQ4OPZCS.js → chunk-KXIGZWG5.js} +720 -615
- package/dist/chunk-KXIGZWG5.js.map +1 -0
- package/dist/{chunk-4HS2JPE2.js → chunk-N2JIKINY.js} +17 -1
- package/dist/chunk-N2JIKINY.js.map +1 -0
- package/dist/{copy-context-CMupQL9h.d.ts → copy-context-By4Nq7yB.d.ts} +6 -1
- package/dist/index.cjs +488 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -3
- package/dist/index.d.ts +50 -3
- package/dist/index.js +484 -114
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +14 -3
- package/dist/ui/admin/index.js +6 -2
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +11 -3
- package/dist/ui/member/index.d.ts +36 -4
- package/dist/ui/member/index.js +6 -2
- package/dist/worker/index.cjs +7293 -6499
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +24 -1
- package/dist/worker/index.d.ts +24 -1
- package/dist/worker/index.js +7241 -6447
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4HS2JPE2.js.map +0 -1
- package/dist/chunk-KZ2AXEZW.js.map +0 -1
- package/dist/chunk-MQ4OPZCS.js.map +0 -1
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
DEFAULT_CHAPTER_COPY,
|
|
5
5
|
formatChapterCopy,
|
|
6
6
|
useChapterCopy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-N2JIKINY.js";
|
|
8
8
|
|
|
9
9
|
// src/ui/admin.tsx
|
|
10
|
-
import { useEffect as useEffect12, useMemo as useMemo3, useRef as useRef3, useState as
|
|
10
|
+
import { useEffect as useEffect12, useMemo as useMemo3, useRef as useRef3, useState as useState24 } from "preact/hooks";
|
|
11
11
|
import { ClerkGate, SignedIn, SignedOut, SignIn, useClerkAuth } from "@odla-ai/auth-clerk";
|
|
12
12
|
import { CrmClient } from "@odla-ai/crm";
|
|
13
13
|
|
|
@@ -458,7 +458,12 @@ function AdminShell({
|
|
|
458
458
|
import { Tabs as Tabs3 } from "@odla-ai/ui/components";
|
|
459
459
|
|
|
460
460
|
// src/ui/admin-billing.tsx
|
|
461
|
-
import {
|
|
461
|
+
import { useState as useState5 } from "preact/hooks";
|
|
462
|
+
import { Button as Button2 } from "@odla-ai/ui/components";
|
|
463
|
+
|
|
464
|
+
// src/ui/payment-recovery-queue.tsx
|
|
465
|
+
import { useState as useState4 } from "preact/hooks";
|
|
466
|
+
import { Button } from "@odla-ai/ui/components";
|
|
462
467
|
|
|
463
468
|
// src/ui/admin-api.ts
|
|
464
469
|
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "preact/hooks";
|
|
@@ -496,36 +501,103 @@ function useAdminResource(getToken, path) {
|
|
|
496
501
|
return { data, loading, error, refresh };
|
|
497
502
|
}
|
|
498
503
|
|
|
499
|
-
// src/ui/
|
|
504
|
+
// src/ui/payment-recovery-queue.tsx
|
|
500
505
|
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
506
|
+
function PaymentRecoveryQueue(props) {
|
|
507
|
+
const network = props.scope === "network";
|
|
508
|
+
const path = network ? "/api/admin/network-commercial-read-model" : "/api/admin/payment-recovery";
|
|
509
|
+
const { data, loading, error, refresh } = useAdminResource(props.getToken, path);
|
|
510
|
+
const [busy, setBusy] = useState4("");
|
|
511
|
+
const [message, setMessage] = useState4("");
|
|
512
|
+
const recover = async (eventId) => {
|
|
513
|
+
setBusy(eventId);
|
|
514
|
+
setMessage("");
|
|
515
|
+
try {
|
|
516
|
+
const result = await adminFetch(props.getToken, path, {
|
|
517
|
+
method: "POST",
|
|
518
|
+
body: JSON.stringify({ eventId, ...network ? { command: "recover-event" } : {} })
|
|
519
|
+
});
|
|
520
|
+
setMessage(result.disposition?.startsWith("quarantine_") || result.matched === false ? "This event still needs its application or payment linkage repaired. Its evidence is saved for another retry." : "Stripe evidence verified and reconciliation retried.");
|
|
521
|
+
refresh();
|
|
522
|
+
} catch (cause) {
|
|
523
|
+
setMessage(cause instanceof Error ? cause.message : "Recovery could not finish. Please retry.");
|
|
524
|
+
} finally {
|
|
525
|
+
setBusy("");
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
const rows = network ? data?.exceptions ?? [] : data?.rows ?? [];
|
|
529
|
+
return /* @__PURE__ */ jsxs4(Panel, { title: "Payment events needing attention", children: [
|
|
530
|
+
loading ? /* @__PURE__ */ jsx4("p", { children: "Checking payment reconciliation\u2026" }) : error ? /* @__PURE__ */ jsxs4("p", { role: "alert", children: [
|
|
531
|
+
"Payment recovery is unavailable: ",
|
|
532
|
+
error
|
|
533
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
534
|
+
rows.length ? /* @__PURE__ */ jsxs4("p", { role: "alert", children: [
|
|
535
|
+
rows.length,
|
|
536
|
+
" payment event",
|
|
537
|
+
rows.length === 1 ? " needs" : "s need",
|
|
538
|
+
" review."
|
|
539
|
+
] }) : /* @__PURE__ */ jsx4("p", { children: "All loaded payment events are reconciled." }),
|
|
540
|
+
data?.truncated ? /* @__PURE__ */ jsx4("p", { role: "alert", children: "This queue is incomplete. Review the remaining events before declaring reconciliation complete." }) : null,
|
|
541
|
+
/* @__PURE__ */ jsx4("ul", { children: rows.map((row) => /* @__PURE__ */ jsxs4("li", { children: [
|
|
542
|
+
row.eventId ? /* @__PURE__ */ jsx4("code", { children: row.eventId }) : null,
|
|
543
|
+
" ",
|
|
544
|
+
/* @__PURE__ */ jsx4("span", { children: row.disposition ?? row.kind }),
|
|
545
|
+
" ",
|
|
546
|
+
row.eventId?.startsWith("evt_") ? /* @__PURE__ */ jsx4(Button, { mini: true, disabled: Boolean(busy), onClick: () => void recover(row.eventId), children: busy === row.eventId ? "Verifying\u2026" : "Verify with Stripe and retry" }) : /* @__PURE__ */ jsx4("span", { children: "Repair the missing payment linkage before retrying." })
|
|
547
|
+
] }, `${row.eventId}:${row.kind}`)) })
|
|
548
|
+
] }),
|
|
549
|
+
message ? /* @__PURE__ */ jsx4("p", { role: "status", children: message }) : null
|
|
550
|
+
] });
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/ui/admin-billing.tsx
|
|
554
|
+
import { DataTable, StatBand } from "@odla-ai/ui/components";
|
|
555
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
501
556
|
var money = (cents) => cents === null ? "Unknown" : `$${(cents / 100).toLocaleString()}`;
|
|
502
557
|
var date = (t) => t ? new Date(t).toLocaleDateString([], { dateStyle: "medium" }) : "\u2014";
|
|
503
|
-
var columns = (copy) => [
|
|
558
|
+
var columns = (copy, cancel, busy) => [
|
|
504
559
|
{ key: "name", header: copy.name, cell: (r) => r.name, sortAs: "string" },
|
|
505
560
|
{ key: "email", header: copy.email, cell: (r) => r.email },
|
|
506
561
|
{ key: "applicationStatus", header: copy.application, cell: (r) => r.applicationStatus },
|
|
507
562
|
{ key: "subscriptionStatus", header: copy.subscription, cell: (r) => r.subscriptionStatus ? `${r.subscriptionStatus}${r.cancelAtPeriodEnd ? ` \xB7 ${copy.cancelling}` : ""}` : "\u2014" },
|
|
508
|
-
{ key: "amountCents", header: "Next-term forecast", cell: (r) => r.amountCents === null ? "Unknown" : /* @__PURE__ */
|
|
509
|
-
r.listAmountCents != null && r.listAmountCents !== r.amountCents ? /* @__PURE__ */
|
|
510
|
-
/* @__PURE__ */
|
|
563
|
+
{ key: "amountCents", header: "Next-term forecast", cell: (r) => r.amountCents === null ? "Unknown" : /* @__PURE__ */ jsxs5("span", { children: [
|
|
564
|
+
r.listAmountCents != null && r.listAmountCents !== r.amountCents ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
565
|
+
/* @__PURE__ */ jsx5("s", { children: money(r.listAmountCents) }),
|
|
511
566
|
" "
|
|
512
567
|
] }) : null,
|
|
513
568
|
money(r.amountCents),
|
|
514
569
|
"/",
|
|
515
570
|
r.interval === "month" ? "mo" : "yr"
|
|
516
571
|
] }), sortAs: "number", sortValue: (r) => r.amountCents ?? -1 },
|
|
572
|
+
{ key: "cancelRenewal", header: "Renewal", cell: (row) => row.subscriptionStatus && ["active", "trialing", "past_due", "unpaid"].includes(row.subscriptionStatus) && !row.cancelAtPeriodEnd ? /* @__PURE__ */ jsx5(Button2, { mini: true, disabled: Boolean(busy), onClick: () => cancel(row.id), children: busy === row.id ? "Canceling\u2026" : "Cancel renewal" }) : row.cancelAtPeriodEnd ? "Ends after paid term" : "\u2014" },
|
|
517
573
|
{ key: "renewalAt", header: copy.renews, cell: (r) => date(r.renewalAt), sortAs: "number", sortValue: (r) => r.renewalAt ?? 0 }
|
|
518
574
|
];
|
|
519
575
|
function BillingBody({ ctx }) {
|
|
520
576
|
const copy = useChapterCopy().admin.billing;
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
577
|
+
const [busy, setBusy] = useState5("");
|
|
578
|
+
const [note, setNote] = useState5("");
|
|
579
|
+
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/billing");
|
|
580
|
+
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.loading });
|
|
581
|
+
if (error || !data) return /* @__PURE__ */ jsxs5(AdminNote, { children: [
|
|
524
582
|
copy.loadFailed,
|
|
525
583
|
error ? `: ${error}` : "",
|
|
526
584
|
"."
|
|
527
585
|
] });
|
|
528
|
-
if (!data.billingReady) return /* @__PURE__ */
|
|
586
|
+
if (!data.billingReady) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.notConfigured });
|
|
587
|
+
const cancel = async (id) => {
|
|
588
|
+
if (!globalThis.confirm?.("Cancel future renewals? Paid access continues through the current term. No refund will be issued.")) return;
|
|
589
|
+
setBusy(id);
|
|
590
|
+
setNote("");
|
|
591
|
+
try {
|
|
592
|
+
await adminFetch(ctx.getToken, `/api/admin/applications/${id}/cancel-renewal`, { method: "POST" });
|
|
593
|
+
setNote("Renewal canceled. Access continues through the paid term.");
|
|
594
|
+
refresh();
|
|
595
|
+
} catch (cause) {
|
|
596
|
+
setNote(cause instanceof Error ? cause.message : "Cancellation could not be confirmed. Please retry.");
|
|
597
|
+
} finally {
|
|
598
|
+
setBusy("");
|
|
599
|
+
}
|
|
600
|
+
};
|
|
529
601
|
const s = data.summary;
|
|
530
602
|
const stats = s ? [
|
|
531
603
|
{ value: s.activeCount, label: copy.active },
|
|
@@ -533,22 +605,24 @@ function BillingBody({ ctx }) {
|
|
|
533
605
|
{ value: s.renewingSoonCount, label: copy.renewingSoon },
|
|
534
606
|
{ value: s.pastDueCount, label: copy.pastDue }
|
|
535
607
|
] : [];
|
|
536
|
-
return /* @__PURE__ */
|
|
537
|
-
|
|
538
|
-
/* @__PURE__ */
|
|
539
|
-
|
|
540
|
-
|
|
608
|
+
return /* @__PURE__ */ jsxs5(AdminPage, { children: [
|
|
609
|
+
note ? /* @__PURE__ */ jsx5("p", { role: "status", children: note }) : null,
|
|
610
|
+
/* @__PURE__ */ jsx5(PaymentRecoveryQueue, { getToken: ctx.getToken }),
|
|
611
|
+
s ? /* @__PURE__ */ jsx5(StatBand, { stats }) : null,
|
|
612
|
+
/* @__PURE__ */ jsxs5(Panel, { title: copy.subscriptions, actions: data.testMode ? /* @__PURE__ */ jsx5("span", { className: "badge", children: copy.testMode }) : void 0, children: [
|
|
613
|
+
data.truncated ? /* @__PURE__ */ jsx5("p", { className: "badge", children: copy.truncated }) : null,
|
|
614
|
+
/* @__PURE__ */ jsx5(DataTable, { rows: data.rows, columns: columns(copy, (id) => void cancel(id), busy), rowKey: (r) => r.id })
|
|
541
615
|
] })
|
|
542
616
|
] });
|
|
543
617
|
}
|
|
544
618
|
function billingSection(options = {}) {
|
|
545
619
|
const { id = "billing", label = "Billing" } = options;
|
|
546
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
620
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx5(BillingBody, { ctx }) };
|
|
547
621
|
}
|
|
548
622
|
|
|
549
623
|
// src/ui/admin-dashboard.tsx
|
|
550
624
|
import { MetricWidget, StepPipeline } from "@odla-ai/ui/components";
|
|
551
|
-
import { Fragment as
|
|
625
|
+
import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
552
626
|
var number = (value) => Math.round(value).toLocaleString();
|
|
553
627
|
var money2 = (cents) => `$${Math.round(cents / 100).toLocaleString()}`;
|
|
554
628
|
var when = (time) => new Date(time).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
@@ -558,50 +632,50 @@ function DashboardBody({ ctx }) {
|
|
|
558
632
|
ctx.getToken,
|
|
559
633
|
"/api/admin/dashboard"
|
|
560
634
|
);
|
|
561
|
-
if (loading) return /* @__PURE__ */
|
|
562
|
-
if (error || !data) return /* @__PURE__ */
|
|
635
|
+
if (loading) return /* @__PURE__ */ jsx6(AdminNote, { children: copy.loading });
|
|
636
|
+
if (error || !data) return /* @__PURE__ */ jsxs6(AdminNote, { children: [
|
|
563
637
|
copy.loadFailed,
|
|
564
638
|
error ? `: ${error}` : "",
|
|
565
639
|
"."
|
|
566
640
|
] });
|
|
567
641
|
const pipelineSteps = Object.entries(data.pipeline).filter(([stage]) => !["declined", "refunded"].includes(stage)).map(([stage, count]) => ({
|
|
568
|
-
icon: /* @__PURE__ */
|
|
642
|
+
icon: /* @__PURE__ */ jsx6("span", { style: { fontSize: 22, fontWeight: 700 }, children: count }),
|
|
569
643
|
title: stage.replaceAll("_", " "),
|
|
570
644
|
description: data.pipelineDelta[stage] ? formatChapterCopy(copy.thisWeek, { count: data.pipelineDelta[stage] }) : copy.noChange
|
|
571
645
|
}));
|
|
572
646
|
const exceptions = ["declined", "refunded"].filter((stage) => stage in data.pipeline).map((stage) => `${stage} ${data.pipeline[stage]}`).join(" \xB7 ");
|
|
573
|
-
return /* @__PURE__ */
|
|
574
|
-
data.incomplete ? /* @__PURE__ */
|
|
575
|
-
/* @__PURE__ */
|
|
647
|
+
return /* @__PURE__ */ jsxs6(AdminPage, { children: [
|
|
648
|
+
data.incomplete ? /* @__PURE__ */ jsx6(AdminNote, { children: "This dashboard snapshot is incomplete. Counts and trends below cover only the loaded records, not the full chapter." }) : null,
|
|
649
|
+
/* @__PURE__ */ jsx6(
|
|
576
650
|
Panel,
|
|
577
651
|
{
|
|
578
|
-
title: /* @__PURE__ */
|
|
652
|
+
title: /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
579
653
|
copy.upcomingCalls,
|
|
580
654
|
" ",
|
|
581
|
-
/* @__PURE__ */
|
|
655
|
+
/* @__PURE__ */ jsxs6("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
582
656
|
"\xB7 ",
|
|
583
657
|
data.timezone
|
|
584
658
|
] })
|
|
585
659
|
] }),
|
|
586
|
-
actions: data.calls.needsAttention ? /* @__PURE__ */
|
|
587
|
-
children: data.agenda.length === 0 ? /* @__PURE__ */
|
|
588
|
-
/* @__PURE__ */
|
|
589
|
-
/* @__PURE__ */
|
|
660
|
+
actions: data.calls.needsAttention ? /* @__PURE__ */ jsx6("span", { className: "badge", children: formatChapterCopy(copy.callsNeedAttention, { count: data.calls.needsAttention }) }) : void 0,
|
|
661
|
+
children: data.agenda.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: copy.noUpcomingCalls }) : /* @__PURE__ */ jsx6("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.agenda.map((meeting) => /* @__PURE__ */ jsxs6("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
662
|
+
/* @__PURE__ */ jsx6("strong", { style: { minWidth: 170 }, children: when(meeting.startAt) }),
|
|
663
|
+
/* @__PURE__ */ jsxs6("span", { style: { flex: 1 }, children: [
|
|
590
664
|
meeting.name,
|
|
591
665
|
meeting.email ? ` \xB7 ${meeting.email}` : ""
|
|
592
666
|
] }),
|
|
593
|
-
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */
|
|
594
|
-
meeting.htmlLink ? /* @__PURE__ */
|
|
595
|
-
meeting.meetUrl ? /* @__PURE__ */
|
|
667
|
+
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */ jsx6("span", { className: "badge", children: copy.drift }) : null,
|
|
668
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx6("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.open }) : null,
|
|
669
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx6("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null
|
|
596
670
|
] }, meeting.id)) })
|
|
597
671
|
}
|
|
598
672
|
),
|
|
599
|
-
/* @__PURE__ */
|
|
600
|
-
/* @__PURE__ */
|
|
601
|
-
data.membersSeries ? /* @__PURE__ */
|
|
673
|
+
/* @__PURE__ */ jsxs6("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 18 }, children: [
|
|
674
|
+
/* @__PURE__ */ jsx6(MetricWidget, { label: copy.applications, data: data.applicationsSeries, format: number }),
|
|
675
|
+
data.membersSeries ? /* @__PURE__ */ jsx6(MetricWidget, { label: copy.newMembers, data: data.membersSeries, format: number }) : /* @__PURE__ */ jsx6(Panel, { title: copy.newMembers, children: /* @__PURE__ */ jsx6("p", { className: "muted", children: copy.newMembersUnavailable }) })
|
|
602
676
|
] }),
|
|
603
|
-
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */
|
|
604
|
-
/* @__PURE__ */
|
|
677
|
+
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */ jsxs6("div", { children: [
|
|
678
|
+
/* @__PURE__ */ jsx6(
|
|
605
679
|
MetricWidget,
|
|
606
680
|
{
|
|
607
681
|
label: copy.revenueAdded,
|
|
@@ -610,7 +684,7 @@ function DashboardBody({ ctx }) {
|
|
|
610
684
|
size: "large"
|
|
611
685
|
}
|
|
612
686
|
),
|
|
613
|
-
/* @__PURE__ */
|
|
687
|
+
/* @__PURE__ */ jsxs6("p", { className: "muted", children: [
|
|
614
688
|
copy.activeMemberships,
|
|
615
689
|
": ",
|
|
616
690
|
data.revenue.activeCount ?? 0,
|
|
@@ -621,31 +695,31 @@ function DashboardBody({ ctx }) {
|
|
|
621
695
|
data.revenue.annualRunRateCents == null ? "Unknown" : money2(data.revenue.annualRunRateCents),
|
|
622
696
|
data.revenue.testMode ? ` \xB7 ${copy.testMode}` : ""
|
|
623
697
|
] })
|
|
624
|
-
] }) : /* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
exceptions ? /* @__PURE__ */
|
|
698
|
+
] }) : /* @__PURE__ */ jsx6(Panel, { title: copy.revenue, children: /* @__PURE__ */ jsx6("p", { className: "muted", children: copy.revenueUnavailable }) }),
|
|
699
|
+
/* @__PURE__ */ jsxs6(Panel, { title: copy.pipeline, children: [
|
|
700
|
+
/* @__PURE__ */ jsx6(StepPipeline, { ariaLabel: copy.pipelineLabel, steps: pipelineSteps }),
|
|
701
|
+
exceptions ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: exceptions }) : null
|
|
628
702
|
] })
|
|
629
703
|
] });
|
|
630
704
|
}
|
|
631
705
|
function dashboardSection(options = {}) {
|
|
632
706
|
const { id = "dashboard", label = "Dashboard" } = options;
|
|
633
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
707
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx6(DashboardBody, { ctx }) };
|
|
634
708
|
}
|
|
635
709
|
|
|
636
710
|
// src/ui/admin-people-workspace.tsx
|
|
637
711
|
import { BillingCard, CrmWorkspace } from "@odla-ai/crm/ui";
|
|
638
712
|
|
|
639
713
|
// src/ui/admin-application-repair.tsx
|
|
640
|
-
import { useState as
|
|
641
|
-
import { Button } from "@odla-ai/ui/components";
|
|
642
|
-
import { jsx as
|
|
714
|
+
import { useState as useState6 } from "preact/hooks";
|
|
715
|
+
import { Button as Button3 } from "@odla-ai/ui/components";
|
|
716
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
643
717
|
function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
|
|
644
|
-
const [busy, setBusy] =
|
|
645
|
-
const [error, setError] =
|
|
646
|
-
return /* @__PURE__ */
|
|
647
|
-
/* @__PURE__ */
|
|
648
|
-
/* @__PURE__ */
|
|
718
|
+
const [busy, setBusy] = useState6(false);
|
|
719
|
+
const [error, setError] = useState6(null);
|
|
720
|
+
return /* @__PURE__ */ jsxs7("section", { className: "panel", children: [
|
|
721
|
+
/* @__PURE__ */ jsx7("p", { children: "This person is missing an application link. Repair checks for one matching chapter application; it does not change membership or profile details." }),
|
|
722
|
+
/* @__PURE__ */ jsx7(Button3, { variant: "secondary", disabled: busy, onClick: async () => {
|
|
649
723
|
setBusy(true);
|
|
650
724
|
setError(null);
|
|
651
725
|
try {
|
|
@@ -657,33 +731,33 @@ function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
|
|
|
657
731
|
setBusy(false);
|
|
658
732
|
}
|
|
659
733
|
}, children: busy ? "Checking application link\u2026" : "Repair application link" }),
|
|
660
|
-
error ? /* @__PURE__ */
|
|
734
|
+
error ? /* @__PURE__ */ jsx7("p", { role: "alert", children: error }) : null
|
|
661
735
|
] });
|
|
662
736
|
}
|
|
663
737
|
|
|
664
738
|
// src/ui/admin-application-stage.tsx
|
|
665
|
-
import { useState as
|
|
666
|
-
import { Button as
|
|
667
|
-
import { Fragment as
|
|
739
|
+
import { useState as useState7 } from "preact/hooks";
|
|
740
|
+
import { Button as Button4, Field, Select } from "@odla-ai/ui/components";
|
|
741
|
+
import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
668
742
|
function ApplicationStagePanel(props) {
|
|
669
743
|
const actions = useAdminResource(
|
|
670
744
|
props.getToken,
|
|
671
745
|
`/api/admin/applications/${encodeURIComponent(props.applicationId)}/actions`
|
|
672
746
|
);
|
|
673
|
-
const [selected, setSelected] =
|
|
674
|
-
const [busy, setBusy] =
|
|
675
|
-
const [error, setError] =
|
|
676
|
-
if (actions.loading) return /* @__PURE__ */
|
|
677
|
-
if (actions.error) return /* @__PURE__ */
|
|
747
|
+
const [selected, setSelected] = useState7("");
|
|
748
|
+
const [busy, setBusy] = useState7(false);
|
|
749
|
+
const [error, setError] = useState7(null);
|
|
750
|
+
if (actions.loading) return /* @__PURE__ */ jsx8("p", { role: "status", children: "Loading available actions\u2026" });
|
|
751
|
+
if (actions.error) return /* @__PURE__ */ jsxs8("p", { role: "alert", children: [
|
|
678
752
|
"Could not load application actions: ",
|
|
679
753
|
actions.error
|
|
680
754
|
] });
|
|
681
755
|
const allowed = actions.data?.allowedStages ?? [];
|
|
682
756
|
const target = allowed.includes(selected) ? selected : "";
|
|
683
|
-
return /* @__PURE__ */
|
|
684
|
-
/* @__PURE__ */
|
|
685
|
-
allowed.length ? /* @__PURE__ */
|
|
686
|
-
/* @__PURE__ */
|
|
757
|
+
return /* @__PURE__ */ jsxs8(Field, { label: "Stage", error, children: [
|
|
758
|
+
/* @__PURE__ */ jsx8("p", { children: actions.data?.status?.replaceAll("_", " ") }),
|
|
759
|
+
allowed.length ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
760
|
+
/* @__PURE__ */ jsx8(
|
|
687
761
|
Select,
|
|
688
762
|
{
|
|
689
763
|
"aria-label": "Move to stage",
|
|
@@ -693,7 +767,7 @@ function ApplicationStagePanel(props) {
|
|
|
693
767
|
options: [{ value: "", label: "Move to\u2026" }, ...allowed.map((value) => ({ value, label: value.replaceAll("_", " ") }))]
|
|
694
768
|
}
|
|
695
769
|
),
|
|
696
|
-
/* @__PURE__ */
|
|
770
|
+
/* @__PURE__ */ jsx8(Button4, { mini: true, disabled: busy || !target, onClick: async () => {
|
|
697
771
|
setBusy(true);
|
|
698
772
|
setError(null);
|
|
699
773
|
try {
|
|
@@ -706,19 +780,19 @@ function ApplicationStagePanel(props) {
|
|
|
706
780
|
actions.refresh();
|
|
707
781
|
}
|
|
708
782
|
}, children: "Move" })
|
|
709
|
-
] }) : /* @__PURE__ */
|
|
783
|
+
] }) : /* @__PURE__ */ jsx8("p", { children: "No stage changes are available under this chapter\u2019s current membership policy." })
|
|
710
784
|
] });
|
|
711
785
|
}
|
|
712
786
|
|
|
713
787
|
// src/ui/operational-profile-form.tsx
|
|
714
|
-
import { Button as
|
|
715
|
-
import { useRef, useState as
|
|
716
|
-
import { jsx as
|
|
788
|
+
import { Button as Button5, Field as Field2, Input, Select as Select2, Textarea } from "@odla-ai/ui/components";
|
|
789
|
+
import { useRef, useState as useState8 } from "preact/hooks";
|
|
790
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
717
791
|
var valueText = (value) => value == null ? "" : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
718
792
|
function OperationalProfileForm(props) {
|
|
719
|
-
const [draft, setDraft] =
|
|
720
|
-
const [busy, setBusy] =
|
|
721
|
-
const [error, setError] =
|
|
793
|
+
const [draft, setDraft] = useState8(() => Object.fromEntries(props.fields.map((field) => [field.id, valueText(props.values[field.id])])));
|
|
794
|
+
const [busy, setBusy] = useState8(false);
|
|
795
|
+
const [error, setError] = useState8(null);
|
|
722
796
|
const pending = useRef(null);
|
|
723
797
|
const changed = props.fields.filter((field) => draft[field.id] !== valueText(props.values[field.id]));
|
|
724
798
|
const save = async () => {
|
|
@@ -746,8 +820,8 @@ function OperationalProfileForm(props) {
|
|
|
746
820
|
setBusy(false);
|
|
747
821
|
}
|
|
748
822
|
};
|
|
749
|
-
return /* @__PURE__ */
|
|
750
|
-
props.fields.map((field) => /* @__PURE__ */
|
|
823
|
+
return /* @__PURE__ */ jsxs9("section", { style: { display: "grid", gap: 16 }, children: [
|
|
824
|
+
props.fields.map((field) => /* @__PURE__ */ jsx9(Field2, { label: field.label, children: field.options || field.type === "boolean" ? /* @__PURE__ */ jsxs9(
|
|
751
825
|
Select2,
|
|
752
826
|
{
|
|
753
827
|
"aria-label": field.label,
|
|
@@ -755,11 +829,11 @@ function OperationalProfileForm(props) {
|
|
|
755
829
|
disabled: busy,
|
|
756
830
|
onChange: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value }),
|
|
757
831
|
children: [
|
|
758
|
-
/* @__PURE__ */
|
|
759
|
-
(field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */
|
|
832
|
+
/* @__PURE__ */ jsx9("option", { value: "", children: "Not set" }),
|
|
833
|
+
(field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */ jsx9("option", { value: option, children: option }, option))
|
|
760
834
|
]
|
|
761
835
|
}
|
|
762
|
-
) : field.type === "json" ? /* @__PURE__ */
|
|
836
|
+
) : field.type === "json" ? /* @__PURE__ */ jsx9(
|
|
763
837
|
Textarea,
|
|
764
838
|
{
|
|
765
839
|
"aria-label": field.label,
|
|
@@ -767,7 +841,7 @@ function OperationalProfileForm(props) {
|
|
|
767
841
|
disabled: busy,
|
|
768
842
|
onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
|
|
769
843
|
}
|
|
770
|
-
) : /* @__PURE__ */
|
|
844
|
+
) : /* @__PURE__ */ jsx9(
|
|
771
845
|
Input,
|
|
772
846
|
{
|
|
773
847
|
"aria-label": field.label,
|
|
@@ -777,26 +851,26 @@ function OperationalProfileForm(props) {
|
|
|
777
851
|
onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
|
|
778
852
|
}
|
|
779
853
|
) }, field.id)),
|
|
780
|
-
error ? /* @__PURE__ */
|
|
781
|
-
/* @__PURE__ */
|
|
854
|
+
error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: error }) : null,
|
|
855
|
+
/* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsx9(Button5, { disabled: busy || !changed.length, onClick: () => void save(), children: busy ? "Saving\u2026" : "Save profile" }) })
|
|
782
856
|
] });
|
|
783
857
|
}
|
|
784
858
|
|
|
785
859
|
// src/ui/admin-person-profile.tsx
|
|
786
|
-
import { Fragment as
|
|
860
|
+
import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
787
861
|
function ChapterPersonProfile(props) {
|
|
788
862
|
const path = `/api/admin/people/profile?${new URLSearchParams({ recordId: props.recordId })}`;
|
|
789
863
|
const resource = useAdminResource(props.getToken, path);
|
|
790
|
-
if (resource.loading) return /* @__PURE__ */
|
|
791
|
-
if (resource.error) return /* @__PURE__ */
|
|
864
|
+
if (resource.loading) return /* @__PURE__ */ jsx10("p", { children: "Loading profile\u2026" });
|
|
865
|
+
if (resource.error) return /* @__PURE__ */ jsx10("p", { role: "alert", children: resource.error });
|
|
792
866
|
const record = resource.data?.record;
|
|
793
|
-
if (!record?.edit) return /* @__PURE__ */
|
|
794
|
-
return /* @__PURE__ */
|
|
795
|
-
/* @__PURE__ */
|
|
867
|
+
if (!record?.edit) return /* @__PURE__ */ jsx10("p", { children: "Profile editing is unavailable." });
|
|
868
|
+
return /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
869
|
+
/* @__PURE__ */ jsxs10("p", { children: [
|
|
796
870
|
String(record.fields.email ?? ""),
|
|
797
871
|
" \u2014 login email changes use the verified account workflow."
|
|
798
872
|
] }),
|
|
799
|
-
/* @__PURE__ */
|
|
873
|
+
/* @__PURE__ */ jsx10(
|
|
800
874
|
OperationalProfileForm,
|
|
801
875
|
{
|
|
802
876
|
fields: record.edit.fields,
|
|
@@ -813,14 +887,14 @@ function ChapterPersonProfile(props) {
|
|
|
813
887
|
}
|
|
814
888
|
|
|
815
889
|
// src/ui/admin-account-sync.tsx
|
|
816
|
-
import { useState as
|
|
817
|
-
import { Button as
|
|
818
|
-
import { jsx as
|
|
890
|
+
import { useState as useState9 } from "preact/hooks";
|
|
891
|
+
import { Button as Button6 } from "@odla-ai/ui/components";
|
|
892
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
819
893
|
function AccountPeopleSync({ getToken, refresh }) {
|
|
820
|
-
const [busy, setBusy] =
|
|
821
|
-
const [message, setMessage] =
|
|
822
|
-
return /* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
894
|
+
const [busy, setBusy] = useState9(false);
|
|
895
|
+
const [message, setMessage] = useState9("");
|
|
896
|
+
return /* @__PURE__ */ jsxs11("div", { children: [
|
|
897
|
+
/* @__PURE__ */ jsx11(Button6, { mini: true, variant: "secondary", disabled: busy, onClick: async () => {
|
|
824
898
|
setBusy(true);
|
|
825
899
|
setMessage("");
|
|
826
900
|
let after = null, count = 0, errors = 0;
|
|
@@ -844,21 +918,21 @@ function AccountPeopleSync({ getToken, refresh }) {
|
|
|
844
918
|
setBusy(false);
|
|
845
919
|
}
|
|
846
920
|
}, children: busy ? "Syncing accounts\u2026" : "Sync accounts" }),
|
|
847
|
-
message ? /* @__PURE__ */
|
|
921
|
+
message ? /* @__PURE__ */ jsx11("p", { role: "status", children: message }) : null
|
|
848
922
|
] });
|
|
849
923
|
}
|
|
850
924
|
|
|
851
925
|
// src/ui/admin-record-actions.tsx
|
|
852
|
-
import { useEffect as useEffect4, useState as
|
|
853
|
-
import { Button as
|
|
854
|
-
import { jsx as
|
|
926
|
+
import { useEffect as useEffect4, useState as useState10 } from "preact/hooks";
|
|
927
|
+
import { Button as Button7, Field as Field3, Select as Select3 } from "@odla-ai/ui/components";
|
|
928
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
855
929
|
function RecordActions({ getToken, record, roles, onChanged }) {
|
|
856
930
|
const copy = useChapterCopy().admin.records;
|
|
857
931
|
const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
|
|
858
932
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
859
|
-
const [access, setAccess] =
|
|
860
|
-
const [busy, setBusy] =
|
|
861
|
-
const [note, setNote] =
|
|
933
|
+
const [access, setAccess] = useState10(null);
|
|
934
|
+
const [busy, setBusy] = useState10(null);
|
|
935
|
+
const [note, setNote] = useState10(null);
|
|
862
936
|
useEffect4(() => {
|
|
863
937
|
if (!userId) {
|
|
864
938
|
setAccess(null);
|
|
@@ -887,9 +961,9 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
887
961
|
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }), copy.approved);
|
|
888
962
|
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
|
|
889
963
|
if (!userId && !applicationId) return null;
|
|
890
|
-
return /* @__PURE__ */
|
|
891
|
-
/* @__PURE__ */
|
|
892
|
-
userId ? /* @__PURE__ */
|
|
964
|
+
return /* @__PURE__ */ jsxs12("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
|
|
965
|
+
/* @__PURE__ */ jsx12("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
|
|
966
|
+
userId ? /* @__PURE__ */ jsx12(Field3, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx12(
|
|
893
967
|
Select3,
|
|
894
968
|
{
|
|
895
969
|
value: access?.role ?? "",
|
|
@@ -897,24 +971,24 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
897
971
|
options: roles.map((r) => ({ value: r, label: r })),
|
|
898
972
|
onChange: (e) => void setRole(e.target.value)
|
|
899
973
|
}
|
|
900
|
-
) }) : /* @__PURE__ */
|
|
901
|
-
applicationId ? /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
974
|
+
) }) : /* @__PURE__ */ jsx12("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
|
|
975
|
+
applicationId ? /* @__PURE__ */ jsxs12("div", { style: { display: "flex", gap: 8 }, children: [
|
|
976
|
+
/* @__PURE__ */ jsx12(Button7, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
|
|
977
|
+
/* @__PURE__ */ jsx12(Button7, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
|
|
904
978
|
] }) : null,
|
|
905
|
-
note ? /* @__PURE__ */
|
|
979
|
+
note ? /* @__PURE__ */ jsx12("p", { className: "muted", style: { margin: 0 }, children: note }) : null
|
|
906
980
|
] });
|
|
907
981
|
}
|
|
908
982
|
|
|
909
983
|
// src/ui/admin-network.tsx
|
|
910
|
-
import { useState as
|
|
911
|
-
import { jsx as
|
|
984
|
+
import { useState as useState11 } from "preact/hooks";
|
|
985
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
912
986
|
function NetworkShareActions(props) {
|
|
913
987
|
const copy = useChapterCopy().admin.network;
|
|
914
988
|
const { recordId, recordType, getToken } = props;
|
|
915
989
|
const targets = useAdminResource(getToken, "/api/admin/network/targets");
|
|
916
|
-
const [busy, setBusy] =
|
|
917
|
-
const [message, setMessage] =
|
|
990
|
+
const [busy, setBusy] = useState11(null);
|
|
991
|
+
const [message, setMessage] = useState11(null);
|
|
918
992
|
const compatible = (targets.data?.targets ?? []).filter((target) => target.types.includes(recordType));
|
|
919
993
|
if (targets.loading || targets.error || compatible.length === 0) return null;
|
|
920
994
|
const push = async (target) => {
|
|
@@ -933,26 +1007,26 @@ function NetworkShareActions(props) {
|
|
|
933
1007
|
setBusy(null);
|
|
934
1008
|
}
|
|
935
1009
|
};
|
|
936
|
-
return /* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
939
|
-
/* @__PURE__ */
|
|
1010
|
+
return /* @__PURE__ */ jsxs13("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
1011
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
1012
|
+
/* @__PURE__ */ jsx13("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
|
|
1013
|
+
/* @__PURE__ */ jsx13("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
|
|
940
1014
|
] }),
|
|
941
|
-
/* @__PURE__ */
|
|
942
|
-
message ? /* @__PURE__ */
|
|
1015
|
+
/* @__PURE__ */ jsx13("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx13("button", { className: "btn secondary mini", disabled: busy !== null, onClick: () => void push(target), children: busy === target.id ? copy.sharing : formatChapterCopy(copy.shareWith, { target: target.name }) }, target.id)) }),
|
|
1016
|
+
message ? /* @__PURE__ */ jsx13("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
|
|
943
1017
|
] });
|
|
944
1018
|
}
|
|
945
1019
|
|
|
946
1020
|
// src/ui/admin-record-activity.tsx
|
|
947
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useState as
|
|
1021
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState12 } from "preact/hooks";
|
|
948
1022
|
import { ActivityFeed } from "@odla-ai/crm/ui";
|
|
949
1023
|
import { parseCrmDiscussionReference as parseCrmDiscussionReference2 } from "@odla-ai/crm";
|
|
950
|
-
import { jsx as
|
|
1024
|
+
import { jsx as jsx14 } from "preact/jsx-runtime";
|
|
951
1025
|
function RecordActivityPanel(props) {
|
|
952
1026
|
const { client, recordId } = props;
|
|
953
|
-
const [activities, setActivities] =
|
|
954
|
-
const [busy, setBusy] =
|
|
955
|
-
const [error, setError] =
|
|
1027
|
+
const [activities, setActivities] = useState12([]);
|
|
1028
|
+
const [busy, setBusy] = useState12(false);
|
|
1029
|
+
const [error, setError] = useState12(null);
|
|
956
1030
|
const reference = typeof window === "undefined" ? null : parseCrmDiscussionReference2(new URL(window.location.href));
|
|
957
1031
|
const focusedActivityId = reference?.kind === "crm:activity" && reference.recordId === recordId ? reference.activityId : void 0;
|
|
958
1032
|
const load = useCallback3(async () => {
|
|
@@ -974,8 +1048,8 @@ function RecordActivityPanel(props) {
|
|
|
974
1048
|
setBusy(false);
|
|
975
1049
|
}
|
|
976
1050
|
};
|
|
977
|
-
if (error) return /* @__PURE__ */
|
|
978
|
-
return /* @__PURE__ */
|
|
1051
|
+
if (error) return /* @__PURE__ */ jsx14("p", { role: "alert", className: "muted", children: error });
|
|
1052
|
+
return /* @__PURE__ */ jsx14(
|
|
979
1053
|
ActivityFeed,
|
|
980
1054
|
{
|
|
981
1055
|
activities,
|
|
@@ -989,17 +1063,17 @@ function RecordActivityPanel(props) {
|
|
|
989
1063
|
}
|
|
990
1064
|
|
|
991
1065
|
// src/ui/admin-record-comms.tsx
|
|
992
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useState as
|
|
993
|
-
import { Button as
|
|
994
|
-
import { jsx as
|
|
1066
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState13 } from "preact/hooks";
|
|
1067
|
+
import { Button as Button8, Field as Field4, Select as Select4 } from "@odla-ai/ui/components";
|
|
1068
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
995
1069
|
function RecordCommsPanel(props) {
|
|
996
1070
|
const copy = useChapterCopy().admin.records;
|
|
997
1071
|
const { client, recordId, getToken } = props;
|
|
998
1072
|
const config = useAdminResource(getToken, "/api/admin/group/email");
|
|
999
1073
|
const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
|
|
1000
|
-
const [template, setTemplate] =
|
|
1001
|
-
const [busy, setBusy] =
|
|
1002
|
-
const [note, setNote] =
|
|
1074
|
+
const [template, setTemplate] = useState13("");
|
|
1075
|
+
const [busy, setBusy] = useState13(false);
|
|
1076
|
+
const [note, setNote] = useState13(null);
|
|
1003
1077
|
const send = async () => {
|
|
1004
1078
|
if (!template) return;
|
|
1005
1079
|
setBusy(true);
|
|
@@ -1013,10 +1087,10 @@ function RecordCommsPanel(props) {
|
|
|
1013
1087
|
setBusy(false);
|
|
1014
1088
|
}
|
|
1015
1089
|
};
|
|
1016
|
-
if (config.loading) return /* @__PURE__ */
|
|
1017
|
-
if (config.error) return /* @__PURE__ */
|
|
1018
|
-
return /* @__PURE__ */
|
|
1019
|
-
/* @__PURE__ */
|
|
1090
|
+
if (config.loading) return /* @__PURE__ */ jsx15("p", { className: "muted", children: copy.loadingTemplates });
|
|
1091
|
+
if (config.error) return /* @__PURE__ */ jsx15("p", { role: "alert", className: "muted", children: config.error });
|
|
1092
|
+
return /* @__PURE__ */ jsxs14("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
|
|
1093
|
+
/* @__PURE__ */ jsx15(Field4, { label: copy.template, children: /* @__PURE__ */ jsx15(
|
|
1020
1094
|
Select4,
|
|
1021
1095
|
{
|
|
1022
1096
|
value: template,
|
|
@@ -1027,15 +1101,15 @@ function RecordCommsPanel(props) {
|
|
|
1027
1101
|
]
|
|
1028
1102
|
}
|
|
1029
1103
|
) }),
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
note ? /* @__PURE__ */
|
|
1104
|
+
/* @__PURE__ */ jsx15("div", { children: /* @__PURE__ */ jsx15(Button8, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
|
|
1105
|
+
note ? /* @__PURE__ */ jsx15("p", { role: "status", className: "muted", children: note }) : null
|
|
1032
1106
|
] });
|
|
1033
1107
|
}
|
|
1034
1108
|
function RecordCommsHistory(props) {
|
|
1035
1109
|
const copy = useChapterCopy().admin.records;
|
|
1036
1110
|
const { client, recordId } = props;
|
|
1037
|
-
const [rows, setRows] =
|
|
1038
|
-
const [error, setError] =
|
|
1111
|
+
const [rows, setRows] = useState13([]);
|
|
1112
|
+
const [error, setError] = useState13(null);
|
|
1039
1113
|
const load = useCallback4(async () => {
|
|
1040
1114
|
try {
|
|
1041
1115
|
const result = await client.emailLog({ recordId, limit: 100 });
|
|
@@ -1046,11 +1120,11 @@ function RecordCommsHistory(props) {
|
|
|
1046
1120
|
}
|
|
1047
1121
|
}, [client, recordId]);
|
|
1048
1122
|
useEffect6(() => void load(), [load]);
|
|
1049
|
-
if (error) return /* @__PURE__ */
|
|
1050
|
-
if (rows.length === 0) return /* @__PURE__ */
|
|
1051
|
-
return /* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
/* @__PURE__ */
|
|
1123
|
+
if (error) return /* @__PURE__ */ jsx15("p", { role: "alert", className: "muted", children: error });
|
|
1124
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx15("p", { className: "muted", children: copy.noMessages });
|
|
1125
|
+
return /* @__PURE__ */ jsx15("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs14("li", { className: "panel", children: [
|
|
1126
|
+
/* @__PURE__ */ jsx15("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
|
|
1127
|
+
/* @__PURE__ */ jsxs14("div", { className: "muted", children: [
|
|
1054
1128
|
row["to"] ? String(row["to"]) : "",
|
|
1055
1129
|
row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
|
|
1056
1130
|
] })
|
|
@@ -1058,9 +1132,9 @@ function RecordCommsHistory(props) {
|
|
|
1058
1132
|
}
|
|
1059
1133
|
|
|
1060
1134
|
// src/ui/admin-record-scheduling.tsx
|
|
1061
|
-
import { useState as
|
|
1062
|
-
import { Button as
|
|
1063
|
-
import { Fragment as
|
|
1135
|
+
import { useState as useState14 } from "preact/hooks";
|
|
1136
|
+
import { Button as Button9 } from "@odla-ai/ui/components";
|
|
1137
|
+
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs15 } from "preact/jsx-runtime";
|
|
1064
1138
|
function RecordSchedulingPanel(props) {
|
|
1065
1139
|
const copy = useChapterCopy().admin.records;
|
|
1066
1140
|
const { getToken, applicationId } = props;
|
|
@@ -1068,8 +1142,8 @@ function RecordSchedulingPanel(props) {
|
|
|
1068
1142
|
getToken,
|
|
1069
1143
|
"/api/admin/meetings?all=1"
|
|
1070
1144
|
);
|
|
1071
|
-
const [busy, setBusy] =
|
|
1072
|
-
const [note, setNote] =
|
|
1145
|
+
const [busy, setBusy] = useState14(null);
|
|
1146
|
+
const [note, setNote] = useState14(null);
|
|
1073
1147
|
const meetings = (resource.data?.meetings ?? []).filter((meeting) => meeting.applicationId === applicationId);
|
|
1074
1148
|
const run = async (meeting, action) => {
|
|
1075
1149
|
let body;
|
|
@@ -1096,33 +1170,33 @@ function RecordSchedulingPanel(props) {
|
|
|
1096
1170
|
setBusy(null);
|
|
1097
1171
|
}
|
|
1098
1172
|
};
|
|
1099
|
-
if (!applicationId) return /* @__PURE__ */
|
|
1100
|
-
if (resource.loading) return /* @__PURE__ */
|
|
1101
|
-
if (resource.error) return /* @__PURE__ */
|
|
1102
|
-
if (meetings.length === 0) return /* @__PURE__ */
|
|
1103
|
-
return /* @__PURE__ */
|
|
1104
|
-
meetings.map((meeting) => /* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */
|
|
1173
|
+
if (!applicationId) return /* @__PURE__ */ jsx16("p", { className: "muted", children: copy.noApplication });
|
|
1174
|
+
if (resource.loading) return /* @__PURE__ */ jsx16("p", { className: "muted", children: copy.loadingMeetings });
|
|
1175
|
+
if (resource.error) return /* @__PURE__ */ jsx16("p", { role: "alert", className: "muted", children: resource.error });
|
|
1176
|
+
if (meetings.length === 0) return /* @__PURE__ */ jsx16("p", { className: "muted", children: copy.noMeetings });
|
|
1177
|
+
return /* @__PURE__ */ jsxs15("div", { style: { display: "grid", gap: 12 }, children: [
|
|
1178
|
+
meetings.map((meeting) => /* @__PURE__ */ jsxs15("section", { className: "panel", children: [
|
|
1179
|
+
/* @__PURE__ */ jsx16("strong", { children: new Date(meeting.startAt).toLocaleString() }),
|
|
1180
|
+
/* @__PURE__ */ jsxs15("p", { className: "muted", children: [
|
|
1107
1181
|
meeting.status,
|
|
1108
1182
|
" \xB7 ",
|
|
1109
1183
|
resource.data?.timezone
|
|
1110
1184
|
] }),
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
meeting.meetUrl ? /* @__PURE__ */
|
|
1113
|
-
meeting.htmlLink ? /* @__PURE__ */
|
|
1114
|
-
meeting.status === "scheduled" ? /* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1185
|
+
/* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
|
|
1186
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx16("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
|
|
1187
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx16("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
|
|
1188
|
+
meeting.status === "scheduled" ? /* @__PURE__ */ jsxs15(Fragment7, { children: [
|
|
1189
|
+
/* @__PURE__ */ jsx16(Button9, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
|
|
1190
|
+
/* @__PURE__ */ jsx16(Button9, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
|
|
1117
1191
|
] }) : null
|
|
1118
1192
|
] })
|
|
1119
1193
|
] }, meeting.id)),
|
|
1120
|
-
note ? /* @__PURE__ */
|
|
1194
|
+
note ? /* @__PURE__ */ jsx16("p", { role: "status", className: "muted", children: note }) : null
|
|
1121
1195
|
] });
|
|
1122
1196
|
}
|
|
1123
1197
|
|
|
1124
1198
|
// src/ui/admin-people-workspace.tsx
|
|
1125
|
-
import { Fragment as
|
|
1199
|
+
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs16 } from "preact/jsx-runtime";
|
|
1126
1200
|
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
1127
1201
|
return {
|
|
1128
1202
|
transitionStage: async ({ record, to }) => {
|
|
@@ -1171,7 +1245,7 @@ function PeopleBody(props) {
|
|
|
1171
1245
|
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
|
|
1172
1246
|
const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
|
|
1173
1247
|
const workspaceClient = client;
|
|
1174
|
-
return /* @__PURE__ */
|
|
1248
|
+
return /* @__PURE__ */ jsx17(AdminPage, { children: /* @__PURE__ */ jsx17(
|
|
1175
1249
|
CrmWorkspace,
|
|
1176
1250
|
{
|
|
1177
1251
|
crm,
|
|
@@ -1187,9 +1261,9 @@ function PeopleBody(props) {
|
|
|
1187
1261
|
collapseClosedDetail,
|
|
1188
1262
|
lifecycle: resolvedLifecycle,
|
|
1189
1263
|
requireLifecycleAdapter: requireLifecycleAdapter ?? lifecycle,
|
|
1190
|
-
renderSummary: (context) => /* @__PURE__ */
|
|
1264
|
+
renderSummary: (context) => /* @__PURE__ */ jsxs16(Fragment8, { children: [
|
|
1191
1265
|
renderSummary?.(context, ctx),
|
|
1192
|
-
type === "person" ? /* @__PURE__ */
|
|
1266
|
+
type === "person" ? /* @__PURE__ */ jsx17(AccountPeopleSync, { getToken: ctx.getToken, refresh: context.query.refresh }) : null
|
|
1193
1267
|
] }),
|
|
1194
1268
|
renderMaster: renderMaster ? (context) => renderMaster(context, ctx) : void 0,
|
|
1195
1269
|
renderDetailHeader: renderDetailHeader ? (context) => renderDetailHeader(context, ctx) : void 0,
|
|
@@ -1203,7 +1277,7 @@ function PeopleBody(props) {
|
|
|
1203
1277
|
const add = (id) => {
|
|
1204
1278
|
const tab = byId.get(id);
|
|
1205
1279
|
if (tab && lifecycle && type === "person" && id === "profile" && !lifecycleAdapter) {
|
|
1206
|
-
tabs.push({ ...tab, panel: /* @__PURE__ */
|
|
1280
|
+
tabs.push({ ...tab, panel: /* @__PURE__ */ jsx17(
|
|
1207
1281
|
ChapterPersonProfile,
|
|
1208
1282
|
{
|
|
1209
1283
|
recordId: record.id,
|
|
@@ -1218,7 +1292,7 @@ function PeopleBody(props) {
|
|
|
1218
1292
|
return;
|
|
1219
1293
|
}
|
|
1220
1294
|
if (tab && lifecycle && applicationId && id === "stage" && !lifecycleAdapter) {
|
|
1221
|
-
tabs.push({ ...tab, panel: /* @__PURE__ */
|
|
1295
|
+
tabs.push({ ...tab, panel: /* @__PURE__ */ jsx17(
|
|
1222
1296
|
ApplicationStagePanel,
|
|
1223
1297
|
{
|
|
1224
1298
|
applicationId,
|
|
@@ -1231,8 +1305,8 @@ function PeopleBody(props) {
|
|
|
1231
1305
|
}
|
|
1232
1306
|
if (tab) tabs.push(lifecycle && !applicationId && id === "stage" ? {
|
|
1233
1307
|
...tab,
|
|
1234
|
-
panel: record.stage == null ? /* @__PURE__ */
|
|
1235
|
-
/* @__PURE__ */
|
|
1308
|
+
panel: record.stage == null ? /* @__PURE__ */ jsx17("p", { children: "Journey stage: Not applicable. No membership application is linked. Administrative access is separate." }) : /* @__PURE__ */ jsxs16(Fragment8, { children: [
|
|
1309
|
+
/* @__PURE__ */ jsx17(ApplicationLinkRepair, { recordId: record.id, getToken: ctx.getToken, onChanged: () => {
|
|
1236
1310
|
context.refreshRecord();
|
|
1237
1311
|
context.refreshList();
|
|
1238
1312
|
} }, record.id),
|
|
@@ -1246,35 +1320,35 @@ function PeopleBody(props) {
|
|
|
1246
1320
|
tabs.push({
|
|
1247
1321
|
id: "comms",
|
|
1248
1322
|
label: copy.comms,
|
|
1249
|
-
panel: /* @__PURE__ */
|
|
1323
|
+
panel: /* @__PURE__ */ jsx17(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
|
|
1250
1324
|
});
|
|
1251
1325
|
tabs.push({
|
|
1252
1326
|
id: "history",
|
|
1253
1327
|
label: copy.commsHistory,
|
|
1254
|
-
panel: /* @__PURE__ */
|
|
1328
|
+
panel: /* @__PURE__ */ jsx17(RecordCommsHistory, { client, recordId: record.id })
|
|
1255
1329
|
});
|
|
1256
1330
|
}
|
|
1257
1331
|
if (lifecycle) {
|
|
1258
1332
|
tabs.push({
|
|
1259
1333
|
id: "scheduling",
|
|
1260
1334
|
label: copy.scheduling,
|
|
1261
|
-
panel: /* @__PURE__ */
|
|
1335
|
+
panel: /* @__PURE__ */ jsx17(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
|
|
1262
1336
|
});
|
|
1263
|
-
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */
|
|
1337
|
+
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsx17(BillingCard, { record }) });
|
|
1264
1338
|
} else {
|
|
1265
1339
|
add("billing");
|
|
1266
1340
|
}
|
|
1267
1341
|
tabs.push({
|
|
1268
1342
|
id: "notes",
|
|
1269
1343
|
label: copy.notes,
|
|
1270
|
-
panel: /* @__PURE__ */
|
|
1344
|
+
panel: /* @__PURE__ */ jsx17(RecordActivityPanel, { client, recordId: record.id })
|
|
1271
1345
|
});
|
|
1272
1346
|
add("connections");
|
|
1273
1347
|
if (lifecycle) {
|
|
1274
1348
|
tabs.push({
|
|
1275
1349
|
id: "access",
|
|
1276
1350
|
label: copy.access,
|
|
1277
|
-
panel: /* @__PURE__ */
|
|
1351
|
+
panel: /* @__PURE__ */ jsx17(
|
|
1278
1352
|
RecordActions,
|
|
1279
1353
|
{
|
|
1280
1354
|
getToken: ctx.getToken,
|
|
@@ -1293,7 +1367,7 @@ function PeopleBody(props) {
|
|
|
1293
1367
|
tabs.push({
|
|
1294
1368
|
id: "sharing",
|
|
1295
1369
|
label: copy.sharing,
|
|
1296
|
-
panel: /* @__PURE__ */
|
|
1370
|
+
panel: /* @__PURE__ */ jsx17(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
|
|
1297
1371
|
});
|
|
1298
1372
|
}
|
|
1299
1373
|
return extendRecordTabs?.(tabs, context) ?? tabs;
|
|
@@ -1303,7 +1377,7 @@ function PeopleBody(props) {
|
|
|
1303
1377
|
}
|
|
1304
1378
|
|
|
1305
1379
|
// src/ui/admin-people.tsx
|
|
1306
|
-
import { jsx as
|
|
1380
|
+
import { jsx as jsx18 } from "preact/jsx-runtime";
|
|
1307
1381
|
var DEFAULT_ROLES = ["provisional", "member", "admin"];
|
|
1308
1382
|
function collectionSection(options) {
|
|
1309
1383
|
const { crm, type } = options;
|
|
@@ -1333,7 +1407,7 @@ function collectionSection(options) {
|
|
|
1333
1407
|
return {
|
|
1334
1408
|
id,
|
|
1335
1409
|
label,
|
|
1336
|
-
render: (ctx) => /* @__PURE__ */
|
|
1410
|
+
render: (ctx) => /* @__PURE__ */ jsx18(
|
|
1337
1411
|
PeopleBody,
|
|
1338
1412
|
{
|
|
1339
1413
|
crm,
|
|
@@ -1407,12 +1481,12 @@ import {
|
|
|
1407
1481
|
Pagination,
|
|
1408
1482
|
Select as Select5
|
|
1409
1483
|
} from "@odla-ai/ui/components";
|
|
1410
|
-
import { useEffect as useEffect7, useMemo, useState as
|
|
1484
|
+
import { useEffect as useEffect7, useMemo, useState as useState16 } from "preact/hooks";
|
|
1411
1485
|
|
|
1412
1486
|
// src/ui/admin-network-notes.tsx
|
|
1413
|
-
import { Button as
|
|
1414
|
-
import { useState as
|
|
1415
|
-
import { jsx as
|
|
1487
|
+
import { Button as Button10, Field as Field5, Textarea as Textarea2 } from "@odla-ai/ui/components";
|
|
1488
|
+
import { useState as useState15 } from "preact/hooks";
|
|
1489
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "preact/jsx-runtime";
|
|
1416
1490
|
function displayValue(value) {
|
|
1417
1491
|
if (Array.isArray(value)) return value.join(", ");
|
|
1418
1492
|
if (value && typeof value === "object") return JSON.stringify(value);
|
|
@@ -1427,9 +1501,9 @@ function PrivateNotes(props) {
|
|
|
1427
1501
|
});
|
|
1428
1502
|
const path = `/api/admin/network/notes?${query}`;
|
|
1429
1503
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1430
|
-
const [body, setBody] =
|
|
1431
|
-
const [busy, setBusy] =
|
|
1432
|
-
const [error, setError] =
|
|
1504
|
+
const [body, setBody] = useState15("");
|
|
1505
|
+
const [busy, setBusy] = useState15(false);
|
|
1506
|
+
const [error, setError] = useState15(null);
|
|
1433
1507
|
const submit = async () => {
|
|
1434
1508
|
const trimmed = body.trim();
|
|
1435
1509
|
if (!trimmed) return;
|
|
@@ -1448,18 +1522,18 @@ function PrivateNotes(props) {
|
|
|
1448
1522
|
setBusy(false);
|
|
1449
1523
|
}
|
|
1450
1524
|
};
|
|
1451
|
-
return /* @__PURE__ */
|
|
1452
|
-
/* @__PURE__ */
|
|
1525
|
+
return /* @__PURE__ */ jsxs17("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1526
|
+
/* @__PURE__ */ jsxs17("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1453
1527
|
"Private to ",
|
|
1454
1528
|
leaderName
|
|
1455
1529
|
] }),
|
|
1456
|
-
/* @__PURE__ */
|
|
1530
|
+
/* @__PURE__ */ jsx19(
|
|
1457
1531
|
Field5,
|
|
1458
1532
|
{
|
|
1459
1533
|
label: "Add a note",
|
|
1460
1534
|
hint: `Visible only to ${leaderName} admins. Nothing is written back to the follower.`,
|
|
1461
1535
|
error,
|
|
1462
|
-
children: /* @__PURE__ */
|
|
1536
|
+
children: /* @__PURE__ */ jsx19(
|
|
1463
1537
|
Textarea2,
|
|
1464
1538
|
{
|
|
1465
1539
|
rows: 4,
|
|
@@ -1470,13 +1544,13 @@ function PrivateNotes(props) {
|
|
|
1470
1544
|
)
|
|
1471
1545
|
}
|
|
1472
1546
|
),
|
|
1473
|
-
/* @__PURE__ */
|
|
1474
|
-
notes.loading ? /* @__PURE__ */
|
|
1475
|
-
notes.error ? /* @__PURE__ */
|
|
1476
|
-
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */
|
|
1477
|
-
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
1479
|
-
/* @__PURE__ */
|
|
1547
|
+
/* @__PURE__ */ jsx19("div", { children: /* @__PURE__ */ jsx19(Button10, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save note" }) }),
|
|
1548
|
+
notes.loading ? /* @__PURE__ */ jsx19("p", { className: "muted", children: "Loading notes\u2026" }) : null,
|
|
1549
|
+
notes.error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: notes.error }) : null,
|
|
1550
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx19("p", { className: "muted", children: "No notes yet." }) : null,
|
|
1551
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs17("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1552
|
+
/* @__PURE__ */ jsx19("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1553
|
+
/* @__PURE__ */ jsxs17("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1480
1554
|
note.authorEmail ?? note.authorId,
|
|
1481
1555
|
" \xB7 ",
|
|
1482
1556
|
new Date(note.createdAt).toLocaleString()
|
|
@@ -1493,9 +1567,9 @@ function SharedNotes(props) {
|
|
|
1493
1567
|
});
|
|
1494
1568
|
const path = `/api/admin/network/shared-notes?${query}`;
|
|
1495
1569
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1496
|
-
const [body, setBody] =
|
|
1497
|
-
const [busy, setBusy] =
|
|
1498
|
-
const [error, setError] =
|
|
1570
|
+
const [body, setBody] = useState15("");
|
|
1571
|
+
const [busy, setBusy] = useState15(false);
|
|
1572
|
+
const [error, setError] = useState15(null);
|
|
1499
1573
|
const submit = async () => {
|
|
1500
1574
|
const trimmed = body.trim();
|
|
1501
1575
|
if (!trimmed) return;
|
|
@@ -1514,18 +1588,18 @@ function SharedNotes(props) {
|
|
|
1514
1588
|
setBusy(false);
|
|
1515
1589
|
}
|
|
1516
1590
|
};
|
|
1517
|
-
return /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1591
|
+
return /* @__PURE__ */ jsxs17("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1592
|
+
/* @__PURE__ */ jsxs17("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1519
1593
|
"Shared with ",
|
|
1520
1594
|
targetName
|
|
1521
1595
|
] }),
|
|
1522
|
-
/* @__PURE__ */
|
|
1596
|
+
/* @__PURE__ */ jsx19(
|
|
1523
1597
|
Field5,
|
|
1524
1598
|
{
|
|
1525
1599
|
label: "Add a shared note",
|
|
1526
1600
|
hint: `This is written to the record's Notes feed for ${targetName} admins.`,
|
|
1527
1601
|
error,
|
|
1528
|
-
children: /* @__PURE__ */
|
|
1602
|
+
children: /* @__PURE__ */ jsx19(
|
|
1529
1603
|
Textarea2,
|
|
1530
1604
|
{
|
|
1531
1605
|
rows: 4,
|
|
@@ -1536,13 +1610,13 @@ function SharedNotes(props) {
|
|
|
1536
1610
|
)
|
|
1537
1611
|
}
|
|
1538
1612
|
),
|
|
1539
|
-
/* @__PURE__ */
|
|
1540
|
-
notes.loading ? /* @__PURE__ */
|
|
1541
|
-
notes.error ? /* @__PURE__ */
|
|
1542
|
-
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */
|
|
1543
|
-
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1613
|
+
/* @__PURE__ */ jsx19("div", { children: /* @__PURE__ */ jsx19(Button10, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Sharing\u2026" : `Share with ${targetName}` }) }),
|
|
1614
|
+
notes.loading ? /* @__PURE__ */ jsx19("p", { className: "muted", children: "Loading shared notes\u2026" }) : null,
|
|
1615
|
+
notes.error ? /* @__PURE__ */ jsx19("p", { role: "alert", children: notes.error }) : null,
|
|
1616
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx19("p", { className: "muted", children: "No shared notes yet." }) : null,
|
|
1617
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs17("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1618
|
+
/* @__PURE__ */ jsx19("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1619
|
+
/* @__PURE__ */ jsxs17("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1546
1620
|
"Shared with ",
|
|
1547
1621
|
targetName,
|
|
1548
1622
|
" \xB7 ",
|
|
@@ -1553,23 +1627,23 @@ function SharedNotes(props) {
|
|
|
1553
1627
|
}
|
|
1554
1628
|
function NetworkRecordDetail(props) {
|
|
1555
1629
|
const { ctx, targetId, targetName, leaderName, sharedEnabled, record } = props;
|
|
1556
|
-
return /* @__PURE__ */
|
|
1557
|
-
/* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
record.stage ? /* @__PURE__ */
|
|
1630
|
+
return /* @__PURE__ */ jsxs17("div", { style: { display: "grid", gap: 14 }, children: [
|
|
1631
|
+
/* @__PURE__ */ jsxs17("header", { children: [
|
|
1632
|
+
/* @__PURE__ */ jsx19("h2", { style: { margin: 0 }, children: record.name }),
|
|
1633
|
+
record.stage ? /* @__PURE__ */ jsx19("span", { className: "badge", style: { marginTop: 8 }, children: record.stage.replaceAll("_", " ") }) : null
|
|
1560
1634
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1564
|
-
/* @__PURE__ */
|
|
1635
|
+
/* @__PURE__ */ jsxs17("dl", { style: { display: "grid", gridTemplateColumns: "minmax(8rem, auto) 1fr", gap: "8px 16px", margin: 0 }, children: [
|
|
1636
|
+
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */ jsxs17("div", { style: { display: "contents" }, children: [
|
|
1637
|
+
/* @__PURE__ */ jsx19("dt", { className: "muted", children: name.replaceAll("_", " ") }),
|
|
1638
|
+
/* @__PURE__ */ jsx19("dd", { style: { margin: 0, overflowWrap: "anywhere" }, children: displayValue(value) })
|
|
1565
1639
|
] }, name)),
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1640
|
+
/* @__PURE__ */ jsxs17("div", { style: { display: "contents" }, children: [
|
|
1641
|
+
/* @__PURE__ */ jsx19("dt", { className: "muted", children: "Updated" }),
|
|
1642
|
+
/* @__PURE__ */ jsx19("dd", { style: { margin: 0 }, children: new Date(record.updatedAt).toLocaleString() })
|
|
1569
1643
|
] })
|
|
1570
1644
|
] }),
|
|
1571
|
-
/* @__PURE__ */
|
|
1572
|
-
/* @__PURE__ */
|
|
1645
|
+
/* @__PURE__ */ jsxs17("div", { style: { display: "grid", gap: 22 }, children: [
|
|
1646
|
+
/* @__PURE__ */ jsx19(
|
|
1573
1647
|
PrivateNotes,
|
|
1574
1648
|
{
|
|
1575
1649
|
ctx,
|
|
@@ -1578,7 +1652,7 @@ function NetworkRecordDetail(props) {
|
|
|
1578
1652
|
leaderName
|
|
1579
1653
|
}
|
|
1580
1654
|
),
|
|
1581
|
-
sharedEnabled ? /* @__PURE__ */
|
|
1655
|
+
sharedEnabled ? /* @__PURE__ */ jsx19(
|
|
1582
1656
|
SharedNotes,
|
|
1583
1657
|
{
|
|
1584
1658
|
ctx,
|
|
@@ -1592,11 +1666,11 @@ function NetworkRecordDetail(props) {
|
|
|
1592
1666
|
}
|
|
1593
1667
|
|
|
1594
1668
|
// src/ui/admin-network-records.tsx
|
|
1595
|
-
import { jsx as
|
|
1669
|
+
import { jsx as jsx20, jsxs as jsxs18 } from "preact/jsx-runtime";
|
|
1596
1670
|
function RecordsExplorer(props) {
|
|
1597
1671
|
const { ctx, target, type, leaderName } = props;
|
|
1598
|
-
const [offset, setOffset] =
|
|
1599
|
-
const [search, setSearch] =
|
|
1672
|
+
const [offset, setOffset] = useState16(0);
|
|
1673
|
+
const [search, setSearch] = useState16("");
|
|
1600
1674
|
const query = new URLSearchParams({
|
|
1601
1675
|
targetId: target.id,
|
|
1602
1676
|
type,
|
|
@@ -1609,16 +1683,16 @@ function RecordsExplorer(props) {
|
|
|
1609
1683
|
`/api/admin/network/records?${query}`
|
|
1610
1684
|
);
|
|
1611
1685
|
const selected = page.data?.records.find((record) => record.id === ctx.route.recordId);
|
|
1612
|
-
const
|
|
1686
|
+
const columns3 = useMemo(() => [
|
|
1613
1687
|
{
|
|
1614
1688
|
key: "name",
|
|
1615
1689
|
header: "Name",
|
|
1616
|
-
cell: (record) => /* @__PURE__ */
|
|
1690
|
+
cell: (record) => /* @__PURE__ */ jsx20("a", { href: ctx.href({ recordId: record.id, detailTab: null }), children: record.name })
|
|
1617
1691
|
},
|
|
1618
1692
|
{
|
|
1619
1693
|
key: "stage",
|
|
1620
1694
|
header: "Stage",
|
|
1621
|
-
cell: (record) => record.stage ? /* @__PURE__ */
|
|
1695
|
+
cell: (record) => record.stage ? /* @__PURE__ */ jsx20("span", { className: "badge", children: record.stage.replaceAll("_", " ") }) : "\u2014"
|
|
1622
1696
|
},
|
|
1623
1697
|
{
|
|
1624
1698
|
key: "updatedAt",
|
|
@@ -1626,7 +1700,7 @@ function RecordsExplorer(props) {
|
|
|
1626
1700
|
cell: (record) => new Date(record.updatedAt).toLocaleDateString()
|
|
1627
1701
|
}
|
|
1628
1702
|
], [ctx]);
|
|
1629
|
-
return /* @__PURE__ */
|
|
1703
|
+
return /* @__PURE__ */ jsx20(
|
|
1630
1704
|
MasterDetail,
|
|
1631
1705
|
{
|
|
1632
1706
|
collapseClosedDetail: true,
|
|
@@ -1634,13 +1708,13 @@ function RecordsExplorer(props) {
|
|
|
1634
1708
|
detailLabel: "Follower record details, private notes, and shared notes",
|
|
1635
1709
|
detailOpen: Boolean(selected),
|
|
1636
1710
|
closeHref: ctx.href({ recordId: null, detailTab: null }),
|
|
1637
|
-
master: /* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1711
|
+
master: /* @__PURE__ */ jsxs18("div", { style: { display: "grid", gap: 14, padding: "var(--ui-space-4)" }, children: [
|
|
1712
|
+
/* @__PURE__ */ jsxs18("h2", { style: { margin: 0, fontSize: 18 }, children: [
|
|
1639
1713
|
target.name,
|
|
1640
1714
|
" \xB7 ",
|
|
1641
1715
|
type.replaceAll("_", " ")
|
|
1642
1716
|
] }),
|
|
1643
|
-
/* @__PURE__ */
|
|
1717
|
+
/* @__PURE__ */ jsx20(
|
|
1644
1718
|
DataTable2,
|
|
1645
1719
|
{
|
|
1646
1720
|
manual: true,
|
|
@@ -1653,14 +1727,14 @@ function RecordsExplorer(props) {
|
|
|
1653
1727
|
ctx.navigate({ recordId: null, detailTab: null });
|
|
1654
1728
|
},
|
|
1655
1729
|
rows: page.data?.records ?? [],
|
|
1656
|
-
columns:
|
|
1730
|
+
columns: columns3,
|
|
1657
1731
|
rowKey: (record) => record.id,
|
|
1658
1732
|
loading: page.loading,
|
|
1659
1733
|
emptyState: page.error ? `Could not load records: ${page.error}` : "No records found.",
|
|
1660
1734
|
ariaLabel: `${target.name} ${type} records`
|
|
1661
1735
|
}
|
|
1662
1736
|
),
|
|
1663
|
-
/* @__PURE__ */
|
|
1737
|
+
/* @__PURE__ */ jsx20(
|
|
1664
1738
|
Pagination,
|
|
1665
1739
|
{
|
|
1666
1740
|
offset: page.data?.offset ?? offset,
|
|
@@ -1675,7 +1749,7 @@ function RecordsExplorer(props) {
|
|
|
1675
1749
|
}
|
|
1676
1750
|
)
|
|
1677
1751
|
] }),
|
|
1678
|
-
detail: selected ? /* @__PURE__ */
|
|
1752
|
+
detail: selected ? /* @__PURE__ */ jsx20(
|
|
1679
1753
|
NetworkRecordDetail,
|
|
1680
1754
|
{
|
|
1681
1755
|
ctx,
|
|
@@ -1692,8 +1766,8 @@ function RecordsExplorer(props) {
|
|
|
1692
1766
|
function NetworkRecordsWorkspace(props) {
|
|
1693
1767
|
const { chapter, ctx } = props;
|
|
1694
1768
|
const targets = useAdminResource(ctx.getToken, "/api/admin/network/targets");
|
|
1695
|
-
const [targetId, setTargetId] =
|
|
1696
|
-
const [type, setType] =
|
|
1769
|
+
const [targetId, setTargetId] = useState16("");
|
|
1770
|
+
const [type, setType] = useState16("");
|
|
1697
1771
|
const target = targets.data?.targets.find((candidate) => candidate.id === targetId) ?? targets.data?.targets[0];
|
|
1698
1772
|
useEffect7(() => {
|
|
1699
1773
|
if (target && target.id !== targetId) setTargetId(target.id);
|
|
@@ -1702,16 +1776,16 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1702
1776
|
const first = target?.types[0] ?? "";
|
|
1703
1777
|
if (target && !target.types.includes(type)) setType(first);
|
|
1704
1778
|
}, [target, type]);
|
|
1705
|
-
if (targets.loading) return /* @__PURE__ */
|
|
1706
|
-
if (targets.error || !targets.data) return /* @__PURE__ */
|
|
1779
|
+
if (targets.loading) return /* @__PURE__ */ jsx20(AdminNote, { children: "Loading follower records\u2026" });
|
|
1780
|
+
if (targets.error || !targets.data) return /* @__PURE__ */ jsxs18(AdminNote, { children: [
|
|
1707
1781
|
"Could not load followers",
|
|
1708
1782
|
targets.error ? `: ${targets.error}` : "",
|
|
1709
1783
|
"."
|
|
1710
1784
|
] });
|
|
1711
|
-
if (!target) return /* @__PURE__ */
|
|
1712
|
-
return /* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1785
|
+
if (!target) return /* @__PURE__ */ jsx20(AdminPage, { children: /* @__PURE__ */ jsx20(Panel, { title: "Follower records", children: /* @__PURE__ */ jsx20("p", { className: "muted", children: "No followers are configured." }) }) });
|
|
1786
|
+
return /* @__PURE__ */ jsxs18(AdminPage, { children: [
|
|
1787
|
+
/* @__PURE__ */ jsxs18("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "end" }, children: [
|
|
1788
|
+
/* @__PURE__ */ jsx20(Field6, { label: "Chapter", style: { minWidth: 240 }, children: /* @__PURE__ */ jsx20(
|
|
1715
1789
|
Select5,
|
|
1716
1790
|
{
|
|
1717
1791
|
value: target.id,
|
|
@@ -1723,7 +1797,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1723
1797
|
}
|
|
1724
1798
|
}
|
|
1725
1799
|
) }),
|
|
1726
|
-
/* @__PURE__ */
|
|
1800
|
+
/* @__PURE__ */ jsx20(Field6, { label: "Record type", style: { minWidth: 180 }, children: /* @__PURE__ */ jsx20(
|
|
1727
1801
|
Select5,
|
|
1728
1802
|
{
|
|
1729
1803
|
value: type,
|
|
@@ -1735,7 +1809,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1735
1809
|
}
|
|
1736
1810
|
) })
|
|
1737
1811
|
] }),
|
|
1738
|
-
type ? /* @__PURE__ */
|
|
1812
|
+
type ? /* @__PURE__ */ jsx20(
|
|
1739
1813
|
RecordsExplorer,
|
|
1740
1814
|
{
|
|
1741
1815
|
ctx,
|
|
@@ -1744,7 +1818,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1744
1818
|
leaderName: chapter.name
|
|
1745
1819
|
},
|
|
1746
1820
|
`${target.id}:${type}`
|
|
1747
|
-
) : /* @__PURE__ */
|
|
1821
|
+
) : /* @__PURE__ */ jsx20(Panel, { children: /* @__PURE__ */ jsx20("p", { className: "muted", children: "This follower has no browsable record types configured." }) })
|
|
1748
1822
|
] });
|
|
1749
1823
|
}
|
|
1750
1824
|
function networkRecordsWorkspace(chapter) {
|
|
@@ -1752,63 +1826,63 @@ function networkRecordsWorkspace(chapter) {
|
|
|
1752
1826
|
id: "network",
|
|
1753
1827
|
label: chapter.copy.admin.workspaces.network,
|
|
1754
1828
|
defaultViewId: "records",
|
|
1755
|
-
render: (ctx) => /* @__PURE__ */
|
|
1829
|
+
render: (ctx) => /* @__PURE__ */ jsx20(NetworkRecordsWorkspace, { chapter, ctx })
|
|
1756
1830
|
};
|
|
1757
1831
|
}
|
|
1758
1832
|
|
|
1759
1833
|
// src/ui/admin-portfolio.tsx
|
|
1760
1834
|
import { StatBand as StatBand2, StepPipeline as StepPipeline2 } from "@odla-ai/ui/components";
|
|
1761
|
-
import { jsx as
|
|
1835
|
+
import { jsx as jsx21, jsxs as jsxs19 } from "preact/jsx-runtime";
|
|
1762
1836
|
function LeaderNetworkOverview({ ctx }) {
|
|
1763
1837
|
const copy = useChapterCopy().admin.network;
|
|
1764
1838
|
const { data, loading, error } = useAdminResource(
|
|
1765
1839
|
ctx.getToken,
|
|
1766
1840
|
"/api/admin/network/rollup"
|
|
1767
1841
|
);
|
|
1768
|
-
if (loading) return /* @__PURE__ */
|
|
1769
|
-
if (error || !data) return /* @__PURE__ */
|
|
1842
|
+
if (loading) return /* @__PURE__ */ jsx21(AdminNote, { children: copy.loadingRollup });
|
|
1843
|
+
if (error || !data) return /* @__PURE__ */ jsxs19(AdminNote, { children: [
|
|
1770
1844
|
copy.loadRollupFailed,
|
|
1771
1845
|
error ? `: ${error}` : "",
|
|
1772
1846
|
"."
|
|
1773
1847
|
] });
|
|
1774
|
-
if (data.configured === 0) return /* @__PURE__ */
|
|
1848
|
+
if (data.configured === 0) return /* @__PURE__ */ jsx21(AdminPage, { children: /* @__PURE__ */ jsx21(Panel, { title: copy.overview, children: /* @__PURE__ */ jsx21("p", { className: "muted", children: copy.noFollowers }) }) });
|
|
1775
1849
|
const stats = [
|
|
1776
1850
|
{ value: data.configured.toLocaleString(), label: copy.configuredFollowers },
|
|
1777
1851
|
{ value: data.available.toLocaleString(), label: copy.reachableFollowers },
|
|
1778
1852
|
{ value: data.totalRecords.toLocaleString(), label: copy.recordsAcrossFollowers }
|
|
1779
1853
|
];
|
|
1780
1854
|
const steps = data.types.map((type) => ({
|
|
1781
|
-
icon: /* @__PURE__ */
|
|
1855
|
+
icon: /* @__PURE__ */ jsx21("span", { style: { fontSize: 22, fontWeight: 700 }, children: type.total }),
|
|
1782
1856
|
title: type.type.replaceAll("_", " "),
|
|
1783
1857
|
description: Object.entries(type.stages).filter(([, count]) => count > 0).map(([stage, count]) => `${stage.replaceAll("_", " ")} ${count}`).join(" \xB7 ") || copy.noRecordTotals
|
|
1784
1858
|
}));
|
|
1785
|
-
return /* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1793
|
-
target.error ? /* @__PURE__ */
|
|
1794
|
-
/* @__PURE__ */
|
|
1859
|
+
return /* @__PURE__ */ jsxs19(AdminPage, { children: [
|
|
1860
|
+
/* @__PURE__ */ jsx21(StatBand2, { stats }),
|
|
1861
|
+
/* @__PURE__ */ jsx21(Panel, { title: copy.followers, children: /* @__PURE__ */ jsx21("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.targets.map((target) => /* @__PURE__ */ jsxs19("li", { style: { display: "flex", gap: 12, alignItems: "baseline", flexWrap: "wrap" }, children: [
|
|
1862
|
+
/* @__PURE__ */ jsx21("strong", { style: { minWidth: 180 }, children: target.name }),
|
|
1863
|
+
/* @__PURE__ */ jsx21("span", { className: "badge", children: target.transport.reachable === null ? copy.transportNotChecked : target.transport.reachable ? copy.transportReachable : copy.transportUnavailable }),
|
|
1864
|
+
/* @__PURE__ */ jsx21("span", { className: "muted", children: target.transport.kind === "binding" ? formatChapterCopy(copy.bindingTransport, { binding: target.transport.binding ?? "" }) : copy.urlTransport }),
|
|
1865
|
+
/* @__PURE__ */ jsx21("span", { className: "muted", children: target.configuration.activeRevision === null ? copy.noActiveConfiguration : formatChapterCopy(copy.activeConfiguration, { revision: target.configuration.activeRevision }) }),
|
|
1866
|
+
/* @__PURE__ */ jsx21("span", { className: "muted", children: target.publicSite.available === null ? copy.publicSiteNotChecked : target.publicSite.available ? copy.publicSiteAvailable : copy.publicSiteUnavailable }),
|
|
1867
|
+
target.error ? /* @__PURE__ */ jsx21("span", { className: "muted", children: target.error }) : null,
|
|
1868
|
+
/* @__PURE__ */ jsx21("a", { href: target.url, target: "_blank", rel: "noreferrer", style: { marginLeft: "auto" }, children: target.url })
|
|
1795
1869
|
] }, target.id)) }) }),
|
|
1796
|
-
/* @__PURE__ */
|
|
1870
|
+
/* @__PURE__ */ jsx21(Panel, { title: copy.recordTotals, children: steps.length ? /* @__PURE__ */ jsx21(StepPipeline2, { ariaLabel: copy.recordTotals, steps }) : /* @__PURE__ */ jsx21("p", { className: "muted", children: copy.noRecordTotals }) })
|
|
1797
1871
|
] });
|
|
1798
1872
|
}
|
|
1799
1873
|
|
|
1800
1874
|
// src/ui/admin-admission.tsx
|
|
1801
|
-
import { useEffect as useEffect8, useRef as useRef2, useState as
|
|
1802
|
-
import { jsx as
|
|
1875
|
+
import { useEffect as useEffect8, useRef as useRef2, useState as useState17 } from "preact/hooks";
|
|
1876
|
+
import { jsx as jsx22, jsxs as jsxs20 } from "preact/jsx-runtime";
|
|
1803
1877
|
function AdmissionGrantsWorkspace(props) {
|
|
1804
1878
|
const copy = useChapterCopy().admin.admission;
|
|
1805
1879
|
const resource = useAdminResource(props.getToken, "/api/admin/admission-grants");
|
|
1806
|
-
const [email, setEmail] =
|
|
1807
|
-
const [decision, setDecision] =
|
|
1808
|
-
const [note, setNote] =
|
|
1809
|
-
const [expires, setExpires] =
|
|
1810
|
-
const [busy, setBusy] =
|
|
1811
|
-
const [error, setError] =
|
|
1880
|
+
const [email, setEmail] = useState17("");
|
|
1881
|
+
const [decision, setDecision] = useState17("waive_interview");
|
|
1882
|
+
const [note, setNote] = useState17("");
|
|
1883
|
+
const [expires, setExpires] = useState17("");
|
|
1884
|
+
const [busy, setBusy] = useState17(false);
|
|
1885
|
+
const [error, setError] = useState17(null);
|
|
1812
1886
|
const submit = async (event) => {
|
|
1813
1887
|
event.preventDefault();
|
|
1814
1888
|
setBusy(true);
|
|
@@ -1845,48 +1919,48 @@ function AdmissionGrantsWorkspace(props) {
|
|
|
1845
1919
|
setBusy(false);
|
|
1846
1920
|
}
|
|
1847
1921
|
};
|
|
1848
|
-
return /* @__PURE__ */
|
|
1849
|
-
/* @__PURE__ */
|
|
1850
|
-
/* @__PURE__ */
|
|
1851
|
-
/* @__PURE__ */
|
|
1852
|
-
/* @__PURE__ */
|
|
1922
|
+
return /* @__PURE__ */ jsxs20(AdminPage, { children: [
|
|
1923
|
+
/* @__PURE__ */ jsxs20(Panel, { title: copy.title, children: [
|
|
1924
|
+
/* @__PURE__ */ jsx22("p", { className: "muted", style: { margin: 0 }, children: copy.description }),
|
|
1925
|
+
/* @__PURE__ */ jsxs20("form", { onSubmit: (event) => void submit(event), style: { display: "grid", gap: 12, gridTemplateColumns: "minmax(220px,2fr) minmax(180px,1fr) minmax(160px,1fr)" }, children: [
|
|
1926
|
+
/* @__PURE__ */ jsxs20("label", { children: [
|
|
1853
1927
|
copy.email,
|
|
1854
|
-
/* @__PURE__ */
|
|
1928
|
+
/* @__PURE__ */ jsx22("input", { required: true, type: "email", value: email, onInput: (event) => setEmail(event.currentTarget.value) })
|
|
1855
1929
|
] }),
|
|
1856
|
-
/* @__PURE__ */
|
|
1930
|
+
/* @__PURE__ */ jsxs20("label", { children: [
|
|
1857
1931
|
copy.decision,
|
|
1858
|
-
/* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
1860
|
-
/* @__PURE__ */
|
|
1932
|
+
/* @__PURE__ */ jsxs20("select", { value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
|
|
1933
|
+
/* @__PURE__ */ jsx22("option", { value: "waive_interview", children: copy.waive }),
|
|
1934
|
+
/* @__PURE__ */ jsx22("option", { value: "require_interview", children: copy.require })
|
|
1861
1935
|
] })
|
|
1862
1936
|
] }),
|
|
1863
|
-
/* @__PURE__ */
|
|
1937
|
+
/* @__PURE__ */ jsxs20("label", { children: [
|
|
1864
1938
|
copy.expires,
|
|
1865
|
-
/* @__PURE__ */
|
|
1939
|
+
/* @__PURE__ */ jsx22("input", { type: "date", value: expires, onInput: (event) => setExpires(event.currentTarget.value) })
|
|
1866
1940
|
] }),
|
|
1867
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsxs20("label", { style: { gridColumn: "1 / -1" }, children: [
|
|
1868
1942
|
copy.note,
|
|
1869
|
-
/* @__PURE__ */
|
|
1943
|
+
/* @__PURE__ */ jsx22("input", { value: note, maxLength: 2e3, onInput: (event) => setNote(event.currentTarget.value) })
|
|
1870
1944
|
] }),
|
|
1871
|
-
/* @__PURE__ */
|
|
1945
|
+
/* @__PURE__ */ jsx22("button", { className: "btn secondary", type: "submit", disabled: busy, children: copy.add })
|
|
1872
1946
|
] }),
|
|
1873
|
-
error ? /* @__PURE__ */
|
|
1947
|
+
error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: error }) : null
|
|
1874
1948
|
] }),
|
|
1875
|
-
/* @__PURE__ */
|
|
1876
|
-
resource.loading ? /* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1949
|
+
/* @__PURE__ */ jsxs20(Panel, { title: copy.ledger, children: [
|
|
1950
|
+
resource.loading ? /* @__PURE__ */ jsx22("p", { className: "muted", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: resource.error }) : null,
|
|
1951
|
+
/* @__PURE__ */ jsx22("div", { style: { display: "grid", gap: 10 }, children: (resource.data?.grants ?? []).map((grant) => {
|
|
1878
1952
|
const state = grant.consumedAt ? copy.consumed : grant.revokedAt || !grant.active ? copy.revoked : grant.expiresAt && grant.expiresAt <= Date.now() ? copy.expired : copy.active;
|
|
1879
|
-
return /* @__PURE__ */
|
|
1880
|
-
/* @__PURE__ */
|
|
1953
|
+
return /* @__PURE__ */ jsxs20("article", { className: "card", style: { padding: 14 }, children: [
|
|
1954
|
+
/* @__PURE__ */ jsx22("strong", { children: grant.email }),
|
|
1881
1955
|
" \xB7 ",
|
|
1882
1956
|
grant.decision === "waive_interview" ? copy.waive : copy.require,
|
|
1883
|
-
/* @__PURE__ */
|
|
1957
|
+
/* @__PURE__ */ jsxs20("p", { className: "muted", style: { margin: "6px 0" }, children: [
|
|
1884
1958
|
grant.sourceScope === "local" ? copy.local : copy.parent,
|
|
1885
1959
|
" \xB7 ",
|
|
1886
1960
|
state,
|
|
1887
1961
|
grant.note ? ` \xB7 ${grant.note}` : ""
|
|
1888
1962
|
] }),
|
|
1889
|
-
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */
|
|
1963
|
+
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */ jsx22("button", { className: "btn secondary mini", disabled: busy, onClick: () => void revoke(grant), children: copy.revoke }) : null
|
|
1890
1964
|
] }, grant.id);
|
|
1891
1965
|
}) })
|
|
1892
1966
|
] })
|
|
@@ -1897,19 +1971,19 @@ function admissionGrantsWorkspace(label = "Admission") {
|
|
|
1897
1971
|
id: "admission",
|
|
1898
1972
|
label,
|
|
1899
1973
|
defaultViewId: "grants",
|
|
1900
|
-
render: (ctx) => /* @__PURE__ */
|
|
1974
|
+
render: (ctx) => /* @__PURE__ */ jsx22(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
|
|
1901
1975
|
};
|
|
1902
1976
|
}
|
|
1903
1977
|
function LeaderAdmissionAction(props) {
|
|
1904
|
-
return /* @__PURE__ */
|
|
1978
|
+
return /* @__PURE__ */ jsx22(LeaderAdmissionForm, { ...props }, JSON.stringify([props.targetId, props.personId, props.sourceRecordId]));
|
|
1905
1979
|
}
|
|
1906
1980
|
function LeaderAdmissionForm(props) {
|
|
1907
|
-
const [decision, setDecision] =
|
|
1908
|
-
const [note, setNote] =
|
|
1909
|
-
const [loaded, setLoaded] =
|
|
1910
|
-
const [busy, setBusy] =
|
|
1911
|
-
const [error, setError] =
|
|
1912
|
-
const [status, setStatus] =
|
|
1981
|
+
const [decision, setDecision] = useState17("");
|
|
1982
|
+
const [note, setNote] = useState17("");
|
|
1983
|
+
const [loaded, setLoaded] = useState17(false);
|
|
1984
|
+
const [busy, setBusy] = useState17(false);
|
|
1985
|
+
const [error, setError] = useState17(null);
|
|
1986
|
+
const [status, setStatus] = useState17(null);
|
|
1913
1987
|
const attempt = useRef2(null);
|
|
1914
1988
|
useEffect8(() => {
|
|
1915
1989
|
let live = true;
|
|
@@ -1954,22 +2028,22 @@ function LeaderAdmissionForm(props) {
|
|
|
1954
2028
|
setBusy(false);
|
|
1955
2029
|
}
|
|
1956
2030
|
};
|
|
1957
|
-
return /* @__PURE__ */
|
|
1958
|
-
/* @__PURE__ */
|
|
2031
|
+
return /* @__PURE__ */ jsxs20("div", { style: { display: "grid", gap: 8 }, children: [
|
|
2032
|
+
/* @__PURE__ */ jsxs20("label", { children: [
|
|
1959
2033
|
"Interview",
|
|
1960
|
-
/* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
2034
|
+
/* @__PURE__ */ jsxs20("select", { disabled: !loaded || busy, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
|
|
2035
|
+
/* @__PURE__ */ jsx22("option", { value: "", children: loaded ? "Choose an invitation setting" : "Loading saved setting\u2026" }),
|
|
2036
|
+
/* @__PURE__ */ jsx22("option", { value: "waive_interview", children: "Already completed" }),
|
|
2037
|
+
/* @__PURE__ */ jsx22("option", { value: "require_interview", children: "Required" })
|
|
1964
2038
|
] })
|
|
1965
2039
|
] }),
|
|
1966
|
-
/* @__PURE__ */
|
|
2040
|
+
/* @__PURE__ */ jsxs20("label", { children: [
|
|
1967
2041
|
"Internal note",
|
|
1968
|
-
/* @__PURE__ */
|
|
2042
|
+
/* @__PURE__ */ jsx22("input", { disabled: !loaded || busy, maxLength: 2e3, value: note, onInput: (event) => setNote(event.currentTarget.value) })
|
|
1969
2043
|
] }),
|
|
1970
|
-
/* @__PURE__ */
|
|
1971
|
-
error ? /* @__PURE__ */
|
|
1972
|
-
status ? /* @__PURE__ */
|
|
2044
|
+
/* @__PURE__ */ jsx22("button", { className: "btn secondary", type: "button", disabled: busy || !loaded || !decision, onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save invitation setting" }),
|
|
2045
|
+
error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: error }) : null,
|
|
2046
|
+
status ? /* @__PURE__ */ jsx22("p", { role: "status", children: status }) : null
|
|
1973
2047
|
] });
|
|
1974
2048
|
}
|
|
1975
2049
|
|
|
@@ -1977,19 +2051,19 @@ function LeaderAdmissionForm(props) {
|
|
|
1977
2051
|
import { Tabs as Tabs2 } from "@odla-ai/ui/components";
|
|
1978
2052
|
|
|
1979
2053
|
// src/ui/admin-availability.tsx
|
|
1980
|
-
import { useState as
|
|
1981
|
-
import { Button as
|
|
1982
|
-
import { jsx as
|
|
2054
|
+
import { useState as useState18 } from "preact/hooks";
|
|
2055
|
+
import { Button as Button11, Checkbox, Field as Field7, Input as Input2 } from "@odla-ai/ui/components";
|
|
2056
|
+
import { jsx as jsx23, jsxs as jsxs21 } from "preact/jsx-runtime";
|
|
1983
2057
|
function AvailabilityBody({ ctx }) {
|
|
1984
2058
|
const copy = useChapterCopy().admin.availability;
|
|
1985
2059
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
|
|
1986
|
-
const [draft, setDraft] =
|
|
1987
|
-
const [busy, setBusy] =
|
|
1988
|
-
const [note, setNote] =
|
|
1989
|
-
const [errors, setErrors] =
|
|
2060
|
+
const [draft, setDraft] = useState18(null);
|
|
2061
|
+
const [busy, setBusy] = useState18(false);
|
|
2062
|
+
const [note, setNote] = useState18(null);
|
|
2063
|
+
const [errors, setErrors] = useState18({});
|
|
1990
2064
|
const cfg = draft ?? data?.scheduling ?? null;
|
|
1991
|
-
if (loading) return /* @__PURE__ */
|
|
1992
|
-
if (error || !cfg) return /* @__PURE__ */
|
|
2065
|
+
if (loading) return /* @__PURE__ */ jsx23(AdminNote, { children: copy.loading });
|
|
2066
|
+
if (error || !cfg) return /* @__PURE__ */ jsxs21(AdminNote, { children: [
|
|
1993
2067
|
copy.loadFailed,
|
|
1994
2068
|
error ? `: ${error}` : "",
|
|
1995
2069
|
"."
|
|
@@ -2019,29 +2093,29 @@ function AvailabilityBody({ ctx }) {
|
|
|
2019
2093
|
setBusy(false);
|
|
2020
2094
|
}
|
|
2021
2095
|
};
|
|
2022
|
-
const numField = (key, label) => /* @__PURE__ */
|
|
2023
|
-
return /* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2025
|
-
/* @__PURE__ */
|
|
2096
|
+
const numField = (key, label) => /* @__PURE__ */ jsx23(Field7, { label, error: errors[key], children: /* @__PURE__ */ jsx23(Input2, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
|
|
2097
|
+
return /* @__PURE__ */ jsx23(AdminPage, { children: /* @__PURE__ */ jsxs21(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx23("span", { className: "muted", children: note }) : void 0, children: [
|
|
2098
|
+
/* @__PURE__ */ jsx23(Field7, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx23("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx23(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
|
|
2099
|
+
/* @__PURE__ */ jsxs21("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
|
|
2026
2100
|
numField("startHour", copy.startHour),
|
|
2027
2101
|
numField("endHour", copy.endHour),
|
|
2028
2102
|
numField("slotMinutes", copy.slotMinutes),
|
|
2029
2103
|
numField("minNoticeHours", copy.minNoticeHours),
|
|
2030
2104
|
numField("windowDays", copy.windowDays),
|
|
2031
|
-
/* @__PURE__ */
|
|
2105
|
+
/* @__PURE__ */ jsx23(Field7, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx23(Input2, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
2032
2106
|
] }),
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
/* @__PURE__ */
|
|
2107
|
+
/* @__PURE__ */ jsx23(Field7, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx23(Input2, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
2108
|
+
/* @__PURE__ */ jsx23("div", { children: /* @__PURE__ */ jsx23(Button11, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
2035
2109
|
] }) });
|
|
2036
2110
|
}
|
|
2037
2111
|
function availabilitySection(options = {}) {
|
|
2038
2112
|
const { id = "availability", label = "Availability" } = options;
|
|
2039
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
2113
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx23(AvailabilityBody, { ctx }) };
|
|
2040
2114
|
}
|
|
2041
2115
|
|
|
2042
2116
|
// src/ui/admin-builder-library.tsx
|
|
2043
|
-
import { useEffect as useEffect9, useMemo as useMemo2, useState as
|
|
2044
|
-
import { jsx as
|
|
2117
|
+
import { useEffect as useEffect9, useMemo as useMemo2, useState as useState19 } from "preact/hooks";
|
|
2118
|
+
import { jsx as jsx24, jsxs as jsxs22 } from "preact/jsx-runtime";
|
|
2045
2119
|
var KINDS = ["document", "note", "link", "inspiration", "image", "file"];
|
|
2046
2120
|
var KIND_LABEL = { document: "Document", note: "Note", link: "Link", inspiration: "Inspiration", image: "Image", file: "File" };
|
|
2047
2121
|
var STATUS_LABEL = { draft: "Draft", published: "Published", archived: "Archived" };
|
|
@@ -2073,13 +2147,13 @@ function localResource(kind, chapterId, mode) {
|
|
|
2073
2147
|
};
|
|
2074
2148
|
}
|
|
2075
2149
|
function ResourcePreview({ item }) {
|
|
2076
|
-
return /* @__PURE__ */
|
|
2077
|
-
/* @__PURE__ */
|
|
2078
|
-
/* @__PURE__ */
|
|
2079
|
-
item.summary ? /* @__PURE__ */
|
|
2080
|
-
item.bodyMarkdown ? /* @__PURE__ */
|
|
2081
|
-
item.url ? /* @__PURE__ */
|
|
2082
|
-
item.brandAsset ? /* @__PURE__ */
|
|
2150
|
+
return /* @__PURE__ */ jsxs22("div", { className: "card", style: { padding: 14 }, children: [
|
|
2151
|
+
/* @__PURE__ */ jsx24("div", { className: "card-label", children: "Preview" }),
|
|
2152
|
+
/* @__PURE__ */ jsx24("h3", { style: { margin: "6px 0" }, children: item.title || "Untitled" }),
|
|
2153
|
+
item.summary ? /* @__PURE__ */ jsx24("p", { style: { margin: "0 0 10px" }, children: item.summary }) : null,
|
|
2154
|
+
item.bodyMarkdown ? /* @__PURE__ */ jsx24("pre", { style: { margin: 0, whiteSpace: "pre-wrap", font: "inherit", lineHeight: 1.55 }, children: item.bodyMarkdown }) : null,
|
|
2155
|
+
item.url ? /* @__PURE__ */ jsx24("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url }) : null,
|
|
2156
|
+
item.brandAsset ? /* @__PURE__ */ jsxs22("div", { style: META, children: [
|
|
2083
2157
|
"Brand asset \xB7 ",
|
|
2084
2158
|
item.brandAsset.bookId,
|
|
2085
2159
|
" / ",
|
|
@@ -2091,14 +2165,14 @@ function ResourcePreview({ item }) {
|
|
|
2091
2165
|
function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
2092
2166
|
const remote = useAdminResource(ctx.getToken, "/api/admin/builder-library");
|
|
2093
2167
|
const seed = initialData ?? remote.data;
|
|
2094
|
-
const [resources, setResources] =
|
|
2095
|
-
const [selectedKey, setSelectedKey] =
|
|
2096
|
-
const [query, setQuery] =
|
|
2097
|
-
const [kindFilter, setKindFilter] =
|
|
2098
|
-
const [statusFilter, setStatusFilter] =
|
|
2099
|
-
const [newKind, setNewKind] =
|
|
2100
|
-
const [saveError, setSaveError] =
|
|
2101
|
-
const [saving, setSaving] =
|
|
2168
|
+
const [resources, setResources] = useState19(() => [...seed?.local?.document.resources ?? []]);
|
|
2169
|
+
const [selectedKey, setSelectedKey] = useState19(() => seed?.local?.document.resources[0]?.id ? localKey(seed.local.document.resources[0].id) : null);
|
|
2170
|
+
const [query, setQuery] = useState19("");
|
|
2171
|
+
const [kindFilter, setKindFilter] = useState19("all");
|
|
2172
|
+
const [statusFilter, setStatusFilter] = useState19("all");
|
|
2173
|
+
const [newKind, setNewKind] = useState19("document");
|
|
2174
|
+
const [saveError, setSaveError] = useState19(null);
|
|
2175
|
+
const [saving, setSaving] = useState19(false);
|
|
2102
2176
|
useEffect9(() => {
|
|
2103
2177
|
if (!remote.data) return;
|
|
2104
2178
|
const next = [...remote.data.local?.document.resources ?? []];
|
|
@@ -2115,8 +2189,8 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
|
2115
2189
|
return (!query.trim() || haystack.includes(query.toLowerCase().trim())) && (kindFilter === "all" || resource.kind === kindFilter) && (statusFilter === "all" || resource.status === statusFilter);
|
|
2116
2190
|
});
|
|
2117
2191
|
const selected = entries.find((entry) => entry.key === selectedKey) ?? visible[0] ?? null;
|
|
2118
|
-
if (!data && remote.loading) return /* @__PURE__ */
|
|
2119
|
-
if (remote.error || !data) return /* @__PURE__ */
|
|
2192
|
+
if (!data && remote.loading) return /* @__PURE__ */ jsx24(AdminPage, { children: /* @__PURE__ */ jsx24(AdminNote, { children: "Loading Builder Library\u2026" }) });
|
|
2193
|
+
if (remote.error || !data) return /* @__PURE__ */ jsx24(AdminPage, { children: /* @__PURE__ */ jsxs22(AdminNote, { children: [
|
|
2120
2194
|
"Builder Library could not be loaded: ",
|
|
2121
2195
|
remote.error ?? "unknown error"
|
|
2122
2196
|
] }) });
|
|
@@ -2152,170 +2226,170 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
|
2152
2226
|
}
|
|
2153
2227
|
};
|
|
2154
2228
|
const scopeValue = selected?.resource.scope.kind === "chapter" ? `chapter:${selected.resource.scope.chapterId}` : selected?.resource.scope.kind ?? "built_not_found";
|
|
2155
|
-
return /* @__PURE__ */
|
|
2156
|
-
/* @__PURE__ */
|
|
2229
|
+
return /* @__PURE__ */ jsxs22(AdminPage, { children: [
|
|
2230
|
+
/* @__PURE__ */ jsxs22(Panel, { title: /* @__PURE__ */ jsxs22("span", { children: [
|
|
2157
2231
|
"Builder Library ",
|
|
2158
|
-
/* @__PURE__ */
|
|
2159
|
-
] }), actions: /* @__PURE__ */
|
|
2160
|
-
/* @__PURE__ */
|
|
2161
|
-
/* @__PURE__ */
|
|
2232
|
+
/* @__PURE__ */ jsx24("span", { className: "muted", children: "\xB7 private" })
|
|
2233
|
+
] }), actions: /* @__PURE__ */ jsxs22("div", { style: ROW, children: [
|
|
2234
|
+
/* @__PURE__ */ jsx24("select", { "aria-label": "New resource type", value: newKind, onChange: (event) => setNewKind(event.currentTarget.value), children: KINDS.map((kind) => /* @__PURE__ */ jsx24("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }),
|
|
2235
|
+
/* @__PURE__ */ jsx24("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring, onClick: add, children: "Add resource" })
|
|
2162
2236
|
] }), children: [
|
|
2163
|
-
/* @__PURE__ */
|
|
2164
|
-
/* @__PURE__ */
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
/* @__PURE__ */
|
|
2167
|
-
/* @__PURE__ */
|
|
2168
|
-
KINDS.map((kind) => /* @__PURE__ */
|
|
2237
|
+
/* @__PURE__ */ jsx24("p", { className: "muted", style: { margin: 0 }, children: "Documents, links, and Brand assets used by site-building agents. Nothing here appears on a member page." }),
|
|
2238
|
+
/* @__PURE__ */ jsxs22("div", { style: ROW, children: [
|
|
2239
|
+
/* @__PURE__ */ jsx24("input", { "aria-label": "Search resources", placeholder: "Search resources", value: query, onInput: (event) => setQuery(event.currentTarget.value), style: { flex: "1 1 220px" } }),
|
|
2240
|
+
/* @__PURE__ */ jsxs22("select", { "aria-label": "Filter by type", value: kindFilter, onChange: (event) => setKindFilter(event.currentTarget.value), children: [
|
|
2241
|
+
/* @__PURE__ */ jsx24("option", { value: "all", children: "All types" }),
|
|
2242
|
+
KINDS.map((kind) => /* @__PURE__ */ jsx24("option", { value: kind, children: KIND_LABEL[kind] }, kind))
|
|
2169
2243
|
] }),
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2172
|
-
/* @__PURE__ */
|
|
2173
|
-
/* @__PURE__ */
|
|
2174
|
-
/* @__PURE__ */
|
|
2244
|
+
/* @__PURE__ */ jsxs22("select", { "aria-label": "Filter by status", value: statusFilter, onChange: (event) => setStatusFilter(event.currentTarget.value), children: [
|
|
2245
|
+
/* @__PURE__ */ jsx24("option", { value: "all", children: "All statuses" }),
|
|
2246
|
+
/* @__PURE__ */ jsx24("option", { value: "draft", children: "Draft" }),
|
|
2247
|
+
/* @__PURE__ */ jsx24("option", { value: "published", children: "Published" }),
|
|
2248
|
+
/* @__PURE__ */ jsx24("option", { value: "archived", children: "Archived" })
|
|
2175
2249
|
] })
|
|
2176
2250
|
] })
|
|
2177
2251
|
] }),
|
|
2178
|
-
/* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2252
|
+
/* @__PURE__ */ jsxs22("div", { style: GRID, "data-builder-library-cms": true, children: [
|
|
2253
|
+
/* @__PURE__ */ jsx24(Panel, { title: `${visible.length} resource${visible.length === 1 ? "" : "s"}`, children: /* @__PURE__ */ jsx24("div", { style: { ...STACK, maxHeight: "70vh", overflow: "auto" }, children: visible.length === 0 ? /* @__PURE__ */ jsx24("p", { className: "muted", children: "No resources match these filters." }) : visible.map((entry) => /* @__PURE__ */ jsxs22("button", { type: "button", style: { ...ITEM, ...selected?.key === entry.key ? { borderColor: "var(--ui-accent)", boxShadow: "0 0 0 1px var(--ui-accent)" } : {} }, onClick: () => setSelectedKey(entry.key), children: [
|
|
2254
|
+
/* @__PURE__ */ jsxs22("div", { style: { ...ROW, justifyContent: "space-between" }, children: [
|
|
2255
|
+
/* @__PURE__ */ jsx24("strong", { children: entry.resource.title }),
|
|
2256
|
+
/* @__PURE__ */ jsx24("span", { className: "badge", children: KIND_LABEL[entry.resource.kind] })
|
|
2183
2257
|
] }),
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
/* @__PURE__ */
|
|
2190
|
-
/* @__PURE__ */
|
|
2258
|
+
/* @__PURE__ */ jsx24("p", { className: "muted", style: { margin: "6px 0", lineHeight: 1.4 }, children: contentSummary(entry.resource) }),
|
|
2259
|
+
/* @__PURE__ */ jsxs22("div", { style: META, children: [
|
|
2260
|
+
/* @__PURE__ */ jsx24("span", { children: STATUS_LABEL[entry.resource.status] }),
|
|
2261
|
+
/* @__PURE__ */ jsx24("span", { children: "\xB7" }),
|
|
2262
|
+
/* @__PURE__ */ jsx24("span", { children: scopeLabel(entry.resource.scope) }),
|
|
2263
|
+
/* @__PURE__ */ jsx24("span", { children: "\xB7" }),
|
|
2264
|
+
/* @__PURE__ */ jsx24("span", { children: entry.sourceId })
|
|
2191
2265
|
] })
|
|
2192
2266
|
] }, entry.key)) }) }),
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2195
|
-
/* @__PURE__ */
|
|
2267
|
+
/* @__PURE__ */ jsxs22(Panel, { title: selected ? selected.resource.title : "Select a resource", actions: selected?.local ? /* @__PURE__ */ jsxs22("div", { style: ROW, children: [
|
|
2268
|
+
/* @__PURE__ */ jsx24("button", { className: "btn secondary", type: "button", disabled: saving, onClick: () => update({ status: selected.resource.status === "archived" ? "draft" : "archived" }), children: selected.resource.status === "archived" ? "Restore draft" : "Archive" }),
|
|
2269
|
+
/* @__PURE__ */ jsx24("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save changes" })
|
|
2196
2270
|
] }) : null, children: [
|
|
2197
|
-
!selected ? /* @__PURE__ */
|
|
2198
|
-
/* @__PURE__ */
|
|
2271
|
+
!selected ? /* @__PURE__ */ jsx24("p", { className: "muted", children: "Add a resource or choose one from the list." }) : selected.local ? /* @__PURE__ */ jsx24(LocalEditor, { selected: selected.resource, data, scopeValue, saving, update, setResources, setSelectedKey }) : /* @__PURE__ */ jsxs22("div", { style: STACK, children: [
|
|
2272
|
+
/* @__PURE__ */ jsxs22("div", { style: META, children: [
|
|
2199
2273
|
"Read only \xB7 supplied by ",
|
|
2200
2274
|
selected.sourceId,
|
|
2201
2275
|
" \xB7 source revision ",
|
|
2202
2276
|
selected.sourceRevision
|
|
2203
2277
|
] }),
|
|
2204
|
-
/* @__PURE__ */
|
|
2205
|
-
/* @__PURE__ */
|
|
2278
|
+
/* @__PURE__ */ jsxs22("div", { children: [
|
|
2279
|
+
/* @__PURE__ */ jsx24("span", { className: "badge", children: STATUS_LABEL[selected.resource.status] }),
|
|
2206
2280
|
" ",
|
|
2207
|
-
/* @__PURE__ */
|
|
2281
|
+
/* @__PURE__ */ jsx24("span", { className: "badge", children: scopeLabel(selected.resource.scope) })
|
|
2208
2282
|
] }),
|
|
2209
|
-
/* @__PURE__ */
|
|
2210
|
-
data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */
|
|
2283
|
+
/* @__PURE__ */ jsx24(ResourcePreview, { item: selected.resource }),
|
|
2284
|
+
data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */ jsx24("button", { className: "btn secondary", type: "button", onClick: () => void promote(selected), children: "Copy to all chapters" }) : null
|
|
2211
2285
|
] }),
|
|
2212
|
-
saveError ? /* @__PURE__ */
|
|
2213
|
-
/* @__PURE__ */
|
|
2214
|
-
/* @__PURE__ */
|
|
2286
|
+
saveError ? /* @__PURE__ */ jsxs22("div", { className: "notice danger", role: "alert", children: [
|
|
2287
|
+
/* @__PURE__ */ jsx24("strong", { children: "Fix these items before saving:" }),
|
|
2288
|
+
/* @__PURE__ */ jsx24("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx24("li", { children: error }, error)) })
|
|
2215
2289
|
] }) : null
|
|
2216
2290
|
] })
|
|
2217
2291
|
] })
|
|
2218
2292
|
] });
|
|
2219
2293
|
}
|
|
2220
2294
|
function LocalEditor({ selected, data, scopeValue, update, setResources, setSelectedKey }) {
|
|
2221
|
-
return /* @__PURE__ */
|
|
2222
|
-
/* @__PURE__ */
|
|
2295
|
+
return /* @__PURE__ */ jsxs22("div", { style: STACK, children: [
|
|
2296
|
+
/* @__PURE__ */ jsxs22("div", { style: META, children: [
|
|
2223
2297
|
"Local \xB7 resource revision ",
|
|
2224
2298
|
selected.revision,
|
|
2225
2299
|
" \xB7 library revision ",
|
|
2226
2300
|
data.local?.revision ?? 0
|
|
2227
2301
|
] }),
|
|
2228
|
-
/* @__PURE__ */
|
|
2229
|
-
/* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
2231
|
-
/* @__PURE__ */
|
|
2302
|
+
/* @__PURE__ */ jsxs22("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
2303
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2304
|
+
/* @__PURE__ */ jsx24("span", { children: "Title" }),
|
|
2305
|
+
/* @__PURE__ */ jsx24("input", { value: selected.title, onInput: (event) => update({ title: event.currentTarget.value }) })
|
|
2232
2306
|
] }),
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2307
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2308
|
+
/* @__PURE__ */ jsx24("span", { children: "ID" }),
|
|
2309
|
+
/* @__PURE__ */ jsx24("input", { value: selected.id, onInput: (event) => {
|
|
2236
2310
|
const id = cleanId(event.currentTarget.value);
|
|
2237
2311
|
setResources((items) => items.map((item) => item.id === selected.id ? { ...item, id } : item));
|
|
2238
2312
|
setSelectedKey(localKey(id));
|
|
2239
2313
|
} })
|
|
2240
2314
|
] }),
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2315
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2316
|
+
/* @__PURE__ */ jsx24("span", { children: "Type" }),
|
|
2317
|
+
/* @__PURE__ */ jsx24("select", { value: selected.kind, onChange: (event) => {
|
|
2244
2318
|
const kind = event.currentTarget.value;
|
|
2245
2319
|
update({ kind, bodyMarkdown: kind === "document" || kind === "note" ? selected.bodyMarkdown ?? "Start writing\u2026" : void 0, url: kind === "link" || kind === "inspiration" ? selected.url ?? "https://example.com" : void 0, brandAsset: kind === "image" || kind === "file" ? selected.brandAsset ?? { bookId: "brand-book", assetId: "replace-me" } : void 0 });
|
|
2246
|
-
}, children: KINDS.map((kind) => /* @__PURE__ */
|
|
2320
|
+
}, children: KINDS.map((kind) => /* @__PURE__ */ jsx24("option", { value: kind, children: KIND_LABEL[kind] }, kind)) })
|
|
2247
2321
|
] }),
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
2322
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2323
|
+
/* @__PURE__ */ jsx24("span", { children: "Status" }),
|
|
2324
|
+
/* @__PURE__ */ jsxs22("select", { value: selected.status, onChange: (event) => update({ status: event.currentTarget.value }), children: [
|
|
2325
|
+
/* @__PURE__ */ jsx24("option", { value: "draft", children: "Draft" }),
|
|
2326
|
+
/* @__PURE__ */ jsx24("option", { value: "published", children: "Published" }),
|
|
2327
|
+
/* @__PURE__ */ jsx24("option", { value: "archived", children: "Archived" })
|
|
2254
2328
|
] })
|
|
2255
2329
|
] }),
|
|
2256
|
-
/* @__PURE__ */
|
|
2257
|
-
/* @__PURE__ */
|
|
2258
|
-
/* @__PURE__ */
|
|
2330
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2331
|
+
/* @__PURE__ */ jsx24("span", { children: "Visible to" }),
|
|
2332
|
+
/* @__PURE__ */ jsxs22("select", { value: scopeValue, onChange: (event) => {
|
|
2259
2333
|
const value = event.currentTarget.value;
|
|
2260
2334
|
update({ scope: value === "global" ? { kind: "global" } : value === "built_not_found" ? { kind: "built_not_found" } : { kind: "chapter", chapterId: value.slice("chapter:".length) } });
|
|
2261
2335
|
}, children: [
|
|
2262
|
-
data.mode === "hub" ? /* @__PURE__ */
|
|
2263
|
-
data.mode === "hub" ? /* @__PURE__ */
|
|
2264
|
-
data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */
|
|
2336
|
+
data.mode === "hub" ? /* @__PURE__ */ jsx24("option", { value: "built_not_found", children: "Built Not Found only" }) : null,
|
|
2337
|
+
data.mode === "hub" ? /* @__PURE__ */ jsx24("option", { value: "global", children: "All chapters" }) : null,
|
|
2338
|
+
data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */ jsxs22("option", { value: `chapter:${chapterId}`, children: [
|
|
2265
2339
|
"Only ",
|
|
2266
2340
|
chapterId
|
|
2267
2341
|
] }, chapterId))
|
|
2268
2342
|
] })
|
|
2269
2343
|
] }),
|
|
2270
|
-
/* @__PURE__ */
|
|
2271
|
-
/* @__PURE__ */
|
|
2272
|
-
/* @__PURE__ */
|
|
2344
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2345
|
+
/* @__PURE__ */ jsx24("span", { children: "Tags" }),
|
|
2346
|
+
/* @__PURE__ */ jsx24("input", { value: selected.tags.join(", "), placeholder: "brand, membership", onInput: (event) => update({ tags: event.currentTarget.value.split(",").map((tag) => tag.trim()).filter(Boolean) }) })
|
|
2273
2347
|
] })
|
|
2274
2348
|
] }),
|
|
2275
|
-
/* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2277
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2350
|
+
/* @__PURE__ */ jsx24("span", { children: "Summary" }),
|
|
2351
|
+
/* @__PURE__ */ jsx24("textarea", { rows: 2, value: selected.summary ?? "", onInput: (event) => update({ summary: event.currentTarget.value || void 0 }) })
|
|
2278
2352
|
] }),
|
|
2279
|
-
selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */
|
|
2280
|
-
/* @__PURE__ */
|
|
2281
|
-
/* @__PURE__ */
|
|
2353
|
+
selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2354
|
+
/* @__PURE__ */ jsx24("span", { children: "Markdown" }),
|
|
2355
|
+
/* @__PURE__ */ jsx24("textarea", { rows: 12, value: selected.bodyMarkdown ?? "", onInput: (event) => update({ bodyMarkdown: event.currentTarget.value }) })
|
|
2282
2356
|
] }) : null,
|
|
2283
|
-
selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */
|
|
2284
|
-
/* @__PURE__ */
|
|
2285
|
-
/* @__PURE__ */
|
|
2357
|
+
selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2358
|
+
/* @__PURE__ */ jsx24("span", { children: "HTTPS URL" }),
|
|
2359
|
+
/* @__PURE__ */ jsx24("input", { type: "url", value: selected.url ?? "", onInput: (event) => update({ url: event.currentTarget.value }) })
|
|
2286
2360
|
] }) : null,
|
|
2287
|
-
selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */
|
|
2288
|
-
/* @__PURE__ */
|
|
2289
|
-
/* @__PURE__ */
|
|
2290
|
-
/* @__PURE__ */
|
|
2361
|
+
selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */ jsxs22("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
2362
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2363
|
+
/* @__PURE__ */ jsx24("span", { children: "Brand book ID" }),
|
|
2364
|
+
/* @__PURE__ */ jsx24("input", { value: selected.brandAsset?.bookId ?? "", onInput: (event) => update({ brandAsset: { bookId: event.currentTarget.value, assetId: selected.brandAsset?.assetId ?? "", ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) })
|
|
2291
2365
|
] }),
|
|
2292
|
-
/* @__PURE__ */
|
|
2293
|
-
/* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2366
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2367
|
+
/* @__PURE__ */ jsx24("span", { children: "Asset ID" }),
|
|
2368
|
+
/* @__PURE__ */ jsx24("input", { value: selected.brandAsset?.assetId ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: event.currentTarget.value, ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) })
|
|
2295
2369
|
] }),
|
|
2296
|
-
/* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2370
|
+
/* @__PURE__ */ jsxs22("label", { className: "field", children: [
|
|
2371
|
+
/* @__PURE__ */ jsx24("span", { children: "Filename (optional)" }),
|
|
2372
|
+
/* @__PURE__ */ jsx24("input", { value: selected.brandAsset?.filename ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: selected.brandAsset?.assetId ?? "", ...event.currentTarget.value ? { filename: event.currentTarget.value } : {} } }) })
|
|
2299
2373
|
] })
|
|
2300
2374
|
] }) : null,
|
|
2301
|
-
/* @__PURE__ */
|
|
2375
|
+
/* @__PURE__ */ jsx24(ResourcePreview, { item: selected })
|
|
2302
2376
|
] });
|
|
2303
2377
|
}
|
|
2304
2378
|
function builderLibrarySection() {
|
|
2305
|
-
return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */
|
|
2379
|
+
return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */ jsx24(BuilderLibraryAdmin, { ctx }) };
|
|
2306
2380
|
}
|
|
2307
2381
|
|
|
2308
2382
|
// src/ui/admin-email.tsx
|
|
2309
|
-
import { useState as
|
|
2310
|
-
import { Button as
|
|
2383
|
+
import { useState as useState21 } from "preact/hooks";
|
|
2384
|
+
import { Button as Button13, Checkbox as Checkbox2, DataTable as DataTable3, Field as Field9, Input as Input4, Textarea as Textarea4 } from "@odla-ai/ui/components";
|
|
2311
2385
|
|
|
2312
2386
|
// src/ui/admin-email-resolution.tsx
|
|
2313
|
-
import { useState as
|
|
2314
|
-
import { Button as
|
|
2315
|
-
import { jsx as
|
|
2387
|
+
import { useState as useState20 } from "preact/hooks";
|
|
2388
|
+
import { Button as Button12, Field as Field8, Input as Input3, Textarea as Textarea3 } from "@odla-ai/ui/components";
|
|
2389
|
+
import { jsx as jsx25, jsxs as jsxs23 } from "preact/jsx-runtime";
|
|
2316
2390
|
function EmailDeliveryResolution({ row, ctx, onResolved }) {
|
|
2317
|
-
const [open, setOpen] =
|
|
2318
|
-
const [busy, setBusy] =
|
|
2391
|
+
const [open, setOpen] = useState20(false), [reason, setReason] = useState20(""), [messageId, setMessageId] = useState20("");
|
|
2392
|
+
const [busy, setBusy] = useState20(false), [note, setNote] = useState20("");
|
|
2319
2393
|
if (!["sending", "uncertain"].includes(row.deliveryState ?? "") || !row.deliveryAttempt) return null;
|
|
2320
2394
|
const resolve2 = async (decision) => {
|
|
2321
2395
|
setBusy(true);
|
|
@@ -2339,45 +2413,45 @@ function EmailDeliveryResolution({ row, ctx, onResolved }) {
|
|
|
2339
2413
|
setBusy(false);
|
|
2340
2414
|
}
|
|
2341
2415
|
};
|
|
2342
|
-
if (!open) return /* @__PURE__ */
|
|
2343
|
-
return /* @__PURE__ */
|
|
2344
|
-
/* @__PURE__ */
|
|
2416
|
+
if (!open) return /* @__PURE__ */ jsx25(Button12, { mini: true, variant: "secondary", onClick: () => setOpen(true), children: "Review delivery" });
|
|
2417
|
+
return /* @__PURE__ */ jsxs23("div", { children: [
|
|
2418
|
+
/* @__PURE__ */ jsxs23("p", { children: [
|
|
2345
2419
|
"Attempt ",
|
|
2346
2420
|
row.deliveryAttempt,
|
|
2347
2421
|
": ",
|
|
2348
2422
|
row.subject,
|
|
2349
2423
|
". Check provider evidence, not just whether the message is in an inbox. This action does not send mail."
|
|
2350
2424
|
] }),
|
|
2351
|
-
/* @__PURE__ */
|
|
2352
|
-
/* @__PURE__ */
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
/* @__PURE__ */
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
note ? /* @__PURE__ */
|
|
2425
|
+
/* @__PURE__ */ jsx25(Field8, { label: "Provider evidence", children: /* @__PURE__ */ jsx25(Textarea3, { value: reason, onInput: (e) => setReason(e.target.value) }) }),
|
|
2426
|
+
/* @__PURE__ */ jsx25(Field8, { label: "Provider message ID (optional)", children: /* @__PURE__ */ jsx25(Input3, { value: messageId, onInput: (e) => setMessageId(e.target.value) }) }),
|
|
2427
|
+
/* @__PURE__ */ jsx25(Button12, { mini: true, disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_delivered"), children: "Confirm delivered" }),
|
|
2428
|
+
/* @__PURE__ */ jsx25(Button12, { mini: true, variant: "secondary", disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_not_delivered"), children: "Confirm not delivered" }),
|
|
2429
|
+
/* @__PURE__ */ jsx25(Button12, { mini: true, variant: "secondary", disabled: busy, onClick: () => setOpen(false), children: "Cancel" }),
|
|
2430
|
+
note ? /* @__PURE__ */ jsx25("p", { role: "alert", children: note }) : null
|
|
2357
2431
|
] });
|
|
2358
2432
|
}
|
|
2359
2433
|
|
|
2360
2434
|
// src/ui/admin-email.tsx
|
|
2361
|
-
import { jsx as
|
|
2435
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "preact/jsx-runtime";
|
|
2362
2436
|
var logColumns = (copy, ctx, refresh) => [
|
|
2363
2437
|
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
2364
2438
|
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
2365
2439
|
{ key: "to", header: copy.toColumn, cell: (r) => r.to },
|
|
2366
|
-
{ key: "status", header: copy.statusColumn, cell: (r) => /* @__PURE__ */
|
|
2367
|
-
r.deliveryState && r.deliveryState !== "sent" ? /* @__PURE__ */
|
|
2368
|
-
/* @__PURE__ */
|
|
2440
|
+
{ key: "status", header: copy.statusColumn, cell: (r) => /* @__PURE__ */ jsxs24("div", { children: [
|
|
2441
|
+
r.deliveryState && r.deliveryState !== "sent" ? /* @__PURE__ */ jsx26("span", { className: "badge", children: r.deliveryState === "retry_ready" ? "Original workflow may retry" : r.deliveryState === "sending" ? "Sending \u2014 unconfirmed" : "Delivery uncertain" }) : r.error ? /* @__PURE__ */ jsx26("span", { className: "badge", children: copy.failed }) : r.transport === "log-only" ? "Logged only \u2014 not sent" : r.redirected ? /* @__PURE__ */ jsx26("span", { className: "badge", children: copy.redirected }) : copy.delivered,
|
|
2442
|
+
/* @__PURE__ */ jsx26(EmailDeliveryResolution, { row: r, ctx, onResolved: refresh })
|
|
2369
2443
|
] }) }
|
|
2370
2444
|
];
|
|
2371
2445
|
function EmailBody({ ctx }) {
|
|
2372
2446
|
const copy = useChapterCopy().admin.email;
|
|
2373
2447
|
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
2374
2448
|
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
2375
|
-
const [draft, setDraft] =
|
|
2376
|
-
const [busy, setBusy] =
|
|
2377
|
-
const [note, setNote] =
|
|
2449
|
+
const [draft, setDraft] = useState21(null);
|
|
2450
|
+
const [busy, setBusy] = useState21(null);
|
|
2451
|
+
const [note, setNote] = useState21(null);
|
|
2378
2452
|
const data = draft ?? cfg.data;
|
|
2379
|
-
if (cfg.loading) return /* @__PURE__ */
|
|
2380
|
-
if (cfg.error || !data) return /* @__PURE__ */
|
|
2453
|
+
if (cfg.loading) return /* @__PURE__ */ jsx26(AdminNote, { children: copy.loading });
|
|
2454
|
+
if (cfg.error || !data) return /* @__PURE__ */ jsxs24(AdminNote, { children: [
|
|
2381
2455
|
copy.loadFailed,
|
|
2382
2456
|
cfg.error ? `: ${cfg.error}` : "",
|
|
2383
2457
|
"."
|
|
@@ -2410,44 +2484,44 @@ function EmailBody({ ctx }) {
|
|
|
2410
2484
|
}) : copy.testFailed);
|
|
2411
2485
|
log.refresh();
|
|
2412
2486
|
});
|
|
2413
|
-
return /* @__PURE__ */
|
|
2414
|
-
/* @__PURE__ */
|
|
2487
|
+
return /* @__PURE__ */ jsxs24(AdminPage, { children: [
|
|
2488
|
+
/* @__PURE__ */ jsxs24(
|
|
2415
2489
|
Panel,
|
|
2416
2490
|
{
|
|
2417
2491
|
title: copy.delivery,
|
|
2418
|
-
actions: /* @__PURE__ */
|
|
2492
|
+
actions: /* @__PURE__ */ jsxs24("span", { className: "muted", children: [
|
|
2419
2493
|
data.envName,
|
|
2420
2494
|
" \xB7 ",
|
|
2421
2495
|
data.transport,
|
|
2422
2496
|
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
2423
2497
|
] }),
|
|
2424
2498
|
children: [
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2499
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx26(Input4, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
2500
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.replyTo, children: /* @__PURE__ */ jsx26(Input4, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
2501
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx26(Input4, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
2428
2502
|
]
|
|
2429
2503
|
}
|
|
2430
2504
|
),
|
|
2431
|
-
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */
|
|
2432
|
-
/* @__PURE__ */
|
|
2433
|
-
/* @__PURE__ */
|
|
2434
|
-
/* @__PURE__ */
|
|
2505
|
+
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */ jsxs24(Panel, { title: key, actions: /* @__PURE__ */ jsx26(Button13, { variant: "secondary", mini: true, disabled: busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }), children: [
|
|
2506
|
+
/* @__PURE__ */ jsx26(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
2507
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.subject, children: /* @__PURE__ */ jsx26(Input4, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
2508
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.body, children: /* @__PURE__ */ jsx26(Textarea4, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
|
|
2435
2509
|
] }, key)),
|
|
2436
|
-
/* @__PURE__ */
|
|
2437
|
-
/* @__PURE__ */
|
|
2438
|
-
note ? /* @__PURE__ */
|
|
2510
|
+
/* @__PURE__ */ jsxs24("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
2511
|
+
/* @__PURE__ */ jsx26(Button13, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
2512
|
+
note ? /* @__PURE__ */ jsx26("span", { className: "muted", children: note }) : null
|
|
2439
2513
|
] }),
|
|
2440
|
-
/* @__PURE__ */
|
|
2514
|
+
/* @__PURE__ */ jsx26(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx26(DataTable3, { rows: log.data?.sends ?? [], columns: logColumns(copy, ctx, log.refresh), rowKey: (r) => r.id }) })
|
|
2441
2515
|
] });
|
|
2442
2516
|
}
|
|
2443
2517
|
function emailSection(options = {}) {
|
|
2444
2518
|
const { id = "email", label = "Email" } = options;
|
|
2445
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
2519
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx26(EmailBody, { ctx }) };
|
|
2446
2520
|
}
|
|
2447
2521
|
|
|
2448
2522
|
// src/ui/admin-member-content.tsx
|
|
2449
|
-
import { useEffect as useEffect10, useState as
|
|
2450
|
-
import { jsx as
|
|
2523
|
+
import { useEffect as useEffect10, useState as useState22 } from "preact/hooks";
|
|
2524
|
+
import { jsx as jsx27, jsxs as jsxs25 } from "preact/jsx-runtime";
|
|
2451
2525
|
var emptyDraft = () => ({ pages: [], upsellOverrides: [] });
|
|
2452
2526
|
function accessLabel(page) {
|
|
2453
2527
|
return page.audience.kind === "all-members" ? "All members" : `Tiers: ${page.audience.tierIds.join(", ")}`;
|
|
@@ -2457,17 +2531,17 @@ function MemberContentAdmin({
|
|
|
2457
2531
|
initialData
|
|
2458
2532
|
}) {
|
|
2459
2533
|
const resource = useAdminResource(ctx.getToken, "/api/admin/member-content");
|
|
2460
|
-
const [draft, setDraft] =
|
|
2461
|
-
const [saveError, setSaveError] =
|
|
2462
|
-
const [saving, setSaving] =
|
|
2534
|
+
const [draft, setDraft] = useState22(JSON.stringify(initialData?.chapter?.document ?? emptyDraft(), null, 2));
|
|
2535
|
+
const [saveError, setSaveError] = useState22(null);
|
|
2536
|
+
const [saving, setSaving] = useState22(false);
|
|
2463
2537
|
useEffect10(() => {
|
|
2464
2538
|
if (!resource.data) return;
|
|
2465
2539
|
setDraft(JSON.stringify(resource.data.chapter?.document ?? emptyDraft(), null, 2));
|
|
2466
2540
|
}, [resource.data]);
|
|
2467
2541
|
const loaded = initialData ?? resource.data;
|
|
2468
|
-
if (!loaded && resource.loading) return /* @__PURE__ */
|
|
2542
|
+
if (!loaded && resource.loading) return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsx27(AdminNote, { children: "Loading member content\u2026" }) });
|
|
2469
2543
|
if (resource.error || !loaded) {
|
|
2470
|
-
return /* @__PURE__ */
|
|
2544
|
+
return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsxs25(AdminNote, { children: [
|
|
2471
2545
|
"Member content could not be loaded: ",
|
|
2472
2546
|
resource.error ?? "unknown error"
|
|
2473
2547
|
] }) });
|
|
@@ -2498,35 +2572,35 @@ function MemberContentAdmin({
|
|
|
2498
2572
|
setSaving(false);
|
|
2499
2573
|
}
|
|
2500
2574
|
};
|
|
2501
|
-
return /* @__PURE__ */
|
|
2502
|
-
/* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
/* @__PURE__ */
|
|
2506
|
-
/* @__PURE__ */
|
|
2507
|
-
/* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2575
|
+
return /* @__PURE__ */ jsxs25(AdminPage, { children: [
|
|
2576
|
+
/* @__PURE__ */ jsxs25(Panel, { children: [
|
|
2577
|
+
/* @__PURE__ */ jsx27("div", { className: "card-label", children: "Member content" }),
|
|
2578
|
+
/* @__PURE__ */ jsx27("h2", { children: "One catalog, two clear owners" }),
|
|
2579
|
+
/* @__PURE__ */ jsx27("p", { children: "Parent pages are inherited and read-only here. Chapter pages and delegated upsells are edited in the local document. Access uses exact tier IDs; no tier order is assumed." }),
|
|
2580
|
+
/* @__PURE__ */ jsxs25("div", { className: "stat-band", children: [
|
|
2581
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2582
|
+
/* @__PURE__ */ jsx27("strong", { children: inherited.length }),
|
|
2583
|
+
/* @__PURE__ */ jsx27("span", { children: "Inherited pages" })
|
|
2510
2584
|
] }),
|
|
2511
|
-
/* @__PURE__ */
|
|
2512
|
-
/* @__PURE__ */
|
|
2513
|
-
/* @__PURE__ */
|
|
2585
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2586
|
+
/* @__PURE__ */ jsx27("strong", { children: local.length }),
|
|
2587
|
+
/* @__PURE__ */ jsx27("span", { children: "Chapter pages" })
|
|
2514
2588
|
] }),
|
|
2515
|
-
/* @__PURE__ */
|
|
2516
|
-
/* @__PURE__ */
|
|
2517
|
-
/* @__PURE__ */
|
|
2589
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2590
|
+
/* @__PURE__ */ jsx27("strong", { children: data.tiers.length }),
|
|
2591
|
+
/* @__PURE__ */ jsx27("span", { children: "Declared tiers" })
|
|
2518
2592
|
] })
|
|
2519
2593
|
] })
|
|
2520
2594
|
] }),
|
|
2521
|
-
/* @__PURE__ */
|
|
2522
|
-
/* @__PURE__ */
|
|
2523
|
-
/* @__PURE__ */
|
|
2595
|
+
/* @__PURE__ */ jsxs25(Panel, { children: [
|
|
2596
|
+
/* @__PURE__ */ jsx27("div", { className: "card-label", children: "Inherited \xB7 read only" }),
|
|
2597
|
+
/* @__PURE__ */ jsxs25("h2", { children: [
|
|
2524
2598
|
"Parent pages ",
|
|
2525
2599
|
data.parent ? `\xB7 revision ${data.parent.revision}` : ""
|
|
2526
2600
|
] }),
|
|
2527
|
-
inherited.length === 0 ? /* @__PURE__ */
|
|
2528
|
-
/* @__PURE__ */
|
|
2529
|
-
/* @__PURE__ */
|
|
2601
|
+
inherited.length === 0 ? /* @__PURE__ */ jsx27(AdminNote, { children: "No Parent pages have been delivered." }) : /* @__PURE__ */ jsx27("div", { className: "data-list", "data-member-content-inherited": true, children: inherited.map((page) => /* @__PURE__ */ jsxs25("article", { className: "card", children: [
|
|
2602
|
+
/* @__PURE__ */ jsx27("strong", { children: page.title }),
|
|
2603
|
+
/* @__PURE__ */ jsxs25("div", { className: "meeting-note", children: [
|
|
2530
2604
|
"/",
|
|
2531
2605
|
page.slug,
|
|
2532
2606
|
" \xB7 ",
|
|
@@ -2534,22 +2608,22 @@ function MemberContentAdmin({
|
|
|
2534
2608
|
" \xB7 ",
|
|
2535
2609
|
accessLabel(page)
|
|
2536
2610
|
] }),
|
|
2537
|
-
/* @__PURE__ */
|
|
2611
|
+
/* @__PURE__ */ jsxs25("div", { className: "meeting-note", children: [
|
|
2538
2612
|
"Upsell: ",
|
|
2539
2613
|
page.upsell ? `${page.upsell.owner} controlled` : "none"
|
|
2540
2614
|
] })
|
|
2541
2615
|
] }, page.id)) })
|
|
2542
2616
|
] }),
|
|
2543
|
-
/* @__PURE__ */
|
|
2544
|
-
/* @__PURE__ */
|
|
2545
|
-
/* @__PURE__ */
|
|
2617
|
+
/* @__PURE__ */ jsxs25(Panel, { children: [
|
|
2618
|
+
/* @__PURE__ */ jsx27("div", { className: "card-label", children: "Chapter \xB7 editable" }),
|
|
2619
|
+
/* @__PURE__ */ jsxs25("h2", { children: [
|
|
2546
2620
|
"Local document \xB7 revision ",
|
|
2547
2621
|
data.chapter?.revision ?? 0
|
|
2548
2622
|
] }),
|
|
2549
|
-
/* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2623
|
+
/* @__PURE__ */ jsx27("p", { className: "meeting-note", children: "Use the published MemberContentDocument fields. The server supplies schemaVersion, revision, timestamp, and chapter source; unknown fields and undeclared tiers are rejected." }),
|
|
2624
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2625
|
+
/* @__PURE__ */ jsx27("span", { children: "Pages and delegated upsells (JSON)" }),
|
|
2626
|
+
/* @__PURE__ */ jsx27(
|
|
2553
2627
|
"textarea",
|
|
2554
2628
|
{
|
|
2555
2629
|
rows: 24,
|
|
@@ -2560,12 +2634,12 @@ function MemberContentAdmin({
|
|
|
2560
2634
|
}
|
|
2561
2635
|
)
|
|
2562
2636
|
] }),
|
|
2563
|
-
saveError ? /* @__PURE__ */
|
|
2564
|
-
/* @__PURE__ */
|
|
2565
|
-
/* @__PURE__ */
|
|
2637
|
+
saveError ? /* @__PURE__ */ jsxs25("div", { className: "notice danger", role: "alert", children: [
|
|
2638
|
+
/* @__PURE__ */ jsx27("strong", { children: "Fix these items before saving:" }),
|
|
2639
|
+
/* @__PURE__ */ jsx27("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx27("li", { children: error }, error)) })
|
|
2566
2640
|
] }) : null,
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
!data.localAuthoring ? /* @__PURE__ */
|
|
2641
|
+
/* @__PURE__ */ jsx27("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save chapter content" }),
|
|
2642
|
+
!data.localAuthoring ? /* @__PURE__ */ jsx27(AdminNote, { children: "Local authoring is disabled in the checked-in Chapter config." }) : null
|
|
2569
2643
|
] })
|
|
2570
2644
|
] });
|
|
2571
2645
|
}
|
|
@@ -2573,12 +2647,12 @@ function memberContentSection() {
|
|
|
2573
2647
|
return {
|
|
2574
2648
|
id: "member-content",
|
|
2575
2649
|
label: "Member content",
|
|
2576
|
-
render: (ctx) => /* @__PURE__ */
|
|
2650
|
+
render: (ctx) => /* @__PURE__ */ jsx27(MemberContentAdmin, { ctx })
|
|
2577
2651
|
};
|
|
2578
2652
|
}
|
|
2579
2653
|
|
|
2580
2654
|
// src/ui/admin-settings-workspace.tsx
|
|
2581
|
-
import { jsx as
|
|
2655
|
+
import { jsx as jsx28 } from "preact/jsx-runtime";
|
|
2582
2656
|
var viewContext = (ctx, viewId, active) => {
|
|
2583
2657
|
const route = { workspaceId: ctx.route.workspaceId, viewId, ...active && ctx.route.recordId ? { recordId: ctx.route.recordId } : {}, ...active && ctx.route.detailTab ? { detailTab: ctx.route.detailTab } : {} };
|
|
2584
2658
|
const targetFromView = (target) => {
|
|
@@ -2596,7 +2670,7 @@ function SettingsWorkspace({ chapter, ctx }) {
|
|
|
2596
2670
|
const email = emailSection({ label: copy.workspaces.email });
|
|
2597
2671
|
const content = memberContentSection();
|
|
2598
2672
|
const library = builderLibrarySection();
|
|
2599
|
-
return /* @__PURE__ */
|
|
2673
|
+
return /* @__PURE__ */ jsx28(Tabs2, { ariaLabel: copy.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2600
2674
|
{ value: "calendar", label: copy.workspaces.calendar, href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }), panel: calendar.render(viewContext(ctx, "calendar", active === "calendar")) },
|
|
2601
2675
|
{ value: "email", label: copy.workspaces.email, href: ctx.href({ viewId: "email", recordId: null, detailTab: null }), panel: email.render(viewContext(ctx, "email", active === "email")) },
|
|
2602
2676
|
...chapter.memberContent ? [{ value: "member-content", label: "Member content", href: ctx.href({ viewId: "member-content", recordId: null, detailTab: null }), panel: content.render(viewContext(ctx, "member-content", active === "member-content")) }] : [],
|
|
@@ -2607,14 +2681,14 @@ function LeaderSettingsWorkspace({ chapter, ctx }) {
|
|
|
2607
2681
|
const email = emailSection({ label: chapter.copy.admin.workspaces.email });
|
|
2608
2682
|
const library = builderLibrarySection();
|
|
2609
2683
|
const active = ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : "email";
|
|
2610
|
-
return /* @__PURE__ */
|
|
2684
|
+
return /* @__PURE__ */ jsx28(Tabs2, { ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2611
2685
|
{ value: "email", label: chapter.copy.admin.workspaces.email, href: ctx.href({ viewId: "email", recordId: null, detailTab: null }), panel: email.render(viewContext(ctx, "email", active === "email")) },
|
|
2612
2686
|
...chapter.builderLibrary ? [{ value: "builder-library", label: "Builder Library", href: ctx.href({ viewId: "builder-library", recordId: null, detailTab: null }), panel: library.render(viewContext(ctx, "builder-library", active === "builder-library")) }] : []
|
|
2613
2687
|
] });
|
|
2614
2688
|
}
|
|
2615
2689
|
|
|
2616
2690
|
// src/ui/admin-workspaces.tsx
|
|
2617
|
-
import { jsx as
|
|
2691
|
+
import { jsx as jsx29 } from "preact/jsx-runtime";
|
|
2618
2692
|
var adminViewContext = (ctx, viewId, active) => {
|
|
2619
2693
|
const route = {
|
|
2620
2694
|
workspaceId: ctx.route.workspaceId,
|
|
@@ -2640,7 +2714,7 @@ function DashboardWorkspace({ chapter, ctx }) {
|
|
|
2640
2714
|
const active = ctx.route.viewId === "billing" ? "billing" : "overview";
|
|
2641
2715
|
const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
|
|
2642
2716
|
const billing = billingSection({ label: copy.workspaces.billing });
|
|
2643
|
-
return /* @__PURE__ */
|
|
2717
|
+
return /* @__PURE__ */ jsx29(
|
|
2644
2718
|
Tabs3,
|
|
2645
2719
|
{
|
|
2646
2720
|
ariaLabel: copy.workspaces.dashboardViewsLabel,
|
|
@@ -2668,7 +2742,7 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
2668
2742
|
const fallback = types[0]?.[0] ?? "person";
|
|
2669
2743
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
2670
2744
|
const networkSharing = chapter.network.targets.length > 0;
|
|
2671
|
-
return /* @__PURE__ */
|
|
2745
|
+
return /* @__PURE__ */ jsx29(
|
|
2672
2746
|
Tabs3,
|
|
2673
2747
|
{
|
|
2674
2748
|
ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
|
|
@@ -2698,8 +2772,8 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2698
2772
|
const types = Object.entries(chapter.crm.config.types).filter(([, def]) => def.workspace === "portfolio");
|
|
2699
2773
|
const fallback = types[0]?.[0] ?? "";
|
|
2700
2774
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
2701
|
-
if (!active) return /* @__PURE__ */
|
|
2702
|
-
return /* @__PURE__ */
|
|
2775
|
+
if (!active) return /* @__PURE__ */ jsx29(LeaderNetworkOverview, { ctx });
|
|
2776
|
+
return /* @__PURE__ */ jsx29(
|
|
2703
2777
|
Tabs3,
|
|
2704
2778
|
{
|
|
2705
2779
|
ariaLabel: copy.workspaces.portfolioViewsLabel,
|
|
@@ -2723,7 +2797,7 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2723
2797
|
);
|
|
2724
2798
|
}
|
|
2725
2799
|
function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
2726
|
-
return /* @__PURE__ */
|
|
2800
|
+
return /* @__PURE__ */ jsx29(
|
|
2727
2801
|
Tabs3,
|
|
2728
2802
|
{
|
|
2729
2803
|
ariaLabel: chapter.copy.admin.workspaces.dashboardViewsLabel,
|
|
@@ -2733,7 +2807,7 @@ function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
|
2733
2807
|
value: "overview",
|
|
2734
2808
|
label: chapter.copy.admin.workspaces.overview,
|
|
2735
2809
|
href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
|
|
2736
|
-
panel: /* @__PURE__ */
|
|
2810
|
+
panel: /* @__PURE__ */ jsx29(LeaderNetworkOverview, { ctx: adminViewContext(ctx, "overview", true) })
|
|
2737
2811
|
}]
|
|
2738
2812
|
}
|
|
2739
2813
|
);
|
|
@@ -2744,7 +2818,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2744
2818
|
id: "people",
|
|
2745
2819
|
label: copy.workspaces.people,
|
|
2746
2820
|
defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
|
|
2747
|
-
render: (ctx) => /* @__PURE__ */
|
|
2821
|
+
render: (ctx) => /* @__PURE__ */ jsx29(PeopleWorkspace, { chapter, ctx })
|
|
2748
2822
|
};
|
|
2749
2823
|
if (chapter.mode === "hub" && chapter.network.targets.length === 0) return [people];
|
|
2750
2824
|
if (chapter.mode === "hub") {
|
|
@@ -2754,7 +2828,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2754
2828
|
id: "dashboard",
|
|
2755
2829
|
label: copy.workspaces.dashboard,
|
|
2756
2830
|
defaultViewId: "overview",
|
|
2757
|
-
render: (ctx) => /* @__PURE__ */
|
|
2831
|
+
render: (ctx) => /* @__PURE__ */ jsx29(LeaderDashboardWorkspace, { chapter, ctx })
|
|
2758
2832
|
},
|
|
2759
2833
|
networkRecordsWorkspace(chapter),
|
|
2760
2834
|
people,
|
|
@@ -2762,13 +2836,13 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2762
2836
|
id: "portfolio",
|
|
2763
2837
|
label: copy.workspaces.portfolio,
|
|
2764
2838
|
defaultViewId: portfolioTypes[0]?.[0],
|
|
2765
|
-
render: (ctx) => /* @__PURE__ */
|
|
2839
|
+
render: (ctx) => /* @__PURE__ */ jsx29(PortfolioWorkspace, { chapter, ctx })
|
|
2766
2840
|
},
|
|
2767
2841
|
{
|
|
2768
2842
|
id: "settings",
|
|
2769
2843
|
label: copy.workspaces.settings,
|
|
2770
2844
|
defaultViewId: "email",
|
|
2771
|
-
render: (ctx) => /* @__PURE__ */
|
|
2845
|
+
render: (ctx) => /* @__PURE__ */ jsx29(LeaderSettingsWorkspace, { chapter, ctx })
|
|
2772
2846
|
}
|
|
2773
2847
|
];
|
|
2774
2848
|
}
|
|
@@ -2777,7 +2851,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2777
2851
|
id: "dashboard",
|
|
2778
2852
|
label: copy.workspaces.dashboard,
|
|
2779
2853
|
defaultViewId: "overview",
|
|
2780
|
-
render: (ctx) => /* @__PURE__ */
|
|
2854
|
+
render: (ctx) => /* @__PURE__ */ jsx29(DashboardWorkspace, { chapter, ctx })
|
|
2781
2855
|
},
|
|
2782
2856
|
people,
|
|
2783
2857
|
admissionGrantsWorkspace(copy.workspaces.admission),
|
|
@@ -2785,17 +2859,17 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2785
2859
|
id: "settings",
|
|
2786
2860
|
label: copy.workspaces.settings,
|
|
2787
2861
|
defaultViewId: "calendar",
|
|
2788
|
-
render: (ctx) => /* @__PURE__ */
|
|
2862
|
+
render: (ctx) => /* @__PURE__ */ jsx29(SettingsWorkspace, { chapter, ctx })
|
|
2789
2863
|
}
|
|
2790
2864
|
];
|
|
2791
2865
|
}
|
|
2792
2866
|
|
|
2793
2867
|
// src/ui/admin-frame.tsx
|
|
2794
2868
|
import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
|
|
2795
|
-
import { jsx as
|
|
2869
|
+
import { jsx as jsx30, jsxs as jsxs26 } from "preact/jsx-runtime";
|
|
2796
2870
|
function AdminTheme(props) {
|
|
2797
2871
|
const brand = props.chapter?.brand;
|
|
2798
|
-
return /* @__PURE__ */
|
|
2872
|
+
return /* @__PURE__ */ jsx30(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs26(
|
|
2799
2873
|
ThemeScope2,
|
|
2800
2874
|
{
|
|
2801
2875
|
ref: props.scopeRef,
|
|
@@ -2804,7 +2878,7 @@ function AdminTheme(props) {
|
|
|
2804
2878
|
accent: brand?.accent,
|
|
2805
2879
|
colorScheme: brand?.colorScheme ?? "light",
|
|
2806
2880
|
children: [
|
|
2807
|
-
/* @__PURE__ */
|
|
2881
|
+
/* @__PURE__ */ jsx30(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
|
|
2808
2882
|
props.children
|
|
2809
2883
|
]
|
|
2810
2884
|
}
|
|
@@ -2824,10 +2898,10 @@ function signInRedirect(basePath, workspaces) {
|
|
|
2824
2898
|
}
|
|
2825
2899
|
|
|
2826
2900
|
// src/ui/admin-appearance.ts
|
|
2827
|
-
import { useEffect as useEffect11, useState as
|
|
2901
|
+
import { useEffect as useEffect11, useState as useState23 } from "preact/hooks";
|
|
2828
2902
|
import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
|
|
2829
2903
|
function useScopedClerkAppearance(scope) {
|
|
2830
|
-
const [appearance, setAppearance] =
|
|
2904
|
+
const [appearance, setAppearance] = useState23();
|
|
2831
2905
|
useEffect11(() => {
|
|
2832
2906
|
setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
|
|
2833
2907
|
}, [scope]);
|
|
@@ -2859,7 +2933,7 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
|
|
|
2859
2933
|
}
|
|
2860
2934
|
|
|
2861
2935
|
// src/ui/admin.tsx
|
|
2862
|
-
import { jsx as
|
|
2936
|
+
import { jsx as jsx31, jsxs as jsxs27 } from "preact/jsx-runtime";
|
|
2863
2937
|
function Authed(props) {
|
|
2864
2938
|
const copy = useChapterCopy().admin;
|
|
2865
2939
|
const {
|
|
@@ -2887,7 +2961,7 @@ function Authed(props) {
|
|
|
2887
2961
|
}),
|
|
2888
2962
|
[getToken, crmBasePath]
|
|
2889
2963
|
);
|
|
2890
|
-
const [state, setState] =
|
|
2964
|
+
const [state, setState] = useState24({ status: "checking" });
|
|
2891
2965
|
useEffect12(() => {
|
|
2892
2966
|
let live = true;
|
|
2893
2967
|
void (async () => {
|
|
@@ -2907,17 +2981,17 @@ function Authed(props) {
|
|
|
2907
2981
|
live = false;
|
|
2908
2982
|
};
|
|
2909
2983
|
}, [getToken, apiBase, auth]);
|
|
2910
|
-
if (state.status === "checking") return /* @__PURE__ */
|
|
2984
|
+
if (state.status === "checking") return /* @__PURE__ */ jsx31(Gate, { brand, children: /* @__PURE__ */ jsx31("p", { style: S.muted, children: copy.auth.checking }) });
|
|
2911
2985
|
if (state.status === "denied") {
|
|
2912
|
-
return /* @__PURE__ */
|
|
2913
|
-
/* @__PURE__ */
|
|
2914
|
-
/* @__PURE__ */
|
|
2986
|
+
return /* @__PURE__ */ jsx31(Gate, { brand, children: /* @__PURE__ */ jsxs27("div", { className: "card", style: S.card, children: [
|
|
2987
|
+
/* @__PURE__ */ jsx31("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
|
|
2988
|
+
/* @__PURE__ */ jsx31("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
|
|
2915
2989
|
account: state.email ?? copy.auth.thisAccount
|
|
2916
2990
|
}) }),
|
|
2917
|
-
/* @__PURE__ */
|
|
2991
|
+
/* @__PURE__ */ jsx31("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
|
|
2918
2992
|
] }) });
|
|
2919
2993
|
}
|
|
2920
|
-
return /* @__PURE__ */
|
|
2994
|
+
return /* @__PURE__ */ jsx31(
|
|
2921
2995
|
AdminShell,
|
|
2922
2996
|
{
|
|
2923
2997
|
workspaces,
|
|
@@ -2959,7 +3033,7 @@ function ChapterAdmin(props) {
|
|
|
2959
3033
|
const chrome = props.chrome ?? "embedded";
|
|
2960
3034
|
const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
|
|
2961
3035
|
const redirect = signInRedirect(basePath, workspaces);
|
|
2962
|
-
const [publishableKey, setPublishableKey] =
|
|
3036
|
+
const [publishableKey, setPublishableKey] = useState24(void 0);
|
|
2963
3037
|
const scopeRef = useRef3(null);
|
|
2964
3038
|
const appearance = useScopedClerkAppearance(scopeRef);
|
|
2965
3039
|
useEffect12(() => {
|
|
@@ -2971,23 +3045,23 @@ function ChapterAdmin(props) {
|
|
|
2971
3045
|
}, [apiBase, auth]);
|
|
2972
3046
|
let content;
|
|
2973
3047
|
if (publishableKey === void 0 || publishableKey && !appearance) {
|
|
2974
|
-
content = /* @__PURE__ */
|
|
3048
|
+
content = /* @__PURE__ */ jsx31(Gate, { brand, children: /* @__PURE__ */ jsx31("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
2975
3049
|
} else if (!publishableKey) {
|
|
2976
|
-
content = /* @__PURE__ */
|
|
2977
|
-
/* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
3050
|
+
content = /* @__PURE__ */ jsx31(Gate, { brand, children: /* @__PURE__ */ jsxs27("div", { className: "card", style: S.card, children: [
|
|
3051
|
+
/* @__PURE__ */ jsx31("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
|
|
3052
|
+
/* @__PURE__ */ jsx31("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
|
|
2979
3053
|
] }) });
|
|
2980
3054
|
} else if (workspaces.length === 0) {
|
|
2981
|
-
content = /* @__PURE__ */
|
|
3055
|
+
content = /* @__PURE__ */ jsx31(Gate, { brand, children: /* @__PURE__ */ jsx31("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
|
|
2982
3056
|
} else {
|
|
2983
|
-
content = /* @__PURE__ */
|
|
3057
|
+
content = /* @__PURE__ */ jsxs27(
|
|
2984
3058
|
ClerkGate,
|
|
2985
3059
|
{
|
|
2986
3060
|
publishableKey,
|
|
2987
3061
|
appearance,
|
|
2988
3062
|
afterSignOutUrl: "/",
|
|
2989
3063
|
children: [
|
|
2990
|
-
/* @__PURE__ */
|
|
3064
|
+
/* @__PURE__ */ jsx31(SignedOut, { children: /* @__PURE__ */ jsx31(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx31(
|
|
2991
3065
|
SignIn,
|
|
2992
3066
|
{
|
|
2993
3067
|
routing: "hash",
|
|
@@ -2995,7 +3069,7 @@ function ChapterAdmin(props) {
|
|
|
2995
3069
|
signUpForceRedirectUrl: redirect
|
|
2996
3070
|
}
|
|
2997
3071
|
) }) }),
|
|
2998
|
-
/* @__PURE__ */
|
|
3072
|
+
/* @__PURE__ */ jsx31(SignedIn, { children: /* @__PURE__ */ jsx31(
|
|
2999
3073
|
Authed,
|
|
3000
3074
|
{
|
|
3001
3075
|
workspaces,
|
|
@@ -3016,7 +3090,7 @@ function ChapterAdmin(props) {
|
|
|
3016
3090
|
}
|
|
3017
3091
|
);
|
|
3018
3092
|
}
|
|
3019
|
-
return /* @__PURE__ */
|
|
3093
|
+
return /* @__PURE__ */ jsx31(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
|
|
3020
3094
|
}
|
|
3021
3095
|
|
|
3022
3096
|
// src/ui/admin-defaults.ts
|
|
@@ -3025,17 +3099,17 @@ function defaultAdminSections(chapter) {
|
|
|
3025
3099
|
}
|
|
3026
3100
|
|
|
3027
3101
|
// src/ui/admin-meetings.tsx
|
|
3028
|
-
import { useState as
|
|
3029
|
-
import { Button as
|
|
3030
|
-
import { Fragment as
|
|
3102
|
+
import { useState as useState25 } from "preact/hooks";
|
|
3103
|
+
import { Button as Button14 } from "@odla-ai/ui/components";
|
|
3104
|
+
import { Fragment as Fragment9, jsx as jsx32, jsxs as jsxs28 } from "preact/jsx-runtime";
|
|
3031
3105
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
3032
3106
|
function MeetingsBody({ ctx }) {
|
|
3033
3107
|
const copy = useChapterCopy().admin.meetings;
|
|
3034
3108
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
|
|
3035
|
-
const [busy, setBusy] =
|
|
3036
|
-
const [note, setNote] =
|
|
3037
|
-
if (loading) return /* @__PURE__ */
|
|
3038
|
-
if (error || !data) return /* @__PURE__ */
|
|
3109
|
+
const [busy, setBusy] = useState25(null);
|
|
3110
|
+
const [note, setNote] = useState25(null);
|
|
3111
|
+
if (loading) return /* @__PURE__ */ jsx32(AdminNote, { children: copy.loading });
|
|
3112
|
+
if (error || !data) return /* @__PURE__ */ jsxs28(AdminNote, { children: [
|
|
3039
3113
|
copy.loadFailed,
|
|
3040
3114
|
error ? `: ${error}` : "",
|
|
3041
3115
|
"."
|
|
@@ -3066,43 +3140,43 @@ function MeetingsBody({ ctx }) {
|
|
|
3066
3140
|
}
|
|
3067
3141
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
3068
3142
|
};
|
|
3069
|
-
return /* @__PURE__ */
|
|
3143
|
+
return /* @__PURE__ */ jsx32(AdminPage, { children: /* @__PURE__ */ jsx32(Panel, { title: /* @__PURE__ */ jsxs28(Fragment9, { children: [
|
|
3070
3144
|
copy.agenda,
|
|
3071
3145
|
" ",
|
|
3072
|
-
/* @__PURE__ */
|
|
3146
|
+
/* @__PURE__ */ jsxs28("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
3073
3147
|
"\xB7 ",
|
|
3074
3148
|
data.timezone
|
|
3075
3149
|
] })
|
|
3076
|
-
] }), actions: note ? /* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3080
|
-
m.drift && m.drift !== "none" ? /* @__PURE__ */
|
|
3150
|
+
] }), actions: note ? /* @__PURE__ */ jsx32("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx32("p", { className: "muted", style: { margin: 0 }, children: copy.empty }) : /* @__PURE__ */ jsx32("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs28("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
|
|
3151
|
+
/* @__PURE__ */ jsx32("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
|
|
3152
|
+
/* @__PURE__ */ jsx32("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
|
|
3153
|
+
/* @__PURE__ */ jsx32("span", { className: "badge", children: m.status }),
|
|
3154
|
+
m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs28("span", { className: "badge", children: [
|
|
3081
3155
|
copy.drift,
|
|
3082
3156
|
": ",
|
|
3083
3157
|
m.drift
|
|
3084
3158
|
] }) : null,
|
|
3085
|
-
m.meetUrl ? /* @__PURE__ */
|
|
3086
|
-
m.status === "scheduled" ? /* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
/* @__PURE__ */
|
|
3159
|
+
m.meetUrl ? /* @__PURE__ */ jsx32("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
3160
|
+
m.status === "scheduled" ? /* @__PURE__ */ jsxs28(Fragment9, { children: [
|
|
3161
|
+
/* @__PURE__ */ jsx32(Button14, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
3162
|
+
/* @__PURE__ */ jsx32(Button14, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
3089
3163
|
] }) : null
|
|
3090
3164
|
] }, m.id)) }) }) });
|
|
3091
3165
|
}
|
|
3092
3166
|
function meetingsSection(options = {}) {
|
|
3093
3167
|
const { id = "meetings", label = "Meetings" } = options;
|
|
3094
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
3168
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx32(MeetingsBody, { ctx }) };
|
|
3095
3169
|
}
|
|
3096
3170
|
|
|
3097
3171
|
// src/ui/admin-network-record-editor.tsx
|
|
3098
|
-
import { Button as
|
|
3099
|
-
import { useRef as useRef4, useState as
|
|
3100
|
-
import { Fragment as
|
|
3172
|
+
import { Button as Button15, Field as Field10, Select as Select6 } from "@odla-ai/ui/components";
|
|
3173
|
+
import { useRef as useRef4, useState as useState26 } from "preact/hooks";
|
|
3174
|
+
import { Fragment as Fragment10, jsx as jsx33, jsxs as jsxs29 } from "preact/jsx-runtime";
|
|
3101
3175
|
function NetworkRecordEditor(props) {
|
|
3102
3176
|
const { ctx, record } = props;
|
|
3103
|
-
const [stage, setStage] =
|
|
3104
|
-
const [busy, setBusy] =
|
|
3105
|
-
const [error, setError] =
|
|
3177
|
+
const [stage, setStage] = useState26("");
|
|
3178
|
+
const [busy, setBusy] = useState26(false);
|
|
3179
|
+
const [error, setError] = useState26(null);
|
|
3106
3180
|
const attempt = useRef4(null);
|
|
3107
3181
|
const pending = record.edit?.pendingAction;
|
|
3108
3182
|
const superAdmin = ctx.currentUser?.superAdmin === true;
|
|
@@ -3135,11 +3209,11 @@ function NetworkRecordEditor(props) {
|
|
|
3135
3209
|
setBusy(false);
|
|
3136
3210
|
}
|
|
3137
3211
|
};
|
|
3138
|
-
return /* @__PURE__ */
|
|
3139
|
-
superAdmin && record.edit ? /* @__PURE__ */
|
|
3140
|
-
/* @__PURE__ */
|
|
3141
|
-
/* @__PURE__ */
|
|
3142
|
-
/* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsxs29("div", { style: { display: "grid", gap: 24 }, children: [
|
|
3213
|
+
superAdmin && record.edit ? /* @__PURE__ */ jsxs29(Fragment10, { children: [
|
|
3214
|
+
/* @__PURE__ */ jsxs29("section", { children: [
|
|
3215
|
+
/* @__PURE__ */ jsx33("h3", { children: "Chapter profile" }),
|
|
3216
|
+
/* @__PURE__ */ jsx33(
|
|
3143
3217
|
OperationalProfileForm,
|
|
3144
3218
|
{
|
|
3145
3219
|
fields: record.edit.fields,
|
|
@@ -3149,27 +3223,27 @@ function NetworkRecordEditor(props) {
|
|
|
3149
3223
|
`${record.id}:${record.revision}`
|
|
3150
3224
|
)
|
|
3151
3225
|
] }),
|
|
3152
|
-
/* @__PURE__ */
|
|
3153
|
-
/* @__PURE__ */
|
|
3154
|
-
pending ? /* @__PURE__ */
|
|
3226
|
+
/* @__PURE__ */ jsxs29("section", { style: { display: "grid", gap: 12 }, children: [
|
|
3227
|
+
/* @__PURE__ */ jsx33("h3", { children: "Membership actions" }),
|
|
3228
|
+
pending ? /* @__PURE__ */ jsxs29("p", { children: [
|
|
3155
3229
|
"An application action is pending: ",
|
|
3156
3230
|
pending.stage.replaceAll("_", " "),
|
|
3157
3231
|
". ",
|
|
3158
3232
|
ownPending ? "Retry reconciles the same operation." : "The original operator must reconcile it."
|
|
3159
|
-
] }) : record.edit.allowedStages.length ? /* @__PURE__ */
|
|
3160
|
-
/* @__PURE__ */
|
|
3161
|
-
record.edit.allowedStages.map((action) => /* @__PURE__ */
|
|
3162
|
-
] }) }) : /* @__PURE__ */
|
|
3163
|
-
pending || record.edit.allowedStages.length ? /* @__PURE__ */
|
|
3164
|
-
error ? /* @__PURE__ */
|
|
3233
|
+
] }) : record.edit.allowedStages.length ? /* @__PURE__ */ jsx33(Field10, { label: "Action", children: /* @__PURE__ */ jsxs29(Select6, { "aria-label": "Action", value: stage, disabled: busy, onChange: (event) => setStage(event.currentTarget.value), children: [
|
|
3234
|
+
/* @__PURE__ */ jsx33("option", { value: "", children: "Choose an action" }),
|
|
3235
|
+
record.edit.allowedStages.map((action) => /* @__PURE__ */ jsx33("option", { value: action.id, children: action.label }, action.id))
|
|
3236
|
+
] }) }) : /* @__PURE__ */ jsx33("p", { children: "No membership action is currently allowed." }),
|
|
3237
|
+
pending || record.edit.allowedStages.length ? /* @__PURE__ */ jsx33("div", { children: /* @__PURE__ */ jsx33(Button15, { disabled: busy || !ownPending || !pending && !stage, onClick: () => void move(), children: busy ? "Applying\u2026" : pending ? "Retry pending action" : "Apply action" }) }) : null,
|
|
3238
|
+
error ? /* @__PURE__ */ jsx33("p", { role: "alert", children: error }) : null
|
|
3165
3239
|
] })
|
|
3166
3240
|
] }) : null,
|
|
3167
|
-
superAdmin && props.admissionGrants ? /* @__PURE__ */
|
|
3168
|
-
/* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
/* @__PURE__ */
|
|
3241
|
+
superAdmin && props.admissionGrants ? /* @__PURE__ */ jsxs29("section", { children: [
|
|
3242
|
+
/* @__PURE__ */ jsx33("h3", { children: "Next invitation" }),
|
|
3243
|
+
/* @__PURE__ */ jsx33("p", { children: "This setting applies to the next eligible signup; it does not replace approval of an existing application." }),
|
|
3244
|
+
/* @__PURE__ */ jsx33(LeaderAdmissionAction, { getToken: ctx.getToken, targetId: props.targetId, sourceRecordId: record.id })
|
|
3171
3245
|
] }) : null,
|
|
3172
|
-
/* @__PURE__ */
|
|
3246
|
+
/* @__PURE__ */ jsx33(
|
|
3173
3247
|
NetworkRecordDetail,
|
|
3174
3248
|
{
|
|
3175
3249
|
ctx,
|
|
@@ -3183,6 +3257,35 @@ function NetworkRecordEditor(props) {
|
|
|
3183
3257
|
] });
|
|
3184
3258
|
}
|
|
3185
3259
|
|
|
3260
|
+
// src/ui/network-seat-billing.tsx
|
|
3261
|
+
import { DataTable as DataTable4 } from "@odla-ai/ui/components";
|
|
3262
|
+
import { jsx as jsx34, jsxs as jsxs30 } from "preact/jsx-runtime";
|
|
3263
|
+
var money3 = (row, cents) => row.currency ? new Intl.NumberFormat("en-US", {
|
|
3264
|
+
style: "currency",
|
|
3265
|
+
currency: row.currency
|
|
3266
|
+
}).format(cents / 100) : "Unknown";
|
|
3267
|
+
var columns2 = [
|
|
3268
|
+
{ key: "seatId", header: "Seat", cell: (row) => /* @__PURE__ */ jsx34("code", { children: row.seatId }) },
|
|
3269
|
+
{ key: "sourceKey", header: "Chapter / runtime", cell: (row) => row.sourceKey },
|
|
3270
|
+
{ key: "status", header: "Payment", cell: (row) => row.status ?? "Unknown" },
|
|
3271
|
+
{ key: "amountReceivedCents", header: "Collected", cell: (row) => money3(row, row.amountReceivedCents) },
|
|
3272
|
+
{ key: "refundedCents", header: "Refunded", cell: (row) => money3(row, row.refundedCents) },
|
|
3273
|
+
{ key: "membershipId", header: "Primary membership", cell: (row) => /* @__PURE__ */ jsx34("code", { children: row.membershipId }) },
|
|
3274
|
+
{ key: "paymentIntentId", header: "Stripe payment", cell: (row) => /* @__PURE__ */ jsx34("code", { children: row.paymentIntentId }) },
|
|
3275
|
+
{ key: "subscriptionItemId", header: "Renewal item", cell: (row) => row.subscriptionItemId ? /* @__PURE__ */ jsx34("code", { children: row.subscriptionItemId }) : "Awaiting provider linkage" },
|
|
3276
|
+
{ key: "currentPeriodEnd", header: "Paid through", cell: (row) => row.currentPeriodEnd ? new Date(row.currentPeriodEnd * 1e3).toLocaleDateString() : "Awaiting provider linkage" }
|
|
3277
|
+
];
|
|
3278
|
+
function NetworkSeatBilling(props) {
|
|
3279
|
+
const { data, loading, error } = useAdminResource(props.getToken, "/api/admin/network-commercial-read-model");
|
|
3280
|
+
return /* @__PURE__ */ jsxs30(Panel, { title: "Additional membership seats", children: [
|
|
3281
|
+
/* @__PURE__ */ jsx34("p", { children: "Seat payments are included in the network totals. Renewal items share the primary subscription\u2019s term." }),
|
|
3282
|
+
loading ? /* @__PURE__ */ jsx34("p", { children: "Loading seats\u2026" }) : error ? /* @__PURE__ */ jsxs30("p", { role: "alert", children: [
|
|
3283
|
+
"Seats could not be loaded: ",
|
|
3284
|
+
error
|
|
3285
|
+
] }) : /* @__PURE__ */ jsx34(DataTable4, { rows: data?.seats ?? [], columns: columns2, rowKey: (row) => `${row.sourceKey}:${row.seatId}` })
|
|
3286
|
+
] });
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3186
3289
|
export {
|
|
3187
3290
|
AdminPage,
|
|
3188
3291
|
Panel,
|
|
@@ -3194,6 +3297,7 @@ export {
|
|
|
3194
3297
|
adminSectionHref,
|
|
3195
3298
|
adminFetch,
|
|
3196
3299
|
useAdminResource,
|
|
3300
|
+
PaymentRecoveryQueue,
|
|
3197
3301
|
billingSection,
|
|
3198
3302
|
dashboardSection,
|
|
3199
3303
|
RecordActions,
|
|
@@ -3219,6 +3323,7 @@ export {
|
|
|
3219
3323
|
ChapterAdmin,
|
|
3220
3324
|
defaultAdminSections,
|
|
3221
3325
|
meetingsSection,
|
|
3222
|
-
NetworkRecordEditor
|
|
3326
|
+
NetworkRecordEditor,
|
|
3327
|
+
NetworkSeatBilling
|
|
3223
3328
|
};
|
|
3224
|
-
//# sourceMappingURL=chunk-
|
|
3329
|
+
//# sourceMappingURL=chunk-KXIGZWG5.js.map
|