@odla-ai/chapter 0.42.1 → 0.42.2

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 CHANGED
@@ -892,6 +892,7 @@ export const chapter = defineChapter({
892
892
  // parent may mutate. Omission keeps the edge read-only.
893
893
  editableFields: { person: ["name", "email", "phone", "linkedin"] },
894
894
  stageTransitions: ["person"],
895
+ admissionGrants: true,
895
896
  }],
896
897
  },
897
898
  });
@@ -917,6 +918,16 @@ retry with the same input is idempotent, while stale state is `409`, an
917
918
  undelegated operation is `403`, invalid fields or transitions are `422`, and
918
919
  ordinary browse-only edges remain read-only.
919
920
 
921
+ Admission decisions use a separate default-deny lane. With
922
+ `admissionGrants: true` on both halves of an edge, the leader's
923
+ `POST /api/admin/network/admissions` resolves the person's current email from
924
+ its CRM, persists the complete desired target document, and delivers it through
925
+ the configured service binding or signed URL. Delivery state is durable as
926
+ `saved`, `pending`, `acknowledged`, `retrying`, or `rejected`; retry and revoke
927
+ routes publish the same or a higher desired revision. A follower may declare an
928
+ admission-only reader without `fields`, which does not make CRM records
929
+ browsable or editable.
930
+
920
931
  The Network detail view also has two deliberate note lanes:
921
932
 
922
933
  - `GET/POST /api/admin/network/notes` stores private annotations in the
@@ -951,6 +962,7 @@ export const chapter = defineChapter({
951
962
  sharedNotes: ["person"],
952
963
  editableFields: { person: ["name", "email", "phone", "linkedin"] },
953
964
  stageTransitions: ["person"],
965
+ admissionGrants: true,
954
966
  }],
955
967
  },
956
968
  });
@@ -961,7 +973,7 @@ Treat the network as websites connected by explicit delivery edges:
961
973
  - each website is a node with its own `appId`, ODLA tenants/keys, CRM config,
962
974
  Clerk application, publishable keys, issuer, roles, and local users;
963
975
  - each `network.targets[]` entry is a directed edge from one leader to one
964
- follower: `{ id, name, url, binding?, secretName?, fields, editableFields?, stageTransitions? }`;
976
+ follower: `{ id, name, url, binding?, secretName?, fields, editableFields?, stageTransitions?, admissionGrants? }`;
965
977
  - the payload is a versioned, allowlisted business record
966
978
  `{ version: 2, source: { siteId, recordId }, type, input }`, never an
967
979
  identity-provider session, another site's pipeline/account/billing state, or
@@ -1620,6 +1620,48 @@ function admissionGrantsWorkspace(label = "Admission") {
1620
1620
  render: (ctx) => /* @__PURE__ */ jsx16(AdmissionGrantsWorkspace, { getToken: ctx.getToken })
1621
1621
  };
1622
1622
  }
1623
+ function LeaderAdmissionAction(props) {
1624
+ const [decision, setDecision] = useState11("waive_interview");
1625
+ const [note, setNote] = useState11("");
1626
+ const [busy, setBusy] = useState11(false);
1627
+ const [error, setError] = useState11(null);
1628
+ const submit = async () => {
1629
+ setBusy(true);
1630
+ setError(null);
1631
+ try {
1632
+ const result = await adminFetch(props.getToken, "/api/admin/network/admissions", {
1633
+ method: "POST",
1634
+ body: JSON.stringify({
1635
+ targetId: props.targetId,
1636
+ personId: props.personId,
1637
+ decision,
1638
+ ...note.trim() ? { note: note.trim() } : {},
1639
+ mutationId: crypto.randomUUID()
1640
+ })
1641
+ });
1642
+ props.onSaved?.(result);
1643
+ } catch (cause) {
1644
+ setError(cause instanceof Error ? cause.message : String(cause));
1645
+ } finally {
1646
+ setBusy(false);
1647
+ }
1648
+ };
1649
+ return /* @__PURE__ */ jsxs13("div", { style: { display: "grid", gap: 8 }, children: [
1650
+ /* @__PURE__ */ jsxs13("label", { children: [
1651
+ "Interview",
1652
+ /* @__PURE__ */ jsxs13("select", { value: decision, onChange: (event) => setDecision(event.currentTarget.value), children: [
1653
+ /* @__PURE__ */ jsx16("option", { value: "waive_interview", children: "Already completed" }),
1654
+ /* @__PURE__ */ jsx16("option", { value: "require_interview", children: "Required" })
1655
+ ] })
1656
+ ] }),
1657
+ /* @__PURE__ */ jsxs13("label", { children: [
1658
+ "Internal note",
1659
+ /* @__PURE__ */ jsx16("input", { maxLength: 2e3, value: note, onInput: (event) => setNote(event.currentTarget.value) })
1660
+ ] }),
1661
+ /* @__PURE__ */ jsx16("button", { className: "btn secondary", type: "button", disabled: busy, onClick: () => void submit(), children: busy ? "Saving\u2026" : "Prepare for chapter" }),
1662
+ error ? /* @__PURE__ */ jsx16("p", { role: "alert", children: error }) : null
1663
+ ] });
1664
+ }
1623
1665
 
1624
1666
  // src/ui/admin-settings-workspace.tsx
1625
1667
  import { Tabs as Tabs2 } from "@odla-ai/ui/components";
@@ -2709,6 +2751,7 @@ export {
2709
2751
  LeaderNetworkOverview,
2710
2752
  AdmissionGrantsWorkspace,
2711
2753
  admissionGrantsWorkspace,
2754
+ LeaderAdmissionAction,
2712
2755
  availabilitySection,
2713
2756
  BuilderLibraryAdmin,
2714
2757
  builderLibrarySection,
@@ -2722,4 +2765,4 @@ export {
2722
2765
  defaultAdminSections,
2723
2766
  meetingsSection
2724
2767
  };
2725
- //# sourceMappingURL=chunk-MMDWXTJU.js.map
2768
+ //# sourceMappingURL=chunk-HWUTCXBV.js.map