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