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