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