@odla-ai/chapter 0.54.1 → 0.54.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 +14 -3
- package/dist/{chunk-NDZCR3OW.js → chunk-ZAE24YKY.js} +54 -21
- package/dist/{chunk-NDZCR3OW.js.map → chunk-ZAE24YKY.js.map} +1 -1
- package/dist/{copy-context-qvFo6eA7.d.ts → copy-context-CaUXIxUI.d.ts} +17 -2
- package/dist/index.cjs +281 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -17
- package/dist/index.d.ts +76 -17
- package/dist/index.js +266 -117
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- package/dist/ui/admin/index.js +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/worker/index.cjs +688 -544
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +17 -2
- package/dist/worker/index.d.ts +17 -2
- package/dist/worker/index.js +634 -490
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1520,7 +1520,7 @@ These bite silently — a smoke test won't catch them:
|
|
|
1520
1520
|
| `submitConfirmation` | `POST /api/applications` (submit) — a free tier always; a paid tier only when `paymentConfirmation` is disabled, so a paid applicant is confirmed once | template `enabled` flag |
|
|
1521
1521
|
| `prepEmail` | `POST /api/schedule/book` — once per application; a reschedule sends no second one | template `enabled` flag |
|
|
1522
1522
|
| `paymentConfirmation` | Stripe webhook, first successful invoice | template `enabled` flag |
|
|
1523
|
-
| `seatAccepted` | `POST /api/named-seat/accept` — to the seat's purchaser, once per seat, naming the recipient and tier |
|
|
1523
|
+
| `seatAccepted` | `POST /api/named-seat/accept` — to the seat's purchaser, once per seat, naming the recipient and tier | explicit `enabled: true` on a stored `emailTemplates.seatAccepted`; missing copy blocks delivery |
|
|
1524
1524
|
|
|
1525
1525
|
```ts
|
|
1526
1526
|
defineChapter({ /* … */ sends: { adminNotification: "payment" } });
|
|
@@ -1528,8 +1528,19 @@ These bite silently — a smoke test won't catch them:
|
|
|
1528
1528
|
|
|
1529
1529
|
**Template variables.** Every value comes from stored data — the frozen
|
|
1530
1530
|
checkout the applicant consented to, the tier catalog, the booked meeting,
|
|
1531
|
-
the request origin — never from a price or a name written into code.
|
|
1532
|
-
unknown
|
|
1531
|
+
the request origin — never from a price or a name written into code. Sends
|
|
1532
|
+
reject unknown placeholders, missing required values, internal draft notes,
|
|
1533
|
+
incomplete copy, and unresolved tier display names. Optional blanks must be
|
|
1534
|
+
declared by the event (for example, a free tier’s payment amount suffix).
|
|
1535
|
+
|
|
1536
|
+
**Readiness on page open.** The Email page checks every built-in and host event
|
|
1537
|
+
against saved templates and current tier names. Missing templates and invalid
|
|
1538
|
+
values appear with the event’s trigger and recipient. Explicitly disabled
|
|
1539
|
+
emails stay off. `emails.events` declares custom reminder contracts; pass the
|
|
1540
|
+
same event to `sendTemplated`. Gift invitations and acceptance notices require
|
|
1541
|
+
stored copy; delivery never supplies a hidden fallback. Hub mode uses the
|
|
1542
|
+
site’s actual CRM template registry and CRM settings. All automatic sends
|
|
1543
|
+
require explicit `enabled: true`; validation never enables a template.
|
|
1533
1544
|
|
|
1534
1545
|
| Variable | Templates | Value |
|
|
1535
1546
|
|---|---|---|
|
|
@@ -2627,7 +2627,7 @@ function EmailDeliveryResolution({ row, ctx, onResolved }) {
|
|
|
2627
2627
|
}
|
|
2628
2628
|
|
|
2629
2629
|
// src/ui/admin-email.tsx
|
|
2630
|
-
import { jsx as jsx29, jsxs as jsxs27 } from "preact/jsx-runtime";
|
|
2630
|
+
import { Fragment as Fragment12, jsx as jsx29, jsxs as jsxs27 } from "preact/jsx-runtime";
|
|
2631
2631
|
var logColumns = (copy, ctx, refresh) => [
|
|
2632
2632
|
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
2633
2633
|
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
@@ -2652,7 +2652,7 @@ function EmailBody({ ctx }) {
|
|
|
2652
2652
|
"."
|
|
2653
2653
|
] });
|
|
2654
2654
|
const edit = (patch) => setDraft({ ...data, ...patch });
|
|
2655
|
-
const editTemplate = (key, patch) => edit({ emailTemplates: { ...data.emailTemplates, [key]: { ...data.emailTemplates[key], ...patch } } });
|
|
2655
|
+
const editTemplate = (key, patch) => edit({ emailTemplates: { ...data.emailTemplates, [key]: { subject: "", text: "", enabled: false, ...data.emailTemplates[key], ...patch } } });
|
|
2656
2656
|
const run = async (key, fn) => {
|
|
2657
2657
|
setBusy(key);
|
|
2658
2658
|
setNote(null);
|
|
@@ -2680,6 +2680,24 @@ function EmailBody({ ctx }) {
|
|
|
2680
2680
|
log.refresh();
|
|
2681
2681
|
});
|
|
2682
2682
|
return /* @__PURE__ */ jsxs27(AdminPage, { children: [
|
|
2683
|
+
/* @__PURE__ */ jsxs27(Panel, { title: "Email readiness", actions: /* @__PURE__ */ jsx29(Button16, { variant: "secondary", mini: true, disabled: !!draft, onClick: () => cfg.refresh(), children: "Check again" }), children: [
|
|
2684
|
+
/* @__PURE__ */ jsx29("p", { children: "Each event is checked against its saved email and current tier names when this page opens. Missing copy or required values blocks delivery." }),
|
|
2685
|
+
data.validation ? /* @__PURE__ */ jsxs27(Fragment12, { children: [
|
|
2686
|
+
/* @__PURE__ */ jsxs27("p", { children: [
|
|
2687
|
+
data.validation.events.filter((event) => event.status === "blocked").length,
|
|
2688
|
+
" blocked \xB7 ",
|
|
2689
|
+
data.validation.events.filter((event) => event.status === "disabled").length,
|
|
2690
|
+
" switched off \xB7 ",
|
|
2691
|
+
data.validation.events.filter((event) => event.status === "ready").length,
|
|
2692
|
+
" ready"
|
|
2693
|
+
] }),
|
|
2694
|
+
data.validation.issues.length ? /* @__PURE__ */ jsx29("ul", { role: "alert", children: data.validation.issues.map((issue, i) => /* @__PURE__ */ jsx29("li", { children: issue.message }, i)) }) : null,
|
|
2695
|
+
draft ? /* @__PURE__ */ jsx29("p", { role: "status", children: "Save your changes to refresh these checks." }) : /* @__PURE__ */ jsxs27("p", { className: "muted", children: [
|
|
2696
|
+
"Checked ",
|
|
2697
|
+
new Date(data.validation.checkedAt).toLocaleString()
|
|
2698
|
+
] })
|
|
2699
|
+
] }) : /* @__PURE__ */ jsx29("p", { role: "alert", children: "Email validation is unavailable. Refresh after the server has been updated." })
|
|
2700
|
+
] }),
|
|
2683
2701
|
/* @__PURE__ */ jsxs27(
|
|
2684
2702
|
Panel,
|
|
2685
2703
|
{
|
|
@@ -2693,16 +2711,31 @@ function EmailBody({ ctx }) {
|
|
|
2693
2711
|
children: [
|
|
2694
2712
|
/* @__PURE__ */ jsx29(Field13, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx29(Input6, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
2695
2713
|
/* @__PURE__ */ jsx29(Field13, { label: copy.replyTo, children: /* @__PURE__ */ jsx29(Input6, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
2696
|
-
/* @__PURE__ */ jsx29(Field13, { label: copy.fromName, hint: copy.fromNameHint, children: /* @__PURE__ */ jsx29(Input6, { value: data.fromName ?? "", onInput: (e) => edit({ fromName: e.target.value }) }) }),
|
|
2714
|
+
data.testAvailable ? /* @__PURE__ */ jsx29(Field13, { label: copy.fromName, hint: copy.fromNameHint, children: /* @__PURE__ */ jsx29(Input6, { value: data.fromName ?? "", onInput: (e) => edit({ fromName: e.target.value }) }) }) : null,
|
|
2697
2715
|
/* @__PURE__ */ jsx29(Field13, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx29(Input6, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
2698
2716
|
]
|
|
2699
2717
|
}
|
|
2700
2718
|
),
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
/* @__PURE__ */
|
|
2704
|
-
|
|
2705
|
-
|
|
2719
|
+
(data.validation?.events ?? []).map((event) => {
|
|
2720
|
+
const key = event.key, t = data.emailTemplates[key] ?? { subject: "", text: "", enabled: false };
|
|
2721
|
+
return /* @__PURE__ */ jsxs27(Panel, { title: event.label, actions: /* @__PURE__ */ jsx29("span", { children: event.status === "ready" ? "Ready" : event.status === "disabled" ? "Switched off" : "Blocked" }), children: [
|
|
2722
|
+
/* @__PURE__ */ jsxs27("p", { children: [
|
|
2723
|
+
event.trigger,
|
|
2724
|
+
". Recipient: ",
|
|
2725
|
+
event.audience,
|
|
2726
|
+
"."
|
|
2727
|
+
] }),
|
|
2728
|
+
event.issues.length ? /* @__PURE__ */ jsx29("ul", { children: event.issues.map((issue, i) => /* @__PURE__ */ jsx29("li", { children: issue.message }, i)) }) : null,
|
|
2729
|
+
/* @__PURE__ */ jsx29(Checkbox2, { label: copy.enabled, checked: t.enabled === true, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
2730
|
+
/* @__PURE__ */ jsx29(Field13, { label: copy.subject, children: /* @__PURE__ */ jsx29(Input6, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
2731
|
+
/* @__PURE__ */ jsx29(Field13, { label: copy.body, children: /* @__PURE__ */ jsx29(Textarea5, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) }),
|
|
2732
|
+
/* @__PURE__ */ jsxs27("p", { className: "muted", children: [
|
|
2733
|
+
"Available values: ",
|
|
2734
|
+
event.variables.map((name) => `{{${name}}}`).join(", ")
|
|
2735
|
+
] }),
|
|
2736
|
+
data.testAvailable ? /* @__PURE__ */ jsx29(Button16, { variant: "secondary", mini: true, disabled: !!draft || event.issues.length > 0 || busy === `test:${key}`, onClick: () => void test(key), children: copy.sendTest }) : null
|
|
2737
|
+
] }, key);
|
|
2738
|
+
}),
|
|
2706
2739
|
/* @__PURE__ */ jsxs27("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
2707
2740
|
/* @__PURE__ */ jsx29(Button16, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
2708
2741
|
note ? /* @__PURE__ */ jsx29("span", { className: "muted", children: note }) : null
|
|
@@ -3298,7 +3331,7 @@ function defaultAdminSections(chapter) {
|
|
|
3298
3331
|
// src/ui/admin-meetings.tsx
|
|
3299
3332
|
import { useState as useState28 } from "preact/hooks";
|
|
3300
3333
|
import { Button as Button17 } from "@odla-ai/ui/components";
|
|
3301
|
-
import { Fragment as
|
|
3334
|
+
import { Fragment as Fragment13, jsx as jsx35, jsxs as jsxs31 } from "preact/jsx-runtime";
|
|
3302
3335
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
3303
3336
|
function MeetingsBody({ ctx }) {
|
|
3304
3337
|
const copy = useChapterCopy().admin.meetings;
|
|
@@ -3337,7 +3370,7 @@ function MeetingsBody({ ctx }) {
|
|
|
3337
3370
|
}
|
|
3338
3371
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
3339
3372
|
};
|
|
3340
|
-
return /* @__PURE__ */ jsx35(AdminPage, { children: /* @__PURE__ */ jsx35(Panel, { title: /* @__PURE__ */ jsxs31(
|
|
3373
|
+
return /* @__PURE__ */ jsx35(AdminPage, { children: /* @__PURE__ */ jsx35(Panel, { title: /* @__PURE__ */ jsxs31(Fragment13, { children: [
|
|
3341
3374
|
copy.agenda,
|
|
3342
3375
|
" ",
|
|
3343
3376
|
/* @__PURE__ */ jsxs31("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
@@ -3354,7 +3387,7 @@ function MeetingsBody({ ctx }) {
|
|
|
3354
3387
|
m.drift
|
|
3355
3388
|
] }) : null,
|
|
3356
3389
|
m.meetUrl ? /* @__PURE__ */ jsx35("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
3357
|
-
m.status === "scheduled" ? /* @__PURE__ */ jsxs31(
|
|
3390
|
+
m.status === "scheduled" ? /* @__PURE__ */ jsxs31(Fragment13, { children: [
|
|
3358
3391
|
/* @__PURE__ */ jsx35(Button17, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
3359
3392
|
/* @__PURE__ */ jsx35(Button17, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
3360
3393
|
] }) : null
|
|
@@ -3368,7 +3401,7 @@ function meetingsSection(options = {}) {
|
|
|
3368
3401
|
// src/ui/admin-network-record-editor.tsx
|
|
3369
3402
|
import { Button as Button18, Field as Field14, Select as Select10 } from "@odla-ai/ui/components";
|
|
3370
3403
|
import { useRef as useRef4, useState as useState29 } from "preact/hooks";
|
|
3371
|
-
import { Fragment as
|
|
3404
|
+
import { Fragment as Fragment14, jsx as jsx36, jsxs as jsxs32 } from "preact/jsx-runtime";
|
|
3372
3405
|
function NetworkRecordEditor(props) {
|
|
3373
3406
|
const { ctx, record } = props;
|
|
3374
3407
|
const [stage, setStage] = useState29("");
|
|
@@ -3407,7 +3440,7 @@ function NetworkRecordEditor(props) {
|
|
|
3407
3440
|
}
|
|
3408
3441
|
};
|
|
3409
3442
|
return /* @__PURE__ */ jsxs32("div", { style: { display: "grid", gap: 24 }, children: [
|
|
3410
|
-
superAdmin && record.edit ? /* @__PURE__ */ jsxs32(
|
|
3443
|
+
superAdmin && record.edit ? /* @__PURE__ */ jsxs32(Fragment14, { children: [
|
|
3411
3444
|
/* @__PURE__ */ jsxs32("section", { children: [
|
|
3412
3445
|
/* @__PURE__ */ jsx36("h3", { children: "Chapter profile" }),
|
|
3413
3446
|
/* @__PURE__ */ jsx36(
|
|
@@ -3508,7 +3541,7 @@ function NetworkSeatBilling(props) {
|
|
|
3508
3541
|
// src/ui/membership-policy-versions.tsx
|
|
3509
3542
|
import { Button as Button20, DataTable as DataTable5 } from "@odla-ai/ui/components";
|
|
3510
3543
|
import { useState as useState31 } from "preact/hooks";
|
|
3511
|
-
import { Fragment as
|
|
3544
|
+
import { Fragment as Fragment15, jsx as jsx38, jsxs as jsxs34 } from "preact/jsx-runtime";
|
|
3512
3545
|
var PATH = "/api/admin/membership-policy-versions";
|
|
3513
3546
|
var RETERM = "/api/admin/membership-discount/reterm";
|
|
3514
3547
|
var day = (ms) => new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
|
@@ -3524,17 +3557,17 @@ var State = ({ row }) => /* @__PURE__ */ jsx38("span", { className: row.state ==
|
|
|
3524
3557
|
var superseded = (row) => row.supersededAt === void 0 ? "\u2014" : day(row.supersededAt);
|
|
3525
3558
|
var Source = ({ row }) => /* @__PURE__ */ jsxs34("span", { children: [
|
|
3526
3559
|
row.source ?? /* @__PURE__ */ jsx38("span", { className: "muted", children: "Unrecorded" }),
|
|
3527
|
-
row.actor ? /* @__PURE__ */ jsxs34(
|
|
3560
|
+
row.actor ? /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3528
3561
|
" \xB7 ",
|
|
3529
3562
|
/* @__PURE__ */ jsx38("code", { children: row.actor })
|
|
3530
3563
|
] }) : null,
|
|
3531
|
-
row.recordedAt !== void 0 ? /* @__PURE__ */ jsxs34(
|
|
3564
|
+
row.recordedAt !== void 0 ? /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3532
3565
|
" \xB7 ",
|
|
3533
3566
|
day(row.recordedAt)
|
|
3534
3567
|
] }) : null
|
|
3535
3568
|
] });
|
|
3536
3569
|
var offeringColumns = [
|
|
3537
|
-
{ key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs34(
|
|
3570
|
+
{ key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3538
3571
|
/* @__PURE__ */ jsxs34("code", { children: [
|
|
3539
3572
|
"v",
|
|
3540
3573
|
row.version
|
|
@@ -3552,7 +3585,7 @@ var offeringColumns = [
|
|
|
3552
3585
|
{ key: "source", header: "Source", cell: (row) => /* @__PURE__ */ jsx38(Source, { row }) }
|
|
3553
3586
|
];
|
|
3554
3587
|
var policyColumns = (getToken) => [
|
|
3555
|
-
{ key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs34(
|
|
3588
|
+
{ key: "version", header: "Version", cell: (row) => /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3556
3589
|
/* @__PURE__ */ jsxs34("code", { children: [
|
|
3557
3590
|
"v",
|
|
3558
3591
|
row.version
|
|
@@ -3560,7 +3593,7 @@ var policyColumns = (getToken) => [
|
|
|
3560
3593
|
" ",
|
|
3561
3594
|
/* @__PURE__ */ jsx38(State, { row })
|
|
3562
3595
|
] }), sortAs: "number", sortValue: (row) => row.version },
|
|
3563
|
-
{ key: "cohort", header: "Cohort", cell: (row) => /* @__PURE__ */ jsxs34(
|
|
3596
|
+
{ key: "cohort", header: "Cohort", cell: (row) => /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3564
3597
|
row.cohortLabel,
|
|
3565
3598
|
" ",
|
|
3566
3599
|
/* @__PURE__ */ jsxs34("span", { className: "muted", children: [
|
|
@@ -3617,7 +3650,7 @@ function MembershipPolicyVersions(props) {
|
|
|
3617
3650
|
"Offering versions could not be loaded: ",
|
|
3618
3651
|
error ?? "empty response"
|
|
3619
3652
|
] }) });
|
|
3620
|
-
return /* @__PURE__ */ jsxs34(
|
|
3653
|
+
return /* @__PURE__ */ jsxs34(Fragment15, { children: [
|
|
3621
3654
|
/* @__PURE__ */ jsxs34(Panel, { title: "Membership offering versions", actions: data.truncated ? /* @__PURE__ */ jsx38("span", { className: "badge", children: "Truncated" }) : void 0, children: [
|
|
3622
3655
|
/* @__PURE__ */ jsxs34("p", { className: "muted", children: [
|
|
3623
3656
|
"The list price a card shows comes from the active offering; the payable price applies the active discount policy. State is judged as of ",
|
|
@@ -3704,4 +3737,4 @@ export {
|
|
|
3704
3737
|
NetworkSeatBilling,
|
|
3705
3738
|
MembershipPolicyVersions
|
|
3706
3739
|
};
|
|
3707
|
-
//# sourceMappingURL=chunk-
|
|
3740
|
+
//# sourceMappingURL=chunk-ZAE24YKY.js.map
|