@odla-ai/chapter 0.39.0 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/{chunk-SI3JHSEX.js → chunk-4K2Q5JBC.js} +660 -553
- package/dist/chunk-4K2Q5JBC.js.map +1 -0
- package/dist/{copy-context-Pbs3keLx.d.ts → copy-context-Buz_27Ed.d.ts} +26 -0
- package/dist/index.cjs +457 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -2
- package/dist/index.d.ts +196 -2
- package/dist/index.js +457 -130
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +106 -3
- package/dist/ui/admin/index.js +5 -1
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +5 -1
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/worker/index.cjs +587 -108
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +127 -1
- package/dist/worker/index.d.ts +127 -1
- package/dist/worker/index.js +583 -104
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/greenfield.md +15 -0
- package/dist/chunk-SI3JHSEX.js.map +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-IAWBE5PX.js";
|
|
8
8
|
|
|
9
9
|
// src/ui/admin.tsx
|
|
10
|
-
import { useEffect as
|
|
10
|
+
import { useEffect as useEffect11, useMemo as useMemo2, useRef, useState as useState17 } 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
|
|
|
@@ -438,11 +438,10 @@ function AdminShell({
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
// src/ui/admin-workspaces.tsx
|
|
441
|
-
import { Tabs as
|
|
441
|
+
import { Tabs as Tabs3 } from "@odla-ai/ui/components";
|
|
442
442
|
|
|
443
|
-
// src/ui/admin-
|
|
444
|
-
import {
|
|
445
|
-
import { Button, Checkbox, Field, Input } from "@odla-ai/ui/components";
|
|
443
|
+
// src/ui/admin-billing.tsx
|
|
444
|
+
import { DataTable, StatBand } from "@odla-ai/ui/components";
|
|
446
445
|
|
|
447
446
|
// src/ui/admin-api.ts
|
|
448
447
|
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "preact/hooks";
|
|
@@ -480,70 +479,8 @@ function useAdminResource(getToken, path) {
|
|
|
480
479
|
return { data, loading, error, refresh };
|
|
481
480
|
}
|
|
482
481
|
|
|
483
|
-
// src/ui/admin-availability.tsx
|
|
484
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
485
|
-
function AvailabilityBody({ ctx }) {
|
|
486
|
-
const copy = useChapterCopy().admin.availability;
|
|
487
|
-
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
|
|
488
|
-
const [draft, setDraft] = useState4(null);
|
|
489
|
-
const [busy, setBusy] = useState4(false);
|
|
490
|
-
const [note, setNote] = useState4(null);
|
|
491
|
-
const [errors, setErrors] = useState4({});
|
|
492
|
-
const cfg = draft ?? data?.scheduling ?? null;
|
|
493
|
-
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children: copy.loading });
|
|
494
|
-
if (error || !cfg) return /* @__PURE__ */ jsxs4(AdminNote, { children: [
|
|
495
|
-
copy.loadFailed,
|
|
496
|
-
error ? `: ${error}` : "",
|
|
497
|
-
"."
|
|
498
|
-
] });
|
|
499
|
-
const edit = (patch) => setDraft({ ...cfg, ...patch });
|
|
500
|
-
const toggleDay = (d) => edit({ days: cfg.days.includes(d) ? cfg.days.filter((x) => x !== d) : [...cfg.days, d].sort() });
|
|
501
|
-
const num = (v, fallback) => Number.isFinite(Number(v)) ? Number(v) : fallback;
|
|
502
|
-
const save = async () => {
|
|
503
|
-
setBusy(true);
|
|
504
|
-
setNote(null);
|
|
505
|
-
setErrors({});
|
|
506
|
-
const token = await ctx.getToken();
|
|
507
|
-
try {
|
|
508
|
-
const r = await fetch("/api/admin/scheduling", { method: "PUT", headers: { authorization: `Bearer ${token ?? ""}`, "content-type": "application/json" }, body: JSON.stringify(cfg) });
|
|
509
|
-
const body = await r.json().catch(() => ({}));
|
|
510
|
-
if (!r.ok) {
|
|
511
|
-
setErrors(body.errors ?? {});
|
|
512
|
-
setNote(body.error ?? `save failed (${r.status})`);
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
setDraft(null);
|
|
516
|
-
refresh();
|
|
517
|
-
setNote(copy.saved);
|
|
518
|
-
} catch (e) {
|
|
519
|
-
setNote(e instanceof Error ? e.message : String(e));
|
|
520
|
-
} finally {
|
|
521
|
-
setBusy(false);
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
const numField = (key, label) => /* @__PURE__ */ jsx4(Field, { label, error: errors[key], children: /* @__PURE__ */ jsx4(Input, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
|
|
525
|
-
return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx4("span", { className: "muted", children: note }) : void 0, children: [
|
|
526
|
-
/* @__PURE__ */ jsx4(Field, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx4(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
|
|
527
|
-
/* @__PURE__ */ jsxs4("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
|
|
528
|
-
numField("startHour", copy.startHour),
|
|
529
|
-
numField("endHour", copy.endHour),
|
|
530
|
-
numField("slotMinutes", copy.slotMinutes),
|
|
531
|
-
numField("minNoticeHours", copy.minNoticeHours),
|
|
532
|
-
numField("windowDays", copy.windowDays),
|
|
533
|
-
/* @__PURE__ */ jsx4(Field, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx4(Input, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
534
|
-
] }),
|
|
535
|
-
/* @__PURE__ */ jsx4(Field, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx4(Input, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
536
|
-
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
537
|
-
] }) });
|
|
538
|
-
}
|
|
539
|
-
function availabilitySection(options = {}) {
|
|
540
|
-
const { id = "availability", label = "Availability" } = options;
|
|
541
|
-
return { id, label, render: (ctx) => /* @__PURE__ */ jsx4(AvailabilityBody, { ctx }) };
|
|
542
|
-
}
|
|
543
|
-
|
|
544
482
|
// src/ui/admin-billing.tsx
|
|
545
|
-
import {
|
|
546
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
483
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
547
484
|
var money = (cents) => `$${(cents / 100).toLocaleString()}`;
|
|
548
485
|
var date = (t) => t ? new Date(t).toLocaleDateString([], { dateStyle: "medium" }) : "\u2014";
|
|
549
486
|
var columns = (copy) => [
|
|
@@ -557,13 +494,13 @@ var columns = (copy) => [
|
|
|
557
494
|
function BillingBody({ ctx }) {
|
|
558
495
|
const copy = useChapterCopy().admin.billing;
|
|
559
496
|
const { data, loading, error } = useAdminResource(ctx.getToken, "/api/admin/billing");
|
|
560
|
-
if (loading) return /* @__PURE__ */
|
|
561
|
-
if (error || !data) return /* @__PURE__ */
|
|
497
|
+
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children: copy.loading });
|
|
498
|
+
if (error || !data) return /* @__PURE__ */ jsxs4(AdminNote, { children: [
|
|
562
499
|
copy.loadFailed,
|
|
563
500
|
error ? `: ${error}` : "",
|
|
564
501
|
"."
|
|
565
502
|
] });
|
|
566
|
-
if (!data.billingReady) return /* @__PURE__ */
|
|
503
|
+
if (!data.billingReady) return /* @__PURE__ */ jsx4(AdminNote, { children: copy.notConfigured });
|
|
567
504
|
const s = data.summary;
|
|
568
505
|
const stats = s ? [
|
|
569
506
|
{ value: s.activeCount, label: copy.active },
|
|
@@ -571,22 +508,22 @@ function BillingBody({ ctx }) {
|
|
|
571
508
|
{ value: s.renewingSoonCount, label: copy.renewingSoon },
|
|
572
509
|
{ value: s.pastDueCount, label: copy.pastDue }
|
|
573
510
|
] : [];
|
|
574
|
-
return /* @__PURE__ */
|
|
575
|
-
s ? /* @__PURE__ */
|
|
576
|
-
/* @__PURE__ */
|
|
577
|
-
data.truncated ? /* @__PURE__ */
|
|
578
|
-
/* @__PURE__ */
|
|
511
|
+
return /* @__PURE__ */ jsxs4(AdminPage, { children: [
|
|
512
|
+
s ? /* @__PURE__ */ jsx4(StatBand, { stats }) : null,
|
|
513
|
+
/* @__PURE__ */ jsxs4(Panel, { title: copy.subscriptions, actions: data.testMode ? /* @__PURE__ */ jsx4("span", { className: "badge", children: copy.testMode }) : void 0, children: [
|
|
514
|
+
data.truncated ? /* @__PURE__ */ jsx4("p", { className: "badge", children: copy.truncated }) : null,
|
|
515
|
+
/* @__PURE__ */ jsx4(DataTable, { rows: data.rows, columns: columns(copy), rowKey: (r) => r.id })
|
|
579
516
|
] })
|
|
580
517
|
] });
|
|
581
518
|
}
|
|
582
519
|
function billingSection(options = {}) {
|
|
583
520
|
const { id = "billing", label = "Billing" } = options;
|
|
584
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
521
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx4(BillingBody, { ctx }) };
|
|
585
522
|
}
|
|
586
523
|
|
|
587
524
|
// src/ui/admin-dashboard.tsx
|
|
588
525
|
import { MetricWidget, StepPipeline } from "@odla-ai/ui/components";
|
|
589
|
-
import { Fragment as Fragment2, jsx as
|
|
526
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
590
527
|
var number = (value) => Math.round(value).toLocaleString();
|
|
591
528
|
var money2 = (cents) => `$${Math.round(cents / 100).toLocaleString()}`;
|
|
592
529
|
var when = (time) => new Date(time).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
@@ -596,49 +533,49 @@ function DashboardBody({ ctx }) {
|
|
|
596
533
|
ctx.getToken,
|
|
597
534
|
"/api/admin/dashboard"
|
|
598
535
|
);
|
|
599
|
-
if (loading) return /* @__PURE__ */
|
|
600
|
-
if (error || !data) return /* @__PURE__ */
|
|
536
|
+
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.loading });
|
|
537
|
+
if (error || !data) return /* @__PURE__ */ jsxs5(AdminNote, { children: [
|
|
601
538
|
copy.loadFailed,
|
|
602
539
|
error ? `: ${error}` : "",
|
|
603
540
|
"."
|
|
604
541
|
] });
|
|
605
542
|
const pipelineSteps = Object.entries(data.pipeline).filter(([stage]) => !["declined", "refunded"].includes(stage)).map(([stage, count]) => ({
|
|
606
|
-
icon: /* @__PURE__ */
|
|
543
|
+
icon: /* @__PURE__ */ jsx5("span", { style: { fontSize: 22, fontWeight: 700 }, children: count }),
|
|
607
544
|
title: stage.replaceAll("_", " "),
|
|
608
545
|
description: data.pipelineDelta[stage] ? formatChapterCopy(copy.thisWeek, { count: data.pipelineDelta[stage] }) : copy.noChange
|
|
609
546
|
}));
|
|
610
547
|
const exceptions = ["declined", "refunded"].filter((stage) => stage in data.pipeline).map((stage) => `${stage} ${data.pipeline[stage]}`).join(" \xB7 ");
|
|
611
|
-
return /* @__PURE__ */
|
|
612
|
-
/* @__PURE__ */
|
|
548
|
+
return /* @__PURE__ */ jsxs5(AdminPage, { children: [
|
|
549
|
+
/* @__PURE__ */ jsx5(
|
|
613
550
|
Panel,
|
|
614
551
|
{
|
|
615
|
-
title: /* @__PURE__ */
|
|
552
|
+
title: /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
616
553
|
copy.upcomingCalls,
|
|
617
554
|
" ",
|
|
618
|
-
/* @__PURE__ */
|
|
555
|
+
/* @__PURE__ */ jsxs5("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
619
556
|
"\xB7 ",
|
|
620
557
|
data.timezone
|
|
621
558
|
] })
|
|
622
559
|
] }),
|
|
623
|
-
actions: data.calls.needsAttention ? /* @__PURE__ */
|
|
624
|
-
children: data.agenda.length === 0 ? /* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
560
|
+
actions: data.calls.needsAttention ? /* @__PURE__ */ jsx5("span", { className: "badge", children: formatChapterCopy(copy.callsNeedAttention, { count: data.calls.needsAttention }) }) : void 0,
|
|
561
|
+
children: data.agenda.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "muted", style: { margin: 0 }, children: copy.noUpcomingCalls }) : /* @__PURE__ */ jsx5("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.agenda.map((meeting) => /* @__PURE__ */ jsxs5("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
562
|
+
/* @__PURE__ */ jsx5("strong", { style: { minWidth: 170 }, children: when(meeting.startAt) }),
|
|
563
|
+
/* @__PURE__ */ jsxs5("span", { style: { flex: 1 }, children: [
|
|
627
564
|
meeting.name,
|
|
628
565
|
meeting.email ? ` \xB7 ${meeting.email}` : ""
|
|
629
566
|
] }),
|
|
630
|
-
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */
|
|
631
|
-
meeting.htmlLink ? /* @__PURE__ */
|
|
632
|
-
meeting.meetUrl ? /* @__PURE__ */
|
|
567
|
+
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */ jsx5("span", { className: "badge", children: copy.drift }) : null,
|
|
568
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx5("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.open }) : null,
|
|
569
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx5("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null
|
|
633
570
|
] }, meeting.id)) })
|
|
634
571
|
}
|
|
635
572
|
),
|
|
636
|
-
/* @__PURE__ */
|
|
637
|
-
/* @__PURE__ */
|
|
638
|
-
data.membersSeries ? /* @__PURE__ */
|
|
573
|
+
/* @__PURE__ */ jsxs5("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 18 }, children: [
|
|
574
|
+
/* @__PURE__ */ jsx5(MetricWidget, { label: copy.applications, data: data.applicationsSeries, format: number }),
|
|
575
|
+
data.membersSeries ? /* @__PURE__ */ jsx5(MetricWidget, { label: copy.newMembers, data: data.membersSeries, format: number }) : /* @__PURE__ */ jsx5(Panel, { title: copy.newMembers, children: /* @__PURE__ */ jsx5("p", { className: "muted", children: copy.newMembersUnavailable }) })
|
|
639
576
|
] }),
|
|
640
|
-
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */
|
|
641
|
-
/* @__PURE__ */
|
|
577
|
+
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */ jsxs5("div", { children: [
|
|
578
|
+
/* @__PURE__ */ jsx5(
|
|
642
579
|
MetricWidget,
|
|
643
580
|
{
|
|
644
581
|
label: copy.revenueAdded,
|
|
@@ -647,7 +584,7 @@ function DashboardBody({ ctx }) {
|
|
|
647
584
|
size: "large"
|
|
648
585
|
}
|
|
649
586
|
),
|
|
650
|
-
/* @__PURE__ */
|
|
587
|
+
/* @__PURE__ */ jsxs5("p", { className: "muted", children: [
|
|
651
588
|
copy.activeMemberships,
|
|
652
589
|
": ",
|
|
653
590
|
data.revenue.activeCount ?? 0,
|
|
@@ -658,119 +595,32 @@ function DashboardBody({ ctx }) {
|
|
|
658
595
|
money2(data.revenue.annualRunRateCents ?? 0),
|
|
659
596
|
data.revenue.testMode ? ` \xB7 ${copy.testMode}` : ""
|
|
660
597
|
] })
|
|
661
|
-
] }) : /* @__PURE__ */
|
|
662
|
-
/* @__PURE__ */
|
|
663
|
-
/* @__PURE__ */
|
|
664
|
-
exceptions ? /* @__PURE__ */
|
|
598
|
+
] }) : /* @__PURE__ */ jsx5(Panel, { title: copy.revenue, children: /* @__PURE__ */ jsx5("p", { className: "muted", children: copy.revenueUnavailable }) }),
|
|
599
|
+
/* @__PURE__ */ jsxs5(Panel, { title: copy.pipeline, children: [
|
|
600
|
+
/* @__PURE__ */ jsx5(StepPipeline, { ariaLabel: copy.pipelineLabel, steps: pipelineSteps }),
|
|
601
|
+
exceptions ? /* @__PURE__ */ jsx5("p", { className: "muted", style: { margin: 0 }, children: exceptions }) : null
|
|
665
602
|
] })
|
|
666
603
|
] });
|
|
667
604
|
}
|
|
668
605
|
function dashboardSection(options = {}) {
|
|
669
606
|
const { id = "dashboard", label = "Dashboard" } = options;
|
|
670
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// src/ui/admin-email.tsx
|
|
674
|
-
import { useState as useState5 } from "preact/hooks";
|
|
675
|
-
import { Button as Button2, Checkbox as Checkbox2, DataTable as DataTable2, Field as Field2, Input as Input2, Textarea } from "@odla-ai/ui/components";
|
|
676
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
677
|
-
var logColumns = (copy) => [
|
|
678
|
-
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
679
|
-
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
680
|
-
{ key: "to", header: copy.toColumn, cell: (r) => r.to },
|
|
681
|
-
{ key: "status", header: copy.statusColumn, cell: (r) => r.error ? /* @__PURE__ */ jsx7("span", { className: "badge", children: copy.failed }) : r.redirected ? /* @__PURE__ */ jsx7("span", { className: "badge", children: copy.redirected }) : copy.delivered }
|
|
682
|
-
];
|
|
683
|
-
function EmailBody({ ctx }) {
|
|
684
|
-
const copy = useChapterCopy().admin.email;
|
|
685
|
-
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
686
|
-
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
687
|
-
const [draft, setDraft] = useState5(null);
|
|
688
|
-
const [busy, setBusy] = useState5(null);
|
|
689
|
-
const [note, setNote] = useState5(null);
|
|
690
|
-
const data = draft ?? cfg.data;
|
|
691
|
-
if (cfg.loading) return /* @__PURE__ */ jsx7(AdminNote, { children: copy.loading });
|
|
692
|
-
if (cfg.error || !data) return /* @__PURE__ */ jsxs7(AdminNote, { children: [
|
|
693
|
-
copy.loadFailed,
|
|
694
|
-
cfg.error ? `: ${cfg.error}` : "",
|
|
695
|
-
"."
|
|
696
|
-
] });
|
|
697
|
-
const edit = (patch) => setDraft({ ...data, ...patch });
|
|
698
|
-
const editTemplate = (key, patch) => edit({ emailTemplates: { ...data.emailTemplates, [key]: { ...data.emailTemplates[key], ...patch } } });
|
|
699
|
-
const run = async (key, fn) => {
|
|
700
|
-
setBusy(key);
|
|
701
|
-
setNote(null);
|
|
702
|
-
try {
|
|
703
|
-
await fn();
|
|
704
|
-
} catch (e) {
|
|
705
|
-
setNote(e instanceof Error ? e.message : String(e));
|
|
706
|
-
} finally {
|
|
707
|
-
setBusy(null);
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
|
-
const save = () => run("save", async () => {
|
|
711
|
-
await adminFetch(ctx.getToken, "/api/admin/group/email", { method: "PUT", body: JSON.stringify(data) });
|
|
712
|
-
setDraft(null);
|
|
713
|
-
cfg.refresh();
|
|
714
|
-
setNote(copy.saved);
|
|
715
|
-
});
|
|
716
|
-
const test = (template) => run(`test:${template}`, async () => {
|
|
717
|
-
const r = await adminFetch(ctx.getToken, "/api/admin/email/test", { method: "POST", body: JSON.stringify({ template }) });
|
|
718
|
-
setNote(r.ok ? formatChapterCopy(copy.sent, {
|
|
719
|
-
template,
|
|
720
|
-
address: r.to,
|
|
721
|
-
redirected: r.redirected ? ` (${copy.redirected})` : ""
|
|
722
|
-
}) : copy.testFailed);
|
|
723
|
-
log.refresh();
|
|
724
|
-
});
|
|
725
|
-
return /* @__PURE__ */ jsxs7(AdminPage, { children: [
|
|
726
|
-
/* @__PURE__ */ jsxs7(
|
|
727
|
-
Panel,
|
|
728
|
-
{
|
|
729
|
-
title: copy.delivery,
|
|
730
|
-
actions: /* @__PURE__ */ jsxs7("span", { className: "muted", children: [
|
|
731
|
-
data.envName,
|
|
732
|
-
" \xB7 ",
|
|
733
|
-
data.transport,
|
|
734
|
-
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
735
|
-
] }),
|
|
736
|
-
children: [
|
|
737
|
-
/* @__PURE__ */ jsx7(Field2, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx7(Input2, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
738
|
-
/* @__PURE__ */ jsx7(Field2, { label: copy.replyTo, children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
739
|
-
/* @__PURE__ */ jsx7(Field2, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
740
|
-
]
|
|
741
|
-
}
|
|
742
|
-
),
|
|
743
|
-
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */ jsxs7(Panel, { title: key, actions: /* @__PURE__ */ jsx7(Button2, { variant: "secondary", mini: true, disabled: busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }), children: [
|
|
744
|
-
/* @__PURE__ */ jsx7(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
745
|
-
/* @__PURE__ */ jsx7(Field2, { label: copy.subject, children: /* @__PURE__ */ jsx7(Input2, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
746
|
-
/* @__PURE__ */ jsx7(Field2, { label: copy.body, children: /* @__PURE__ */ jsx7(Textarea, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
|
|
747
|
-
] }, key)),
|
|
748
|
-
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
749
|
-
/* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
750
|
-
note ? /* @__PURE__ */ jsx7("span", { className: "muted", children: note }) : null
|
|
751
|
-
] }),
|
|
752
|
-
/* @__PURE__ */ jsx7(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx7(DataTable2, { rows: log.data?.sends ?? [], columns: logColumns(copy), rowKey: (r) => r.id }) })
|
|
753
|
-
] });
|
|
754
|
-
}
|
|
755
|
-
function emailSection(options = {}) {
|
|
756
|
-
const { id = "email", label = "Email" } = options;
|
|
757
|
-
return { id, label, render: (ctx) => /* @__PURE__ */ jsx7(EmailBody, { ctx }) };
|
|
607
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx5(DashboardBody, { ctx }) };
|
|
758
608
|
}
|
|
759
609
|
|
|
760
610
|
// src/ui/admin-people-workspace.tsx
|
|
761
611
|
import { BillingCard, CrmWorkspace } from "@odla-ai/crm/ui";
|
|
762
612
|
|
|
763
613
|
// src/ui/admin-record-actions.tsx
|
|
764
|
-
import { useEffect as useEffect4, useState as
|
|
765
|
-
import { Button
|
|
766
|
-
import { jsx as
|
|
614
|
+
import { useEffect as useEffect4, useState as useState4 } from "preact/hooks";
|
|
615
|
+
import { Button, Field, Select } from "@odla-ai/ui/components";
|
|
616
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
767
617
|
function RecordActions({ getToken, record, roles, onChanged }) {
|
|
768
618
|
const copy = useChapterCopy().admin.records;
|
|
769
619
|
const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
|
|
770
620
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
771
|
-
const [access, setAccess] =
|
|
772
|
-
const [busy, setBusy] =
|
|
773
|
-
const [note, setNote] =
|
|
621
|
+
const [access, setAccess] = useState4(null);
|
|
622
|
+
const [busy, setBusy] = useState4(null);
|
|
623
|
+
const [note, setNote] = useState4(null);
|
|
774
624
|
useEffect4(() => {
|
|
775
625
|
if (!userId) {
|
|
776
626
|
setAccess(null);
|
|
@@ -799,9 +649,9 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
799
649
|
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }), copy.approved);
|
|
800
650
|
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
|
|
801
651
|
if (!userId && !applicationId) return null;
|
|
802
|
-
return /* @__PURE__ */
|
|
803
|
-
/* @__PURE__ */
|
|
804
|
-
userId ? /* @__PURE__ */
|
|
652
|
+
return /* @__PURE__ */ jsxs6("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
|
|
653
|
+
/* @__PURE__ */ jsx6("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
|
|
654
|
+
userId ? /* @__PURE__ */ jsx6(Field, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx6(
|
|
805
655
|
Select,
|
|
806
656
|
{
|
|
807
657
|
value: access?.role ?? "",
|
|
@@ -809,24 +659,24 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
809
659
|
options: roles.map((r) => ({ value: r, label: r })),
|
|
810
660
|
onChange: (e) => void setRole(e.target.value)
|
|
811
661
|
}
|
|
812
|
-
) }) : /* @__PURE__ */
|
|
813
|
-
applicationId ? /* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
/* @__PURE__ */
|
|
662
|
+
) }) : /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
|
|
663
|
+
applicationId ? /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8 }, children: [
|
|
664
|
+
/* @__PURE__ */ jsx6(Button, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
|
|
665
|
+
/* @__PURE__ */ jsx6(Button, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
|
|
816
666
|
] }) : null,
|
|
817
|
-
note ? /* @__PURE__ */
|
|
667
|
+
note ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: note }) : null
|
|
818
668
|
] });
|
|
819
669
|
}
|
|
820
670
|
|
|
821
671
|
// src/ui/admin-network.tsx
|
|
822
|
-
import { useState as
|
|
823
|
-
import { jsx as
|
|
672
|
+
import { useState as useState5 } from "preact/hooks";
|
|
673
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
824
674
|
function NetworkShareActions(props) {
|
|
825
675
|
const copy = useChapterCopy().admin.network;
|
|
826
676
|
const { recordId, recordType, getToken } = props;
|
|
827
677
|
const targets = useAdminResource(getToken, "/api/admin/network/targets");
|
|
828
|
-
const [busy, setBusy] =
|
|
829
|
-
const [message, setMessage] =
|
|
678
|
+
const [busy, setBusy] = useState5(null);
|
|
679
|
+
const [message, setMessage] = useState5(null);
|
|
830
680
|
const compatible = (targets.data?.targets ?? []).filter((target) => target.types.includes(recordType));
|
|
831
681
|
if (targets.loading || targets.error || compatible.length === 0) return null;
|
|
832
682
|
const push = async (target) => {
|
|
@@ -845,26 +695,26 @@ function NetworkShareActions(props) {
|
|
|
845
695
|
setBusy(null);
|
|
846
696
|
}
|
|
847
697
|
};
|
|
848
|
-
return /* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
698
|
+
return /* @__PURE__ */ jsxs7("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
699
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
700
|
+
/* @__PURE__ */ jsx7("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
|
|
701
|
+
/* @__PURE__ */ jsx7("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
|
|
852
702
|
] }),
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
message ? /* @__PURE__ */
|
|
703
|
+
/* @__PURE__ */ jsx7("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx7("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)) }),
|
|
704
|
+
message ? /* @__PURE__ */ jsx7("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
|
|
855
705
|
] });
|
|
856
706
|
}
|
|
857
707
|
|
|
858
708
|
// src/ui/admin-record-activity.tsx
|
|
859
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useState as
|
|
709
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState6 } from "preact/hooks";
|
|
860
710
|
import { ActivityFeed } from "@odla-ai/crm/ui";
|
|
861
711
|
import { parseCrmDiscussionReference as parseCrmDiscussionReference2 } from "@odla-ai/crm";
|
|
862
|
-
import { jsx as
|
|
712
|
+
import { jsx as jsx8 } from "preact/jsx-runtime";
|
|
863
713
|
function RecordActivityPanel(props) {
|
|
864
714
|
const { client, recordId } = props;
|
|
865
|
-
const [activities, setActivities] =
|
|
866
|
-
const [busy, setBusy] =
|
|
867
|
-
const [error, setError] =
|
|
715
|
+
const [activities, setActivities] = useState6([]);
|
|
716
|
+
const [busy, setBusy] = useState6(false);
|
|
717
|
+
const [error, setError] = useState6(null);
|
|
868
718
|
const reference = typeof window === "undefined" ? null : parseCrmDiscussionReference2(new URL(window.location.href));
|
|
869
719
|
const focusedActivityId = reference?.kind === "crm:activity" && reference.recordId === recordId ? reference.activityId : void 0;
|
|
870
720
|
const load = useCallback3(async () => {
|
|
@@ -886,8 +736,8 @@ function RecordActivityPanel(props) {
|
|
|
886
736
|
setBusy(false);
|
|
887
737
|
}
|
|
888
738
|
};
|
|
889
|
-
if (error) return /* @__PURE__ */
|
|
890
|
-
return /* @__PURE__ */
|
|
739
|
+
if (error) return /* @__PURE__ */ jsx8("p", { role: "alert", className: "muted", children: error });
|
|
740
|
+
return /* @__PURE__ */ jsx8(
|
|
891
741
|
ActivityFeed,
|
|
892
742
|
{
|
|
893
743
|
activities,
|
|
@@ -901,17 +751,17 @@ function RecordActivityPanel(props) {
|
|
|
901
751
|
}
|
|
902
752
|
|
|
903
753
|
// src/ui/admin-record-comms.tsx
|
|
904
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useState as
|
|
905
|
-
import { Button as
|
|
906
|
-
import { jsx as
|
|
754
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState7 } from "preact/hooks";
|
|
755
|
+
import { Button as Button2, Field as Field2, Select as Select2 } from "@odla-ai/ui/components";
|
|
756
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
907
757
|
function RecordCommsPanel(props) {
|
|
908
758
|
const copy = useChapterCopy().admin.records;
|
|
909
759
|
const { client, recordId, getToken } = props;
|
|
910
760
|
const config = useAdminResource(getToken, "/api/admin/group/email");
|
|
911
761
|
const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
|
|
912
|
-
const [template, setTemplate] =
|
|
913
|
-
const [busy, setBusy] =
|
|
914
|
-
const [note, setNote] =
|
|
762
|
+
const [template, setTemplate] = useState7("");
|
|
763
|
+
const [busy, setBusy] = useState7(false);
|
|
764
|
+
const [note, setNote] = useState7(null);
|
|
915
765
|
const send = async () => {
|
|
916
766
|
if (!template) return;
|
|
917
767
|
setBusy(true);
|
|
@@ -925,10 +775,10 @@ function RecordCommsPanel(props) {
|
|
|
925
775
|
setBusy(false);
|
|
926
776
|
}
|
|
927
777
|
};
|
|
928
|
-
if (config.loading) return /* @__PURE__ */
|
|
929
|
-
if (config.error) return /* @__PURE__ */
|
|
930
|
-
return /* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
778
|
+
if (config.loading) return /* @__PURE__ */ jsx9("p", { className: "muted", children: copy.loadingTemplates });
|
|
779
|
+
if (config.error) return /* @__PURE__ */ jsx9("p", { role: "alert", className: "muted", children: config.error });
|
|
780
|
+
return /* @__PURE__ */ jsxs8("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
|
|
781
|
+
/* @__PURE__ */ jsx9(Field2, { label: copy.template, children: /* @__PURE__ */ jsx9(
|
|
932
782
|
Select2,
|
|
933
783
|
{
|
|
934
784
|
value: template,
|
|
@@ -939,15 +789,15 @@ function RecordCommsPanel(props) {
|
|
|
939
789
|
]
|
|
940
790
|
}
|
|
941
791
|
) }),
|
|
942
|
-
/* @__PURE__ */
|
|
943
|
-
note ? /* @__PURE__ */
|
|
792
|
+
/* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsx9(Button2, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
|
|
793
|
+
note ? /* @__PURE__ */ jsx9("p", { role: "status", className: "muted", children: note }) : null
|
|
944
794
|
] });
|
|
945
795
|
}
|
|
946
796
|
function RecordCommsHistory(props) {
|
|
947
797
|
const copy = useChapterCopy().admin.records;
|
|
948
798
|
const { client, recordId } = props;
|
|
949
|
-
const [rows, setRows] =
|
|
950
|
-
const [error, setError] =
|
|
799
|
+
const [rows, setRows] = useState7([]);
|
|
800
|
+
const [error, setError] = useState7(null);
|
|
951
801
|
const load = useCallback4(async () => {
|
|
952
802
|
try {
|
|
953
803
|
const result = await client.emailLog({ recordId, limit: 100 });
|
|
@@ -958,11 +808,11 @@ function RecordCommsHistory(props) {
|
|
|
958
808
|
}
|
|
959
809
|
}, [client, recordId]);
|
|
960
810
|
useEffect6(() => void load(), [load]);
|
|
961
|
-
if (error) return /* @__PURE__ */
|
|
962
|
-
if (rows.length === 0) return /* @__PURE__ */
|
|
963
|
-
return /* @__PURE__ */
|
|
964
|
-
/* @__PURE__ */
|
|
965
|
-
/* @__PURE__ */
|
|
811
|
+
if (error) return /* @__PURE__ */ jsx9("p", { role: "alert", className: "muted", children: error });
|
|
812
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx9("p", { className: "muted", children: copy.noMessages });
|
|
813
|
+
return /* @__PURE__ */ jsx9("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs8("li", { className: "panel", children: [
|
|
814
|
+
/* @__PURE__ */ jsx9("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
|
|
815
|
+
/* @__PURE__ */ jsxs8("div", { className: "muted", children: [
|
|
966
816
|
row["to"] ? String(row["to"]) : "",
|
|
967
817
|
row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
|
|
968
818
|
] })
|
|
@@ -970,9 +820,9 @@ function RecordCommsHistory(props) {
|
|
|
970
820
|
}
|
|
971
821
|
|
|
972
822
|
// src/ui/admin-record-scheduling.tsx
|
|
973
|
-
import { useState as
|
|
974
|
-
import { Button as
|
|
975
|
-
import { Fragment as Fragment3, jsx as
|
|
823
|
+
import { useState as useState8 } from "preact/hooks";
|
|
824
|
+
import { Button as Button3 } from "@odla-ai/ui/components";
|
|
825
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
976
826
|
function RecordSchedulingPanel(props) {
|
|
977
827
|
const copy = useChapterCopy().admin.records;
|
|
978
828
|
const { getToken, applicationId } = props;
|
|
@@ -980,8 +830,8 @@ function RecordSchedulingPanel(props) {
|
|
|
980
830
|
getToken,
|
|
981
831
|
"/api/admin/meetings?all=1"
|
|
982
832
|
);
|
|
983
|
-
const [busy, setBusy] =
|
|
984
|
-
const [note, setNote] =
|
|
833
|
+
const [busy, setBusy] = useState8(null);
|
|
834
|
+
const [note, setNote] = useState8(null);
|
|
985
835
|
const meetings = (resource.data?.meetings ?? []).filter((meeting) => meeting.applicationId === applicationId);
|
|
986
836
|
const run = async (meeting, action) => {
|
|
987
837
|
let body;
|
|
@@ -1008,33 +858,33 @@ function RecordSchedulingPanel(props) {
|
|
|
1008
858
|
setBusy(null);
|
|
1009
859
|
}
|
|
1010
860
|
};
|
|
1011
|
-
if (!applicationId) return /* @__PURE__ */
|
|
1012
|
-
if (resource.loading) return /* @__PURE__ */
|
|
1013
|
-
if (resource.error) return /* @__PURE__ */
|
|
1014
|
-
if (meetings.length === 0) return /* @__PURE__ */
|
|
1015
|
-
return /* @__PURE__ */
|
|
1016
|
-
meetings.map((meeting) => /* @__PURE__ */
|
|
1017
|
-
/* @__PURE__ */
|
|
1018
|
-
/* @__PURE__ */
|
|
861
|
+
if (!applicationId) return /* @__PURE__ */ jsx10("p", { className: "muted", children: copy.noApplication });
|
|
862
|
+
if (resource.loading) return /* @__PURE__ */ jsx10("p", { className: "muted", children: copy.loadingMeetings });
|
|
863
|
+
if (resource.error) return /* @__PURE__ */ jsx10("p", { role: "alert", className: "muted", children: resource.error });
|
|
864
|
+
if (meetings.length === 0) return /* @__PURE__ */ jsx10("p", { className: "muted", children: copy.noMeetings });
|
|
865
|
+
return /* @__PURE__ */ jsxs9("div", { style: { display: "grid", gap: 12 }, children: [
|
|
866
|
+
meetings.map((meeting) => /* @__PURE__ */ jsxs9("section", { className: "panel", children: [
|
|
867
|
+
/* @__PURE__ */ jsx10("strong", { children: new Date(meeting.startAt).toLocaleString() }),
|
|
868
|
+
/* @__PURE__ */ jsxs9("p", { className: "muted", children: [
|
|
1019
869
|
meeting.status,
|
|
1020
870
|
" \xB7 ",
|
|
1021
871
|
resource.data?.timezone
|
|
1022
872
|
] }),
|
|
1023
|
-
/* @__PURE__ */
|
|
1024
|
-
meeting.meetUrl ? /* @__PURE__ */
|
|
1025
|
-
meeting.htmlLink ? /* @__PURE__ */
|
|
1026
|
-
meeting.status === "scheduled" ? /* @__PURE__ */
|
|
1027
|
-
/* @__PURE__ */
|
|
1028
|
-
/* @__PURE__ */
|
|
873
|
+
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
|
|
874
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx10("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
|
|
875
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx10("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
|
|
876
|
+
meeting.status === "scheduled" ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
877
|
+
/* @__PURE__ */ jsx10(Button3, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
|
|
878
|
+
/* @__PURE__ */ jsx10(Button3, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
|
|
1029
879
|
] }) : null
|
|
1030
880
|
] })
|
|
1031
881
|
] }, meeting.id)),
|
|
1032
|
-
note ? /* @__PURE__ */
|
|
882
|
+
note ? /* @__PURE__ */ jsx10("p", { role: "status", className: "muted", children: note }) : null
|
|
1033
883
|
] });
|
|
1034
884
|
}
|
|
1035
885
|
|
|
1036
886
|
// src/ui/admin-people-workspace.tsx
|
|
1037
|
-
import { jsx as
|
|
887
|
+
import { jsx as jsx11 } from "preact/jsx-runtime";
|
|
1038
888
|
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
1039
889
|
return {
|
|
1040
890
|
transitionStage: async ({ record, to }) => {
|
|
@@ -1083,7 +933,7 @@ function PeopleBody(props) {
|
|
|
1083
933
|
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
|
|
1084
934
|
const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
|
|
1085
935
|
const workspaceClient = client;
|
|
1086
|
-
return /* @__PURE__ */
|
|
936
|
+
return /* @__PURE__ */ jsx11(AdminPage, { children: /* @__PURE__ */ jsx11(
|
|
1087
937
|
CrmWorkspace,
|
|
1088
938
|
{
|
|
1089
939
|
crm,
|
|
@@ -1119,35 +969,35 @@ function PeopleBody(props) {
|
|
|
1119
969
|
tabs.push({
|
|
1120
970
|
id: "comms",
|
|
1121
971
|
label: copy.comms,
|
|
1122
|
-
panel: /* @__PURE__ */
|
|
972
|
+
panel: /* @__PURE__ */ jsx11(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
|
|
1123
973
|
});
|
|
1124
974
|
tabs.push({
|
|
1125
975
|
id: "history",
|
|
1126
976
|
label: copy.commsHistory,
|
|
1127
|
-
panel: /* @__PURE__ */
|
|
977
|
+
panel: /* @__PURE__ */ jsx11(RecordCommsHistory, { client, recordId: record.id })
|
|
1128
978
|
});
|
|
1129
979
|
}
|
|
1130
980
|
if (lifecycle) {
|
|
1131
981
|
tabs.push({
|
|
1132
982
|
id: "scheduling",
|
|
1133
983
|
label: copy.scheduling,
|
|
1134
|
-
panel: /* @__PURE__ */
|
|
984
|
+
panel: /* @__PURE__ */ jsx11(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
|
|
1135
985
|
});
|
|
1136
|
-
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */
|
|
986
|
+
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsx11(BillingCard, { record }) });
|
|
1137
987
|
} else {
|
|
1138
988
|
add("billing");
|
|
1139
989
|
}
|
|
1140
990
|
tabs.push({
|
|
1141
991
|
id: "notes",
|
|
1142
992
|
label: copy.notes,
|
|
1143
|
-
panel: /* @__PURE__ */
|
|
993
|
+
panel: /* @__PURE__ */ jsx11(RecordActivityPanel, { client, recordId: record.id })
|
|
1144
994
|
});
|
|
1145
995
|
add("connections");
|
|
1146
996
|
if (lifecycle) {
|
|
1147
997
|
tabs.push({
|
|
1148
998
|
id: "access",
|
|
1149
999
|
label: copy.access,
|
|
1150
|
-
panel: /* @__PURE__ */
|
|
1000
|
+
panel: /* @__PURE__ */ jsx11(
|
|
1151
1001
|
RecordActions,
|
|
1152
1002
|
{
|
|
1153
1003
|
getToken: ctx.getToken,
|
|
@@ -1166,7 +1016,7 @@ function PeopleBody(props) {
|
|
|
1166
1016
|
tabs.push({
|
|
1167
1017
|
id: "sharing",
|
|
1168
1018
|
label: copy.sharing,
|
|
1169
|
-
panel: /* @__PURE__ */
|
|
1019
|
+
panel: /* @__PURE__ */ jsx11(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
|
|
1170
1020
|
});
|
|
1171
1021
|
}
|
|
1172
1022
|
return extendRecordTabs?.(tabs, context) ?? tabs;
|
|
@@ -1176,7 +1026,7 @@ function PeopleBody(props) {
|
|
|
1176
1026
|
}
|
|
1177
1027
|
|
|
1178
1028
|
// src/ui/admin-people.tsx
|
|
1179
|
-
import { jsx as
|
|
1029
|
+
import { jsx as jsx12 } from "preact/jsx-runtime";
|
|
1180
1030
|
var DEFAULT_ROLES = ["provisional", "member", "admin"];
|
|
1181
1031
|
function collectionSection(options) {
|
|
1182
1032
|
const { crm, type } = options;
|
|
@@ -1206,7 +1056,7 @@ function collectionSection(options) {
|
|
|
1206
1056
|
return {
|
|
1207
1057
|
id,
|
|
1208
1058
|
label,
|
|
1209
|
-
render: (ctx) => /* @__PURE__ */
|
|
1059
|
+
render: (ctx) => /* @__PURE__ */ jsx12(
|
|
1210
1060
|
PeopleBody,
|
|
1211
1061
|
{
|
|
1212
1062
|
crm,
|
|
@@ -1274,18 +1124,18 @@ function peopleSection(options) {
|
|
|
1274
1124
|
|
|
1275
1125
|
// src/ui/admin-network-records.tsx
|
|
1276
1126
|
import {
|
|
1277
|
-
DataTable as
|
|
1278
|
-
Field as
|
|
1127
|
+
DataTable as DataTable2,
|
|
1128
|
+
Field as Field4,
|
|
1279
1129
|
MasterDetail,
|
|
1280
1130
|
Pagination,
|
|
1281
1131
|
Select as Select3
|
|
1282
1132
|
} from "@odla-ai/ui/components";
|
|
1283
|
-
import { useEffect as useEffect7, useMemo, useState as
|
|
1133
|
+
import { useEffect as useEffect7, useMemo, useState as useState10 } from "preact/hooks";
|
|
1284
1134
|
|
|
1285
1135
|
// src/ui/admin-network-notes.tsx
|
|
1286
|
-
import { Button as
|
|
1287
|
-
import { useState as
|
|
1288
|
-
import { jsx as
|
|
1136
|
+
import { Button as Button4, Field as Field3, Textarea } from "@odla-ai/ui/components";
|
|
1137
|
+
import { useState as useState9 } from "preact/hooks";
|
|
1138
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
1289
1139
|
function displayValue(value) {
|
|
1290
1140
|
if (Array.isArray(value)) return value.join(", ");
|
|
1291
1141
|
if (value && typeof value === "object") return JSON.stringify(value);
|
|
@@ -1300,9 +1150,9 @@ function PrivateNotes(props) {
|
|
|
1300
1150
|
});
|
|
1301
1151
|
const path = `/api/admin/network/notes?${query}`;
|
|
1302
1152
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1303
|
-
const [body, setBody] =
|
|
1304
|
-
const [busy, setBusy] =
|
|
1305
|
-
const [error, setError] =
|
|
1153
|
+
const [body, setBody] = useState9("");
|
|
1154
|
+
const [busy, setBusy] = useState9(false);
|
|
1155
|
+
const [error, setError] = useState9(null);
|
|
1306
1156
|
const submit = async () => {
|
|
1307
1157
|
const trimmed = body.trim();
|
|
1308
1158
|
if (!trimmed) return;
|
|
@@ -1321,19 +1171,19 @@ function PrivateNotes(props) {
|
|
|
1321
1171
|
setBusy(false);
|
|
1322
1172
|
}
|
|
1323
1173
|
};
|
|
1324
|
-
return /* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1174
|
+
return /* @__PURE__ */ jsxs10("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1175
|
+
/* @__PURE__ */ jsxs10("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1326
1176
|
"Private to ",
|
|
1327
1177
|
leaderName
|
|
1328
1178
|
] }),
|
|
1329
|
-
/* @__PURE__ */
|
|
1330
|
-
|
|
1179
|
+
/* @__PURE__ */ jsx13(
|
|
1180
|
+
Field3,
|
|
1331
1181
|
{
|
|
1332
1182
|
label: "Add a note",
|
|
1333
1183
|
hint: `Visible only to ${leaderName} admins. Nothing is written back to the follower.`,
|
|
1334
1184
|
error,
|
|
1335
|
-
children: /* @__PURE__ */
|
|
1336
|
-
|
|
1185
|
+
children: /* @__PURE__ */ jsx13(
|
|
1186
|
+
Textarea,
|
|
1337
1187
|
{
|
|
1338
1188
|
rows: 4,
|
|
1339
1189
|
value: body,
|
|
@@ -1343,13 +1193,13 @@ function PrivateNotes(props) {
|
|
|
1343
1193
|
)
|
|
1344
1194
|
}
|
|
1345
1195
|
),
|
|
1346
|
-
/* @__PURE__ */
|
|
1347
|
-
notes.loading ? /* @__PURE__ */
|
|
1348
|
-
notes.error ? /* @__PURE__ */
|
|
1349
|
-
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */
|
|
1350
|
-
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */
|
|
1351
|
-
/* @__PURE__ */
|
|
1352
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(Button4, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save note" }) }),
|
|
1197
|
+
notes.loading ? /* @__PURE__ */ jsx13("p", { className: "muted", children: "Loading notes\u2026" }) : null,
|
|
1198
|
+
notes.error ? /* @__PURE__ */ jsx13("p", { role: "alert", children: notes.error }) : null,
|
|
1199
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx13("p", { className: "muted", children: "No notes yet." }) : null,
|
|
1200
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs10("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1201
|
+
/* @__PURE__ */ jsx13("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1202
|
+
/* @__PURE__ */ jsxs10("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1353
1203
|
note.authorEmail ?? note.authorId,
|
|
1354
1204
|
" \xB7 ",
|
|
1355
1205
|
new Date(note.createdAt).toLocaleString()
|
|
@@ -1366,9 +1216,9 @@ function SharedNotes(props) {
|
|
|
1366
1216
|
});
|
|
1367
1217
|
const path = `/api/admin/network/shared-notes?${query}`;
|
|
1368
1218
|
const notes = useAdminResource(ctx.getToken, path);
|
|
1369
|
-
const [body, setBody] =
|
|
1370
|
-
const [busy, setBusy] =
|
|
1371
|
-
const [error, setError] =
|
|
1219
|
+
const [body, setBody] = useState9("");
|
|
1220
|
+
const [busy, setBusy] = useState9(false);
|
|
1221
|
+
const [error, setError] = useState9(null);
|
|
1372
1222
|
const submit = async () => {
|
|
1373
1223
|
const trimmed = body.trim();
|
|
1374
1224
|
if (!trimmed) return;
|
|
@@ -1387,19 +1237,19 @@ function SharedNotes(props) {
|
|
|
1387
1237
|
setBusy(false);
|
|
1388
1238
|
}
|
|
1389
1239
|
};
|
|
1390
|
-
return /* @__PURE__ */
|
|
1391
|
-
/* @__PURE__ */
|
|
1240
|
+
return /* @__PURE__ */ jsxs10("section", { style: { display: "grid", gap: 12 }, children: [
|
|
1241
|
+
/* @__PURE__ */ jsxs10("h3", { style: { margin: "8px 0 0" }, children: [
|
|
1392
1242
|
"Shared with ",
|
|
1393
1243
|
targetName
|
|
1394
1244
|
] }),
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
|
|
1245
|
+
/* @__PURE__ */ jsx13(
|
|
1246
|
+
Field3,
|
|
1397
1247
|
{
|
|
1398
1248
|
label: "Add a shared note",
|
|
1399
1249
|
hint: `This is written to the record's Notes feed for ${targetName} admins.`,
|
|
1400
1250
|
error,
|
|
1401
|
-
children: /* @__PURE__ */
|
|
1402
|
-
|
|
1251
|
+
children: /* @__PURE__ */ jsx13(
|
|
1252
|
+
Textarea,
|
|
1403
1253
|
{
|
|
1404
1254
|
rows: 4,
|
|
1405
1255
|
value: body,
|
|
@@ -1409,13 +1259,13 @@ function SharedNotes(props) {
|
|
|
1409
1259
|
)
|
|
1410
1260
|
}
|
|
1411
1261
|
),
|
|
1412
|
-
/* @__PURE__ */
|
|
1413
|
-
notes.loading ? /* @__PURE__ */
|
|
1414
|
-
notes.error ? /* @__PURE__ */
|
|
1415
|
-
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */
|
|
1416
|
-
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1418
|
-
/* @__PURE__ */
|
|
1262
|
+
/* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(Button4, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Sharing\u2026" : `Share with ${targetName}` }) }),
|
|
1263
|
+
notes.loading ? /* @__PURE__ */ jsx13("p", { className: "muted", children: "Loading shared notes\u2026" }) : null,
|
|
1264
|
+
notes.error ? /* @__PURE__ */ jsx13("p", { role: "alert", children: notes.error }) : null,
|
|
1265
|
+
!notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx13("p", { className: "muted", children: "No shared notes yet." }) : null,
|
|
1266
|
+
(notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs10("article", { className: "panel", style: { padding: 14 }, children: [
|
|
1267
|
+
/* @__PURE__ */ jsx13("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
|
|
1268
|
+
/* @__PURE__ */ jsxs10("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
|
|
1419
1269
|
"Shared with ",
|
|
1420
1270
|
targetName,
|
|
1421
1271
|
" \xB7 ",
|
|
@@ -1426,23 +1276,23 @@ function SharedNotes(props) {
|
|
|
1426
1276
|
}
|
|
1427
1277
|
function NetworkRecordDetail(props) {
|
|
1428
1278
|
const { ctx, targetId, targetName, leaderName, sharedEnabled, record } = props;
|
|
1429
|
-
return /* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
record.stage ? /* @__PURE__ */
|
|
1279
|
+
return /* @__PURE__ */ jsxs10("div", { style: { display: "grid", gap: 14 }, children: [
|
|
1280
|
+
/* @__PURE__ */ jsxs10("header", { children: [
|
|
1281
|
+
/* @__PURE__ */ jsx13("h2", { style: { margin: 0 }, children: record.name }),
|
|
1282
|
+
record.stage ? /* @__PURE__ */ jsx13("span", { className: "badge", style: { marginTop: 8 }, children: record.stage.replaceAll("_", " ") }) : null
|
|
1433
1283
|
] }),
|
|
1434
|
-
/* @__PURE__ */
|
|
1435
|
-
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */
|
|
1436
|
-
/* @__PURE__ */
|
|
1437
|
-
/* @__PURE__ */
|
|
1284
|
+
/* @__PURE__ */ jsxs10("dl", { style: { display: "grid", gridTemplateColumns: "minmax(8rem, auto) 1fr", gap: "8px 16px", margin: 0 }, children: [
|
|
1285
|
+
Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */ jsxs10("div", { style: { display: "contents" }, children: [
|
|
1286
|
+
/* @__PURE__ */ jsx13("dt", { className: "muted", children: name.replaceAll("_", " ") }),
|
|
1287
|
+
/* @__PURE__ */ jsx13("dd", { style: { margin: 0, overflowWrap: "anywhere" }, children: displayValue(value) })
|
|
1438
1288
|
] }, name)),
|
|
1439
|
-
/* @__PURE__ */
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
/* @__PURE__ */
|
|
1289
|
+
/* @__PURE__ */ jsxs10("div", { style: { display: "contents" }, children: [
|
|
1290
|
+
/* @__PURE__ */ jsx13("dt", { className: "muted", children: "Updated" }),
|
|
1291
|
+
/* @__PURE__ */ jsx13("dd", { style: { margin: 0 }, children: new Date(record.updatedAt).toLocaleString() })
|
|
1442
1292
|
] })
|
|
1443
1293
|
] }),
|
|
1444
|
-
/* @__PURE__ */
|
|
1445
|
-
/* @__PURE__ */
|
|
1294
|
+
/* @__PURE__ */ jsxs10("div", { style: { display: "grid", gap: 22 }, children: [
|
|
1295
|
+
/* @__PURE__ */ jsx13(
|
|
1446
1296
|
PrivateNotes,
|
|
1447
1297
|
{
|
|
1448
1298
|
ctx,
|
|
@@ -1451,7 +1301,7 @@ function NetworkRecordDetail(props) {
|
|
|
1451
1301
|
leaderName
|
|
1452
1302
|
}
|
|
1453
1303
|
),
|
|
1454
|
-
sharedEnabled ? /* @__PURE__ */
|
|
1304
|
+
sharedEnabled ? /* @__PURE__ */ jsx13(
|
|
1455
1305
|
SharedNotes,
|
|
1456
1306
|
{
|
|
1457
1307
|
ctx,
|
|
@@ -1465,11 +1315,11 @@ function NetworkRecordDetail(props) {
|
|
|
1465
1315
|
}
|
|
1466
1316
|
|
|
1467
1317
|
// src/ui/admin-network-records.tsx
|
|
1468
|
-
import { jsx as
|
|
1318
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
1469
1319
|
function RecordsExplorer(props) {
|
|
1470
1320
|
const { ctx, target, type, leaderName } = props;
|
|
1471
|
-
const [offset, setOffset] =
|
|
1472
|
-
const [search, setSearch] =
|
|
1321
|
+
const [offset, setOffset] = useState10(0);
|
|
1322
|
+
const [search, setSearch] = useState10("");
|
|
1473
1323
|
const query = new URLSearchParams({
|
|
1474
1324
|
targetId: target.id,
|
|
1475
1325
|
type,
|
|
@@ -1486,12 +1336,12 @@ function RecordsExplorer(props) {
|
|
|
1486
1336
|
{
|
|
1487
1337
|
key: "name",
|
|
1488
1338
|
header: "Name",
|
|
1489
|
-
cell: (record) => /* @__PURE__ */
|
|
1339
|
+
cell: (record) => /* @__PURE__ */ jsx14("a", { href: ctx.href({ recordId: record.id, detailTab: null }), children: record.name })
|
|
1490
1340
|
},
|
|
1491
1341
|
{
|
|
1492
1342
|
key: "stage",
|
|
1493
1343
|
header: "Stage",
|
|
1494
|
-
cell: (record) => record.stage ? /* @__PURE__ */
|
|
1344
|
+
cell: (record) => record.stage ? /* @__PURE__ */ jsx14("span", { className: "badge", children: record.stage.replaceAll("_", " ") }) : "\u2014"
|
|
1495
1345
|
},
|
|
1496
1346
|
{
|
|
1497
1347
|
key: "updatedAt",
|
|
@@ -1499,7 +1349,7 @@ function RecordsExplorer(props) {
|
|
|
1499
1349
|
cell: (record) => new Date(record.updatedAt).toLocaleDateString()
|
|
1500
1350
|
}
|
|
1501
1351
|
], [ctx]);
|
|
1502
|
-
return /* @__PURE__ */
|
|
1352
|
+
return /* @__PURE__ */ jsx14(
|
|
1503
1353
|
MasterDetail,
|
|
1504
1354
|
{
|
|
1505
1355
|
collapseClosedDetail: true,
|
|
@@ -1507,14 +1357,14 @@ function RecordsExplorer(props) {
|
|
|
1507
1357
|
detailLabel: "Follower record details, private notes, and shared notes",
|
|
1508
1358
|
detailOpen: Boolean(selected),
|
|
1509
1359
|
closeHref: ctx.href({ recordId: null, detailTab: null }),
|
|
1510
|
-
master: /* @__PURE__ */
|
|
1511
|
-
/* @__PURE__ */
|
|
1360
|
+
master: /* @__PURE__ */ jsxs11("div", { style: { display: "grid", gap: 14, padding: "var(--ui-space-4)" }, children: [
|
|
1361
|
+
/* @__PURE__ */ jsxs11("h2", { style: { margin: 0, fontSize: 18 }, children: [
|
|
1512
1362
|
target.name,
|
|
1513
1363
|
" \xB7 ",
|
|
1514
1364
|
type.replaceAll("_", " ")
|
|
1515
1365
|
] }),
|
|
1516
|
-
/* @__PURE__ */
|
|
1517
|
-
|
|
1366
|
+
/* @__PURE__ */ jsx14(
|
|
1367
|
+
DataTable2,
|
|
1518
1368
|
{
|
|
1519
1369
|
manual: true,
|
|
1520
1370
|
filterable: true,
|
|
@@ -1533,7 +1383,7 @@ function RecordsExplorer(props) {
|
|
|
1533
1383
|
ariaLabel: `${target.name} ${type} records`
|
|
1534
1384
|
}
|
|
1535
1385
|
),
|
|
1536
|
-
/* @__PURE__ */
|
|
1386
|
+
/* @__PURE__ */ jsx14(
|
|
1537
1387
|
Pagination,
|
|
1538
1388
|
{
|
|
1539
1389
|
offset: page.data?.offset ?? offset,
|
|
@@ -1548,7 +1398,7 @@ function RecordsExplorer(props) {
|
|
|
1548
1398
|
}
|
|
1549
1399
|
)
|
|
1550
1400
|
] }),
|
|
1551
|
-
detail: selected ? /* @__PURE__ */
|
|
1401
|
+
detail: selected ? /* @__PURE__ */ jsx14(
|
|
1552
1402
|
NetworkRecordDetail,
|
|
1553
1403
|
{
|
|
1554
1404
|
ctx,
|
|
@@ -1565,8 +1415,8 @@ function RecordsExplorer(props) {
|
|
|
1565
1415
|
function NetworkRecordsWorkspace(props) {
|
|
1566
1416
|
const { chapter, ctx } = props;
|
|
1567
1417
|
const targets = useAdminResource(ctx.getToken, "/api/admin/network/targets");
|
|
1568
|
-
const [targetId, setTargetId] =
|
|
1569
|
-
const [type, setType] =
|
|
1418
|
+
const [targetId, setTargetId] = useState10("");
|
|
1419
|
+
const [type, setType] = useState10("");
|
|
1570
1420
|
const target = targets.data?.targets.find((candidate) => candidate.id === targetId) ?? targets.data?.targets[0];
|
|
1571
1421
|
useEffect7(() => {
|
|
1572
1422
|
if (target && target.id !== targetId) setTargetId(target.id);
|
|
@@ -1575,16 +1425,16 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1575
1425
|
const first = target?.types[0] ?? "";
|
|
1576
1426
|
if (target && !target.types.includes(type)) setType(first);
|
|
1577
1427
|
}, [target, type]);
|
|
1578
|
-
if (targets.loading) return /* @__PURE__ */
|
|
1579
|
-
if (targets.error || !targets.data) return /* @__PURE__ */
|
|
1428
|
+
if (targets.loading) return /* @__PURE__ */ jsx14(AdminNote, { children: "Loading follower records\u2026" });
|
|
1429
|
+
if (targets.error || !targets.data) return /* @__PURE__ */ jsxs11(AdminNote, { children: [
|
|
1580
1430
|
"Could not load followers",
|
|
1581
1431
|
targets.error ? `: ${targets.error}` : "",
|
|
1582
1432
|
"."
|
|
1583
1433
|
] });
|
|
1584
|
-
if (!target) return /* @__PURE__ */
|
|
1585
|
-
return /* @__PURE__ */
|
|
1586
|
-
/* @__PURE__ */
|
|
1587
|
-
/* @__PURE__ */
|
|
1434
|
+
if (!target) return /* @__PURE__ */ jsx14(AdminPage, { children: /* @__PURE__ */ jsx14(Panel, { title: "Follower records", children: /* @__PURE__ */ jsx14("p", { className: "muted", children: "No followers are configured." }) }) });
|
|
1435
|
+
return /* @__PURE__ */ jsxs11(AdminPage, { children: [
|
|
1436
|
+
/* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "end" }, children: [
|
|
1437
|
+
/* @__PURE__ */ jsx14(Field4, { label: "Chapter", style: { minWidth: 240 }, children: /* @__PURE__ */ jsx14(
|
|
1588
1438
|
Select3,
|
|
1589
1439
|
{
|
|
1590
1440
|
value: target.id,
|
|
@@ -1596,7 +1446,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1596
1446
|
}
|
|
1597
1447
|
}
|
|
1598
1448
|
) }),
|
|
1599
|
-
/* @__PURE__ */
|
|
1449
|
+
/* @__PURE__ */ jsx14(Field4, { label: "Record type", style: { minWidth: 180 }, children: /* @__PURE__ */ jsx14(
|
|
1600
1450
|
Select3,
|
|
1601
1451
|
{
|
|
1602
1452
|
value: type,
|
|
@@ -1608,7 +1458,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1608
1458
|
}
|
|
1609
1459
|
) })
|
|
1610
1460
|
] }),
|
|
1611
|
-
type ? /* @__PURE__ */
|
|
1461
|
+
type ? /* @__PURE__ */ jsx14(
|
|
1612
1462
|
RecordsExplorer,
|
|
1613
1463
|
{
|
|
1614
1464
|
ctx,
|
|
@@ -1617,7 +1467,7 @@ function NetworkRecordsWorkspace(props) {
|
|
|
1617
1467
|
leaderName: chapter.name
|
|
1618
1468
|
},
|
|
1619
1469
|
`${target.id}:${type}`
|
|
1620
|
-
) : /* @__PURE__ */
|
|
1470
|
+
) : /* @__PURE__ */ jsx14(Panel, { children: /* @__PURE__ */ jsx14("p", { className: "muted", children: "This follower has no browsable record types configured." }) })
|
|
1621
1471
|
] });
|
|
1622
1472
|
}
|
|
1623
1473
|
function networkRecordsWorkspace(chapter) {
|
|
@@ -1625,60 +1475,60 @@ function networkRecordsWorkspace(chapter) {
|
|
|
1625
1475
|
id: "network",
|
|
1626
1476
|
label: chapter.copy.admin.workspaces.network,
|
|
1627
1477
|
defaultViewId: "records",
|
|
1628
|
-
render: (ctx) => /* @__PURE__ */
|
|
1478
|
+
render: (ctx) => /* @__PURE__ */ jsx14(NetworkRecordsWorkspace, { chapter, ctx })
|
|
1629
1479
|
};
|
|
1630
1480
|
}
|
|
1631
1481
|
|
|
1632
1482
|
// src/ui/admin-portfolio.tsx
|
|
1633
1483
|
import { StatBand as StatBand2, StepPipeline as StepPipeline2 } from "@odla-ai/ui/components";
|
|
1634
|
-
import { jsx as
|
|
1484
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
1635
1485
|
function LeaderNetworkOverview({ ctx }) {
|
|
1636
1486
|
const copy = useChapterCopy().admin.network;
|
|
1637
1487
|
const { data, loading, error } = useAdminResource(
|
|
1638
1488
|
ctx.getToken,
|
|
1639
1489
|
"/api/admin/network/rollup"
|
|
1640
1490
|
);
|
|
1641
|
-
if (loading) return /* @__PURE__ */
|
|
1642
|
-
if (error || !data) return /* @__PURE__ */
|
|
1491
|
+
if (loading) return /* @__PURE__ */ jsx15(AdminNote, { children: copy.loadingRollup });
|
|
1492
|
+
if (error || !data) return /* @__PURE__ */ jsxs12(AdminNote, { children: [
|
|
1643
1493
|
copy.loadRollupFailed,
|
|
1644
1494
|
error ? `: ${error}` : "",
|
|
1645
1495
|
"."
|
|
1646
1496
|
] });
|
|
1647
|
-
if (data.configured === 0) return /* @__PURE__ */
|
|
1497
|
+
if (data.configured === 0) return /* @__PURE__ */ jsx15(AdminPage, { children: /* @__PURE__ */ jsx15(Panel, { title: copy.overview, children: /* @__PURE__ */ jsx15("p", { className: "muted", children: copy.noFollowers }) }) });
|
|
1648
1498
|
const stats = [
|
|
1649
1499
|
{ value: data.configured.toLocaleString(), label: copy.configuredFollowers },
|
|
1650
1500
|
{ value: data.available.toLocaleString(), label: copy.reachableFollowers },
|
|
1651
1501
|
{ value: data.totalRecords.toLocaleString(), label: copy.recordsAcrossFollowers }
|
|
1652
1502
|
];
|
|
1653
1503
|
const steps = data.types.map((type) => ({
|
|
1654
|
-
icon: /* @__PURE__ */
|
|
1504
|
+
icon: /* @__PURE__ */ jsx15("span", { style: { fontSize: 22, fontWeight: 700 }, children: type.total }),
|
|
1655
1505
|
title: type.type.replaceAll("_", " "),
|
|
1656
1506
|
description: Object.entries(type.stages).filter(([, count]) => count > 0).map(([stage, count]) => `${stage.replaceAll("_", " ")} ${count}`).join(" \xB7 ") || copy.noRecordTotals
|
|
1657
1507
|
}));
|
|
1658
|
-
return /* @__PURE__ */
|
|
1659
|
-
/* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
1661
|
-
/* @__PURE__ */
|
|
1662
|
-
/* @__PURE__ */
|
|
1663
|
-
target.error ? /* @__PURE__ */
|
|
1664
|
-
/* @__PURE__ */
|
|
1508
|
+
return /* @__PURE__ */ jsxs12(AdminPage, { children: [
|
|
1509
|
+
/* @__PURE__ */ jsx15(StatBand2, { stats }),
|
|
1510
|
+
/* @__PURE__ */ jsx15(Panel, { title: copy.followers, children: /* @__PURE__ */ jsx15("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.targets.map((target) => /* @__PURE__ */ jsxs12("li", { style: { display: "flex", gap: 12, alignItems: "baseline", flexWrap: "wrap" }, children: [
|
|
1511
|
+
/* @__PURE__ */ jsx15("strong", { style: { minWidth: 180 }, children: target.name }),
|
|
1512
|
+
/* @__PURE__ */ jsx15("span", { className: "badge", children: target.available ? copy.available : copy.unavailable }),
|
|
1513
|
+
target.error ? /* @__PURE__ */ jsx15("span", { className: "muted", children: target.error }) : null,
|
|
1514
|
+
/* @__PURE__ */ jsx15("a", { href: target.url, target: "_blank", rel: "noreferrer", style: { marginLeft: "auto" }, children: target.url })
|
|
1665
1515
|
] }, target.id)) }) }),
|
|
1666
|
-
/* @__PURE__ */
|
|
1516
|
+
/* @__PURE__ */ jsx15(Panel, { title: copy.recordTotals, children: steps.length ? /* @__PURE__ */ jsx15(StepPipeline2, { ariaLabel: copy.recordTotals, steps }) : /* @__PURE__ */ jsx15("p", { className: "muted", children: copy.noRecordTotals }) })
|
|
1667
1517
|
] });
|
|
1668
1518
|
}
|
|
1669
1519
|
|
|
1670
1520
|
// src/ui/admin-admission.tsx
|
|
1671
|
-
import { useState as
|
|
1672
|
-
import { jsx as
|
|
1521
|
+
import { useState as useState11 } from "preact/hooks";
|
|
1522
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
1673
1523
|
function AdmissionGrantsWorkspace(props) {
|
|
1674
1524
|
const copy = useChapterCopy().admin.admission;
|
|
1675
1525
|
const resource = useAdminResource(props.getToken, "/api/admin/admission-grants");
|
|
1676
|
-
const [email, setEmail] =
|
|
1677
|
-
const [decision, setDecision] =
|
|
1678
|
-
const [note, setNote] =
|
|
1679
|
-
const [expires, setExpires] =
|
|
1680
|
-
const [busy, setBusy] =
|
|
1681
|
-
const [error, setError] =
|
|
1526
|
+
const [email, setEmail] = useState11("");
|
|
1527
|
+
const [decision, setDecision] = useState11("waive_interview");
|
|
1528
|
+
const [note, setNote] = useState11("");
|
|
1529
|
+
const [expires, setExpires] = useState11("");
|
|
1530
|
+
const [busy, setBusy] = useState11(false);
|
|
1531
|
+
const [error, setError] = useState11(null);
|
|
1682
1532
|
const submit = async (event) => {
|
|
1683
1533
|
event.preventDefault();
|
|
1684
1534
|
setBusy(true);
|
|
@@ -1715,48 +1565,48 @@ function AdmissionGrantsWorkspace(props) {
|
|
|
1715
1565
|
setBusy(false);
|
|
1716
1566
|
}
|
|
1717
1567
|
};
|
|
1718
|
-
return /* @__PURE__ */
|
|
1719
|
-
/* @__PURE__ */
|
|
1720
|
-
/* @__PURE__ */
|
|
1721
|
-
/* @__PURE__ */
|
|
1722
|
-
/* @__PURE__ */
|
|
1568
|
+
return /* @__PURE__ */ jsxs13(AdminPage, { children: [
|
|
1569
|
+
/* @__PURE__ */ jsxs13(Panel, { title: copy.title, children: [
|
|
1570
|
+
/* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: 0 }, children: copy.description }),
|
|
1571
|
+
/* @__PURE__ */ jsxs13("form", { onSubmit: (event) => void submit(event), style: { display: "grid", gap: 12, gridTemplateColumns: "minmax(220px,2fr) minmax(180px,1fr) minmax(160px,1fr)" }, children: [
|
|
1572
|
+
/* @__PURE__ */ jsxs13("label", { children: [
|
|
1723
1573
|
copy.email,
|
|
1724
|
-
/* @__PURE__ */
|
|
1574
|
+
/* @__PURE__ */ jsx16("input", { required: true, type: "email", value: email, onInput: (event) => setEmail(event.currentTarget.value) })
|
|
1725
1575
|
] }),
|
|
1726
|
-
/* @__PURE__ */
|
|
1576
|
+
/* @__PURE__ */ jsxs13("label", { children: [
|
|
1727
1577
|
copy.decision,
|
|
1728
|
-
/* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1578
|
+
/* @__PURE__ */ jsxs13("select", { value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
|
|
1579
|
+
/* @__PURE__ */ jsx16("option", { value: "waive_interview", children: copy.waive }),
|
|
1580
|
+
/* @__PURE__ */ jsx16("option", { value: "require_interview", children: copy.require })
|
|
1731
1581
|
] })
|
|
1732
1582
|
] }),
|
|
1733
|
-
/* @__PURE__ */
|
|
1583
|
+
/* @__PURE__ */ jsxs13("label", { children: [
|
|
1734
1584
|
copy.expires,
|
|
1735
|
-
/* @__PURE__ */
|
|
1585
|
+
/* @__PURE__ */ jsx16("input", { type: "date", value: expires, onInput: (event) => setExpires(event.currentTarget.value) })
|
|
1736
1586
|
] }),
|
|
1737
|
-
/* @__PURE__ */
|
|
1587
|
+
/* @__PURE__ */ jsxs13("label", { style: { gridColumn: "1 / -1" }, children: [
|
|
1738
1588
|
copy.note,
|
|
1739
|
-
/* @__PURE__ */
|
|
1589
|
+
/* @__PURE__ */ jsx16("input", { value: note, maxLength: 2e3, onInput: (event) => setNote(event.currentTarget.value) })
|
|
1740
1590
|
] }),
|
|
1741
|
-
/* @__PURE__ */
|
|
1591
|
+
/* @__PURE__ */ jsx16("button", { className: "btn secondary", type: "submit", disabled: busy, children: copy.add })
|
|
1742
1592
|
] }),
|
|
1743
|
-
error ? /* @__PURE__ */
|
|
1593
|
+
error ? /* @__PURE__ */ jsx16("p", { role: "alert", children: error }) : null
|
|
1744
1594
|
] }),
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
resource.loading ? /* @__PURE__ */
|
|
1747
|
-
/* @__PURE__ */
|
|
1595
|
+
/* @__PURE__ */ jsxs13(Panel, { title: copy.ledger, children: [
|
|
1596
|
+
resource.loading ? /* @__PURE__ */ jsx16("p", { className: "muted", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx16("p", { role: "alert", children: resource.error }) : null,
|
|
1597
|
+
/* @__PURE__ */ jsx16("div", { style: { display: "grid", gap: 10 }, children: (resource.data?.grants ?? []).map((grant) => {
|
|
1748
1598
|
const state = grant.consumedAt ? copy.consumed : grant.revokedAt || !grant.active ? copy.revoked : grant.expiresAt && grant.expiresAt <= Date.now() ? copy.expired : copy.active;
|
|
1749
|
-
return /* @__PURE__ */
|
|
1750
|
-
/* @__PURE__ */
|
|
1599
|
+
return /* @__PURE__ */ jsxs13("article", { className: "card", style: { padding: 14 }, children: [
|
|
1600
|
+
/* @__PURE__ */ jsx16("strong", { children: grant.email }),
|
|
1751
1601
|
" \xB7 ",
|
|
1752
1602
|
grant.decision === "waive_interview" ? copy.waive : copy.require,
|
|
1753
|
-
/* @__PURE__ */
|
|
1603
|
+
/* @__PURE__ */ jsxs13("p", { className: "muted", style: { margin: "6px 0" }, children: [
|
|
1754
1604
|
grant.sourceScope === "local" ? copy.local : copy.parent,
|
|
1755
1605
|
" \xB7 ",
|
|
1756
1606
|
state,
|
|
1757
1607
|
grant.note ? ` \xB7 ${grant.note}` : ""
|
|
1758
1608
|
] }),
|
|
1759
|
-
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */
|
|
1609
|
+
grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */ jsx16("button", { className: "btn secondary mini", disabled: busy, onClick: () => void revoke(grant), children: copy.revoke }) : null
|
|
1760
1610
|
] }, grant.id);
|
|
1761
1611
|
}) })
|
|
1762
1612
|
] })
|
|
@@ -1767,14 +1617,285 @@ function admissionGrantsWorkspace(label = "Admission") {
|
|
|
1767
1617
|
id: "admission",
|
|
1768
1618
|
label,
|
|
1769
1619
|
defaultViewId: "grants",
|
|
1770
|
-
render: (ctx) => /* @__PURE__ */
|
|
1620
|
+
render: (ctx) => /* @__PURE__ */ jsx16(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
|
|
1771
1621
|
};
|
|
1772
1622
|
}
|
|
1773
1623
|
|
|
1774
|
-
// src/ui/admin-
|
|
1775
|
-
import {
|
|
1624
|
+
// src/ui/admin-settings-workspace.tsx
|
|
1625
|
+
import { Tabs as Tabs2 } from "@odla-ai/ui/components";
|
|
1626
|
+
|
|
1627
|
+
// src/ui/admin-availability.tsx
|
|
1628
|
+
import { useState as useState12 } from "preact/hooks";
|
|
1629
|
+
import { Button as Button5, Checkbox, Field as Field5, Input } from "@odla-ai/ui/components";
|
|
1630
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
1631
|
+
function AvailabilityBody({ ctx }) {
|
|
1632
|
+
const copy = useChapterCopy().admin.availability;
|
|
1633
|
+
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
|
|
1634
|
+
const [draft, setDraft] = useState12(null);
|
|
1635
|
+
const [busy, setBusy] = useState12(false);
|
|
1636
|
+
const [note, setNote] = useState12(null);
|
|
1637
|
+
const [errors, setErrors] = useState12({});
|
|
1638
|
+
const cfg = draft ?? data?.scheduling ?? null;
|
|
1639
|
+
if (loading) return /* @__PURE__ */ jsx17(AdminNote, { children: copy.loading });
|
|
1640
|
+
if (error || !cfg) return /* @__PURE__ */ jsxs14(AdminNote, { children: [
|
|
1641
|
+
copy.loadFailed,
|
|
1642
|
+
error ? `: ${error}` : "",
|
|
1643
|
+
"."
|
|
1644
|
+
] });
|
|
1645
|
+
const edit = (patch) => setDraft({ ...cfg, ...patch });
|
|
1646
|
+
const toggleDay = (d) => edit({ days: cfg.days.includes(d) ? cfg.days.filter((x) => x !== d) : [...cfg.days, d].sort() });
|
|
1647
|
+
const num = (v, fallback) => Number.isFinite(Number(v)) ? Number(v) : fallback;
|
|
1648
|
+
const save = async () => {
|
|
1649
|
+
setBusy(true);
|
|
1650
|
+
setNote(null);
|
|
1651
|
+
setErrors({});
|
|
1652
|
+
const token = await ctx.getToken();
|
|
1653
|
+
try {
|
|
1654
|
+
const r = await fetch("/api/admin/scheduling", { method: "PUT", headers: { authorization: `Bearer ${token ?? ""}`, "content-type": "application/json" }, body: JSON.stringify(cfg) });
|
|
1655
|
+
const body = await r.json().catch(() => ({}));
|
|
1656
|
+
if (!r.ok) {
|
|
1657
|
+
setErrors(body.errors ?? {});
|
|
1658
|
+
setNote(body.error ?? `save failed (${r.status})`);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
setDraft(null);
|
|
1662
|
+
refresh();
|
|
1663
|
+
setNote(copy.saved);
|
|
1664
|
+
} catch (e) {
|
|
1665
|
+
setNote(e instanceof Error ? e.message : String(e));
|
|
1666
|
+
} finally {
|
|
1667
|
+
setBusy(false);
|
|
1668
|
+
}
|
|
1669
|
+
};
|
|
1670
|
+
const numField = (key, label) => /* @__PURE__ */ jsx17(Field5, { label, error: errors[key], children: /* @__PURE__ */ jsx17(Input, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
|
|
1671
|
+
return /* @__PURE__ */ jsx17(AdminPage, { children: /* @__PURE__ */ jsxs14(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx17("span", { className: "muted", children: note }) : void 0, children: [
|
|
1672
|
+
/* @__PURE__ */ jsx17(Field5, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx17("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx17(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
|
|
1673
|
+
/* @__PURE__ */ jsxs14("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
|
|
1674
|
+
numField("startHour", copy.startHour),
|
|
1675
|
+
numField("endHour", copy.endHour),
|
|
1676
|
+
numField("slotMinutes", copy.slotMinutes),
|
|
1677
|
+
numField("minNoticeHours", copy.minNoticeHours),
|
|
1678
|
+
numField("windowDays", copy.windowDays),
|
|
1679
|
+
/* @__PURE__ */ jsx17(Field5, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx17(Input, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
1680
|
+
] }),
|
|
1681
|
+
/* @__PURE__ */ jsx17(Field5, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx17(Input, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
1682
|
+
/* @__PURE__ */ jsx17("div", { children: /* @__PURE__ */ jsx17(Button5, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
1683
|
+
] }) });
|
|
1684
|
+
}
|
|
1685
|
+
function availabilitySection(options = {}) {
|
|
1686
|
+
const { id = "availability", label = "Availability" } = options;
|
|
1687
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx17(AvailabilityBody, { ctx }) };
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/ui/admin-builder-library.tsx
|
|
1691
|
+
import { useEffect as useEffect8, useState as useState13 } from "preact/hooks";
|
|
1692
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "preact/jsx-runtime";
|
|
1693
|
+
var emptyDraft = () => ({ resources: [] });
|
|
1694
|
+
var scopeLabel = (resource) => resource.scope.kind === "chapter" ? `Only ${resource.scope.chapterId}` : resource.scope.kind === "built_not_found" ? "Only Built Not Found" : "Every chapter";
|
|
1695
|
+
function BuilderLibraryAdmin({ ctx, initialData }) {
|
|
1696
|
+
const resource = useAdminResource(ctx.getToken, "/api/admin/builder-library");
|
|
1697
|
+
const [draft, setDraft] = useState13(JSON.stringify(initialData?.local?.document ?? emptyDraft(), null, 2));
|
|
1698
|
+
const [saveError, setSaveError] = useState13(null);
|
|
1699
|
+
const [saving, setSaving] = useState13(false);
|
|
1700
|
+
useEffect8(() => {
|
|
1701
|
+
if (resource.data) setDraft(JSON.stringify(resource.data.local?.document ?? emptyDraft(), null, 2));
|
|
1702
|
+
}, [resource.data]);
|
|
1703
|
+
const data = initialData ?? resource.data;
|
|
1704
|
+
if (!data && resource.loading) return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsx18(AdminNote, { children: "Loading Builder Library\u2026" }) });
|
|
1705
|
+
if (resource.error || !data) return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsxs15(AdminNote, { children: [
|
|
1706
|
+
"Builder Library could not be loaded: ",
|
|
1707
|
+
resource.error ?? "unknown error"
|
|
1708
|
+
] }) });
|
|
1709
|
+
const save = async () => {
|
|
1710
|
+
setSaveError(null);
|
|
1711
|
+
let document2;
|
|
1712
|
+
try {
|
|
1713
|
+
document2 = JSON.parse(draft);
|
|
1714
|
+
} catch (error) {
|
|
1715
|
+
setSaveError([`JSON is invalid: ${error instanceof Error ? error.message : String(error)}`]);
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
setSaving(true);
|
|
1719
|
+
try {
|
|
1720
|
+
await adminFetch(ctx.getToken, "/api/admin/builder-library", { method: "PUT", body: JSON.stringify({ expectedRevision: data.local?.revision ?? 0, document: document2 }) });
|
|
1721
|
+
resource.refresh();
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
const structured = error;
|
|
1724
|
+
setSaveError(structured.errors ?? [error instanceof Error ? error.message : String(error)]);
|
|
1725
|
+
} finally {
|
|
1726
|
+
setSaving(false);
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
const promote = async (chapterId, resourceId) => {
|
|
1730
|
+
setSaveError(null);
|
|
1731
|
+
try {
|
|
1732
|
+
await adminFetch(ctx.getToken, "/api/admin/builder-library/promote", { method: "POST", body: JSON.stringify({ chapterId, resourceId, expectedRevision: data.local?.revision ?? 0 }) });
|
|
1733
|
+
resource.refresh();
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
setSaveError([error instanceof Error ? error.message : String(error)]);
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
return /* @__PURE__ */ jsxs15(AdminPage, { children: [
|
|
1739
|
+
/* @__PURE__ */ jsxs15(Panel, { children: [
|
|
1740
|
+
/* @__PURE__ */ jsx18("div", { className: "card-label", children: "Builder Library \xB7 private" }),
|
|
1741
|
+
/* @__PURE__ */ jsx18("h2", { children: "References for the agents building sites" }),
|
|
1742
|
+
/* @__PURE__ */ jsx18("p", { children: "This is not member content. Saving a document, Brand asset reference, link, or inspiration here never publishes it to a member page." }),
|
|
1743
|
+
/* @__PURE__ */ jsxs15("div", { className: "stat-band", children: [
|
|
1744
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1745
|
+
/* @__PURE__ */ jsx18("strong", { children: data.local?.document.resources.length ?? 0 }),
|
|
1746
|
+
/* @__PURE__ */ jsx18("span", { children: "Local resources" })
|
|
1747
|
+
] }),
|
|
1748
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1749
|
+
/* @__PURE__ */ jsx18("strong", { children: data.sources.reduce((count, source) => count + source.document.resources.length, 0) }),
|
|
1750
|
+
/* @__PURE__ */ jsx18("span", { children: "Shared in" })
|
|
1751
|
+
] }),
|
|
1752
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
1753
|
+
/* @__PURE__ */ jsx18("strong", { children: "3" }),
|
|
1754
|
+
/* @__PURE__ */ jsx18("span", { children: "Scopes only" })
|
|
1755
|
+
] })
|
|
1756
|
+
] }),
|
|
1757
|
+
/* @__PURE__ */ jsx18(AdminNote, { children: "Visibility is always one chapter, only Built Not Found, or every chapter. Chapters never see sibling libraries." })
|
|
1758
|
+
] }),
|
|
1759
|
+
data.sources.map((source) => /* @__PURE__ */ jsxs15(Panel, { children: [
|
|
1760
|
+
/* @__PURE__ */ jsxs15("div", { className: "card-label", children: [
|
|
1761
|
+
data.mode === "hub" ? "Chapter material" : "Global material",
|
|
1762
|
+
" \xB7 read only"
|
|
1763
|
+
] }),
|
|
1764
|
+
/* @__PURE__ */ jsxs15("h2", { children: [
|
|
1765
|
+
source.sourceId,
|
|
1766
|
+
" \xB7 revision ",
|
|
1767
|
+
source.revision
|
|
1768
|
+
] }),
|
|
1769
|
+
source.document.resources.length === 0 ? /* @__PURE__ */ jsx18(AdminNote, { children: "No resources delivered." }) : /* @__PURE__ */ jsx18("div", { className: "data-list", children: source.document.resources.map((item) => /* @__PURE__ */ jsxs15("article", { className: "card", children: [
|
|
1770
|
+
/* @__PURE__ */ jsx18("strong", { children: item.title }),
|
|
1771
|
+
/* @__PURE__ */ jsxs15("div", { className: "meeting-note", children: [
|
|
1772
|
+
item.kind,
|
|
1773
|
+
" \xB7 ",
|
|
1774
|
+
item.status,
|
|
1775
|
+
" \xB7 ",
|
|
1776
|
+
scopeLabel(item)
|
|
1777
|
+
] }),
|
|
1778
|
+
item.summary ? /* @__PURE__ */ jsx18("p", { children: item.summary }) : null,
|
|
1779
|
+
data.mode === "hub" && item.scope.kind === "chapter" ? /* @__PURE__ */ jsx18("button", { className: "btn", type: "button", onClick: () => void promote(source.sourceId, item.id), children: "Make global" }) : null
|
|
1780
|
+
] }, item.id)) })
|
|
1781
|
+
] }, source.sourceId)),
|
|
1782
|
+
/* @__PURE__ */ jsxs15(Panel, { children: [
|
|
1783
|
+
/* @__PURE__ */ jsxs15("div", { className: "card-label", children: [
|
|
1784
|
+
data.mode === "hub" ? "Built Not Found" : "This chapter",
|
|
1785
|
+
" \xB7 editable"
|
|
1786
|
+
] }),
|
|
1787
|
+
/* @__PURE__ */ jsxs15("h2", { children: [
|
|
1788
|
+
"Local library \xB7 revision ",
|
|
1789
|
+
data.local?.revision ?? 0
|
|
1790
|
+
] }),
|
|
1791
|
+
/* @__PURE__ */ jsx18("p", { className: "meeting-note", children: "Use documents/notes, HTTPS links/inspiration, or canonical Brand book and asset IDs. The server owns revisions, actor history, and timestamps." }),
|
|
1792
|
+
/* @__PURE__ */ jsxs15("label", { className: "field", children: [
|
|
1793
|
+
/* @__PURE__ */ jsx18("span", { children: "Builder resources (JSON)" }),
|
|
1794
|
+
/* @__PURE__ */ jsx18("textarea", { rows: 24, value: draft, disabled: !data.localAuthoring || saving, onInput: (event) => setDraft(event.currentTarget.value), spellcheck: false })
|
|
1795
|
+
] }),
|
|
1796
|
+
saveError ? /* @__PURE__ */ jsxs15("div", { className: "notice danger", role: "alert", children: [
|
|
1797
|
+
/* @__PURE__ */ jsx18("strong", { children: "Fix these items before saving:" }),
|
|
1798
|
+
/* @__PURE__ */ jsx18("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx18("li", { children: error }, error)) })
|
|
1799
|
+
] }) : null,
|
|
1800
|
+
/* @__PURE__ */ jsx18("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save Builder Library" })
|
|
1801
|
+
] })
|
|
1802
|
+
] });
|
|
1803
|
+
}
|
|
1804
|
+
function builderLibrarySection() {
|
|
1805
|
+
return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */ jsx18(BuilderLibraryAdmin, { ctx }) };
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/ui/admin-email.tsx
|
|
1809
|
+
import { useState as useState14 } from "preact/hooks";
|
|
1810
|
+
import { Button as Button6, Checkbox as Checkbox2, DataTable as DataTable3, Field as Field6, Input as Input2, Textarea as Textarea2 } from "@odla-ai/ui/components";
|
|
1776
1811
|
import { jsx as jsx19, jsxs as jsxs16 } from "preact/jsx-runtime";
|
|
1777
|
-
var
|
|
1812
|
+
var logColumns = (copy) => [
|
|
1813
|
+
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
1814
|
+
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
1815
|
+
{ key: "to", header: copy.toColumn, cell: (r) => r.to },
|
|
1816
|
+
{ key: "status", header: copy.statusColumn, cell: (r) => r.error ? /* @__PURE__ */ jsx19("span", { className: "badge", children: copy.failed }) : r.redirected ? /* @__PURE__ */ jsx19("span", { className: "badge", children: copy.redirected }) : copy.delivered }
|
|
1817
|
+
];
|
|
1818
|
+
function EmailBody({ ctx }) {
|
|
1819
|
+
const copy = useChapterCopy().admin.email;
|
|
1820
|
+
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
1821
|
+
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
1822
|
+
const [draft, setDraft] = useState14(null);
|
|
1823
|
+
const [busy, setBusy] = useState14(null);
|
|
1824
|
+
const [note, setNote] = useState14(null);
|
|
1825
|
+
const data = draft ?? cfg.data;
|
|
1826
|
+
if (cfg.loading) return /* @__PURE__ */ jsx19(AdminNote, { children: copy.loading });
|
|
1827
|
+
if (cfg.error || !data) return /* @__PURE__ */ jsxs16(AdminNote, { children: [
|
|
1828
|
+
copy.loadFailed,
|
|
1829
|
+
cfg.error ? `: ${cfg.error}` : "",
|
|
1830
|
+
"."
|
|
1831
|
+
] });
|
|
1832
|
+
const edit = (patch) => setDraft({ ...data, ...patch });
|
|
1833
|
+
const editTemplate = (key, patch) => edit({ emailTemplates: { ...data.emailTemplates, [key]: { ...data.emailTemplates[key], ...patch } } });
|
|
1834
|
+
const run = async (key, fn) => {
|
|
1835
|
+
setBusy(key);
|
|
1836
|
+
setNote(null);
|
|
1837
|
+
try {
|
|
1838
|
+
await fn();
|
|
1839
|
+
} catch (e) {
|
|
1840
|
+
setNote(e instanceof Error ? e.message : String(e));
|
|
1841
|
+
} finally {
|
|
1842
|
+
setBusy(null);
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
const save = () => run("save", async () => {
|
|
1846
|
+
await adminFetch(ctx.getToken, "/api/admin/group/email", { method: "PUT", body: JSON.stringify(data) });
|
|
1847
|
+
setDraft(null);
|
|
1848
|
+
cfg.refresh();
|
|
1849
|
+
setNote(copy.saved);
|
|
1850
|
+
});
|
|
1851
|
+
const test = (template) => run(`test:${template}`, async () => {
|
|
1852
|
+
const r = await adminFetch(ctx.getToken, "/api/admin/email/test", { method: "POST", body: JSON.stringify({ template }) });
|
|
1853
|
+
setNote(r.ok ? formatChapterCopy(copy.sent, {
|
|
1854
|
+
template,
|
|
1855
|
+
address: r.to,
|
|
1856
|
+
redirected: r.redirected ? ` (${copy.redirected})` : ""
|
|
1857
|
+
}) : copy.testFailed);
|
|
1858
|
+
log.refresh();
|
|
1859
|
+
});
|
|
1860
|
+
return /* @__PURE__ */ jsxs16(AdminPage, { children: [
|
|
1861
|
+
/* @__PURE__ */ jsxs16(
|
|
1862
|
+
Panel,
|
|
1863
|
+
{
|
|
1864
|
+
title: copy.delivery,
|
|
1865
|
+
actions: /* @__PURE__ */ jsxs16("span", { className: "muted", children: [
|
|
1866
|
+
data.envName,
|
|
1867
|
+
" \xB7 ",
|
|
1868
|
+
data.transport,
|
|
1869
|
+
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
1870
|
+
] }),
|
|
1871
|
+
children: [
|
|
1872
|
+
/* @__PURE__ */ jsx19(Field6, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx19(Input2, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
1873
|
+
/* @__PURE__ */ jsx19(Field6, { label: copy.replyTo, children: /* @__PURE__ */ jsx19(Input2, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
1874
|
+
/* @__PURE__ */ jsx19(Field6, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx19(Input2, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
1875
|
+
]
|
|
1876
|
+
}
|
|
1877
|
+
),
|
|
1878
|
+
Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */ jsxs16(Panel, { title: key, actions: /* @__PURE__ */ jsx19(Button6, { variant: "secondary", mini: true, disabled: busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }), children: [
|
|
1879
|
+
/* @__PURE__ */ jsx19(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
1880
|
+
/* @__PURE__ */ jsx19(Field6, { label: copy.subject, children: /* @__PURE__ */ jsx19(Input2, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
1881
|
+
/* @__PURE__ */ jsx19(Field6, { label: copy.body, children: /* @__PURE__ */ jsx19(Textarea2, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
|
|
1882
|
+
] }, key)),
|
|
1883
|
+
/* @__PURE__ */ jsxs16("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
1884
|
+
/* @__PURE__ */ jsx19(Button6, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
1885
|
+
note ? /* @__PURE__ */ jsx19("span", { className: "muted", children: note }) : null
|
|
1886
|
+
] }),
|
|
1887
|
+
/* @__PURE__ */ jsx19(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx19(DataTable3, { rows: log.data?.sends ?? [], columns: logColumns(copy), rowKey: (r) => r.id }) })
|
|
1888
|
+
] });
|
|
1889
|
+
}
|
|
1890
|
+
function emailSection(options = {}) {
|
|
1891
|
+
const { id = "email", label = "Email" } = options;
|
|
1892
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx19(EmailBody, { ctx }) };
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// src/ui/admin-member-content.tsx
|
|
1896
|
+
import { useEffect as useEffect9, useState as useState15 } from "preact/hooks";
|
|
1897
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "preact/jsx-runtime";
|
|
1898
|
+
var emptyDraft2 = () => ({ pages: [], upsellOverrides: [] });
|
|
1778
1899
|
function accessLabel(page) {
|
|
1779
1900
|
return page.audience.kind === "all-members" ? "All members" : `Tiers: ${page.audience.tierIds.join(", ")}`;
|
|
1780
1901
|
}
|
|
@@ -1783,17 +1904,17 @@ function MemberContentAdmin({
|
|
|
1783
1904
|
initialData
|
|
1784
1905
|
}) {
|
|
1785
1906
|
const resource = useAdminResource(ctx.getToken, "/api/admin/member-content");
|
|
1786
|
-
const [draft, setDraft] =
|
|
1787
|
-
const [saveError, setSaveError] =
|
|
1788
|
-
const [saving, setSaving] =
|
|
1789
|
-
|
|
1907
|
+
const [draft, setDraft] = useState15(JSON.stringify(initialData?.chapter?.document ?? emptyDraft2(), null, 2));
|
|
1908
|
+
const [saveError, setSaveError] = useState15(null);
|
|
1909
|
+
const [saving, setSaving] = useState15(false);
|
|
1910
|
+
useEffect9(() => {
|
|
1790
1911
|
if (!resource.data) return;
|
|
1791
|
-
setDraft(JSON.stringify(resource.data.chapter?.document ??
|
|
1912
|
+
setDraft(JSON.stringify(resource.data.chapter?.document ?? emptyDraft2(), null, 2));
|
|
1792
1913
|
}, [resource.data]);
|
|
1793
1914
|
const loaded = initialData ?? resource.data;
|
|
1794
|
-
if (!loaded && resource.loading) return /* @__PURE__ */
|
|
1915
|
+
if (!loaded && resource.loading) return /* @__PURE__ */ jsx20(AdminPage, { children: /* @__PURE__ */ jsx20(AdminNote, { children: "Loading member content\u2026" }) });
|
|
1795
1916
|
if (resource.error || !loaded) {
|
|
1796
|
-
return /* @__PURE__ */
|
|
1917
|
+
return /* @__PURE__ */ jsx20(AdminPage, { children: /* @__PURE__ */ jsxs17(AdminNote, { children: [
|
|
1797
1918
|
"Member content could not be loaded: ",
|
|
1798
1919
|
resource.error ?? "unknown error"
|
|
1799
1920
|
] }) });
|
|
@@ -1824,35 +1945,35 @@ function MemberContentAdmin({
|
|
|
1824
1945
|
setSaving(false);
|
|
1825
1946
|
}
|
|
1826
1947
|
};
|
|
1827
|
-
return /* @__PURE__ */
|
|
1828
|
-
/* @__PURE__ */
|
|
1829
|
-
/* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
1831
|
-
/* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
/* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1948
|
+
return /* @__PURE__ */ jsxs17(AdminPage, { children: [
|
|
1949
|
+
/* @__PURE__ */ jsxs17(Panel, { children: [
|
|
1950
|
+
/* @__PURE__ */ jsx20("div", { className: "card-label", children: "Member content" }),
|
|
1951
|
+
/* @__PURE__ */ jsx20("h2", { children: "One catalog, two clear owners" }),
|
|
1952
|
+
/* @__PURE__ */ jsx20("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." }),
|
|
1953
|
+
/* @__PURE__ */ jsxs17("div", { className: "stat-band", children: [
|
|
1954
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
1955
|
+
/* @__PURE__ */ jsx20("strong", { children: inherited.length }),
|
|
1956
|
+
/* @__PURE__ */ jsx20("span", { children: "Inherited pages" })
|
|
1836
1957
|
] }),
|
|
1837
|
-
/* @__PURE__ */
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
1958
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
1959
|
+
/* @__PURE__ */ jsx20("strong", { children: local.length }),
|
|
1960
|
+
/* @__PURE__ */ jsx20("span", { children: "Chapter pages" })
|
|
1840
1961
|
] }),
|
|
1841
|
-
/* @__PURE__ */
|
|
1842
|
-
/* @__PURE__ */
|
|
1843
|
-
/* @__PURE__ */
|
|
1962
|
+
/* @__PURE__ */ jsxs17("div", { children: [
|
|
1963
|
+
/* @__PURE__ */ jsx20("strong", { children: data.tiers.length }),
|
|
1964
|
+
/* @__PURE__ */ jsx20("span", { children: "Declared tiers" })
|
|
1844
1965
|
] })
|
|
1845
1966
|
] })
|
|
1846
1967
|
] }),
|
|
1847
|
-
/* @__PURE__ */
|
|
1848
|
-
/* @__PURE__ */
|
|
1849
|
-
/* @__PURE__ */
|
|
1968
|
+
/* @__PURE__ */ jsxs17(Panel, { children: [
|
|
1969
|
+
/* @__PURE__ */ jsx20("div", { className: "card-label", children: "Inherited \xB7 read only" }),
|
|
1970
|
+
/* @__PURE__ */ jsxs17("h2", { children: [
|
|
1850
1971
|
"Parent pages ",
|
|
1851
1972
|
data.parent ? `\xB7 revision ${data.parent.revision}` : ""
|
|
1852
1973
|
] }),
|
|
1853
|
-
inherited.length === 0 ? /* @__PURE__ */
|
|
1854
|
-
/* @__PURE__ */
|
|
1855
|
-
/* @__PURE__ */
|
|
1974
|
+
inherited.length === 0 ? /* @__PURE__ */ jsx20(AdminNote, { children: "No Parent pages have been delivered." }) : /* @__PURE__ */ jsx20("div", { className: "data-list", "data-member-content-inherited": true, children: inherited.map((page) => /* @__PURE__ */ jsxs17("article", { className: "card", children: [
|
|
1975
|
+
/* @__PURE__ */ jsx20("strong", { children: page.title }),
|
|
1976
|
+
/* @__PURE__ */ jsxs17("div", { className: "meeting-note", children: [
|
|
1856
1977
|
"/",
|
|
1857
1978
|
page.slug,
|
|
1858
1979
|
" \xB7 ",
|
|
@@ -1860,22 +1981,22 @@ function MemberContentAdmin({
|
|
|
1860
1981
|
" \xB7 ",
|
|
1861
1982
|
accessLabel(page)
|
|
1862
1983
|
] }),
|
|
1863
|
-
/* @__PURE__ */
|
|
1984
|
+
/* @__PURE__ */ jsxs17("div", { className: "meeting-note", children: [
|
|
1864
1985
|
"Upsell: ",
|
|
1865
1986
|
page.upsell ? `${page.upsell.owner} controlled` : "none"
|
|
1866
1987
|
] })
|
|
1867
1988
|
] }, page.id)) })
|
|
1868
1989
|
] }),
|
|
1869
|
-
/* @__PURE__ */
|
|
1870
|
-
/* @__PURE__ */
|
|
1871
|
-
/* @__PURE__ */
|
|
1990
|
+
/* @__PURE__ */ jsxs17(Panel, { children: [
|
|
1991
|
+
/* @__PURE__ */ jsx20("div", { className: "card-label", children: "Chapter \xB7 editable" }),
|
|
1992
|
+
/* @__PURE__ */ jsxs17("h2", { children: [
|
|
1872
1993
|
"Local document \xB7 revision ",
|
|
1873
1994
|
data.chapter?.revision ?? 0
|
|
1874
1995
|
] }),
|
|
1875
|
-
/* @__PURE__ */
|
|
1876
|
-
/* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1996
|
+
/* @__PURE__ */ jsx20("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." }),
|
|
1997
|
+
/* @__PURE__ */ jsxs17("label", { className: "field", children: [
|
|
1998
|
+
/* @__PURE__ */ jsx20("span", { children: "Pages and delegated upsells (JSON)" }),
|
|
1999
|
+
/* @__PURE__ */ jsx20(
|
|
1879
2000
|
"textarea",
|
|
1880
2001
|
{
|
|
1881
2002
|
rows: 24,
|
|
@@ -1886,12 +2007,12 @@ function MemberContentAdmin({
|
|
|
1886
2007
|
}
|
|
1887
2008
|
)
|
|
1888
2009
|
] }),
|
|
1889
|
-
saveError ? /* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
2010
|
+
saveError ? /* @__PURE__ */ jsxs17("div", { className: "notice danger", role: "alert", children: [
|
|
2011
|
+
/* @__PURE__ */ jsx20("strong", { children: "Fix these items before saving:" }),
|
|
2012
|
+
/* @__PURE__ */ jsx20("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx20("li", { children: error }, error)) })
|
|
1892
2013
|
] }) : null,
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
!data.localAuthoring ? /* @__PURE__ */
|
|
2014
|
+
/* @__PURE__ */ jsx20("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save chapter content" }),
|
|
2015
|
+
!data.localAuthoring ? /* @__PURE__ */ jsx20(AdminNote, { children: "Local authoring is disabled in the checked-in Chapter config." }) : null
|
|
1895
2016
|
] })
|
|
1896
2017
|
] });
|
|
1897
2018
|
}
|
|
@@ -1899,12 +2020,48 @@ function memberContentSection() {
|
|
|
1899
2020
|
return {
|
|
1900
2021
|
id: "member-content",
|
|
1901
2022
|
label: "Member content",
|
|
1902
|
-
render: (ctx) => /* @__PURE__ */
|
|
2023
|
+
render: (ctx) => /* @__PURE__ */ jsx20(MemberContentAdmin, { ctx })
|
|
1903
2024
|
};
|
|
1904
2025
|
}
|
|
1905
2026
|
|
|
2027
|
+
// src/ui/admin-settings-workspace.tsx
|
|
2028
|
+
import { jsx as jsx21 } from "preact/jsx-runtime";
|
|
2029
|
+
var viewContext = (ctx, viewId, active) => {
|
|
2030
|
+
const route = { workspaceId: ctx.route.workspaceId, viewId, ...active && ctx.route.recordId ? { recordId: ctx.route.recordId } : {}, ...active && ctx.route.detailTab ? { detailTab: ctx.route.detailTab } : {} };
|
|
2031
|
+
const targetFromView = (target) => {
|
|
2032
|
+
if (typeof target === "string") return target;
|
|
2033
|
+
if (target.workspaceId && target.workspaceId !== route.workspaceId) return target;
|
|
2034
|
+
if (target.viewId && target.viewId !== route.viewId) return target;
|
|
2035
|
+
return { ...route, ...target };
|
|
2036
|
+
};
|
|
2037
|
+
return { ...ctx, route, navigate: (target) => ctx.navigate(targetFromView(target)), href: (target) => ctx.href(targetFromView(target)) };
|
|
2038
|
+
};
|
|
2039
|
+
function SettingsWorkspace({ chapter, ctx }) {
|
|
2040
|
+
const copy = chapter.copy.admin;
|
|
2041
|
+
const active = ctx.route.viewId === "email" ? "email" : ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : ctx.route.viewId === "member-content" && chapter.memberContent ? "member-content" : "calendar";
|
|
2042
|
+
const calendar = availabilitySection({ id: "calendar", label: copy.workspaces.calendar });
|
|
2043
|
+
const email = emailSection({ label: copy.workspaces.email });
|
|
2044
|
+
const content = memberContentSection();
|
|
2045
|
+
const library = builderLibrarySection();
|
|
2046
|
+
return /* @__PURE__ */ jsx21(Tabs2, { ariaLabel: copy.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2047
|
+
{ value: "calendar", label: copy.workspaces.calendar, href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }), panel: calendar.render(viewContext(ctx, "calendar", active === "calendar")) },
|
|
2048
|
+
{ value: "email", label: copy.workspaces.email, href: ctx.href({ viewId: "email", recordId: null, detailTab: null }), panel: email.render(viewContext(ctx, "email", active === "email")) },
|
|
2049
|
+
...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")) }] : [],
|
|
2050
|
+
...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")) }] : []
|
|
2051
|
+
] });
|
|
2052
|
+
}
|
|
2053
|
+
function LeaderSettingsWorkspace({ chapter, ctx }) {
|
|
2054
|
+
const email = emailSection({ label: chapter.copy.admin.workspaces.email });
|
|
2055
|
+
const library = builderLibrarySection();
|
|
2056
|
+
const active = ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : "email";
|
|
2057
|
+
return /* @__PURE__ */ jsx21(Tabs2, { ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
|
|
2058
|
+
{ 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")) },
|
|
2059
|
+
...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")) }] : []
|
|
2060
|
+
] });
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1906
2063
|
// src/ui/admin-workspaces.tsx
|
|
1907
|
-
import { jsx as
|
|
2064
|
+
import { jsx as jsx22 } from "preact/jsx-runtime";
|
|
1908
2065
|
var adminViewContext = (ctx, viewId, active) => {
|
|
1909
2066
|
const route = {
|
|
1910
2067
|
workspaceId: ctx.route.workspaceId,
|
|
@@ -1930,8 +2087,8 @@ function DashboardWorkspace({ chapter, ctx }) {
|
|
|
1930
2087
|
const active = ctx.route.viewId === "billing" ? "billing" : "overview";
|
|
1931
2088
|
const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
|
|
1932
2089
|
const billing = billingSection({ label: copy.workspaces.billing });
|
|
1933
|
-
return /* @__PURE__ */
|
|
1934
|
-
|
|
2090
|
+
return /* @__PURE__ */ jsx22(
|
|
2091
|
+
Tabs3,
|
|
1935
2092
|
{
|
|
1936
2093
|
ariaLabel: copy.workspaces.dashboardViewsLabel,
|
|
1937
2094
|
value: active,
|
|
@@ -1953,48 +2110,13 @@ function DashboardWorkspace({ chapter, ctx }) {
|
|
|
1953
2110
|
}
|
|
1954
2111
|
);
|
|
1955
2112
|
}
|
|
1956
|
-
function SettingsWorkspace({ chapter, ctx }) {
|
|
1957
|
-
const copy = chapter.copy.admin;
|
|
1958
|
-
const active = ctx.route.viewId === "email" ? "email" : ctx.route.viewId === "member-content" && chapter.memberContent ? "member-content" : "calendar";
|
|
1959
|
-
const calendar = availabilitySection({ id: "calendar", label: copy.workspaces.calendar });
|
|
1960
|
-
const email = emailSection({ label: copy.workspaces.email });
|
|
1961
|
-
const content = memberContentSection();
|
|
1962
|
-
return /* @__PURE__ */ jsx20(
|
|
1963
|
-
Tabs2,
|
|
1964
|
-
{
|
|
1965
|
-
ariaLabel: copy.workspaces.settingsViewsLabel,
|
|
1966
|
-
value: active,
|
|
1967
|
-
mount: "active",
|
|
1968
|
-
items: [
|
|
1969
|
-
{
|
|
1970
|
-
value: "calendar",
|
|
1971
|
-
label: copy.workspaces.calendar,
|
|
1972
|
-
href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }),
|
|
1973
|
-
panel: calendar.render(adminViewContext(ctx, "calendar", active === "calendar"))
|
|
1974
|
-
},
|
|
1975
|
-
{
|
|
1976
|
-
value: "email",
|
|
1977
|
-
label: copy.workspaces.email,
|
|
1978
|
-
href: ctx.href({ viewId: "email", recordId: null, detailTab: null }),
|
|
1979
|
-
panel: email.render(adminViewContext(ctx, "email", active === "email"))
|
|
1980
|
-
},
|
|
1981
|
-
...chapter.memberContent ? [{
|
|
1982
|
-
value: "member-content",
|
|
1983
|
-
label: "Member content",
|
|
1984
|
-
href: ctx.href({ viewId: "member-content", recordId: null, detailTab: null }),
|
|
1985
|
-
panel: content.render(adminViewContext(ctx, "member-content", active === "member-content"))
|
|
1986
|
-
}] : []
|
|
1987
|
-
]
|
|
1988
|
-
}
|
|
1989
|
-
);
|
|
1990
|
-
}
|
|
1991
2113
|
function PeopleWorkspace({ chapter, ctx }) {
|
|
1992
2114
|
const types = Object.entries(chapter.crm.config.types).filter(([, def]) => (def.workspace ?? "people") === "people");
|
|
1993
2115
|
const fallback = types[0]?.[0] ?? "person";
|
|
1994
2116
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
1995
2117
|
const networkSharing = chapter.network.targets.length > 0;
|
|
1996
|
-
return /* @__PURE__ */
|
|
1997
|
-
|
|
2118
|
+
return /* @__PURE__ */ jsx22(
|
|
2119
|
+
Tabs3,
|
|
1998
2120
|
{
|
|
1999
2121
|
ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
|
|
2000
2122
|
value: active,
|
|
@@ -2023,9 +2145,9 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2023
2145
|
const types = Object.entries(chapter.crm.config.types).filter(([, def]) => def.workspace === "portfolio");
|
|
2024
2146
|
const fallback = types[0]?.[0] ?? "";
|
|
2025
2147
|
const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
|
|
2026
|
-
if (!active) return /* @__PURE__ */
|
|
2027
|
-
return /* @__PURE__ */
|
|
2028
|
-
|
|
2148
|
+
if (!active) return /* @__PURE__ */ jsx22(LeaderNetworkOverview, { ctx });
|
|
2149
|
+
return /* @__PURE__ */ jsx22(
|
|
2150
|
+
Tabs3,
|
|
2029
2151
|
{
|
|
2030
2152
|
ariaLabel: copy.workspaces.portfolioViewsLabel,
|
|
2031
2153
|
value: active,
|
|
@@ -2048,8 +2170,8 @@ function PortfolioWorkspace({ chapter, ctx }) {
|
|
|
2048
2170
|
);
|
|
2049
2171
|
}
|
|
2050
2172
|
function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
2051
|
-
return /* @__PURE__ */
|
|
2052
|
-
|
|
2173
|
+
return /* @__PURE__ */ jsx22(
|
|
2174
|
+
Tabs3,
|
|
2053
2175
|
{
|
|
2054
2176
|
ariaLabel: chapter.copy.admin.workspaces.dashboardViewsLabel,
|
|
2055
2177
|
value: "overview",
|
|
@@ -2058,24 +2180,7 @@ function LeaderDashboardWorkspace({ chapter, ctx }) {
|
|
|
2058
2180
|
value: "overview",
|
|
2059
2181
|
label: chapter.copy.admin.workspaces.overview,
|
|
2060
2182
|
href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
|
|
2061
|
-
panel: /* @__PURE__ */
|
|
2062
|
-
}]
|
|
2063
|
-
}
|
|
2064
|
-
);
|
|
2065
|
-
}
|
|
2066
|
-
function LeaderSettingsWorkspace({ chapter, ctx }) {
|
|
2067
|
-
const email = emailSection({ label: chapter.copy.admin.workspaces.email });
|
|
2068
|
-
return /* @__PURE__ */ jsx20(
|
|
2069
|
-
Tabs2,
|
|
2070
|
-
{
|
|
2071
|
-
ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel,
|
|
2072
|
-
value: "email",
|
|
2073
|
-
mount: "active",
|
|
2074
|
-
items: [{
|
|
2075
|
-
value: "email",
|
|
2076
|
-
label: chapter.copy.admin.workspaces.email,
|
|
2077
|
-
href: ctx.href({ viewId: "email", recordId: null, detailTab: null }),
|
|
2078
|
-
panel: email.render(adminViewContext(ctx, "email", true))
|
|
2183
|
+
panel: /* @__PURE__ */ jsx22(LeaderNetworkOverview, { ctx: adminViewContext(ctx, "overview", true) })
|
|
2079
2184
|
}]
|
|
2080
2185
|
}
|
|
2081
2186
|
);
|
|
@@ -2086,7 +2191,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2086
2191
|
id: "people",
|
|
2087
2192
|
label: copy.workspaces.people,
|
|
2088
2193
|
defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
|
|
2089
|
-
render: (ctx) => /* @__PURE__ */
|
|
2194
|
+
render: (ctx) => /* @__PURE__ */ jsx22(PeopleWorkspace, { chapter, ctx })
|
|
2090
2195
|
};
|
|
2091
2196
|
if (chapter.mode === "hub" && chapter.network.targets.length === 0) return [people];
|
|
2092
2197
|
if (chapter.mode === "hub") {
|
|
@@ -2096,7 +2201,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2096
2201
|
id: "dashboard",
|
|
2097
2202
|
label: copy.workspaces.dashboard,
|
|
2098
2203
|
defaultViewId: "overview",
|
|
2099
|
-
render: (ctx) => /* @__PURE__ */
|
|
2204
|
+
render: (ctx) => /* @__PURE__ */ jsx22(LeaderDashboardWorkspace, { chapter, ctx })
|
|
2100
2205
|
},
|
|
2101
2206
|
networkRecordsWorkspace(chapter),
|
|
2102
2207
|
people,
|
|
@@ -2104,13 +2209,13 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2104
2209
|
id: "portfolio",
|
|
2105
2210
|
label: copy.workspaces.portfolio,
|
|
2106
2211
|
defaultViewId: portfolioTypes[0]?.[0],
|
|
2107
|
-
render: (ctx) => /* @__PURE__ */
|
|
2212
|
+
render: (ctx) => /* @__PURE__ */ jsx22(PortfolioWorkspace, { chapter, ctx })
|
|
2108
2213
|
},
|
|
2109
2214
|
{
|
|
2110
2215
|
id: "settings",
|
|
2111
2216
|
label: copy.workspaces.settings,
|
|
2112
2217
|
defaultViewId: "email",
|
|
2113
|
-
render: (ctx) => /* @__PURE__ */
|
|
2218
|
+
render: (ctx) => /* @__PURE__ */ jsx22(LeaderSettingsWorkspace, { chapter, ctx })
|
|
2114
2219
|
}
|
|
2115
2220
|
];
|
|
2116
2221
|
}
|
|
@@ -2119,7 +2224,7 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2119
2224
|
id: "dashboard",
|
|
2120
2225
|
label: copy.workspaces.dashboard,
|
|
2121
2226
|
defaultViewId: "overview",
|
|
2122
|
-
render: (ctx) => /* @__PURE__ */
|
|
2227
|
+
render: (ctx) => /* @__PURE__ */ jsx22(DashboardWorkspace, { chapter, ctx })
|
|
2123
2228
|
},
|
|
2124
2229
|
people,
|
|
2125
2230
|
admissionGrantsWorkspace(copy.workspaces.admission),
|
|
@@ -2127,17 +2232,17 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
2127
2232
|
id: "settings",
|
|
2128
2233
|
label: copy.workspaces.settings,
|
|
2129
2234
|
defaultViewId: "calendar",
|
|
2130
|
-
render: (ctx) => /* @__PURE__ */
|
|
2235
|
+
render: (ctx) => /* @__PURE__ */ jsx22(SettingsWorkspace, { chapter, ctx })
|
|
2131
2236
|
}
|
|
2132
2237
|
];
|
|
2133
2238
|
}
|
|
2134
2239
|
|
|
2135
2240
|
// src/ui/admin-frame.tsx
|
|
2136
2241
|
import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
|
|
2137
|
-
import { jsx as
|
|
2242
|
+
import { jsx as jsx23, jsxs as jsxs18 } from "preact/jsx-runtime";
|
|
2138
2243
|
function AdminTheme(props) {
|
|
2139
2244
|
const brand = props.chapter?.brand;
|
|
2140
|
-
return /* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ jsx23(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs18(
|
|
2141
2246
|
ThemeScope2,
|
|
2142
2247
|
{
|
|
2143
2248
|
ref: props.scopeRef,
|
|
@@ -2146,7 +2251,7 @@ function AdminTheme(props) {
|
|
|
2146
2251
|
accent: brand?.accent,
|
|
2147
2252
|
colorScheme: brand?.colorScheme ?? "light",
|
|
2148
2253
|
children: [
|
|
2149
|
-
/* @__PURE__ */
|
|
2254
|
+
/* @__PURE__ */ jsx23(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
|
|
2150
2255
|
props.children
|
|
2151
2256
|
]
|
|
2152
2257
|
}
|
|
@@ -2166,11 +2271,11 @@ function signInRedirect(basePath, workspaces) {
|
|
|
2166
2271
|
}
|
|
2167
2272
|
|
|
2168
2273
|
// src/ui/admin-appearance.ts
|
|
2169
|
-
import { useEffect as
|
|
2274
|
+
import { useEffect as useEffect10, useState as useState16 } from "preact/hooks";
|
|
2170
2275
|
import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
|
|
2171
2276
|
function useScopedClerkAppearance(scope) {
|
|
2172
|
-
const [appearance, setAppearance] =
|
|
2173
|
-
|
|
2277
|
+
const [appearance, setAppearance] = useState16();
|
|
2278
|
+
useEffect10(() => {
|
|
2174
2279
|
setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
|
|
2175
2280
|
}, [scope]);
|
|
2176
2281
|
return appearance;
|
|
@@ -2201,7 +2306,7 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
|
|
|
2201
2306
|
}
|
|
2202
2307
|
|
|
2203
2308
|
// src/ui/admin.tsx
|
|
2204
|
-
import { jsx as
|
|
2309
|
+
import { jsx as jsx24, jsxs as jsxs19 } from "preact/jsx-runtime";
|
|
2205
2310
|
function Authed(props) {
|
|
2206
2311
|
const copy = useChapterCopy().admin;
|
|
2207
2312
|
const {
|
|
@@ -2228,8 +2333,8 @@ function Authed(props) {
|
|
|
2228
2333
|
}),
|
|
2229
2334
|
[getToken, crmBasePath]
|
|
2230
2335
|
);
|
|
2231
|
-
const [state, setState] =
|
|
2232
|
-
|
|
2336
|
+
const [state, setState] = useState17({ status: "checking" });
|
|
2337
|
+
useEffect11(() => {
|
|
2233
2338
|
let live = true;
|
|
2234
2339
|
void (async () => {
|
|
2235
2340
|
try {
|
|
@@ -2248,17 +2353,17 @@ function Authed(props) {
|
|
|
2248
2353
|
live = false;
|
|
2249
2354
|
};
|
|
2250
2355
|
}, [getToken, apiBase, auth]);
|
|
2251
|
-
if (state.status === "checking") return /* @__PURE__ */
|
|
2356
|
+
if (state.status === "checking") return /* @__PURE__ */ jsx24(Gate, { brand, children: /* @__PURE__ */ jsx24("p", { style: S.muted, children: copy.auth.checking }) });
|
|
2252
2357
|
if (state.status === "denied") {
|
|
2253
|
-
return /* @__PURE__ */
|
|
2254
|
-
/* @__PURE__ */
|
|
2255
|
-
/* @__PURE__ */
|
|
2358
|
+
return /* @__PURE__ */ jsx24(Gate, { brand, children: /* @__PURE__ */ jsxs19("div", { className: "card", style: S.card, children: [
|
|
2359
|
+
/* @__PURE__ */ jsx24("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
|
|
2360
|
+
/* @__PURE__ */ jsx24("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
|
|
2256
2361
|
account: state.email ?? copy.auth.thisAccount
|
|
2257
2362
|
}) }),
|
|
2258
|
-
/* @__PURE__ */
|
|
2363
|
+
/* @__PURE__ */ jsx24("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
|
|
2259
2364
|
] }) });
|
|
2260
2365
|
}
|
|
2261
|
-
return /* @__PURE__ */
|
|
2366
|
+
return /* @__PURE__ */ jsx24(
|
|
2262
2367
|
AdminShell,
|
|
2263
2368
|
{
|
|
2264
2369
|
workspaces,
|
|
@@ -2299,10 +2404,10 @@ function ChapterAdmin(props) {
|
|
|
2299
2404
|
const chrome = props.chrome ?? "embedded";
|
|
2300
2405
|
const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
|
|
2301
2406
|
const redirect = signInRedirect(basePath, workspaces);
|
|
2302
|
-
const [publishableKey, setPublishableKey] =
|
|
2407
|
+
const [publishableKey, setPublishableKey] = useState17(void 0);
|
|
2303
2408
|
const scopeRef = useRef(null);
|
|
2304
2409
|
const appearance = useScopedClerkAppearance(scopeRef);
|
|
2305
|
-
|
|
2410
|
+
useEffect11(() => {
|
|
2306
2411
|
let live = true;
|
|
2307
2412
|
void loadChapterAdminConfig(auth, apiBase).then((config) => live && setPublishableKey(config.publishableKey ?? null)).catch(() => live && setPublishableKey(null));
|
|
2308
2413
|
return () => {
|
|
@@ -2311,23 +2416,23 @@ function ChapterAdmin(props) {
|
|
|
2311
2416
|
}, [apiBase, auth]);
|
|
2312
2417
|
let content;
|
|
2313
2418
|
if (publishableKey === void 0 || publishableKey && !appearance) {
|
|
2314
|
-
content = /* @__PURE__ */
|
|
2419
|
+
content = /* @__PURE__ */ jsx24(Gate, { brand, children: /* @__PURE__ */ jsx24("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
2315
2420
|
} else if (!publishableKey) {
|
|
2316
|
-
content = /* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2421
|
+
content = /* @__PURE__ */ jsx24(Gate, { brand, children: /* @__PURE__ */ jsxs19("div", { className: "card", style: S.card, children: [
|
|
2422
|
+
/* @__PURE__ */ jsx24("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
|
|
2423
|
+
/* @__PURE__ */ jsx24("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
|
|
2319
2424
|
] }) });
|
|
2320
2425
|
} else if (workspaces.length === 0) {
|
|
2321
|
-
content = /* @__PURE__ */
|
|
2426
|
+
content = /* @__PURE__ */ jsx24(Gate, { brand, children: /* @__PURE__ */ jsx24("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
|
|
2322
2427
|
} else {
|
|
2323
|
-
content = /* @__PURE__ */
|
|
2428
|
+
content = /* @__PURE__ */ jsxs19(
|
|
2324
2429
|
ClerkGate,
|
|
2325
2430
|
{
|
|
2326
2431
|
publishableKey,
|
|
2327
2432
|
appearance,
|
|
2328
2433
|
afterSignOutUrl: "/",
|
|
2329
2434
|
children: [
|
|
2330
|
-
/* @__PURE__ */
|
|
2435
|
+
/* @__PURE__ */ jsx24(SignedOut, { children: /* @__PURE__ */ jsx24(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx24(
|
|
2331
2436
|
SignIn,
|
|
2332
2437
|
{
|
|
2333
2438
|
routing: "hash",
|
|
@@ -2335,7 +2440,7 @@ function ChapterAdmin(props) {
|
|
|
2335
2440
|
signUpForceRedirectUrl: redirect
|
|
2336
2441
|
}
|
|
2337
2442
|
) }) }),
|
|
2338
|
-
/* @__PURE__ */
|
|
2443
|
+
/* @__PURE__ */ jsx24(SignedIn, { children: /* @__PURE__ */ jsx24(
|
|
2339
2444
|
Authed,
|
|
2340
2445
|
{
|
|
2341
2446
|
workspaces,
|
|
@@ -2355,7 +2460,7 @@ function ChapterAdmin(props) {
|
|
|
2355
2460
|
}
|
|
2356
2461
|
);
|
|
2357
2462
|
}
|
|
2358
|
-
return /* @__PURE__ */
|
|
2463
|
+
return /* @__PURE__ */ jsx24(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
|
|
2359
2464
|
}
|
|
2360
2465
|
|
|
2361
2466
|
// src/ui/admin-defaults.ts
|
|
@@ -2364,17 +2469,17 @@ function defaultAdminSections(chapter) {
|
|
|
2364
2469
|
}
|
|
2365
2470
|
|
|
2366
2471
|
// src/ui/admin-meetings.tsx
|
|
2367
|
-
import { useState as
|
|
2472
|
+
import { useState as useState18 } from "preact/hooks";
|
|
2368
2473
|
import { Button as Button7 } from "@odla-ai/ui/components";
|
|
2369
|
-
import { Fragment as Fragment4, jsx as
|
|
2474
|
+
import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs20 } from "preact/jsx-runtime";
|
|
2370
2475
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
2371
2476
|
function MeetingsBody({ ctx }) {
|
|
2372
2477
|
const copy = useChapterCopy().admin.meetings;
|
|
2373
2478
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
|
|
2374
|
-
const [busy, setBusy] =
|
|
2375
|
-
const [note, setNote] =
|
|
2376
|
-
if (loading) return /* @__PURE__ */
|
|
2377
|
-
if (error || !data) return /* @__PURE__ */
|
|
2479
|
+
const [busy, setBusy] = useState18(null);
|
|
2480
|
+
const [note, setNote] = useState18(null);
|
|
2481
|
+
if (loading) return /* @__PURE__ */ jsx25(AdminNote, { children: copy.loading });
|
|
2482
|
+
if (error || !data) return /* @__PURE__ */ jsxs20(AdminNote, { children: [
|
|
2378
2483
|
copy.loadFailed,
|
|
2379
2484
|
error ? `: ${error}` : "",
|
|
2380
2485
|
"."
|
|
@@ -2405,32 +2510,32 @@ function MeetingsBody({ ctx }) {
|
|
|
2405
2510
|
}
|
|
2406
2511
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
2407
2512
|
};
|
|
2408
|
-
return /* @__PURE__ */
|
|
2513
|
+
return /* @__PURE__ */ jsx25(AdminPage, { children: /* @__PURE__ */ jsx25(Panel, { title: /* @__PURE__ */ jsxs20(Fragment4, { children: [
|
|
2409
2514
|
copy.agenda,
|
|
2410
2515
|
" ",
|
|
2411
|
-
/* @__PURE__ */
|
|
2516
|
+
/* @__PURE__ */ jsxs20("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
2412
2517
|
"\xB7 ",
|
|
2413
2518
|
data.timezone
|
|
2414
2519
|
] })
|
|
2415
|
-
] }), actions: note ? /* @__PURE__ */
|
|
2416
|
-
/* @__PURE__ */
|
|
2417
|
-
/* @__PURE__ */
|
|
2418
|
-
/* @__PURE__ */
|
|
2419
|
-
m.drift && m.drift !== "none" ? /* @__PURE__ */
|
|
2520
|
+
] }), actions: note ? /* @__PURE__ */ jsx25("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx25("p", { className: "muted", style: { margin: 0 }, children: copy.empty }) : /* @__PURE__ */ jsx25("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs20("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
|
|
2521
|
+
/* @__PURE__ */ jsx25("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
|
|
2522
|
+
/* @__PURE__ */ jsx25("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
|
|
2523
|
+
/* @__PURE__ */ jsx25("span", { className: "badge", children: m.status }),
|
|
2524
|
+
m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs20("span", { className: "badge", children: [
|
|
2420
2525
|
copy.drift,
|
|
2421
2526
|
": ",
|
|
2422
2527
|
m.drift
|
|
2423
2528
|
] }) : null,
|
|
2424
|
-
m.meetUrl ? /* @__PURE__ */
|
|
2425
|
-
m.status === "scheduled" ? /* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2529
|
+
m.meetUrl ? /* @__PURE__ */ jsx25("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
2530
|
+
m.status === "scheduled" ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
|
|
2531
|
+
/* @__PURE__ */ jsx25(Button7, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
2532
|
+
/* @__PURE__ */ jsx25(Button7, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
2428
2533
|
] }) : null
|
|
2429
2534
|
] }, m.id)) }) }) });
|
|
2430
2535
|
}
|
|
2431
2536
|
function meetingsSection(options = {}) {
|
|
2432
2537
|
const { id = "meetings", label = "Meetings" } = options;
|
|
2433
|
-
return { id, label, render: (ctx) => /* @__PURE__ */
|
|
2538
|
+
return { id, label, render: (ctx) => /* @__PURE__ */ jsx25(MeetingsBody, { ctx }) };
|
|
2434
2539
|
}
|
|
2435
2540
|
|
|
2436
2541
|
export {
|
|
@@ -2444,10 +2549,8 @@ export {
|
|
|
2444
2549
|
adminSectionHref,
|
|
2445
2550
|
adminFetch,
|
|
2446
2551
|
useAdminResource,
|
|
2447
|
-
availabilitySection,
|
|
2448
2552
|
billingSection,
|
|
2449
2553
|
dashboardSection,
|
|
2450
|
-
emailSection,
|
|
2451
2554
|
RecordActions,
|
|
2452
2555
|
NetworkShareActions,
|
|
2453
2556
|
applicationLifecycleAdapter,
|
|
@@ -2458,6 +2561,10 @@ export {
|
|
|
2458
2561
|
LeaderNetworkOverview,
|
|
2459
2562
|
AdmissionGrantsWorkspace,
|
|
2460
2563
|
admissionGrantsWorkspace,
|
|
2564
|
+
availabilitySection,
|
|
2565
|
+
BuilderLibraryAdmin,
|
|
2566
|
+
builderLibrarySection,
|
|
2567
|
+
emailSection,
|
|
2461
2568
|
MemberContentAdmin,
|
|
2462
2569
|
memberContentSection,
|
|
2463
2570
|
defaultAdminWorkspaces,
|
|
@@ -2467,4 +2574,4 @@ export {
|
|
|
2467
2574
|
defaultAdminSections,
|
|
2468
2575
|
meetingsSection
|
|
2469
2576
|
};
|
|
2470
|
-
//# sourceMappingURL=chunk-
|
|
2577
|
+
//# sourceMappingURL=chunk-4K2Q5JBC.js.map
|