@odla-ai/chapter 0.48.3 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -2
- package/dist/{chunk-MQ4OPZCS.js → chunk-4EA5V4JE.js} +994 -648
- package/dist/chunk-4EA5V4JE.js.map +1 -0
- package/dist/{chunk-4HS2JPE2.js → chunk-JXX4BFDP.js} +21 -2
- package/dist/chunk-JXX4BFDP.js.map +1 -0
- package/dist/{chunk-KZ2AXEZW.js → chunk-OS46ZE46.js} +765 -543
- package/dist/chunk-OS46ZE46.js.map +1 -0
- package/dist/{copy-context-CMupQL9h.d.ts → copy-context-Ddat__H5.d.ts} +16 -3
- package/dist/index.cjs +591 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -8
- package/dist/index.d.ts +101 -8
- package/dist/index.js +587 -154
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +64 -5
- package/dist/ui/admin/index.js +14 -2
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +19 -3
- package/dist/ui/member/index.d.ts +44 -4
- package/dist/ui/member/index.js +6 -2
- package/dist/worker/index.cjs +5114 -4064
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +105 -6
- package/dist/worker/index.d.ts +105 -6
- package/dist/worker/index.js +5104 -4054
- 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-JXX4BFDP.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 useState27 } 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,229 @@ 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
|
|
|
713
|
+
// src/ui/admin-lifecycle-adapter.ts
|
|
714
|
+
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
715
|
+
return {
|
|
716
|
+
transitionStage: async ({ record, to, client }) => {
|
|
717
|
+
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
718
|
+
if (!applicationId) {
|
|
719
|
+
const crmClient = ctx.client ?? client;
|
|
720
|
+
if (!crmClient) throw new Error(copy.workflowMissing);
|
|
721
|
+
await crmClient.setStage(record.id, to);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const path = `/api/admin/applications/${encodeURIComponent(applicationId)}`;
|
|
725
|
+
if (to === "approved") {
|
|
726
|
+
await adminFetch(ctx.getToken, `${path}/approve`, { method: "POST" });
|
|
727
|
+
} else if (to === "refunded") {
|
|
728
|
+
await adminFetch(ctx.getToken, `${path}/refund`, { method: "POST" });
|
|
729
|
+
} else {
|
|
730
|
+
await adminFetch(ctx.getToken, path, {
|
|
731
|
+
method: "PATCH",
|
|
732
|
+
body: JSON.stringify({ status: to })
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/ui/admin-named-seat.tsx
|
|
740
|
+
import { useState as useState6 } from "preact/hooks";
|
|
741
|
+
import { Button as Button3 } from "@odla-ai/ui/components";
|
|
742
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
743
|
+
function NamedSeatRecordPanel(props) {
|
|
744
|
+
return props.superAdmin ? /* @__PURE__ */ jsx7(SeatPanel, { ...props }) : null;
|
|
745
|
+
}
|
|
746
|
+
function SeatPanel(props) {
|
|
747
|
+
const resource = useAdminResource(props.getToken, `/api/admin/applications/${encodeURIComponent(props.applicationId)}/named-seat`);
|
|
748
|
+
const [busy, setBusy] = useState6(false);
|
|
749
|
+
const [note, setNote] = useState6(null);
|
|
750
|
+
if (resource.loading) return /* @__PURE__ */ jsx7("p", { role: "status", children: "Loading additional seat\u2026" });
|
|
751
|
+
if (resource.error || !resource.data) return null;
|
|
752
|
+
const { data } = resource, seat = data.seat;
|
|
753
|
+
const money4 = new Intl.NumberFormat("en-US", { style: "currency", currency: data.currency }).format(data.amountCents / 100);
|
|
754
|
+
const refund = async () => {
|
|
755
|
+
setBusy(true);
|
|
756
|
+
setNote(null);
|
|
757
|
+
try {
|
|
758
|
+
const receipt = await adminFetch(
|
|
759
|
+
props.getToken,
|
|
760
|
+
`/api/admin/applications/${encodeURIComponent(props.applicationId)}/named-seat/refund`,
|
|
761
|
+
{ method: "POST" }
|
|
762
|
+
);
|
|
763
|
+
setNote(receipt.replayed ? "This seat was already refunded." : `Refunded ${new Intl.NumberFormat("en-US", {
|
|
764
|
+
style: "currency",
|
|
765
|
+
currency: data.currency
|
|
766
|
+
}).format((receipt.refundedCents ?? data.amountCents) / 100)}.`);
|
|
767
|
+
props.onChanged?.();
|
|
768
|
+
resource.refresh();
|
|
769
|
+
} catch (failure) {
|
|
770
|
+
setNote(failure instanceof Error ? failure.message : "Refund could not be completed.");
|
|
771
|
+
} finally {
|
|
772
|
+
setBusy(false);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
return /* @__PURE__ */ jsxs7("section", { className: "panel", "aria-label": "Additional seat", style: { display: "grid", gap: 8, marginTop: 16 }, children: [
|
|
776
|
+
/* @__PURE__ */ jsx7("h3", { style: { margin: 0, fontSize: 16 }, children: "Additional seat" }),
|
|
777
|
+
!seat ? /* @__PURE__ */ jsx7("p", { className: "muted", style: { margin: 0 }, children: "No additional seat has been purchased." }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
|
|
778
|
+
/* @__PURE__ */ jsxs7("p", { style: { margin: 0 }, children: [
|
|
779
|
+
seat.recipientName,
|
|
780
|
+
" \xB7 ",
|
|
781
|
+
seat.recipientEmail,
|
|
782
|
+
" \xB7 ",
|
|
783
|
+
seat.status.replaceAll("_", " ")
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsxs7("p", { className: "muted", style: { margin: 0 }, children: [
|
|
786
|
+
money4,
|
|
787
|
+
" paid in full. The seat waits for its recipient and is never refunded automatically."
|
|
788
|
+
] }),
|
|
789
|
+
data.refundable ? /* @__PURE__ */ jsx7("div", { children: /* @__PURE__ */ jsx7(Button3, { variant: "danger", mini: true, disabled: busy, onClick: () => void refund(), children: busy ? "Refunding\u2026" : "Refund seat" }) }) : null
|
|
790
|
+
] }),
|
|
791
|
+
note ? /* @__PURE__ */ jsx7("p", { className: "muted", role: "status", style: { margin: 0 }, children: note }) : null
|
|
792
|
+
] });
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/ui/admin-candidate-stage.tsx
|
|
796
|
+
import { useState as useState7 } from "preact/hooks";
|
|
797
|
+
import { Button as Button4, Field, Select } from "@odla-ai/ui/components";
|
|
798
|
+
import { Fragment as Fragment6, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
799
|
+
var APPLICATION_STAGES = [
|
|
800
|
+
"submitted",
|
|
801
|
+
"paid_pending_vetting",
|
|
802
|
+
"call_scheduled",
|
|
803
|
+
"interviewed",
|
|
804
|
+
"approved",
|
|
805
|
+
"declined",
|
|
806
|
+
"refunded"
|
|
807
|
+
];
|
|
808
|
+
function CandidateStagePanel(props) {
|
|
809
|
+
const [selected, setSelected] = useState7("");
|
|
810
|
+
const [busy, setBusy] = useState7(false);
|
|
811
|
+
const [error, setError] = useState7(null);
|
|
812
|
+
const options = props.stages.filter((stage) => !props.applicationStages.includes(stage.id) && stage.id !== props.current);
|
|
813
|
+
const label = (id) => props.stages.find((stage) => stage.id === id)?.label ?? id.replaceAll("_", " ");
|
|
814
|
+
if (!options.length && props.current == null) {
|
|
815
|
+
return /* @__PURE__ */ jsx8("p", { children: "Journey stage: Not applicable. No membership application is linked. Administrative access is separate." });
|
|
816
|
+
}
|
|
817
|
+
const target = options.some((stage) => stage.id === selected) ? selected : "";
|
|
818
|
+
return /* @__PURE__ */ jsxs8(Field, { label: "Stage", error, children: [
|
|
819
|
+
/* @__PURE__ */ jsx8("p", { children: props.current == null ? "No stage yet. Membership statuses begin when this person applies." : label(props.current) }),
|
|
820
|
+
options.length ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
|
|
821
|
+
/* @__PURE__ */ jsx8(
|
|
822
|
+
Select,
|
|
823
|
+
{
|
|
824
|
+
"aria-label": "Move to stage",
|
|
825
|
+
value: target,
|
|
826
|
+
disabled: busy,
|
|
827
|
+
onChange: (event) => setSelected(event.currentTarget.value),
|
|
828
|
+
options: [{ value: "", label: "Move to\u2026" }, ...options.map((stage) => ({ value: stage.id, label: label(stage.id) }))]
|
|
829
|
+
}
|
|
830
|
+
),
|
|
831
|
+
/* @__PURE__ */ jsx8(Button4, { mini: true, disabled: busy || !target, onClick: async () => {
|
|
832
|
+
setBusy(true);
|
|
833
|
+
setError(null);
|
|
834
|
+
try {
|
|
835
|
+
await props.onMove(target);
|
|
836
|
+
setSelected("");
|
|
837
|
+
} catch (failure) {
|
|
838
|
+
setError(failure instanceof Error ? failure.message : "Stage change failed");
|
|
839
|
+
} finally {
|
|
840
|
+
setBusy(false);
|
|
841
|
+
}
|
|
842
|
+
}, children: "Move" })
|
|
843
|
+
] }) : /* @__PURE__ */ jsx8("p", { children: "No pre-application stages are configured for this chapter\u2019s CRM." })
|
|
844
|
+
] });
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/ui/admin-record-admission.tsx
|
|
848
|
+
import { useState as useState8 } from "preact/hooks";
|
|
849
|
+
import { Button as Button5, Field as Field2, Select as Select2 } from "@odla-ai/ui/components";
|
|
850
|
+
import { Fragment as Fragment7, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
851
|
+
function RecordAdmissionPanel(props) {
|
|
852
|
+
const copy = useChapterCopy().admin.admission;
|
|
853
|
+
const resource = useAdminResource(props.getToken, "/api/admin/admission-grants");
|
|
854
|
+
const [decision, setDecision] = useState8("waive_interview");
|
|
855
|
+
const [note, setNote] = useState8("");
|
|
856
|
+
const [busy, setBusy] = useState8(false);
|
|
857
|
+
const [error, setError] = useState8(null);
|
|
858
|
+
if (!props.email) return /* @__PURE__ */ jsx9("p", { className: "muted", children: "This record has no email yet. Add one before setting an invitation." });
|
|
859
|
+
const email = props.email.trim().toLowerCase();
|
|
860
|
+
const grants = (resource.data?.grants ?? []).filter((grant) => grant.email.trim().toLowerCase() === email);
|
|
861
|
+
const effective = grants.find((grant) => grant.active && !grant.consumedAt && !grant.revokedAt && (!grant.expiresAt || grant.expiresAt > Date.now()));
|
|
862
|
+
const run = async (work) => {
|
|
863
|
+
setBusy(true);
|
|
864
|
+
setError(null);
|
|
865
|
+
try {
|
|
866
|
+
await work();
|
|
867
|
+
setNote("");
|
|
868
|
+
resource.refresh();
|
|
869
|
+
props.onChanged?.();
|
|
870
|
+
} catch (failure) {
|
|
871
|
+
setError(failure instanceof Error ? failure.message : String(failure));
|
|
872
|
+
} finally {
|
|
873
|
+
setBusy(false);
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
const save = () => run(() => adminFetch(props.getToken, "/api/admin/admission-grants", {
|
|
877
|
+
method: "POST",
|
|
878
|
+
body: JSON.stringify({ email: props.email, decision, ...note.trim() ? { note: note.trim() } : {} })
|
|
879
|
+
}));
|
|
880
|
+
const revoke = (grant) => run(() => adminFetch(props.getToken, `/api/admin/admission-grants/${encodeURIComponent(grant.id)}`, { method: "DELETE" }));
|
|
881
|
+
return /* @__PURE__ */ jsxs9("section", { "aria-label": "Invitation", style: { display: "grid", gap: 12 }, children: [
|
|
882
|
+
resource.loading ? /* @__PURE__ */ jsx9("p", { role: "status", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: resource.error }) : effective ? /* @__PURE__ */ jsxs9("p", { style: { margin: 0 }, "data-testid": "effective-invitation", children: [
|
|
883
|
+
/* @__PURE__ */ jsx9("strong", { children: effective.decision === "waive_interview" ? copy.waive : copy.require }),
|
|
884
|
+
" \xB7 ",
|
|
885
|
+
effective.sourceScope === "local" ? copy.local : copy.parent,
|
|
886
|
+
effective.note ? ` \xB7 ${effective.note}` : "",
|
|
887
|
+
effective.sourceScope === "local" ? /* @__PURE__ */ jsxs9(Fragment7, { children: [
|
|
888
|
+
" ",
|
|
889
|
+
/* @__PURE__ */ jsx9(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void revoke(effective), children: copy.revoke })
|
|
890
|
+
] }) : null
|
|
891
|
+
] }) : /* @__PURE__ */ jsxs9("p", { className: "muted", style: { margin: 0 }, children: [
|
|
892
|
+
"No invitation setting for ",
|
|
893
|
+
email,
|
|
894
|
+
". The next signup follows the chapter\u2019s ordinary interview rule."
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsx9(Field2, { label: copy.decision, children: /* @__PURE__ */ jsx9(
|
|
897
|
+
Select2,
|
|
898
|
+
{
|
|
899
|
+
value: decision,
|
|
900
|
+
disabled: busy,
|
|
901
|
+
onChange: (event) => setDecision(event.currentTarget.value),
|
|
902
|
+
options: [{ value: "waive_interview", label: copy.waive }, { value: "require_interview", label: copy.require }]
|
|
903
|
+
}
|
|
904
|
+
) }),
|
|
905
|
+
/* @__PURE__ */ jsx9(Field2, { label: copy.note, children: /* @__PURE__ */ jsx9("input", { value: note, maxLength: 2e3, disabled: busy, onInput: (event) => setNote(event.currentTarget.value) }) }),
|
|
906
|
+
/* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsx9(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void save(), children: copy.add }) }),
|
|
907
|
+
error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: error }) : null
|
|
908
|
+
] });
|
|
909
|
+
}
|
|
910
|
+
|
|
639
911
|
// src/ui/admin-application-repair.tsx
|
|
640
|
-
import { useState as
|
|
641
|
-
import { Button } from "@odla-ai/ui/components";
|
|
642
|
-
import { jsx as
|
|
912
|
+
import { useState as useState9 } from "preact/hooks";
|
|
913
|
+
import { Button as Button6 } from "@odla-ai/ui/components";
|
|
914
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
643
915
|
function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
|
|
644
|
-
const [busy, setBusy] =
|
|
645
|
-
const [error, setError] =
|
|
646
|
-
return /* @__PURE__ */
|
|
647
|
-
/* @__PURE__ */
|
|
648
|
-
/* @__PURE__ */
|
|
916
|
+
const [busy, setBusy] = useState9(false);
|
|
917
|
+
const [error, setError] = useState9(null);
|
|
918
|
+
return /* @__PURE__ */ jsxs10("section", { className: "panel", children: [
|
|
919
|
+
/* @__PURE__ */ jsx10("p", { children: "This person is missing an application link. Repair checks for one matching chapter application; it does not change membership or profile details." }),
|
|
920
|
+
/* @__PURE__ */ jsx10(Button6, { variant: "secondary", disabled: busy, onClick: async () => {
|
|
649
921
|
setBusy(true);
|
|
650
922
|
setError(null);
|
|
651
923
|
try {
|
|
@@ -657,34 +929,34 @@ function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
|
|
|
657
929
|
setBusy(false);
|
|
658
930
|
}
|
|
659
931
|
}, children: busy ? "Checking application link\u2026" : "Repair application link" }),
|
|
660
|
-
error ? /* @__PURE__ */
|
|
932
|
+
error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: error }) : null
|
|
661
933
|
] });
|
|
662
934
|
}
|
|
663
935
|
|
|
664
936
|
// src/ui/admin-application-stage.tsx
|
|
665
|
-
import { useState as
|
|
666
|
-
import { Button as
|
|
667
|
-
import { Fragment as
|
|
937
|
+
import { useState as useState10 } from "preact/hooks";
|
|
938
|
+
import { Button as Button7, Field as Field3, Select as Select3 } from "@odla-ai/ui/components";
|
|
939
|
+
import { Fragment as Fragment8, jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
668
940
|
function ApplicationStagePanel(props) {
|
|
669
941
|
const actions = useAdminResource(
|
|
670
942
|
props.getToken,
|
|
671
943
|
`/api/admin/applications/${encodeURIComponent(props.applicationId)}/actions`
|
|
672
944
|
);
|
|
673
|
-
const [selected, setSelected] =
|
|
674
|
-
const [busy, setBusy] =
|
|
675
|
-
const [error, setError] =
|
|
676
|
-
if (actions.loading) return /* @__PURE__ */
|
|
677
|
-
if (actions.error) return /* @__PURE__ */
|
|
945
|
+
const [selected, setSelected] = useState10("");
|
|
946
|
+
const [busy, setBusy] = useState10(false);
|
|
947
|
+
const [error, setError] = useState10(null);
|
|
948
|
+
if (actions.loading) return /* @__PURE__ */ jsx11("p", { role: "status", children: "Loading available actions\u2026" });
|
|
949
|
+
if (actions.error) return /* @__PURE__ */ jsxs11("p", { role: "alert", children: [
|
|
678
950
|
"Could not load application actions: ",
|
|
679
951
|
actions.error
|
|
680
952
|
] });
|
|
681
953
|
const allowed = actions.data?.allowedStages ?? [];
|
|
682
954
|
const target = allowed.includes(selected) ? selected : "";
|
|
683
|
-
return /* @__PURE__ */
|
|
684
|
-
/* @__PURE__ */
|
|
685
|
-
allowed.length ? /* @__PURE__ */
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
|
|
955
|
+
return /* @__PURE__ */ jsxs11(Field3, { label: "Stage", error, children: [
|
|
956
|
+
/* @__PURE__ */ jsx11("p", { children: actions.data?.status?.replaceAll("_", " ") }),
|
|
957
|
+
allowed.length ? /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
958
|
+
/* @__PURE__ */ jsx11(
|
|
959
|
+
Select3,
|
|
688
960
|
{
|
|
689
961
|
"aria-label": "Move to stage",
|
|
690
962
|
value: target,
|
|
@@ -693,7 +965,7 @@ function ApplicationStagePanel(props) {
|
|
|
693
965
|
options: [{ value: "", label: "Move to\u2026" }, ...allowed.map((value) => ({ value, label: value.replaceAll("_", " ") }))]
|
|
694
966
|
}
|
|
695
967
|
),
|
|
696
|
-
/* @__PURE__ */
|
|
968
|
+
/* @__PURE__ */ jsx11(Button7, { mini: true, disabled: busy || !target, onClick: async () => {
|
|
697
969
|
setBusy(true);
|
|
698
970
|
setError(null);
|
|
699
971
|
try {
|
|
@@ -706,19 +978,19 @@ function ApplicationStagePanel(props) {
|
|
|
706
978
|
actions.refresh();
|
|
707
979
|
}
|
|
708
980
|
}, children: "Move" })
|
|
709
|
-
] }) : /* @__PURE__ */
|
|
981
|
+
] }) : /* @__PURE__ */ jsx11("p", { children: "No stage changes are available under this chapter\u2019s current membership policy." })
|
|
710
982
|
] });
|
|
711
983
|
}
|
|
712
984
|
|
|
713
985
|
// src/ui/operational-profile-form.tsx
|
|
714
|
-
import { Button as
|
|
715
|
-
import { useRef, useState as
|
|
716
|
-
import { jsx as
|
|
986
|
+
import { Button as Button8, Field as Field4, Input, Select as Select4, Textarea } from "@odla-ai/ui/components";
|
|
987
|
+
import { useRef, useState as useState11 } from "preact/hooks";
|
|
988
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
717
989
|
var valueText = (value) => value == null ? "" : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
718
990
|
function OperationalProfileForm(props) {
|
|
719
|
-
const [draft, setDraft] =
|
|
720
|
-
const [busy, setBusy] =
|
|
721
|
-
const [error, setError] =
|
|
991
|
+
const [draft, setDraft] = useState11(() => Object.fromEntries(props.fields.map((field) => [field.id, valueText(props.values[field.id])])));
|
|
992
|
+
const [busy, setBusy] = useState11(false);
|
|
993
|
+
const [error, setError] = useState11(null);
|
|
722
994
|
const pending = useRef(null);
|
|
723
995
|
const changed = props.fields.filter((field) => draft[field.id] !== valueText(props.values[field.id]));
|
|
724
996
|
const save = async () => {
|
|
@@ -746,20 +1018,20 @@ function OperationalProfileForm(props) {
|
|
|
746
1018
|
setBusy(false);
|
|
747
1019
|
}
|
|
748
1020
|
};
|
|
749
|
-
return /* @__PURE__ */
|
|
750
|
-
props.fields.map((field) => /* @__PURE__ */
|
|
751
|
-
|
|
1021
|
+
return /* @__PURE__ */ jsxs12("section", { style: { display: "grid", gap: 16 }, children: [
|
|
1022
|
+
props.fields.map((field) => /* @__PURE__ */ jsx12(Field4, { label: field.label, children: field.options || field.type === "boolean" ? /* @__PURE__ */ jsxs12(
|
|
1023
|
+
Select4,
|
|
752
1024
|
{
|
|
753
1025
|
"aria-label": field.label,
|
|
754
1026
|
value: draft[field.id],
|
|
755
1027
|
disabled: busy,
|
|
756
1028
|
onChange: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value }),
|
|
757
1029
|
children: [
|
|
758
|
-
/* @__PURE__ */
|
|
759
|
-
(field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */
|
|
1030
|
+
/* @__PURE__ */ jsx12("option", { value: "", children: "Not set" }),
|
|
1031
|
+
(field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */ jsx12("option", { value: option, children: option }, option))
|
|
760
1032
|
]
|
|
761
1033
|
}
|
|
762
|
-
) : field.type === "json" ? /* @__PURE__ */
|
|
1034
|
+
) : field.type === "json" ? /* @__PURE__ */ jsx12(
|
|
763
1035
|
Textarea,
|
|
764
1036
|
{
|
|
765
1037
|
"aria-label": field.label,
|
|
@@ -767,7 +1039,7 @@ function OperationalProfileForm(props) {
|
|
|
767
1039
|
disabled: busy,
|
|
768
1040
|
onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
|
|
769
1041
|
}
|
|
770
|
-
) : /* @__PURE__ */
|
|
1042
|
+
) : /* @__PURE__ */ jsx12(
|
|
771
1043
|
Input,
|
|
772
1044
|
{
|
|
773
1045
|
"aria-label": field.label,
|
|
@@ -777,26 +1049,26 @@ function OperationalProfileForm(props) {
|
|
|
777
1049
|
onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
|
|
778
1050
|
}
|
|
779
1051
|
) }, field.id)),
|
|
780
|
-
error ? /* @__PURE__ */
|
|
781
|
-
/* @__PURE__ */
|
|
1052
|
+
error ? /* @__PURE__ */ jsx12("p", { role: "alert", children: error }) : null,
|
|
1053
|
+
/* @__PURE__ */ jsx12("div", { children: /* @__PURE__ */ jsx12(Button8, { disabled: busy || !changed.length, onClick: () => void save(), children: busy ? "Saving\u2026" : "Save profile" }) })
|
|
782
1054
|
] });
|
|
783
1055
|
}
|
|
784
1056
|
|
|
785
1057
|
// src/ui/admin-person-profile.tsx
|
|
786
|
-
import { Fragment as
|
|
1058
|
+
import { Fragment as Fragment9, jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
787
1059
|
function ChapterPersonProfile(props) {
|
|
788
1060
|
const path = `/api/admin/people/profile?${new URLSearchParams({ recordId: props.recordId })}`;
|
|
789
1061
|
const resource = useAdminResource(props.getToken, path);
|
|
790
|
-
if (resource.loading) return /* @__PURE__ */
|
|
791
|
-
if (resource.error) return /* @__PURE__ */
|
|
1062
|
+
if (resource.loading) return /* @__PURE__ */ jsx13("p", { children: "Loading profile\u2026" });
|
|
1063
|
+
if (resource.error) return /* @__PURE__ */ jsx13("p", { role: "alert", children: resource.error });
|
|
792
1064
|
const record = resource.data?.record;
|
|
793
|
-
if (!record?.edit) return /* @__PURE__ */
|
|
794
|
-
return /* @__PURE__ */
|
|
795
|
-
/* @__PURE__ */
|
|
1065
|
+
if (!record?.edit) return /* @__PURE__ */ jsx13("p", { children: "Profile editing is unavailable." });
|
|
1066
|
+
return /* @__PURE__ */ jsxs13(Fragment9, { children: [
|
|
1067
|
+
/* @__PURE__ */ jsxs13("p", { children: [
|
|
796
1068
|
String(record.fields.email ?? ""),
|
|
797
1069
|
" \u2014 login email changes use the verified account workflow."
|
|
798
1070
|
] }),
|
|
799
|
-
/* @__PURE__ */
|
|
1071
|
+
/* @__PURE__ */ jsx13(
|
|
800
1072
|
OperationalProfileForm,
|
|
801
1073
|
{
|
|
802
1074
|
fields: record.edit.fields,
|
|
@@ -813,14 +1085,14 @@ function ChapterPersonProfile(props) {
|
|
|
813
1085
|
}
|
|
814
1086
|
|
|
815
1087
|
// src/ui/admin-account-sync.tsx
|
|
816
|
-
import { useState as
|
|
817
|
-
import { Button as
|
|
818
|
-
import { jsx as
|
|
1088
|
+
import { useState as useState12 } from "preact/hooks";
|
|
1089
|
+
import { Button as Button9 } from "@odla-ai/ui/components";
|
|
1090
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
819
1091
|
function AccountPeopleSync({ getToken, refresh }) {
|
|
820
|
-
const [busy, setBusy] =
|
|
821
|
-
const [message, setMessage] =
|
|
822
|
-
return /* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
1092
|
+
const [busy, setBusy] = useState12(false);
|
|
1093
|
+
const [message, setMessage] = useState12("");
|
|
1094
|
+
return /* @__PURE__ */ jsxs14("div", { children: [
|
|
1095
|
+
/* @__PURE__ */ jsx14(Button9, { mini: true, variant: "secondary", disabled: busy, onClick: async () => {
|
|
824
1096
|
setBusy(true);
|
|
825
1097
|
setMessage("");
|
|
826
1098
|
let after = null, count = 0, errors = 0;
|
|
@@ -844,21 +1116,21 @@ function AccountPeopleSync({ getToken, refresh }) {
|
|
|
844
1116
|
setBusy(false);
|
|
845
1117
|
}
|
|
846
1118
|
}, children: busy ? "Syncing accounts\u2026" : "Sync accounts" }),
|
|
847
|
-
message ? /* @__PURE__ */
|
|
1119
|
+
message ? /* @__PURE__ */ jsx14("p", { role: "status", children: message }) : null
|
|
848
1120
|
] });
|
|
849
1121
|
}
|
|
850
1122
|
|
|
851
1123
|
// src/ui/admin-record-actions.tsx
|
|
852
|
-
import { useEffect as useEffect4, useState as
|
|
853
|
-
import { Button as
|
|
854
|
-
import { jsx as
|
|
1124
|
+
import { useEffect as useEffect4, useState as useState13 } from "preact/hooks";
|
|
1125
|
+
import { Button as Button10, Field as Field5, Select as Select5 } from "@odla-ai/ui/components";
|
|
1126
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
|
|
855
1127
|
function RecordActions({ getToken, record, roles, onChanged }) {
|
|
856
1128
|
const copy = useChapterCopy().admin.records;
|
|
857
1129
|
const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
|
|
858
1130
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
859
|
-
const [access, setAccess] =
|
|
860
|
-
const [busy, setBusy] =
|
|
861
|
-
const [note, setNote] =
|
|
1131
|
+
const [access, setAccess] = useState13(null);
|
|
1132
|
+
const [busy, setBusy] = useState13(null);
|
|
1133
|
+
const [note, setNote] = useState13(null);
|
|
862
1134
|
useEffect4(() => {
|
|
863
1135
|
if (!userId) {
|
|
864
1136
|
setAccess(null);
|
|
@@ -887,34 +1159,34 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
887
1159
|
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }), copy.approved);
|
|
888
1160
|
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
|
|
889
1161
|
if (!userId && !applicationId) return null;
|
|
890
|
-
return /* @__PURE__ */
|
|
891
|
-
/* @__PURE__ */
|
|
892
|
-
userId ? /* @__PURE__ */
|
|
893
|
-
|
|
1162
|
+
return /* @__PURE__ */ jsxs15("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
|
|
1163
|
+
/* @__PURE__ */ jsx15("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
|
|
1164
|
+
userId ? /* @__PURE__ */ jsx15(Field5, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx15(
|
|
1165
|
+
Select5,
|
|
894
1166
|
{
|
|
895
1167
|
value: access?.role ?? "",
|
|
896
1168
|
disabled: busy === "role" || access?.superAdmin,
|
|
897
1169
|
options: roles.map((r) => ({ value: r, label: r })),
|
|
898
1170
|
onChange: (e) => void setRole(e.target.value)
|
|
899
1171
|
}
|
|
900
|
-
) }) : /* @__PURE__ */
|
|
901
|
-
applicationId ? /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
1172
|
+
) }) : /* @__PURE__ */ jsx15("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
|
|
1173
|
+
applicationId ? /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: 8 }, children: [
|
|
1174
|
+
/* @__PURE__ */ jsx15(Button10, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
|
|
1175
|
+
/* @__PURE__ */ jsx15(Button10, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
|
|
904
1176
|
] }) : null,
|
|
905
|
-
note ? /* @__PURE__ */
|
|
1177
|
+
note ? /* @__PURE__ */ jsx15("p", { className: "muted", style: { margin: 0 }, children: note }) : null
|
|
906
1178
|
] });
|
|
907
1179
|
}
|
|
908
1180
|
|
|
909
1181
|
// src/ui/admin-network.tsx
|
|
910
|
-
import { useState as
|
|
911
|
-
import { jsx as
|
|
1182
|
+
import { useState as useState14 } from "preact/hooks";
|
|
1183
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
|
|
912
1184
|
function NetworkShareActions(props) {
|
|
913
1185
|
const copy = useChapterCopy().admin.network;
|
|
914
1186
|
const { recordId, recordType, getToken } = props;
|
|
915
1187
|
const targets = useAdminResource(getToken, "/api/admin/network/targets");
|
|
916
|
-
const [busy, setBusy] =
|
|
917
|
-
const [message, setMessage] =
|
|
1188
|
+
const [busy, setBusy] = useState14(null);
|
|
1189
|
+
const [message, setMessage] = useState14(null);
|
|
918
1190
|
const compatible = (targets.data?.targets ?? []).filter((target) => target.types.includes(recordType));
|
|
919
1191
|
if (targets.loading || targets.error || compatible.length === 0) return null;
|
|
920
1192
|
const push = async (target) => {
|
|
@@ -933,26 +1205,26 @@ function NetworkShareActions(props) {
|
|
|
933
1205
|
setBusy(null);
|
|
934
1206
|
}
|
|
935
1207
|
};
|
|
936
|
-
return /* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
939
|
-
/* @__PURE__ */
|
|
1208
|
+
return /* @__PURE__ */ jsxs16("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
1209
|
+
/* @__PURE__ */ jsxs16("div", { children: [
|
|
1210
|
+
/* @__PURE__ */ jsx16("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
|
|
1211
|
+
/* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
|
|
940
1212
|
] }),
|
|
941
|
-
/* @__PURE__ */
|
|
942
|
-
message ? /* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ jsx16("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx16("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)) }),
|
|
1214
|
+
message ? /* @__PURE__ */ jsx16("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
|
|
943
1215
|
] });
|
|
944
1216
|
}
|
|
945
1217
|
|
|
946
1218
|
// src/ui/admin-record-activity.tsx
|
|
947
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useState as
|
|
1219
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState15 } from "preact/hooks";
|
|
948
1220
|
import { ActivityFeed } from "@odla-ai/crm/ui";
|
|
949
1221
|
import { parseCrmDiscussionReference as parseCrmDiscussionReference2 } from "@odla-ai/crm";
|
|
950
|
-
import { jsx as
|
|
1222
|
+
import { jsx as jsx17 } from "preact/jsx-runtime";
|
|
951
1223
|
function RecordActivityPanel(props) {
|
|
952
1224
|
const { client, recordId } = props;
|
|
953
|
-
const [activities, setActivities] =
|
|
954
|
-
const [busy, setBusy] =
|
|
955
|
-
const [error, setError] =
|
|
1225
|
+
const [activities, setActivities] = useState15([]);
|
|
1226
|
+
const [busy, setBusy] = useState15(false);
|
|
1227
|
+
const [error, setError] = useState15(null);
|
|
956
1228
|
const reference = typeof window === "undefined" ? null : parseCrmDiscussionReference2(new URL(window.location.href));
|
|
957
1229
|
const focusedActivityId = reference?.kind === "crm:activity" && reference.recordId === recordId ? reference.activityId : void 0;
|
|
958
1230
|
const load = useCallback3(async () => {
|
|
@@ -974,8 +1246,8 @@ function RecordActivityPanel(props) {
|
|
|
974
1246
|
setBusy(false);
|
|
975
1247
|
}
|
|
976
1248
|
};
|
|
977
|
-
if (error) return /* @__PURE__ */
|
|
978
|
-
return /* @__PURE__ */
|
|
1249
|
+
if (error) return /* @__PURE__ */ jsx17("p", { role: "alert", className: "muted", children: error });
|
|
1250
|
+
return /* @__PURE__ */ jsx17(
|
|
979
1251
|
ActivityFeed,
|
|
980
1252
|
{
|
|
981
1253
|
activities,
|
|
@@ -989,17 +1261,17 @@ function RecordActivityPanel(props) {
|
|
|
989
1261
|
}
|
|
990
1262
|
|
|
991
1263
|
// src/ui/admin-record-comms.tsx
|
|
992
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useState as
|
|
993
|
-
import { Button as
|
|
994
|
-
import { jsx as
|
|
1264
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState16 } from "preact/hooks";
|
|
1265
|
+
import { Button as Button11, Field as Field6, Select as Select6 } from "@odla-ai/ui/components";
|
|
1266
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "preact/jsx-runtime";
|
|
995
1267
|
function RecordCommsPanel(props) {
|
|
996
1268
|
const copy = useChapterCopy().admin.records;
|
|
997
1269
|
const { client, recordId, getToken } = props;
|
|
998
1270
|
const config = useAdminResource(getToken, "/api/admin/group/email");
|
|
999
1271
|
const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
|
|
1000
|
-
const [template, setTemplate] =
|
|
1001
|
-
const [busy, setBusy] =
|
|
1002
|
-
const [note, setNote] =
|
|
1272
|
+
const [template, setTemplate] = useState16("");
|
|
1273
|
+
const [busy, setBusy] = useState16(false);
|
|
1274
|
+
const [note, setNote] = useState16(null);
|
|
1003
1275
|
const send = async () => {
|
|
1004
1276
|
if (!template) return;
|
|
1005
1277
|
setBusy(true);
|
|
@@ -1013,11 +1285,11 @@ function RecordCommsPanel(props) {
|
|
|
1013
1285
|
setBusy(false);
|
|
1014
1286
|
}
|
|
1015
1287
|
};
|
|
1016
|
-
if (config.loading) return /* @__PURE__ */
|
|
1017
|
-
if (config.error) return /* @__PURE__ */
|
|
1018
|
-
return /* @__PURE__ */
|
|
1019
|
-
/* @__PURE__ */
|
|
1020
|
-
|
|
1288
|
+
if (config.loading) return /* @__PURE__ */ jsx18("p", { className: "muted", children: copy.loadingTemplates });
|
|
1289
|
+
if (config.error) return /* @__PURE__ */ jsx18("p", { role: "alert", className: "muted", children: config.error });
|
|
1290
|
+
return /* @__PURE__ */ jsxs17("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
|
|
1291
|
+
/* @__PURE__ */ jsx18(Field6, { label: copy.template, children: /* @__PURE__ */ jsx18(
|
|
1292
|
+
Select6,
|
|
1021
1293
|
{
|
|
1022
1294
|
value: template,
|
|
1023
1295
|
onChange: (event) => setTemplate(event.currentTarget.value),
|
|
@@ -1027,15 +1299,15 @@ function RecordCommsPanel(props) {
|
|
|
1027
1299
|
]
|
|
1028
1300
|
}
|
|
1029
1301
|
) }),
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
note ? /* @__PURE__ */
|
|
1302
|
+
/* @__PURE__ */ jsx18("div", { children: /* @__PURE__ */ jsx18(Button11, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
|
|
1303
|
+
note ? /* @__PURE__ */ jsx18("p", { role: "status", className: "muted", children: note }) : null
|
|
1032
1304
|
] });
|
|
1033
1305
|
}
|
|
1034
1306
|
function RecordCommsHistory(props) {
|
|
1035
1307
|
const copy = useChapterCopy().admin.records;
|
|
1036
1308
|
const { client, recordId } = props;
|
|
1037
|
-
const [rows, setRows] =
|
|
1038
|
-
const [error, setError] =
|
|
1309
|
+
const [rows, setRows] = useState16([]);
|
|
1310
|
+
const [error, setError] = useState16(null);
|
|
1039
1311
|
const load = useCallback4(async () => {
|
|
1040
1312
|
try {
|
|
1041
1313
|
const result = await client.emailLog({ recordId, limit: 100 });
|
|
@@ -1046,11 +1318,11 @@ function RecordCommsHistory(props) {
|
|
|
1046
1318
|
}
|
|
1047
1319
|
}, [client, recordId]);
|
|
1048
1320
|
useEffect6(() => void load(), [load]);
|
|
1049
|
-
if (error) return /* @__PURE__ */
|
|
1050
|
-
if (rows.length === 0) return /* @__PURE__ */
|
|
1051
|
-
return /* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
/* @__PURE__ */
|
|
1321
|
+
if (error) return /* @__PURE__ */ jsx18("p", { role: "alert", className: "muted", children: error });
|
|
1322
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx18("p", { className: "muted", children: copy.noMessages });
|
|
1323
|
+
return /* @__PURE__ */ jsx18("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs17("li", { className: "panel", children: [
|
|
1324
|
+
/* @__PURE__ */ jsx18("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
|
|
1325
|
+
/* @__PURE__ */ jsxs17("div", { className: "muted", children: [
|
|
1054
1326
|
row["to"] ? String(row["to"]) : "",
|
|
1055
1327
|
row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
|
|
1056
1328
|
] })
|
|
@@ -1058,9 +1330,9 @@ function RecordCommsHistory(props) {
|
|
|
1058
1330
|
}
|
|
1059
1331
|
|
|
1060
1332
|
// src/ui/admin-record-scheduling.tsx
|
|
1061
|
-
import { useState as
|
|
1062
|
-
import { Button as
|
|
1063
|
-
import { Fragment as
|
|
1333
|
+
import { useState as useState17 } from "preact/hooks";
|
|
1334
|
+
import { Button as Button12 } from "@odla-ai/ui/components";
|
|
1335
|
+
import { Fragment as Fragment10, jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
|
|
1064
1336
|
function RecordSchedulingPanel(props) {
|
|
1065
1337
|
const copy = useChapterCopy().admin.records;
|
|
1066
1338
|
const { getToken, applicationId } = props;
|
|
@@ -1068,8 +1340,8 @@ function RecordSchedulingPanel(props) {
|
|
|
1068
1340
|
getToken,
|
|
1069
1341
|
"/api/admin/meetings?all=1"
|
|
1070
1342
|
);
|
|
1071
|
-
const [busy, setBusy] =
|
|
1072
|
-
const [note, setNote] =
|
|
1343
|
+
const [busy, setBusy] = useState17(null);
|
|
1344
|
+
const [note, setNote] = useState17(null);
|
|
1073
1345
|
const meetings = (resource.data?.meetings ?? []).filter((meeting) => meeting.applicationId === applicationId);
|
|
1074
1346
|
const run = async (meeting, action) => {
|
|
1075
1347
|
let body;
|
|
@@ -1096,54 +1368,33 @@ function RecordSchedulingPanel(props) {
|
|
|
1096
1368
|
setBusy(null);
|
|
1097
1369
|
}
|
|
1098
1370
|
};
|
|
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__ */
|
|
1371
|
+
if (!applicationId) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.noApplication });
|
|
1372
|
+
if (resource.loading) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.loadingMeetings });
|
|
1373
|
+
if (resource.error) return /* @__PURE__ */ jsx19("p", { role: "alert", className: "muted", children: resource.error });
|
|
1374
|
+
if (meetings.length === 0) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.noMeetings });
|
|
1375
|
+
return /* @__PURE__ */ jsxs18("div", { style: { display: "grid", gap: 12 }, children: [
|
|
1376
|
+
meetings.map((meeting) => /* @__PURE__ */ jsxs18("section", { className: "panel", children: [
|
|
1377
|
+
/* @__PURE__ */ jsx19("strong", { children: new Date(meeting.startAt).toLocaleString() }),
|
|
1378
|
+
/* @__PURE__ */ jsxs18("p", { className: "muted", children: [
|
|
1107
1379
|
meeting.status,
|
|
1108
1380
|
" \xB7 ",
|
|
1109
1381
|
resource.data?.timezone
|
|
1110
1382
|
] }),
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
meeting.meetUrl ? /* @__PURE__ */
|
|
1113
|
-
meeting.htmlLink ? /* @__PURE__ */
|
|
1114
|
-
meeting.status === "scheduled" ? /* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ jsxs18("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
|
|
1384
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx19("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
|
|
1385
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx19("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
|
|
1386
|
+
meeting.status === "scheduled" ? /* @__PURE__ */ jsxs18(Fragment10, { children: [
|
|
1387
|
+
/* @__PURE__ */ jsx19(Button12, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
|
|
1388
|
+
/* @__PURE__ */ jsx19(Button12, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
|
|
1117
1389
|
] }) : null
|
|
1118
1390
|
] })
|
|
1119
1391
|
] }, meeting.id)),
|
|
1120
|
-
note ? /* @__PURE__ */
|
|
1392
|
+
note ? /* @__PURE__ */ jsx19("p", { role: "status", className: "muted", children: note }) : null
|
|
1121
1393
|
] });
|
|
1122
1394
|
}
|
|
1123
1395
|
|
|
1124
1396
|
// src/ui/admin-people-workspace.tsx
|
|
1125
|
-
import { Fragment as
|
|
1126
|
-
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
1127
|
-
return {
|
|
1128
|
-
transitionStage: async ({ record, to }) => {
|
|
1129
|
-
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
1130
|
-
if (!applicationId) {
|
|
1131
|
-
throw new Error(copy.workflowMissing);
|
|
1132
|
-
}
|
|
1133
|
-
const path = `/api/admin/applications/${encodeURIComponent(applicationId)}`;
|
|
1134
|
-
if (to === "approved") {
|
|
1135
|
-
await adminFetch(ctx.getToken, `${path}/approve`, { method: "POST" });
|
|
1136
|
-
} else if (to === "refunded") {
|
|
1137
|
-
await adminFetch(ctx.getToken, `${path}/refund`, { method: "POST" });
|
|
1138
|
-
} else {
|
|
1139
|
-
await adminFetch(ctx.getToken, path, {
|
|
1140
|
-
method: "PATCH",
|
|
1141
|
-
body: JSON.stringify({ status: to })
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
};
|
|
1146
|
-
}
|
|
1397
|
+
import { Fragment as Fragment11, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
|
|
1147
1398
|
function PeopleBody(props) {
|
|
1148
1399
|
const copy = useChapterCopy().admin.records;
|
|
1149
1400
|
const {
|
|
@@ -1154,6 +1405,7 @@ function PeopleBody(props) {
|
|
|
1154
1405
|
roles,
|
|
1155
1406
|
lifecycle,
|
|
1156
1407
|
networkSharing,
|
|
1408
|
+
applicationStages = APPLICATION_STAGES,
|
|
1157
1409
|
collapseClosedDetail,
|
|
1158
1410
|
lifecycleAdapter,
|
|
1159
1411
|
requireLifecycleAdapter,
|
|
@@ -1171,7 +1423,7 @@ function PeopleBody(props) {
|
|
|
1171
1423
|
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
|
|
1172
1424
|
const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
|
|
1173
1425
|
const workspaceClient = client;
|
|
1174
|
-
return /* @__PURE__ */
|
|
1426
|
+
return /* @__PURE__ */ jsx20(AdminPage, { children: /* @__PURE__ */ jsx20(
|
|
1175
1427
|
CrmWorkspace,
|
|
1176
1428
|
{
|
|
1177
1429
|
crm,
|
|
@@ -1187,9 +1439,9 @@ function PeopleBody(props) {
|
|
|
1187
1439
|
collapseClosedDetail,
|
|
1188
1440
|
lifecycle: resolvedLifecycle,
|
|
1189
1441
|
requireLifecycleAdapter: requireLifecycleAdapter ?? lifecycle,
|
|
1190
|
-
renderSummary: (context) => /* @__PURE__ */
|
|
1442
|
+
renderSummary: (context) => /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1191
1443
|
renderSummary?.(context, ctx),
|
|
1192
|
-
type === "person" ? /* @__PURE__ */
|
|
1444
|
+
type === "person" ? /* @__PURE__ */ jsx20(AccountPeopleSync, { getToken: ctx.getToken, refresh: context.query.refresh }) : null
|
|
1193
1445
|
] }),
|
|
1194
1446
|
renderMaster: renderMaster ? (context) => renderMaster(context, ctx) : void 0,
|
|
1195
1447
|
renderDetailHeader: renderDetailHeader ? (context) => renderDetailHeader(context, ctx) : void 0,
|
|
@@ -1203,7 +1455,7 @@ function PeopleBody(props) {
|
|
|
1203
1455
|
const add = (id) => {
|
|
1204
1456
|
const tab = byId.get(id);
|
|
1205
1457
|
if (tab && lifecycle && type === "person" && id === "profile" && !lifecycleAdapter) {
|
|
1206
|
-
tabs.push({ ...tab, panel: /* @__PURE__ */
|
|
1458
|
+
tabs.push({ ...tab, panel: /* @__PURE__ */ jsx20(
|
|
1207
1459
|
ChapterPersonProfile,
|
|
1208
1460
|
{
|
|
1209
1461
|
recordId: record.id,
|
|
@@ -1218,7 +1470,7 @@ function PeopleBody(props) {
|
|
|
1218
1470
|
return;
|
|
1219
1471
|
}
|
|
1220
1472
|
if (tab && lifecycle && applicationId && id === "stage" && !lifecycleAdapter) {
|
|
1221
|
-
tabs.push({ ...tab, panel: /* @__PURE__ */
|
|
1473
|
+
tabs.push({ ...tab, panel: /* @__PURE__ */ jsx20(
|
|
1222
1474
|
ApplicationStagePanel,
|
|
1223
1475
|
{
|
|
1224
1476
|
applicationId,
|
|
@@ -1231,8 +1483,17 @@ function PeopleBody(props) {
|
|
|
1231
1483
|
}
|
|
1232
1484
|
if (tab) tabs.push(lifecycle && !applicationId && id === "stage" ? {
|
|
1233
1485
|
...tab,
|
|
1234
|
-
panel: record.stage == null
|
|
1235
|
-
|
|
1486
|
+
panel: record.stage == null || !applicationStages.includes(record.stage) ? /* @__PURE__ */ jsx20(
|
|
1487
|
+
CandidateStagePanel,
|
|
1488
|
+
{
|
|
1489
|
+
stages: def.pipeline?.stages ?? [],
|
|
1490
|
+
current: record.stage ?? null,
|
|
1491
|
+
applicationStages,
|
|
1492
|
+
onMove: context.moveStage
|
|
1493
|
+
},
|
|
1494
|
+
record.id + record.updatedAt
|
|
1495
|
+
) : /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1496
|
+
/* @__PURE__ */ jsx20(ApplicationLinkRepair, { recordId: record.id, getToken: ctx.getToken, onChanged: () => {
|
|
1236
1497
|
context.refreshRecord();
|
|
1237
1498
|
context.refreshList();
|
|
1238
1499
|
} }, record.id),
|
|
@@ -1246,35 +1507,60 @@ function PeopleBody(props) {
|
|
|
1246
1507
|
tabs.push({
|
|
1247
1508
|
id: "comms",
|
|
1248
1509
|
label: copy.comms,
|
|
1249
|
-
panel: /* @__PURE__ */
|
|
1510
|
+
panel: /* @__PURE__ */ jsx20(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
|
|
1250
1511
|
});
|
|
1251
1512
|
tabs.push({
|
|
1252
1513
|
id: "history",
|
|
1253
1514
|
label: copy.commsHistory,
|
|
1254
|
-
panel: /* @__PURE__ */
|
|
1515
|
+
panel: /* @__PURE__ */ jsx20(RecordCommsHistory, { client, recordId: record.id })
|
|
1255
1516
|
});
|
|
1256
1517
|
}
|
|
1257
1518
|
if (lifecycle) {
|
|
1258
1519
|
tabs.push({
|
|
1259
1520
|
id: "scheduling",
|
|
1260
1521
|
label: copy.scheduling,
|
|
1261
|
-
panel: /* @__PURE__ */
|
|
1522
|
+
panel: /* @__PURE__ */ jsx20(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
|
|
1262
1523
|
});
|
|
1263
|
-
tabs.push({ id: "
|
|
1524
|
+
tabs.push({ id: "invitation", label: copy.invitation, panel: /* @__PURE__ */ jsx20(
|
|
1525
|
+
RecordAdmissionPanel,
|
|
1526
|
+
{
|
|
1527
|
+
getToken: ctx.getToken,
|
|
1528
|
+
email: record.primaryEmail ?? (typeof record.fields?.email === "string" ? record.fields.email : null),
|
|
1529
|
+
onChanged: () => {
|
|
1530
|
+
context.refreshRecord();
|
|
1531
|
+
context.refreshList();
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
) });
|
|
1535
|
+
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1536
|
+
/* @__PURE__ */ jsx20(BillingCard, { record }),
|
|
1537
|
+
applicationId ? /* @__PURE__ */ jsx20(
|
|
1538
|
+
NamedSeatRecordPanel,
|
|
1539
|
+
{
|
|
1540
|
+
getToken: ctx.getToken,
|
|
1541
|
+
applicationId,
|
|
1542
|
+
superAdmin: ctx.currentUser?.superAdmin === true,
|
|
1543
|
+
onChanged: () => {
|
|
1544
|
+
context.refreshRecord();
|
|
1545
|
+
context.refreshList();
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
) : null
|
|
1549
|
+
] }) });
|
|
1264
1550
|
} else {
|
|
1265
1551
|
add("billing");
|
|
1266
1552
|
}
|
|
1267
1553
|
tabs.push({
|
|
1268
1554
|
id: "notes",
|
|
1269
1555
|
label: copy.notes,
|
|
1270
|
-
panel: /* @__PURE__ */
|
|
1556
|
+
panel: /* @__PURE__ */ jsx20(RecordActivityPanel, { client, recordId: record.id })
|
|
1271
1557
|
});
|
|
1272
1558
|
add("connections");
|
|
1273
1559
|
if (lifecycle) {
|
|
1274
1560
|
tabs.push({
|
|
1275
1561
|
id: "access",
|
|
1276
1562
|
label: copy.access,
|
|
1277
|
-
panel: /* @__PURE__ */
|
|
1563
|
+
panel: /* @__PURE__ */ jsx20(
|
|
1278
1564
|
RecordActions,
|
|
1279
1565
|
{
|
|
1280
1566
|
getToken: ctx.getToken,
|
|
@@ -1293,7 +1579,7 @@ function PeopleBody(props) {
|
|
|
1293
1579
|
tabs.push({
|
|
1294
1580
|
id: "sharing",
|
|
1295
1581
|
label: copy.sharing,
|
|
1296
|
-
panel: /* @__PURE__ */
|
|
1582
|
+
panel: /* @__PURE__ */ jsx20(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
|
|
1297
1583
|
});
|
|
1298
1584
|
}
|
|
1299
1585
|
return extendRecordTabs?.(tabs, context) ?? tabs;
|
|
@@ -1303,7 +1589,7 @@ function PeopleBody(props) {
|
|
|
1303
1589
|
}
|
|
1304
1590
|
|
|
1305
1591
|
// src/ui/admin-people.tsx
|
|
1306
|
-
import { jsx as
|
|
1592
|
+
import { jsx as jsx21 } from "preact/jsx-runtime";
|
|
1307
1593
|
var DEFAULT_ROLES = ["provisional", "member", "admin"];
|
|
1308
1594
|
function collectionSection(options) {
|
|
1309
1595
|
const { crm, type } = options;
|
|
@@ -1318,6 +1604,7 @@ function collectionSection(options) {
|
|
|
1318
1604
|
lifecycle = type === "person",
|
|
1319
1605
|
roles = DEFAULT_ROLES,
|
|
1320
1606
|
networkSharing = false,
|
|
1607
|
+
applicationStages,
|
|
1321
1608
|
collapseClosedDetail = true,
|
|
1322
1609
|
lifecycleAdapter,
|
|
1323
1610
|
requireLifecycleAdapter,
|
|
@@ -1333,7 +1620,7 @@ function collectionSection(options) {
|
|
|
1333
1620
|
return {
|
|
1334
1621
|
id,
|
|
1335
1622
|
label,
|
|
1336
|
-
render: (ctx) => /* @__PURE__ */
|
|
1623
|
+
render: (ctx) => /* @__PURE__ */ jsx21(
|
|
1337
1624
|
PeopleBody,
|
|
1338
1625
|
{
|
|
1339
1626
|
crm,
|
|
@@ -1343,6 +1630,7 @@ function collectionSection(options) {
|
|
|
1343
1630
|
roles,
|
|
1344
1631
|
lifecycle,
|
|
1345
1632
|
networkSharing,
|
|
1633
|
+
applicationStages,
|
|
1346
1634
|
collapseClosedDetail,
|
|
1347
1635
|
lifecycleAdapter,
|
|
1348
1636
|
requireLifecycleAdapter,
|
|
@@ -1402,17 +1690,17 @@ function peopleSection(options) {
|
|
|
1402
1690
|
// src/ui/admin-network-records.tsx
|
|
1403
1691
|
import {
|
|
1404
1692
|
DataTable as DataTable2,
|
|
1405
|
-
Field as
|
|
1693
|
+
Field as Field8,
|
|
1406
1694
|
MasterDetail,
|
|
1407
1695
|
Pagination,
|
|
1408
|
-
Select as
|
|
1696
|
+
Select as Select7
|
|
1409
1697
|
} from "@odla-ai/ui/components";
|
|
1410
|
-
import { useEffect as useEffect7, useMemo, useState as
|
|
1698
|
+
import { useEffect as useEffect7, useMemo, useState as useState19 } from "preact/hooks";
|
|
1411
1699
|
|
|
1412
1700
|
// src/ui/admin-network-notes.tsx
|
|
1413
|
-
import { Button as
|
|
1414
|
-
import { useState as
|
|
1415
|
-
import { jsx as
|
|
1701
|
+
import { Button as Button13, Field as Field7, Textarea as Textarea2 } from "@odla-ai/ui/components";
|
|
1702
|
+
import { useState as useState18 } from "preact/hooks";
|
|
1703
|
+
import { jsx as jsx22, jsxs as jsxs20 } from "preact/jsx-runtime";
|
|
1416
1704
|
function displayValue(value) {
|
|
1417
1705
|
if (Array.isArray(value)) return value.join(", ");
|
|
1418
1706
|
if (value && typeof value === "object") return JSON.stringify(value);
|
|
@@ -1427,9 +1715,9 @@ function PrivateNotes(props) {
|
|
|
1427
1715
|
});
|
|
1428
1716
|
const path = `/api/admin/network/notes?${query}`;
|
|
1429
1717
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1430
|
-
const [body, setBody] =
|
|
1431
|
-
const [busy, setBusy] =
|
|
1432
|
-
const [error, setError] =
|
|
1718
|
+
const [body, setBody] = useState18("");
|
|
1719
|
+
const [busy, setBusy] = useState18(false);
|
|
1720
|
+
const [error, setError] = useState18(null);
|
|
1433
1721
|
const submit = async () => {
|
|
1434
1722
|
const trimmed = body.trim();
|
|
1435
1723
|
if (!trimmed) return;
|
|
@@ -1448,18 +1736,18 @@ function PrivateNotes(props) {
|
|
|
1448
1736
|
setBusy(false);
|
|
1449
1737
|
}
|
|
1450
1738
|
};
|
|
1451
|
-
return /* @__PURE__ */
|
|
1452
|
-
/* @__PURE__ */
|
|
1739
|
+
return /* @__PURE__ */ jsxs20("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1740
|
+
/* @__PURE__ */ jsxs20("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1453
1741
|
"Private to ",
|
|
1454
1742
|
leaderName
|
|
1455
1743
|
] }),
|
|
1456
|
-
/* @__PURE__ */
|
|
1457
|
-
|
|
1744
|
+
/* @__PURE__ */ jsx22(
|
|
1745
|
+
Field7,
|
|
1458
1746
|
{
|
|
1459
1747
|
label: "Add a note",
|
|
1460
1748
|
hint: `Visible only to ${leaderName} admins. Nothing is written back to the follower.`,
|
|
1461
1749
|
error,
|
|
1462
|
-
children: /* @__PURE__ */
|
|
1750
|
+
children: /* @__PURE__ */ jsx22(
|
|
1463
1751
|
Textarea2,
|
|
1464
1752
|
{
|
|
1465
1753
|
rows: 4,
|
|
@@ -1470,13 +1758,13 @@ function PrivateNotes(props) {
|
|
|
1470
1758
|
)
|
|
1471
1759
|
}
|
|
1472
1760
|
),
|
|
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__ */
|
|
1761
|
+
/* @__PURE__ */ jsx22("div", { children: /* @__PURE__ */ jsx22(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save note" }) }),
|
|
1762
|
+
notes.loading ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "Loading notes\u2026" }) : null,
|
|
1763
|
+
notes.error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: notes.error }) : null,
|
|
1764
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "No notes yet." }) : null,
|
|
1765
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs20("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1766
|
+
/* @__PURE__ */ jsx22("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1767
|
+
/* @__PURE__ */ jsxs20("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1480
1768
|
note.authorEmail ?? note.authorId,
|
|
1481
1769
|
" \xB7 ",
|
|
1482
1770
|
new Date(note.createdAt).toLocaleString()
|
|
@@ -1493,9 +1781,9 @@ function SharedNotes(props) {
|
|
|
1493
1781
|
});
|
|
1494
1782
|
const path = `/api/admin/network/shared-notes?${query}`;
|
|
1495
1783
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1496
|
-
const [body, setBody] =
|
|
1497
|
-
const [busy, setBusy] =
|
|
1498
|
-
const [error, setError] =
|
|
1784
|
+
const [body, setBody] = useState18("");
|
|
1785
|
+
const [busy, setBusy] = useState18(false);
|
|
1786
|
+
const [error, setError] = useState18(null);
|
|
1499
1787
|
const submit = async () => {
|
|
1500
1788
|
const trimmed = body.trim();
|
|
1501
1789
|
if (!trimmed) return;
|
|
@@ -1514,18 +1802,18 @@ function SharedNotes(props) {
|
|
|
1514
1802
|
setBusy(false);
|
|
1515
1803
|
}
|
|
1516
1804
|
};
|
|
1517
|
-
return /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1805
|
+
return /* @__PURE__ */ jsxs20("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1806
|
+
/* @__PURE__ */ jsxs20("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1519
1807
|
"Shared with ",
|
|
1520
1808
|
targetName
|
|
1521
1809
|
] }),
|
|
1522
|
-
/* @__PURE__ */
|
|
1523
|
-
|
|
1810
|
+
/* @__PURE__ */ jsx22(
|
|
1811
|
+
Field7,
|
|
1524
1812
|
{
|
|
1525
1813
|
label: "Add a shared note",
|
|
1526
1814
|
hint: `This is written to the record's Notes feed for ${targetName} admins.`,
|
|
1527
1815
|
error,
|
|
1528
|
-
children: /* @__PURE__ */
|
|
1816
|
+
children: /* @__PURE__ */ jsx22(
|
|
1529
1817
|
Textarea2,
|
|
1530
1818
|
{
|
|
1531
1819
|
rows: 4,
|
|
@@ -1536,13 +1824,13 @@ function SharedNotes(props) {
|
|
|
1536
1824
|
)
|
|
1537
1825
|
}
|
|
1538
1826
|
),
|
|
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__ */
|
|
1827
|
+
/* @__PURE__ */ jsx22("div", { children: /* @__PURE__ */ jsx22(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Sharing\u2026" : `Share with ${targetName}` }) }),
|
|
1828
|
+
notes.loading ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "Loading shared notes\u2026" }) : null,
|
|
1829
|
+
notes.error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: notes.error }) : null,
|
|
1830
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "No shared notes yet." }) : null,
|
|
1831
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs20("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1832
|
+
/* @__PURE__ */ jsx22("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1833
|
+
/* @__PURE__ */ jsxs20("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1546
1834
|
"Shared with ",
|
|
1547
1835
|
targetName,
|
|
1548
1836
|
" \xB7 ",
|
|
@@ -1553,23 +1841,23 @@ function SharedNotes(props) {
|
|
|
1553
1841
|
}
|
|
1554
1842
|
function NetworkRecordDetail(props) {
|
|
1555
1843
|
const { ctx, targetId, targetName, leaderName, sharedEnabled, record } = props;
|
|
1556
|
-
return /* @__PURE__ */
|
|
1557
|
-
/* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
record.stage ? /* @__PURE__ */
|
|
1844
|
+
return /* @__PURE__ */ jsxs20("div", { style: { display: "grid", gap: 14 }, children: [
|
|
1845
|
+
/* @__PURE__ */ jsxs20("header", { children: [
|
|
1846
|
+
/* @__PURE__ */ jsx22("h2", { style: { margin: 0 }, children: record.name }),
|
|
1847
|
+
record.stage ? /* @__PURE__ */ jsx22("span", { className: "badge", style: { marginTop: 8 }, children: record.stage.replaceAll("_", " ") }) : null
|
|
1560
1848
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1564
|
-
/* @__PURE__ */
|
|
1849
|
+
/* @__PURE__ */ jsxs20("dl", { style: { display: "grid", gridTemplateColumns: "minmax(8rem, auto) 1fr", gap: "8px 16px", margin: 0 }, children: [
|
|
1850
|
+
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */ jsxs20("div", { style: { display: "contents" }, children: [
|
|
1851
|
+
/* @__PURE__ */ jsx22("dt", { className: "muted", children: name.replaceAll("_", " ") }),
|
|
1852
|
+
/* @__PURE__ */ jsx22("dd", { style: { margin: 0, overflowWrap: "anywhere" }, children: displayValue(value) })
|
|
1565
1853
|
] }, name)),
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1854
|
+
/* @__PURE__ */ jsxs20("div", { style: { display: "contents" }, children: [
|
|
1855
|
+
/* @__PURE__ */ jsx22("dt", { className: "muted", children: "Updated" }),
|
|
1856
|
+
/* @__PURE__ */ jsx22("dd", { style: { margin: 0 }, children: new Date(record.updatedAt).toLocaleString() })
|
|
1569
1857
|
] })
|
|
1570
1858
|
] }),
|
|
1571
|
-
/* @__PURE__ */
|
|
1572
|
-
/* @__PURE__ */
|
|
1859
|
+
/* @__PURE__ */ jsxs20("div", { style: { display: "grid", gap: 22 }, children: [
|
|
1860
|
+
/* @__PURE__ */ jsx22(
|
|
1573
1861
|
PrivateNotes,
|
|
1574
1862
|
{
|
|
1575
1863
|
ctx,
|
|
@@ -1578,7 +1866,7 @@ function NetworkRecordDetail(props) {
|
|
|
1578
1866
|
leaderName
|
|
1579
1867
|
}
|
|
1580
1868
|
),
|
|
1581
|
-
sharedEnabled ? /* @__PURE__ */
|
|
1869
|
+
sharedEnabled ? /* @__PURE__ */ jsx22(
|
|
1582
1870
|
SharedNotes,
|
|
1583
1871
|
{
|
|
1584
1872
|
ctx,
|
|
@@ -1592,11 +1880,11 @@ function NetworkRecordDetail(props) {
|
|
|
1592
1880
|
}
|
|
1593
1881
|
|
|
1594
1882
|
// src/ui/admin-network-records.tsx
|
|
1595
|
-
import { jsx as
|
|
1883
|
+
import { jsx as jsx23, jsxs as jsxs21 } from "preact/jsx-runtime";
|
|
1596
1884
|
function RecordsExplorer(props) {
|
|
1597
1885
|
const { ctx, target, type, leaderName } = props;
|
|
1598
|
-
const [offset, setOffset] =
|
|
1599
|
-
const [search, setSearch] =
|
|
1886
|
+
const [offset, setOffset] = useState19(0);
|
|
1887
|
+
const [search, setSearch] = useState19("");
|
|
1600
1888
|
const query = new URLSearchParams({
|
|
1601
1889
|
targetId: target.id,
|
|
1602
1890
|
type,
|
|
@@ -1609,16 +1897,16 @@ function RecordsExplorer(props) {
|
|
|
1609
1897
|
`/api/admin/network/records?${query}`
|
|
1610
1898
|
);
|
|
1611
1899
|
const selected = page.data?.records.find((record) => record.id === ctx.route.recordId);
|
|
1612
|
-
const
|
|
1900
|
+
const columns3 = useMemo(() => [
|
|
1613
1901
|
{
|
|
1614
1902
|
key: "name",
|
|
1615
1903
|
header: "Name",
|
|
1616
|
-
cell: (record) => /* @__PURE__ */
|
|
1904
|
+
cell: (record) => /* @__PURE__ */ jsx23("a", { href: ctx.href({ recordId: record.id, detailTab: null }), children: record.name })
|
|
1617
1905
|
},
|
|
1618
1906
|
{
|
|
1619
1907
|
key: "stage",
|
|
1620
1908
|
header: "Stage",
|
|
1621
|
-
cell: (record) => record.stage ? /* @__PURE__ */
|
|
1909
|
+
cell: (record) => record.stage ? /* @__PURE__ */ jsx23("span", { className: "badge", children: record.stage.replaceAll("_", " ") }) : "\u2014"
|
|
1622
1910
|
},
|
|
1623
1911
|
{
|
|
1624
1912
|
key: "updatedAt",
|
|
@@ -1626,7 +1914,7 @@ function RecordsExplorer(props) {
|
|
|
1626
1914
|
cell: (record) => new Date(record.updatedAt).toLocaleDateString()
|
|
1627
1915
|
}
|
|
1628
1916
|
], [ctx]);
|
|
1629
|
-
return /* @__PURE__ */
|
|
1917
|
+
return /* @__PURE__ */ jsx23(
|
|
1630
1918
|
MasterDetail,
|
|
1631
1919
|
{
|
|
1632
1920
|
collapseClosedDetail: true,
|
|
@@ -1634,13 +1922,13 @@ function RecordsExplorer(props) {
|
|
|
1634
1922
|
detailLabel: "Follower record details, private notes, and shared notes",
|
|
1635
1923
|
detailOpen: Boolean(selected),
|
|
1636
1924
|
closeHref: ctx.href({ recordId: null, detailTab: null }),
|
|
1637
|
-
master: /* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1925
|
+
master: /* @__PURE__ */ jsxs21("div", { style: { display: "grid", gap: 14, padding: "var(--ui-space-4)" }, children: [
|
|
1926
|
+
/* @__PURE__ */ jsxs21("h2", { style: { margin: 0, fontSize: 18 }, children: [
|
|
1639
1927
|
target.name,
|
|
1640
1928
|
" \xB7 ",
|
|
1641
1929
|
type.replaceAll("_", " ")
|
|
1642
1930
|
] }),
|
|
1643
|
-
/* @__PURE__ */
|
|
1931
|
+
/* @__PURE__ */ jsx23(
|
|
1644
1932
|
DataTable2,
|
|
1645
1933
|
{
|
|
1646
1934
|
manual: true,
|
|
@@ -1653,14 +1941,14 @@ function RecordsExplorer(props) {
|
|
|
1653
1941
|
ctx.navigate({ recordId: null, detailTab: null });
|
|
1654
1942
|
},
|
|
1655
1943
|
rows: page.data?.records ?? [],
|
|
1656
|
-
columns:
|
|
1944
|
+
columns: columns3,
|
|
1657
1945
|
rowKey: (record) => record.id,
|
|
1658
1946
|
loading: page.loading,
|
|
1659
1947
|
emptyState: page.error ? `Could not load records: ${page.error}` : "No records found.",
|
|
1660
1948
|
ariaLabel: `${target.name} ${type} records`
|
|
1661
1949
|
}
|
|
1662
1950
|
),
|
|
1663
|
-
/* @__PURE__ */
|
|
1951
|
+
/* @__PURE__ */ jsx23(
|
|
1664
1952
|
Pagination,
|
|
1665
1953
|
{
|
|
1666
1954
|
offset: page.data?.offset ?? offset,
|
|
@@ -1675,7 +1963,7 @@ function RecordsExplorer(props) {
|
|
|
1675
1963
|
}
|
|
1676
1964
|
)
|
|
1677
1965
|
] }),
|
|
1678
|
-
detail: selected ? /* @__PURE__ */
|
|
1966
|
+
detail: selected ? /* @__PURE__ */ jsx23(
|
|
1679
1967
|
NetworkRecordDetail,
|
|
1680
1968
|
{
|
|
1681
1969
|
ctx,
|
|
@@ -1692,8 +1980,8 @@ function RecordsExplorer(props) {
|
|
|
1692
1980
|
function NetworkRecordsWorkspace(props) {
|
|
1693
1981
|
const { chapter, ctx } = props;
|
|
1694
1982
|
const targets = useAdminResource(ctx.getToken, "/api/admin/network/targets");
|
|
1695
|
-
const [targetId, setTargetId] =
|
|
1696
|
-
const [type, setType] =
|
|
1983
|
+
const [targetId, setTargetId] = useState19("");
|
|
1984
|
+
const [type, setType] = useState19("");
|
|
1697
1985
|
const target = targets.data?.targets.find((candidate) => candidate.id === targetId) ?? targets.data?.targets[0];
|
|
1698
1986
|
useEffect7(() => {
|
|
1699
1987
|
if (target && target.id !== targetId) setTargetId(target.id);
|
|
@@ -1702,17 +1990,17 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1702
1990
|
const first = target?.types[0] ?? "";
|
|
1703
1991
|
if (target && !target.types.includes(type)) setType(first);
|
|
1704
1992
|
}, [target, type]);
|
|
1705
|
-
if (targets.loading) return /* @__PURE__ */
|
|
1706
|
-
if (targets.error || !targets.data) return /* @__PURE__ */
|
|
1993
|
+
if (targets.loading) return /* @__PURE__ */ jsx23(AdminNote, { children: "Loading follower records\u2026" });
|
|
1994
|
+
if (targets.error || !targets.data) return /* @__PURE__ */ jsxs21(AdminNote, { children: [
|
|
1707
1995
|
"Could not load followers",
|
|
1708
1996
|
targets.error ? `: ${targets.error}` : "",
|
|
1709
1997
|
"."
|
|
1710
1998
|
] });
|
|
1711
|
-
if (!target) return /* @__PURE__ */
|
|
1712
|
-
return /* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
|
|
1999
|
+
if (!target) return /* @__PURE__ */ jsx23(AdminPage, { children: /* @__PURE__ */ jsx23(Panel, { title: "Follower records", children: /* @__PURE__ */ jsx23("p", { className: "muted", children: "No followers are configured." }) }) });
|
|
2000
|
+
return /* @__PURE__ */ jsxs21(AdminPage, { children: [
|
|
2001
|
+
/* @__PURE__ */ jsxs21("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "end" }, children: [
|
|
2002
|
+
/* @__PURE__ */ jsx23(Field8, { label: "Chapter", style: { minWidth: 240 }, children: /* @__PURE__ */ jsx23(
|
|
2003
|
+
Select7,
|
|
1716
2004
|
{
|
|
1717
2005
|
value: target.id,
|
|
1718
2006
|
options: targets.data.targets.map((item) => ({ value: item.id, label: item.name })),
|
|
@@ -1723,8 +2011,8 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1723
2011
|
}
|
|
1724
2012
|
}
|
|
1725
2013
|
) }),
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
|
|
2014
|
+
/* @__PURE__ */ jsx23(Field8, { label: "Record type", style: { minWidth: 180 }, children: /* @__PURE__ */ jsx23(
|
|
2015
|
+
Select7,
|
|
1728
2016
|
{
|
|
1729
2017
|
value: type,
|
|
1730
2018
|
options: target.types.map((item) => ({ value: item, label: item.replaceAll("_", " ") })),
|
|
@@ -1735,7 +2023,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1735
2023
|
}
|
|
1736
2024
|
) })
|
|
1737
2025
|
] }),
|
|
1738
|
-
type ? /* @__PURE__ */
|
|
2026
|
+
type ? /* @__PURE__ */ jsx23(
|
|
1739
2027
|
RecordsExplorer,
|
|
1740
2028
|
{
|
|
1741
2029
|
ctx,
|
|
@@ -1744,7 +2032,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1744
2032
|
leaderName: chapter.name
|
|
1745
2033
|
},
|
|
1746
2034
|
`${target.id}:${type}`
|
|
1747
|
-
) : /* @__PURE__ */
|
|
2035
|
+
) : /* @__PURE__ */ jsx23(Panel, { children: /* @__PURE__ */ jsx23("p", { className: "muted", children: "This follower has no browsable record types configured." }) })
|
|
1748
2036
|
] });
|
|
1749
2037
|
}
|
|
1750
2038
|
function networkRecordsWorkspace(chapter) {
|
|
@@ -1752,63 +2040,63 @@ function networkRecordsWorkspace(chapter) {
|
|
|
1752
2040
|
id: "network",
|
|
1753
2041
|
label: chapter.copy.admin.workspaces.network,
|
|
1754
2042
|
defaultViewId: "records",
|
|
1755
|
-
render: (ctx) => /* @__PURE__ */
|
|
2043
|
+
render: (ctx) => /* @__PURE__ */ jsx23(NetworkRecordsWorkspace, { chapter, ctx })
|
|
1756
2044
|
};
|
|
1757
2045
|
}
|
|
1758
2046
|
|
|
1759
2047
|
// src/ui/admin-portfolio.tsx
|
|
1760
2048
|
import { StatBand as StatBand2, StepPipeline as StepPipeline2 } from "@odla-ai/ui/components";
|
|
1761
|
-
import { jsx as
|
|
2049
|
+
import { jsx as jsx24, jsxs as jsxs22 } from "preact/jsx-runtime";
|
|
1762
2050
|
function LeaderNetworkOverview({ ctx }) {
|
|
1763
2051
|
const copy = useChapterCopy().admin.network;
|
|
1764
2052
|
const { data, loading, error } = useAdminResource(
|
|
1765
2053
|
ctx.getToken,
|
|
1766
2054
|
"/api/admin/network/rollup"
|
|
1767
2055
|
);
|
|
1768
|
-
if (loading) return /* @__PURE__ */
|
|
1769
|
-
if (error || !data) return /* @__PURE__ */
|
|
2056
|
+
if (loading) return /* @__PURE__ */ jsx24(AdminNote, { children: copy.loadingRollup });
|
|
2057
|
+
if (error || !data) return /* @__PURE__ */ jsxs22(AdminNote, { children: [
|
|
1770
2058
|
copy.loadRollupFailed,
|
|
1771
2059
|
error ? `: ${error}` : "",
|
|
1772
2060
|
"."
|
|
1773
2061
|
] });
|
|
1774
|
-
if (data.configured === 0) return /* @__PURE__ */
|
|
2062
|
+
if (data.configured === 0) return /* @__PURE__ */ jsx24(AdminPage, { children: /* @__PURE__ */ jsx24(Panel, { title: copy.overview, children: /* @__PURE__ */ jsx24("p", { className: "muted", children: copy.noFollowers }) }) });
|
|
1775
2063
|
const stats = [
|
|
1776
2064
|
{ value: data.configured.toLocaleString(), label: copy.configuredFollowers },
|
|
1777
2065
|
{ value: data.available.toLocaleString(), label: copy.reachableFollowers },
|
|
1778
2066
|
{ value: data.totalRecords.toLocaleString(), label: copy.recordsAcrossFollowers }
|
|
1779
2067
|
];
|
|
1780
2068
|
const steps = data.types.map((type) => ({
|
|
1781
|
-
icon: /* @__PURE__ */
|
|
2069
|
+
icon: /* @__PURE__ */ jsx24("span", { style: { fontSize: 22, fontWeight: 700 }, children: type.total }),
|
|
1782
2070
|
title: type.type.replaceAll("_", " "),
|
|
1783
2071
|
description: Object.entries(type.stages).filter(([, count]) => count > 0).map(([stage, count]) => `${stage.replaceAll("_", " ")} ${count}`).join(" \xB7 ") || copy.noRecordTotals
|
|
1784
2072
|
}));
|
|
1785
|
-
return /* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1793
|
-
target.error ? /* @__PURE__ */
|
|
1794
|
-
/* @__PURE__ */
|
|
2073
|
+
return /* @__PURE__ */ jsxs22(AdminPage, { children: [
|
|
2074
|
+
/* @__PURE__ */ jsx24(StatBand2, { stats }),
|
|
2075
|
+
/* @__PURE__ */ jsx24(Panel, { title: copy.followers, children: /* @__PURE__ */ jsx24("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.targets.map((target) => /* @__PURE__ */ jsxs22("li", { style: { display: "flex", gap: 12, alignItems: "baseline", flexWrap: "wrap" }, children: [
|
|
2076
|
+
/* @__PURE__ */ jsx24("strong", { style: { minWidth: 180 }, children: target.name }),
|
|
2077
|
+
/* @__PURE__ */ jsx24("span", { className: "badge", children: target.transport.reachable === null ? copy.transportNotChecked : target.transport.reachable ? copy.transportReachable : copy.transportUnavailable }),
|
|
2078
|
+
/* @__PURE__ */ jsx24("span", { className: "muted", children: target.transport.kind === "binding" ? formatChapterCopy(copy.bindingTransport, { binding: target.transport.binding ?? "" }) : copy.urlTransport }),
|
|
2079
|
+
/* @__PURE__ */ jsx24("span", { className: "muted", children: target.configuration.activeRevision === null ? copy.noActiveConfiguration : formatChapterCopy(copy.activeConfiguration, { revision: target.configuration.activeRevision }) }),
|
|
2080
|
+
/* @__PURE__ */ jsx24("span", { className: "muted", children: target.publicSite.available === null ? copy.publicSiteNotChecked : target.publicSite.available ? copy.publicSiteAvailable : copy.publicSiteUnavailable }),
|
|
2081
|
+
target.error ? /* @__PURE__ */ jsx24("span", { className: "muted", children: target.error }) : null,
|
|
2082
|
+
/* @__PURE__ */ jsx24("a", { href: target.url, target: "_blank", rel: "noreferrer", style: { marginLeft: "auto" }, children: target.url })
|
|
1795
2083
|
] }, target.id)) }) }),
|
|
1796
|
-
/* @__PURE__ */
|
|
2084
|
+
/* @__PURE__ */ jsx24(Panel, { title: copy.recordTotals, children: steps.length ? /* @__PURE__ */ jsx24(StepPipeline2, { ariaLabel: copy.recordTotals, steps }) : /* @__PURE__ */ jsx24("p", { className: "muted", children: copy.noRecordTotals }) })
|
|
1797
2085
|
] });
|
|
1798
2086
|
}
|
|
1799
2087
|
|
|
1800
2088
|
// src/ui/admin-admission.tsx
|
|
1801
|
-
import { useEffect as useEffect8, useRef as useRef2, useState as
|
|
1802
|
-
import { jsx as
|
|
2089
|
+
import { useEffect as useEffect8, useRef as useRef2, useState as useState20 } from "preact/hooks";
|
|
2090
|
+
import { jsx as jsx25, jsxs as jsxs23 } from "preact/jsx-runtime";
|
|
1803
2091
|
function AdmissionGrantsWorkspace(props) {
|
|
1804
2092
|
const copy = useChapterCopy().admin.admission;
|
|
1805
2093
|
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] =
|
|
2094
|
+
const [email, setEmail] = useState20("");
|
|
2095
|
+
const [decision, setDecision] = useState20("waive_interview");
|
|
2096
|
+
const [note, setNote] = useState20("");
|
|
2097
|
+
const [expires, setExpires] = useState20("");
|
|
2098
|
+
const [busy, setBusy] = useState20(false);
|
|
2099
|
+
const [error, setError] = useState20(null);
|
|
1812
2100
|
const submit = async (event) => {
|
|
1813
2101
|
event.preventDefault();
|
|
1814
2102
|
setBusy(true);
|
|
@@ -1845,48 +2133,48 @@ function AdmissionGrantsWorkspace(props) {
|
|
|
1845
2133
|
setBusy(false);
|
|
1846
2134
|
}
|
|
1847
2135
|
};
|
|
1848
|
-
return /* @__PURE__ */
|
|
1849
|
-
/* @__PURE__ */
|
|
1850
|
-
/* @__PURE__ */
|
|
1851
|
-
/* @__PURE__ */
|
|
1852
|
-
/* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ jsxs23(AdminPage, { children: [
|
|
2137
|
+
/* @__PURE__ */ jsxs23(Panel, { title: copy.title, children: [
|
|
2138
|
+
/* @__PURE__ */ jsx25("p", { className: "muted", style: { margin: 0 }, children: copy.description }),
|
|
2139
|
+
/* @__PURE__ */ jsxs23("form", { onSubmit: (event) => void submit(event), style: { display: "grid", gap: 12, gridTemplateColumns: "minmax(220px,2fr) minmax(180px,1fr) minmax(160px,1fr)" }, children: [
|
|
2140
|
+
/* @__PURE__ */ jsxs23("label", { children: [
|
|
1853
2141
|
copy.email,
|
|
1854
|
-
/* @__PURE__ */
|
|
2142
|
+
/* @__PURE__ */ jsx25("input", { required: true, type: "email", value: email, onInput: (event) => setEmail(event.currentTarget.value) })
|
|
1855
2143
|
] }),
|
|
1856
|
-
/* @__PURE__ */
|
|
2144
|
+
/* @__PURE__ */ jsxs23("label", { children: [
|
|
1857
2145
|
copy.decision,
|
|
1858
|
-
/* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
1860
|
-
/* @__PURE__ */
|
|
2146
|
+
/* @__PURE__ */ jsxs23("select", { value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
|
|
2147
|
+
/* @__PURE__ */ jsx25("option", { value: "waive_interview", children: copy.waive }),
|
|
2148
|
+
/* @__PURE__ */ jsx25("option", { value: "require_interview", children: copy.require })
|
|
1861
2149
|
] })
|
|
1862
2150
|
] }),
|
|
1863
|
-
/* @__PURE__ */
|
|
2151
|
+
/* @__PURE__ */ jsxs23("label", { children: [
|
|
1864
2152
|
copy.expires,
|
|
1865
|
-
/* @__PURE__ */
|
|
2153
|
+
/* @__PURE__ */ jsx25("input", { type: "date", value: expires, onInput: (event) => setExpires(event.currentTarget.value) })
|
|
1866
2154
|
] }),
|
|
1867
|
-
/* @__PURE__ */
|
|
2155
|
+
/* @__PURE__ */ jsxs23("label", { style: { gridColumn: "1 / -1" }, children: [
|
|
1868
2156
|
copy.note,
|
|
1869
|
-
/* @__PURE__ */
|
|
2157
|
+
/* @__PURE__ */ jsx25("input", { value: note, maxLength: 2e3, onInput: (event) => setNote(event.currentTarget.value) })
|
|
1870
2158
|
] }),
|
|
1871
|
-
/* @__PURE__ */
|
|
2159
|
+
/* @__PURE__ */ jsx25("button", { className: "btn secondary", type: "submit", disabled: busy, children: copy.add })
|
|
1872
2160
|
] }),
|
|
1873
|
-
error ? /* @__PURE__ */
|
|
2161
|
+
error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: error }) : null
|
|
1874
2162
|
] }),
|
|
1875
|
-
/* @__PURE__ */
|
|
1876
|
-
resource.loading ? /* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
2163
|
+
/* @__PURE__ */ jsxs23(Panel, { title: copy.ledger, children: [
|
|
2164
|
+
resource.loading ? /* @__PURE__ */ jsx25("p", { className: "muted", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: resource.error }) : null,
|
|
2165
|
+
/* @__PURE__ */ jsx25("div", { style: { display: "grid", gap: 10 }, children: (resource.data?.grants ?? []).map((grant) => {
|
|
1878
2166
|
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__ */
|
|
2167
|
+
return /* @__PURE__ */ jsxs23("article", { className: "card", style: { padding: 14 }, children: [
|
|
2168
|
+
/* @__PURE__ */ jsx25("strong", { children: grant.email }),
|
|
1881
2169
|
" \xB7 ",
|
|
1882
2170
|
grant.decision === "waive_interview" ? copy.waive : copy.require,
|
|
1883
|
-
/* @__PURE__ */
|
|
2171
|
+
/* @__PURE__ */ jsxs23("p", { className: "muted", style: { margin: "6px 0" }, children: [
|
|
1884
2172
|
grant.sourceScope === "local" ? copy.local : copy.parent,
|
|
1885
2173
|
" \xB7 ",
|
|
1886
2174
|
state,
|
|
1887
2175
|
grant.note ? ` \xB7 ${grant.note}` : ""
|
|
1888
2176
|
] }),
|
|
1889
|
-
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */
|
|
2177
|
+
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */ jsx25("button", { className: "btn secondary mini", disabled: busy, onClick: () => void revoke(grant), children: copy.revoke }) : null
|
|
1890
2178
|
] }, grant.id);
|
|
1891
2179
|
}) })
|
|
1892
2180
|
] })
|
|
@@ -1897,19 +2185,19 @@ function admissionGrantsWorkspace(label = "Admission") {
|
|
|
1897
2185
|
id: "admission",
|
|
1898
2186
|
label,
|
|
1899
2187
|
defaultViewId: "grants",
|
|
1900
|
-
render: (ctx) => /* @__PURE__ */
|
|
2188
|
+
render: (ctx) => /* @__PURE__ */ jsx25(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
|
|
1901
2189
|
};
|
|
1902
2190
|
}
|
|
1903
2191
|
function LeaderAdmissionAction(props) {
|
|
1904
|
-
return /* @__PURE__ */
|
|
2192
|
+
return /* @__PURE__ */ jsx25(LeaderAdmissionForm, { ...props }, JSON.stringify([props.targetId, props.personId, props.sourceRecordId]));
|
|
1905
2193
|
}
|
|
1906
2194
|
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] =
|
|
2195
|
+
const [decision, setDecision] = useState20("");
|
|
2196
|
+
const [note, setNote] = useState20("");
|
|
2197
|
+
const [loaded, setLoaded] = useState20(false);
|
|
2198
|
+
const [busy, setBusy] = useState20(false);
|
|
2199
|
+
const [error, setError] = useState20(null);
|
|
2200
|
+
const [status, setStatus] = useState20(null);
|
|
1913
2201
|
const attempt = useRef2(null);
|
|
1914
2202
|
useEffect8(() => {
|
|
1915
2203
|
let live = true;
|
|
@@ -1954,22 +2242,22 @@ function LeaderAdmissionForm(props) {
|
|
|
1954
2242
|
setBusy(false);
|
|
1955
2243
|
}
|
|
1956
2244
|
};
|
|
1957
|
-
return /* @__PURE__ */
|
|
1958
|
-
/* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ jsxs23("div", { style: { display: "grid", gap: 8 }, children: [
|
|
2246
|
+
/* @__PURE__ */ jsxs23("label", { children: [
|
|
1959
2247
|
"Interview",
|
|
1960
|
-
/* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
2248
|
+
/* @__PURE__ */ jsxs23("select", { disabled: !loaded || busy, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
|
|
2249
|
+
/* @__PURE__ */ jsx25("option", { value: "", children: loaded ? "Choose an invitation setting" : "Loading saved setting\u2026" }),
|
|
2250
|
+
/* @__PURE__ */ jsx25("option", { value: "waive_interview", children: "Already completed" }),
|
|
2251
|
+
/* @__PURE__ */ jsx25("option", { value: "require_interview", children: "Required" })
|
|
1964
2252
|
] })
|
|
1965
2253
|
] }),
|
|
1966
|
-
/* @__PURE__ */
|
|
2254
|
+
/* @__PURE__ */ jsxs23("label", { children: [
|
|
1967
2255
|
"Internal note",
|
|
1968
|
-
/* @__PURE__ */
|
|
2256
|
+
/* @__PURE__ */ jsx25("input", { disabled: !loaded || busy, maxLength: 2e3, value: note, onInput: (event) => setNote(event.currentTarget.value) })
|
|
1969
2257
|
] }),
|
|
1970
|
-
/* @__PURE__ */
|
|
1971
|
-
error ? /* @__PURE__ */
|
|
1972
|
-
status ? /* @__PURE__ */
|
|
2258
|
+
/* @__PURE__ */ jsx25("button", { className: "btn secondary", type: "button", disabled: busy || !loaded || !decision, onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save invitation setting" }),
|
|
2259
|
+
error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: error }) : null,
|
|
2260
|
+
status ? /* @__PURE__ */ jsx25("p", { role: "status", children: status }) : null
|
|
1973
2261
|
] });
|
|
1974
2262
|
}
|
|
1975
2263
|
|
|
@@ -1977,19 +2265,19 @@ function LeaderAdmissionForm(props) {
|
|
|
1977
2265
|
import { Tabs as Tabs2 } from "@odla-ai/ui/components";
|
|
1978
2266
|
|
|
1979
2267
|
// src/ui/admin-availability.tsx
|
|
1980
|
-
import { useState as
|
|
1981
|
-
import { Button as
|
|
1982
|
-
import { jsx as
|
|
2268
|
+
import { useState as useState21 } from "preact/hooks";
|
|
2269
|
+
import { Button as Button14, Checkbox, Field as Field9, Input as Input2 } from "@odla-ai/ui/components";
|
|
2270
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "preact/jsx-runtime";
|
|
1983
2271
|
function AvailabilityBody({ ctx }) {
|
|
1984
2272
|
const copy = useChapterCopy().admin.availability;
|
|
1985
2273
|
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] =
|
|
2274
|
+
const [draft, setDraft] = useState21(null);
|
|
2275
|
+
const [busy, setBusy] = useState21(false);
|
|
2276
|
+
const [note, setNote] = useState21(null);
|
|
2277
|
+
const [errors, setErrors] = useState21({});
|
|
1990
2278
|
const cfg = draft ?? data?.scheduling ?? null;
|
|
1991
|
-
if (loading) return /* @__PURE__ */
|
|
1992
|
-
if (error || !cfg) return /* @__PURE__ */
|
|
2279
|
+
if (loading) return /* @__PURE__ */ jsx26(AdminNote, { children: copy.loading });
|
|
2280
|
+
if (error || !cfg) return /* @__PURE__ */ jsxs24(AdminNote, { children: [
|
|
1993
2281
|
copy.loadFailed,
|
|
1994
2282
|
error ? `: ${error}` : "",
|
|
1995
2283
|
"."
|
|
@@ -2019,29 +2307,29 @@ function AvailabilityBody({ ctx }) {
|
|
|
2019
2307
|
setBusy(false);
|
|
2020
2308
|
}
|
|
2021
2309
|
};
|
|
2022
|
-
const numField = (key, label) => /* @__PURE__ */
|
|
2023
|
-
return /* @__PURE__ */
|
|
2024
|
-
/* @__PURE__ */
|
|
2025
|
-
/* @__PURE__ */
|
|
2310
|
+
const numField = (key, label) => /* @__PURE__ */ jsx26(Field9, { label, error: errors[key], children: /* @__PURE__ */ jsx26(Input2, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
|
|
2311
|
+
return /* @__PURE__ */ jsx26(AdminPage, { children: /* @__PURE__ */ jsxs24(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx26("span", { className: "muted", children: note }) : void 0, children: [
|
|
2312
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx26("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx26(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
|
|
2313
|
+
/* @__PURE__ */ jsxs24("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
|
|
2026
2314
|
numField("startHour", copy.startHour),
|
|
2027
2315
|
numField("endHour", copy.endHour),
|
|
2028
2316
|
numField("slotMinutes", copy.slotMinutes),
|
|
2029
2317
|
numField("minNoticeHours", copy.minNoticeHours),
|
|
2030
2318
|
numField("windowDays", copy.windowDays),
|
|
2031
|
-
/* @__PURE__ */
|
|
2319
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx26(Input2, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
2032
2320
|
] }),
|
|
2033
|
-
/* @__PURE__ */
|
|
2034
|
-
/* @__PURE__ */
|
|
2321
|
+
/* @__PURE__ */ jsx26(Field9, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx26(Input2, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
2322
|
+
/* @__PURE__ */ jsx26("div", { children: /* @__PURE__ */ jsx26(Button14, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
2035
2323
|
] }) });
|
|
2036
2324
|
}
|
|
2037
2325
|
function availabilitySection(options = {}) {
|
|
2038
2326
|
const { id = "availability", label = "Availability" } = options;
|
|
2039
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
2327
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx26(AvailabilityBody, { ctx }) };
|
|
2040
2328
|
}
|
|
2041
2329
|
|
|
2042
2330
|
// src/ui/admin-builder-library.tsx
|
|
2043
|
-
import { useEffect as useEffect9, useMemo as useMemo2, useState as
|
|
2044
|
-
import { jsx as
|
|
2331
|
+
import { useEffect as useEffect9, useMemo as useMemo2, useState as useState22 } from "preact/hooks";
|
|
2332
|
+
import { jsx as jsx27, jsxs as jsxs25 } from "preact/jsx-runtime";
|
|
2045
2333
|
var KINDS = ["document", "note", "link", "inspiration", "image", "file"];
|
|
2046
2334
|
var KIND_LABEL = { document: "Document", note: "Note", link: "Link", inspiration: "Inspiration", image: "Image", file: "File" };
|
|
2047
2335
|
var STATUS_LABEL = { draft: "Draft", published: "Published", archived: "Archived" };
|
|
@@ -2073,13 +2361,13 @@ function localResource(kind, chapterId, mode) {
|
|
|
2073
2361
|
};
|
|
2074
2362
|
}
|
|
2075
2363
|
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__ */
|
|
2364
|
+
return /* @__PURE__ */ jsxs25("div", { className: "card", style: { padding: 14 }, children: [
|
|
2365
|
+
/* @__PURE__ */ jsx27("div", { className: "card-label", children: "Preview" }),
|
|
2366
|
+
/* @__PURE__ */ jsx27("h3", { style: { margin: "6px 0" }, children: item.title || "Untitled" }),
|
|
2367
|
+
item.summary ? /* @__PURE__ */ jsx27("p", { style: { margin: "0 0 10px" }, children: item.summary }) : null,
|
|
2368
|
+
item.bodyMarkdown ? /* @__PURE__ */ jsx27("pre", { style: { margin: 0, whiteSpace: "pre-wrap", font: "inherit", lineHeight: 1.55 }, children: item.bodyMarkdown }) : null,
|
|
2369
|
+
item.url ? /* @__PURE__ */ jsx27("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url }) : null,
|
|
2370
|
+
item.brandAsset ? /* @__PURE__ */ jsxs25("div", { style: META, children: [
|
|
2083
2371
|
"Brand asset \xB7 ",
|
|
2084
2372
|
item.brandAsset.bookId,
|
|
2085
2373
|
" / ",
|
|
@@ -2091,14 +2379,14 @@ function ResourcePreview({ item }) {
|
|
|
2091
2379
|
function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
2092
2380
|
const remote = useAdminResource(ctx.getToken, "/api/admin/builder-library");
|
|
2093
2381
|
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] =
|
|
2382
|
+
const [resources, setResources] = useState22(() => [...seed?.local?.document.resources ?? []]);
|
|
2383
|
+
const [selectedKey, setSelectedKey] = useState22(() => seed?.local?.document.resources[0]?.id ? localKey(seed.local.document.resources[0].id) : null);
|
|
2384
|
+
const [query, setQuery] = useState22("");
|
|
2385
|
+
const [kindFilter, setKindFilter] = useState22("all");
|
|
2386
|
+
const [statusFilter, setStatusFilter] = useState22("all");
|
|
2387
|
+
const [newKind, setNewKind] = useState22("document");
|
|
2388
|
+
const [saveError, setSaveError] = useState22(null);
|
|
2389
|
+
const [saving, setSaving] = useState22(false);
|
|
2102
2390
|
useEffect9(() => {
|
|
2103
2391
|
if (!remote.data) return;
|
|
2104
2392
|
const next = [...remote.data.local?.document.resources ?? []];
|
|
@@ -2115,8 +2403,8 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
|
2115
2403
|
return (!query.trim() || haystack.includes(query.toLowerCase().trim())) && (kindFilter === "all" || resource.kind === kindFilter) && (statusFilter === "all" || resource.status === statusFilter);
|
|
2116
2404
|
});
|
|
2117
2405
|
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__ */
|
|
2406
|
+
if (!data && remote.loading) return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsx27(AdminNote, { children: "Loading Builder Library\u2026" }) });
|
|
2407
|
+
if (remote.error || !data) return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsxs25(AdminNote, { children: [
|
|
2120
2408
|
"Builder Library could not be loaded: ",
|
|
2121
2409
|
remote.error ?? "unknown error"
|
|
2122
2410
|
] }) });
|
|
@@ -2152,170 +2440,170 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
|
2152
2440
|
}
|
|
2153
2441
|
};
|
|
2154
2442
|
const scopeValue = selected?.resource.scope.kind === "chapter" ? `chapter:${selected.resource.scope.chapterId}` : selected?.resource.scope.kind ?? "built_not_found";
|
|
2155
|
-
return /* @__PURE__ */
|
|
2156
|
-
/* @__PURE__ */
|
|
2443
|
+
return /* @__PURE__ */ jsxs25(AdminPage, { children: [
|
|
2444
|
+
/* @__PURE__ */ jsxs25(Panel, { title: /* @__PURE__ */ jsxs25("span", { children: [
|
|
2157
2445
|
"Builder Library ",
|
|
2158
|
-
/* @__PURE__ */
|
|
2159
|
-
] }), actions: /* @__PURE__ */
|
|
2160
|
-
/* @__PURE__ */
|
|
2161
|
-
/* @__PURE__ */
|
|
2446
|
+
/* @__PURE__ */ jsx27("span", { className: "muted", children: "\xB7 private" })
|
|
2447
|
+
] }), actions: /* @__PURE__ */ jsxs25("div", { style: ROW, children: [
|
|
2448
|
+
/* @__PURE__ */ jsx27("select", { "aria-label": "New resource type", value: newKind, onChange: (event) => setNewKind(event.currentTarget.value), children: KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }),
|
|
2449
|
+
/* @__PURE__ */ jsx27("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring, onClick: add, children: "Add resource" })
|
|
2162
2450
|
] }), children: [
|
|
2163
|
-
/* @__PURE__ */
|
|
2164
|
-
/* @__PURE__ */
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
/* @__PURE__ */
|
|
2167
|
-
/* @__PURE__ */
|
|
2168
|
-
KINDS.map((kind) => /* @__PURE__ */
|
|
2451
|
+
/* @__PURE__ */ jsx27("p", { className: "muted", style: { margin: 0 }, children: "Documents, links, and Brand assets used by site-building agents. Nothing here appears on a member page." }),
|
|
2452
|
+
/* @__PURE__ */ jsxs25("div", { style: ROW, children: [
|
|
2453
|
+
/* @__PURE__ */ jsx27("input", { "aria-label": "Search resources", placeholder: "Search resources", value: query, onInput: (event) => setQuery(event.currentTarget.value), style: { flex: "1 1 220px" } }),
|
|
2454
|
+
/* @__PURE__ */ jsxs25("select", { "aria-label": "Filter by type", value: kindFilter, onChange: (event) => setKindFilter(event.currentTarget.value), children: [
|
|
2455
|
+
/* @__PURE__ */ jsx27("option", { value: "all", children: "All types" }),
|
|
2456
|
+
KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind))
|
|
2169
2457
|
] }),
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2172
|
-
/* @__PURE__ */
|
|
2173
|
-
/* @__PURE__ */
|
|
2174
|
-
/* @__PURE__ */
|
|
2458
|
+
/* @__PURE__ */ jsxs25("select", { "aria-label": "Filter by status", value: statusFilter, onChange: (event) => setStatusFilter(event.currentTarget.value), children: [
|
|
2459
|
+
/* @__PURE__ */ jsx27("option", { value: "all", children: "All statuses" }),
|
|
2460
|
+
/* @__PURE__ */ jsx27("option", { value: "draft", children: "Draft" }),
|
|
2461
|
+
/* @__PURE__ */ jsx27("option", { value: "published", children: "Published" }),
|
|
2462
|
+
/* @__PURE__ */ jsx27("option", { value: "archived", children: "Archived" })
|
|
2175
2463
|
] })
|
|
2176
2464
|
] })
|
|
2177
2465
|
] }),
|
|
2178
|
-
/* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2466
|
+
/* @__PURE__ */ jsxs25("div", { style: GRID, "data-builder-library-cms": true, children: [
|
|
2467
|
+
/* @__PURE__ */ jsx27(Panel, { title: `${visible.length} resource${visible.length === 1 ? "" : "s"}`, children: /* @__PURE__ */ jsx27("div", { style: { ...STACK, maxHeight: "70vh", overflow: "auto" }, children: visible.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "muted", children: "No resources match these filters." }) : visible.map((entry) => /* @__PURE__ */ jsxs25("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: [
|
|
2468
|
+
/* @__PURE__ */ jsxs25("div", { style: { ...ROW, justifyContent: "space-between" }, children: [
|
|
2469
|
+
/* @__PURE__ */ jsx27("strong", { children: entry.resource.title }),
|
|
2470
|
+
/* @__PURE__ */ jsx27("span", { className: "badge", children: KIND_LABEL[entry.resource.kind] })
|
|
2183
2471
|
] }),
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
/* @__PURE__ */
|
|
2190
|
-
/* @__PURE__ */
|
|
2472
|
+
/* @__PURE__ */ jsx27("p", { className: "muted", style: { margin: "6px 0", lineHeight: 1.4 }, children: contentSummary(entry.resource) }),
|
|
2473
|
+
/* @__PURE__ */ jsxs25("div", { style: META, children: [
|
|
2474
|
+
/* @__PURE__ */ jsx27("span", { children: STATUS_LABEL[entry.resource.status] }),
|
|
2475
|
+
/* @__PURE__ */ jsx27("span", { children: "\xB7" }),
|
|
2476
|
+
/* @__PURE__ */ jsx27("span", { children: scopeLabel(entry.resource.scope) }),
|
|
2477
|
+
/* @__PURE__ */ jsx27("span", { children: "\xB7" }),
|
|
2478
|
+
/* @__PURE__ */ jsx27("span", { children: entry.sourceId })
|
|
2191
2479
|
] })
|
|
2192
2480
|
] }, entry.key)) }) }),
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2195
|
-
/* @__PURE__ */
|
|
2481
|
+
/* @__PURE__ */ jsxs25(Panel, { title: selected ? selected.resource.title : "Select a resource", actions: selected?.local ? /* @__PURE__ */ jsxs25("div", { style: ROW, children: [
|
|
2482
|
+
/* @__PURE__ */ jsx27("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" }),
|
|
2483
|
+
/* @__PURE__ */ jsx27("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save changes" })
|
|
2196
2484
|
] }) : null, children: [
|
|
2197
|
-
!selected ? /* @__PURE__ */
|
|
2198
|
-
/* @__PURE__ */
|
|
2485
|
+
!selected ? /* @__PURE__ */ jsx27("p", { className: "muted", children: "Add a resource or choose one from the list." }) : selected.local ? /* @__PURE__ */ jsx27(LocalEditor, { selected: selected.resource, data, scopeValue, saving, update, setResources, setSelectedKey }) : /* @__PURE__ */ jsxs25("div", { style: STACK, children: [
|
|
2486
|
+
/* @__PURE__ */ jsxs25("div", { style: META, children: [
|
|
2199
2487
|
"Read only \xB7 supplied by ",
|
|
2200
2488
|
selected.sourceId,
|
|
2201
2489
|
" \xB7 source revision ",
|
|
2202
2490
|
selected.sourceRevision
|
|
2203
2491
|
] }),
|
|
2204
|
-
/* @__PURE__ */
|
|
2205
|
-
/* @__PURE__ */
|
|
2492
|
+
/* @__PURE__ */ jsxs25("div", { children: [
|
|
2493
|
+
/* @__PURE__ */ jsx27("span", { className: "badge", children: STATUS_LABEL[selected.resource.status] }),
|
|
2206
2494
|
" ",
|
|
2207
|
-
/* @__PURE__ */
|
|
2495
|
+
/* @__PURE__ */ jsx27("span", { className: "badge", children: scopeLabel(selected.resource.scope) })
|
|
2208
2496
|
] }),
|
|
2209
|
-
/* @__PURE__ */
|
|
2210
|
-
data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */
|
|
2497
|
+
/* @__PURE__ */ jsx27(ResourcePreview, { item: selected.resource }),
|
|
2498
|
+
data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */ jsx27("button", { className: "btn secondary", type: "button", onClick: () => void promote(selected), children: "Copy to all chapters" }) : null
|
|
2211
2499
|
] }),
|
|
2212
|
-
saveError ? /* @__PURE__ */
|
|
2213
|
-
/* @__PURE__ */
|
|
2214
|
-
/* @__PURE__ */
|
|
2500
|
+
saveError ? /* @__PURE__ */ jsxs25("div", { className: "notice danger", role: "alert", children: [
|
|
2501
|
+
/* @__PURE__ */ jsx27("strong", { children: "Fix these items before saving:" }),
|
|
2502
|
+
/* @__PURE__ */ jsx27("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx27("li", { children: error }, error)) })
|
|
2215
2503
|
] }) : null
|
|
2216
2504
|
] })
|
|
2217
2505
|
] })
|
|
2218
2506
|
] });
|
|
2219
2507
|
}
|
|
2220
2508
|
function LocalEditor({ selected, data, scopeValue, update, setResources, setSelectedKey }) {
|
|
2221
|
-
return /* @__PURE__ */
|
|
2222
|
-
/* @__PURE__ */
|
|
2509
|
+
return /* @__PURE__ */ jsxs25("div", { style: STACK, children: [
|
|
2510
|
+
/* @__PURE__ */ jsxs25("div", { style: META, children: [
|
|
2223
2511
|
"Local \xB7 resource revision ",
|
|
2224
2512
|
selected.revision,
|
|
2225
2513
|
" \xB7 library revision ",
|
|
2226
2514
|
data.local?.revision ?? 0
|
|
2227
2515
|
] }),
|
|
2228
|
-
/* @__PURE__ */
|
|
2229
|
-
/* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
2231
|
-
/* @__PURE__ */
|
|
2516
|
+
/* @__PURE__ */ jsxs25("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
2517
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2518
|
+
/* @__PURE__ */ jsx27("span", { children: "Title" }),
|
|
2519
|
+
/* @__PURE__ */ jsx27("input", { value: selected.title, onInput: (event) => update({ title: event.currentTarget.value }) })
|
|
2232
2520
|
] }),
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2521
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2522
|
+
/* @__PURE__ */ jsx27("span", { children: "ID" }),
|
|
2523
|
+
/* @__PURE__ */ jsx27("input", { value: selected.id, onInput: (event) => {
|
|
2236
2524
|
const id = cleanId(event.currentTarget.value);
|
|
2237
2525
|
setResources((items) => items.map((item) => item.id === selected.id ? { ...item, id } : item));
|
|
2238
2526
|
setSelectedKey(localKey(id));
|
|
2239
2527
|
} })
|
|
2240
2528
|
] }),
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2529
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2530
|
+
/* @__PURE__ */ jsx27("span", { children: "Type" }),
|
|
2531
|
+
/* @__PURE__ */ jsx27("select", { value: selected.kind, onChange: (event) => {
|
|
2244
2532
|
const kind = event.currentTarget.value;
|
|
2245
2533
|
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__ */
|
|
2534
|
+
}, children: KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind)) })
|
|
2247
2535
|
] }),
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
2536
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2537
|
+
/* @__PURE__ */ jsx27("span", { children: "Status" }),
|
|
2538
|
+
/* @__PURE__ */ jsxs25("select", { value: selected.status, onChange: (event) => update({ status: event.currentTarget.value }), children: [
|
|
2539
|
+
/* @__PURE__ */ jsx27("option", { value: "draft", children: "Draft" }),
|
|
2540
|
+
/* @__PURE__ */ jsx27("option", { value: "published", children: "Published" }),
|
|
2541
|
+
/* @__PURE__ */ jsx27("option", { value: "archived", children: "Archived" })
|
|
2254
2542
|
] })
|
|
2255
2543
|
] }),
|
|
2256
|
-
/* @__PURE__ */
|
|
2257
|
-
/* @__PURE__ */
|
|
2258
|
-
/* @__PURE__ */
|
|
2544
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2545
|
+
/* @__PURE__ */ jsx27("span", { children: "Visible to" }),
|
|
2546
|
+
/* @__PURE__ */ jsxs25("select", { value: scopeValue, onChange: (event) => {
|
|
2259
2547
|
const value = event.currentTarget.value;
|
|
2260
2548
|
update({ scope: value === "global" ? { kind: "global" } : value === "built_not_found" ? { kind: "built_not_found" } : { kind: "chapter", chapterId: value.slice("chapter:".length) } });
|
|
2261
2549
|
}, children: [
|
|
2262
|
-
data.mode === "hub" ? /* @__PURE__ */
|
|
2263
|
-
data.mode === "hub" ? /* @__PURE__ */
|
|
2264
|
-
data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */
|
|
2550
|
+
data.mode === "hub" ? /* @__PURE__ */ jsx27("option", { value: "built_not_found", children: "Built Not Found only" }) : null,
|
|
2551
|
+
data.mode === "hub" ? /* @__PURE__ */ jsx27("option", { value: "global", children: "All chapters" }) : null,
|
|
2552
|
+
data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */ jsxs25("option", { value: `chapter:${chapterId}`, children: [
|
|
2265
2553
|
"Only ",
|
|
2266
2554
|
chapterId
|
|
2267
2555
|
] }, chapterId))
|
|
2268
2556
|
] })
|
|
2269
2557
|
] }),
|
|
2270
|
-
/* @__PURE__ */
|
|
2271
|
-
/* @__PURE__ */
|
|
2272
|
-
/* @__PURE__ */
|
|
2558
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2559
|
+
/* @__PURE__ */ jsx27("span", { children: "Tags" }),
|
|
2560
|
+
/* @__PURE__ */ jsx27("input", { value: selected.tags.join(", "), placeholder: "brand, membership", onInput: (event) => update({ tags: event.currentTarget.value.split(",").map((tag) => tag.trim()).filter(Boolean) }) })
|
|
2273
2561
|
] })
|
|
2274
2562
|
] }),
|
|
2275
|
-
/* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2277
|
-
/* @__PURE__ */
|
|
2563
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2564
|
+
/* @__PURE__ */ jsx27("span", { children: "Summary" }),
|
|
2565
|
+
/* @__PURE__ */ jsx27("textarea", { rows: 2, value: selected.summary ?? "", onInput: (event) => update({ summary: event.currentTarget.value || void 0 }) })
|
|
2278
2566
|
] }),
|
|
2279
|
-
selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */
|
|
2280
|
-
/* @__PURE__ */
|
|
2281
|
-
/* @__PURE__ */
|
|
2567
|
+
selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2568
|
+
/* @__PURE__ */ jsx27("span", { children: "Markdown" }),
|
|
2569
|
+
/* @__PURE__ */ jsx27("textarea", { rows: 12, value: selected.bodyMarkdown ?? "", onInput: (event) => update({ bodyMarkdown: event.currentTarget.value }) })
|
|
2282
2570
|
] }) : null,
|
|
2283
|
-
selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */
|
|
2284
|
-
/* @__PURE__ */
|
|
2285
|
-
/* @__PURE__ */
|
|
2571
|
+
selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2572
|
+
/* @__PURE__ */ jsx27("span", { children: "HTTPS URL" }),
|
|
2573
|
+
/* @__PURE__ */ jsx27("input", { type: "url", value: selected.url ?? "", onInput: (event) => update({ url: event.currentTarget.value }) })
|
|
2286
2574
|
] }) : null,
|
|
2287
|
-
selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */
|
|
2288
|
-
/* @__PURE__ */
|
|
2289
|
-
/* @__PURE__ */
|
|
2290
|
-
/* @__PURE__ */
|
|
2575
|
+
selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */ jsxs25("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
|
|
2576
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2577
|
+
/* @__PURE__ */ jsx27("span", { children: "Brand book ID" }),
|
|
2578
|
+
/* @__PURE__ */ jsx27("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
2579
|
] }),
|
|
2292
|
-
/* @__PURE__ */
|
|
2293
|
-
/* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2580
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2581
|
+
/* @__PURE__ */ jsx27("span", { children: "Asset ID" }),
|
|
2582
|
+
/* @__PURE__ */ jsx27("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
2583
|
] }),
|
|
2296
|
-
/* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2584
|
+
/* @__PURE__ */ jsxs25("label", { className: "field", children: [
|
|
2585
|
+
/* @__PURE__ */ jsx27("span", { children: "Filename (optional)" }),
|
|
2586
|
+
/* @__PURE__ */ jsx27("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
2587
|
] })
|
|
2300
2588
|
] }) : null,
|
|
2301
|
-
/* @__PURE__ */
|
|
2589
|
+
/* @__PURE__ */ jsx27(ResourcePreview, { item: selected })
|
|
2302
2590
|
] });
|
|
2303
2591
|
}
|
|
2304
2592
|
function builderLibrarySection() {
|
|
2305
|
-
return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */
|
|
2593
|
+
return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */ jsx27(BuilderLibraryAdmin, { ctx }) };
|
|
2306
2594
|
}
|
|
2307
2595
|
|
|
2308
2596
|
// src/ui/admin-email.tsx
|
|
2309
|
-
import { useState as
|
|
2310
|
-
import { Button as
|
|
2597
|
+
import { useState as useState24 } from "preact/hooks";
|
|
2598
|
+
import { Button as Button16, Checkbox as Checkbox2, DataTable as DataTable3, Field as Field11, Input as Input4, Textarea as Textarea4 } from "@odla-ai/ui/components";
|
|
2311
2599
|
|
|
2312
2600
|
// src/ui/admin-email-resolution.tsx
|
|
2313
|
-
import { useState as
|
|
2314
|
-
import { Button as
|
|
2315
|
-
import { jsx as
|
|
2601
|
+
import { useState as useState23 } from "preact/hooks";
|
|
2602
|
+
import { Button as Button15, Field as Field10, Input as Input3, Textarea as Textarea3 } from "@odla-ai/ui/components";
|
|
2603
|
+
import { jsx as jsx28, jsxs as jsxs26 } from "preact/jsx-runtime";
|
|
2316
2604
|
function EmailDeliveryResolution({ row, ctx, onResolved }) {
|
|
2317
|
-
const [open, setOpen] =
|
|
2318
|
-
const [busy, setBusy] =
|
|
2605
|
+
const [open, setOpen] = useState23(false), [reason, setReason] = useState23(""), [messageId, setMessageId] = useState23("");
|
|
2606
|
+
const [busy, setBusy] = useState23(false), [note, setNote] = useState23("");
|
|
2319
2607
|
if (!["sending", "uncertain"].includes(row.deliveryState ?? "") || !row.deliveryAttempt) return null;
|
|
2320
2608
|
const resolve2 = async (decision) => {
|
|
2321
2609
|
setBusy(true);
|
|
@@ -2339,45 +2627,45 @@ function EmailDeliveryResolution({ row, ctx, onResolved }) {
|
|
|
2339
2627
|
setBusy(false);
|
|
2340
2628
|
}
|
|
2341
2629
|
};
|
|
2342
|
-
if (!open) return /* @__PURE__ */
|
|
2343
|
-
return /* @__PURE__ */
|
|
2344
|
-
/* @__PURE__ */
|
|
2630
|
+
if (!open) return /* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", onClick: () => setOpen(true), children: "Review delivery" });
|
|
2631
|
+
return /* @__PURE__ */ jsxs26("div", { children: [
|
|
2632
|
+
/* @__PURE__ */ jsxs26("p", { children: [
|
|
2345
2633
|
"Attempt ",
|
|
2346
2634
|
row.deliveryAttempt,
|
|
2347
2635
|
": ",
|
|
2348
2636
|
row.subject,
|
|
2349
2637
|
". Check provider evidence, not just whether the message is in an inbox. This action does not send mail."
|
|
2350
2638
|
] }),
|
|
2351
|
-
/* @__PURE__ */
|
|
2352
|
-
/* @__PURE__ */
|
|
2353
|
-
/* @__PURE__ */
|
|
2354
|
-
/* @__PURE__ */
|
|
2355
|
-
/* @__PURE__ */
|
|
2356
|
-
note ? /* @__PURE__ */
|
|
2639
|
+
/* @__PURE__ */ jsx28(Field10, { label: "Provider evidence", children: /* @__PURE__ */ jsx28(Textarea3, { value: reason, onInput: (e) => setReason(e.target.value) }) }),
|
|
2640
|
+
/* @__PURE__ */ jsx28(Field10, { label: "Provider message ID (optional)", children: /* @__PURE__ */ jsx28(Input3, { value: messageId, onInput: (e) => setMessageId(e.target.value) }) }),
|
|
2641
|
+
/* @__PURE__ */ jsx28(Button15, { mini: true, disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_delivered"), children: "Confirm delivered" }),
|
|
2642
|
+
/* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_not_delivered"), children: "Confirm not delivered" }),
|
|
2643
|
+
/* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", disabled: busy, onClick: () => setOpen(false), children: "Cancel" }),
|
|
2644
|
+
note ? /* @__PURE__ */ jsx28("p", { role: "alert", children: note }) : null
|
|
2357
2645
|
] });
|
|
2358
2646
|
}
|
|
2359
2647
|
|
|
2360
2648
|
// src/ui/admin-email.tsx
|
|
2361
|
-
import { jsx as
|
|
2649
|
+
import { jsx as jsx29, jsxs as jsxs27 } from "preact/jsx-runtime";
|
|
2362
2650
|
var logColumns = (copy, ctx, refresh) => [
|
|
2363
2651
|
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
2364
2652
|
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
2365
2653
|
{ 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__ */
|
|
2654
|
+
{ key: "status", header: copy.statusColumn, cell: (r) => /* @__PURE__ */ jsxs27("div", { children: [
|
|
2655
|
+
r.deliveryState && r.deliveryState !== "sent" ? /* @__PURE__ */ jsx29("span", { className: "badge", children: r.deliveryState === "retry_ready" ? "Original workflow may retry" : r.deliveryState === "sending" ? "Sending \u2014 unconfirmed" : "Delivery uncertain" }) : r.error ? /* @__PURE__ */ jsx29("span", { className: "badge", children: copy.failed }) : r.transport === "log-only" ? "Logged only \u2014 not sent" : r.redirected ? /* @__PURE__ */ jsx29("span", { className: "badge", children: copy.redirected }) : copy.delivered,
|
|
2656
|
+
/* @__PURE__ */ jsx29(EmailDeliveryResolution, { row: r, ctx, onResolved: refresh })
|
|
2369
2657
|
] }) }
|
|
2370
2658
|
];
|
|
2371
2659
|
function EmailBody({ ctx }) {
|
|
2372
2660
|
const copy = useChapterCopy().admin.email;
|
|
2373
2661
|
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
2374
2662
|
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
2375
|
-
const [draft, setDraft] =
|
|
2376
|
-
const [busy, setBusy] =
|
|
2377
|
-
const [note, setNote] =
|
|
2663
|
+
const [draft, setDraft] = useState24(null);
|
|
2664
|
+
const [busy, setBusy] = useState24(null);
|
|
2665
|
+
const [note, setNote] = useState24(null);
|
|
2378
2666
|
const data = draft ?? cfg.data;
|
|
2379
|
-
if (cfg.loading) return /* @__PURE__ */
|
|
2380
|
-
if (cfg.error || !data) return /* @__PURE__ */
|
|
2667
|
+
if (cfg.loading) return /* @__PURE__ */ jsx29(AdminNote, { children: copy.loading });
|
|
2668
|
+
if (cfg.error || !data) return /* @__PURE__ */ jsxs27(AdminNote, { children: [
|
|
2381
2669
|
copy.loadFailed,
|
|
2382
2670
|
cfg.error ? `: ${cfg.error}` : "",
|
|
2383
2671
|
"."
|
|
@@ -2410,44 +2698,44 @@ function EmailBody({ ctx }) {
|
|
|
2410
2698
|
}) : copy.testFailed);
|
|
2411
2699
|
log.refresh();
|
|
2412
2700
|
});
|
|
2413
|
-
return /* @__PURE__ */
|
|
2414
|
-
/* @__PURE__ */
|
|
2701
|
+
return /* @__PURE__ */ jsxs27(AdminPage, { children: [
|
|
2702
|
+
/* @__PURE__ */ jsxs27(
|
|
2415
2703
|
Panel,
|
|
2416
2704
|
{
|
|
2417
2705
|
title: copy.delivery,
|
|
2418
|
-
actions: /* @__PURE__ */
|
|
2706
|
+
actions: /* @__PURE__ */ jsxs27("span", { className: "muted", children: [
|
|
2419
2707
|
data.envName,
|
|
2420
2708
|
" \xB7 ",
|
|
2421
2709
|
data.transport,
|
|
2422
2710
|
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
2423
2711
|
] }),
|
|
2424
2712
|
children: [
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2713
|
+
/* @__PURE__ */ jsx29(Field11, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx29(Input4, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
2714
|
+
/* @__PURE__ */ jsx29(Field11, { label: copy.replyTo, children: /* @__PURE__ */ jsx29(Input4, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
2715
|
+
/* @__PURE__ */ jsx29(Field11, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx29(Input4, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
2428
2716
|
]
|
|
2429
2717
|
}
|
|
2430
2718
|
),
|
|
2431
|
-
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */
|
|
2432
|
-
/* @__PURE__ */
|
|
2433
|
-
/* @__PURE__ */
|
|
2434
|
-
/* @__PURE__ */
|
|
2719
|
+
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */ jsxs27(Panel, { title: key, actions: /* @__PURE__ */ jsx29(Button16, { variant: "secondary", mini: true, disabled: busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }), children: [
|
|
2720
|
+
/* @__PURE__ */ jsx29(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
2721
|
+
/* @__PURE__ */ jsx29(Field11, { label: copy.subject, children: /* @__PURE__ */ jsx29(Input4, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
2722
|
+
/* @__PURE__ */ jsx29(Field11, { label: copy.body, children: /* @__PURE__ */ jsx29(Textarea4, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
|
|
2435
2723
|
] }, key)),
|
|
2436
|
-
/* @__PURE__ */
|
|
2437
|
-
/* @__PURE__ */
|
|
2438
|
-
note ? /* @__PURE__ */
|
|
2724
|
+
/* @__PURE__ */ jsxs27("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
2725
|
+
/* @__PURE__ */ jsx29(Button16, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
2726
|
+
note ? /* @__PURE__ */ jsx29("span", { className: "muted", children: note }) : null
|
|
2439
2727
|
] }),
|
|
2440
|
-
/* @__PURE__ */
|
|
2728
|
+
/* @__PURE__ */ jsx29(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx29(DataTable3, { rows: log.data?.sends ?? [], columns: logColumns(copy, ctx, log.refresh), rowKey: (r) => r.id }) })
|
|
2441
2729
|
] });
|
|
2442
2730
|
}
|
|
2443
2731
|
function emailSection(options = {}) {
|
|
2444
2732
|
const { id = "email", label = "Email" } = options;
|
|
2445
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
2733
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx29(EmailBody, { ctx }) };
|
|
2446
2734
|
}
|
|
2447
2735
|
|
|
2448
2736
|
// src/ui/admin-member-content.tsx
|
|
2449
|
-
import { useEffect as useEffect10, useState as
|
|
2450
|
-
import { jsx as
|
|
2737
|
+
import { useEffect as useEffect10, useState as useState25 } from "preact/hooks";
|
|
2738
|
+
import { jsx as jsx30, jsxs as jsxs28 } from "preact/jsx-runtime";
|
|
2451
2739
|
var emptyDraft = () => ({ pages: [], upsellOverrides: [] });
|
|
2452
2740
|
function accessLabel(page) {
|
|
2453
2741
|
return page.audience.kind === "all-members" ? "All members" : `Tiers: ${page.audience.tierIds.join(", ")}`;
|
|
@@ -2457,17 +2745,17 @@ function MemberContentAdmin({
|
|
|
2457
2745
|
initialData
|
|
2458
2746
|
}) {
|
|
2459
2747
|
const resource = useAdminResource(ctx.getToken, "/api/admin/member-content");
|
|
2460
|
-
const [draft, setDraft] =
|
|
2461
|
-
const [saveError, setSaveError] =
|
|
2462
|
-
const [saving, setSaving] =
|
|
2748
|
+
const [draft, setDraft] = useState25(JSON.stringify(initialData?.chapter?.document ?? emptyDraft(), null, 2));
|
|
2749
|
+
const [saveError, setSaveError] = useState25(null);
|
|
2750
|
+
const [saving, setSaving] = useState25(false);
|
|
2463
2751
|
useEffect10(() => {
|
|
2464
2752
|
if (!resource.data) return;
|
|
2465
2753
|
setDraft(JSON.stringify(resource.data.chapter?.document ?? emptyDraft(), null, 2));
|
|
2466
2754
|
}, [resource.data]);
|
|
2467
2755
|
const loaded = initialData ?? resource.data;
|
|
2468
|
-
if (!loaded && resource.loading) return /* @__PURE__ */
|
|
2756
|
+
if (!loaded && resource.loading) return /* @__PURE__ */ jsx30(AdminPage, { children: /* @__PURE__ */ jsx30(AdminNote, { children: "Loading member content\u2026" }) });
|
|
2469
2757
|
if (resource.error || !loaded) {
|
|
2470
|
-
return /* @__PURE__ */
|
|
2758
|
+
return /* @__PURE__ */ jsx30(AdminPage, { children: /* @__PURE__ */ jsxs28(AdminNote, { children: [
|
|
2471
2759
|
"Member content could not be loaded: ",
|
|
2472
2760
|
resource.error ?? "unknown error"
|
|
2473
2761
|
] }) });
|
|
@@ -2498,35 +2786,35 @@ function MemberContentAdmin({
|
|
|
2498
2786
|
setSaving(false);
|
|
2499
2787
|
}
|
|
2500
2788
|
};
|
|
2501
|
-
return /* @__PURE__ */
|
|
2502
|
-
/* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
2504
|
-
/* @__PURE__ */
|
|
2505
|
-
/* @__PURE__ */
|
|
2506
|
-
/* @__PURE__ */
|
|
2507
|
-
/* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
/* @__PURE__ */
|
|
2789
|
+
return /* @__PURE__ */ jsxs28(AdminPage, { children: [
|
|
2790
|
+
/* @__PURE__ */ jsxs28(Panel, { children: [
|
|
2791
|
+
/* @__PURE__ */ jsx30("div", { className: "card-label", children: "Member content" }),
|
|
2792
|
+
/* @__PURE__ */ jsx30("h2", { children: "One catalog, two clear owners" }),
|
|
2793
|
+
/* @__PURE__ */ jsx30("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." }),
|
|
2794
|
+
/* @__PURE__ */ jsxs28("div", { className: "stat-band", children: [
|
|
2795
|
+
/* @__PURE__ */ jsxs28("div", { children: [
|
|
2796
|
+
/* @__PURE__ */ jsx30("strong", { children: inherited.length }),
|
|
2797
|
+
/* @__PURE__ */ jsx30("span", { children: "Inherited pages" })
|
|
2510
2798
|
] }),
|
|
2511
|
-
/* @__PURE__ */
|
|
2512
|
-
/* @__PURE__ */
|
|
2513
|
-
/* @__PURE__ */
|
|
2799
|
+
/* @__PURE__ */ jsxs28("div", { children: [
|
|
2800
|
+
/* @__PURE__ */ jsx30("strong", { children: local.length }),
|
|
2801
|
+
/* @__PURE__ */ jsx30("span", { children: "Chapter pages" })
|
|
2514
2802
|
] }),
|
|
2515
|
-
/* @__PURE__ */
|
|
2516
|
-
/* @__PURE__ */
|
|
2517
|
-
/* @__PURE__ */
|
|
2803
|
+
/* @__PURE__ */ jsxs28("div", { children: [
|
|
2804
|
+
/* @__PURE__ */ jsx30("strong", { children: data.tiers.length }),
|
|
2805
|
+
/* @__PURE__ */ jsx30("span", { children: "Declared tiers" })
|
|
2518
2806
|
] })
|
|
2519
2807
|
] })
|
|
2520
2808
|
] }),
|
|
2521
|
-
/* @__PURE__ */
|
|
2522
|
-
/* @__PURE__ */
|
|
2523
|
-
/* @__PURE__ */
|
|
2809
|
+
/* @__PURE__ */ jsxs28(Panel, { children: [
|
|
2810
|
+
/* @__PURE__ */ jsx30("div", { className: "card-label", children: "Inherited \xB7 read only" }),
|
|
2811
|
+
/* @__PURE__ */ jsxs28("h2", { children: [
|
|
2524
2812
|
"Parent pages ",
|
|
2525
2813
|
data.parent ? `\xB7 revision ${data.parent.revision}` : ""
|
|
2526
2814
|
] }),
|
|
2527
|
-
inherited.length === 0 ? /* @__PURE__ */
|
|
2528
|
-
/* @__PURE__ */
|
|
2529
|
-
/* @__PURE__ */
|
|
2815
|
+
inherited.length === 0 ? /* @__PURE__ */ jsx30(AdminNote, { children: "No Parent pages have been delivered." }) : /* @__PURE__ */ jsx30("div", { className: "data-list", "data-member-content-inherited": true, children: inherited.map((page) => /* @__PURE__ */ jsxs28("article", { className: "card", children: [
|
|
2816
|
+
/* @__PURE__ */ jsx30("strong", { children: page.title }),
|
|
2817
|
+
/* @__PURE__ */ jsxs28("div", { className: "meeting-note", children: [
|
|
2530
2818
|
"/",
|
|
2531
2819
|
page.slug,
|
|
2532
2820
|
" \xB7 ",
|
|
@@ -2534,22 +2822,22 @@ function MemberContentAdmin({
|
|
|
2534
2822
|
" \xB7 ",
|
|
2535
2823
|
accessLabel(page)
|
|
2536
2824
|
] }),
|
|
2537
|
-
/* @__PURE__ */
|
|
2825
|
+
/* @__PURE__ */ jsxs28("div", { className: "meeting-note", children: [
|
|
2538
2826
|
"Upsell: ",
|
|
2539
2827
|
page.upsell ? `${page.upsell.owner} controlled` : "none"
|
|
2540
2828
|
] })
|
|
2541
2829
|
] }, page.id)) })
|
|
2542
2830
|
] }),
|
|
2543
|
-
/* @__PURE__ */
|
|
2544
|
-
/* @__PURE__ */
|
|
2545
|
-
/* @__PURE__ */
|
|
2831
|
+
/* @__PURE__ */ jsxs28(Panel, { children: [
|
|
2832
|
+
/* @__PURE__ */ jsx30("div", { className: "card-label", children: "Chapter \xB7 editable" }),
|
|
2833
|
+
/* @__PURE__ */ jsxs28("h2", { children: [
|
|
2546
2834
|
"Local document \xB7 revision ",
|
|
2547
2835
|
data.chapter?.revision ?? 0
|
|
2548
2836
|
] }),
|
|
2549
|
-
/* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2837
|
+
/* @__PURE__ */ jsx30("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." }),
|
|
2838
|
+
/* @__PURE__ */ jsxs28("label", { className: "field", children: [
|
|
2839
|
+
/* @__PURE__ */ jsx30("span", { children: "Pages and delegated upsells (JSON)" }),
|
|
2840
|
+
/* @__PURE__ */ jsx30(
|
|
2553
2841
|
"textarea",
|
|
2554
2842
|
{
|
|
2555
2843
|
rows: 24,
|
|
@@ -2560,12 +2848,12 @@ function MemberContentAdmin({
|
|
|
2560
2848
|
}
|
|
2561
2849
|
)
|
|
2562
2850
|
] }),
|
|
2563
|
-
saveError ? /* @__PURE__ */
|
|
2564
|
-
/* @__PURE__ */
|
|
2565
|
-
/* @__PURE__ */
|
|
2851
|
+
saveError ? /* @__PURE__ */ jsxs28("div", { className: "notice danger", role: "alert", children: [
|
|
2852
|
+
/* @__PURE__ */ jsx30("strong", { children: "Fix these items before saving:" }),
|
|
2853
|
+
/* @__PURE__ */ jsx30("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx30("li", { children: error }, error)) })
|
|
2566
2854
|
] }) : null,
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
!data.localAuthoring ? /* @__PURE__ */
|
|
2855
|
+
/* @__PURE__ */ jsx30("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save chapter content" }),
|
|
2856
|
+
!data.localAuthoring ? /* @__PURE__ */ jsx30(AdminNote, { children: "Local authoring is disabled in the checked-in Chapter config." }) : null
|
|
2569
2857
|
] })
|
|
2570
2858
|
] });
|
|
2571
2859
|
}
|
|
@@ -2573,12 +2861,12 @@ function memberContentSection() {
|
|
|
2573
2861
|
return {
|
|
2574
2862
|
id: "member-content",
|
|
2575
2863
|
label: "Member content",
|
|
2576
|
-
render: (ctx) => /* @__PURE__ */
|
|
2864
|
+
render: (ctx) => /* @__PURE__ */ jsx30(MemberContentAdmin, { ctx })
|
|
2577
2865
|
};
|
|
2578
2866
|
}
|
|
2579
2867
|
|
|
2580
2868
|
// src/ui/admin-settings-workspace.tsx
|
|
2581
|
-
import { jsx as
|
|
2869
|
+
import { jsx as jsx31 } from "preact/jsx-runtime";
|
|
2582
2870
|
var viewContext = (ctx, viewId, active) => {
|
|
2583
2871
|
const route = { workspaceId: ctx.route.workspaceId, viewId, ...active && ctx.route.recordId ? { recordId: ctx.route.recordId } : {}, ...active && ctx.route.detailTab ? { detailTab: ctx.route.detailTab } : {} };
|
|
2584
2872
|
const targetFromView = (target) => {
|
|
@@ -2596,7 +2884,7 @@ function SettingsWorkspace({ chapter, ctx }) {
|
|
|
2596
2884
|
const email = emailSection({ label: copy.workspaces.email });
|
|
2597
2885
|
const content = memberContentSection();
|
|
2598
2886
|
const library = builderLibrarySection();
|
|
2599
|
-
return /* @__PURE__ */
|
|
2887
|
+
return /* @__PURE__ */ jsx31(Tabs2, { ariaLabel: copy.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2600
2888
|
{ value: "calendar", label: copy.workspaces.calendar, href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }), panel: calendar.render(viewContext(ctx, "calendar", active === "calendar")) },
|
|
2601
2889
|
{ value: "email", label: copy.workspaces.email, href: ctx.href({ viewId: "email", recordId: null, detailTab: null }), panel: email.render(viewContext(ctx, "email", active === "email")) },
|
|
2602
2890
|
...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 +2895,14 @@ function LeaderSettingsWorkspace({ chapter, ctx }) {
|
|
|
2607
2895
|
const email = emailSection({ label: chapter.copy.admin.workspaces.email });
|
|
2608
2896
|
const library = builderLibrarySection();
|
|
2609
2897
|
const active = ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : "email";
|
|
2610
|
-
return /* @__PURE__ */
|
|
2898
|
+
return /* @__PURE__ */ jsx31(Tabs2, { ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2611
2899
|
{ 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
2900
|
...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
2901
|
] });
|
|
2614
2902
|
}
|
|
2615
2903
|
|
|
2616
2904
|
// src/ui/admin-workspaces.tsx
|
|
2617
|
-
import { jsx as
|
|
2905
|
+
import { jsx as jsx32 } from "preact/jsx-runtime";
|
|
2618
2906
|
var adminViewContext = (ctx, viewId, active) => {
|
|
2619
2907
|
const route = {
|
|
2620
2908
|
workspaceId: ctx.route.workspaceId,
|
|
@@ -2640,7 +2928,7 @@ function DashboardWorkspace({ chapter, ctx }) {
|
|
|
2640
2928
|
const active = ctx.route.viewId === "billing" ? "billing" : "overview";
|
|
2641
2929
|
const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
|
|
2642
2930
|
const billing = billingSection({ label: copy.workspaces.billing });
|
|
2643
|
-
return /* @__PURE__ */
|
|
2931
|
+
return /* @__PURE__ */ jsx32(
|
|
2644
2932
|
Tabs3,
|
|
2645
2933
|
{
|
|
2646
2934
|
ariaLabel: copy.workspaces.dashboardViewsLabel,
|
|
@@ -2668,7 +2956,7 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
2668
2956
|
const fallback = types[0]?.[0] ?? "person";
|
|
2669
2957
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
2670
2958
|
const networkSharing = chapter.network.targets.length > 0;
|
|
2671
|
-
return /* @__PURE__ */
|
|
2959
|
+
return /* @__PURE__ */ jsx32(
|
|
2672
2960
|
Tabs3,
|
|
2673
2961
|
{
|
|
2674
2962
|
ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
|
|
@@ -2681,7 +2969,8 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
2681
2969
|
type,
|
|
2682
2970
|
lifecycle: chapter.mode === "chapter" && type === "person",
|
|
2683
2971
|
roles: chapter.auth.ladder,
|
|
2684
|
-
networkSharing
|
|
2972
|
+
networkSharing,
|
|
2973
|
+
applicationStages: chapter.pipeline.stages
|
|
2685
2974
|
});
|
|
2686
2975
|
return {
|
|
2687
2976
|
value: type,
|
|
@@ -2698,8 +2987,8 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2698
2987
|
const types = Object.entries(chapter.crm.config.types).filter(([, def]) => def.workspace === "portfolio");
|
|
2699
2988
|
const fallback = types[0]?.[0] ?? "";
|
|
2700
2989
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
2701
|
-
if (!active) return /* @__PURE__ */
|
|
2702
|
-
return /* @__PURE__ */
|
|
2990
|
+
if (!active) return /* @__PURE__ */ jsx32(LeaderNetworkOverview, { ctx });
|
|
2991
|
+
return /* @__PURE__ */ jsx32(
|
|
2703
2992
|
Tabs3,
|
|
2704
2993
|
{
|
|
2705
2994
|
ariaLabel: copy.workspaces.portfolioViewsLabel,
|
|
@@ -2723,7 +3012,7 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2723
3012
|
);
|
|
2724
3013
|
}
|
|
2725
3014
|
function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
2726
|
-
return /* @__PURE__ */
|
|
3015
|
+
return /* @__PURE__ */ jsx32(
|
|
2727
3016
|
Tabs3,
|
|
2728
3017
|
{
|
|
2729
3018
|
ariaLabel: chapter.copy.admin.workspaces.dashboardViewsLabel,
|
|
@@ -2733,7 +3022,7 @@ function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
|
2733
3022
|
value: "overview",
|
|
2734
3023
|
label: chapter.copy.admin.workspaces.overview,
|
|
2735
3024
|
href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
|
|
2736
|
-
panel: /* @__PURE__ */
|
|
3025
|
+
panel: /* @__PURE__ */ jsx32(LeaderNetworkOverview, { ctx: adminViewContext(ctx, "overview", true) })
|
|
2737
3026
|
}]
|
|
2738
3027
|
}
|
|
2739
3028
|
);
|
|
@@ -2744,7 +3033,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2744
3033
|
id: "people",
|
|
2745
3034
|
label: copy.workspaces.people,
|
|
2746
3035
|
defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
|
|
2747
|
-
render: (ctx) => /* @__PURE__ */
|
|
3036
|
+
render: (ctx) => /* @__PURE__ */ jsx32(PeopleWorkspace, { chapter, ctx })
|
|
2748
3037
|
};
|
|
2749
3038
|
if (chapter.mode === "hub" && chapter.network.targets.length === 0) return [people];
|
|
2750
3039
|
if (chapter.mode === "hub") {
|
|
@@ -2754,7 +3043,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2754
3043
|
id: "dashboard",
|
|
2755
3044
|
label: copy.workspaces.dashboard,
|
|
2756
3045
|
defaultViewId: "overview",
|
|
2757
|
-
render: (ctx) => /* @__PURE__ */
|
|
3046
|
+
render: (ctx) => /* @__PURE__ */ jsx32(LeaderDashboardWorkspace, { chapter, ctx })
|
|
2758
3047
|
},
|
|
2759
3048
|
networkRecordsWorkspace(chapter),
|
|
2760
3049
|
people,
|
|
@@ -2762,13 +3051,13 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2762
3051
|
id: "portfolio",
|
|
2763
3052
|
label: copy.workspaces.portfolio,
|
|
2764
3053
|
defaultViewId: portfolioTypes[0]?.[0],
|
|
2765
|
-
render: (ctx) => /* @__PURE__ */
|
|
3054
|
+
render: (ctx) => /* @__PURE__ */ jsx32(PortfolioWorkspace, { chapter, ctx })
|
|
2766
3055
|
},
|
|
2767
3056
|
{
|
|
2768
3057
|
id: "settings",
|
|
2769
3058
|
label: copy.workspaces.settings,
|
|
2770
3059
|
defaultViewId: "email",
|
|
2771
|
-
render: (ctx) => /* @__PURE__ */
|
|
3060
|
+
render: (ctx) => /* @__PURE__ */ jsx32(LeaderSettingsWorkspace, { chapter, ctx })
|
|
2772
3061
|
}
|
|
2773
3062
|
];
|
|
2774
3063
|
}
|
|
@@ -2777,7 +3066,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2777
3066
|
id: "dashboard",
|
|
2778
3067
|
label: copy.workspaces.dashboard,
|
|
2779
3068
|
defaultViewId: "overview",
|
|
2780
|
-
render: (ctx) => /* @__PURE__ */
|
|
3069
|
+
render: (ctx) => /* @__PURE__ */ jsx32(DashboardWorkspace, { chapter, ctx })
|
|
2781
3070
|
},
|
|
2782
3071
|
people,
|
|
2783
3072
|
admissionGrantsWorkspace(copy.workspaces.admission),
|
|
@@ -2785,17 +3074,17 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2785
3074
|
id: "settings",
|
|
2786
3075
|
label: copy.workspaces.settings,
|
|
2787
3076
|
defaultViewId: "calendar",
|
|
2788
|
-
render: (ctx) => /* @__PURE__ */
|
|
3077
|
+
render: (ctx) => /* @__PURE__ */ jsx32(SettingsWorkspace, { chapter, ctx })
|
|
2789
3078
|
}
|
|
2790
3079
|
];
|
|
2791
3080
|
}
|
|
2792
3081
|
|
|
2793
3082
|
// src/ui/admin-frame.tsx
|
|
2794
3083
|
import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
|
|
2795
|
-
import { jsx as
|
|
3084
|
+
import { jsx as jsx33, jsxs as jsxs29 } from "preact/jsx-runtime";
|
|
2796
3085
|
function AdminTheme(props) {
|
|
2797
3086
|
const brand = props.chapter?.brand;
|
|
2798
|
-
return /* @__PURE__ */
|
|
3087
|
+
return /* @__PURE__ */ jsx33(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs29(
|
|
2799
3088
|
ThemeScope2,
|
|
2800
3089
|
{
|
|
2801
3090
|
ref: props.scopeRef,
|
|
@@ -2804,7 +3093,7 @@ function AdminTheme(props) {
|
|
|
2804
3093
|
accent: brand?.accent,
|
|
2805
3094
|
colorScheme: brand?.colorScheme ?? "light",
|
|
2806
3095
|
children: [
|
|
2807
|
-
/* @__PURE__ */
|
|
3096
|
+
/* @__PURE__ */ jsx33(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
|
|
2808
3097
|
props.children
|
|
2809
3098
|
]
|
|
2810
3099
|
}
|
|
@@ -2824,10 +3113,10 @@ function signInRedirect(basePath, workspaces) {
|
|
|
2824
3113
|
}
|
|
2825
3114
|
|
|
2826
3115
|
// src/ui/admin-appearance.ts
|
|
2827
|
-
import { useEffect as useEffect11, useState as
|
|
3116
|
+
import { useEffect as useEffect11, useState as useState26 } from "preact/hooks";
|
|
2828
3117
|
import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
|
|
2829
3118
|
function useScopedClerkAppearance(scope) {
|
|
2830
|
-
const [appearance, setAppearance] =
|
|
3119
|
+
const [appearance, setAppearance] = useState26();
|
|
2831
3120
|
useEffect11(() => {
|
|
2832
3121
|
setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
|
|
2833
3122
|
}, [scope]);
|
|
@@ -2859,7 +3148,7 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
|
|
|
2859
3148
|
}
|
|
2860
3149
|
|
|
2861
3150
|
// src/ui/admin.tsx
|
|
2862
|
-
import { jsx as
|
|
3151
|
+
import { jsx as jsx34, jsxs as jsxs30 } from "preact/jsx-runtime";
|
|
2863
3152
|
function Authed(props) {
|
|
2864
3153
|
const copy = useChapterCopy().admin;
|
|
2865
3154
|
const {
|
|
@@ -2887,7 +3176,7 @@ function Authed(props) {
|
|
|
2887
3176
|
}),
|
|
2888
3177
|
[getToken, crmBasePath]
|
|
2889
3178
|
);
|
|
2890
|
-
const [state, setState] =
|
|
3179
|
+
const [state, setState] = useState27({ status: "checking" });
|
|
2891
3180
|
useEffect12(() => {
|
|
2892
3181
|
let live = true;
|
|
2893
3182
|
void (async () => {
|
|
@@ -2907,17 +3196,17 @@ function Authed(props) {
|
|
|
2907
3196
|
live = false;
|
|
2908
3197
|
};
|
|
2909
3198
|
}, [getToken, apiBase, auth]);
|
|
2910
|
-
if (state.status === "checking") return /* @__PURE__ */
|
|
3199
|
+
if (state.status === "checking") return /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.auth.checking }) });
|
|
2911
3200
|
if (state.status === "denied") {
|
|
2912
|
-
return /* @__PURE__ */
|
|
2913
|
-
/* @__PURE__ */
|
|
2914
|
-
/* @__PURE__ */
|
|
3201
|
+
return /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsxs30("div", { className: "card", style: S.card, children: [
|
|
3202
|
+
/* @__PURE__ */ jsx34("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
|
|
3203
|
+
/* @__PURE__ */ jsx34("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
|
|
2915
3204
|
account: state.email ?? copy.auth.thisAccount
|
|
2916
3205
|
}) }),
|
|
2917
|
-
/* @__PURE__ */
|
|
3206
|
+
/* @__PURE__ */ jsx34("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
|
|
2918
3207
|
] }) });
|
|
2919
3208
|
}
|
|
2920
|
-
return /* @__PURE__ */
|
|
3209
|
+
return /* @__PURE__ */ jsx34(
|
|
2921
3210
|
AdminShell,
|
|
2922
3211
|
{
|
|
2923
3212
|
workspaces,
|
|
@@ -2959,7 +3248,7 @@ function ChapterAdmin(props) {
|
|
|
2959
3248
|
const chrome = props.chrome ?? "embedded";
|
|
2960
3249
|
const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
|
|
2961
3250
|
const redirect = signInRedirect(basePath, workspaces);
|
|
2962
|
-
const [publishableKey, setPublishableKey] =
|
|
3251
|
+
const [publishableKey, setPublishableKey] = useState27(void 0);
|
|
2963
3252
|
const scopeRef = useRef3(null);
|
|
2964
3253
|
const appearance = useScopedClerkAppearance(scopeRef);
|
|
2965
3254
|
useEffect12(() => {
|
|
@@ -2971,23 +3260,23 @@ function ChapterAdmin(props) {
|
|
|
2971
3260
|
}, [apiBase, auth]);
|
|
2972
3261
|
let content;
|
|
2973
3262
|
if (publishableKey === void 0 || publishableKey && !appearance) {
|
|
2974
|
-
content = /* @__PURE__ */
|
|
3263
|
+
content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
2975
3264
|
} else if (!publishableKey) {
|
|
2976
|
-
content = /* @__PURE__ */
|
|
2977
|
-
/* @__PURE__ */
|
|
2978
|
-
/* @__PURE__ */
|
|
3265
|
+
content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsxs30("div", { className: "card", style: S.card, children: [
|
|
3266
|
+
/* @__PURE__ */ jsx34("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
|
|
3267
|
+
/* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
|
|
2979
3268
|
] }) });
|
|
2980
3269
|
} else if (workspaces.length === 0) {
|
|
2981
|
-
content = /* @__PURE__ */
|
|
3270
|
+
content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
|
|
2982
3271
|
} else {
|
|
2983
|
-
content = /* @__PURE__ */
|
|
3272
|
+
content = /* @__PURE__ */ jsxs30(
|
|
2984
3273
|
ClerkGate,
|
|
2985
3274
|
{
|
|
2986
3275
|
publishableKey,
|
|
2987
3276
|
appearance,
|
|
2988
3277
|
afterSignOutUrl: "/",
|
|
2989
3278
|
children: [
|
|
2990
|
-
/* @__PURE__ */
|
|
3279
|
+
/* @__PURE__ */ jsx34(SignedOut, { children: /* @__PURE__ */ jsx34(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx34(
|
|
2991
3280
|
SignIn,
|
|
2992
3281
|
{
|
|
2993
3282
|
routing: "hash",
|
|
@@ -2995,7 +3284,7 @@ function ChapterAdmin(props) {
|
|
|
2995
3284
|
signUpForceRedirectUrl: redirect
|
|
2996
3285
|
}
|
|
2997
3286
|
) }) }),
|
|
2998
|
-
/* @__PURE__ */
|
|
3287
|
+
/* @__PURE__ */ jsx34(SignedIn, { children: /* @__PURE__ */ jsx34(
|
|
2999
3288
|
Authed,
|
|
3000
3289
|
{
|
|
3001
3290
|
workspaces,
|
|
@@ -3016,7 +3305,7 @@ function ChapterAdmin(props) {
|
|
|
3016
3305
|
}
|
|
3017
3306
|
);
|
|
3018
3307
|
}
|
|
3019
|
-
return /* @__PURE__ */
|
|
3308
|
+
return /* @__PURE__ */ jsx34(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
|
|
3020
3309
|
}
|
|
3021
3310
|
|
|
3022
3311
|
// src/ui/admin-defaults.ts
|
|
@@ -3025,17 +3314,17 @@ function defaultAdminSections(chapter) {
|
|
|
3025
3314
|
}
|
|
3026
3315
|
|
|
3027
3316
|
// src/ui/admin-meetings.tsx
|
|
3028
|
-
import { useState as
|
|
3029
|
-
import { Button as
|
|
3030
|
-
import { Fragment as
|
|
3317
|
+
import { useState as useState28 } from "preact/hooks";
|
|
3318
|
+
import { Button as Button17 } from "@odla-ai/ui/components";
|
|
3319
|
+
import { Fragment as Fragment12, jsx as jsx35, jsxs as jsxs31 } from "preact/jsx-runtime";
|
|
3031
3320
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
3032
3321
|
function MeetingsBody({ ctx }) {
|
|
3033
3322
|
const copy = useChapterCopy().admin.meetings;
|
|
3034
3323
|
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__ */
|
|
3324
|
+
const [busy, setBusy] = useState28(null);
|
|
3325
|
+
const [note, setNote] = useState28(null);
|
|
3326
|
+
if (loading) return /* @__PURE__ */ jsx35(AdminNote, { children: copy.loading });
|
|
3327
|
+
if (error || !data) return /* @__PURE__ */ jsxs31(AdminNote, { children: [
|
|
3039
3328
|
copy.loadFailed,
|
|
3040
3329
|
error ? `: ${error}` : "",
|
|
3041
3330
|
"."
|
|
@@ -3066,43 +3355,43 @@ function MeetingsBody({ ctx }) {
|
|
|
3066
3355
|
}
|
|
3067
3356
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
3068
3357
|
};
|
|
3069
|
-
return /* @__PURE__ */
|
|
3358
|
+
return /* @__PURE__ */ jsx35(AdminPage, { children: /* @__PURE__ */ jsx35(Panel, { title: /* @__PURE__ */ jsxs31(Fragment12, { children: [
|
|
3070
3359
|
copy.agenda,
|
|
3071
3360
|
" ",
|
|
3072
|
-
/* @__PURE__ */
|
|
3361
|
+
/* @__PURE__ */ jsxs31("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
3073
3362
|
"\xB7 ",
|
|
3074
3363
|
data.timezone
|
|
3075
3364
|
] })
|
|
3076
|
-
] }), actions: note ? /* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
3078
|
-
/* @__PURE__ */
|
|
3079
|
-
/* @__PURE__ */
|
|
3080
|
-
m.drift && m.drift !== "none" ? /* @__PURE__ */
|
|
3365
|
+
] }), actions: note ? /* @__PURE__ */ jsx35("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx35("p", { className: "muted", style: { margin: 0 }, children: copy.empty }) : /* @__PURE__ */ jsx35("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs31("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
|
|
3366
|
+
/* @__PURE__ */ jsx35("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
|
|
3367
|
+
/* @__PURE__ */ jsx35("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
|
|
3368
|
+
/* @__PURE__ */ jsx35("span", { className: "badge", children: m.status }),
|
|
3369
|
+
m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs31("span", { className: "badge", children: [
|
|
3081
3370
|
copy.drift,
|
|
3082
3371
|
": ",
|
|
3083
3372
|
m.drift
|
|
3084
3373
|
] }) : null,
|
|
3085
|
-
m.meetUrl ? /* @__PURE__ */
|
|
3086
|
-
m.status === "scheduled" ? /* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3088
|
-
/* @__PURE__ */
|
|
3374
|
+
m.meetUrl ? /* @__PURE__ */ jsx35("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
3375
|
+
m.status === "scheduled" ? /* @__PURE__ */ jsxs31(Fragment12, { children: [
|
|
3376
|
+
/* @__PURE__ */ jsx35(Button17, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
3377
|
+
/* @__PURE__ */ jsx35(Button17, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
3089
3378
|
] }) : null
|
|
3090
3379
|
] }, m.id)) }) }) });
|
|
3091
3380
|
}
|
|
3092
3381
|
function meetingsSection(options = {}) {
|
|
3093
3382
|
const { id = "meetings", label = "Meetings" } = options;
|
|
3094
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
3383
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx35(MeetingsBody, { ctx }) };
|
|
3095
3384
|
}
|
|
3096
3385
|
|
|
3097
3386
|
// src/ui/admin-network-record-editor.tsx
|
|
3098
|
-
import { Button as
|
|
3099
|
-
import { useRef as useRef4, useState as
|
|
3100
|
-
import { Fragment as
|
|
3387
|
+
import { Button as Button18, Field as Field12, Select as Select8 } from "@odla-ai/ui/components";
|
|
3388
|
+
import { useRef as useRef4, useState as useState29 } from "preact/hooks";
|
|
3389
|
+
import { Fragment as Fragment13, jsx as jsx36, jsxs as jsxs32 } from "preact/jsx-runtime";
|
|
3101
3390
|
function NetworkRecordEditor(props) {
|
|
3102
3391
|
const { ctx, record } = props;
|
|
3103
|
-
const [stage, setStage] =
|
|
3104
|
-
const [busy, setBusy] =
|
|
3105
|
-
const [error, setError] =
|
|
3392
|
+
const [stage, setStage] = useState29("");
|
|
3393
|
+
const [busy, setBusy] = useState29(false);
|
|
3394
|
+
const [error, setError] = useState29(null);
|
|
3106
3395
|
const attempt = useRef4(null);
|
|
3107
3396
|
const pending = record.edit?.pendingAction;
|
|
3108
3397
|
const superAdmin = ctx.currentUser?.superAdmin === true;
|
|
@@ -3135,11 +3424,11 @@ function NetworkRecordEditor(props) {
|
|
|
3135
3424
|
setBusy(false);
|
|
3136
3425
|
}
|
|
3137
3426
|
};
|
|
3138
|
-
return /* @__PURE__ */
|
|
3139
|
-
superAdmin && record.edit ? /* @__PURE__ */
|
|
3140
|
-
/* @__PURE__ */
|
|
3141
|
-
/* @__PURE__ */
|
|
3142
|
-
/* @__PURE__ */
|
|
3427
|
+
return /* @__PURE__ */ jsxs32("div", { style: { display: "grid", gap: 24 }, children: [
|
|
3428
|
+
superAdmin && record.edit ? /* @__PURE__ */ jsxs32(Fragment13, { children: [
|
|
3429
|
+
/* @__PURE__ */ jsxs32("section", { children: [
|
|
3430
|
+
/* @__PURE__ */ jsx36("h3", { children: "Chapter profile" }),
|
|
3431
|
+
/* @__PURE__ */ jsx36(
|
|
3143
3432
|
OperationalProfileForm,
|
|
3144
3433
|
{
|
|
3145
3434
|
fields: record.edit.fields,
|
|
@@ -3149,27 +3438,27 @@ function NetworkRecordEditor(props) {
|
|
|
3149
3438
|
`${record.id}:${record.revision}`
|
|
3150
3439
|
)
|
|
3151
3440
|
] }),
|
|
3152
|
-
/* @__PURE__ */
|
|
3153
|
-
/* @__PURE__ */
|
|
3154
|
-
pending ? /* @__PURE__ */
|
|
3441
|
+
/* @__PURE__ */ jsxs32("section", { style: { display: "grid", gap: 12 }, children: [
|
|
3442
|
+
/* @__PURE__ */ jsx36("h3", { children: "Membership actions" }),
|
|
3443
|
+
pending ? /* @__PURE__ */ jsxs32("p", { children: [
|
|
3155
3444
|
"An application action is pending: ",
|
|
3156
3445
|
pending.stage.replaceAll("_", " "),
|
|
3157
3446
|
". ",
|
|
3158
3447
|
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__ */
|
|
3448
|
+
] }) : record.edit.allowedStages.length ? /* @__PURE__ */ jsx36(Field12, { label: "Action", children: /* @__PURE__ */ jsxs32(Select8, { "aria-label": "Action", value: stage, disabled: busy, onChange: (event) => setStage(event.currentTarget.value), children: [
|
|
3449
|
+
/* @__PURE__ */ jsx36("option", { value: "", children: "Choose an action" }),
|
|
3450
|
+
record.edit.allowedStages.map((action) => /* @__PURE__ */ jsx36("option", { value: action.id, children: action.label }, action.id))
|
|
3451
|
+
] }) }) : /* @__PURE__ */ jsx36("p", { children: "No membership action is currently allowed." }),
|
|
3452
|
+
pending || record.edit.allowedStages.length ? /* @__PURE__ */ jsx36("div", { children: /* @__PURE__ */ jsx36(Button18, { disabled: busy || !ownPending || !pending && !stage, onClick: () => void move(), children: busy ? "Applying\u2026" : pending ? "Retry pending action" : "Apply action" }) }) : null,
|
|
3453
|
+
error ? /* @__PURE__ */ jsx36("p", { role: "alert", children: error }) : null
|
|
3165
3454
|
] })
|
|
3166
3455
|
] }) : null,
|
|
3167
|
-
superAdmin && props.admissionGrants ? /* @__PURE__ */
|
|
3168
|
-
/* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
/* @__PURE__ */
|
|
3456
|
+
superAdmin && props.admissionGrants ? /* @__PURE__ */ jsxs32("section", { children: [
|
|
3457
|
+
/* @__PURE__ */ jsx36("h3", { children: "Next invitation" }),
|
|
3458
|
+
/* @__PURE__ */ jsx36("p", { children: "This setting applies to the next eligible signup; it does not replace approval of an existing application." }),
|
|
3459
|
+
/* @__PURE__ */ jsx36(LeaderAdmissionAction, { getToken: ctx.getToken, targetId: props.targetId, sourceRecordId: record.id })
|
|
3171
3460
|
] }) : null,
|
|
3172
|
-
/* @__PURE__ */
|
|
3461
|
+
/* @__PURE__ */ jsx36(
|
|
3173
3462
|
NetworkRecordDetail,
|
|
3174
3463
|
{
|
|
3175
3464
|
ctx,
|
|
@@ -3183,6 +3472,57 @@ function NetworkRecordEditor(props) {
|
|
|
3183
3472
|
] });
|
|
3184
3473
|
}
|
|
3185
3474
|
|
|
3475
|
+
// src/ui/network-seat-billing.tsx
|
|
3476
|
+
import { useState as useState30 } from "preact/hooks";
|
|
3477
|
+
import { Button as Button19, DataTable as DataTable4 } from "@odla-ai/ui/components";
|
|
3478
|
+
import { jsx as jsx37, jsxs as jsxs33 } from "preact/jsx-runtime";
|
|
3479
|
+
var money3 = (row, cents) => row.currency ? new Intl.NumberFormat("en-US", {
|
|
3480
|
+
style: "currency",
|
|
3481
|
+
currency: row.currency
|
|
3482
|
+
}).format(cents / 100) : "Unknown";
|
|
3483
|
+
var columns2 = (refund, busy) => [
|
|
3484
|
+
{ key: "seatId", header: "Seat", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.seatId }) },
|
|
3485
|
+
{ key: "sourceKey", header: "Chapter / runtime", cell: (row) => row.sourceKey },
|
|
3486
|
+
{ key: "status", header: "Payment", cell: (row) => row.status ?? "Unknown" },
|
|
3487
|
+
{ key: "amountReceivedCents", header: "Collected", cell: (row) => money3(row, row.amountReceivedCents) },
|
|
3488
|
+
{ key: "refundedCents", header: "Refunded", cell: (row) => money3(row, row.refundedCents) },
|
|
3489
|
+
{ key: "membershipId", header: "Primary membership", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.membershipId }) },
|
|
3490
|
+
{ key: "paymentIntentId", header: "Stripe payment", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.paymentIntentId }) },
|
|
3491
|
+
{ key: "subscriptionItemId", header: "Renewal item", cell: (row) => row.subscriptionItemId ? /* @__PURE__ */ jsx37("code", { children: row.subscriptionItemId }) : "Awaiting provider linkage" },
|
|
3492
|
+
{ key: "currentPeriodEnd", header: "Paid through", cell: (row) => row.currentPeriodEnd ? new Date(row.currentPeriodEnd * 1e3).toLocaleDateString() : "Awaiting provider linkage" },
|
|
3493
|
+
{ key: "refund", header: "Refund", cell: (row) => row.refundedCents < row.amountReceivedCents ? /* @__PURE__ */ jsx37(Button19, { variant: "danger", mini: true, disabled: busy !== null, onClick: () => refund(row), children: busy === row.seatId ? "Refunding\u2026" : "Refund seat" }) : "Refunded" }
|
|
3494
|
+
];
|
|
3495
|
+
function NetworkSeatBilling(props) {
|
|
3496
|
+
const { data, loading, error, refresh } = useAdminResource(props.getToken, "/api/admin/network-commercial-read-model");
|
|
3497
|
+
const [busy, setBusy] = useState30(null);
|
|
3498
|
+
const [note, setNote] = useState30(null);
|
|
3499
|
+
const refund = async (row) => {
|
|
3500
|
+
setBusy(row.seatId);
|
|
3501
|
+
setNote(null);
|
|
3502
|
+
try {
|
|
3503
|
+
const receipt = await adminFetch(
|
|
3504
|
+
props.getToken,
|
|
3505
|
+
`/api/admin/membership-seats/${encodeURIComponent(row.seatId)}/refund`,
|
|
3506
|
+
{ method: "POST" }
|
|
3507
|
+
);
|
|
3508
|
+
setNote(receipt.replayed ? `Seat ${row.seatId} was already refunded.` : `Refunded ${money3(row, receipt.refundedCents ?? row.amountReceivedCents)} for seat ${row.seatId}.`);
|
|
3509
|
+
refresh();
|
|
3510
|
+
} catch (failure) {
|
|
3511
|
+
setNote(failure instanceof Error ? failure.message : "Refund could not be completed.");
|
|
3512
|
+
} finally {
|
|
3513
|
+
setBusy(null);
|
|
3514
|
+
}
|
|
3515
|
+
};
|
|
3516
|
+
return /* @__PURE__ */ jsxs33(Panel, { title: "Additional membership seats", children: [
|
|
3517
|
+
/* @__PURE__ */ jsx37("p", { children: "Seat payments are included in the network totals. Renewal items share the primary subscription\u2019s term. An unaccepted seat is never refunded automatically." }),
|
|
3518
|
+
loading ? /* @__PURE__ */ jsx37("p", { children: "Loading seats\u2026" }) : error ? /* @__PURE__ */ jsxs33("p", { role: "alert", children: [
|
|
3519
|
+
"Seats could not be loaded: ",
|
|
3520
|
+
error
|
|
3521
|
+
] }) : /* @__PURE__ */ jsx37(DataTable4, { rows: data?.seats ?? [], columns: columns2((row) => void refund(row), busy), rowKey: (row) => `${row.sourceKey}:${row.seatId}` }),
|
|
3522
|
+
note ? /* @__PURE__ */ jsx37("p", { role: "status", children: note }) : null
|
|
3523
|
+
] });
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3186
3526
|
export {
|
|
3187
3527
|
AdminPage,
|
|
3188
3528
|
Panel,
|
|
@@ -3194,11 +3534,16 @@ export {
|
|
|
3194
3534
|
adminSectionHref,
|
|
3195
3535
|
adminFetch,
|
|
3196
3536
|
useAdminResource,
|
|
3537
|
+
PaymentRecoveryQueue,
|
|
3197
3538
|
billingSection,
|
|
3198
3539
|
dashboardSection,
|
|
3540
|
+
applicationLifecycleAdapter,
|
|
3541
|
+
NamedSeatRecordPanel,
|
|
3542
|
+
APPLICATION_STAGES,
|
|
3543
|
+
CandidateStagePanel,
|
|
3544
|
+
RecordAdmissionPanel,
|
|
3199
3545
|
RecordActions,
|
|
3200
3546
|
NetworkShareActions,
|
|
3201
|
-
applicationLifecycleAdapter,
|
|
3202
3547
|
collectionSection,
|
|
3203
3548
|
peopleSection,
|
|
3204
3549
|
NetworkRecordsWorkspace,
|
|
@@ -3219,6 +3564,7 @@ export {
|
|
|
3219
3564
|
ChapterAdmin,
|
|
3220
3565
|
defaultAdminSections,
|
|
3221
3566
|
meetingsSection,
|
|
3222
|
-
NetworkRecordEditor
|
|
3567
|
+
NetworkRecordEditor,
|
|
3568
|
+
NetworkSeatBilling
|
|
3223
3569
|
};
|
|
3224
|
-
//# sourceMappingURL=chunk-
|
|
3570
|
+
//# sourceMappingURL=chunk-4EA5V4JE.js.map
|