@odla-ai/chapter 0.56.2 → 0.57.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 +57 -0
- package/dist/{chunk-E7CB5K2D.js → chunk-CKC6X5H2.js} +47 -3
- package/dist/chunk-CKC6X5H2.js.map +1 -0
- package/dist/{chunk-4M2PN5K3.js → chunk-FHKZVF5G.js} +3 -1
- package/dist/chunk-FHKZVF5G.js.map +1 -0
- package/dist/{chunk-ZAE24YKY.js → chunk-L2IPS2FQ.js} +51 -4
- package/dist/chunk-L2IPS2FQ.js.map +1 -0
- package/dist/{copy-context-CaUXIxUI.d.ts → copy-context-DE4YMAbz.d.ts} +1 -1
- package/dist/index.cjs +176 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -3
- package/dist/index.d.ts +36 -3
- package/dist/index.js +176 -13
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +4 -3
- package/dist/ui/admin/index.js +2 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/dist/ui/member/index.d.ts +10 -3
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +1558 -1125
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +149 -3
- package/dist/worker/index.d.ts +149 -3
- package/dist/worker/index.js +1558 -1125
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4M2PN5K3.js.map +0 -1
- package/dist/chunk-E7CB5K2D.js.map +0 -1
- package/dist/chunk-ZAE24YKY.js.map +0 -1
package/README.md
CHANGED
|
@@ -1850,6 +1850,63 @@ These bite silently — a smoke test won't catch them:
|
|
|
1850
1850
|
named and whether that person joined without folding the seat status machine
|
|
1851
1851
|
itself; `NamedSeatRecipientLine` renders it.
|
|
1852
1852
|
|
|
1853
|
+
**Decisions after the conversation (0.57.0).** Four operations close the
|
|
1854
|
+
gaps a chapter hits once applicants are real people (BNF bugs fa96b11c,
|
|
1855
|
+
f7bc92ee, 38000934, c3c1d338, 79590edc):
|
|
1856
|
+
|
|
1857
|
+
- `POST /api/admin/applications/:id/decline` refuses an applicant who holds
|
|
1858
|
+
no payment: a free-tier applicant, or a paid-tier applicant who never
|
|
1859
|
+
paid. The application closes as `declined` (`canceled: true`), the CRM
|
|
1860
|
+
stage follows, and nothing is emailed. Under a network authority the
|
|
1861
|
+
answer to "is a payment held?" comes from the authority's access record,
|
|
1862
|
+
not from local provider ids, so a checkout that was started and abandoned
|
|
1863
|
+
can still be declined; a held payment is refused with `payment_held` and
|
|
1864
|
+
the refund is the only way to end it. A gifted seat's recipient is refused
|
|
1865
|
+
with `named_seat_recipient`. `GET …/actions` offers `declined` under an
|
|
1866
|
+
authority only while no checkout has reached the provider, the hub's
|
|
1867
|
+
delegated stage move runs the same operation, and `RecordActions` carries
|
|
1868
|
+
a Decline control beside Approve and Refund (`admin.records.decline`,
|
|
1869
|
+
`admin.records.declined` copy).
|
|
1870
|
+
- `POST /api/admin/applications/:id/refund` with a JSON body
|
|
1871
|
+
`{ continueAs: "<free tier id>" }` is "not a paid fit": the ordinary refund,
|
|
1872
|
+
then the same application moves to that free tier at the last approvable
|
|
1873
|
+
stage and is approved at once, so the person keeps member access as, for
|
|
1874
|
+
example, an Associate. The refunded purchase is archived in
|
|
1875
|
+
`membershipCheckoutHistory`, its billing fields leave the row, and the tier
|
|
1876
|
+
is checked before any money moves. Repeating the call re-approves without a
|
|
1877
|
+
second refund. The response adds `continued: { tierId, status, rolePromoted,
|
|
1878
|
+
emailLogged }`.
|
|
1879
|
+
- The purchaser may give an invited or accepted seat to a different person
|
|
1880
|
+
until it is active: `POST /api/named-seat/regift`
|
|
1881
|
+
`{ recipientName, recipientEmail }` from the members page
|
|
1882
|
+
(`NamedSeatPurchase` shows the control while the offer's `seat.regiftable`
|
|
1883
|
+
is true), or `POST /api/admin/applications/:id/named-seat/regift` by a super
|
|
1884
|
+
admin on the purchaser's behalf (`NamedSeatRecordPanel`). The authority's
|
|
1885
|
+
`seat-regift` command keeps the payment and renewal line exactly as they
|
|
1886
|
+
are, so an included seat works the same way; the previous invitation stops
|
|
1887
|
+
resolving, a new invitation effect is queued, the former recipient is kept
|
|
1888
|
+
on the seat's `formerRecipients`, and a recipient who had already accepted
|
|
1889
|
+
gets a `release_named_recipient` effect that closes their application as
|
|
1890
|
+
`declined` without a refund (the chapter also does this at once). Active
|
|
1891
|
+
seats are final (`seat_not_regiftable`). Upgrade and re-provision the
|
|
1892
|
+
network owner first: the seat gains optional `regiftedAt` and
|
|
1893
|
+
`formerRecipients`, and an effect optional `recipientUserId` and
|
|
1894
|
+
`recipientApplicationId`.
|
|
1895
|
+
- A second-seat invitation goes out only once the buying member is approved.
|
|
1896
|
+
`drainMembershipEffects` holds an `invite_named_recipient` effect while the
|
|
1897
|
+
primary is `paid_pending_approval` and reports it under `held`; the
|
|
1898
|
+
receiver's eligibility read says the same. The offer's `seat` carries
|
|
1899
|
+
`invitationHeld: true` meanwhile, and `NamedSeatRecipientLine` says the
|
|
1900
|
+
invitation is sent once the membership is approved.
|
|
1901
|
+
- Anyone whose earlier applications in the chapter all ended without
|
|
1902
|
+
approval (declined, refunded, canceled) may apply again from the public
|
|
1903
|
+
form. The new attempt is a new application row linked to the same person:
|
|
1904
|
+
it adopts the account bound to the closed attempt, takes over the unique
|
|
1905
|
+
signup key, and re-stages the same CRM person; the closed row stays as
|
|
1906
|
+
history. An open application still answers `account_already_exists`.
|
|
1907
|
+
`resolveSignupEligibility` returns the predecessor it chose, and
|
|
1908
|
+
`closedApplication` is the one definition of "ended without approval".
|
|
1909
|
+
|
|
1853
1910
|
Delivery logs distinguish sent, logged-only, in-flight, uncertain and
|
|
1854
1911
|
retry-approved outcomes. The authenticated operator resolution route
|
|
1855
1912
|
`/api/admin/email/log/:id/resolve` records evidence against the exact attempt;
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DEFAULT_JOIN_COPY,
|
|
3
3
|
formatChapterCopy,
|
|
4
4
|
useChapterCopy
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FHKZVF5G.js";
|
|
6
6
|
import {
|
|
7
7
|
collectFormFields
|
|
8
8
|
} from "./chunk-IVXVECKR.js";
|
|
@@ -1048,7 +1048,7 @@ var NAMED_SEAT_RECIPIENT_WORDS = {
|
|
|
1048
1048
|
};
|
|
1049
1049
|
var day = (at) => new Date(at).toLocaleDateString("en-US", { timeZone: "UTC" });
|
|
1050
1050
|
function NamedSeatRecipientLine({ seat }) {
|
|
1051
|
-
const words = NAMED_SEAT_RECIPIENT_WORDS[seat.recipientState] ?? seat.status.replaceAll("_", " ");
|
|
1051
|
+
const words = seat.invitationHeld ? "named; the invitation is sent once your membership is approved" : NAMED_SEAT_RECIPIENT_WORDS[seat.recipientState] ?? seat.status.replaceAll("_", " ");
|
|
1052
1052
|
return /* @__PURE__ */ jsxs13("p", { className: "member-named-seat-recipient", children: [
|
|
1053
1053
|
seat.recipientName,
|
|
1054
1054
|
" \xB7 ",
|
|
@@ -1062,6 +1062,7 @@ function NamedSeatPurchase({ api }) {
|
|
|
1062
1062
|
const [data, setData] = useState10(null), [error, setError] = useState10("");
|
|
1063
1063
|
const [name, setName] = useState10(""), [email, setEmail] = useState10(""), [reviewing, setReviewing] = useState10(false);
|
|
1064
1064
|
const [terms, setTerms] = useState10(false), [pending, setPending] = useState10(false), [granting, setGranting] = useState10(false);
|
|
1065
|
+
const [renaming, setRenaming] = useState10(false), [nextName, setNextName] = useState10(""), [nextEmail, setNextEmail] = useState10("");
|
|
1065
1066
|
const load = async () => {
|
|
1066
1067
|
try {
|
|
1067
1068
|
const value = await api("/api/named-seat");
|
|
@@ -1098,11 +1099,54 @@ function NamedSeatPurchase({ api }) {
|
|
|
1098
1099
|
}
|
|
1099
1100
|
};
|
|
1100
1101
|
const money = data ? new Intl.NumberFormat("en-US", { style: "currency", currency: data.currency }).format(data.amountCents / 100) : "";
|
|
1102
|
+
const regift = async (event) => {
|
|
1103
|
+
event.preventDefault();
|
|
1104
|
+
setGranting(true);
|
|
1105
|
+
setError("");
|
|
1106
|
+
try {
|
|
1107
|
+
await api("/api/named-seat/regift", {
|
|
1108
|
+
method: "POST",
|
|
1109
|
+
headers: { "content-type": "application/json" },
|
|
1110
|
+
body: JSON.stringify({ recipientName: nextName, recipientEmail: nextEmail })
|
|
1111
|
+
});
|
|
1112
|
+
setRenaming(false);
|
|
1113
|
+
await load();
|
|
1114
|
+
} catch {
|
|
1115
|
+
setError("The seat could not be given to someone else. Refresh to check its status before trying again.");
|
|
1116
|
+
} finally {
|
|
1117
|
+
setGranting(false);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1101
1120
|
return /* @__PURE__ */ jsx14(JourneyFrame, { children: /* @__PURE__ */ jsxs13("section", { className: "card member-named-seat", "aria-label": "Named additional seat", children: [
|
|
1102
1121
|
/* @__PURE__ */ jsx14("h2", { children: "Named additional seat" }),
|
|
1103
1122
|
error ? /* @__PURE__ */ jsx14("p", { role: "alert", children: error }) : null,
|
|
1104
1123
|
data ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
1105
1124
|
data.seat ? /* @__PURE__ */ jsx14(NamedSeatRecipientLine, { seat: data.seat }) : null,
|
|
1125
|
+
data.seat?.regiftable && !renaming ? /* @__PURE__ */ jsx14("button", { className: "btn secondary", type: "button", onClick: () => {
|
|
1126
|
+
setRenaming(true);
|
|
1127
|
+
setNextName("");
|
|
1128
|
+
setNextEmail("");
|
|
1129
|
+
}, children: "Give the seat to someone else" }) : null,
|
|
1130
|
+
data.seat?.regiftable && renaming ? /* @__PURE__ */ jsxs13("form", { className: "member-named-seat-regift", "aria-label": "Give the seat to someone else", onSubmit: (event) => void regift(event), children: [
|
|
1131
|
+
/* @__PURE__ */ jsxs13("p", { children: [
|
|
1132
|
+
"The seat moves to the person you name here. ",
|
|
1133
|
+
data.seat.recipientName,
|
|
1134
|
+
"'s invitation stops working, and nothing is charged or refunded."
|
|
1135
|
+
] }),
|
|
1136
|
+
/* @__PURE__ */ jsxs13("label", { children: [
|
|
1137
|
+
"New recipient name",
|
|
1138
|
+
/* @__PURE__ */ jsx14("input", { required: true, maxLength: 160, value: nextName, onInput: (event) => setNextName(event.currentTarget.value) })
|
|
1139
|
+
] }),
|
|
1140
|
+
/* @__PURE__ */ jsxs13("label", { children: [
|
|
1141
|
+
"New recipient email",
|
|
1142
|
+
/* @__PURE__ */ jsx14("input", { required: true, type: "email", maxLength: 254, value: nextEmail, onInput: (event) => setNextEmail(event.currentTarget.value) })
|
|
1143
|
+
] }),
|
|
1144
|
+
/* @__PURE__ */ jsx14("button", { className: "btn primary", type: "submit", disabled: granting, children: granting ? "Changing the recipient\u2026" : "Give the seat to this person" }),
|
|
1145
|
+
/* @__PURE__ */ jsxs13("button", { className: "btn secondary", type: "button", disabled: granting, onClick: () => setRenaming(false), children: [
|
|
1146
|
+
"Keep ",
|
|
1147
|
+
data.seat.recipientName
|
|
1148
|
+
] })
|
|
1149
|
+
] }) : null,
|
|
1106
1150
|
data.eligible ? /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
1107
1151
|
/* @__PURE__ */ jsxs13("p", { children: [
|
|
1108
1152
|
data.included ? "Included with your membership at no charge." : `${money} paid in full now.`,
|
|
@@ -2562,4 +2606,4 @@ export {
|
|
|
2562
2606
|
membershipApplicationHref,
|
|
2563
2607
|
MembershipCards
|
|
2564
2608
|
};
|
|
2565
|
-
//# sourceMappingURL=chunk-
|
|
2609
|
+
//# sourceMappingURL=chunk-CKC6X5H2.js.map
|