@odla-ai/chapter 0.61.0 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,13 @@ import {
2
2
  BrandStyle,
3
3
  ChapterCopyProvider,
4
4
  DEFAULT_CHAPTER_COPY,
5
+ fmtMoney,
5
6
  formatChapterCopy,
6
7
  useChapterCopy
7
- } from "./chunk-PKVHXKGJ.js";
8
+ } from "./chunk-OXYMVK2O.js";
8
9
 
9
10
  // src/ui/admin.tsx
10
- import { useEffect as useEffect12, useMemo as useMemo3, useRef as useRef3, useState as useState27 } from "preact/hooks";
11
+ import { useEffect as useEffect14, useMemo as useMemo3, useRef as useRef3, useState as useState31 } from "preact/hooks";
11
12
  import { ClerkGate, SignedIn, SignedOut, SignIn, useClerkAuth } from "@odla-ai/auth-clerk";
12
13
  import { CrmClient } from "@odla-ai/crm";
13
14
 
@@ -710,6 +711,74 @@ function dashboardSection(options = {}) {
710
711
  // src/ui/admin-people-workspace.tsx
711
712
  import { BillingCard, CrmWorkspace } from "@odla-ai/crm/ui";
712
713
 
714
+ // src/ui/admin-member-tier.tsx
715
+ import { useEffect as useEffect4, useState as useState6 } from "preact/hooks";
716
+ import { Select } from "@odla-ai/ui/components";
717
+ import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
718
+ function MemberTierCard(props) {
719
+ const path = `/api/admin/applications/${encodeURIComponent(props.applicationId)}/entitlement-tier`;
720
+ const view = useAdminResource(props.getToken, path);
721
+ const [tierId, setTierId] = useState6("");
722
+ const [reason, setReason] = useState6("");
723
+ const [paymentReference, setPaymentReference] = useState6("");
724
+ const [status, setStatus] = useState6(null);
725
+ useEffect4(() => {
726
+ if (view.data && !tierId) setTierId(view.data.entitledTierId ?? view.data.tiers[0]?.id ?? "");
727
+ }, [view.data]);
728
+ if (!view.data) return null;
729
+ const send = async (method) => {
730
+ try {
731
+ const result = await adminFetch(props.getToken, path, { method, body: JSON.stringify({ tierId, reason, paymentReference }) });
732
+ setStatus(`Portal tier is now ${result.toTierId ?? "the purchased tier"}.`);
733
+ setReason("");
734
+ view.refresh();
735
+ } catch (error) {
736
+ setStatus(error instanceof Error ? error.message : String(error));
737
+ }
738
+ };
739
+ const changed = view.data.entitledTierId !== view.data.purchasedTierId;
740
+ return /* @__PURE__ */ jsxs7("div", { className: "card", "data-member-tier": true, children: [
741
+ /* @__PURE__ */ jsx7("div", { className: "card-label", children: "Portal tier" }),
742
+ /* @__PURE__ */ jsxs7("p", { className: "meeting-note", children: [
743
+ "Purchased: ",
744
+ view.data.purchasedTierId ?? "none",
745
+ " \xB7 Portal: ",
746
+ view.data.entitledTierId ?? "none",
747
+ view.data.entitlementTierEndsAt ? ` until ${new Date(view.data.entitlementTierEndsAt).toLocaleDateString("en-US")}` : "",
748
+ ". Use after a payment taken outside checkout; the membership purchase is unchanged."
749
+ ] }),
750
+ /* @__PURE__ */ jsxs7("label", { className: "field", children: [
751
+ /* @__PURE__ */ jsx7("span", { children: "Tier" }),
752
+ /* @__PURE__ */ jsx7(Select, { value: tierId, onChange: (event) => setTierId(event.currentTarget.value), children: view.data.tiers.map((tier) => /* @__PURE__ */ jsx7("option", { value: tier.id, children: tier.name }, tier.id)) })
753
+ ] }),
754
+ /* @__PURE__ */ jsxs7("label", { className: "field", children: [
755
+ /* @__PURE__ */ jsx7("span", { children: "Reason" }),
756
+ /* @__PURE__ */ jsx7("input", { required: true, value: reason, onInput: (event) => setReason(event.currentTarget.value) })
757
+ ] }),
758
+ /* @__PURE__ */ jsxs7("label", { className: "field", children: [
759
+ /* @__PURE__ */ jsx7("span", { children: "Payment reference (optional)" }),
760
+ /* @__PURE__ */ jsx7("input", { value: paymentReference, onInput: (event) => setPaymentReference(event.currentTarget.value) })
761
+ ] }),
762
+ /* @__PURE__ */ jsxs7("div", { className: "account-actions", children: [
763
+ /* @__PURE__ */ jsx7("button", { className: "btn primary mini", type: "button", disabled: !reason.trim(), onClick: () => void send("POST"), children: "Change portal tier" }),
764
+ changed ? /* @__PURE__ */ jsx7("button", { className: "btn secondary mini", type: "button", disabled: !reason.trim(), onClick: () => void send("DELETE"), children: "Revert to purchased tier" }) : null
765
+ ] }),
766
+ status ? /* @__PURE__ */ jsx7("p", { role: "status", children: status }) : null,
767
+ view.data.history.length ? /* @__PURE__ */ jsx7("ul", { className: "meeting-note", children: view.data.history.map((row) => /* @__PURE__ */ jsxs7("li", { children: [
768
+ new Date(Number(row.at)).toLocaleDateString("en-US"),
769
+ ": ",
770
+ String(row.fromTierId ?? "none"),
771
+ " \u2192 ",
772
+ String(row.toTierId ?? "purchased"),
773
+ " by ",
774
+ String(row.actor),
775
+ " (",
776
+ String(row.reason),
777
+ ")"
778
+ ] }, String(row.id))) }) : null
779
+ ] });
780
+ }
781
+
713
782
  // src/ui/admin-lifecycle-adapter.ts
714
783
  function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
715
784
  return {
@@ -739,18 +808,18 @@ function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.reco
739
808
  }
740
809
 
741
810
  // src/ui/admin-named-seat.tsx
742
- import { useState as useState6 } from "preact/hooks";
811
+ import { useState as useState7 } from "preact/hooks";
743
812
  import { Button as Button3 } from "@odla-ai/ui/components";
744
- import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
813
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
745
814
  function NamedSeatRecordPanel(props) {
746
- return props.superAdmin ? /* @__PURE__ */ jsx7(SeatPanel, { ...props }) : null;
815
+ return props.superAdmin ? /* @__PURE__ */ jsx8(SeatPanel, { ...props }) : null;
747
816
  }
748
817
  function SeatPanel(props) {
749
818
  const resource = useAdminResource(props.getToken, `/api/admin/applications/${encodeURIComponent(props.applicationId)}/named-seat`);
750
- const [busy, setBusy] = useState6(false);
751
- const [note, setNote] = useState6(null);
752
- const [renaming, setRenaming] = useState6(false), [name, setName] = useState6(""), [email, setEmail] = useState6("");
753
- if (resource.loading) return /* @__PURE__ */ jsx7("p", { role: "status", children: "Loading additional seat\u2026" });
819
+ const [busy, setBusy] = useState7(false);
820
+ const [note, setNote] = useState7(null);
821
+ const [renaming, setRenaming] = useState7(false), [name, setName] = useState7(""), [email, setEmail] = useState7("");
822
+ if (resource.loading) return /* @__PURE__ */ jsx8("p", { role: "status", children: "Loading additional seat\u2026" });
754
823
  if (resource.error || !resource.data) return null;
755
824
  const { data } = resource, seat = data.seat;
756
825
  const regift = async (event) => {
@@ -795,10 +864,10 @@ function SeatPanel(props) {
795
864
  setBusy(false);
796
865
  }
797
866
  };
798
- return /* @__PURE__ */ jsxs7("section", { className: "panel", "aria-label": "Additional seat", style: { display: "grid", gap: 8, marginTop: 16 }, children: [
799
- /* @__PURE__ */ jsx7("h3", { style: { margin: 0, fontSize: 16 }, children: "Additional seat" }),
800
- !seat ? /* @__PURE__ */ jsx7("p", { className: "muted", style: { margin: 0 }, children: "No additional seat has been purchased." }) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
801
- /* @__PURE__ */ jsxs7("p", { style: { margin: 0 }, children: [
867
+ return /* @__PURE__ */ jsxs8("section", { className: "panel", "aria-label": "Additional seat", style: { display: "grid", gap: 8, marginTop: 16 }, children: [
868
+ /* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: 16 }, children: "Additional seat" }),
869
+ !seat ? /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children: "No additional seat has been purchased." }) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
870
+ /* @__PURE__ */ jsxs8("p", { style: { margin: 0 }, children: [
802
871
  seat.recipientName,
803
872
  " \xB7 ",
804
873
  seat.recipientEmail,
@@ -806,41 +875,41 @@ function SeatPanel(props) {
806
875
  (seat.recipientState ?? seat.status).replaceAll("_", " "),
807
876
  seat.invitationHeld ? " (invitation waits for the purchaser's approval)" : ""
808
877
  ] }),
809
- /* @__PURE__ */ jsxs7("p", { className: "muted", style: { margin: 0 }, children: [
878
+ /* @__PURE__ */ jsxs8("p", { className: "muted", style: { margin: 0 }, children: [
810
879
  data.amountCents === 0 ? "Included with the membership; nothing was charged." : `${money6} paid in full.`,
811
880
  " The seat waits for its recipient and is never refunded automatically."
812
881
  ] }),
813
- data.refundable || data.regiftable ? /* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 8 }, children: [
814
- data.refundable ? /* @__PURE__ */ jsx7(Button3, { variant: "danger", mini: true, disabled: busy, onClick: () => void refund(), children: busy ? "Refunding\u2026" : "Refund seat" }) : null,
815
- data.regiftable && !renaming ? /* @__PURE__ */ jsx7(Button3, { variant: "secondary", mini: true, disabled: busy, onClick: () => {
882
+ data.refundable || data.regiftable ? /* @__PURE__ */ jsxs8("div", { style: { display: "flex", gap: 8 }, children: [
883
+ data.refundable ? /* @__PURE__ */ jsx8(Button3, { variant: "danger", mini: true, disabled: busy, onClick: () => void refund(), children: busy ? "Refunding\u2026" : "Refund seat" }) : null,
884
+ data.regiftable && !renaming ? /* @__PURE__ */ jsx8(Button3, { variant: "secondary", mini: true, disabled: busy, onClick: () => {
816
885
  setRenaming(true);
817
886
  setName("");
818
887
  setEmail("");
819
888
  }, children: "Name someone else" }) : null
820
889
  ] }) : null,
821
- renaming ? /* @__PURE__ */ jsxs7("form", { "aria-label": "Name a different recipient", onSubmit: (event) => void regift(event), style: { display: "grid", gap: 8 }, children: [
822
- /* @__PURE__ */ jsxs7("label", { children: [
890
+ renaming ? /* @__PURE__ */ jsxs8("form", { "aria-label": "Name a different recipient", onSubmit: (event) => void regift(event), style: { display: "grid", gap: 8 }, children: [
891
+ /* @__PURE__ */ jsxs8("label", { children: [
823
892
  "Recipient name",
824
- /* @__PURE__ */ jsx7("input", { required: true, maxLength: 160, value: name, onInput: (event) => setName(event.currentTarget.value) })
893
+ /* @__PURE__ */ jsx8("input", { required: true, maxLength: 160, value: name, onInput: (event) => setName(event.currentTarget.value) })
825
894
  ] }),
826
- /* @__PURE__ */ jsxs7("label", { children: [
895
+ /* @__PURE__ */ jsxs8("label", { children: [
827
896
  "Recipient email",
828
- /* @__PURE__ */ jsx7("input", { required: true, type: "email", maxLength: 254, value: email, onInput: (event) => setEmail(event.currentTarget.value) })
897
+ /* @__PURE__ */ jsx8("input", { required: true, type: "email", maxLength: 254, value: email, onInput: (event) => setEmail(event.currentTarget.value) })
829
898
  ] }),
830
- /* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 8 }, children: [
831
- /* @__PURE__ */ jsx7(Button3, { mini: true, type: "submit", disabled: busy, children: busy ? "Changing\u2026" : "Give the seat to this person" }),
832
- /* @__PURE__ */ jsx7(Button3, { variant: "secondary", mini: true, type: "button", disabled: busy, onClick: () => setRenaming(false), children: "Cancel" })
899
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", gap: 8 }, children: [
900
+ /* @__PURE__ */ jsx8(Button3, { mini: true, type: "submit", disabled: busy, children: busy ? "Changing\u2026" : "Give the seat to this person" }),
901
+ /* @__PURE__ */ jsx8(Button3, { variant: "secondary", mini: true, type: "button", disabled: busy, onClick: () => setRenaming(false), children: "Cancel" })
833
902
  ] })
834
903
  ] }) : null
835
904
  ] }),
836
- note ? /* @__PURE__ */ jsx7("p", { className: "muted", role: "status", style: { margin: 0 }, children: note }) : null
905
+ note ? /* @__PURE__ */ jsx8("p", { className: "muted", role: "status", style: { margin: 0 }, children: note }) : null
837
906
  ] });
838
907
  }
839
908
 
840
909
  // src/ui/admin-candidate-stage.tsx
841
- import { useState as useState7 } from "preact/hooks";
842
- import { Button as Button4, Field, Select } from "@odla-ai/ui/components";
843
- import { Fragment as Fragment6, jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
910
+ import { useState as useState8 } from "preact/hooks";
911
+ import { Button as Button4, Field, Select as Select2 } from "@odla-ai/ui/components";
912
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
844
913
  var APPLICATION_STAGES = [
845
914
  "submitted",
846
915
  "paid_pending_vetting",
@@ -851,20 +920,20 @@ var APPLICATION_STAGES = [
851
920
  "refunded"
852
921
  ];
853
922
  function CandidateStagePanel(props) {
854
- const [selected, setSelected] = useState7("");
855
- const [busy, setBusy] = useState7(false);
856
- const [error, setError] = useState7(null);
923
+ const [selected, setSelected] = useState8("");
924
+ const [busy, setBusy] = useState8(false);
925
+ const [error, setError] = useState8(null);
857
926
  const options = props.stages.filter((stage) => !props.applicationStages.includes(stage.id) && stage.id !== props.current);
858
927
  const label = (id) => props.stages.find((stage) => stage.id === id)?.label ?? id.replaceAll("_", " ");
859
928
  if (!options.length && props.current == null) {
860
- return /* @__PURE__ */ jsx8("p", { children: "Journey stage: Not applicable. No membership application is linked. Administrative access is separate." });
929
+ return /* @__PURE__ */ jsx9("p", { children: "Journey stage: Not applicable. No membership application is linked. Administrative access is separate." });
861
930
  }
862
931
  const target = options.some((stage) => stage.id === selected) ? selected : "";
863
- return /* @__PURE__ */ jsxs8(Field, { label: "Stage", error, children: [
864
- /* @__PURE__ */ jsx8("p", { children: props.current == null ? "No stage yet. Membership statuses begin when this person applies." : label(props.current) }),
865
- options.length ? /* @__PURE__ */ jsxs8(Fragment6, { children: [
866
- /* @__PURE__ */ jsx8(
867
- Select,
932
+ return /* @__PURE__ */ jsxs9(Field, { label: "Stage", error, children: [
933
+ /* @__PURE__ */ jsx9("p", { children: props.current == null ? "No stage yet. Membership statuses begin when this person applies." : label(props.current) }),
934
+ options.length ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
935
+ /* @__PURE__ */ jsx9(
936
+ Select2,
868
937
  {
869
938
  "aria-label": "Move to stage",
870
939
  value: target,
@@ -873,7 +942,7 @@ function CandidateStagePanel(props) {
873
942
  options: [{ value: "", label: "Move to\u2026" }, ...options.map((stage) => ({ value: stage.id, label: label(stage.id) }))]
874
943
  }
875
944
  ),
876
- /* @__PURE__ */ jsx8(Button4, { mini: true, disabled: busy || !target, onClick: async () => {
945
+ /* @__PURE__ */ jsx9(Button4, { mini: true, disabled: busy || !target, onClick: async () => {
877
946
  setBusy(true);
878
947
  setError(null);
879
948
  try {
@@ -885,22 +954,22 @@ function CandidateStagePanel(props) {
885
954
  setBusy(false);
886
955
  }
887
956
  }, children: "Move" })
888
- ] }) : /* @__PURE__ */ jsx8("p", { children: "No pre-application stages are configured for this chapter\u2019s CRM." })
957
+ ] }) : /* @__PURE__ */ jsx9("p", { children: "No pre-application stages are configured for this chapter\u2019s CRM." })
889
958
  ] });
890
959
  }
891
960
 
892
961
  // src/ui/admin-record-admission.tsx
893
- import { useState as useState8 } from "preact/hooks";
894
- import { Button as Button5, Field as Field2, Select as Select2 } from "@odla-ai/ui/components";
895
- import { Fragment as Fragment7, jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
962
+ import { useState as useState9 } from "preact/hooks";
963
+ import { Button as Button5, Field as Field2, Select as Select3 } from "@odla-ai/ui/components";
964
+ import { Fragment as Fragment7, jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
896
965
  function RecordAdmissionPanel(props) {
897
966
  const copy = useChapterCopy().admin.admission;
898
967
  const resource = useAdminResource(props.getToken, "/api/admin/admission-grants");
899
- const [decision, setDecision] = useState8("waive_interview");
900
- const [note, setNote] = useState8("");
901
- const [busy, setBusy] = useState8(false);
902
- const [error, setError] = useState8(null);
903
- if (!props.email) return /* @__PURE__ */ jsx9("p", { className: "muted", children: "This record has no email yet. Add one before setting an invitation." });
968
+ const [decision, setDecision] = useState9("waive_interview");
969
+ const [note, setNote] = useState9("");
970
+ const [busy, setBusy] = useState9(false);
971
+ const [error, setError] = useState9(null);
972
+ if (!props.email) return /* @__PURE__ */ jsx10("p", { className: "muted", children: "This record has no email yet. Add one before setting an invitation." });
904
973
  const email = props.email.trim().toLowerCase();
905
974
  const grants = (resource.data?.grants ?? []).filter((grant) => grant.email.trim().toLowerCase() === email);
906
975
  const effective = grants.find((grant) => grant.active && !grant.consumedAt && !grant.revokedAt && (!grant.expiresAt || grant.expiresAt > Date.now()));
@@ -923,23 +992,23 @@ function RecordAdmissionPanel(props) {
923
992
  body: JSON.stringify({ email: props.email, decision, ...note.trim() ? { note: note.trim() } : {} })
924
993
  }));
925
994
  const revoke = (grant) => run(() => adminFetch(props.getToken, `/api/admin/admission-grants/${encodeURIComponent(grant.id)}`, { method: "DELETE" }));
926
- return /* @__PURE__ */ jsxs9("section", { "aria-label": "Invitation", style: { display: "grid", gap: 12 }, children: [
927
- resource.loading ? /* @__PURE__ */ jsx9("p", { role: "status", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: resource.error }) : effective ? /* @__PURE__ */ jsxs9("p", { style: { margin: 0 }, "data-testid": "effective-invitation", children: [
928
- /* @__PURE__ */ jsx9("strong", { children: effective.decision === "waive_interview" ? copy.waive : copy.require }),
995
+ return /* @__PURE__ */ jsxs10("section", { "aria-label": "Invitation", style: { display: "grid", gap: 12 }, children: [
996
+ resource.loading ? /* @__PURE__ */ jsx10("p", { role: "status", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: resource.error }) : effective ? /* @__PURE__ */ jsxs10("p", { style: { margin: 0 }, "data-testid": "effective-invitation", children: [
997
+ /* @__PURE__ */ jsx10("strong", { children: effective.decision === "waive_interview" ? copy.waive : copy.require }),
929
998
  " \xB7 ",
930
999
  effective.sourceScope === "local" ? copy.local : copy.parent,
931
1000
  effective.note ? ` \xB7 ${effective.note}` : "",
932
- effective.sourceScope === "local" ? /* @__PURE__ */ jsxs9(Fragment7, { children: [
1001
+ effective.sourceScope === "local" ? /* @__PURE__ */ jsxs10(Fragment7, { children: [
933
1002
  " ",
934
- /* @__PURE__ */ jsx9(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void revoke(effective), children: copy.revoke })
1003
+ /* @__PURE__ */ jsx10(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void revoke(effective), children: copy.revoke })
935
1004
  ] }) : null
936
- ] }) : /* @__PURE__ */ jsxs9("p", { className: "muted", style: { margin: 0 }, children: [
1005
+ ] }) : /* @__PURE__ */ jsxs10("p", { className: "muted", style: { margin: 0 }, children: [
937
1006
  "No invitation setting for ",
938
1007
  email,
939
1008
  ". The next signup follows the chapter\u2019s ordinary interview rule."
940
1009
  ] }),
941
- /* @__PURE__ */ jsx9(Field2, { label: copy.decision, children: /* @__PURE__ */ jsx9(
942
- Select2,
1010
+ /* @__PURE__ */ jsx10(Field2, { label: copy.decision, children: /* @__PURE__ */ jsx10(
1011
+ Select3,
943
1012
  {
944
1013
  value: decision,
945
1014
  disabled: busy,
@@ -947,22 +1016,22 @@ function RecordAdmissionPanel(props) {
947
1016
  options: [{ value: "waive_interview", label: copy.waive }, { value: "require_interview", label: copy.require }]
948
1017
  }
949
1018
  ) }),
950
- /* @__PURE__ */ jsx9(Field2, { label: copy.note, children: /* @__PURE__ */ jsx9("input", { value: note, maxLength: 2e3, disabled: busy, onInput: (event) => setNote(event.currentTarget.value) }) }),
951
- /* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsx9(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void save(), children: copy.add }) }),
952
- error ? /* @__PURE__ */ jsx9("p", { role: "alert", children: error }) : null
1019
+ /* @__PURE__ */ jsx10(Field2, { label: copy.note, children: /* @__PURE__ */ jsx10("input", { value: note, maxLength: 2e3, disabled: busy, onInput: (event) => setNote(event.currentTarget.value) }) }),
1020
+ /* @__PURE__ */ jsx10("div", { children: /* @__PURE__ */ jsx10(Button5, { variant: "secondary", mini: true, disabled: busy, onClick: () => void save(), children: copy.add }) }),
1021
+ error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: error }) : null
953
1022
  ] });
954
1023
  }
955
1024
 
956
1025
  // src/ui/admin-application-repair.tsx
957
- import { useState as useState9 } from "preact/hooks";
1026
+ import { useState as useState10 } from "preact/hooks";
958
1027
  import { Button as Button6 } from "@odla-ai/ui/components";
959
- import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
1028
+ import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
960
1029
  function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
961
- const [busy, setBusy] = useState9(false);
962
- const [error, setError] = useState9(null);
963
- return /* @__PURE__ */ jsxs10("section", { className: "panel", children: [
964
- /* @__PURE__ */ jsx10("p", { children: "This person is missing an application link. Repair checks for one matching chapter application; it does not change membership or profile details." }),
965
- /* @__PURE__ */ jsx10(Button6, { variant: "secondary", disabled: busy, onClick: async () => {
1030
+ const [busy, setBusy] = useState10(false);
1031
+ const [error, setError] = useState10(null);
1032
+ return /* @__PURE__ */ jsxs11("section", { className: "panel", children: [
1033
+ /* @__PURE__ */ jsx11("p", { children: "This person is missing an application link. Repair checks for one matching chapter application; it does not change membership or profile details." }),
1034
+ /* @__PURE__ */ jsx11(Button6, { variant: "secondary", disabled: busy, onClick: async () => {
966
1035
  setBusy(true);
967
1036
  setError(null);
968
1037
  try {
@@ -974,34 +1043,34 @@ function ApplicationLinkRepair({ recordId, getToken, onChanged }) {
974
1043
  setBusy(false);
975
1044
  }
976
1045
  }, children: busy ? "Checking application link\u2026" : "Repair application link" }),
977
- error ? /* @__PURE__ */ jsx10("p", { role: "alert", children: error }) : null
1046
+ error ? /* @__PURE__ */ jsx11("p", { role: "alert", children: error }) : null
978
1047
  ] });
979
1048
  }
980
1049
 
981
1050
  // src/ui/admin-application-stage.tsx
982
- import { useState as useState10 } from "preact/hooks";
983
- import { Button as Button7, Field as Field3, Select as Select3 } from "@odla-ai/ui/components";
984
- import { Fragment as Fragment8, jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
1051
+ import { useState as useState11 } from "preact/hooks";
1052
+ import { Button as Button7, Field as Field3, Select as Select4 } from "@odla-ai/ui/components";
1053
+ import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
985
1054
  function ApplicationStagePanel(props) {
986
1055
  const actions = useAdminResource(
987
1056
  props.getToken,
988
1057
  `/api/admin/applications/${encodeURIComponent(props.applicationId)}/actions`
989
1058
  );
990
- const [selected, setSelected] = useState10("");
991
- const [busy, setBusy] = useState10(false);
992
- const [error, setError] = useState10(null);
993
- if (actions.loading) return /* @__PURE__ */ jsx11("p", { role: "status", children: "Loading available actions\u2026" });
994
- if (actions.error) return /* @__PURE__ */ jsxs11("p", { role: "alert", children: [
1059
+ const [selected, setSelected] = useState11("");
1060
+ const [busy, setBusy] = useState11(false);
1061
+ const [error, setError] = useState11(null);
1062
+ if (actions.loading) return /* @__PURE__ */ jsx12("p", { role: "status", children: "Loading available actions\u2026" });
1063
+ if (actions.error) return /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
995
1064
  "Could not load application actions: ",
996
1065
  actions.error
997
1066
  ] });
998
1067
  const allowed = actions.data?.allowedStages ?? [];
999
1068
  const target = allowed.includes(selected) ? selected : "";
1000
- return /* @__PURE__ */ jsxs11(Field3, { label: "Stage", error, children: [
1001
- /* @__PURE__ */ jsx11("p", { children: actions.data?.status?.replaceAll("_", " ") }),
1002
- allowed.length ? /* @__PURE__ */ jsxs11(Fragment8, { children: [
1003
- /* @__PURE__ */ jsx11(
1004
- Select3,
1069
+ return /* @__PURE__ */ jsxs12(Field3, { label: "Stage", error, children: [
1070
+ /* @__PURE__ */ jsx12("p", { children: actions.data?.status?.replaceAll("_", " ") }),
1071
+ allowed.length ? /* @__PURE__ */ jsxs12(Fragment8, { children: [
1072
+ /* @__PURE__ */ jsx12(
1073
+ Select4,
1005
1074
  {
1006
1075
  "aria-label": "Move to stage",
1007
1076
  value: target,
@@ -1010,7 +1079,7 @@ function ApplicationStagePanel(props) {
1010
1079
  options: [{ value: "", label: "Move to\u2026" }, ...allowed.map((value) => ({ value, label: value.replaceAll("_", " ") }))]
1011
1080
  }
1012
1081
  ),
1013
- /* @__PURE__ */ jsx11(Button7, { mini: true, disabled: busy || !target, onClick: async () => {
1082
+ /* @__PURE__ */ jsx12(Button7, { mini: true, disabled: busy || !target, onClick: async () => {
1014
1083
  setBusy(true);
1015
1084
  setError(null);
1016
1085
  try {
@@ -1023,19 +1092,19 @@ function ApplicationStagePanel(props) {
1023
1092
  actions.refresh();
1024
1093
  }
1025
1094
  }, children: "Move" })
1026
- ] }) : /* @__PURE__ */ jsx11("p", { children: "No stage changes are available under this chapter\u2019s current membership policy." })
1095
+ ] }) : /* @__PURE__ */ jsx12("p", { children: "No stage changes are available under this chapter\u2019s current membership policy." })
1027
1096
  ] });
1028
1097
  }
1029
1098
 
1030
1099
  // src/ui/operational-profile-form.tsx
1031
- import { Button as Button8, Field as Field4, Input, Select as Select4, Textarea } from "@odla-ai/ui/components";
1032
- import { useRef, useState as useState11 } from "preact/hooks";
1033
- import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
1100
+ import { Button as Button8, Field as Field4, Input, Select as Select5, Textarea } from "@odla-ai/ui/components";
1101
+ import { useRef, useState as useState12 } from "preact/hooks";
1102
+ import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
1034
1103
  var valueText = (value) => value == null ? "" : typeof value === "object" ? JSON.stringify(value) : String(value);
1035
1104
  function OperationalProfileForm(props) {
1036
- const [draft, setDraft] = useState11(() => Object.fromEntries(props.fields.map((field) => [field.id, valueText(props.values[field.id])])));
1037
- const [busy, setBusy] = useState11(false);
1038
- const [error, setError] = useState11(null);
1105
+ const [draft, setDraft] = useState12(() => Object.fromEntries(props.fields.map((field) => [field.id, valueText(props.values[field.id])])));
1106
+ const [busy, setBusy] = useState12(false);
1107
+ const [error, setError] = useState12(null);
1039
1108
  const pending = useRef(null);
1040
1109
  const changed = props.fields.filter((field) => draft[field.id] !== valueText(props.values[field.id]));
1041
1110
  const save = async () => {
@@ -1063,20 +1132,20 @@ function OperationalProfileForm(props) {
1063
1132
  setBusy(false);
1064
1133
  }
1065
1134
  };
1066
- return /* @__PURE__ */ jsxs12("section", { style: { display: "grid", gap: 16 }, children: [
1067
- props.fields.map((field) => /* @__PURE__ */ jsx12(Field4, { label: field.label, children: field.options || field.type === "boolean" ? /* @__PURE__ */ jsxs12(
1068
- Select4,
1135
+ return /* @__PURE__ */ jsxs13("section", { style: { display: "grid", gap: 16 }, children: [
1136
+ props.fields.map((field) => /* @__PURE__ */ jsx13(Field4, { label: field.label, children: field.options || field.type === "boolean" ? /* @__PURE__ */ jsxs13(
1137
+ Select5,
1069
1138
  {
1070
1139
  "aria-label": field.label,
1071
1140
  value: draft[field.id],
1072
1141
  disabled: busy,
1073
1142
  onChange: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value }),
1074
1143
  children: [
1075
- /* @__PURE__ */ jsx12("option", { value: "", children: "Not set" }),
1076
- (field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */ jsx12("option", { value: option, children: option }, option))
1144
+ /* @__PURE__ */ jsx13("option", { value: "", children: "Not set" }),
1145
+ (field.options ?? ["true", "false"]).map((option) => /* @__PURE__ */ jsx13("option", { value: option, children: option }, option))
1077
1146
  ]
1078
1147
  }
1079
- ) : field.type === "json" ? /* @__PURE__ */ jsx12(
1148
+ ) : field.type === "json" ? /* @__PURE__ */ jsx13(
1080
1149
  Textarea,
1081
1150
  {
1082
1151
  "aria-label": field.label,
@@ -1084,7 +1153,7 @@ function OperationalProfileForm(props) {
1084
1153
  disabled: busy,
1085
1154
  onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
1086
1155
  }
1087
- ) : /* @__PURE__ */ jsx12(
1156
+ ) : /* @__PURE__ */ jsx13(
1088
1157
  Input,
1089
1158
  {
1090
1159
  "aria-label": field.label,
@@ -1094,26 +1163,26 @@ function OperationalProfileForm(props) {
1094
1163
  onInput: (event) => setDraft({ ...draft, [field.id]: event.currentTarget.value })
1095
1164
  }
1096
1165
  ) }, field.id)),
1097
- error ? /* @__PURE__ */ jsx12("p", { role: "alert", children: error }) : null,
1098
- /* @__PURE__ */ jsx12("div", { children: /* @__PURE__ */ jsx12(Button8, { disabled: busy || !changed.length, onClick: () => void save(), children: busy ? "Saving\u2026" : "Save profile" }) })
1166
+ error ? /* @__PURE__ */ jsx13("p", { role: "alert", children: error }) : null,
1167
+ /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(Button8, { disabled: busy || !changed.length, onClick: () => void save(), children: busy ? "Saving\u2026" : "Save profile" }) })
1099
1168
  ] });
1100
1169
  }
1101
1170
 
1102
1171
  // src/ui/admin-person-profile.tsx
1103
- import { Fragment as Fragment9, jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
1172
+ import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1104
1173
  function ChapterPersonProfile(props) {
1105
1174
  const path = `/api/admin/people/profile?${new URLSearchParams({ recordId: props.recordId })}`;
1106
1175
  const resource = useAdminResource(props.getToken, path);
1107
- if (resource.loading) return /* @__PURE__ */ jsx13("p", { children: "Loading profile\u2026" });
1108
- if (resource.error) return /* @__PURE__ */ jsx13("p", { role: "alert", children: resource.error });
1176
+ if (resource.loading) return /* @__PURE__ */ jsx14("p", { children: "Loading profile\u2026" });
1177
+ if (resource.error) return /* @__PURE__ */ jsx14("p", { role: "alert", children: resource.error });
1109
1178
  const record = resource.data?.record;
1110
- if (!record?.edit) return /* @__PURE__ */ jsx13("p", { children: "Profile editing is unavailable." });
1111
- return /* @__PURE__ */ jsxs13(Fragment9, { children: [
1112
- /* @__PURE__ */ jsxs13("p", { children: [
1179
+ if (!record?.edit) return /* @__PURE__ */ jsx14("p", { children: "Profile editing is unavailable." });
1180
+ return /* @__PURE__ */ jsxs14(Fragment9, { children: [
1181
+ /* @__PURE__ */ jsxs14("p", { children: [
1113
1182
  String(record.fields.email ?? ""),
1114
1183
  " \u2014 login email changes use the verified account workflow."
1115
1184
  ] }),
1116
- /* @__PURE__ */ jsx13(
1185
+ /* @__PURE__ */ jsx14(
1117
1186
  OperationalProfileForm,
1118
1187
  {
1119
1188
  fields: record.edit.fields,
@@ -1130,14 +1199,14 @@ function ChapterPersonProfile(props) {
1130
1199
  }
1131
1200
 
1132
1201
  // src/ui/admin-account-sync.tsx
1133
- import { useState as useState12 } from "preact/hooks";
1202
+ import { useState as useState13 } from "preact/hooks";
1134
1203
  import { Button as Button9 } from "@odla-ai/ui/components";
1135
- import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
1204
+ import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1136
1205
  function AccountPeopleSync({ getToken, refresh }) {
1137
- const [busy, setBusy] = useState12(false);
1138
- const [message, setMessage] = useState12("");
1139
- return /* @__PURE__ */ jsxs14("div", { children: [
1140
- /* @__PURE__ */ jsx14(Button9, { mini: true, variant: "secondary", disabled: busy, onClick: async () => {
1206
+ const [busy, setBusy] = useState13(false);
1207
+ const [message, setMessage] = useState13("");
1208
+ return /* @__PURE__ */ jsxs15("div", { children: [
1209
+ /* @__PURE__ */ jsx15(Button9, { mini: true, variant: "secondary", disabled: busy, onClick: async () => {
1141
1210
  setBusy(true);
1142
1211
  setMessage("");
1143
1212
  let after = null, count = 0, errors = 0;
@@ -1161,22 +1230,22 @@ function AccountPeopleSync({ getToken, refresh }) {
1161
1230
  setBusy(false);
1162
1231
  }
1163
1232
  }, children: busy ? "Syncing accounts\u2026" : "Sync accounts" }),
1164
- message ? /* @__PURE__ */ jsx14("p", { role: "status", children: message }) : null
1233
+ message ? /* @__PURE__ */ jsx15("p", { role: "status", children: message }) : null
1165
1234
  ] });
1166
1235
  }
1167
1236
 
1168
1237
  // src/ui/admin-record-actions.tsx
1169
- import { useEffect as useEffect4, useState as useState13 } from "preact/hooks";
1170
- import { Button as Button10, Field as Field5, Select as Select5 } from "@odla-ai/ui/components";
1171
- import { jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
1238
+ import { useEffect as useEffect5, useState as useState14 } from "preact/hooks";
1239
+ import { Button as Button10, Field as Field5, Select as Select6 } from "@odla-ai/ui/components";
1240
+ import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1172
1241
  function RecordActions({ getToken, record, roles, onChanged }) {
1173
1242
  const copy = useChapterCopy().admin.records;
1174
1243
  const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
1175
1244
  const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
1176
- const [access, setAccess] = useState13(null);
1177
- const [busy, setBusy] = useState13(null);
1178
- const [note, setNote] = useState13(null);
1179
- useEffect4(() => {
1245
+ const [access, setAccess] = useState14(null);
1246
+ const [busy, setBusy] = useState14(null);
1247
+ const [note, setNote] = useState14(null);
1248
+ useEffect5(() => {
1180
1249
  if (!userId) {
1181
1250
  setAccess(null);
1182
1251
  return;
@@ -1205,35 +1274,35 @@ function RecordActions({ getToken, record, roles, onChanged }) {
1205
1274
  const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
1206
1275
  const decline = () => act("decline", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/decline`, { method: "POST" }), copy.declined);
1207
1276
  if (!userId && !applicationId) return null;
1208
- return /* @__PURE__ */ jsxs15("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
1209
- /* @__PURE__ */ jsx15("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
1210
- userId ? /* @__PURE__ */ jsx15(Field5, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx15(
1211
- Select5,
1277
+ return /* @__PURE__ */ jsxs16("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
1278
+ /* @__PURE__ */ jsx16("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
1279
+ userId ? /* @__PURE__ */ jsx16(Field5, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx16(
1280
+ Select6,
1212
1281
  {
1213
1282
  value: access?.role ?? "",
1214
1283
  disabled: busy === "role" || access?.superAdmin,
1215
1284
  options: roles.map((r) => ({ value: r, label: r })),
1216
1285
  onChange: (e) => void setRole(e.target.value)
1217
1286
  }
1218
- ) }) : /* @__PURE__ */ jsx15("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
1219
- applicationId ? /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: 8 }, children: [
1220
- /* @__PURE__ */ jsx15(Button10, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
1221
- /* @__PURE__ */ jsx15(Button10, { variant: "secondary", mini: true, disabled: busy === "decline", onClick: () => void decline(), children: copy.decline }),
1222
- /* @__PURE__ */ jsx15(Button10, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
1287
+ ) }) : /* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
1288
+ applicationId ? /* @__PURE__ */ jsxs16("div", { style: { display: "flex", gap: 8 }, children: [
1289
+ /* @__PURE__ */ jsx16(Button10, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
1290
+ /* @__PURE__ */ jsx16(Button10, { variant: "secondary", mini: true, disabled: busy === "decline", onClick: () => void decline(), children: copy.decline }),
1291
+ /* @__PURE__ */ jsx16(Button10, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
1223
1292
  ] }) : null,
1224
- note ? /* @__PURE__ */ jsx15("p", { className: "muted", style: { margin: 0 }, children: note }) : null
1293
+ note ? /* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: 0 }, children: note }) : null
1225
1294
  ] });
1226
1295
  }
1227
1296
 
1228
1297
  // src/ui/admin-network.tsx
1229
- import { useState as useState14 } from "preact/hooks";
1230
- import { jsx as jsx16, jsxs as jsxs16 } from "preact/jsx-runtime";
1298
+ import { useState as useState15 } from "preact/hooks";
1299
+ import { jsx as jsx17, jsxs as jsxs17 } from "preact/jsx-runtime";
1231
1300
  function NetworkShareActions(props) {
1232
1301
  const copy = useChapterCopy().admin.network;
1233
1302
  const { recordId, recordType, getToken } = props;
1234
1303
  const targets = useAdminResource(getToken, "/api/admin/network/targets");
1235
- const [busy, setBusy] = useState14(null);
1236
- const [message, setMessage] = useState14(null);
1304
+ const [busy, setBusy] = useState15(null);
1305
+ const [message, setMessage] = useState15(null);
1237
1306
  const compatible = (targets.data?.targets ?? []).filter((target) => target.types.includes(recordType));
1238
1307
  if (targets.loading || targets.error || compatible.length === 0) return null;
1239
1308
  const push = async (target) => {
@@ -1252,26 +1321,26 @@ function NetworkShareActions(props) {
1252
1321
  setBusy(null);
1253
1322
  }
1254
1323
  };
1255
- return /* @__PURE__ */ jsxs16("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
1256
- /* @__PURE__ */ jsxs16("div", { children: [
1257
- /* @__PURE__ */ jsx16("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
1258
- /* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
1324
+ return /* @__PURE__ */ jsxs17("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
1325
+ /* @__PURE__ */ jsxs17("div", { children: [
1326
+ /* @__PURE__ */ jsx17("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
1327
+ /* @__PURE__ */ jsx17("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
1259
1328
  ] }),
1260
- /* @__PURE__ */ jsx16("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx16("button", { className: "btn secondary mini", disabled: busy !== null, onClick: () => void push(target), children: busy === target.id ? copy.sharing : formatChapterCopy(copy.shareWith, { target: target.name }) }, target.id)) }),
1261
- message ? /* @__PURE__ */ jsx16("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
1329
+ /* @__PURE__ */ jsx17("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx17("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)) }),
1330
+ message ? /* @__PURE__ */ jsx17("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
1262
1331
  ] });
1263
1332
  }
1264
1333
 
1265
1334
  // src/ui/admin-record-activity.tsx
1266
- import { useCallback as useCallback3, useEffect as useEffect5, useState as useState15 } from "preact/hooks";
1335
+ import { useCallback as useCallback3, useEffect as useEffect6, useState as useState16 } from "preact/hooks";
1267
1336
  import { ActivityFeed } from "@odla-ai/crm/ui";
1268
1337
  import { parseCrmDiscussionReference as parseCrmDiscussionReference2 } from "@odla-ai/crm";
1269
- import { jsx as jsx17 } from "preact/jsx-runtime";
1338
+ import { jsx as jsx18 } from "preact/jsx-runtime";
1270
1339
  function RecordActivityPanel(props) {
1271
1340
  const { client, recordId } = props;
1272
- const [activities, setActivities] = useState15([]);
1273
- const [busy, setBusy] = useState15(false);
1274
- const [error, setError] = useState15(null);
1341
+ const [activities, setActivities] = useState16([]);
1342
+ const [busy, setBusy] = useState16(false);
1343
+ const [error, setError] = useState16(null);
1275
1344
  const reference = typeof window === "undefined" ? null : parseCrmDiscussionReference2(new URL(window.location.href));
1276
1345
  const focusedActivityId = reference?.kind === "crm:activity" && reference.recordId === recordId ? reference.activityId : void 0;
1277
1346
  const load = useCallback3(async () => {
@@ -1283,7 +1352,7 @@ function RecordActivityPanel(props) {
1283
1352
  setError(failure instanceof Error ? failure.message : String(failure));
1284
1353
  }
1285
1354
  }, [client, recordId]);
1286
- useEffect5(() => void load(), [load]);
1355
+ useEffect6(() => void load(), [load]);
1287
1356
  const run = async (action) => {
1288
1357
  setBusy(true);
1289
1358
  try {
@@ -1293,8 +1362,8 @@ function RecordActivityPanel(props) {
1293
1362
  setBusy(false);
1294
1363
  }
1295
1364
  };
1296
- if (error) return /* @__PURE__ */ jsx17("p", { role: "alert", className: "muted", children: error });
1297
- return /* @__PURE__ */ jsx17(
1365
+ if (error) return /* @__PURE__ */ jsx18("p", { role: "alert", className: "muted", children: error });
1366
+ return /* @__PURE__ */ jsx18(
1298
1367
  ActivityFeed,
1299
1368
  {
1300
1369
  activities,
@@ -1308,17 +1377,17 @@ function RecordActivityPanel(props) {
1308
1377
  }
1309
1378
 
1310
1379
  // src/ui/admin-record-comms.tsx
1311
- import { useCallback as useCallback4, useEffect as useEffect6, useState as useState16 } from "preact/hooks";
1312
- import { Button as Button11, Field as Field6, Select as Select6 } from "@odla-ai/ui/components";
1313
- import { jsx as jsx18, jsxs as jsxs17 } from "preact/jsx-runtime";
1380
+ import { useCallback as useCallback4, useEffect as useEffect7, useState as useState17 } from "preact/hooks";
1381
+ import { Button as Button11, Field as Field6, Select as Select7 } from "@odla-ai/ui/components";
1382
+ import { jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
1314
1383
  function RecordCommsPanel(props) {
1315
1384
  const copy = useChapterCopy().admin.records;
1316
1385
  const { client, recordId, getToken } = props;
1317
1386
  const config = useAdminResource(getToken, "/api/admin/group/email");
1318
1387
  const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
1319
- const [template, setTemplate] = useState16("");
1320
- const [busy, setBusy] = useState16(false);
1321
- const [note, setNote] = useState16(null);
1388
+ const [template, setTemplate] = useState17("");
1389
+ const [busy, setBusy] = useState17(false);
1390
+ const [note, setNote] = useState17(null);
1322
1391
  const send = async () => {
1323
1392
  if (!template) return;
1324
1393
  setBusy(true);
@@ -1332,11 +1401,11 @@ function RecordCommsPanel(props) {
1332
1401
  setBusy(false);
1333
1402
  }
1334
1403
  };
1335
- if (config.loading) return /* @__PURE__ */ jsx18("p", { className: "muted", children: copy.loadingTemplates });
1336
- if (config.error) return /* @__PURE__ */ jsx18("p", { role: "alert", className: "muted", children: config.error });
1337
- return /* @__PURE__ */ jsxs17("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
1338
- /* @__PURE__ */ jsx18(Field6, { label: copy.template, children: /* @__PURE__ */ jsx18(
1339
- Select6,
1404
+ if (config.loading) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.loadingTemplates });
1405
+ if (config.error) return /* @__PURE__ */ jsx19("p", { role: "alert", className: "muted", children: config.error });
1406
+ return /* @__PURE__ */ jsxs18("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
1407
+ /* @__PURE__ */ jsx19(Field6, { label: copy.template, children: /* @__PURE__ */ jsx19(
1408
+ Select7,
1340
1409
  {
1341
1410
  value: template,
1342
1411
  onChange: (event) => setTemplate(event.currentTarget.value),
@@ -1346,15 +1415,15 @@ function RecordCommsPanel(props) {
1346
1415
  ]
1347
1416
  }
1348
1417
  ) }),
1349
- /* @__PURE__ */ jsx18("div", { children: /* @__PURE__ */ jsx18(Button11, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
1350
- note ? /* @__PURE__ */ jsx18("p", { role: "status", className: "muted", children: note }) : null
1418
+ /* @__PURE__ */ jsx19("div", { children: /* @__PURE__ */ jsx19(Button11, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
1419
+ note ? /* @__PURE__ */ jsx19("p", { role: "status", className: "muted", children: note }) : null
1351
1420
  ] });
1352
1421
  }
1353
1422
  function RecordCommsHistory(props) {
1354
1423
  const copy = useChapterCopy().admin.records;
1355
1424
  const { client, recordId } = props;
1356
- const [rows, setRows] = useState16([]);
1357
- const [error, setError] = useState16(null);
1425
+ const [rows, setRows] = useState17([]);
1426
+ const [error, setError] = useState17(null);
1358
1427
  const load = useCallback4(async () => {
1359
1428
  try {
1360
1429
  const result = await client.emailLog({ recordId, limit: 100 });
@@ -1364,12 +1433,12 @@ function RecordCommsHistory(props) {
1364
1433
  setError(failure instanceof Error ? failure.message : String(failure));
1365
1434
  }
1366
1435
  }, [client, recordId]);
1367
- useEffect6(() => void load(), [load]);
1368
- if (error) return /* @__PURE__ */ jsx18("p", { role: "alert", className: "muted", children: error });
1369
- if (rows.length === 0) return /* @__PURE__ */ jsx18("p", { className: "muted", children: copy.noMessages });
1370
- return /* @__PURE__ */ jsx18("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs17("li", { className: "panel", children: [
1371
- /* @__PURE__ */ jsx18("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
1372
- /* @__PURE__ */ jsxs17("div", { className: "muted", children: [
1436
+ useEffect7(() => void load(), [load]);
1437
+ if (error) return /* @__PURE__ */ jsx19("p", { role: "alert", className: "muted", children: error });
1438
+ if (rows.length === 0) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.noMessages });
1439
+ return /* @__PURE__ */ jsx19("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs18("li", { className: "panel", children: [
1440
+ /* @__PURE__ */ jsx19("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
1441
+ /* @__PURE__ */ jsxs18("div", { className: "muted", children: [
1373
1442
  row["to"] ? String(row["to"]) : "",
1374
1443
  row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
1375
1444
  ] })
@@ -1377,9 +1446,9 @@ function RecordCommsHistory(props) {
1377
1446
  }
1378
1447
 
1379
1448
  // src/ui/admin-record-scheduling.tsx
1380
- import { useState as useState17 } from "preact/hooks";
1449
+ import { useState as useState18 } from "preact/hooks";
1381
1450
  import { Button as Button12 } from "@odla-ai/ui/components";
1382
- import { Fragment as Fragment10, jsx as jsx19, jsxs as jsxs18 } from "preact/jsx-runtime";
1451
+ import { Fragment as Fragment10, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
1383
1452
  function RecordSchedulingPanel(props) {
1384
1453
  const copy = useChapterCopy().admin.records;
1385
1454
  const { getToken, applicationId } = props;
@@ -1387,8 +1456,8 @@ function RecordSchedulingPanel(props) {
1387
1456
  getToken,
1388
1457
  "/api/admin/meetings?all=1"
1389
1458
  );
1390
- const [busy, setBusy] = useState17(null);
1391
- const [note, setNote] = useState17(null);
1459
+ const [busy, setBusy] = useState18(null);
1460
+ const [note, setNote] = useState18(null);
1392
1461
  const meetings = (resource.data?.meetings ?? []).filter((meeting) => meeting.applicationId === applicationId);
1393
1462
  const run = async (meeting, action) => {
1394
1463
  let body;
@@ -1415,33 +1484,33 @@ function RecordSchedulingPanel(props) {
1415
1484
  setBusy(null);
1416
1485
  }
1417
1486
  };
1418
- if (!applicationId) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.noApplication });
1419
- if (resource.loading) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.loadingMeetings });
1420
- if (resource.error) return /* @__PURE__ */ jsx19("p", { role: "alert", className: "muted", children: resource.error });
1421
- if (meetings.length === 0) return /* @__PURE__ */ jsx19("p", { className: "muted", children: copy.noMeetings });
1422
- return /* @__PURE__ */ jsxs18("div", { style: { display: "grid", gap: 12 }, children: [
1423
- meetings.map((meeting) => /* @__PURE__ */ jsxs18("section", { className: "panel", children: [
1424
- /* @__PURE__ */ jsx19("strong", { children: new Date(meeting.startAt).toLocaleString() }),
1425
- /* @__PURE__ */ jsxs18("p", { className: "muted", children: [
1487
+ if (!applicationId) return /* @__PURE__ */ jsx20("p", { className: "muted", children: copy.noApplication });
1488
+ if (resource.loading) return /* @__PURE__ */ jsx20("p", { className: "muted", children: copy.loadingMeetings });
1489
+ if (resource.error) return /* @__PURE__ */ jsx20("p", { role: "alert", className: "muted", children: resource.error });
1490
+ if (meetings.length === 0) return /* @__PURE__ */ jsx20("p", { className: "muted", children: copy.noMeetings });
1491
+ return /* @__PURE__ */ jsxs19("div", { style: { display: "grid", gap: 12 }, children: [
1492
+ meetings.map((meeting) => /* @__PURE__ */ jsxs19("section", { className: "panel", children: [
1493
+ /* @__PURE__ */ jsx20("strong", { children: new Date(meeting.startAt).toLocaleString() }),
1494
+ /* @__PURE__ */ jsxs19("p", { className: "muted", children: [
1426
1495
  meeting.status,
1427
1496
  " \xB7 ",
1428
1497
  resource.data?.timezone
1429
1498
  ] }),
1430
- /* @__PURE__ */ jsxs18("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
1431
- meeting.meetUrl ? /* @__PURE__ */ jsx19("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
1432
- meeting.htmlLink ? /* @__PURE__ */ jsx19("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
1433
- meeting.status === "scheduled" ? /* @__PURE__ */ jsxs18(Fragment10, { children: [
1434
- /* @__PURE__ */ jsx19(Button12, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
1435
- /* @__PURE__ */ jsx19(Button12, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
1499
+ /* @__PURE__ */ jsxs19("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
1500
+ meeting.meetUrl ? /* @__PURE__ */ jsx20("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
1501
+ meeting.htmlLink ? /* @__PURE__ */ jsx20("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
1502
+ meeting.status === "scheduled" ? /* @__PURE__ */ jsxs19(Fragment10, { children: [
1503
+ /* @__PURE__ */ jsx20(Button12, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
1504
+ /* @__PURE__ */ jsx20(Button12, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
1436
1505
  ] }) : null
1437
1506
  ] })
1438
1507
  ] }, meeting.id)),
1439
- note ? /* @__PURE__ */ jsx19("p", { role: "status", className: "muted", children: note }) : null
1508
+ note ? /* @__PURE__ */ jsx20("p", { role: "status", className: "muted", children: note }) : null
1440
1509
  ] });
1441
1510
  }
1442
1511
 
1443
1512
  // src/ui/admin-people-workspace.tsx
1444
- import { Fragment as Fragment11, jsx as jsx20, jsxs as jsxs19 } from "preact/jsx-runtime";
1513
+ import { Fragment as Fragment11, jsx as jsx21, jsxs as jsxs20 } from "preact/jsx-runtime";
1445
1514
  function PeopleBody(props) {
1446
1515
  const copy = useChapterCopy().admin.records;
1447
1516
  const {
@@ -1470,7 +1539,7 @@ function PeopleBody(props) {
1470
1539
  const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
1471
1540
  const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
1472
1541
  const workspaceClient = client;
1473
- return /* @__PURE__ */ jsx20(AdminPage, { children: /* @__PURE__ */ jsx20(
1542
+ return /* @__PURE__ */ jsx21(AdminPage, { children: /* @__PURE__ */ jsx21(
1474
1543
  CrmWorkspace,
1475
1544
  {
1476
1545
  crm,
@@ -1486,9 +1555,9 @@ function PeopleBody(props) {
1486
1555
  collapseClosedDetail,
1487
1556
  lifecycle: resolvedLifecycle,
1488
1557
  requireLifecycleAdapter: requireLifecycleAdapter ?? lifecycle,
1489
- renderSummary: (context) => /* @__PURE__ */ jsxs19(Fragment11, { children: [
1558
+ renderSummary: (context) => /* @__PURE__ */ jsxs20(Fragment11, { children: [
1490
1559
  renderSummary?.(context, ctx),
1491
- type === "person" ? /* @__PURE__ */ jsx20(AccountPeopleSync, { getToken: ctx.getToken, refresh: context.query.refresh }) : null
1560
+ type === "person" ? /* @__PURE__ */ jsx21(AccountPeopleSync, { getToken: ctx.getToken, refresh: context.query.refresh }) : null
1492
1561
  ] }),
1493
1562
  renderMaster: renderMaster ? (context) => renderMaster(context, ctx) : void 0,
1494
1563
  renderDetailHeader: renderDetailHeader ? (context) => renderDetailHeader(context, ctx) : void 0,
@@ -1502,7 +1571,7 @@ function PeopleBody(props) {
1502
1571
  const add = (id) => {
1503
1572
  const tab = byId.get(id);
1504
1573
  if (tab && lifecycle && type === "person" && id === "profile" && !lifecycleAdapter) {
1505
- tabs.push({ ...tab, panel: /* @__PURE__ */ jsx20(
1574
+ tabs.push({ ...tab, panel: /* @__PURE__ */ jsx21(
1506
1575
  ChapterPersonProfile,
1507
1576
  {
1508
1577
  recordId: record.id,
@@ -1517,7 +1586,7 @@ function PeopleBody(props) {
1517
1586
  return;
1518
1587
  }
1519
1588
  if (tab && lifecycle && applicationId && id === "stage" && !lifecycleAdapter) {
1520
- tabs.push({ ...tab, panel: /* @__PURE__ */ jsx20(
1589
+ tabs.push({ ...tab, panel: /* @__PURE__ */ jsx21(
1521
1590
  ApplicationStagePanel,
1522
1591
  {
1523
1592
  applicationId,
@@ -1530,7 +1599,7 @@ function PeopleBody(props) {
1530
1599
  }
1531
1600
  if (tab) tabs.push(lifecycle && !applicationId && id === "stage" ? {
1532
1601
  ...tab,
1533
- panel: record.stage == null || !applicationStages.includes(record.stage) ? /* @__PURE__ */ jsx20(
1602
+ panel: record.stage == null || !applicationStages.includes(record.stage) ? /* @__PURE__ */ jsx21(
1534
1603
  CandidateStagePanel,
1535
1604
  {
1536
1605
  stages: def.pipeline?.stages ?? [],
@@ -1539,8 +1608,8 @@ function PeopleBody(props) {
1539
1608
  onMove: context.moveStage
1540
1609
  },
1541
1610
  record.id + record.updatedAt
1542
- ) : /* @__PURE__ */ jsxs19(Fragment11, { children: [
1543
- /* @__PURE__ */ jsx20(ApplicationLinkRepair, { recordId: record.id, getToken: ctx.getToken, onChanged: () => {
1611
+ ) : /* @__PURE__ */ jsxs20(Fragment11, { children: [
1612
+ /* @__PURE__ */ jsx21(ApplicationLinkRepair, { recordId: record.id, getToken: ctx.getToken, onChanged: () => {
1544
1613
  context.refreshRecord();
1545
1614
  context.refreshList();
1546
1615
  } }, record.id),
@@ -1554,21 +1623,21 @@ function PeopleBody(props) {
1554
1623
  tabs.push({
1555
1624
  id: "comms",
1556
1625
  label: copy.comms,
1557
- panel: /* @__PURE__ */ jsx20(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
1626
+ panel: /* @__PURE__ */ jsx21(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
1558
1627
  });
1559
1628
  tabs.push({
1560
1629
  id: "history",
1561
1630
  label: copy.commsHistory,
1562
- panel: /* @__PURE__ */ jsx20(RecordCommsHistory, { client, recordId: record.id })
1631
+ panel: /* @__PURE__ */ jsx21(RecordCommsHistory, { client, recordId: record.id })
1563
1632
  });
1564
1633
  }
1565
1634
  if (lifecycle) {
1566
1635
  tabs.push({
1567
1636
  id: "scheduling",
1568
1637
  label: copy.scheduling,
1569
- panel: /* @__PURE__ */ jsx20(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
1638
+ panel: /* @__PURE__ */ jsx21(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
1570
1639
  });
1571
- tabs.push({ id: "invitation", label: copy.invitation, panel: /* @__PURE__ */ jsx20(
1640
+ tabs.push({ id: "invitation", label: copy.invitation, panel: /* @__PURE__ */ jsx21(
1572
1641
  RecordAdmissionPanel,
1573
1642
  {
1574
1643
  getToken: ctx.getToken,
@@ -1579,9 +1648,10 @@ function PeopleBody(props) {
1579
1648
  }
1580
1649
  }
1581
1650
  ) });
1582
- tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsxs19(Fragment11, { children: [
1583
- /* @__PURE__ */ jsx20(BillingCard, { record }),
1584
- applicationId ? /* @__PURE__ */ jsx20(
1651
+ tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsxs20(Fragment11, { children: [
1652
+ /* @__PURE__ */ jsx21(BillingCard, { record }),
1653
+ applicationId ? /* @__PURE__ */ jsx21(MemberTierCard, { getToken: ctx.getToken, applicationId }) : null,
1654
+ applicationId ? /* @__PURE__ */ jsx21(
1585
1655
  NamedSeatRecordPanel,
1586
1656
  {
1587
1657
  getToken: ctx.getToken,
@@ -1600,14 +1670,14 @@ function PeopleBody(props) {
1600
1670
  tabs.push({
1601
1671
  id: "notes",
1602
1672
  label: copy.notes,
1603
- panel: /* @__PURE__ */ jsx20(RecordActivityPanel, { client, recordId: record.id })
1673
+ panel: /* @__PURE__ */ jsx21(RecordActivityPanel, { client, recordId: record.id })
1604
1674
  });
1605
1675
  add("connections");
1606
1676
  if (lifecycle) {
1607
1677
  tabs.push({
1608
1678
  id: "access",
1609
1679
  label: copy.access,
1610
- panel: /* @__PURE__ */ jsx20(
1680
+ panel: /* @__PURE__ */ jsx21(
1611
1681
  RecordActions,
1612
1682
  {
1613
1683
  getToken: ctx.getToken,
@@ -1626,7 +1696,7 @@ function PeopleBody(props) {
1626
1696
  tabs.push({
1627
1697
  id: "sharing",
1628
1698
  label: copy.sharing,
1629
- panel: /* @__PURE__ */ jsx20(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
1699
+ panel: /* @__PURE__ */ jsx21(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
1630
1700
  });
1631
1701
  }
1632
1702
  return extendRecordTabs?.(tabs, context) ?? tabs;
@@ -1636,7 +1706,7 @@ function PeopleBody(props) {
1636
1706
  }
1637
1707
 
1638
1708
  // src/ui/admin-people.tsx
1639
- import { jsx as jsx21 } from "preact/jsx-runtime";
1709
+ import { jsx as jsx22 } from "preact/jsx-runtime";
1640
1710
  var DEFAULT_ROLES = ["provisional", "member", "admin"];
1641
1711
  function collectionSection(options) {
1642
1712
  const { crm, type } = options;
@@ -1667,7 +1737,7 @@ function collectionSection(options) {
1667
1737
  return {
1668
1738
  id,
1669
1739
  label,
1670
- render: (ctx) => /* @__PURE__ */ jsx21(
1740
+ render: (ctx) => /* @__PURE__ */ jsx22(
1671
1741
  PeopleBody,
1672
1742
  {
1673
1743
  crm,
@@ -1740,14 +1810,14 @@ import {
1740
1810
  Field as Field8,
1741
1811
  MasterDetail,
1742
1812
  Pagination,
1743
- Select as Select7
1813
+ Select as Select8
1744
1814
  } from "@odla-ai/ui/components";
1745
- import { useEffect as useEffect7, useMemo, useState as useState19 } from "preact/hooks";
1815
+ import { useEffect as useEffect8, useMemo, useState as useState20 } from "preact/hooks";
1746
1816
 
1747
1817
  // src/ui/admin-network-notes.tsx
1748
1818
  import { Button as Button13, Field as Field7, Textarea as Textarea2 } from "@odla-ai/ui/components";
1749
- import { useState as useState18 } from "preact/hooks";
1750
- import { jsx as jsx22, jsxs as jsxs20 } from "preact/jsx-runtime";
1819
+ import { useState as useState19 } from "preact/hooks";
1820
+ import { jsx as jsx23, jsxs as jsxs21 } from "preact/jsx-runtime";
1751
1821
  function displayValue(value) {
1752
1822
  if (Array.isArray(value)) return value.join(", ");
1753
1823
  if (value && typeof value === "object") return JSON.stringify(value);
@@ -1762,9 +1832,9 @@ function PrivateNotes(props) {
1762
1832
  });
1763
1833
  const path = `/api/admin/network/notes?${query}`;
1764
1834
  const notes = useAdminResource(ctx.getToken, path);
1765
- const [body, setBody] = useState18("");
1766
- const [busy, setBusy] = useState18(false);
1767
- const [error, setError] = useState18(null);
1835
+ const [body, setBody] = useState19("");
1836
+ const [busy, setBusy] = useState19(false);
1837
+ const [error, setError] = useState19(null);
1768
1838
  const submit = async () => {
1769
1839
  const trimmed = body.trim();
1770
1840
  if (!trimmed) return;
@@ -1783,18 +1853,18 @@ function PrivateNotes(props) {
1783
1853
  setBusy(false);
1784
1854
  }
1785
1855
  };
1786
- return /* @__PURE__ */ jsxs20("section", { style: { display: "grid", gap: 12 }, children: [
1787
- /* @__PURE__ */ jsxs20("h3", { style: { margin: "8px 0 0" }, children: [
1856
+ return /* @__PURE__ */ jsxs21("section", { style: { display: "grid", gap: 12 }, children: [
1857
+ /* @__PURE__ */ jsxs21("h3", { style: { margin: "8px 0 0" }, children: [
1788
1858
  "Private to ",
1789
1859
  leaderName
1790
1860
  ] }),
1791
- /* @__PURE__ */ jsx22(
1861
+ /* @__PURE__ */ jsx23(
1792
1862
  Field7,
1793
1863
  {
1794
1864
  label: "Add a note",
1795
1865
  hint: `Visible only to ${leaderName} admins. Nothing is written back to the follower.`,
1796
1866
  error,
1797
- children: /* @__PURE__ */ jsx22(
1867
+ children: /* @__PURE__ */ jsx23(
1798
1868
  Textarea2,
1799
1869
  {
1800
1870
  rows: 4,
@@ -1805,13 +1875,13 @@ function PrivateNotes(props) {
1805
1875
  )
1806
1876
  }
1807
1877
  ),
1808
- /* @__PURE__ */ jsx22("div", { children: /* @__PURE__ */ jsx22(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save note" }) }),
1809
- notes.loading ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "Loading notes\u2026" }) : null,
1810
- notes.error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: notes.error }) : null,
1811
- !notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "No notes yet." }) : null,
1812
- (notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs20("article", { className: "panel", style: { padding: 14 }, children: [
1813
- /* @__PURE__ */ jsx22("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
1814
- /* @__PURE__ */ jsxs20("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
1878
+ /* @__PURE__ */ jsx23("div", { children: /* @__PURE__ */ jsx23(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save note" }) }),
1879
+ notes.loading ? /* @__PURE__ */ jsx23("p", { className: "muted", children: "Loading notes\u2026" }) : null,
1880
+ notes.error ? /* @__PURE__ */ jsx23("p", { role: "alert", children: notes.error }) : null,
1881
+ !notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx23("p", { className: "muted", children: "No notes yet." }) : null,
1882
+ (notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs21("article", { className: "panel", style: { padding: 14 }, children: [
1883
+ /* @__PURE__ */ jsx23("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
1884
+ /* @__PURE__ */ jsxs21("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
1815
1885
  note.authorEmail ?? note.authorId,
1816
1886
  " \xB7 ",
1817
1887
  new Date(note.createdAt).toLocaleString()
@@ -1828,9 +1898,9 @@ function SharedNotes(props) {
1828
1898
  });
1829
1899
  const path = `/api/admin/network/shared-notes?${query}`;
1830
1900
  const notes = useAdminResource(ctx.getToken, path);
1831
- const [body, setBody] = useState18("");
1832
- const [busy, setBusy] = useState18(false);
1833
- const [error, setError] = useState18(null);
1901
+ const [body, setBody] = useState19("");
1902
+ const [busy, setBusy] = useState19(false);
1903
+ const [error, setError] = useState19(null);
1834
1904
  const submit = async () => {
1835
1905
  const trimmed = body.trim();
1836
1906
  if (!trimmed) return;
@@ -1849,18 +1919,18 @@ function SharedNotes(props) {
1849
1919
  setBusy(false);
1850
1920
  }
1851
1921
  };
1852
- return /* @__PURE__ */ jsxs20("section", { style: { display: "grid", gap: 12 }, children: [
1853
- /* @__PURE__ */ jsxs20("h3", { style: { margin: "8px 0 0" }, children: [
1922
+ return /* @__PURE__ */ jsxs21("section", { style: { display: "grid", gap: 12 }, children: [
1923
+ /* @__PURE__ */ jsxs21("h3", { style: { margin: "8px 0 0" }, children: [
1854
1924
  "Shared with ",
1855
1925
  targetName
1856
1926
  ] }),
1857
- /* @__PURE__ */ jsx22(
1927
+ /* @__PURE__ */ jsx23(
1858
1928
  Field7,
1859
1929
  {
1860
1930
  label: "Add a shared note",
1861
1931
  hint: `This is written to the record's Notes feed for ${targetName} admins.`,
1862
1932
  error,
1863
- children: /* @__PURE__ */ jsx22(
1933
+ children: /* @__PURE__ */ jsx23(
1864
1934
  Textarea2,
1865
1935
  {
1866
1936
  rows: 4,
@@ -1871,13 +1941,13 @@ function SharedNotes(props) {
1871
1941
  )
1872
1942
  }
1873
1943
  ),
1874
- /* @__PURE__ */ jsx22("div", { children: /* @__PURE__ */ jsx22(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Sharing\u2026" : `Share with ${targetName}` }) }),
1875
- notes.loading ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "Loading shared notes\u2026" }) : null,
1876
- notes.error ? /* @__PURE__ */ jsx22("p", { role: "alert", children: notes.error }) : null,
1877
- !notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx22("p", { className: "muted", children: "No shared notes yet." }) : null,
1878
- (notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs20("article", { className: "panel", style: { padding: 14 }, children: [
1879
- /* @__PURE__ */ jsx22("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
1880
- /* @__PURE__ */ jsxs20("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
1944
+ /* @__PURE__ */ jsx23("div", { children: /* @__PURE__ */ jsx23(Button13, { disabled: busy || !body.trim(), onClick: () => void submit(), children: busy ? "Sharing\u2026" : `Share with ${targetName}` }) }),
1945
+ notes.loading ? /* @__PURE__ */ jsx23("p", { className: "muted", children: "Loading shared notes\u2026" }) : null,
1946
+ notes.error ? /* @__PURE__ */ jsx23("p", { role: "alert", children: notes.error }) : null,
1947
+ !notes.loading && !notes.error && (notes.data?.notes.length ?? 0) === 0 ? /* @__PURE__ */ jsx23("p", { className: "muted", children: "No shared notes yet." }) : null,
1948
+ (notes.data?.notes ?? []).map((note) => /* @__PURE__ */ jsxs21("article", { className: "panel", style: { padding: 14 }, children: [
1949
+ /* @__PURE__ */ jsx23("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: note.body }),
1950
+ /* @__PURE__ */ jsxs21("p", { className: "muted", style: { margin: "8px 0 0", fontSize: 12 }, children: [
1881
1951
  "Shared with ",
1882
1952
  targetName,
1883
1953
  " \xB7 ",
@@ -1888,23 +1958,23 @@ function SharedNotes(props) {
1888
1958
  }
1889
1959
  function NetworkRecordDetail(props) {
1890
1960
  const { ctx, targetId, targetName, leaderName, sharedEnabled, record } = props;
1891
- return /* @__PURE__ */ jsxs20("div", { style: { display: "grid", gap: 14 }, children: [
1892
- /* @__PURE__ */ jsxs20("header", { children: [
1893
- /* @__PURE__ */ jsx22("h2", { style: { margin: 0 }, children: record.name }),
1894
- record.stage ? /* @__PURE__ */ jsx22("span", { className: "badge", style: { marginTop: 8 }, children: record.stage.replaceAll("_", " ") }) : null
1961
+ return /* @__PURE__ */ jsxs21("div", { style: { display: "grid", gap: 14 }, children: [
1962
+ /* @__PURE__ */ jsxs21("header", { children: [
1963
+ /* @__PURE__ */ jsx23("h2", { style: { margin: 0 }, children: record.name }),
1964
+ record.stage ? /* @__PURE__ */ jsx23("span", { className: "badge", style: { marginTop: 8 }, children: record.stage.replaceAll("_", " ") }) : null
1895
1965
  ] }),
1896
- /* @__PURE__ */ jsxs20("dl", { style: { display: "grid", gridTemplateColumns: "minmax(8rem, auto) 1fr", gap: "8px 16px", margin: 0 }, children: [
1897
- Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */ jsxs20("div", { style: { display: "contents" }, children: [
1898
- /* @__PURE__ */ jsx22("dt", { className: "muted", children: name.replaceAll("_", " ") }),
1899
- /* @__PURE__ */ jsx22("dd", { style: { margin: 0, overflowWrap: "anywhere" }, children: displayValue(value) })
1966
+ /* @__PURE__ */ jsxs21("dl", { style: { display: "grid", gridTemplateColumns: "minmax(8rem, auto) 1fr", gap: "8px 16px", margin: 0 }, children: [
1967
+ Object.entries(record.fields).map(([name, value]) => /* @__PURE__ */ jsxs21("div", { style: { display: "contents" }, children: [
1968
+ /* @__PURE__ */ jsx23("dt", { className: "muted", children: name.replaceAll("_", " ") }),
1969
+ /* @__PURE__ */ jsx23("dd", { style: { margin: 0, overflowWrap: "anywhere" }, children: displayValue(value) })
1900
1970
  ] }, name)),
1901
- /* @__PURE__ */ jsxs20("div", { style: { display: "contents" }, children: [
1902
- /* @__PURE__ */ jsx22("dt", { className: "muted", children: "Updated" }),
1903
- /* @__PURE__ */ jsx22("dd", { style: { margin: 0 }, children: new Date(record.updatedAt).toLocaleString() })
1971
+ /* @__PURE__ */ jsxs21("div", { style: { display: "contents" }, children: [
1972
+ /* @__PURE__ */ jsx23("dt", { className: "muted", children: "Updated" }),
1973
+ /* @__PURE__ */ jsx23("dd", { style: { margin: 0 }, children: new Date(record.updatedAt).toLocaleString() })
1904
1974
  ] })
1905
1975
  ] }),
1906
- /* @__PURE__ */ jsxs20("div", { style: { display: "grid", gap: 22 }, children: [
1907
- /* @__PURE__ */ jsx22(
1976
+ /* @__PURE__ */ jsxs21("div", { style: { display: "grid", gap: 22 }, children: [
1977
+ /* @__PURE__ */ jsx23(
1908
1978
  PrivateNotes,
1909
1979
  {
1910
1980
  ctx,
@@ -1913,7 +1983,7 @@ function NetworkRecordDetail(props) {
1913
1983
  leaderName
1914
1984
  }
1915
1985
  ),
1916
- sharedEnabled ? /* @__PURE__ */ jsx22(
1986
+ sharedEnabled ? /* @__PURE__ */ jsx23(
1917
1987
  SharedNotes,
1918
1988
  {
1919
1989
  ctx,
@@ -1927,11 +1997,11 @@ function NetworkRecordDetail(props) {
1927
1997
  }
1928
1998
 
1929
1999
  // src/ui/admin-network-records.tsx
1930
- import { jsx as jsx23, jsxs as jsxs21 } from "preact/jsx-runtime";
2000
+ import { jsx as jsx24, jsxs as jsxs22 } from "preact/jsx-runtime";
1931
2001
  function RecordsExplorer(props) {
1932
2002
  const { ctx, target, type, leaderName } = props;
1933
- const [offset, setOffset] = useState19(0);
1934
- const [search, setSearch] = useState19("");
2003
+ const [offset, setOffset] = useState20(0);
2004
+ const [search, setSearch] = useState20("");
1935
2005
  const query = new URLSearchParams({
1936
2006
  targetId: target.id,
1937
2007
  type,
@@ -1948,12 +2018,12 @@ function RecordsExplorer(props) {
1948
2018
  {
1949
2019
  key: "name",
1950
2020
  header: "Name",
1951
- cell: (record) => /* @__PURE__ */ jsx23("a", { href: ctx.href({ recordId: record.id, detailTab: null }), children: record.name })
2021
+ cell: (record) => /* @__PURE__ */ jsx24("a", { href: ctx.href({ recordId: record.id, detailTab: null }), children: record.name })
1952
2022
  },
1953
2023
  {
1954
2024
  key: "stage",
1955
2025
  header: "Stage",
1956
- cell: (record) => record.stage ? /* @__PURE__ */ jsx23("span", { className: "badge", children: record.stage.replaceAll("_", " ") }) : "\u2014"
2026
+ cell: (record) => record.stage ? /* @__PURE__ */ jsx24("span", { className: "badge", children: record.stage.replaceAll("_", " ") }) : "\u2014"
1957
2027
  },
1958
2028
  {
1959
2029
  key: "updatedAt",
@@ -1961,7 +2031,7 @@ function RecordsExplorer(props) {
1961
2031
  cell: (record) => new Date(record.updatedAt).toLocaleDateString()
1962
2032
  }
1963
2033
  ], [ctx]);
1964
- return /* @__PURE__ */ jsx23(
2034
+ return /* @__PURE__ */ jsx24(
1965
2035
  MasterDetail,
1966
2036
  {
1967
2037
  collapseClosedDetail: true,
@@ -1969,13 +2039,13 @@ function RecordsExplorer(props) {
1969
2039
  detailLabel: "Follower record details, private notes, and shared notes",
1970
2040
  detailOpen: Boolean(selected),
1971
2041
  closeHref: ctx.href({ recordId: null, detailTab: null }),
1972
- master: /* @__PURE__ */ jsxs21("div", { style: { display: "grid", gap: 14, padding: "var(--ui-space-4)" }, children: [
1973
- /* @__PURE__ */ jsxs21("h2", { style: { margin: 0, fontSize: 18 }, children: [
2042
+ master: /* @__PURE__ */ jsxs22("div", { style: { display: "grid", gap: 14, padding: "var(--ui-space-4)" }, children: [
2043
+ /* @__PURE__ */ jsxs22("h2", { style: { margin: 0, fontSize: 18 }, children: [
1974
2044
  target.name,
1975
2045
  " \xB7 ",
1976
2046
  type.replaceAll("_", " ")
1977
2047
  ] }),
1978
- /* @__PURE__ */ jsx23(
2048
+ /* @__PURE__ */ jsx24(
1979
2049
  DataTable2,
1980
2050
  {
1981
2051
  manual: true,
@@ -1995,7 +2065,7 @@ function RecordsExplorer(props) {
1995
2065
  ariaLabel: `${target.name} ${type} records`
1996
2066
  }
1997
2067
  ),
1998
- /* @__PURE__ */ jsx23(
2068
+ /* @__PURE__ */ jsx24(
1999
2069
  Pagination,
2000
2070
  {
2001
2071
  offset: page.data?.offset ?? offset,
@@ -2010,7 +2080,7 @@ function RecordsExplorer(props) {
2010
2080
  }
2011
2081
  )
2012
2082
  ] }),
2013
- detail: selected ? /* @__PURE__ */ jsx23(
2083
+ detail: selected ? /* @__PURE__ */ jsx24(
2014
2084
  NetworkRecordDetail,
2015
2085
  {
2016
2086
  ctx,
@@ -2027,27 +2097,27 @@ function RecordsExplorer(props) {
2027
2097
  function NetworkRecordsWorkspace(props) {
2028
2098
  const { chapter, ctx } = props;
2029
2099
  const targets = useAdminResource(ctx.getToken, "/api/admin/network/targets");
2030
- const [targetId, setTargetId] = useState19("");
2031
- const [type, setType] = useState19("");
2100
+ const [targetId, setTargetId] = useState20("");
2101
+ const [type, setType] = useState20("");
2032
2102
  const target = targets.data?.targets.find((candidate) => candidate.id === targetId) ?? targets.data?.targets[0];
2033
- useEffect7(() => {
2103
+ useEffect8(() => {
2034
2104
  if (target && target.id !== targetId) setTargetId(target.id);
2035
2105
  }, [target, targetId]);
2036
- useEffect7(() => {
2106
+ useEffect8(() => {
2037
2107
  const first = target?.types[0] ?? "";
2038
2108
  if (target && !target.types.includes(type)) setType(first);
2039
2109
  }, [target, type]);
2040
- if (targets.loading) return /* @__PURE__ */ jsx23(AdminNote, { children: "Loading follower records\u2026" });
2041
- if (targets.error || !targets.data) return /* @__PURE__ */ jsxs21(AdminNote, { children: [
2110
+ if (targets.loading) return /* @__PURE__ */ jsx24(AdminNote, { children: "Loading follower records\u2026" });
2111
+ if (targets.error || !targets.data) return /* @__PURE__ */ jsxs22(AdminNote, { children: [
2042
2112
  "Could not load followers",
2043
2113
  targets.error ? `: ${targets.error}` : "",
2044
2114
  "."
2045
2115
  ] });
2046
- if (!target) return /* @__PURE__ */ jsx23(AdminPage, { children: /* @__PURE__ */ jsx23(Panel, { title: "Follower records", children: /* @__PURE__ */ jsx23("p", { className: "muted", children: "No followers are configured." }) }) });
2047
- return /* @__PURE__ */ jsxs21(AdminPage, { children: [
2048
- /* @__PURE__ */ jsxs21("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "end" }, children: [
2049
- /* @__PURE__ */ jsx23(Field8, { label: "Chapter", style: { minWidth: 240 }, children: /* @__PURE__ */ jsx23(
2050
- Select7,
2116
+ if (!target) return /* @__PURE__ */ jsx24(AdminPage, { children: /* @__PURE__ */ jsx24(Panel, { title: "Follower records", children: /* @__PURE__ */ jsx24("p", { className: "muted", children: "No followers are configured." }) }) });
2117
+ return /* @__PURE__ */ jsxs22(AdminPage, { children: [
2118
+ /* @__PURE__ */ jsxs22("div", { style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "end" }, children: [
2119
+ /* @__PURE__ */ jsx24(Field8, { label: "Chapter", style: { minWidth: 240 }, children: /* @__PURE__ */ jsx24(
2120
+ Select8,
2051
2121
  {
2052
2122
  value: target.id,
2053
2123
  options: targets.data.targets.map((item) => ({ value: item.id, label: item.name })),
@@ -2058,8 +2128,8 @@ function NetworkRecordsWorkspace(props) {
2058
2128
  }
2059
2129
  }
2060
2130
  ) }),
2061
- /* @__PURE__ */ jsx23(Field8, { label: "Record type", style: { minWidth: 180 }, children: /* @__PURE__ */ jsx23(
2062
- Select7,
2131
+ /* @__PURE__ */ jsx24(Field8, { label: "Record type", style: { minWidth: 180 }, children: /* @__PURE__ */ jsx24(
2132
+ Select8,
2063
2133
  {
2064
2134
  value: type,
2065
2135
  options: target.types.map((item) => ({ value: item, label: item.replaceAll("_", " ") })),
@@ -2070,7 +2140,7 @@ function NetworkRecordsWorkspace(props) {
2070
2140
  }
2071
2141
  ) })
2072
2142
  ] }),
2073
- type ? /* @__PURE__ */ jsx23(
2143
+ type ? /* @__PURE__ */ jsx24(
2074
2144
  RecordsExplorer,
2075
2145
  {
2076
2146
  ctx,
@@ -2079,7 +2149,7 @@ function NetworkRecordsWorkspace(props) {
2079
2149
  leaderName: chapter.name
2080
2150
  },
2081
2151
  `${target.id}:${type}`
2082
- ) : /* @__PURE__ */ jsx23(Panel, { children: /* @__PURE__ */ jsx23("p", { className: "muted", children: "This follower has no browsable record types configured." }) })
2152
+ ) : /* @__PURE__ */ jsx24(Panel, { children: /* @__PURE__ */ jsx24("p", { className: "muted", children: "This follower has no browsable record types configured." }) })
2083
2153
  ] });
2084
2154
  }
2085
2155
  function networkRecordsWorkspace(chapter) {
@@ -2087,7 +2157,7 @@ function networkRecordsWorkspace(chapter) {
2087
2157
  id: "network",
2088
2158
  label: chapter.copy.admin.workspaces.network,
2089
2159
  defaultViewId: "records",
2090
- render: (ctx) => /* @__PURE__ */ jsx23(NetworkRecordsWorkspace, { chapter, ctx })
2160
+ render: (ctx) => /* @__PURE__ */ jsx24(NetworkRecordsWorkspace, { chapter, ctx })
2091
2161
  };
2092
2162
  }
2093
2163
 
@@ -2098,20 +2168,20 @@ import { StatBand as StatBand2, StepPipeline as StepPipeline2 } from "@odla-ai/u
2098
2168
  var EDGE_UNPAIRED = "edge_unpaired";
2099
2169
 
2100
2170
  // src/ui/admin-portfolio.tsx
2101
- import { jsx as jsx24, jsxs as jsxs22 } from "preact/jsx-runtime";
2171
+ import { jsx as jsx25, jsxs as jsxs23 } from "preact/jsx-runtime";
2102
2172
  function LeaderNetworkOverview({ ctx }) {
2103
2173
  const copy = useChapterCopy().admin.network;
2104
2174
  const { data, loading, error } = useAdminResource(
2105
2175
  ctx.getToken,
2106
2176
  "/api/admin/network/rollup"
2107
2177
  );
2108
- if (loading) return /* @__PURE__ */ jsx24(AdminNote, { children: copy.loadingRollup });
2109
- if (error || !data) return /* @__PURE__ */ jsxs22(AdminNote, { children: [
2178
+ if (loading) return /* @__PURE__ */ jsx25(AdminNote, { children: copy.loadingRollup });
2179
+ if (error || !data) return /* @__PURE__ */ jsxs23(AdminNote, { children: [
2110
2180
  copy.loadRollupFailed,
2111
2181
  error ? `: ${error}` : "",
2112
2182
  "."
2113
2183
  ] });
2114
- return /* @__PURE__ */ jsx24(LeaderNetworkOverviewView, { data });
2184
+ return /* @__PURE__ */ jsx25(LeaderNetworkOverviewView, { data });
2115
2185
  }
2116
2186
  function unpairedEdge(target) {
2117
2187
  const error = target.transport.error;
@@ -2119,63 +2189,63 @@ function unpairedEdge(target) {
2119
2189
  }
2120
2190
  function LeaderNetworkOverviewView({ data }) {
2121
2191
  const copy = useChapterCopy().admin.network;
2122
- if (data.configured === 0) return /* @__PURE__ */ jsx24(AdminPage, { children: /* @__PURE__ */ jsx24(Panel, { title: copy.overview, children: /* @__PURE__ */ jsx24("p", { className: "muted", children: copy.noFollowers }) }) });
2192
+ if (data.configured === 0) return /* @__PURE__ */ jsx25(AdminPage, { children: /* @__PURE__ */ jsx25(Panel, { title: copy.overview, children: /* @__PURE__ */ jsx25("p", { className: "muted", children: copy.noFollowers }) }) });
2123
2193
  const stats = [
2124
2194
  { value: data.configured.toLocaleString(), label: copy.configuredFollowers },
2125
2195
  { value: data.available.toLocaleString(), label: copy.reachableFollowers },
2126
2196
  { value: data.totalRecords.toLocaleString(), label: copy.recordsAcrossFollowers }
2127
2197
  ];
2128
2198
  const steps = data.types.map((type) => ({
2129
- icon: /* @__PURE__ */ jsx24("span", { style: { fontSize: 22, fontWeight: 700 }, children: type.total }),
2199
+ icon: /* @__PURE__ */ jsx25("span", { style: { fontSize: 22, fontWeight: 700 }, children: type.total }),
2130
2200
  title: type.type.replaceAll("_", " "),
2131
2201
  description: Object.entries(type.stages).filter(([, count]) => count > 0).map(([stage, count]) => `${stage.replaceAll("_", " ")} ${count}`).join(" \xB7 ") || copy.noRecordTotals
2132
2202
  }));
2133
- return /* @__PURE__ */ jsxs22(AdminPage, { children: [
2134
- /* @__PURE__ */ jsx24(StatBand2, { stats }),
2135
- /* @__PURE__ */ jsx24(Panel, { title: copy.followers, children: /* @__PURE__ */ jsx24("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.targets.map((target) => /* @__PURE__ */ jsx24(FollowerRow, { target }, target.id)) }) }),
2136
- /* @__PURE__ */ jsx24(Panel, { title: copy.recordTotals, children: steps.length ? /* @__PURE__ */ jsx24(StepPipeline2, { ariaLabel: copy.recordTotals, steps }) : /* @__PURE__ */ jsx24("p", { className: "muted", children: copy.noRecordTotals }) })
2203
+ return /* @__PURE__ */ jsxs23(AdminPage, { children: [
2204
+ /* @__PURE__ */ jsx25(StatBand2, { stats }),
2205
+ /* @__PURE__ */ jsx25(Panel, { title: copy.followers, children: /* @__PURE__ */ jsx25("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.targets.map((target) => /* @__PURE__ */ jsx25(FollowerRow, { target }, target.id)) }) }),
2206
+ /* @__PURE__ */ jsx25(Panel, { title: copy.recordTotals, children: steps.length ? /* @__PURE__ */ jsx25(StepPipeline2, { ariaLabel: copy.recordTotals, steps }) : /* @__PURE__ */ jsx25("p", { className: "muted", children: copy.noRecordTotals }) })
2137
2207
  ] });
2138
2208
  }
2139
2209
  function FollowerRow({ target }) {
2140
2210
  const copy = useChapterCopy().admin.network;
2141
2211
  const unpaired = unpairedEdge(target);
2142
- return /* @__PURE__ */ jsxs22(
2212
+ return /* @__PURE__ */ jsxs23(
2143
2213
  "li",
2144
2214
  {
2145
2215
  "data-network-target": target.id,
2146
2216
  "data-edge-state": unpaired ? "unpaired" : "paired",
2147
2217
  style: { display: "flex", gap: 12, alignItems: "baseline", flexWrap: "wrap" },
2148
2218
  children: [
2149
- /* @__PURE__ */ jsx24("strong", { style: { minWidth: 180 }, children: target.name }),
2150
- /* @__PURE__ */ jsx24("span", { className: "badge", children: unpaired ? copy.edgeUnpaired : target.transport.reachable === null ? copy.transportNotChecked : target.transport.reachable ? copy.transportReachable : copy.transportUnavailable }),
2151
- /* @__PURE__ */ jsx24("span", { className: "muted", children: target.transport.kind === "binding" ? formatChapterCopy(copy.bindingTransport, { binding: target.transport.binding ?? "" }) : copy.urlTransport }),
2152
- /* @__PURE__ */ jsx24("span", { className: "muted", children: target.configuration.activeRevision === null ? copy.noActiveConfiguration : formatChapterCopy(copy.activeConfiguration, { revision: target.configuration.activeRevision }) }),
2153
- /* @__PURE__ */ jsx24("span", { className: "muted", children: target.publicSite.available === null ? copy.publicSiteNotChecked : target.publicSite.available ? copy.publicSiteAvailable : copy.publicSiteUnavailable }),
2154
- unpaired ? /* @__PURE__ */ jsxs22("span", { className: "muted", style: { flexBasis: "100%" }, children: [
2219
+ /* @__PURE__ */ jsx25("strong", { style: { minWidth: 180 }, children: target.name }),
2220
+ /* @__PURE__ */ jsx25("span", { className: "badge", children: unpaired ? copy.edgeUnpaired : target.transport.reachable === null ? copy.transportNotChecked : target.transport.reachable ? copy.transportReachable : copy.transportUnavailable }),
2221
+ /* @__PURE__ */ jsx25("span", { className: "muted", children: target.transport.kind === "binding" ? formatChapterCopy(copy.bindingTransport, { binding: target.transport.binding ?? "" }) : copy.urlTransport }),
2222
+ /* @__PURE__ */ jsx25("span", { className: "muted", children: target.configuration.activeRevision === null ? copy.noActiveConfiguration : formatChapterCopy(copy.activeConfiguration, { revision: target.configuration.activeRevision }) }),
2223
+ /* @__PURE__ */ jsx25("span", { className: "muted", children: target.publicSite.available === null ? copy.publicSiteNotChecked : target.publicSite.available ? copy.publicSiteAvailable : copy.publicSiteUnavailable }),
2224
+ unpaired ? /* @__PURE__ */ jsxs23("span", { className: "muted", style: { flexBasis: "100%" }, children: [
2155
2225
  copy.edgeUnpairedHint,
2156
2226
  " ",
2157
- /* @__PURE__ */ jsx24("code", { children: unpaired.hint })
2158
- ] }) : target.error ? /* @__PURE__ */ jsx24("span", { className: "muted", children: target.error }) : null,
2159
- /* @__PURE__ */ jsx24("a", { href: target.url, target: "_blank", rel: "noreferrer", style: { marginLeft: "auto" }, children: target.url })
2227
+ /* @__PURE__ */ jsx25("code", { children: unpaired.hint })
2228
+ ] }) : target.error ? /* @__PURE__ */ jsx25("span", { className: "muted", children: target.error }) : null,
2229
+ /* @__PURE__ */ jsx25("a", { href: target.url, target: "_blank", rel: "noreferrer", style: { marginLeft: "auto" }, children: target.url })
2160
2230
  ]
2161
2231
  }
2162
2232
  );
2163
2233
  }
2164
2234
 
2165
2235
  // src/ui/admin-admission.tsx
2166
- import { useEffect as useEffect8, useId, useRef as useRef2, useState as useState20 } from "preact/hooks";
2167
- import { Field as Field9, Input as Input2, Select as Select8 } from "@odla-ai/ui/components";
2168
- import { jsx as jsx25, jsxs as jsxs23 } from "preact/jsx-runtime";
2236
+ import { useEffect as useEffect9, useId, useRef as useRef2, useState as useState21 } from "preact/hooks";
2237
+ import { Field as Field9, Input as Input2, Select as Select9 } from "@odla-ai/ui/components";
2238
+ import { jsx as jsx26, jsxs as jsxs24 } from "preact/jsx-runtime";
2169
2239
  function AdmissionGrantsWorkspace(props) {
2170
2240
  const copy = useChapterCopy().admin.admission;
2171
2241
  const uid = useId();
2172
2242
  const resource = useAdminResource(props.getToken, "/api/admin/admission-grants");
2173
- const [email, setEmail] = useState20("");
2174
- const [decision, setDecision] = useState20("waive_interview");
2175
- const [note, setNote] = useState20("");
2176
- const [expires, setExpires] = useState20("");
2177
- const [busy, setBusy] = useState20(false);
2178
- const [error, setError] = useState20(null);
2243
+ const [email, setEmail] = useState21("");
2244
+ const [decision, setDecision] = useState21("waive_interview");
2245
+ const [note, setNote] = useState21("");
2246
+ const [expires, setExpires] = useState21("");
2247
+ const [busy, setBusy] = useState21(false);
2248
+ const [error, setError] = useState21(null);
2179
2249
  const submit = async (event) => {
2180
2250
  event.preventDefault();
2181
2251
  setBusy(true);
@@ -2212,36 +2282,36 @@ function AdmissionGrantsWorkspace(props) {
2212
2282
  setBusy(false);
2213
2283
  }
2214
2284
  };
2215
- return /* @__PURE__ */ jsxs23(AdminPage, { children: [
2216
- /* @__PURE__ */ jsxs23(Panel, { title: copy.title, children: [
2217
- /* @__PURE__ */ jsx25("p", { className: "muted", style: { margin: 0 }, children: copy.description }),
2218
- /* @__PURE__ */ jsxs23("form", { onSubmit: (event) => void submit(event), style: { display: "grid", gap: 12, gridTemplateColumns: "minmax(220px,2fr) minmax(180px,1fr) minmax(160px,1fr)" }, children: [
2219
- /* @__PURE__ */ jsx25(Field9, { label: copy.email, htmlFor: `${uid}-email`, children: /* @__PURE__ */ jsx25(Input2, { id: `${uid}-email`, required: true, type: "email", value: email, onInput: (event) => setEmail(event.currentTarget.value) }) }),
2220
- /* @__PURE__ */ jsx25(Field9, { label: copy.decision, htmlFor: `${uid}-decision`, children: /* @__PURE__ */ jsxs23(Select8, { id: `${uid}-decision`, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
2221
- /* @__PURE__ */ jsx25("option", { value: "waive_interview", children: copy.waive }),
2222
- /* @__PURE__ */ jsx25("option", { value: "require_interview", children: copy.require })
2285
+ return /* @__PURE__ */ jsxs24(AdminPage, { children: [
2286
+ /* @__PURE__ */ jsxs24(Panel, { title: copy.title, children: [
2287
+ /* @__PURE__ */ jsx26("p", { className: "muted", style: { margin: 0 }, children: copy.description }),
2288
+ /* @__PURE__ */ jsxs24("form", { onSubmit: (event) => void submit(event), style: { display: "grid", gap: 12, gridTemplateColumns: "minmax(220px,2fr) minmax(180px,1fr) minmax(160px,1fr)" }, children: [
2289
+ /* @__PURE__ */ jsx26(Field9, { label: copy.email, htmlFor: `${uid}-email`, children: /* @__PURE__ */ jsx26(Input2, { id: `${uid}-email`, required: true, type: "email", value: email, onInput: (event) => setEmail(event.currentTarget.value) }) }),
2290
+ /* @__PURE__ */ jsx26(Field9, { label: copy.decision, htmlFor: `${uid}-decision`, children: /* @__PURE__ */ jsxs24(Select9, { id: `${uid}-decision`, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
2291
+ /* @__PURE__ */ jsx26("option", { value: "waive_interview", children: copy.waive }),
2292
+ /* @__PURE__ */ jsx26("option", { value: "require_interview", children: copy.require })
2223
2293
  ] }) }),
2224
- /* @__PURE__ */ jsx25(Field9, { label: copy.expires, htmlFor: `${uid}-expires`, children: /* @__PURE__ */ jsx25(Input2, { id: `${uid}-expires`, type: "date", value: expires, onInput: (event) => setExpires(event.currentTarget.value) }) }),
2225
- /* @__PURE__ */ jsx25(Field9, { label: copy.note, htmlFor: `${uid}-note`, style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsx25(Input2, { id: `${uid}-note`, value: note, maxLength: 2e3, onInput: (event) => setNote(event.currentTarget.value) }) }),
2226
- /* @__PURE__ */ jsx25("button", { className: "btn secondary", type: "submit", disabled: busy, children: copy.add })
2294
+ /* @__PURE__ */ jsx26(Field9, { label: copy.expires, htmlFor: `${uid}-expires`, children: /* @__PURE__ */ jsx26(Input2, { id: `${uid}-expires`, type: "date", value: expires, onInput: (event) => setExpires(event.currentTarget.value) }) }),
2295
+ /* @__PURE__ */ jsx26(Field9, { label: copy.note, htmlFor: `${uid}-note`, style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsx26(Input2, { id: `${uid}-note`, value: note, maxLength: 2e3, onInput: (event) => setNote(event.currentTarget.value) }) }),
2296
+ /* @__PURE__ */ jsx26("button", { className: "btn secondary", type: "submit", disabled: busy, children: copy.add })
2227
2297
  ] }),
2228
- error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: error }) : null
2298
+ error ? /* @__PURE__ */ jsx26("p", { role: "alert", children: error }) : null
2229
2299
  ] }),
2230
- /* @__PURE__ */ jsxs23(Panel, { title: copy.ledger, children: [
2231
- resource.loading ? /* @__PURE__ */ jsx25("p", { className: "muted", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: resource.error }) : null,
2232
- /* @__PURE__ */ jsx25("div", { style: { display: "grid", gap: 10 }, children: (resource.data?.grants ?? []).map((grant) => {
2300
+ /* @__PURE__ */ jsxs24(Panel, { title: copy.ledger, children: [
2301
+ resource.loading ? /* @__PURE__ */ jsx26("p", { className: "muted", children: copy.loading }) : resource.error ? /* @__PURE__ */ jsx26("p", { role: "alert", children: resource.error }) : null,
2302
+ /* @__PURE__ */ jsx26("div", { style: { display: "grid", gap: 10 }, children: (resource.data?.grants ?? []).map((grant) => {
2233
2303
  const state = grant.consumedAt ? copy.consumed : grant.revokedAt || !grant.active ? copy.revoked : grant.expiresAt && grant.expiresAt <= Date.now() ? copy.expired : copy.active;
2234
- return /* @__PURE__ */ jsxs23("article", { className: "card", style: { padding: 14 }, children: [
2235
- /* @__PURE__ */ jsx25("strong", { children: grant.email }),
2304
+ return /* @__PURE__ */ jsxs24("article", { className: "card", style: { padding: 14 }, children: [
2305
+ /* @__PURE__ */ jsx26("strong", { children: grant.email }),
2236
2306
  " \xB7 ",
2237
2307
  grant.decision === "waive_interview" ? copy.waive : copy.require,
2238
- /* @__PURE__ */ jsxs23("p", { className: "muted", style: { margin: "6px 0" }, children: [
2308
+ /* @__PURE__ */ jsxs24("p", { className: "muted", style: { margin: "6px 0" }, children: [
2239
2309
  grant.sourceScope === "local" ? copy.local : copy.parent,
2240
2310
  " \xB7 ",
2241
2311
  state,
2242
2312
  grant.note ? ` \xB7 ${grant.note}` : ""
2243
2313
  ] }),
2244
- grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */ jsx25("button", { className: "btn secondary mini", disabled: busy, onClick: () => void revoke(grant), children: copy.revoke }) : null
2314
+ grant.sourceScope === "local" && state === copy.active ? /* @__PURE__ */ jsx26("button", { className: "btn secondary mini", disabled: busy, onClick: () => void revoke(grant), children: copy.revoke }) : null
2245
2315
  ] }, grant.id);
2246
2316
  }) })
2247
2317
  ] })
@@ -2252,22 +2322,22 @@ function admissionGrantsWorkspace(label = "Admission") {
2252
2322
  id: "admission",
2253
2323
  label,
2254
2324
  defaultViewId: "grants",
2255
- render: (ctx) => /* @__PURE__ */ jsx25(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
2325
+ render: (ctx) => /* @__PURE__ */ jsx26(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
2256
2326
  };
2257
2327
  }
2258
2328
  function LeaderAdmissionAction(props) {
2259
- return /* @__PURE__ */ jsx25(LeaderAdmissionForm, { ...props }, JSON.stringify([props.targetId, props.personId, props.sourceRecordId]));
2329
+ return /* @__PURE__ */ jsx26(LeaderAdmissionForm, { ...props }, JSON.stringify([props.targetId, props.personId, props.sourceRecordId]));
2260
2330
  }
2261
2331
  function LeaderAdmissionForm(props) {
2262
2332
  const uid = useId();
2263
- const [decision, setDecision] = useState20("");
2264
- const [note, setNote] = useState20("");
2265
- const [loaded, setLoaded] = useState20(false);
2266
- const [busy, setBusy] = useState20(false);
2267
- const [error, setError] = useState20(null);
2268
- const [status, setStatus] = useState20(null);
2333
+ const [decision, setDecision] = useState21("");
2334
+ const [note, setNote] = useState21("");
2335
+ const [loaded, setLoaded] = useState21(false);
2336
+ const [busy, setBusy] = useState21(false);
2337
+ const [error, setError] = useState21(null);
2338
+ const [status, setStatus] = useState21(null);
2269
2339
  const attempt = useRef2(null);
2270
- useEffect8(() => {
2340
+ useEffect9(() => {
2271
2341
  let live = true;
2272
2342
  const personId = props.sourceRecordId ? `chapter-person:${props.targetId}:${props.sourceRecordId}` : props.personId;
2273
2343
  void adminFetch(props.getToken, `/api/admin/network/admissions?targetId=${encodeURIComponent(props.targetId)}`).then((result) => {
@@ -2310,16 +2380,16 @@ function LeaderAdmissionForm(props) {
2310
2380
  setBusy(false);
2311
2381
  }
2312
2382
  };
2313
- return /* @__PURE__ */ jsxs23("div", { style: { display: "grid", gap: 8 }, children: [
2314
- /* @__PURE__ */ jsx25(Field9, { label: "Interview", htmlFor: `${uid}-decision`, children: /* @__PURE__ */ jsxs23(Select8, { id: `${uid}-decision`, disabled: !loaded || busy, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
2315
- /* @__PURE__ */ jsx25("option", { value: "", children: loaded ? "Choose an invitation setting" : "Loading saved setting\u2026" }),
2316
- /* @__PURE__ */ jsx25("option", { value: "waive_interview", children: "Already completed" }),
2317
- /* @__PURE__ */ jsx25("option", { value: "require_interview", children: "Required" })
2383
+ return /* @__PURE__ */ jsxs24("div", { style: { display: "grid", gap: 8 }, children: [
2384
+ /* @__PURE__ */ jsx26(Field9, { label: "Interview", htmlFor: `${uid}-decision`, children: /* @__PURE__ */ jsxs24(Select9, { id: `${uid}-decision`, disabled: !loaded || busy, value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
2385
+ /* @__PURE__ */ jsx26("option", { value: "", children: loaded ? "Choose an invitation setting" : "Loading saved setting\u2026" }),
2386
+ /* @__PURE__ */ jsx26("option", { value: "waive_interview", children: "Already completed" }),
2387
+ /* @__PURE__ */ jsx26("option", { value: "require_interview", children: "Required" })
2318
2388
  ] }) }),
2319
- /* @__PURE__ */ jsx25(Field9, { label: "Internal note", htmlFor: `${uid}-note`, children: /* @__PURE__ */ jsx25(Input2, { id: `${uid}-note`, disabled: !loaded || busy, maxLength: 2e3, value: note, onInput: (event) => setNote(event.currentTarget.value) }) }),
2320
- /* @__PURE__ */ jsx25("button", { className: "btn secondary", type: "button", disabled: busy || !loaded || !decision, onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save invitation setting" }),
2321
- error ? /* @__PURE__ */ jsx25("p", { role: "alert", children: error }) : null,
2322
- status ? /* @__PURE__ */ jsx25("p", { role: "status", children: status }) : null
2389
+ /* @__PURE__ */ jsx26(Field9, { label: "Internal note", htmlFor: `${uid}-note`, children: /* @__PURE__ */ jsx26(Input2, { id: `${uid}-note`, disabled: !loaded || busy, maxLength: 2e3, value: note, onInput: (event) => setNote(event.currentTarget.value) }) }),
2390
+ /* @__PURE__ */ jsx26("button", { className: "btn secondary", type: "button", disabled: busy || !loaded || !decision, onClick: () => void submit(), children: busy ? "Saving\u2026" : "Save invitation setting" }),
2391
+ error ? /* @__PURE__ */ jsx26("p", { role: "alert", children: error }) : null,
2392
+ status ? /* @__PURE__ */ jsx26("p", { role: "status", children: status }) : null
2323
2393
  ] });
2324
2394
  }
2325
2395
 
@@ -2327,19 +2397,19 @@ function LeaderAdmissionForm(props) {
2327
2397
  import { Tabs as Tabs2 } from "@odla-ai/ui/components";
2328
2398
 
2329
2399
  // src/ui/admin-availability.tsx
2330
- import { useState as useState21 } from "preact/hooks";
2400
+ import { useState as useState22 } from "preact/hooks";
2331
2401
  import { Button as Button14, Checkbox, Field as Field10, Input as Input3 } from "@odla-ai/ui/components";
2332
- import { jsx as jsx26, jsxs as jsxs24 } from "preact/jsx-runtime";
2402
+ import { jsx as jsx27, jsxs as jsxs25 } from "preact/jsx-runtime";
2333
2403
  function AvailabilityBody({ ctx }) {
2334
2404
  const copy = useChapterCopy().admin.availability;
2335
2405
  const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
2336
- const [draft, setDraft] = useState21(null);
2337
- const [busy, setBusy] = useState21(false);
2338
- const [note, setNote] = useState21(null);
2339
- const [errors, setErrors] = useState21({});
2406
+ const [draft, setDraft] = useState22(null);
2407
+ const [busy, setBusy] = useState22(false);
2408
+ const [note, setNote] = useState22(null);
2409
+ const [errors, setErrors] = useState22({});
2340
2410
  const cfg = draft ?? data?.scheduling ?? null;
2341
- if (loading) return /* @__PURE__ */ jsx26(AdminNote, { children: copy.loading });
2342
- if (error || !cfg) return /* @__PURE__ */ jsxs24(AdminNote, { children: [
2411
+ if (loading) return /* @__PURE__ */ jsx27(AdminNote, { children: copy.loading });
2412
+ if (error || !cfg) return /* @__PURE__ */ jsxs25(AdminNote, { children: [
2343
2413
  copy.loadFailed,
2344
2414
  error ? `: ${error}` : "",
2345
2415
  "."
@@ -2369,30 +2439,30 @@ function AvailabilityBody({ ctx }) {
2369
2439
  setBusy(false);
2370
2440
  }
2371
2441
  };
2372
- const numField = (key, label) => /* @__PURE__ */ jsx26(Field10, { label, error: errors[key], children: /* @__PURE__ */ jsx26(Input3, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
2373
- return /* @__PURE__ */ jsx26(AdminPage, { children: /* @__PURE__ */ jsxs24(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx26("span", { className: "muted", children: note }) : void 0, children: [
2374
- /* @__PURE__ */ jsx26(Field10, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx26("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx26(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
2375
- /* @__PURE__ */ jsxs24("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
2442
+ const numField = (key, label) => /* @__PURE__ */ jsx27(Field10, { label, error: errors[key], children: /* @__PURE__ */ jsx27(Input3, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
2443
+ return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsxs25(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx27("span", { className: "muted", children: note }) : void 0, children: [
2444
+ /* @__PURE__ */ jsx27(Field10, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx27("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx27(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
2445
+ /* @__PURE__ */ jsxs25("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
2376
2446
  numField("startHour", copy.startHour),
2377
2447
  numField("endHour", copy.endHour),
2378
2448
  numField("slotMinutes", copy.slotMinutes),
2379
2449
  numField("minNoticeHours", copy.minNoticeHours),
2380
2450
  numField("windowDays", copy.windowDays),
2381
- /* @__PURE__ */ jsx26(Field10, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx26(Input3, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
2451
+ /* @__PURE__ */ jsx27(Field10, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx27(Input3, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
2382
2452
  ] }),
2383
- /* @__PURE__ */ jsx26(Field10, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx26(Input3, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
2384
- /* @__PURE__ */ jsx26("div", { children: /* @__PURE__ */ jsx26(Button14, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
2453
+ /* @__PURE__ */ jsx27(Field10, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx27(Input3, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
2454
+ /* @__PURE__ */ jsx27("div", { children: /* @__PURE__ */ jsx27(Button14, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
2385
2455
  ] }) });
2386
2456
  }
2387
2457
  function availabilitySection(options = {}) {
2388
2458
  const { id = "availability", label = "Availability" } = options;
2389
- return { id, label, render: (ctx) => /* @__PURE__ */ jsx26(AvailabilityBody, { ctx }) };
2459
+ return { id, label, render: (ctx) => /* @__PURE__ */ jsx27(AvailabilityBody, { ctx }) };
2390
2460
  }
2391
2461
 
2392
2462
  // src/ui/admin-builder-library.tsx
2393
- import { useEffect as useEffect9, useId as useId2, useMemo as useMemo2, useState as useState22 } from "preact/hooks";
2394
- import { Field as Field11, Input as Input4, Select as Select9, Textarea as Textarea3 } from "@odla-ai/ui/components";
2395
- import { jsx as jsx27, jsxs as jsxs25 } from "preact/jsx-runtime";
2463
+ import { useEffect as useEffect10, useId as useId2, useMemo as useMemo2, useState as useState23 } from "preact/hooks";
2464
+ import { Field as Field11, Input as Input4, Select as Select10, Textarea as Textarea3 } from "@odla-ai/ui/components";
2465
+ import { jsx as jsx28, jsxs as jsxs26 } from "preact/jsx-runtime";
2396
2466
  var KINDS = ["document", "note", "link", "inspiration", "image", "file"];
2397
2467
  var KIND_LABEL = { document: "Document", note: "Note", link: "Link", inspiration: "Inspiration", image: "Image", file: "File" };
2398
2468
  var STATUS_LABEL = { draft: "Draft", published: "Published", archived: "Archived" };
@@ -2424,13 +2494,13 @@ function localResource(kind, chapterId, mode) {
2424
2494
  };
2425
2495
  }
2426
2496
  function ResourcePreview({ item }) {
2427
- return /* @__PURE__ */ jsxs25("div", { className: "card", style: { padding: 14 }, children: [
2428
- /* @__PURE__ */ jsx27("div", { className: "card-label", children: "Preview" }),
2429
- /* @__PURE__ */ jsx27("h3", { style: { margin: "6px 0" }, children: item.title || "Untitled" }),
2430
- item.summary ? /* @__PURE__ */ jsx27("p", { style: { margin: "0 0 10px" }, children: item.summary }) : null,
2431
- item.bodyMarkdown ? /* @__PURE__ */ jsx27("pre", { style: { margin: 0, whiteSpace: "pre-wrap", font: "inherit", lineHeight: 1.55 }, children: item.bodyMarkdown }) : null,
2432
- item.url ? /* @__PURE__ */ jsx27("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url }) : null,
2433
- item.brandAsset ? /* @__PURE__ */ jsxs25("div", { style: META, children: [
2497
+ return /* @__PURE__ */ jsxs26("div", { className: "card", style: { padding: 14 }, children: [
2498
+ /* @__PURE__ */ jsx28("div", { className: "card-label", children: "Preview" }),
2499
+ /* @__PURE__ */ jsx28("h3", { style: { margin: "6px 0" }, children: item.title || "Untitled" }),
2500
+ item.summary ? /* @__PURE__ */ jsx28("p", { style: { margin: "0 0 10px" }, children: item.summary }) : null,
2501
+ item.bodyMarkdown ? /* @__PURE__ */ jsx28("pre", { style: { margin: 0, whiteSpace: "pre-wrap", font: "inherit", lineHeight: 1.55 }, children: item.bodyMarkdown }) : null,
2502
+ item.url ? /* @__PURE__ */ jsx28("a", { href: item.url, target: "_blank", rel: "noreferrer", children: item.url }) : null,
2503
+ item.brandAsset ? /* @__PURE__ */ jsxs26("div", { style: META, children: [
2434
2504
  "Brand asset \xB7 ",
2435
2505
  item.brandAsset.bookId,
2436
2506
  " / ",
@@ -2442,15 +2512,15 @@ function ResourcePreview({ item }) {
2442
2512
  function BuilderLibraryAdmin({ ctx, initialData }) {
2443
2513
  const remote = useAdminResource(ctx.getToken, "/api/admin/builder-library");
2444
2514
  const seed = initialData ?? remote.data;
2445
- const [resources, setResources] = useState22(() => [...seed?.local?.document.resources ?? []]);
2446
- const [selectedKey, setSelectedKey] = useState22(() => seed?.local?.document.resources[0]?.id ? localKey(seed.local.document.resources[0].id) : null);
2447
- const [query, setQuery] = useState22("");
2448
- const [kindFilter, setKindFilter] = useState22("all");
2449
- const [statusFilter, setStatusFilter] = useState22("all");
2450
- const [newKind, setNewKind] = useState22("document");
2451
- const [saveError, setSaveError] = useState22(null);
2452
- const [saving, setSaving] = useState22(false);
2453
- useEffect9(() => {
2515
+ const [resources, setResources] = useState23(() => [...seed?.local?.document.resources ?? []]);
2516
+ const [selectedKey, setSelectedKey] = useState23(() => seed?.local?.document.resources[0]?.id ? localKey(seed.local.document.resources[0].id) : null);
2517
+ const [query, setQuery] = useState23("");
2518
+ const [kindFilter, setKindFilter] = useState23("all");
2519
+ const [statusFilter, setStatusFilter] = useState23("all");
2520
+ const [newKind, setNewKind] = useState23("document");
2521
+ const [saveError, setSaveError] = useState23(null);
2522
+ const [saving, setSaving] = useState23(false);
2523
+ useEffect10(() => {
2454
2524
  if (!remote.data) return;
2455
2525
  const next = [...remote.data.local?.document.resources ?? []];
2456
2526
  setResources(next);
@@ -2466,8 +2536,8 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
2466
2536
  return (!query.trim() || haystack.includes(query.toLowerCase().trim())) && (kindFilter === "all" || resource.kind === kindFilter) && (statusFilter === "all" || resource.status === statusFilter);
2467
2537
  });
2468
2538
  const selected = entries.find((entry) => entry.key === selectedKey) ?? visible[0] ?? null;
2469
- if (!data && remote.loading) return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsx27(AdminNote, { children: "Loading Builder Library\u2026" }) });
2470
- if (remote.error || !data) return /* @__PURE__ */ jsx27(AdminPage, { children: /* @__PURE__ */ jsxs25(AdminNote, { children: [
2539
+ if (!data && remote.loading) return /* @__PURE__ */ jsx28(AdminPage, { children: /* @__PURE__ */ jsx28(AdminNote, { children: "Loading Builder Library\u2026" }) });
2540
+ if (remote.error || !data) return /* @__PURE__ */ jsx28(AdminPage, { children: /* @__PURE__ */ jsxs26(AdminNote, { children: [
2471
2541
  "Builder Library could not be loaded: ",
2472
2542
  remote.error ?? "unknown error"
2473
2543
  ] }) });
@@ -2503,66 +2573,66 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
2503
2573
  }
2504
2574
  };
2505
2575
  const scopeValue = selected?.resource.scope.kind === "chapter" ? `chapter:${selected.resource.scope.chapterId}` : selected?.resource.scope.kind ?? "built_not_found";
2506
- return /* @__PURE__ */ jsxs25(AdminPage, { children: [
2507
- /* @__PURE__ */ jsxs25(Panel, { title: /* @__PURE__ */ jsxs25("span", { children: [
2576
+ return /* @__PURE__ */ jsxs26(AdminPage, { children: [
2577
+ /* @__PURE__ */ jsxs26(Panel, { title: /* @__PURE__ */ jsxs26("span", { children: [
2508
2578
  "Builder Library ",
2509
- /* @__PURE__ */ jsx27("span", { className: "muted", children: "\xB7 private" })
2510
- ] }), actions: /* @__PURE__ */ jsxs25("div", { style: ROW, children: [
2511
- /* @__PURE__ */ jsx27(Select9, { "aria-label": "New resource type", value: newKind, onChange: (event) => setNewKind(event.currentTarget.value), children: KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }),
2512
- /* @__PURE__ */ jsx27("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring, onClick: add, children: "Add resource" })
2579
+ /* @__PURE__ */ jsx28("span", { className: "muted", children: "\xB7 private" })
2580
+ ] }), actions: /* @__PURE__ */ jsxs26("div", { style: ROW, children: [
2581
+ /* @__PURE__ */ jsx28(Select10, { "aria-label": "New resource type", value: newKind, onChange: (event) => setNewKind(event.currentTarget.value), children: KINDS.map((kind) => /* @__PURE__ */ jsx28("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }),
2582
+ /* @__PURE__ */ jsx28("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring, onClick: add, children: "Add resource" })
2513
2583
  ] }), children: [
2514
- /* @__PURE__ */ jsx27("p", { className: "muted", style: { margin: 0 }, children: "Documents, links, and Brand assets used by site-building agents. Nothing here appears on a member page." }),
2515
- /* @__PURE__ */ jsxs25("div", { style: ROW, children: [
2516
- /* @__PURE__ */ jsx27(Input4, { "aria-label": "Search resources", placeholder: "Search resources", value: query, onInput: (event) => setQuery(event.currentTarget.value), style: { flex: "1 1 220px" } }),
2517
- /* @__PURE__ */ jsxs25(Select9, { "aria-label": "Filter by type", value: kindFilter, onChange: (event) => setKindFilter(event.currentTarget.value), children: [
2518
- /* @__PURE__ */ jsx27("option", { value: "all", children: "All types" }),
2519
- KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind))
2584
+ /* @__PURE__ */ jsx28("p", { className: "muted", style: { margin: 0 }, children: "Documents, links, and Brand assets used by site-building agents. Nothing here appears on a member page." }),
2585
+ /* @__PURE__ */ jsxs26("div", { style: ROW, children: [
2586
+ /* @__PURE__ */ jsx28(Input4, { "aria-label": "Search resources", placeholder: "Search resources", value: query, onInput: (event) => setQuery(event.currentTarget.value), style: { flex: "1 1 220px" } }),
2587
+ /* @__PURE__ */ jsxs26(Select10, { "aria-label": "Filter by type", value: kindFilter, onChange: (event) => setKindFilter(event.currentTarget.value), children: [
2588
+ /* @__PURE__ */ jsx28("option", { value: "all", children: "All types" }),
2589
+ KINDS.map((kind) => /* @__PURE__ */ jsx28("option", { value: kind, children: KIND_LABEL[kind] }, kind))
2520
2590
  ] }),
2521
- /* @__PURE__ */ jsxs25(Select9, { "aria-label": "Filter by status", value: statusFilter, onChange: (event) => setStatusFilter(event.currentTarget.value), children: [
2522
- /* @__PURE__ */ jsx27("option", { value: "all", children: "All statuses" }),
2523
- /* @__PURE__ */ jsx27("option", { value: "draft", children: "Draft" }),
2524
- /* @__PURE__ */ jsx27("option", { value: "published", children: "Published" }),
2525
- /* @__PURE__ */ jsx27("option", { value: "archived", children: "Archived" })
2591
+ /* @__PURE__ */ jsxs26(Select10, { "aria-label": "Filter by status", value: statusFilter, onChange: (event) => setStatusFilter(event.currentTarget.value), children: [
2592
+ /* @__PURE__ */ jsx28("option", { value: "all", children: "All statuses" }),
2593
+ /* @__PURE__ */ jsx28("option", { value: "draft", children: "Draft" }),
2594
+ /* @__PURE__ */ jsx28("option", { value: "published", children: "Published" }),
2595
+ /* @__PURE__ */ jsx28("option", { value: "archived", children: "Archived" })
2526
2596
  ] })
2527
2597
  ] })
2528
2598
  ] }),
2529
- /* @__PURE__ */ jsxs25("div", { style: GRID, "data-builder-library-cms": true, children: [
2530
- /* @__PURE__ */ jsx27(Panel, { title: `${visible.length} resource${visible.length === 1 ? "" : "s"}`, children: /* @__PURE__ */ jsx27("div", { style: { ...STACK, maxHeight: "70vh", overflow: "auto" }, children: visible.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "muted", children: "No resources match these filters." }) : visible.map((entry) => /* @__PURE__ */ jsxs25("button", { type: "button", style: { ...ITEM, ...selected?.key === entry.key ? { borderColor: "var(--ui-accent)", boxShadow: "0 0 0 1px var(--ui-accent)" } : {} }, onClick: () => setSelectedKey(entry.key), children: [
2531
- /* @__PURE__ */ jsxs25("div", { style: { ...ROW, justifyContent: "space-between" }, children: [
2532
- /* @__PURE__ */ jsx27("strong", { children: entry.resource.title }),
2533
- /* @__PURE__ */ jsx27("span", { className: "badge", children: KIND_LABEL[entry.resource.kind] })
2599
+ /* @__PURE__ */ jsxs26("div", { style: GRID, "data-builder-library-cms": true, children: [
2600
+ /* @__PURE__ */ jsx28(Panel, { title: `${visible.length} resource${visible.length === 1 ? "" : "s"}`, children: /* @__PURE__ */ jsx28("div", { style: { ...STACK, maxHeight: "70vh", overflow: "auto" }, children: visible.length === 0 ? /* @__PURE__ */ jsx28("p", { className: "muted", children: "No resources match these filters." }) : visible.map((entry) => /* @__PURE__ */ jsxs26("button", { type: "button", style: { ...ITEM, ...selected?.key === entry.key ? { borderColor: "var(--ui-accent)", boxShadow: "0 0 0 1px var(--ui-accent)" } : {} }, onClick: () => setSelectedKey(entry.key), children: [
2601
+ /* @__PURE__ */ jsxs26("div", { style: { ...ROW, justifyContent: "space-between" }, children: [
2602
+ /* @__PURE__ */ jsx28("strong", { children: entry.resource.title }),
2603
+ /* @__PURE__ */ jsx28("span", { className: "badge", children: KIND_LABEL[entry.resource.kind] })
2534
2604
  ] }),
2535
- /* @__PURE__ */ jsx27("p", { className: "muted", style: { margin: "6px 0", lineHeight: 1.4 }, children: contentSummary(entry.resource) }),
2536
- /* @__PURE__ */ jsxs25("div", { style: META, children: [
2537
- /* @__PURE__ */ jsx27("span", { children: STATUS_LABEL[entry.resource.status] }),
2538
- /* @__PURE__ */ jsx27("span", { children: "\xB7" }),
2539
- /* @__PURE__ */ jsx27("span", { children: scopeLabel(entry.resource.scope) }),
2540
- /* @__PURE__ */ jsx27("span", { children: "\xB7" }),
2541
- /* @__PURE__ */ jsx27("span", { children: entry.sourceId })
2605
+ /* @__PURE__ */ jsx28("p", { className: "muted", style: { margin: "6px 0", lineHeight: 1.4 }, children: contentSummary(entry.resource) }),
2606
+ /* @__PURE__ */ jsxs26("div", { style: META, children: [
2607
+ /* @__PURE__ */ jsx28("span", { children: STATUS_LABEL[entry.resource.status] }),
2608
+ /* @__PURE__ */ jsx28("span", { children: "\xB7" }),
2609
+ /* @__PURE__ */ jsx28("span", { children: scopeLabel(entry.resource.scope) }),
2610
+ /* @__PURE__ */ jsx28("span", { children: "\xB7" }),
2611
+ /* @__PURE__ */ jsx28("span", { children: entry.sourceId })
2542
2612
  ] })
2543
2613
  ] }, entry.key)) }) }),
2544
- /* @__PURE__ */ jsxs25(Panel, { title: selected ? selected.resource.title : "Select a resource", actions: selected?.local ? /* @__PURE__ */ jsxs25("div", { style: ROW, children: [
2545
- /* @__PURE__ */ jsx27("button", { className: "btn secondary", type: "button", disabled: saving, onClick: () => update({ status: selected.resource.status === "archived" ? "draft" : "archived" }), children: selected.resource.status === "archived" ? "Restore draft" : "Archive" }),
2546
- /* @__PURE__ */ jsx27("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save changes" })
2614
+ /* @__PURE__ */ jsxs26(Panel, { title: selected ? selected.resource.title : "Select a resource", actions: selected?.local ? /* @__PURE__ */ jsxs26("div", { style: ROW, children: [
2615
+ /* @__PURE__ */ jsx28("button", { className: "btn secondary", type: "button", disabled: saving, onClick: () => update({ status: selected.resource.status === "archived" ? "draft" : "archived" }), children: selected.resource.status === "archived" ? "Restore draft" : "Archive" }),
2616
+ /* @__PURE__ */ jsx28("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save changes" })
2547
2617
  ] }) : null, children: [
2548
- !selected ? /* @__PURE__ */ jsx27("p", { className: "muted", children: "Add a resource or choose one from the list." }) : selected.local ? /* @__PURE__ */ jsx27(LocalEditor, { selected: selected.resource, data, scopeValue, saving, update, setResources, setSelectedKey }) : /* @__PURE__ */ jsxs25("div", { style: STACK, children: [
2549
- /* @__PURE__ */ jsxs25("div", { style: META, children: [
2618
+ !selected ? /* @__PURE__ */ jsx28("p", { className: "muted", children: "Add a resource or choose one from the list." }) : selected.local ? /* @__PURE__ */ jsx28(LocalEditor, { selected: selected.resource, data, scopeValue, saving, update, setResources, setSelectedKey }) : /* @__PURE__ */ jsxs26("div", { style: STACK, children: [
2619
+ /* @__PURE__ */ jsxs26("div", { style: META, children: [
2550
2620
  "Read only \xB7 supplied by ",
2551
2621
  selected.sourceId,
2552
2622
  " \xB7 source revision ",
2553
2623
  selected.sourceRevision
2554
2624
  ] }),
2555
- /* @__PURE__ */ jsxs25("div", { children: [
2556
- /* @__PURE__ */ jsx27("span", { className: "badge", children: STATUS_LABEL[selected.resource.status] }),
2625
+ /* @__PURE__ */ jsxs26("div", { children: [
2626
+ /* @__PURE__ */ jsx28("span", { className: "badge", children: STATUS_LABEL[selected.resource.status] }),
2557
2627
  " ",
2558
- /* @__PURE__ */ jsx27("span", { className: "badge", children: scopeLabel(selected.resource.scope) })
2628
+ /* @__PURE__ */ jsx28("span", { className: "badge", children: scopeLabel(selected.resource.scope) })
2559
2629
  ] }),
2560
- /* @__PURE__ */ jsx27(ResourcePreview, { item: selected.resource }),
2561
- data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */ jsx27("button", { className: "btn secondary", type: "button", onClick: () => void promote(selected), children: "Copy to all chapters" }) : null
2630
+ /* @__PURE__ */ jsx28(ResourcePreview, { item: selected.resource }),
2631
+ data.mode === "hub" && selected.resource.scope.kind === "chapter" ? /* @__PURE__ */ jsx28("button", { className: "btn secondary", type: "button", onClick: () => void promote(selected), children: "Copy to all chapters" }) : null
2562
2632
  ] }),
2563
- saveError ? /* @__PURE__ */ jsxs25("div", { className: "notice danger", role: "alert", children: [
2564
- /* @__PURE__ */ jsx27("strong", { children: "Fix these items before saving:" }),
2565
- /* @__PURE__ */ jsx27("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx27("li", { children: error }, error)) })
2633
+ saveError ? /* @__PURE__ */ jsxs26("div", { className: "notice danger", role: "alert", children: [
2634
+ /* @__PURE__ */ jsx28("strong", { children: "Fix these items before saving:" }),
2635
+ /* @__PURE__ */ jsx28("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx28("li", { children: error }, error)) })
2566
2636
  ] }) : null
2567
2637
  ] })
2568
2638
  ] })
@@ -2570,68 +2640,68 @@ function BuilderLibraryAdmin({ ctx, initialData }) {
2570
2640
  }
2571
2641
  function LocalEditor({ selected, data, scopeValue, update, setResources, setSelectedKey }) {
2572
2642
  const uid = useId2();
2573
- return /* @__PURE__ */ jsxs25("div", { style: STACK, children: [
2574
- /* @__PURE__ */ jsxs25("div", { style: META, children: [
2643
+ return /* @__PURE__ */ jsxs26("div", { style: STACK, children: [
2644
+ /* @__PURE__ */ jsxs26("div", { style: META, children: [
2575
2645
  "Local \xB7 resource revision ",
2576
2646
  selected.revision,
2577
2647
  " \xB7 library revision ",
2578
2648
  data.local?.revision ?? 0
2579
2649
  ] }),
2580
- /* @__PURE__ */ jsxs25("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
2581
- /* @__PURE__ */ jsx27(Field11, { label: "Title", htmlFor: `${uid}-title`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-title`, value: selected.title, onInput: (event) => update({ title: event.currentTarget.value }) }) }),
2582
- /* @__PURE__ */ jsx27(Field11, { label: "ID", htmlFor: `${uid}-id`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-id`, value: selected.id, onInput: (event) => {
2650
+ /* @__PURE__ */ jsxs26("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
2651
+ /* @__PURE__ */ jsx28(Field11, { label: "Title", htmlFor: `${uid}-title`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-title`, value: selected.title, onInput: (event) => update({ title: event.currentTarget.value }) }) }),
2652
+ /* @__PURE__ */ jsx28(Field11, { label: "ID", htmlFor: `${uid}-id`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-id`, value: selected.id, onInput: (event) => {
2583
2653
  const id = cleanId(event.currentTarget.value);
2584
2654
  setResources((items) => items.map((item) => item.id === selected.id ? { ...item, id } : item));
2585
2655
  setSelectedKey(localKey(id));
2586
2656
  } }) }),
2587
- /* @__PURE__ */ jsx27(Field11, { label: "Type", htmlFor: `${uid}-kind`, children: /* @__PURE__ */ jsx27(Select9, { id: `${uid}-kind`, value: selected.kind, onChange: (event) => {
2657
+ /* @__PURE__ */ jsx28(Field11, { label: "Type", htmlFor: `${uid}-kind`, children: /* @__PURE__ */ jsx28(Select10, { id: `${uid}-kind`, value: selected.kind, onChange: (event) => {
2588
2658
  const kind = event.currentTarget.value;
2589
2659
  update({ kind, bodyMarkdown: kind === "document" || kind === "note" ? selected.bodyMarkdown ?? "Start writing\u2026" : void 0, url: kind === "link" || kind === "inspiration" ? selected.url ?? "https://example.com" : void 0, brandAsset: kind === "image" || kind === "file" ? selected.brandAsset ?? { bookId: "brand-book", assetId: "replace-me" } : void 0 });
2590
- }, children: KINDS.map((kind) => /* @__PURE__ */ jsx27("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }) }),
2591
- /* @__PURE__ */ jsx27(Field11, { label: "Status", htmlFor: `${uid}-status`, children: /* @__PURE__ */ jsxs25(Select9, { id: `${uid}-status`, value: selected.status, onChange: (event) => update({ status: event.currentTarget.value }), children: [
2592
- /* @__PURE__ */ jsx27("option", { value: "draft", children: "Draft" }),
2593
- /* @__PURE__ */ jsx27("option", { value: "published", children: "Published" }),
2594
- /* @__PURE__ */ jsx27("option", { value: "archived", children: "Archived" })
2660
+ }, children: KINDS.map((kind) => /* @__PURE__ */ jsx28("option", { value: kind, children: KIND_LABEL[kind] }, kind)) }) }),
2661
+ /* @__PURE__ */ jsx28(Field11, { label: "Status", htmlFor: `${uid}-status`, children: /* @__PURE__ */ jsxs26(Select10, { id: `${uid}-status`, value: selected.status, onChange: (event) => update({ status: event.currentTarget.value }), children: [
2662
+ /* @__PURE__ */ jsx28("option", { value: "draft", children: "Draft" }),
2663
+ /* @__PURE__ */ jsx28("option", { value: "published", children: "Published" }),
2664
+ /* @__PURE__ */ jsx28("option", { value: "archived", children: "Archived" })
2595
2665
  ] }) }),
2596
- /* @__PURE__ */ jsx27(Field11, { label: "Visible to", htmlFor: `${uid}-scope`, children: /* @__PURE__ */ jsxs25(Select9, { id: `${uid}-scope`, value: scopeValue, onChange: (event) => {
2666
+ /* @__PURE__ */ jsx28(Field11, { label: "Visible to", htmlFor: `${uid}-scope`, children: /* @__PURE__ */ jsxs26(Select10, { id: `${uid}-scope`, value: scopeValue, onChange: (event) => {
2597
2667
  const value = event.currentTarget.value;
2598
2668
  update({ scope: value === "global" ? { kind: "global" } : value === "built_not_found" ? { kind: "built_not_found" } : { kind: "chapter", chapterId: value.slice("chapter:".length) } });
2599
2669
  }, children: [
2600
- data.mode === "hub" ? /* @__PURE__ */ jsx27("option", { value: "built_not_found", children: "Built Not Found only" }) : null,
2601
- data.mode === "hub" ? /* @__PURE__ */ jsx27("option", { value: "global", children: "All chapters" }) : null,
2602
- data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */ jsxs25("option", { value: `chapter:${chapterId}`, children: [
2670
+ data.mode === "hub" ? /* @__PURE__ */ jsx28("option", { value: "built_not_found", children: "Built Not Found only" }) : null,
2671
+ data.mode === "hub" ? /* @__PURE__ */ jsx28("option", { value: "global", children: "All chapters" }) : null,
2672
+ data.chapterTargets.map(({ chapterId }) => /* @__PURE__ */ jsxs26("option", { value: `chapter:${chapterId}`, children: [
2603
2673
  "Only ",
2604
2674
  chapterId
2605
2675
  ] }, chapterId))
2606
2676
  ] }) }),
2607
- /* @__PURE__ */ jsx27(Field11, { label: "Tags", htmlFor: `${uid}-tags`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-tags`, value: selected.tags.join(", "), placeholder: "brand, membership", onInput: (event) => update({ tags: event.currentTarget.value.split(",").map((tag) => tag.trim()).filter(Boolean) }) }) })
2677
+ /* @__PURE__ */ jsx28(Field11, { label: "Tags", htmlFor: `${uid}-tags`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-tags`, value: selected.tags.join(", "), placeholder: "brand, membership", onInput: (event) => update({ tags: event.currentTarget.value.split(",").map((tag) => tag.trim()).filter(Boolean) }) }) })
2608
2678
  ] }),
2609
- /* @__PURE__ */ jsx27(Field11, { label: "Summary", htmlFor: `${uid}-summary`, children: /* @__PURE__ */ jsx27(Textarea3, { id: `${uid}-summary`, rows: 2, value: selected.summary ?? "", onInput: (event) => update({ summary: event.currentTarget.value || void 0 }) }) }),
2610
- selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */ jsx27(Field11, { label: "Markdown", htmlFor: `${uid}-markdown`, children: /* @__PURE__ */ jsx27(Textarea3, { id: `${uid}-markdown`, rows: 12, value: selected.bodyMarkdown ?? "", onInput: (event) => update({ bodyMarkdown: event.currentTarget.value }) }) }) : null,
2611
- selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */ jsx27(Field11, { label: "HTTPS URL", htmlFor: `${uid}-url`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-url`, type: "url", value: selected.url ?? "", onInput: (event) => update({ url: event.currentTarget.value }) }) }) : null,
2612
- selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */ jsxs25("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
2613
- /* @__PURE__ */ jsx27(Field11, { label: "Brand book ID", htmlFor: `${uid}-book`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-book`, value: selected.brandAsset?.bookId ?? "", onInput: (event) => update({ brandAsset: { bookId: event.currentTarget.value, assetId: selected.brandAsset?.assetId ?? "", ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) }) }),
2614
- /* @__PURE__ */ jsx27(Field11, { label: "Asset ID", htmlFor: `${uid}-asset`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-asset`, value: selected.brandAsset?.assetId ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: event.currentTarget.value, ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) }) }),
2615
- /* @__PURE__ */ jsx27(Field11, { label: "Filename (optional)", htmlFor: `${uid}-filename`, children: /* @__PURE__ */ jsx27(Input4, { id: `${uid}-filename`, value: selected.brandAsset?.filename ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: selected.brandAsset?.assetId ?? "", ...event.currentTarget.value ? { filename: event.currentTarget.value } : {} } }) }) })
2679
+ /* @__PURE__ */ jsx28(Field11, { label: "Summary", htmlFor: `${uid}-summary`, children: /* @__PURE__ */ jsx28(Textarea3, { id: `${uid}-summary`, rows: 2, value: selected.summary ?? "", onInput: (event) => update({ summary: event.currentTarget.value || void 0 }) }) }),
2680
+ selected.kind === "document" || selected.kind === "note" ? /* @__PURE__ */ jsx28(Field11, { label: "Markdown", htmlFor: `${uid}-markdown`, children: /* @__PURE__ */ jsx28(Textarea3, { id: `${uid}-markdown`, rows: 12, value: selected.bodyMarkdown ?? "", onInput: (event) => update({ bodyMarkdown: event.currentTarget.value }) }) }) : null,
2681
+ selected.kind === "link" || selected.kind === "inspiration" ? /* @__PURE__ */ jsx28(Field11, { label: "HTTPS URL", htmlFor: `${uid}-url`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-url`, type: "url", value: selected.url ?? "", onInput: (event) => update({ url: event.currentTarget.value }) }) }) : null,
2682
+ selected.kind === "image" || selected.kind === "file" ? /* @__PURE__ */ jsxs26("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [
2683
+ /* @__PURE__ */ jsx28(Field11, { label: "Brand book ID", htmlFor: `${uid}-book`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-book`, value: selected.brandAsset?.bookId ?? "", onInput: (event) => update({ brandAsset: { bookId: event.currentTarget.value, assetId: selected.brandAsset?.assetId ?? "", ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) }) }),
2684
+ /* @__PURE__ */ jsx28(Field11, { label: "Asset ID", htmlFor: `${uid}-asset`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-asset`, value: selected.brandAsset?.assetId ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: event.currentTarget.value, ...selected.brandAsset?.filename ? { filename: selected.brandAsset.filename } : {} } }) }) }),
2685
+ /* @__PURE__ */ jsx28(Field11, { label: "Filename (optional)", htmlFor: `${uid}-filename`, children: /* @__PURE__ */ jsx28(Input4, { id: `${uid}-filename`, value: selected.brandAsset?.filename ?? "", onInput: (event) => update({ brandAsset: { bookId: selected.brandAsset?.bookId ?? "", assetId: selected.brandAsset?.assetId ?? "", ...event.currentTarget.value ? { filename: event.currentTarget.value } : {} } }) }) })
2616
2686
  ] }) : null,
2617
- /* @__PURE__ */ jsx27(ResourcePreview, { item: selected })
2687
+ /* @__PURE__ */ jsx28(ResourcePreview, { item: selected })
2618
2688
  ] });
2619
2689
  }
2620
2690
  function builderLibrarySection() {
2621
- return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */ jsx27(BuilderLibraryAdmin, { ctx }) };
2691
+ return { id: "builder-library", label: "Builder Library", render: (ctx) => /* @__PURE__ */ jsx28(BuilderLibraryAdmin, { ctx }) };
2622
2692
  }
2623
2693
 
2624
2694
  // src/ui/admin-email.tsx
2625
- import { useState as useState24 } from "preact/hooks";
2695
+ import { useState as useState25 } from "preact/hooks";
2626
2696
  import { Button as Button16, Checkbox as Checkbox2, DataTable as DataTable3, Field as Field13, Input as Input6, Textarea as Textarea5 } from "@odla-ai/ui/components";
2627
2697
 
2628
2698
  // src/ui/admin-email-resolution.tsx
2629
- import { useState as useState23 } from "preact/hooks";
2699
+ import { useState as useState24 } from "preact/hooks";
2630
2700
  import { Button as Button15, Field as Field12, Input as Input5, Textarea as Textarea4 } from "@odla-ai/ui/components";
2631
- import { jsx as jsx28, jsxs as jsxs26 } from "preact/jsx-runtime";
2701
+ import { jsx as jsx29, jsxs as jsxs27 } from "preact/jsx-runtime";
2632
2702
  function EmailDeliveryResolution({ row, ctx, onResolved }) {
2633
- const [open, setOpen] = useState23(false), [reason, setReason] = useState23(""), [messageId, setMessageId] = useState23("");
2634
- const [busy, setBusy] = useState23(false), [note, setNote] = useState23("");
2703
+ const [open, setOpen] = useState24(false), [reason, setReason] = useState24(""), [messageId, setMessageId] = useState24("");
2704
+ const [busy, setBusy] = useState24(false), [note, setNote] = useState24("");
2635
2705
  if (!["sending", "uncertain"].includes(row.deliveryState ?? "") || !row.deliveryAttempt) return null;
2636
2706
  const resolve2 = async (decision) => {
2637
2707
  setBusy(true);
@@ -2655,45 +2725,45 @@ function EmailDeliveryResolution({ row, ctx, onResolved }) {
2655
2725
  setBusy(false);
2656
2726
  }
2657
2727
  };
2658
- if (!open) return /* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", onClick: () => setOpen(true), children: "Review delivery" });
2659
- return /* @__PURE__ */ jsxs26("div", { children: [
2660
- /* @__PURE__ */ jsxs26("p", { children: [
2728
+ if (!open) return /* @__PURE__ */ jsx29(Button15, { mini: true, variant: "secondary", onClick: () => setOpen(true), children: "Review delivery" });
2729
+ return /* @__PURE__ */ jsxs27("div", { children: [
2730
+ /* @__PURE__ */ jsxs27("p", { children: [
2661
2731
  "Attempt ",
2662
2732
  row.deliveryAttempt,
2663
2733
  ": ",
2664
2734
  row.subject,
2665
2735
  ". Check provider evidence, not just whether the message is in an inbox. This action does not send mail."
2666
2736
  ] }),
2667
- /* @__PURE__ */ jsx28(Field12, { label: "Provider evidence", children: /* @__PURE__ */ jsx28(Textarea4, { value: reason, onInput: (e) => setReason(e.target.value) }) }),
2668
- /* @__PURE__ */ jsx28(Field12, { label: "Provider message ID (optional)", children: /* @__PURE__ */ jsx28(Input5, { value: messageId, onInput: (e) => setMessageId(e.target.value) }) }),
2669
- /* @__PURE__ */ jsx28(Button15, { mini: true, disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_delivered"), children: "Confirm delivered" }),
2670
- /* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_not_delivered"), children: "Confirm not delivered" }),
2671
- /* @__PURE__ */ jsx28(Button15, { mini: true, variant: "secondary", disabled: busy, onClick: () => setOpen(false), children: "Cancel" }),
2672
- note ? /* @__PURE__ */ jsx28("p", { role: "alert", children: note }) : null
2737
+ /* @__PURE__ */ jsx29(Field12, { label: "Provider evidence", children: /* @__PURE__ */ jsx29(Textarea4, { value: reason, onInput: (e) => setReason(e.target.value) }) }),
2738
+ /* @__PURE__ */ jsx29(Field12, { label: "Provider message ID (optional)", children: /* @__PURE__ */ jsx29(Input5, { value: messageId, onInput: (e) => setMessageId(e.target.value) }) }),
2739
+ /* @__PURE__ */ jsx29(Button15, { mini: true, disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_delivered"), children: "Confirm delivered" }),
2740
+ /* @__PURE__ */ jsx29(Button15, { mini: true, variant: "secondary", disabled: busy || reason.trim().length < 10, onClick: () => void resolve2("confirmed_not_delivered"), children: "Confirm not delivered" }),
2741
+ /* @__PURE__ */ jsx29(Button15, { mini: true, variant: "secondary", disabled: busy, onClick: () => setOpen(false), children: "Cancel" }),
2742
+ note ? /* @__PURE__ */ jsx29("p", { role: "alert", children: note }) : null
2673
2743
  ] });
2674
2744
  }
2675
2745
 
2676
2746
  // src/ui/admin-email.tsx
2677
- import { Fragment as Fragment12, jsx as jsx29, jsxs as jsxs27 } from "preact/jsx-runtime";
2747
+ import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs28 } from "preact/jsx-runtime";
2678
2748
  var logColumns = (copy, ctx, refresh) => [
2679
2749
  { key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
2680
2750
  { key: "template", header: copy.templateColumn, cell: (r) => r.template },
2681
2751
  { key: "to", header: copy.toColumn, cell: (r) => r.to },
2682
- { key: "status", header: copy.statusColumn, cell: (r) => /* @__PURE__ */ jsxs27("div", { children: [
2683
- r.deliveryState && r.deliveryState !== "sent" ? /* @__PURE__ */ jsx29("span", { className: "badge", children: r.deliveryState === "retry_ready" ? "Original workflow may retry" : r.deliveryState === "sending" ? "Sending \u2014 unconfirmed" : "Delivery uncertain" }) : r.error ? /* @__PURE__ */ jsx29("span", { className: "badge", children: copy.failed }) : r.transport === "log-only" ? "Logged only \u2014 not sent" : r.redirected ? /* @__PURE__ */ jsx29("span", { className: "badge", children: copy.redirected }) : copy.delivered,
2684
- /* @__PURE__ */ jsx29(EmailDeliveryResolution, { row: r, ctx, onResolved: refresh })
2752
+ { key: "status", header: copy.statusColumn, cell: (r) => /* @__PURE__ */ jsxs28("div", { children: [
2753
+ r.deliveryState && r.deliveryState !== "sent" ? /* @__PURE__ */ jsx30("span", { className: "badge", children: r.deliveryState === "retry_ready" ? "Original workflow may retry" : r.deliveryState === "sending" ? "Sending \u2014 unconfirmed" : "Delivery uncertain" }) : r.error ? /* @__PURE__ */ jsx30("span", { className: "badge", children: copy.failed }) : r.transport === "log-only" ? "Logged only \u2014 not sent" : r.redirected ? /* @__PURE__ */ jsx30("span", { className: "badge", children: copy.redirected }) : copy.delivered,
2754
+ /* @__PURE__ */ jsx30(EmailDeliveryResolution, { row: r, ctx, onResolved: refresh })
2685
2755
  ] }) }
2686
2756
  ];
2687
2757
  function EmailBody({ ctx }) {
2688
2758
  const copy = useChapterCopy().admin.email;
2689
2759
  const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
2690
2760
  const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
2691
- const [draft, setDraft] = useState24(null);
2692
- const [busy, setBusy] = useState24(null);
2693
- const [note, setNote] = useState24(null);
2761
+ const [draft, setDraft] = useState25(null);
2762
+ const [busy, setBusy] = useState25(null);
2763
+ const [note, setNote] = useState25(null);
2694
2764
  const data = draft ?? cfg.data;
2695
- if (cfg.loading) return /* @__PURE__ */ jsx29(AdminNote, { children: copy.loading });
2696
- if (cfg.error || !data) return /* @__PURE__ */ jsxs27(AdminNote, { children: [
2765
+ if (cfg.loading) return /* @__PURE__ */ jsx30(AdminNote, { children: copy.loading });
2766
+ if (cfg.error || !data) return /* @__PURE__ */ jsxs28(AdminNote, { children: [
2697
2767
  copy.loadFailed,
2698
2768
  cfg.error ? `: ${cfg.error}` : "",
2699
2769
  "."
@@ -2726,11 +2796,11 @@ function EmailBody({ ctx }) {
2726
2796
  }) : copy.testFailed);
2727
2797
  log.refresh();
2728
2798
  });
2729
- return /* @__PURE__ */ jsxs27(AdminPage, { children: [
2730
- /* @__PURE__ */ jsxs27(Panel, { title: "Email readiness", actions: /* @__PURE__ */ jsx29(Button16, { variant: "secondary", mini: true, disabled: !!draft, onClick: () => cfg.refresh(), children: "Check again" }), children: [
2731
- /* @__PURE__ */ jsx29("p", { children: "Each event is checked against its saved email and current tier names when this page opens. Missing copy or required values blocks delivery." }),
2732
- data.validation ? /* @__PURE__ */ jsxs27(Fragment12, { children: [
2733
- /* @__PURE__ */ jsxs27("p", { children: [
2799
+ return /* @__PURE__ */ jsxs28(AdminPage, { children: [
2800
+ /* @__PURE__ */ jsxs28(Panel, { title: "Email readiness", actions: /* @__PURE__ */ jsx30(Button16, { variant: "secondary", mini: true, disabled: !!draft, onClick: () => cfg.refresh(), children: "Check again" }), children: [
2801
+ /* @__PURE__ */ jsx30("p", { children: "Each event is checked against its saved email and current tier names when this page opens. Missing copy or required values blocks delivery." }),
2802
+ data.validation ? /* @__PURE__ */ jsxs28(Fragment12, { children: [
2803
+ /* @__PURE__ */ jsxs28("p", { children: [
2734
2804
  data.validation.events.filter((event) => event.status === "blocked").length,
2735
2805
  " blocked \xB7 ",
2736
2806
  data.validation.events.filter((event) => event.status === "disabled").length,
@@ -2738,78 +2808,78 @@ function EmailBody({ ctx }) {
2738
2808
  data.validation.events.filter((event) => event.status === "ready").length,
2739
2809
  " ready"
2740
2810
  ] }),
2741
- data.validation.issues.length ? /* @__PURE__ */ jsx29("ul", { role: "alert", children: data.validation.issues.map((issue, i) => /* @__PURE__ */ jsx29("li", { children: issue.message }, i)) }) : null,
2742
- data.validation.warnings?.length ? /* @__PURE__ */ jsx29("ul", { children: data.validation.warnings.map((warning, i) => /* @__PURE__ */ jsxs27("li", { children: [
2811
+ data.validation.issues.length ? /* @__PURE__ */ jsx30("ul", { role: "alert", children: data.validation.issues.map((issue, i) => /* @__PURE__ */ jsx30("li", { children: issue.message }, i)) }) : null,
2812
+ data.validation.warnings?.length ? /* @__PURE__ */ jsx30("ul", { children: data.validation.warnings.map((warning, i) => /* @__PURE__ */ jsxs28("li", { children: [
2743
2813
  copy.spamRisk,
2744
2814
  ": ",
2745
2815
  warning.message
2746
2816
  ] }, i)) }) : null,
2747
- draft ? /* @__PURE__ */ jsx29("p", { role: "status", children: "Save your changes to refresh these checks." }) : /* @__PURE__ */ jsxs27("p", { className: "muted", children: [
2817
+ draft ? /* @__PURE__ */ jsx30("p", { role: "status", children: "Save your changes to refresh these checks." }) : /* @__PURE__ */ jsxs28("p", { className: "muted", children: [
2748
2818
  "Checked ",
2749
2819
  new Date(data.validation.checkedAt).toLocaleString()
2750
2820
  ] })
2751
- ] }) : /* @__PURE__ */ jsx29("p", { role: "alert", children: "Email validation is unavailable. Refresh after the server has been updated." })
2821
+ ] }) : /* @__PURE__ */ jsx30("p", { role: "alert", children: "Email validation is unavailable. Refresh after the server has been updated." })
2752
2822
  ] }),
2753
- /* @__PURE__ */ jsxs27(
2823
+ /* @__PURE__ */ jsxs28(
2754
2824
  Panel,
2755
2825
  {
2756
2826
  title: copy.delivery,
2757
- actions: /* @__PURE__ */ jsxs27("span", { className: "muted", children: [
2827
+ actions: /* @__PURE__ */ jsxs28("span", { className: "muted", children: [
2758
2828
  data.envName,
2759
2829
  " \xB7 ",
2760
2830
  data.transport,
2761
2831
  data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
2762
2832
  ] }),
2763
2833
  children: [
2764
- /* @__PURE__ */ jsx29(Field13, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx29(Input6, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
2765
- /* @__PURE__ */ jsx29(Field13, { label: copy.replyTo, children: /* @__PURE__ */ jsx29(Input6, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
2766
- data.testAvailable ? /* @__PURE__ */ jsx29(Field13, { label: copy.fromName, hint: copy.fromNameHint, children: /* @__PURE__ */ jsx29(Input6, { value: data.fromName ?? "", onInput: (e) => edit({ fromName: e.target.value }) }) }) : null,
2767
- /* @__PURE__ */ jsx29(Field13, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx29(Input6, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
2834
+ /* @__PURE__ */ jsx30(Field13, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx30(Input6, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
2835
+ /* @__PURE__ */ jsx30(Field13, { label: copy.replyTo, children: /* @__PURE__ */ jsx30(Input6, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
2836
+ data.testAvailable ? /* @__PURE__ */ jsx30(Field13, { label: copy.fromName, hint: copy.fromNameHint, children: /* @__PURE__ */ jsx30(Input6, { value: data.fromName ?? "", onInput: (e) => edit({ fromName: e.target.value }) }) }) : null,
2837
+ /* @__PURE__ */ jsx30(Field13, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx30(Input6, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
2768
2838
  ]
2769
2839
  }
2770
2840
  ),
2771
2841
  (data.validation?.events ?? []).map((event) => {
2772
2842
  const key = event.key, t = data.emailTemplates[key] ?? { subject: "", text: "", enabled: false };
2773
- return /* @__PURE__ */ jsxs27(Panel, { title: event.label, actions: /* @__PURE__ */ jsx29("span", { children: event.status === "ready" ? "Ready" : event.status === "disabled" ? "Switched off" : "Blocked" }), children: [
2774
- /* @__PURE__ */ jsxs27("p", { children: [
2843
+ return /* @__PURE__ */ jsxs28(Panel, { title: event.label, actions: /* @__PURE__ */ jsx30("span", { children: event.status === "ready" ? "Ready" : event.status === "disabled" ? "Switched off" : "Blocked" }), children: [
2844
+ /* @__PURE__ */ jsxs28("p", { children: [
2775
2845
  event.trigger,
2776
2846
  ". Recipient: ",
2777
2847
  event.audience,
2778
2848
  "."
2779
2849
  ] }),
2780
- event.issues.length ? /* @__PURE__ */ jsx29("ul", { children: event.issues.map((issue, i) => /* @__PURE__ */ jsx29("li", { children: issue.message }, i)) }) : null,
2781
- event.warnings?.length ? /* @__PURE__ */ jsx29("ul", { className: "muted", children: event.warnings.map((warning, i) => /* @__PURE__ */ jsxs27("li", { children: [
2850
+ event.issues.length ? /* @__PURE__ */ jsx30("ul", { children: event.issues.map((issue, i) => /* @__PURE__ */ jsx30("li", { children: issue.message }, i)) }) : null,
2851
+ event.warnings?.length ? /* @__PURE__ */ jsx30("ul", { className: "muted", children: event.warnings.map((warning, i) => /* @__PURE__ */ jsxs28("li", { children: [
2782
2852
  copy.spamRisk,
2783
2853
  ": ",
2784
2854
  warning.message
2785
2855
  ] }, i)) }) : null,
2786
- /* @__PURE__ */ jsx29(Checkbox2, { label: copy.enabled, checked: t.enabled === true, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
2787
- /* @__PURE__ */ jsx29(Field13, { label: copy.subject, children: /* @__PURE__ */ jsx29(Input6, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
2788
- /* @__PURE__ */ jsx29(Field13, { label: copy.body, children: /* @__PURE__ */ jsx29(Textarea5, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) }),
2789
- /* @__PURE__ */ jsx29(Field13, { label: copy.htmlBody, hint: copy.htmlBodyHint, children: /* @__PURE__ */ jsx29(Textarea5, { rows: 6, value: t.html ?? "", onInput: (e) => editTemplate(key, { html: e.target.value }) }) }),
2790
- /* @__PURE__ */ jsxs27("p", { className: "muted", children: [
2856
+ /* @__PURE__ */ jsx30(Checkbox2, { label: copy.enabled, checked: t.enabled === true, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
2857
+ /* @__PURE__ */ jsx30(Field13, { label: copy.subject, children: /* @__PURE__ */ jsx30(Input6, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
2858
+ /* @__PURE__ */ jsx30(Field13, { label: copy.body, children: /* @__PURE__ */ jsx30(Textarea5, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) }),
2859
+ /* @__PURE__ */ jsx30(Field13, { label: copy.htmlBody, hint: copy.htmlBodyHint, children: /* @__PURE__ */ jsx30(Textarea5, { rows: 6, value: t.html ?? "", onInput: (e) => editTemplate(key, { html: e.target.value }) }) }),
2860
+ /* @__PURE__ */ jsxs28("p", { className: "muted", children: [
2791
2861
  "Available values: ",
2792
2862
  event.variables.map((name) => `{{${name}}}`).join(", ")
2793
2863
  ] }),
2794
- data.testAvailable ? /* @__PURE__ */ jsx29(Button16, { variant: "secondary", mini: true, disabled: !!draft || event.issues.length > 0 || busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }) : null
2864
+ data.testAvailable ? /* @__PURE__ */ jsx30(Button16, { variant: "secondary", mini: true, disabled: !!draft || event.issues.length > 0 || busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }) : null
2795
2865
  ] }, key);
2796
2866
  }),
2797
- /* @__PURE__ */ jsxs27("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
2798
- /* @__PURE__ */ jsx29(Button16, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
2799
- note ? /* @__PURE__ */ jsx29("span", { className: "muted", children: note }) : null
2867
+ /* @__PURE__ */ jsxs28("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
2868
+ /* @__PURE__ */ jsx30(Button16, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
2869
+ note ? /* @__PURE__ */ jsx30("span", { className: "muted", children: note }) : null
2800
2870
  ] }),
2801
- /* @__PURE__ */ jsx29(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx29(DataTable3, { rows: log.data?.sends ?? [], columns: logColumns(copy, ctx, log.refresh), rowKey: (r) => r.id }) })
2871
+ /* @__PURE__ */ jsx30(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx30(DataTable3, { rows: log.data?.sends ?? [], columns: logColumns(copy, ctx, log.refresh), rowKey: (r) => r.id }) })
2802
2872
  ] });
2803
2873
  }
2804
2874
  function emailSection(options = {}) {
2805
2875
  const { id = "email", label = "Email" } = options;
2806
- return { id, label, render: (ctx) => /* @__PURE__ */ jsx29(EmailBody, { ctx }) };
2876
+ return { id, label, render: (ctx) => /* @__PURE__ */ jsx30(EmailBody, { ctx }) };
2807
2877
  }
2808
2878
 
2809
2879
  // src/ui/admin-member-content.tsx
2810
- import { useEffect as useEffect10, useState as useState25 } from "preact/hooks";
2811
- import { jsx as jsx30, jsxs as jsxs28 } from "preact/jsx-runtime";
2812
- var emptyDraft = () => ({ pages: [], upsellOverrides: [] });
2880
+ import { useEffect as useEffect11, useState as useState26 } from "preact/hooks";
2881
+ import { jsx as jsx31, jsxs as jsxs29 } from "preact/jsx-runtime";
2882
+ var emptyDraft = () => ({ schemaVersion: 2, sections: [], pages: [], upsellOverrides: [] });
2813
2883
  function accessLabel(page) {
2814
2884
  return page.audience.kind === "all-members" ? "All members" : `Tiers: ${page.audience.tierIds.join(", ")}`;
2815
2885
  }
@@ -2818,17 +2888,17 @@ function MemberContentAdmin({
2818
2888
  initialData
2819
2889
  }) {
2820
2890
  const resource = useAdminResource(ctx.getToken, "/api/admin/member-content");
2821
- const [draft, setDraft] = useState25(JSON.stringify(initialData?.chapter?.document ?? emptyDraft(), null, 2));
2822
- const [saveError, setSaveError] = useState25(null);
2823
- const [saving, setSaving] = useState25(false);
2824
- useEffect10(() => {
2891
+ const [draft, setDraft] = useState26(JSON.stringify(initialData?.chapter?.document ?? emptyDraft(), null, 2));
2892
+ const [saveError, setSaveError] = useState26(null);
2893
+ const [saving, setSaving] = useState26(false);
2894
+ useEffect11(() => {
2825
2895
  if (!resource.data) return;
2826
2896
  setDraft(JSON.stringify(resource.data.chapter?.document ?? emptyDraft(), null, 2));
2827
2897
  }, [resource.data]);
2828
2898
  const loaded = initialData ?? resource.data;
2829
- if (!loaded && resource.loading) return /* @__PURE__ */ jsx30(AdminPage, { children: /* @__PURE__ */ jsx30(AdminNote, { children: "Loading member content\u2026" }) });
2899
+ if (!loaded && resource.loading) return /* @__PURE__ */ jsx31(AdminPage, { children: /* @__PURE__ */ jsx31(AdminNote, { children: "Loading member content\u2026" }) });
2830
2900
  if (resource.error || !loaded) {
2831
- return /* @__PURE__ */ jsx30(AdminPage, { children: /* @__PURE__ */ jsxs28(AdminNote, { children: [
2901
+ return /* @__PURE__ */ jsx31(AdminPage, { children: /* @__PURE__ */ jsxs29(AdminNote, { children: [
2832
2902
  "Member content could not be loaded: ",
2833
2903
  resource.error ?? "unknown error"
2834
2904
  ] }) });
@@ -2859,35 +2929,35 @@ function MemberContentAdmin({
2859
2929
  setSaving(false);
2860
2930
  }
2861
2931
  };
2862
- return /* @__PURE__ */ jsxs28(AdminPage, { children: [
2863
- /* @__PURE__ */ jsxs28(Panel, { children: [
2864
- /* @__PURE__ */ jsx30("div", { className: "card-label", children: "Member content" }),
2865
- /* @__PURE__ */ jsx30("h2", { children: "One catalog, two clear owners" }),
2866
- /* @__PURE__ */ jsx30("p", { children: "Parent pages are inherited and read-only here. Chapter pages and delegated upsells are edited in the local document. Access uses exact tier IDs; no tier order is assumed." }),
2867
- /* @__PURE__ */ jsxs28("div", { className: "stat-band", children: [
2868
- /* @__PURE__ */ jsxs28("div", { children: [
2869
- /* @__PURE__ */ jsx30("strong", { children: inherited.length }),
2870
- /* @__PURE__ */ jsx30("span", { children: "Inherited pages" })
2932
+ return /* @__PURE__ */ jsxs29(AdminPage, { children: [
2933
+ /* @__PURE__ */ jsxs29(Panel, { children: [
2934
+ /* @__PURE__ */ jsx31("div", { className: "card-label", children: "Member content" }),
2935
+ /* @__PURE__ */ jsx31("h2", { children: "One catalog, two clear owners" }),
2936
+ /* @__PURE__ */ jsx31("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." }),
2937
+ /* @__PURE__ */ jsxs29("div", { className: "stat-band", children: [
2938
+ /* @__PURE__ */ jsxs29("div", { children: [
2939
+ /* @__PURE__ */ jsx31("strong", { children: inherited.length }),
2940
+ /* @__PURE__ */ jsx31("span", { children: "Inherited pages" })
2871
2941
  ] }),
2872
- /* @__PURE__ */ jsxs28("div", { children: [
2873
- /* @__PURE__ */ jsx30("strong", { children: local.length }),
2874
- /* @__PURE__ */ jsx30("span", { children: "Chapter pages" })
2942
+ /* @__PURE__ */ jsxs29("div", { children: [
2943
+ /* @__PURE__ */ jsx31("strong", { children: local.length }),
2944
+ /* @__PURE__ */ jsx31("span", { children: "Chapter pages" })
2875
2945
  ] }),
2876
- /* @__PURE__ */ jsxs28("div", { children: [
2877
- /* @__PURE__ */ jsx30("strong", { children: data.tiers.length }),
2878
- /* @__PURE__ */ jsx30("span", { children: "Declared tiers" })
2946
+ /* @__PURE__ */ jsxs29("div", { children: [
2947
+ /* @__PURE__ */ jsx31("strong", { children: data.tiers.length }),
2948
+ /* @__PURE__ */ jsx31("span", { children: "Declared tiers" })
2879
2949
  ] })
2880
2950
  ] })
2881
2951
  ] }),
2882
- /* @__PURE__ */ jsxs28(Panel, { children: [
2883
- /* @__PURE__ */ jsx30("div", { className: "card-label", children: "Inherited \xB7 read only" }),
2884
- /* @__PURE__ */ jsxs28("h2", { children: [
2952
+ /* @__PURE__ */ jsxs29(Panel, { children: [
2953
+ /* @__PURE__ */ jsx31("div", { className: "card-label", children: "Inherited \xB7 read only" }),
2954
+ /* @__PURE__ */ jsxs29("h2", { children: [
2885
2955
  "Parent pages ",
2886
2956
  data.parent ? `\xB7 revision ${data.parent.revision}` : ""
2887
2957
  ] }),
2888
- inherited.length === 0 ? /* @__PURE__ */ jsx30(AdminNote, { children: "No Parent pages have been delivered." }) : /* @__PURE__ */ jsx30("div", { className: "data-list", "data-member-content-inherited": true, children: inherited.map((page) => /* @__PURE__ */ jsxs28("article", { className: "card", children: [
2889
- /* @__PURE__ */ jsx30("strong", { children: page.title }),
2890
- /* @__PURE__ */ jsxs28("div", { className: "meeting-note", children: [
2958
+ inherited.length === 0 ? /* @__PURE__ */ jsx31(AdminNote, { children: "No Parent pages have been delivered." }) : /* @__PURE__ */ jsx31("div", { className: "data-list", "data-member-content-inherited": true, children: inherited.map((page) => /* @__PURE__ */ jsxs29("article", { className: "card", children: [
2959
+ /* @__PURE__ */ jsx31("strong", { children: page.title }),
2960
+ /* @__PURE__ */ jsxs29("div", { className: "meeting-note", children: [
2891
2961
  "/",
2892
2962
  page.slug,
2893
2963
  " \xB7 ",
@@ -2895,22 +2965,22 @@ function MemberContentAdmin({
2895
2965
  " \xB7 ",
2896
2966
  accessLabel(page)
2897
2967
  ] }),
2898
- /* @__PURE__ */ jsxs28("div", { className: "meeting-note", children: [
2968
+ /* @__PURE__ */ jsxs29("div", { className: "meeting-note", children: [
2899
2969
  "Upsell: ",
2900
2970
  page.upsell ? `${page.upsell.owner} controlled` : "none"
2901
2971
  ] })
2902
2972
  ] }, page.id)) })
2903
2973
  ] }),
2904
- /* @__PURE__ */ jsxs28(Panel, { children: [
2905
- /* @__PURE__ */ jsx30("div", { className: "card-label", children: "Chapter \xB7 editable" }),
2906
- /* @__PURE__ */ jsxs28("h2", { children: [
2974
+ /* @__PURE__ */ jsxs29(Panel, { children: [
2975
+ /* @__PURE__ */ jsx31("div", { className: "card-label", children: "Chapter \xB7 editable" }),
2976
+ /* @__PURE__ */ jsxs29("h2", { children: [
2907
2977
  "Local document \xB7 revision ",
2908
2978
  data.chapter?.revision ?? 0
2909
2979
  ] }),
2910
- /* @__PURE__ */ jsx30("p", { className: "meeting-note", children: "Use the published MemberContentDocument fields. The server supplies schemaVersion, revision, timestamp, and chapter source; unknown fields and undeclared tiers are rejected." }),
2911
- /* @__PURE__ */ jsxs28("label", { className: "field", children: [
2912
- /* @__PURE__ */ jsx30("span", { children: "Pages and delegated upsells (JSON)" }),
2913
- /* @__PURE__ */ jsx30(
2980
+ /* @__PURE__ */ jsx31("p", { className: "meeting-note", children: "Use the published MemberContentDocument fields. schemaVersion 2 adds ordered sections, page kinds (markdown, placeholder, feature), tags and icons; omit it for version 1. The server supplies revision, timestamp, and chapter source; unknown fields and undeclared tiers are rejected." }),
2981
+ /* @__PURE__ */ jsxs29("label", { className: "field", children: [
2982
+ /* @__PURE__ */ jsx31("span", { children: "Pages and delegated upsells (JSON)" }),
2983
+ /* @__PURE__ */ jsx31(
2914
2984
  "textarea",
2915
2985
  {
2916
2986
  rows: 24,
@@ -2921,12 +2991,12 @@ function MemberContentAdmin({
2921
2991
  }
2922
2992
  )
2923
2993
  ] }),
2924
- saveError ? /* @__PURE__ */ jsxs28("div", { className: "notice danger", role: "alert", children: [
2925
- /* @__PURE__ */ jsx30("strong", { children: "Fix these items before saving:" }),
2926
- /* @__PURE__ */ jsx30("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx30("li", { children: error }, error)) })
2994
+ saveError ? /* @__PURE__ */ jsxs29("div", { className: "notice danger", role: "alert", children: [
2995
+ /* @__PURE__ */ jsx31("strong", { children: "Fix these items before saving:" }),
2996
+ /* @__PURE__ */ jsx31("ul", { children: saveError.map((error) => /* @__PURE__ */ jsx31("li", { children: error }, error)) })
2927
2997
  ] }) : null,
2928
- /* @__PURE__ */ jsx30("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save chapter content" }),
2929
- !data.localAuthoring ? /* @__PURE__ */ jsx30(AdminNote, { children: "Local authoring is disabled in the checked-in Chapter config." }) : null
2998
+ /* @__PURE__ */ jsx31("button", { className: "btn primary", type: "button", disabled: !data.localAuthoring || saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save chapter content" }),
2999
+ !data.localAuthoring ? /* @__PURE__ */ jsx31(AdminNote, { children: "Local authoring is disabled in the checked-in Chapter config." }) : null
2930
3000
  ] })
2931
3001
  ] });
2932
3002
  }
@@ -2934,12 +3004,397 @@ function memberContentSection() {
2934
3004
  return {
2935
3005
  id: "member-content",
2936
3006
  label: "Member content",
2937
- render: (ctx) => /* @__PURE__ */ jsx30(MemberContentAdmin, { ctx })
3007
+ render: (ctx) => /* @__PURE__ */ jsx31(MemberContentAdmin, { ctx })
3008
+ };
3009
+ }
3010
+
3011
+ // src/ui/admin-member-portal.tsx
3012
+ import { useState as useState29 } from "preact/hooks";
3013
+
3014
+ // src/ui/admin-member-course.tsx
3015
+ import { useEffect as useEffect12, useState as useState27 } from "preact/hooks";
3016
+ import { jsx as jsx32, jsxs as jsxs30 } from "preact/jsx-runtime";
3017
+ var EXAMPLE = { title: "", timezone: "America/Denver", capacity: 20, signupClosesAt: 0, content: {}, sessions: [], toolkit: [], guides: [] };
3018
+ function CourseAdminPanel({ ctx, courseId }) {
3019
+ const path = `/api/admin/courses/${encodeURIComponent(courseId)}`;
3020
+ const view = useAdminResource(ctx.getToken, path);
3021
+ const [draft, setDraft] = useState27("");
3022
+ const [status, setStatus] = useState27(null);
3023
+ useEffect12(() => {
3024
+ const course2 = view.data?.course;
3025
+ const { enrolledCount: _e, revision: _r, updatedAt: _u, updatedBy: _b, calendarEventId: _c, meetUrl: _m, ...settings } = course2 ?? EXAMPLE;
3026
+ setDraft(JSON.stringify(settings, null, 2));
3027
+ }, [view.data?.course?.revision]);
3028
+ const run = async (target, init, done) => {
3029
+ try {
3030
+ await adminFetch(ctx.getToken, target, init);
3031
+ setStatus(done);
3032
+ view.refresh();
3033
+ } catch (error) {
3034
+ const errors = error.errors;
3035
+ setStatus(errors?.length ? errors.join("; ") : error instanceof Error ? error.message : String(error));
3036
+ }
3037
+ };
3038
+ const save = () => {
3039
+ let course2;
3040
+ try {
3041
+ course2 = JSON.parse(draft);
3042
+ } catch {
3043
+ setStatus("Settings must be valid JSON");
3044
+ return;
3045
+ }
3046
+ void run(path, { method: "PUT", body: JSON.stringify({ course: course2, expectedRevision: Number(view.data?.course?.revision ?? 0) }) }, "Saved");
3047
+ };
3048
+ const download = async () => {
3049
+ const token = await ctx.getToken();
3050
+ const res = await fetch(`${path}/roster.csv`, { headers: token ? { authorization: `Bearer ${token}` } : {} });
3051
+ if (!res.ok) {
3052
+ setStatus("Export failed");
3053
+ return;
3054
+ }
3055
+ const url = URL.createObjectURL(await res.blob());
3056
+ Object.assign(document.createElement("a"), { href: url, download: `${courseId}-roster.csv` }).click();
3057
+ URL.revokeObjectURL(url);
3058
+ };
3059
+ const course = view.data?.course;
3060
+ const move = (memberId, action, label) => /* @__PURE__ */ jsx32(
3061
+ "button",
3062
+ {
3063
+ className: "btn secondary mini",
3064
+ type: "button",
3065
+ onClick: () => void run(`${path}/roster/${encodeURIComponent(String(memberId))}/${action}`, { method: "POST" }, `${label} done`),
3066
+ children: label
3067
+ }
3068
+ );
3069
+ return /* @__PURE__ */ jsxs30(Panel, { children: [
3070
+ /* @__PURE__ */ jsxs30("div", { className: "card-label", children: [
3071
+ "Course \xB7 ",
3072
+ courseId
3073
+ ] }),
3074
+ status ? /* @__PURE__ */ jsx32("p", { role: "status", children: status }) : null,
3075
+ course ? /* @__PURE__ */ jsxs30("p", { className: "meeting-note", children: [
3076
+ String(course.title),
3077
+ " \xB7 ",
3078
+ view.data?.seatsLeft,
3079
+ " of ",
3080
+ String(course.capacity),
3081
+ " seats left \xB7",
3082
+ course.calendarEventId ? " calendar event created" : " no calendar event yet"
3083
+ ] }) : /* @__PURE__ */ jsx32("p", { className: "meeting-note", children: "No settings saved yet." }),
3084
+ course && !course.calendarEventId ? /* @__PURE__ */ jsx32("button", { className: "btn primary mini", type: "button", onClick: () => void run(`${path}/calendar-event`, { method: "POST" }, "Calendar event created"), children: "Create calendar event" }) : null,
3085
+ course ? /* @__PURE__ */ jsx32("button", { className: "btn secondary mini", type: "button", onClick: () => void download(), children: "Export roster (CSV)" }) : null,
3086
+ /* @__PURE__ */ jsx32("h3", { children: "Enrolled" }),
3087
+ /* @__PURE__ */ jsx32("ul", { children: (view.data?.enrolled ?? []).map((row) => /* @__PURE__ */ jsxs30("li", { children: [
3088
+ String(row.email),
3089
+ " ",
3090
+ row.status === "pending" ? "(inviting)" : "",
3091
+ " ",
3092
+ move(row.memberId, "remove", "Remove")
3093
+ ] }, String(row.id))) }),
3094
+ /* @__PURE__ */ jsx32("h3", { children: "Waitlist" }),
3095
+ /* @__PURE__ */ jsx32("ol", { children: (view.data?.waitlist ?? []).map((row) => /* @__PURE__ */ jsxs30("li", { children: [
3096
+ String(row.email),
3097
+ " ",
3098
+ move(row.memberId, "promote", "Move up"),
3099
+ " ",
3100
+ move(row.memberId, "remove", "Remove")
3101
+ ] }, String(row.id))) }),
3102
+ /* @__PURE__ */ jsx32("h3", { children: "Settings" }),
3103
+ /* @__PURE__ */ jsx32("p", { className: "meeting-note", children: "Sessions, materials (a material without a link stays hidden), toolkit, guides, seat count and sign-up close. Capacity cannot drop below seats taken." }),
3104
+ /* @__PURE__ */ jsxs30("label", { className: "field", children: [
3105
+ /* @__PURE__ */ jsx32("span", { children: "Course settings (JSON)" }),
3106
+ /* @__PURE__ */ jsx32("textarea", { rows: 18, value: draft, onInput: (event) => setDraft(event.currentTarget.value) })
3107
+ ] }),
3108
+ /* @__PURE__ */ jsx32("button", { className: "btn primary mini", type: "button", onClick: save, children: "Save settings" })
3109
+ ] });
3110
+ }
3111
+ function CoursesAdminPanel({ ctx }) {
3112
+ const list = useAdminResource(ctx.getToken, "/api/admin/courses");
3113
+ const [newId, setNewId] = useState27("");
3114
+ const [open, setOpen] = useState27(null);
3115
+ const ids = [.../* @__PURE__ */ new Set([...(list.data?.courses ?? []).map((row) => String(row.id)), ...open ? [open] : []])];
3116
+ return /* @__PURE__ */ jsxs30("div", { className: "chapter-admin-stack", "data-courses-admin": true, children: [
3117
+ ids.map((id) => /* @__PURE__ */ jsx32(CourseAdminPanel, { ctx, courseId: id }, id)),
3118
+ /* @__PURE__ */ jsxs30(Panel, { children: [
3119
+ /* @__PURE__ */ jsx32("div", { className: "card-label", children: "New course" }),
3120
+ /* @__PURE__ */ jsxs30("label", { className: "field", children: [
3121
+ /* @__PURE__ */ jsx32("span", { children: "Course id (lowercase slug)" }),
3122
+ /* @__PURE__ */ jsx32("input", { value: newId, onInput: (event) => setNewId(event.currentTarget.value.trim()) })
3123
+ ] }),
3124
+ /* @__PURE__ */ jsx32("button", { className: "btn secondary mini", type: "button", disabled: !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(newId), onClick: () => setOpen(newId), children: "Start" })
3125
+ ] })
3126
+ ] });
3127
+ }
3128
+
3129
+ // src/ui/admin-member-referrals.tsx
3130
+ import { useState as useState28 } from "preact/hooks";
3131
+ import { Select as Select11 } from "@odla-ai/ui/components";
3132
+ import { jsx as jsx33, jsxs as jsxs31 } from "preact/jsx-runtime";
3133
+ function ReferralsAdminPanel({ ctx }) {
3134
+ const view = useAdminResource(ctx.getToken, "/api/admin/referrals");
3135
+ const [status, setStatus] = useState28(null);
3136
+ const run = async (path, done) => {
3137
+ try {
3138
+ await adminFetch(ctx.getToken, path, { method: "POST" });
3139
+ setStatus(done);
3140
+ view.refresh();
3141
+ } catch (error) {
3142
+ setStatus(error instanceof Error ? error.message : String(error));
3143
+ }
3144
+ };
3145
+ const data = view.data;
3146
+ return /* @__PURE__ */ jsxs31(Panel, { children: [
3147
+ /* @__PURE__ */ jsx33("div", { className: "card-label", children: "Referrals" }),
3148
+ status ? /* @__PURE__ */ jsx33("p", { role: "status", children: status }) : null,
3149
+ /* @__PURE__ */ jsx33("button", { className: "btn secondary mini", type: "button", onClick: () => void run("/api/admin/referrals/reconcile", "Reconciled approvals and lapses"), children: "Reconcile credit" }),
3150
+ /* @__PURE__ */ jsx33("h3", { children: "Top referrers" }),
3151
+ /* @__PURE__ */ jsx33("ol", { children: (data?.topReferrers ?? []).map((row) => /* @__PURE__ */ jsxs31("li", { children: [
3152
+ row.name ?? row.memberId,
3153
+ ": ",
3154
+ row.joins,
3155
+ " joined, ",
3156
+ row.applications,
3157
+ " applied"
3158
+ ] }, row.memberId)) }),
3159
+ /* @__PURE__ */ jsxs31("table", { children: [
3160
+ /* @__PURE__ */ jsx33("thead", { children: /* @__PURE__ */ jsxs31("tr", { children: [
3161
+ /* @__PURE__ */ jsx33("th", { children: "Member" }),
3162
+ /* @__PURE__ */ jsx33("th", { children: "Codes" }),
3163
+ /* @__PURE__ */ jsx33("th", { children: "Clicks" }),
3164
+ /* @__PURE__ */ jsx33("th", { children: "Applied" }),
3165
+ /* @__PURE__ */ jsx33("th", { children: "Joined" }),
3166
+ /* @__PURE__ */ jsx33("th", {})
3167
+ ] }) }),
3168
+ /* @__PURE__ */ jsx33("tbody", { children: (data?.referrers ?? []).map((row) => /* @__PURE__ */ jsxs31("tr", { children: [
3169
+ /* @__PURE__ */ jsx33("td", { children: row.name ?? row.memberId }),
3170
+ /* @__PURE__ */ jsx33("td", { children: row.codes.join(", ") }),
3171
+ /* @__PURE__ */ jsx33("td", { children: row.clicks }),
3172
+ /* @__PURE__ */ jsx33("td", { children: row.applications }),
3173
+ /* @__PURE__ */ jsx33("td", { children: row.joins }),
3174
+ /* @__PURE__ */ jsx33("td", { children: /* @__PURE__ */ jsx33(
3175
+ "button",
3176
+ {
3177
+ className: "btn secondary mini",
3178
+ type: "button",
3179
+ onClick: () => void run(`/api/admin/referrals/${encodeURIComponent(row.memberId)}/reissue`, `Issued a new code for ${row.name ?? row.memberId}`),
3180
+ children: "Deactivate and reissue"
3181
+ }
3182
+ ) })
3183
+ ] }, row.memberId)) })
3184
+ ] }),
3185
+ /* @__PURE__ */ jsx33("h3", { children: "How people found us" }),
3186
+ /* @__PURE__ */ jsxs31("table", { children: [
3187
+ /* @__PURE__ */ jsx33("thead", { children: /* @__PURE__ */ jsxs31("tr", { children: [
3188
+ /* @__PURE__ */ jsx33("th", { children: "Person" }),
3189
+ /* @__PURE__ */ jsx33("th", { children: "Referred by" }),
3190
+ /* @__PURE__ */ jsx33("th", { children: "Introduced by" })
3191
+ ] }) }),
3192
+ /* @__PURE__ */ jsx33("tbody", { children: (data?.people ?? []).map((row) => {
3193
+ const referred = row.referredBy;
3194
+ const introduced = row.introducedBy;
3195
+ return /* @__PURE__ */ jsxs31("tr", { children: [
3196
+ /* @__PURE__ */ jsx33("td", { children: String(row.name ?? row.memberId) }),
3197
+ /* @__PURE__ */ jsx33("td", { children: referred?.name ?? "" }),
3198
+ /* @__PURE__ */ jsx33("td", { children: introduced ? `${introduced.text}${introduced.name ? ` (matched: ${introduced.name})` : " (unmatched)"}` : "" })
3199
+ ] }, String(row.memberId));
3200
+ }) })
3201
+ ] })
3202
+ ] });
3203
+ }
3204
+ function CreditLedgerPanel({ ctx }) {
3205
+ const view = useAdminResource(ctx.getToken, "/api/admin/referral-ledger");
3206
+ const [form, setForm] = useState28({ referrerMemberId: "", status: "void", valueCents: "", reason: "" });
3207
+ const [status, setStatus] = useState28(null);
3208
+ const add = async () => {
3209
+ try {
3210
+ await adminFetch(ctx.getToken, "/api/admin/referral-ledger", { method: "POST", body: JSON.stringify({ ...form, valueCents: Math.round(Number(form.valueCents) * 100) }) });
3211
+ setStatus("Row added");
3212
+ setForm({ ...form, valueCents: "", reason: "" });
3213
+ view.refresh();
3214
+ } catch (error) {
3215
+ setStatus(error instanceof Error ? error.message : String(error));
3216
+ }
3217
+ };
3218
+ const money6 = (cents) => typeof cents === "number" ? fmtMoney(cents) : "";
3219
+ const set = (key) => (event) => setForm({ ...form, [key]: event.currentTarget.value });
3220
+ return /* @__PURE__ */ jsxs31(Panel, { children: [
3221
+ /* @__PURE__ */ jsx33("div", { className: "card-label", children: "Credit ledger" }),
3222
+ /* @__PURE__ */ jsxs31("table", { children: [
3223
+ /* @__PURE__ */ jsx33("thead", { children: /* @__PURE__ */ jsxs31("tr", { children: [
3224
+ /* @__PURE__ */ jsx33("th", { children: "Member" }),
3225
+ /* @__PURE__ */ jsx33("th", { children: "Earned" }),
3226
+ /* @__PURE__ */ jsx33("th", { children: "Applied" }),
3227
+ /* @__PURE__ */ jsx33("th", { children: "Carried forward" }),
3228
+ /* @__PURE__ */ jsx33("th", { children: "Void" }),
3229
+ /* @__PURE__ */ jsx33("th", { children: "Balance" }),
3230
+ /* @__PURE__ */ jsx33("th", { children: "Next year's dues" })
3231
+ ] }) }),
3232
+ /* @__PURE__ */ jsx33("tbody", { children: (view.data?.members ?? []).map((row) => /* @__PURE__ */ jsxs31("tr", { children: [
3233
+ /* @__PURE__ */ jsx33("td", { children: row.name ?? row.memberId }),
3234
+ /* @__PURE__ */ jsx33("td", { children: money6(row.totals.earnedCents) }),
3235
+ /* @__PURE__ */ jsx33("td", { children: money6(row.totals.appliedCents) }),
3236
+ /* @__PURE__ */ jsx33("td", { children: money6(row.totals.carriedForwardCents) }),
3237
+ /* @__PURE__ */ jsx33("td", { children: money6(row.totals.voidedCents) }),
3238
+ /* @__PURE__ */ jsx33("td", { children: money6(row.totals.balanceCents) }),
3239
+ /* @__PURE__ */ jsx33("td", { children: row.projectedDuesCents === null ? "" : `${money6(row.projectedDuesCents)} (of ${money6(row.renewalCents)})` })
3240
+ ] }, row.memberId)) })
3241
+ ] }),
3242
+ /* @__PURE__ */ jsx33("h3", { children: "Add a row" }),
3243
+ /* @__PURE__ */ jsx33("p", { className: "meeting-note", children: "Rows are never edited. Void or adjust by adding a row with a reason." }),
3244
+ /* @__PURE__ */ jsxs31("label", { className: "field", children: [
3245
+ /* @__PURE__ */ jsx33("span", { children: "Member id" }),
3246
+ /* @__PURE__ */ jsx33("input", { value: form.referrerMemberId, onInput: set("referrerMemberId") })
3247
+ ] }),
3248
+ /* @__PURE__ */ jsxs31("label", { className: "field", children: [
3249
+ /* @__PURE__ */ jsx33("span", { children: "Kind" }),
3250
+ /* @__PURE__ */ jsxs31(Select11, { value: form.status, onChange: set("status"), children: [
3251
+ /* @__PURE__ */ jsx33("option", { value: "void", children: "Void" }),
3252
+ /* @__PURE__ */ jsx33("option", { value: "earned", children: "Credit (adjustment)" }),
3253
+ /* @__PURE__ */ jsx33("option", { value: "applied", children: "Applied" }),
3254
+ /* @__PURE__ */ jsx33("option", { value: "carried_forward", children: "Carried forward" })
3255
+ ] })
3256
+ ] }),
3257
+ /* @__PURE__ */ jsxs31("label", { className: "field", children: [
3258
+ /* @__PURE__ */ jsx33("span", { children: "Amount" }),
3259
+ /* @__PURE__ */ jsx33("input", { inputMode: "decimal", value: form.valueCents, onInput: set("valueCents") })
3260
+ ] }),
3261
+ /* @__PURE__ */ jsxs31("label", { className: "field", children: [
3262
+ /* @__PURE__ */ jsx33("span", { children: "Reason" }),
3263
+ /* @__PURE__ */ jsx33("input", { value: form.reason, onInput: set("reason") })
3264
+ ] }),
3265
+ /* @__PURE__ */ jsx33("button", { className: "btn primary mini", type: "button", disabled: !form.reason.trim() || !form.referrerMemberId, onClick: () => void add(), children: "Add row" }),
3266
+ status ? /* @__PURE__ */ jsx33("p", { role: "status", children: status }) : null,
3267
+ /* @__PURE__ */ jsx33("h3", { children: "Rows" }),
3268
+ /* @__PURE__ */ jsx33("ul", { className: "meeting-note", children: (view.data?.rows ?? []).map((row) => /* @__PURE__ */ jsxs31("li", { children: [
3269
+ new Date(Number(row.at)).toLocaleDateString("en-US"),
3270
+ " \xB7 ",
3271
+ String(row.referrerMemberId),
3272
+ " \xB7 ",
3273
+ String(row.status),
3274
+ " ",
3275
+ money6(Number(row.valueCents)),
3276
+ row.reason ? ` \xB7 ${String(row.reason)}` : "",
3277
+ row.actor ? ` \xB7 ${String(row.actor)}` : ""
3278
+ ] }, String(row.id))) })
3279
+ ] });
3280
+ }
3281
+
3282
+ // src/ui/admin-member-portal.tsx
3283
+ import { jsx as jsx34, jsxs as jsxs32 } from "preact/jsx-runtime";
3284
+ var when2 = (at) => typeof at === "number" ? new Date(at).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" }) : "";
3285
+ function HubRosterPanel({ ctx }) {
3286
+ const roster = useAdminResource(ctx.getToken, "/api/admin/hub-members");
3287
+ const [email, setEmail] = useState29("");
3288
+ const [status, setStatus] = useState29(null);
3289
+ const act = async (path, init, done) => {
3290
+ try {
3291
+ await adminFetch(ctx.getToken, path, init);
3292
+ setStatus(done);
3293
+ roster.refresh();
3294
+ } catch (error) {
3295
+ setStatus(error instanceof Error ? error.message : String(error));
3296
+ }
3297
+ };
3298
+ return /* @__PURE__ */ jsxs32(Panel, { children: [
3299
+ /* @__PURE__ */ jsx34("div", { className: "card-label", children: "Hub members" }),
3300
+ /* @__PURE__ */ jsx34("p", { className: "meeting-note", children: "Members see the portal and its all-members pages. Granting membership never grants admin access." }),
3301
+ /* @__PURE__ */ jsxs32("form", { onSubmit: (event) => {
3302
+ event.preventDefault();
3303
+ void act("/api/admin/hub-members", { method: "POST", body: JSON.stringify({ email }) }, `Granted ${email}`).then(() => setEmail(""));
3304
+ }, children: [
3305
+ /* @__PURE__ */ jsxs32("label", { className: "field", children: [
3306
+ /* @__PURE__ */ jsx34("span", { children: "Email" }),
3307
+ /* @__PURE__ */ jsx34("input", { type: "email", required: true, value: email, onInput: (event) => setEmail(event.currentTarget.value) })
3308
+ ] }),
3309
+ /* @__PURE__ */ jsx34("button", { className: "btn primary", type: "submit", children: "Grant member access" })
3310
+ ] }),
3311
+ status ? /* @__PURE__ */ jsx34("p", { role: "status", children: status }) : null,
3312
+ /* @__PURE__ */ jsxs32("table", { children: [
3313
+ /* @__PURE__ */ jsx34("thead", { children: /* @__PURE__ */ jsxs32("tr", { children: [
3314
+ /* @__PURE__ */ jsx34("th", { children: "Email" }),
3315
+ /* @__PURE__ */ jsx34("th", { children: "Status" }),
3316
+ /* @__PURE__ */ jsx34("th", { children: "Granted" }),
3317
+ /* @__PURE__ */ jsx34("th", {})
3318
+ ] }) }),
3319
+ /* @__PURE__ */ jsx34("tbody", { children: (roster.data?.members ?? []).map((member) => /* @__PURE__ */ jsxs32("tr", { children: [
3320
+ /* @__PURE__ */ jsx34("td", { children: String(member.email) }),
3321
+ /* @__PURE__ */ jsxs32("td", { children: [
3322
+ String(member.status),
3323
+ member.revokedBy ? ` by ${String(member.revokedBy)}` : ""
3324
+ ] }),
3325
+ /* @__PURE__ */ jsxs32("td", { children: [
3326
+ when2(member.grantedAt),
3327
+ " by ",
3328
+ String(member.grantedBy)
3329
+ ] }),
3330
+ /* @__PURE__ */ jsx34("td", { children: member.status === "active" ? /* @__PURE__ */ jsx34("button", { className: "btn secondary mini", type: "button", onClick: () => void act(`/api/admin/hub-members/${encodeURIComponent(String(member.id))}`, { method: "DELETE" }, `Revoked ${String(member.email)}`), children: "Revoke" }) : /* @__PURE__ */ jsx34("button", { className: "btn secondary mini", type: "button", onClick: () => void act("/api/admin/hub-members", { method: "POST", body: JSON.stringify({ email: member.email }) }, `Granted ${String(member.email)}`), children: "Grant again" }) })
3331
+ ] }, String(member.id))) })
3332
+ ] })
3333
+ ] });
3334
+ }
3335
+ var KIND_LABELS = {
3336
+ introduction_unmatched: "Introduced by matches no member, or several",
3337
+ referral_self: "Self-referral",
3338
+ referral_mutual: "Two members referring each other this season",
3339
+ referral_lapsed_referrer: "Referrer's membership has lapsed"
3340
+ };
3341
+ function PortalReviewPanel({ ctx }) {
3342
+ const queue = useAdminResource(ctx.getToken, "/api/admin/portal-review");
3343
+ const [decisions, setDecisions] = useState29({});
3344
+ const [status, setStatus] = useState29(null);
3345
+ const resolve2 = async (id) => {
3346
+ try {
3347
+ await adminFetch(ctx.getToken, `/api/admin/portal-review/${encodeURIComponent(id)}`, { method: "POST", body: JSON.stringify({ decision: decisions[id] ?? "" }) });
3348
+ setStatus("Resolved");
3349
+ queue.refresh();
3350
+ } catch (error) {
3351
+ setStatus(error instanceof Error ? error.message : String(error));
3352
+ }
3353
+ };
3354
+ const items = queue.data?.items ?? [];
3355
+ return /* @__PURE__ */ jsxs32(Panel, { children: [
3356
+ /* @__PURE__ */ jsxs32("div", { className: "card-label", children: [
3357
+ "Review queue \xB7 ",
3358
+ items.length,
3359
+ " open"
3360
+ ] }),
3361
+ status ? /* @__PURE__ */ jsx34("p", { role: "status", children: status }) : null,
3362
+ items.length === 0 ? /* @__PURE__ */ jsx34("p", { className: "meeting-note", children: "Nothing to review." }) : items.map((item) => {
3363
+ const id = String(item.id);
3364
+ return /* @__PURE__ */ jsxs32("div", { className: "card", "data-review-item": id, children: [
3365
+ /* @__PURE__ */ jsx34("strong", { children: KIND_LABELS[String(item.kind)] ?? String(item.kind) }),
3366
+ /* @__PURE__ */ jsx34("pre", { style: { whiteSpace: "pre-wrap" }, children: JSON.stringify(item.detail) }),
3367
+ /* @__PURE__ */ jsxs32("div", { className: "meeting-note", children: [
3368
+ "Member ",
3369
+ String(item.memberId ?? ""),
3370
+ " \xB7 ",
3371
+ when2(item.createdAt)
3372
+ ] }),
3373
+ /* @__PURE__ */ jsxs32("label", { className: "field", children: [
3374
+ /* @__PURE__ */ jsx34("span", { children: "Decision" }),
3375
+ /* @__PURE__ */ jsx34("input", { value: decisions[id] ?? "", onInput: (event) => setDecisions({ ...decisions, [id]: event.currentTarget.value }) })
3376
+ ] }),
3377
+ /* @__PURE__ */ jsx34("button", { className: "btn primary mini", type: "button", disabled: !(decisions[id] ?? "").trim(), onClick: () => void resolve2(id), children: "Resolve" })
3378
+ ] }, id);
3379
+ })
3380
+ ] });
3381
+ }
3382
+ function memberPortalSection(chapter) {
3383
+ return {
3384
+ id: "member-portal",
3385
+ label: "Member portal",
3386
+ render: (ctx) => /* @__PURE__ */ jsxs32("div", { className: "chapter-admin-stack", children: [
3387
+ chapter.mode === "hub" ? /* @__PURE__ */ jsx34(HubRosterPanel, { ctx }) : null,
3388
+ chapter.memberPortal ? /* @__PURE__ */ jsx34(PortalReviewPanel, { ctx }) : null,
3389
+ chapter.memberPortal?.courses ? /* @__PURE__ */ jsx34(CoursesAdminPanel, { ctx }) : null,
3390
+ chapter.memberPortal?.referrals ? /* @__PURE__ */ jsx34(ReferralsAdminPanel, { ctx }) : null,
3391
+ chapter.memberPortal?.referrals?.creditCents ? /* @__PURE__ */ jsx34(CreditLedgerPanel, { ctx }) : null
3392
+ ] })
2938
3393
  };
2939
3394
  }
2940
3395
 
2941
3396
  // src/ui/admin-settings-workspace.tsx
2942
- import { jsx as jsx31 } from "preact/jsx-runtime";
3397
+ import { jsx as jsx35 } from "preact/jsx-runtime";
2943
3398
  var viewContext = (ctx, viewId, active) => {
2944
3399
  const route = { workspaceId: ctx.route.workspaceId, viewId, ...active && ctx.route.recordId ? { recordId: ctx.route.recordId } : {}, ...active && ctx.route.detailTab ? { detailTab: ctx.route.detailTab } : {} };
2945
3400
  const targetFromView = (target) => {
@@ -2952,30 +3407,40 @@ var viewContext = (ctx, viewId, active) => {
2952
3407
  };
2953
3408
  function SettingsWorkspace({ chapter, ctx }) {
2954
3409
  const copy = chapter.copy.admin;
2955
- 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";
3410
+ 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" : ctx.route.viewId === "member-portal" && chapter.memberPortal ? "member-portal" : "calendar";
2956
3411
  const calendar = availabilitySection({ id: "calendar", label: copy.workspaces.calendar });
2957
3412
  const email = emailSection({ label: copy.workspaces.email });
2958
3413
  const content = memberContentSection();
2959
3414
  const library = builderLibrarySection();
2960
- return /* @__PURE__ */ jsx31(Tabs2, { ariaLabel: copy.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
3415
+ return /* @__PURE__ */ jsx35(Tabs2, { ariaLabel: copy.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
2961
3416
  { value: "calendar", label: copy.workspaces.calendar, href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }), panel: calendar.render(viewContext(ctx, "calendar", active === "calendar")) },
2962
3417
  { value: "email", label: copy.workspaces.email, href: ctx.href({ viewId: "email", recordId: null, detailTab: null }), panel: email.render(viewContext(ctx, "email", active === "email")) },
2963
3418
  ...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")) }] : [],
3419
+ ...chapter.memberPortal ? [portalTab(chapter, ctx, active)] : [],
2964
3420
  ...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")) }] : []
2965
3421
  ] });
2966
3422
  }
3423
+ var portalTab = (chapter, ctx, active) => ({
3424
+ value: "member-portal",
3425
+ label: "Member portal",
3426
+ href: ctx.href({ viewId: "member-portal", recordId: null, detailTab: null }),
3427
+ panel: memberPortalSection(chapter).render(viewContext(ctx, "member-portal", active === "member-portal"))
3428
+ });
2967
3429
  function LeaderSettingsWorkspace({ chapter, ctx }) {
2968
3430
  const email = emailSection({ label: chapter.copy.admin.workspaces.email });
2969
3431
  const library = builderLibrarySection();
2970
- const active = ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : "email";
2971
- return /* @__PURE__ */ jsx31(Tabs2, { ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
3432
+ const members = Boolean(chapter.memberPortal || chapter.memberContent);
3433
+ const active = ctx.route.viewId === "builder-library" && chapter.builderLibrary ? "builder-library" : ctx.route.viewId === "member-content" && chapter.memberContent ? "member-content" : ctx.route.viewId === "member-portal" && members ? "member-portal" : "email";
3434
+ return /* @__PURE__ */ jsx35(Tabs2, { ariaLabel: chapter.copy.admin.workspaces.settingsViewsLabel, value: active, mount: "active", items: [
2972
3435
  { 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")) },
3436
+ ...chapter.memberContent ? [{ value: "member-content", label: "Member content", href: ctx.href({ viewId: "member-content", recordId: null, detailTab: null }), panel: memberContentSection().render(viewContext(ctx, "member-content", active === "member-content")) }] : [],
3437
+ ...members ? [portalTab(chapter, ctx, active)] : [],
2973
3438
  ...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")) }] : []
2974
3439
  ] });
2975
3440
  }
2976
3441
 
2977
3442
  // src/ui/admin-workspaces.tsx
2978
- import { jsx as jsx32 } from "preact/jsx-runtime";
3443
+ import { jsx as jsx36 } from "preact/jsx-runtime";
2979
3444
  var adminViewContext = (ctx, viewId, active) => {
2980
3445
  const route = {
2981
3446
  workspaceId: ctx.route.workspaceId,
@@ -3001,7 +3466,7 @@ function DashboardWorkspace({ chapter, ctx }) {
3001
3466
  const active = ctx.route.viewId === "billing" ? "billing" : "overview";
3002
3467
  const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
3003
3468
  const billing = billingSection({ label: copy.workspaces.billing });
3004
- return /* @__PURE__ */ jsx32(
3469
+ return /* @__PURE__ */ jsx36(
3005
3470
  Tabs3,
3006
3471
  {
3007
3472
  ariaLabel: copy.workspaces.dashboardViewsLabel,
@@ -3029,7 +3494,7 @@ function PeopleWorkspace({ chapter, ctx }) {
3029
3494
  const fallback = types[0]?.[0] ?? "person";
3030
3495
  const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
3031
3496
  const networkSharing = chapter.network.targets.length > 0;
3032
- return /* @__PURE__ */ jsx32(
3497
+ return /* @__PURE__ */ jsx36(
3033
3498
  Tabs3,
3034
3499
  {
3035
3500
  ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
@@ -3060,8 +3525,8 @@ function PortfolioWorkspace({ chapter, ctx }) {
3060
3525
  const types = Object.entries(chapter.crm.config.types).filter(([, def]) => def.workspace === "portfolio");
3061
3526
  const fallback = types[0]?.[0] ?? "";
3062
3527
  const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
3063
- if (!active) return /* @__PURE__ */ jsx32(LeaderNetworkOverview, { ctx });
3064
- return /* @__PURE__ */ jsx32(
3528
+ if (!active) return /* @__PURE__ */ jsx36(LeaderNetworkOverview, { ctx });
3529
+ return /* @__PURE__ */ jsx36(
3065
3530
  Tabs3,
3066
3531
  {
3067
3532
  ariaLabel: copy.workspaces.portfolioViewsLabel,
@@ -3085,7 +3550,7 @@ function PortfolioWorkspace({ chapter, ctx }) {
3085
3550
  );
3086
3551
  }
3087
3552
  function LeaderDashboardWorkspace({ chapter, ctx }) {
3088
- return /* @__PURE__ */ jsx32(
3553
+ return /* @__PURE__ */ jsx36(
3089
3554
  Tabs3,
3090
3555
  {
3091
3556
  ariaLabel: chapter.copy.admin.workspaces.dashboardViewsLabel,
@@ -3095,7 +3560,7 @@ function LeaderDashboardWorkspace({ chapter, ctx }) {
3095
3560
  value: "overview",
3096
3561
  label: chapter.copy.admin.workspaces.overview,
3097
3562
  href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
3098
- panel: /* @__PURE__ */ jsx32(LeaderNetworkOverview, { ctx: adminViewContext(ctx, "overview", true) })
3563
+ panel: /* @__PURE__ */ jsx36(LeaderNetworkOverview, { ctx: adminViewContext(ctx, "overview", true) })
3099
3564
  }]
3100
3565
  }
3101
3566
  );
@@ -3106,7 +3571,7 @@ function defaultAdminWorkspaces(chapter) {
3106
3571
  id: "people",
3107
3572
  label: copy.workspaces.people,
3108
3573
  defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
3109
- render: (ctx) => /* @__PURE__ */ jsx32(PeopleWorkspace, { chapter, ctx })
3574
+ render: (ctx) => /* @__PURE__ */ jsx36(PeopleWorkspace, { chapter, ctx })
3110
3575
  };
3111
3576
  if (chapter.mode === "hub" && chapter.network.targets.length === 0) return [people];
3112
3577
  if (chapter.mode === "hub") {
@@ -3116,7 +3581,7 @@ function defaultAdminWorkspaces(chapter) {
3116
3581
  id: "dashboard",
3117
3582
  label: copy.workspaces.dashboard,
3118
3583
  defaultViewId: "overview",
3119
- render: (ctx) => /* @__PURE__ */ jsx32(LeaderDashboardWorkspace, { chapter, ctx })
3584
+ render: (ctx) => /* @__PURE__ */ jsx36(LeaderDashboardWorkspace, { chapter, ctx })
3120
3585
  },
3121
3586
  networkRecordsWorkspace(chapter),
3122
3587
  people,
@@ -3124,13 +3589,13 @@ function defaultAdminWorkspaces(chapter) {
3124
3589
  id: "portfolio",
3125
3590
  label: copy.workspaces.portfolio,
3126
3591
  defaultViewId: portfolioTypes[0]?.[0],
3127
- render: (ctx) => /* @__PURE__ */ jsx32(PortfolioWorkspace, { chapter, ctx })
3592
+ render: (ctx) => /* @__PURE__ */ jsx36(PortfolioWorkspace, { chapter, ctx })
3128
3593
  },
3129
3594
  {
3130
3595
  id: "settings",
3131
3596
  label: copy.workspaces.settings,
3132
3597
  defaultViewId: "email",
3133
- render: (ctx) => /* @__PURE__ */ jsx32(LeaderSettingsWorkspace, { chapter, ctx })
3598
+ render: (ctx) => /* @__PURE__ */ jsx36(LeaderSettingsWorkspace, { chapter, ctx })
3134
3599
  }
3135
3600
  ];
3136
3601
  }
@@ -3139,7 +3604,7 @@ function defaultAdminWorkspaces(chapter) {
3139
3604
  id: "dashboard",
3140
3605
  label: copy.workspaces.dashboard,
3141
3606
  defaultViewId: "overview",
3142
- render: (ctx) => /* @__PURE__ */ jsx32(DashboardWorkspace, { chapter, ctx })
3607
+ render: (ctx) => /* @__PURE__ */ jsx36(DashboardWorkspace, { chapter, ctx })
3143
3608
  },
3144
3609
  people,
3145
3610
  admissionGrantsWorkspace(copy.workspaces.admission),
@@ -3147,17 +3612,17 @@ function defaultAdminWorkspaces(chapter) {
3147
3612
  id: "settings",
3148
3613
  label: copy.workspaces.settings,
3149
3614
  defaultViewId: "calendar",
3150
- render: (ctx) => /* @__PURE__ */ jsx32(SettingsWorkspace, { chapter, ctx })
3615
+ render: (ctx) => /* @__PURE__ */ jsx36(SettingsWorkspace, { chapter, ctx })
3151
3616
  }
3152
3617
  ];
3153
3618
  }
3154
3619
 
3155
3620
  // src/ui/admin-frame.tsx
3156
3621
  import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
3157
- import { jsx as jsx33, jsxs as jsxs29 } from "preact/jsx-runtime";
3622
+ import { jsx as jsx37, jsxs as jsxs33 } from "preact/jsx-runtime";
3158
3623
  function AdminTheme(props) {
3159
3624
  const brand = props.chapter?.brand;
3160
- return /* @__PURE__ */ jsx33(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs29(
3625
+ return /* @__PURE__ */ jsx37(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs33(
3161
3626
  ThemeScope2,
3162
3627
  {
3163
3628
  ref: props.scopeRef,
@@ -3166,7 +3631,7 @@ function AdminTheme(props) {
3166
3631
  accent: brand?.accent,
3167
3632
  colorScheme: brand?.colorScheme ?? "light",
3168
3633
  children: [
3169
- /* @__PURE__ */ jsx33(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
3634
+ /* @__PURE__ */ jsx37(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
3170
3635
  props.children
3171
3636
  ]
3172
3637
  }
@@ -3186,11 +3651,11 @@ function signInRedirect(basePath, workspaces) {
3186
3651
  }
3187
3652
 
3188
3653
  // src/ui/admin-appearance.ts
3189
- import { useEffect as useEffect11, useState as useState26 } from "preact/hooks";
3654
+ import { useEffect as useEffect13, useState as useState30 } from "preact/hooks";
3190
3655
  import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
3191
3656
  function useScopedClerkAppearance(scope) {
3192
- const [appearance, setAppearance] = useState26();
3193
- useEffect11(() => {
3657
+ const [appearance, setAppearance] = useState30();
3658
+ useEffect13(() => {
3194
3659
  setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
3195
3660
  }, [scope]);
3196
3661
  return appearance;
@@ -3221,7 +3686,7 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
3221
3686
  }
3222
3687
 
3223
3688
  // src/ui/admin.tsx
3224
- import { jsx as jsx34, jsxs as jsxs30 } from "preact/jsx-runtime";
3689
+ import { jsx as jsx38, jsxs as jsxs34 } from "preact/jsx-runtime";
3225
3690
  function Authed(props) {
3226
3691
  const copy = useChapterCopy().admin;
3227
3692
  const {
@@ -3249,8 +3714,8 @@ function Authed(props) {
3249
3714
  }),
3250
3715
  [getToken, crmBasePath]
3251
3716
  );
3252
- const [state, setState] = useState27({ status: "checking" });
3253
- useEffect12(() => {
3717
+ const [state, setState] = useState31({ status: "checking" });
3718
+ useEffect14(() => {
3254
3719
  let live = true;
3255
3720
  void (async () => {
3256
3721
  try {
@@ -3269,17 +3734,17 @@ function Authed(props) {
3269
3734
  live = false;
3270
3735
  };
3271
3736
  }, [getToken, apiBase, auth]);
3272
- if (state.status === "checking") return /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.auth.checking }) });
3737
+ if (state.status === "checking") return /* @__PURE__ */ jsx38(Gate, { brand, children: /* @__PURE__ */ jsx38("p", { style: S.muted, children: copy.auth.checking }) });
3273
3738
  if (state.status === "denied") {
3274
- return /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsxs30("div", { className: "card", style: S.card, children: [
3275
- /* @__PURE__ */ jsx34("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
3276
- /* @__PURE__ */ jsx34("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
3739
+ return /* @__PURE__ */ jsx38(Gate, { brand, children: /* @__PURE__ */ jsxs34("div", { className: "card", style: S.card, children: [
3740
+ /* @__PURE__ */ jsx38("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
3741
+ /* @__PURE__ */ jsx38("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
3277
3742
  account: state.email ?? copy.auth.thisAccount
3278
3743
  }) }),
3279
- /* @__PURE__ */ jsx34("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
3744
+ /* @__PURE__ */ jsx38("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
3280
3745
  ] }) });
3281
3746
  }
3282
- return /* @__PURE__ */ jsx34(
3747
+ return /* @__PURE__ */ jsx38(
3283
3748
  AdminShell,
3284
3749
  {
3285
3750
  workspaces,
@@ -3321,10 +3786,10 @@ function ChapterAdmin(props) {
3321
3786
  const chrome = props.chrome ?? "embedded";
3322
3787
  const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
3323
3788
  const redirect = signInRedirect(basePath, workspaces);
3324
- const [publishableKey, setPublishableKey] = useState27(void 0);
3789
+ const [publishableKey, setPublishableKey] = useState31(void 0);
3325
3790
  const scopeRef = useRef3(null);
3326
3791
  const appearance = useScopedClerkAppearance(scopeRef);
3327
- useEffect12(() => {
3792
+ useEffect14(() => {
3328
3793
  let live = true;
3329
3794
  void loadChapterAdminConfig(auth, apiBase).then((config) => live && setPublishableKey(config.publishableKey ?? null)).catch(() => live && setPublishableKey(null));
3330
3795
  return () => {
@@ -3333,23 +3798,23 @@ function ChapterAdmin(props) {
3333
3798
  }, [apiBase, auth]);
3334
3799
  let content;
3335
3800
  if (publishableKey === void 0 || publishableKey && !appearance) {
3336
- content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.loading }) });
3801
+ content = /* @__PURE__ */ jsx38(Gate, { brand, children: /* @__PURE__ */ jsx38("p", { style: S.muted, children: copy.admin.auth.loading }) });
3337
3802
  } else if (!publishableKey) {
3338
- content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsxs30("div", { className: "card", style: S.card, children: [
3339
- /* @__PURE__ */ jsx34("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
3340
- /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
3803
+ content = /* @__PURE__ */ jsx38(Gate, { brand, children: /* @__PURE__ */ jsxs34("div", { className: "card", style: S.card, children: [
3804
+ /* @__PURE__ */ jsx38("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
3805
+ /* @__PURE__ */ jsx38("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
3341
3806
  ] }) });
3342
3807
  } else if (workspaces.length === 0) {
3343
- content = /* @__PURE__ */ jsx34(Gate, { brand, children: /* @__PURE__ */ jsx34("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
3808
+ content = /* @__PURE__ */ jsx38(Gate, { brand, children: /* @__PURE__ */ jsx38("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
3344
3809
  } else {
3345
- content = /* @__PURE__ */ jsxs30(
3810
+ content = /* @__PURE__ */ jsxs34(
3346
3811
  ClerkGate,
3347
3812
  {
3348
3813
  publishableKey,
3349
3814
  appearance,
3350
3815
  afterSignOutUrl: "/",
3351
3816
  children: [
3352
- /* @__PURE__ */ jsx34(SignedOut, { children: /* @__PURE__ */ jsx34(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx34(
3817
+ /* @__PURE__ */ jsx38(SignedOut, { children: /* @__PURE__ */ jsx38(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx38(
3353
3818
  SignIn,
3354
3819
  {
3355
3820
  routing: "hash",
@@ -3357,7 +3822,7 @@ function ChapterAdmin(props) {
3357
3822
  signUpForceRedirectUrl: redirect
3358
3823
  }
3359
3824
  ) }) }),
3360
- /* @__PURE__ */ jsx34(SignedIn, { children: /* @__PURE__ */ jsx34(
3825
+ /* @__PURE__ */ jsx38(SignedIn, { children: /* @__PURE__ */ jsx38(
3361
3826
  Authed,
3362
3827
  {
3363
3828
  workspaces,
@@ -3378,7 +3843,7 @@ function ChapterAdmin(props) {
3378
3843
  }
3379
3844
  );
3380
3845
  }
3381
- return /* @__PURE__ */ jsx34(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
3846
+ return /* @__PURE__ */ jsx38(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
3382
3847
  }
3383
3848
 
3384
3849
  // src/ui/admin-defaults.ts
@@ -3387,17 +3852,17 @@ function defaultAdminSections(chapter) {
3387
3852
  }
3388
3853
 
3389
3854
  // src/ui/admin-meetings.tsx
3390
- import { useState as useState28 } from "preact/hooks";
3855
+ import { useState as useState32 } from "preact/hooks";
3391
3856
  import { Button as Button17 } from "@odla-ai/ui/components";
3392
- import { Fragment as Fragment13, jsx as jsx35, jsxs as jsxs31 } from "preact/jsx-runtime";
3393
- var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
3857
+ import { Fragment as Fragment13, jsx as jsx39, jsxs as jsxs35 } from "preact/jsx-runtime";
3858
+ var when3 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
3394
3859
  function MeetingsBody({ ctx }) {
3395
3860
  const copy = useChapterCopy().admin.meetings;
3396
3861
  const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
3397
- const [busy, setBusy] = useState28(null);
3398
- const [note, setNote] = useState28(null);
3399
- if (loading) return /* @__PURE__ */ jsx35(AdminNote, { children: copy.loading });
3400
- if (error || !data) return /* @__PURE__ */ jsxs31(AdminNote, { children: [
3862
+ const [busy, setBusy] = useState32(null);
3863
+ const [note, setNote] = useState32(null);
3864
+ if (loading) return /* @__PURE__ */ jsx39(AdminNote, { children: copy.loading });
3865
+ if (error || !data) return /* @__PURE__ */ jsxs35(AdminNote, { children: [
3401
3866
  copy.loadFailed,
3402
3867
  error ? `: ${error}` : "",
3403
3868
  "."
@@ -3428,43 +3893,43 @@ function MeetingsBody({ ctx }) {
3428
3893
  }
3429
3894
  void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
3430
3895
  };
3431
- return /* @__PURE__ */ jsx35(AdminPage, { children: /* @__PURE__ */ jsx35(Panel, { title: /* @__PURE__ */ jsxs31(Fragment13, { children: [
3896
+ return /* @__PURE__ */ jsx39(AdminPage, { children: /* @__PURE__ */ jsx39(Panel, { title: /* @__PURE__ */ jsxs35(Fragment13, { children: [
3432
3897
  copy.agenda,
3433
3898
  " ",
3434
- /* @__PURE__ */ jsxs31("span", { className: "muted", style: { fontWeight: 400 }, children: [
3899
+ /* @__PURE__ */ jsxs35("span", { className: "muted", style: { fontWeight: 400 }, children: [
3435
3900
  "\xB7 ",
3436
3901
  data.timezone
3437
3902
  ] })
3438
- ] }), actions: note ? /* @__PURE__ */ jsx35("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx35("p", { className: "muted", style: { margin: 0 }, children: copy.empty }) : /* @__PURE__ */ jsx35("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs31("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
3439
- /* @__PURE__ */ jsx35("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
3440
- /* @__PURE__ */ jsx35("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
3441
- /* @__PURE__ */ jsx35("span", { className: "badge", children: m.status }),
3442
- m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs31("span", { className: "badge", children: [
3903
+ ] }), actions: note ? /* @__PURE__ */ jsx39("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx39("p", { className: "muted", style: { margin: 0 }, children: copy.empty }) : /* @__PURE__ */ jsx39("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs35("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
3904
+ /* @__PURE__ */ jsx39("strong", { style: { minWidth: 170 }, children: when3(m.startAt) }),
3905
+ /* @__PURE__ */ jsx39("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
3906
+ /* @__PURE__ */ jsx39("span", { className: "badge", children: m.status }),
3907
+ m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs35("span", { className: "badge", children: [
3443
3908
  copy.drift,
3444
3909
  ": ",
3445
3910
  m.drift
3446
3911
  ] }) : null,
3447
- m.meetUrl ? /* @__PURE__ */ jsx35("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
3448
- m.status === "scheduled" ? /* @__PURE__ */ jsxs31(Fragment13, { children: [
3449
- /* @__PURE__ */ jsx35(Button17, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
3450
- /* @__PURE__ */ jsx35(Button17, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
3912
+ m.meetUrl ? /* @__PURE__ */ jsx39("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
3913
+ m.status === "scheduled" ? /* @__PURE__ */ jsxs35(Fragment13, { children: [
3914
+ /* @__PURE__ */ jsx39(Button17, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
3915
+ /* @__PURE__ */ jsx39(Button17, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
3451
3916
  ] }) : null
3452
3917
  ] }, m.id)) }) }) });
3453
3918
  }
3454
3919
  function meetingsSection(options = {}) {
3455
3920
  const { id = "meetings", label = "Meetings" } = options;
3456
- return { id, label, render: (ctx) => /* @__PURE__ */ jsx35(MeetingsBody, { ctx }) };
3921
+ return { id, label, render: (ctx) => /* @__PURE__ */ jsx39(MeetingsBody, { ctx }) };
3457
3922
  }
3458
3923
 
3459
3924
  // src/ui/admin-network-record-editor.tsx
3460
- import { Button as Button18, Field as Field14, Select as Select10 } from "@odla-ai/ui/components";
3461
- import { useRef as useRef4, useState as useState29 } from "preact/hooks";
3462
- import { Fragment as Fragment14, jsx as jsx36, jsxs as jsxs32 } from "preact/jsx-runtime";
3925
+ import { Button as Button18, Field as Field14, Select as Select12 } from "@odla-ai/ui/components";
3926
+ import { useRef as useRef4, useState as useState33 } from "preact/hooks";
3927
+ import { Fragment as Fragment14, jsx as jsx40, jsxs as jsxs36 } from "preact/jsx-runtime";
3463
3928
  function NetworkRecordEditor(props) {
3464
3929
  const { ctx, record } = props;
3465
- const [stage, setStage] = useState29("");
3466
- const [busy, setBusy] = useState29(false);
3467
- const [error, setError] = useState29(null);
3930
+ const [stage, setStage] = useState33("");
3931
+ const [busy, setBusy] = useState33(false);
3932
+ const [error, setError] = useState33(null);
3468
3933
  const attempt = useRef4(null);
3469
3934
  const pending = record.edit?.pendingAction;
3470
3935
  const superAdmin = ctx.currentUser?.superAdmin === true;
@@ -3497,11 +3962,11 @@ function NetworkRecordEditor(props) {
3497
3962
  setBusy(false);
3498
3963
  }
3499
3964
  };
3500
- return /* @__PURE__ */ jsxs32("div", { style: { display: "grid", gap: 24 }, children: [
3501
- superAdmin && record.edit ? /* @__PURE__ */ jsxs32(Fragment14, { children: [
3502
- /* @__PURE__ */ jsxs32("section", { children: [
3503
- /* @__PURE__ */ jsx36("h3", { children: "Chapter profile" }),
3504
- /* @__PURE__ */ jsx36(
3965
+ return /* @__PURE__ */ jsxs36("div", { style: { display: "grid", gap: 24 }, children: [
3966
+ superAdmin && record.edit ? /* @__PURE__ */ jsxs36(Fragment14, { children: [
3967
+ /* @__PURE__ */ jsxs36("section", { children: [
3968
+ /* @__PURE__ */ jsx40("h3", { children: "Chapter profile" }),
3969
+ /* @__PURE__ */ jsx40(
3505
3970
  OperationalProfileForm,
3506
3971
  {
3507
3972
  fields: record.edit.fields,
@@ -3511,27 +3976,27 @@ function NetworkRecordEditor(props) {
3511
3976
  `${record.id}:${record.revision}`
3512
3977
  )
3513
3978
  ] }),
3514
- /* @__PURE__ */ jsxs32("section", { style: { display: "grid", gap: 12 }, children: [
3515
- /* @__PURE__ */ jsx36("h3", { children: "Membership actions" }),
3516
- pending ? /* @__PURE__ */ jsxs32("p", { children: [
3979
+ /* @__PURE__ */ jsxs36("section", { style: { display: "grid", gap: 12 }, children: [
3980
+ /* @__PURE__ */ jsx40("h3", { children: "Membership actions" }),
3981
+ pending ? /* @__PURE__ */ jsxs36("p", { children: [
3517
3982
  "An application action is pending: ",
3518
3983
  pending.stage.replaceAll("_", " "),
3519
3984
  ". ",
3520
3985
  ownPending ? "Retry reconciles the same operation." : "The original operator must reconcile it."
3521
- ] }) : record.edit.allowedStages.length ? /* @__PURE__ */ jsx36(Field14, { label: "Action", children: /* @__PURE__ */ jsxs32(Select10, { "aria-label": "Action", value: stage, disabled: busy, onChange: (event) => setStage(event.currentTarget.value), children: [
3522
- /* @__PURE__ */ jsx36("option", { value: "", children: "Choose an action" }),
3523
- record.edit.allowedStages.map((action) => /* @__PURE__ */ jsx36("option", { value: action.id, children: action.label }, action.id))
3524
- ] }) }) : /* @__PURE__ */ jsx36("p", { children: "No membership action is currently allowed." }),
3525
- pending || record.edit.allowedStages.length ? /* @__PURE__ */ jsx36("div", { children: /* @__PURE__ */ jsx36(Button18, { disabled: busy || !ownPending || !pending && !stage, onClick: () => void move(), children: busy ? "Applying\u2026" : pending ? "Retry pending action" : "Apply action" }) }) : null,
3526
- error ? /* @__PURE__ */ jsx36("p", { role: "alert", children: error }) : null
3986
+ ] }) : record.edit.allowedStages.length ? /* @__PURE__ */ jsx40(Field14, { label: "Action", children: /* @__PURE__ */ jsxs36(Select12, { "aria-label": "Action", value: stage, disabled: busy, onChange: (event) => setStage(event.currentTarget.value), children: [
3987
+ /* @__PURE__ */ jsx40("option", { value: "", children: "Choose an action" }),
3988
+ record.edit.allowedStages.map((action) => /* @__PURE__ */ jsx40("option", { value: action.id, children: action.label }, action.id))
3989
+ ] }) }) : /* @__PURE__ */ jsx40("p", { children: "No membership action is currently allowed." }),
3990
+ pending || record.edit.allowedStages.length ? /* @__PURE__ */ jsx40("div", { children: /* @__PURE__ */ jsx40(Button18, { disabled: busy || !ownPending || !pending && !stage, onClick: () => void move(), children: busy ? "Applying\u2026" : pending ? "Retry pending action" : "Apply action" }) }) : null,
3991
+ error ? /* @__PURE__ */ jsx40("p", { role: "alert", children: error }) : null
3527
3992
  ] })
3528
3993
  ] }) : null,
3529
- superAdmin && props.admissionGrants ? /* @__PURE__ */ jsxs32("section", { children: [
3530
- /* @__PURE__ */ jsx36("h3", { children: "Next invitation" }),
3531
- /* @__PURE__ */ jsx36("p", { children: "This setting applies to the next eligible signup; it does not replace approval of an existing application." }),
3532
- /* @__PURE__ */ jsx36(LeaderAdmissionAction, { getToken: ctx.getToken, targetId: props.targetId, sourceRecordId: record.id })
3994
+ superAdmin && props.admissionGrants ? /* @__PURE__ */ jsxs36("section", { children: [
3995
+ /* @__PURE__ */ jsx40("h3", { children: "Next invitation" }),
3996
+ /* @__PURE__ */ jsx40("p", { children: "This setting applies to the next eligible signup; it does not replace approval of an existing application." }),
3997
+ /* @__PURE__ */ jsx40(LeaderAdmissionAction, { getToken: ctx.getToken, targetId: props.targetId, sourceRecordId: record.id })
3533
3998
  ] }) : null,
3534
- /* @__PURE__ */ jsx36(
3999
+ /* @__PURE__ */ jsx40(
3535
4000
  NetworkRecordDetail,
3536
4001
  {
3537
4002
  ctx,
@@ -3546,29 +4011,29 @@ function NetworkRecordEditor(props) {
3546
4011
  }
3547
4012
 
3548
4013
  // src/ui/network-seat-billing.tsx
3549
- import { useState as useState30 } from "preact/hooks";
4014
+ import { useState as useState34 } from "preact/hooks";
3550
4015
  import { Button as Button19, DataTable as DataTable4 } from "@odla-ai/ui/components";
3551
- import { jsx as jsx37, jsxs as jsxs33 } from "preact/jsx-runtime";
4016
+ import { jsx as jsx41, jsxs as jsxs37 } from "preact/jsx-runtime";
3552
4017
  var money3 = (row, cents) => row.currency ? new Intl.NumberFormat("en-US", {
3553
4018
  style: "currency",
3554
4019
  currency: row.currency
3555
4020
  }).format(cents / 100) : "Unknown";
3556
4021
  var columns2 = (refund, busy) => [
3557
- { key: "seatId", header: "Seat", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.seatId }) },
4022
+ { key: "seatId", header: "Seat", cell: (row) => /* @__PURE__ */ jsx41("code", { children: row.seatId }) },
3558
4023
  { key: "sourceKey", header: "Chapter / runtime", cell: (row) => row.sourceKey },
3559
4024
  { key: "status", header: "Payment", cell: (row) => row.status ?? "Unknown" },
3560
4025
  { key: "amountReceivedCents", header: "Collected", cell: (row) => money3(row, row.amountReceivedCents) },
3561
4026
  { key: "refundedCents", header: "Refunded", cell: (row) => money3(row, row.refundedCents) },
3562
- { key: "membershipId", header: "Primary membership", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.membershipId }) },
3563
- { key: "paymentIntentId", header: "Stripe payment", cell: (row) => /* @__PURE__ */ jsx37("code", { children: row.paymentIntentId }) },
3564
- { key: "subscriptionItemId", header: "Renewal item", cell: (row) => row.subscriptionItemId ? /* @__PURE__ */ jsx37("code", { children: row.subscriptionItemId }) : "Awaiting provider linkage" },
4027
+ { key: "membershipId", header: "Primary membership", cell: (row) => /* @__PURE__ */ jsx41("code", { children: row.membershipId }) },
4028
+ { key: "paymentIntentId", header: "Stripe payment", cell: (row) => /* @__PURE__ */ jsx41("code", { children: row.paymentIntentId }) },
4029
+ { key: "subscriptionItemId", header: "Renewal item", cell: (row) => row.subscriptionItemId ? /* @__PURE__ */ jsx41("code", { children: row.subscriptionItemId }) : "Awaiting provider linkage" },
3565
4030
  { key: "currentPeriodEnd", header: "Paid through", cell: (row) => row.currentPeriodEnd ? new Date(row.currentPeriodEnd * 1e3).toLocaleDateString() : "Awaiting provider linkage" },
3566
- { key: "refund", header: "Refund", cell: (row) => row.refundedCents < row.amountReceivedCents ? /* @__PURE__ */ jsx37(Button19, { variant: "danger", mini: true, disabled: busy !== null, onClick: () => refund(row), children: busy === row.seatId ? "Refunding\u2026" : "Refund seat" }) : "Refunded" }
4031
+ { key: "refund", header: "Refund", cell: (row) => row.refundedCents < row.amountReceivedCents ? /* @__PURE__ */ jsx41(Button19, { variant: "danger", mini: true, disabled: busy !== null, onClick: () => refund(row), children: busy === row.seatId ? "Refunding\u2026" : "Refund seat" }) : "Refunded" }
3567
4032
  ];
3568
4033
  function NetworkSeatBilling(props) {
3569
4034
  const { data, loading, error, refresh } = useAdminResource(props.getToken, "/api/admin/network-commercial-read-model");
3570
- const [busy, setBusy] = useState30(null);
3571
- const [note, setNote] = useState30(null);
4035
+ const [busy, setBusy] = useState34(null);
4036
+ const [note, setNote] = useState34(null);
3572
4037
  const refund = async (row) => {
3573
4038
  setBusy(row.seatId);
3574
4039
  setNote(null);
@@ -3586,20 +4051,20 @@ function NetworkSeatBilling(props) {
3586
4051
  setBusy(null);
3587
4052
  }
3588
4053
  };
3589
- return /* @__PURE__ */ jsxs33(Panel, { title: "Additional membership seats", children: [
3590
- /* @__PURE__ */ jsx37("p", { children: "Seat payments are included in the network totals. Renewal items share the primary subscription\u2019s term. An unaccepted seat is never refunded automatically." }),
3591
- loading ? /* @__PURE__ */ jsx37("p", { children: "Loading seats\u2026" }) : error ? /* @__PURE__ */ jsxs33("p", { role: "alert", children: [
4054
+ return /* @__PURE__ */ jsxs37(Panel, { title: "Additional membership seats", children: [
4055
+ /* @__PURE__ */ jsx41("p", { children: "Seat payments are included in the network totals. Renewal items share the primary subscription\u2019s term. An unaccepted seat is never refunded automatically." }),
4056
+ loading ? /* @__PURE__ */ jsx41("p", { children: "Loading seats\u2026" }) : error ? /* @__PURE__ */ jsxs37("p", { role: "alert", children: [
3592
4057
  "Seats could not be loaded: ",
3593
4058
  error
3594
- ] }) : /* @__PURE__ */ jsx37(DataTable4, { rows: data?.seats ?? [], columns: columns2((row) => void refund(row), busy), rowKey: (row) => `${row.sourceKey}:${row.seatId}` }),
3595
- note ? /* @__PURE__ */ jsx37("p", { role: "status", children: note }) : null
4059
+ ] }) : /* @__PURE__ */ jsx41(DataTable4, { rows: data?.seats ?? [], columns: columns2((row) => void refund(row), busy), rowKey: (row) => `${row.sourceKey}:${row.seatId}` }),
4060
+ note ? /* @__PURE__ */ jsx41("p", { role: "status", children: note }) : null
3596
4061
  ] });
3597
4062
  }
3598
4063
 
3599
4064
  // src/ui/membership-disputes.tsx
3600
- import { useState as useState31 } from "preact/hooks";
4065
+ import { useState as useState35 } from "preact/hooks";
3601
4066
  import { Button as Button20, DataTable as DataTable5 } from "@odla-ai/ui/components";
3602
- import { jsx as jsx38, jsxs as jsxs34 } from "preact/jsx-runtime";
4067
+ import { jsx as jsx42, jsxs as jsxs38 } from "preact/jsx-runtime";
3603
4068
  var money4 = (row, cents) => new Intl.NumberFormat("en-US", { style: "currency", currency: row.currency }).format(cents / 100);
3604
4069
  var day = (at) => at ? new Date(at).toLocaleDateString() : "\u2014";
3605
4070
  var DISPUTE_STATUS_WORDS = {
@@ -3613,35 +4078,35 @@ var DISPUTE_STATUS_WORDS = {
3613
4078
  recharged: "re-charged"
3614
4079
  };
3615
4080
  var columns3 = (begin, busy) => [
3616
- { key: "member", header: "Member", cell: (row) => row.kind === "seat" ? /* @__PURE__ */ jsxs34("span", { children: [
4081
+ { key: "member", header: "Member", cell: (row) => row.kind === "seat" ? /* @__PURE__ */ jsxs38("span", { children: [
3617
4082
  "Seat for ",
3618
4083
  row.recipientName ?? "the named recipient",
3619
4084
  " \xB7 ",
3620
- /* @__PURE__ */ jsx38("code", { children: row.applicationId })
3621
- ] }) : /* @__PURE__ */ jsxs34("span", { children: [
4085
+ /* @__PURE__ */ jsx42("code", { children: row.applicationId })
4086
+ ] }) : /* @__PURE__ */ jsxs38("span", { children: [
3622
4087
  row.memberNumber ? `#${row.memberNumber} \xB7 ` : "",
3623
- /* @__PURE__ */ jsx38("code", { children: row.applicationId })
4088
+ /* @__PURE__ */ jsx42("code", { children: row.applicationId })
3624
4089
  ] }) },
3625
4090
  { key: "chapter", header: "Chapter / runtime", cell: (row) => `${row.chapterId} / ${row.runtime}` },
3626
4091
  { key: "status", header: "Record", cell: (row) => row.status === "payment_disputed" ? `${row.kind === "seat" ? "seat" : "membership"} suspended by dispute` : row.status },
3627
- { key: "dispute", header: "Dispute", cell: (row) => /* @__PURE__ */ jsxs34("span", { children: [
4092
+ { key: "dispute", header: "Dispute", cell: (row) => /* @__PURE__ */ jsxs38("span", { children: [
3628
4093
  DISPUTE_STATUS_WORDS[row.disputeStatus] ?? row.disputeStatus,
3629
4094
  row.disputeReason ? ` \xB7 ${row.disputeReason}` : ""
3630
4095
  ] }) },
3631
4096
  { key: "amount", header: "Amount", cell: (row) => money4(row, row.disputeAmountCents ?? row.amountCents) },
3632
4097
  { key: "opened", header: "Opened", cell: (row) => day(row.disputedAt) },
3633
4098
  { key: "closed", header: "Closed", cell: (row) => day(row.disputeClosedAt) },
3634
- { key: "recharge", header: "Re-charge", cell: (row) => row.rechargePaymentIntentId ? /* @__PURE__ */ jsxs34("span", { children: [
4099
+ { key: "recharge", header: "Re-charge", cell: (row) => row.rechargePaymentIntentId ? /* @__PURE__ */ jsxs38("span", { children: [
3635
4100
  day(row.rechargedAt),
3636
4101
  " \xB7 ",
3637
- /* @__PURE__ */ jsx38("code", { children: row.rechargePaymentIntentId })
3638
- ] }) : row.status === "payment_disputed" && row.disputeStatus === "lost" ? /* @__PURE__ */ jsx38(Button20, { mini: true, disabled: busy !== null, onClick: () => begin(row), children: busy === row.id ? "Charging\u2026" : "Charge again" }) : row.status === "payment_disputed" ? "Respond to the dispute in Stripe" : "\u2014" }
4102
+ /* @__PURE__ */ jsx42("code", { children: row.rechargePaymentIntentId })
4103
+ ] }) : row.status === "payment_disputed" && row.disputeStatus === "lost" ? /* @__PURE__ */ jsx42(Button20, { mini: true, disabled: busy !== null, onClick: () => begin(row), children: busy === row.id ? "Charging\u2026" : "Charge again" }) : row.status === "payment_disputed" ? "Respond to the dispute in Stripe" : "\u2014" }
3639
4104
  ];
3640
4105
  function MembershipDisputes(props) {
3641
4106
  const { data, loading, error, refresh } = useAdminResource(props.getToken, "/api/admin/membership-disputes");
3642
- const [busy, setBusy] = useState31(null);
3643
- const [confirming, setConfirming] = useState31(null);
3644
- const [note, setNote] = useState31(null);
4107
+ const [busy, setBusy] = useState35(null);
4108
+ const [confirming, setConfirming] = useState35(null);
4109
+ const [note, setNote] = useState35(null);
3645
4110
  const recharge = async (row) => {
3646
4111
  setBusy(row.id);
3647
4112
  setNote(null);
@@ -3662,33 +4127,33 @@ function MembershipDisputes(props) {
3662
4127
  }
3663
4128
  };
3664
4129
  const rows = data?.disputes ?? [];
3665
- return /* @__PURE__ */ jsxs34(Panel, { title: "Card disputes", children: [
3666
- /* @__PURE__ */ jsx38("p", { children: "A chargeback suspends the membership until the dispute is won or the member clears it with their bank. Re-charging collects the disputed amount again on the saved card, only after a lost dispute and only when you ask." }),
3667
- loading ? /* @__PURE__ */ jsx38("p", { children: "Loading disputes\u2026" }) : error ? /* @__PURE__ */ jsxs34("p", { role: "alert", children: [
4130
+ return /* @__PURE__ */ jsxs38(Panel, { title: "Card disputes", children: [
4131
+ /* @__PURE__ */ jsx42("p", { children: "A chargeback suspends the membership until the dispute is won or the member clears it with their bank. Re-charging collects the disputed amount again on the saved card, only after a lost dispute and only when you ask." }),
4132
+ loading ? /* @__PURE__ */ jsx42("p", { children: "Loading disputes\u2026" }) : error ? /* @__PURE__ */ jsxs38("p", { role: "alert", children: [
3668
4133
  "Disputes could not be loaded: ",
3669
4134
  error
3670
- ] }) : rows.length ? /* @__PURE__ */ jsx38(DataTable5, { rows, columns: columns3(setConfirming, busy), rowKey: (row) => row.id }) : /* @__PURE__ */ jsx38("p", { children: "No membership carries a dispute." }),
3671
- data?.truncated ? /* @__PURE__ */ jsx38("p", { role: "alert", children: "Only the most recent memberships were checked; older disputes may exist." }) : null,
3672
- confirming ? /* @__PURE__ */ jsxs34("div", { role: "group", "aria-label": "Confirm re-charge", children: [
3673
- /* @__PURE__ */ jsxs34("p", { children: [
4135
+ ] }) : rows.length ? /* @__PURE__ */ jsx42(DataTable5, { rows, columns: columns3(setConfirming, busy), rowKey: (row) => row.id }) : /* @__PURE__ */ jsx42("p", { children: "No membership carries a dispute." }),
4136
+ data?.truncated ? /* @__PURE__ */ jsx42("p", { role: "alert", children: "Only the most recent memberships were checked; older disputes may exist." }) : null,
4137
+ confirming ? /* @__PURE__ */ jsxs38("div", { role: "group", "aria-label": "Confirm re-charge", children: [
4138
+ /* @__PURE__ */ jsxs38("p", { children: [
3674
4139
  "Charge ",
3675
4140
  money4(confirming, confirming.disputeAmountCents ?? confirming.amountCents),
3676
4141
  " again to the card on file for ",
3677
4142
  confirming.kind === "seat" ? `the seat for ${confirming.recipientName ?? "the recipient"}` : confirming.applicationId,
3678
4143
  "?"
3679
4144
  ] }),
3680
- /* @__PURE__ */ jsx38(Button20, { mini: true, onClick: () => void recharge(confirming), children: "Yes, charge the card" }),
4145
+ /* @__PURE__ */ jsx42(Button20, { mini: true, onClick: () => void recharge(confirming), children: "Yes, charge the card" }),
3681
4146
  " ",
3682
- /* @__PURE__ */ jsx38(Button20, { mini: true, onClick: () => setConfirming(null), children: "Cancel" })
4147
+ /* @__PURE__ */ jsx42(Button20, { mini: true, onClick: () => setConfirming(null), children: "Cancel" })
3683
4148
  ] }) : null,
3684
- note ? /* @__PURE__ */ jsx38("p", { role: "status", children: note }) : null
4149
+ note ? /* @__PURE__ */ jsx42("p", { role: "status", children: note }) : null
3685
4150
  ] });
3686
4151
  }
3687
4152
 
3688
4153
  // src/ui/membership-policy-versions.tsx
3689
4154
  import { Button as Button21, DataTable as DataTable6 } from "@odla-ai/ui/components";
3690
- import { useState as useState32 } from "preact/hooks";
3691
- import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs35 } from "preact/jsx-runtime";
4155
+ import { useState as useState36 } from "preact/hooks";
4156
+ import { Fragment as Fragment15, jsx as jsx43, jsxs as jsxs39 } from "preact/jsx-runtime";
3692
4157
  var PATH = "/api/admin/membership-policy-versions";
3693
4158
  var RETERM = "/api/admin/membership-discount/reterm";
3694
4159
  var day2 = (ms) => new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
@@ -3700,50 +4165,50 @@ var MECHANISM = {
3700
4165
  stripe_coupon_forever: "Stripe coupon, applied while active",
3701
4166
  stripe_coupon_term_bound: "Stripe coupon, applied once; issuance refuses unless the offering's fixed term ends on the same date"
3702
4167
  };
3703
- var State = ({ row }) => /* @__PURE__ */ jsx39("span", { className: row.state === "active" ? "badge" : "muted", children: STATE[row.state] });
4168
+ var State = ({ row }) => /* @__PURE__ */ jsx43("span", { className: row.state === "active" ? "badge" : "muted", children: STATE[row.state] });
3704
4169
  var superseded = (row) => row.supersededAt === void 0 ? "\u2014" : day2(row.supersededAt);
3705
- var Source = ({ row }) => /* @__PURE__ */ jsxs35("span", { children: [
3706
- row.source ?? /* @__PURE__ */ jsx39("span", { className: "muted", children: "Unrecorded" }),
3707
- row.actor ? /* @__PURE__ */ jsxs35(Fragment15, { children: [
4170
+ var Source = ({ row }) => /* @__PURE__ */ jsxs39("span", { children: [
4171
+ row.source ?? /* @__PURE__ */ jsx43("span", { className: "muted", children: "Unrecorded" }),
4172
+ row.actor ? /* @__PURE__ */ jsxs39(Fragment15, { children: [
3708
4173
  " \xB7 ",
3709
- /* @__PURE__ */ jsx39("code", { children: row.actor })
4174
+ /* @__PURE__ */ jsx43("code", { children: row.actor })
3710
4175
  ] }) : null,
3711
- row.recordedAt !== void 0 ? /* @__PURE__ */ jsxs35(Fragment15, { children: [
4176
+ row.recordedAt !== void 0 ? /* @__PURE__ */ jsxs39(Fragment15, { children: [
3712
4177
  " \xB7 ",
3713
4178
  day2(row.recordedAt)
3714
4179
  ] }) : null
3715
4180
  ] });
3716
4181
  var offeringColumns = [
3717
- { key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs35(Fragment15, { children: [
3718
- /* @__PURE__ */ jsxs35("code", { children: [
4182
+ { key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs39(Fragment15, { children: [
4183
+ /* @__PURE__ */ jsxs39("code", { children: [
3719
4184
  "v",
3720
4185
  row.version
3721
4186
  ] }),
3722
4187
  " ",
3723
- /* @__PURE__ */ jsx39(State, { row })
4188
+ /* @__PURE__ */ jsx43(State, { row })
3724
4189
  ] }), sortAs: "number", sortValue: (row) => row.version },
3725
- { key: "sku", header: "SKU", cell: (row) => /* @__PURE__ */ jsx39("code", { children: row.baseSkuId }) },
4190
+ { key: "sku", header: "SKU", cell: (row) => /* @__PURE__ */ jsx43("code", { children: row.baseSkuId }) },
3726
4191
  { key: "list", header: "List price", cell: (row) => `${money5(row.basePriceCents, row.currency)} / ${row.interval}` },
3727
4192
  { key: "term", header: "Term", cell: (row) => row.term.kind === "fixed_end" ? `Ends ${day2(row.term.endsAt)}` : `Rolling ${row.term.months} months` },
3728
4193
  { key: "seat", header: "Seat", cell: (row) => `${row.seatPriceCents === 0 ? "Included" : money5(row.seatPriceCents, row.currency)} \xB7 ${row.seatPurchaseWindowCalendarMonths === void 0 ? "no window" : `${row.seatPurchaseWindowCalendarMonths}-month window`}` },
3729
4194
  { key: "effectiveFrom", header: "Effective from", cell: (row) => day2(row.effectiveFrom), sortAs: "number", sortValue: (row) => row.effectiveFrom },
3730
4195
  { key: "effectiveUntil", header: "Effective until", cell: (row) => row.effectiveUntil === void 0 ? "Open" : day2(row.effectiveUntil) },
3731
4196
  { key: "supersededAt", header: "Superseded", cell: superseded },
3732
- { key: "source", header: "Source", cell: (row) => /* @__PURE__ */ jsx39(Source, { row }) }
4197
+ { key: "source", header: "Source", cell: (row) => /* @__PURE__ */ jsx43(Source, { row }) }
3733
4198
  ];
3734
4199
  var policyColumns = (getToken) => [
3735
- { key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs35(Fragment15, { children: [
3736
- /* @__PURE__ */ jsxs35("code", { children: [
4200
+ { key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs39(Fragment15, { children: [
4201
+ /* @__PURE__ */ jsxs39("code", { children: [
3737
4202
  "v",
3738
4203
  row.version
3739
4204
  ] }),
3740
4205
  " ",
3741
- /* @__PURE__ */ jsx39(State, { row })
4206
+ /* @__PURE__ */ jsx43(State, { row })
3742
4207
  ] }), sortAs: "number", sortValue: (row) => row.version },
3743
- { key: "cohort", header: "Cohort", cell: (row) => /* @__PURE__ */ jsxs35(Fragment15, { children: [
4208
+ { key: "cohort", header: "Cohort", cell: (row) => /* @__PURE__ */ jsxs39(Fragment15, { children: [
3744
4209
  row.cohortLabel,
3745
4210
  " ",
3746
- /* @__PURE__ */ jsxs35("span", { className: "muted", children: [
4211
+ /* @__PURE__ */ jsxs39("span", { className: "muted", children: [
3747
4212
  "\xB7 soft target ",
3748
4213
  row.softTarget
3749
4214
  ] })
@@ -3754,8 +4219,8 @@ var policyColumns = (getToken) => [
3754
4219
  { key: "benefit", header: "Benefit duration", cell: (row) => row.benefitDuration.kind === "fixed_end" ? `Until ${day2(row.benefitDuration.endsAt)}` : row.benefitDuration.kind === "first_term" ? "First term" : "While active" },
3755
4220
  { key: "mechanism", header: "Provider mechanism", cell: (row) => MECHANISM[row.mechanism] },
3756
4221
  { key: "supersededAt", header: "Superseded", cell: superseded },
3757
- { key: "source", header: "Source", cell: (row) => /* @__PURE__ */ jsx39(Source, { row }) },
3758
- { key: "members", header: "Existing members", cell: (row) => /* @__PURE__ */ jsx39(RetermAction, { row, getToken }) }
4222
+ { key: "source", header: "Source", cell: (row) => /* @__PURE__ */ jsx43(Source, { row }) },
4223
+ { key: "members", header: "Existing members", cell: (row) => /* @__PURE__ */ jsx43(RetermAction, { row, getToken }) }
3759
4224
  ];
3760
4225
  var summary = (receipt) => {
3761
4226
  const skipped = receipt.skipped.reduce((acc, item) => ({ ...acc, [item.reason]: (acc[item.reason] ?? 0) + 1 }), {});
@@ -3767,9 +4232,9 @@ var summary = (receipt) => {
3767
4232
  return `${parts.join(" \xB7 ")}${receipt.more ? " \xB7 more remain, run again" : ""}`;
3768
4233
  };
3769
4234
  function RetermAction({ row, getToken }) {
3770
- const [busy, setBusy] = useState32(false);
3771
- const [result, setResult] = useState32(null);
3772
- if (row.state !== "active" || row.mechanism !== "stripe_coupon_forever") return /* @__PURE__ */ jsx39("span", { className: "muted", children: row.state !== "active" ? "Not the active version" : "Benefit is not while-active" });
4235
+ const [busy, setBusy] = useState36(false);
4236
+ const [result, setResult] = useState36(null);
4237
+ if (row.state !== "active" || row.mechanism !== "stripe_coupon_forever") return /* @__PURE__ */ jsx43("span", { className: "muted", children: row.state !== "active" ? "Not the active version" : "Benefit is not while-active" });
3773
4238
  const run = async () => {
3774
4239
  if (!globalThis.confirm?.(`Move every paid ${row.cohortLabel} member of ${row.chapterId} onto v${row.version}? Their next renewal invoice carries ${row.percentOffBps / 100}% off while they stay active. A member whose renewal this would raise is left alone.`)) return;
3775
4240
  setBusy(true);
@@ -3783,28 +4248,28 @@ function RetermAction({ row, getToken }) {
3783
4248
  setBusy(false);
3784
4249
  }
3785
4250
  };
3786
- return /* @__PURE__ */ jsxs35("span", { style: { display: "inline-flex", flexDirection: "column", gap: 4 }, children: [
3787
- /* @__PURE__ */ jsx39(Button21, { mini: true, disabled: busy, onClick: run, children: busy ? "Moving\u2026" : "Move members to this benefit" }),
3788
- result ? /* @__PURE__ */ jsx39("span", { role: result.tone === "good" ? "status" : "alert", className: result.tone === "danger" ? "danger" : "muted", children: result.text }) : null
4251
+ return /* @__PURE__ */ jsxs39("span", { style: { display: "inline-flex", flexDirection: "column", gap: 4 }, children: [
4252
+ /* @__PURE__ */ jsx43(Button21, { mini: true, disabled: busy, onClick: run, children: busy ? "Moving\u2026" : "Move members to this benefit" }),
4253
+ result ? /* @__PURE__ */ jsx43("span", { role: result.tone === "good" ? "status" : "alert", className: result.tone === "danger" ? "danger" : "muted", children: result.text }) : null
3789
4254
  ] });
3790
4255
  }
3791
4256
  function MembershipPolicyVersions(props) {
3792
4257
  const path = props.chapterId ? `${PATH}?chapterId=${encodeURIComponent(props.chapterId)}` : PATH;
3793
4258
  const { data, loading, error } = useAdminResource(props.getToken, path);
3794
4259
  const scope = props.chapterId ? `chapter ${props.chapterId}` : "any chapter";
3795
- if (loading) return /* @__PURE__ */ jsx39(Panel, { title: "Membership offering versions", children: /* @__PURE__ */ jsx39("p", { role: "status", children: "Loading offering versions\u2026" }) });
3796
- if (error || !data) return /* @__PURE__ */ jsx39(Panel, { title: "Membership offering versions", children: /* @__PURE__ */ jsxs35("p", { role: "alert", children: [
4260
+ if (loading) return /* @__PURE__ */ jsx43(Panel, { title: "Membership offering versions", children: /* @__PURE__ */ jsx43("p", { role: "status", children: "Loading offering versions\u2026" }) });
4261
+ if (error || !data) return /* @__PURE__ */ jsx43(Panel, { title: "Membership offering versions", children: /* @__PURE__ */ jsxs39("p", { role: "alert", children: [
3797
4262
  "Offering versions could not be loaded: ",
3798
4263
  error ?? "empty response"
3799
4264
  ] }) });
3800
- return /* @__PURE__ */ jsxs35(Fragment15, { children: [
3801
- /* @__PURE__ */ jsxs35(Panel, { title: "Membership offering versions", actions: data.truncated ? /* @__PURE__ */ jsx39("span", { className: "badge", children: "Truncated" }) : void 0, children: [
3802
- /* @__PURE__ */ jsxs35("p", { className: "muted", children: [
4265
+ return /* @__PURE__ */ jsxs39(Fragment15, { children: [
4266
+ /* @__PURE__ */ jsxs39(Panel, { title: "Membership offering versions", actions: data.truncated ? /* @__PURE__ */ jsx43("span", { className: "badge", children: "Truncated" }) : void 0, children: [
4267
+ /* @__PURE__ */ jsxs39("p", { className: "muted", children: [
3803
4268
  "The list price a card shows comes from the active offering; the payable price applies the active discount policy. State is judged as of ",
3804
4269
  day2(data.now),
3805
4270
  ", exactly as checkout issuance judges it."
3806
4271
  ] }),
3807
- /* @__PURE__ */ jsx39(
4272
+ /* @__PURE__ */ jsx43(
3808
4273
  DataTable6,
3809
4274
  {
3810
4275
  rows: data.offerings,
@@ -3815,8 +4280,8 @@ function MembershipPolicyVersions(props) {
3815
4280
  }
3816
4281
  )
3817
4282
  ] }),
3818
- /* @__PURE__ */ jsxs35(Panel, { title: "Discount policy versions", children: [
3819
- /* @__PURE__ */ jsx39(
4283
+ /* @__PURE__ */ jsxs39(Panel, { title: "Discount policy versions", children: [
4284
+ /* @__PURE__ */ jsx43(
3820
4285
  DataTable6,
3821
4286
  {
3822
4287
  rows: data.discountPolicies,
@@ -3826,7 +4291,7 @@ function MembershipPolicyVersions(props) {
3826
4291
  emptyState: `No discount policy versions are recorded for ${scope}.`
3827
4292
  }
3828
4293
  ),
3829
- data.invalid.length ? /* @__PURE__ */ jsxs35("p", { role: "alert", children: [
4294
+ data.invalid.length ? /* @__PURE__ */ jsxs39("p", { role: "alert", children: [
3830
4295
  data.invalid.length,
3831
4296
  " recorded version",
3832
4297
  data.invalid.length === 1 ? " no longer normalizes and blocks" : "s no longer normalize and block",
@@ -3886,4 +4351,4 @@ export {
3886
4351
  MembershipDisputes,
3887
4352
  MembershipPolicyVersions
3888
4353
  };
3889
- //# sourceMappingURL=chunk-24L7FANC.js.map
4354
+ //# sourceMappingURL=chunk-S6VPJFHL.js.map