@odla-ai/chapter 0.23.0 → 0.24.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 +2 -2
- package/dist/{chunk-OYXNI6VR.js → chunk-GMGTJIY3.js} +28 -15
- package/dist/chunk-GMGTJIY3.js.map +1 -0
- package/dist/{chunk-NTZSXAFT.js → chunk-WGQO4WDJ.js} +3 -2
- package/dist/chunk-WGQO4WDJ.js.map +1 -0
- package/dist/{chunk-VCC4S6JU.js → chunk-ZPAJL7KG.js} +28 -21
- package/dist/chunk-ZPAJL7KG.js.map +1 -0
- package/dist/{copy-context-pDogTR4x.d.ts → copy-context-Dp5hvj5W.d.ts} +8 -1
- package/dist/index.cjs +19 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- 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 +2 -2
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +97 -86
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +8 -1
- package/dist/worker/index.d.ts +8 -1
- package/dist/worker/index.js +97 -86
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/adopt-existing.md +1 -1
- package/runbooks/greenfield.md +3 -3
- package/dist/chunk-NTZSXAFT.js.map +0 -1
- package/dist/chunk-OYXNI6VR.js.map +0 -1
- package/dist/chunk-VCC4S6JU.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ email. The host still builds the public pages, routing, and brand presentation;
|
|
|
8
8
|
Chapter supplies their application mechanics.
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
npm i --save-exact @odla-ai/chapter@0.
|
|
11
|
+
npm i --save-exact @odla-ai/chapter@0.24.0
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
> **Agentic experiment.** Built and maintained by AI agents from bounded runbooks
|
|
@@ -654,7 +654,7 @@ These bite silently — a smoke test won't catch them:
|
|
|
654
654
|
invitation mail. Importing the full `@odla-ai/chapter/ui` barrel pulls the admin
|
|
655
655
|
half, so prefer the narrower entry.
|
|
656
656
|
- **Known-good application set** (installs clean, no flags):
|
|
657
|
-
`@odla-ai/chapter` 0.
|
|
657
|
+
`@odla-ai/chapter` 0.24.0, `@odla-ai/brand` 0.2.0,
|
|
658
658
|
`@odla-ai/ui` 0.12.1, `@odla-ai/crm` 0.3.1,
|
|
659
659
|
`@odla-ai/db` 0.6.7,
|
|
660
660
|
`@odla-ai/calendar` 0.2.0, `@odla-ai/email` 0.3.1,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatChapterCopy,
|
|
3
3
|
useChapterCopy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WGQO4WDJ.js";
|
|
5
5
|
|
|
6
6
|
// src/ui/slot-picker.tsx
|
|
7
7
|
import { useMemo, useState } from "preact/hooks";
|
|
@@ -416,18 +416,21 @@ function PaymentStep(props) {
|
|
|
416
416
|
}
|
|
417
417
|
};
|
|
418
418
|
return /* @__PURE__ */ jsxs4("div", { className: "join-pay", children: [
|
|
419
|
-
/* @__PURE__ */ jsxs4("
|
|
420
|
-
/* @__PURE__ */ jsx4(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
419
|
+
/* @__PURE__ */ jsxs4("div", { className: "compliance-box", children: [
|
|
420
|
+
/* @__PURE__ */ jsx4("p", { className: "compliance-policy", children: refundPolicyText }),
|
|
421
|
+
/* @__PURE__ */ jsxs4("label", { className: "compliance-check", children: [
|
|
422
|
+
/* @__PURE__ */ jsx4(
|
|
423
|
+
"input",
|
|
424
|
+
{
|
|
425
|
+
type: "checkbox",
|
|
426
|
+
disabled: status !== "idle",
|
|
427
|
+
onChange: (e) => {
|
|
428
|
+
if (e.currentTarget.checked) void begin();
|
|
429
|
+
}
|
|
427
430
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
+
),
|
|
432
|
+
copy.consent ? /* @__PURE__ */ jsx4("span", { children: copy.consent }) : null
|
|
433
|
+
] })
|
|
431
434
|
] }),
|
|
432
435
|
status === "loading" ? /* @__PURE__ */ jsx4("p", { className: "pay-status", children: copy.preparing }) : null,
|
|
433
436
|
lineItems && renderPriceLines ? renderPriceLines(lineItems) : null,
|
|
@@ -438,6 +441,17 @@ function PaymentStep(props) {
|
|
|
438
441
|
] });
|
|
439
442
|
}
|
|
440
443
|
|
|
444
|
+
// src/ui/form-fields.ts
|
|
445
|
+
function collectFormFields(form) {
|
|
446
|
+
const fields = {};
|
|
447
|
+
for (const key of new Set(form.keys())) {
|
|
448
|
+
const values = form.getAll(key).filter((v) => typeof v === "string");
|
|
449
|
+
if (values.length === 0) continue;
|
|
450
|
+
fields[key] = values.length > 1 ? values : values[0];
|
|
451
|
+
}
|
|
452
|
+
return fields;
|
|
453
|
+
}
|
|
454
|
+
|
|
441
455
|
// src/ui/join-booking.tsx
|
|
442
456
|
import { useEffect as useEffect2, useState as useState5 } from "preact/hooks";
|
|
443
457
|
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
@@ -582,8 +596,7 @@ function JoinIsland(props) {
|
|
|
582
596
|
setSubmitting(true);
|
|
583
597
|
setError(null);
|
|
584
598
|
try {
|
|
585
|
-
const fields =
|
|
586
|
-
for (const [k, v] of new FormData(e.currentTarget).entries()) fields[k] = v;
|
|
599
|
+
const fields = collectFormFields(new FormData(e.currentTarget));
|
|
587
600
|
fields.submissionId = crypto.randomUUID();
|
|
588
601
|
const res = await fetch("/api/applications", {
|
|
589
602
|
method: "POST",
|
|
@@ -684,4 +697,4 @@ export {
|
|
|
684
697
|
loadJoinResume,
|
|
685
698
|
JoinIsland
|
|
686
699
|
};
|
|
687
|
-
//# sourceMappingURL=chunk-
|
|
700
|
+
//# sourceMappingURL=chunk-GMGTJIY3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/slot-picker.tsx","../src/ui/datetime.ts","../src/ui/members.tsx","../src/ui/reschedule.tsx","../src/ui/join.tsx","../src/ui/payment-step.tsx","../src/ui/form-fields.ts","../src/ui/join-booking.tsx"],"sourcesContent":["// SlotPicker — a pure presentational day-chips + time-grid picker, shared by the\n// join booking step and the member-area reschedule. Ported from the proven\n// reference Preact island; behaviour and DOM shape are unchanged.\n//\n// It holds only the active-day UI state; slot data, selection, and booking are\n// the caller's (the picker is `onPick`-driven). `classes` lets the composing\n// island supply its own CSS contract (join styles `slot-*`, reschedule\n// `msched-*`); the defaults work standalone against those class names.\nimport { useMemo, useState } from \"preact/hooks\";\nimport { dayLabel, groupSlotsByDay, timeLabel } from \"./datetime.js\";\nimport type { Slot } from \"./datetime.js\";\n\n/** CSS class names for the four picker parts — overridable so one component\n * serves several differently-styled surfaces. */\nexport interface SlotPickerClasses {\n days: string;\n day: string;\n times: string;\n time: string;\n}\n\nconst DEFAULT_CLASSES: SlotPickerClasses = {\n days: \"slot-days\",\n day: \"slot-day\",\n times: \"slot-grid\",\n time: \"slot-time\",\n};\n\n/** Props for {@link SlotPicker}. Generic over the slot type so a caller's richer\n * slot object survives through `onPick`. */\nexport interface SlotPickerProps<T extends Slot = Slot> {\n /** Bookable slots, ideally already chronological. */\n slots: readonly T[];\n /** The group's scheduling timezone; all labels render in it. */\n timezone: string;\n /** The currently-selected slot's start, for the pressed state. */\n selectedStartAt?: number;\n /** Called with the full slot object when a time is chosen. */\n onPick: (slot: T) => void;\n /** Called when the active day changes (callers clear their selection). */\n onDayChange?: () => void;\n classes?: SlotPickerClasses;\n}\n\n/** A day-chips + time-grid slot picker. Presentational and `onPick`-driven: it\n * owns only the active-day selection; the caller owns slot data, the chosen\n * slot, and booking. Shared by the join booking step and the member reschedule. */\nexport function SlotPicker<T extends Slot = Slot>(props: SlotPickerProps<T>) {\n const { slots, timezone, selectedStartAt, onPick, onDayChange, classes = DEFAULT_CLASSES } = props;\n const byDay = useMemo(() => groupSlotsByDay(slots, timezone), [slots, timezone]);\n const dayKeys = [...byDay.keys()];\n const [activeDay, setActiveDay] = useState<string | undefined>(dayKeys[0]);\n\n // Keep the active day valid if the slot set changed under us (e.g. a reload\n // after a 409): fall back to the first available day.\n const day = activeDay !== undefined && byDay.has(activeDay) ? activeDay : dayKeys[0];\n const times = (day !== undefined ? byDay.get(day) : undefined) ?? [];\n\n return (\n <>\n <div className={classes.days}>\n {dayKeys.map((key) => {\n const first = byDay.get(key)?.[0];\n return (\n <button\n key={key}\n type=\"button\"\n className={classes.day}\n aria-pressed={key === day}\n onClick={() => {\n setActiveDay(key);\n onDayChange?.();\n }}\n >\n {first ? dayLabel(first.startAt, timezone) : key}\n </button>\n );\n })}\n </div>\n <div className={classes.times}>\n {times.map((s) => (\n <button\n key={s.startAt}\n type=\"button\"\n className={classes.time}\n aria-pressed={selectedStartAt === s.startAt}\n onClick={() => onPick(s)}\n >\n {timeLabel(s.startAt, timezone)}\n </button>\n ))}\n </div>\n </>\n );\n}\n","// Date/timezone formatting for the chapter member islands (join booking, member\n// area). Ported from proven production helpers. Every time renders in the\n// group's SCHEDULING timezone with an explicit abbreviation — never the viewer's\n// unlabeled local zone — so an applicant always sees the time the chapter meant.\n// Pure and framework-free, so it is unit-testable without a DOM.\n\n/** A bookable slot: a start instant in epoch milliseconds. */\nexport interface Slot {\n startAt: number;\n}\n\n/** The timezone's short abbreviation (e.g. \"PST\"); falls back to the id if the\n * runtime can't resolve it. */\nexport function tzShort(tz: string): string {\n try {\n const parts = new Intl.DateTimeFormat(undefined, { timeZone: tz, timeZoneName: \"short\" }).formatToParts(\n new Date(),\n );\n return parts.find((p) => p.type === \"timeZoneName\")?.value ?? tz;\n } catch {\n return tz;\n }\n}\n\n/** DST-safe day bucket key (ISO date in the target zone). Groups slots by the\n * calendar day a viewer in `tz` would see, not by UTC midnight. */\nexport function dayKey(ms: number, tz: string): string {\n return new Date(ms).toLocaleDateString(\"en-CA\", { timeZone: tz });\n}\n\n/** Short day label for a day chip, e.g. \"Mon, Jun 3\". */\nexport function dayLabel(ms: number, tz: string): string {\n return new Date(ms).toLocaleDateString(undefined, { timeZone: tz, weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\n/** Time-of-day label for a slot button, e.g. \"2:30 PM\". */\nexport function timeLabel(ms: number, tz: string): string {\n return new Date(ms).toLocaleTimeString(undefined, { timeZone: tz, hour: \"numeric\", minute: \"2-digit\" });\n}\n\n/** Full, human confirmation label, e.g. \"Monday, June 3, 2:30 PM PST\". */\nexport function fullLabel(ms: number, tz: string): string {\n return new Date(ms).toLocaleString(undefined, {\n timeZone: tz,\n weekday: \"long\",\n month: \"long\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n timeZoneName: \"short\",\n });\n}\n\n/** Currency label from integer cents, e.g. 100000 → \"$1,000\". */\nexport function fmtMoney(cents: number): string {\n return \"$\" + Math.round(cents / 100).toLocaleString();\n}\n\n/** Short date label, e.g. \"Jun 3, 2026\" (used for renewal/refund copy). */\nexport function fmtDate(ms: number): string {\n return new Date(ms).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\", year: \"numeric\" });\n}\n\n/** Bucket slots into an insertion-ordered `Map<dayKey, Slot[]>` for the picker.\n * Input order is preserved within each day, so a server that returns slots\n * chronologically yields chronological chips + times. */\nexport function groupSlotsByDay<T extends Slot>(slots: readonly T[], tz: string): Map<string, T[]> {\n const byDay = new Map<string, T[]>();\n for (const s of slots) {\n const k = dayKey(s.startAt, tz);\n const bucket = byDay.get(k);\n if (bucket) bucket.push(s);\n else byDay.set(k, [s]);\n }\n return byDay;\n}\n","// The member area island. Reads GET /api/me (role + the member's own\n// application, folded server-side) and renders the account header plus, for a\n// provisional member, their application card with book/reschedule; a full member\n// sees the caller-supplied `memberContent`. Ported from the reference members island to\n// Preact, driven by the injected `api` (no Clerk SDK dependency).\nimport type { ComponentChildren } from \"preact\";\nimport { useEffect, useState } from \"preact/hooks\";\nimport { Rescheduler } from \"./reschedule.js\";\nimport { fmtDate, fullLabel } from \"./datetime.js\";\nimport type { ApiFn } from \"./api.js\";\nimport type { MemberApplication } from \"../session.js\";\nimport { formatChapterCopy, type ChapterCopy } from \"../copy.js\";\nimport { useChapterCopy } from \"./copy-context.js\";\n\ninterface Me {\n email: string | null;\n role: string;\n application: MemberApplication | null;\n}\n\n/** Props for {@link MembersArea}. */\nexport interface MembersAreaProps {\n api: ApiFn;\n /** Sign the member out (the host owns Clerk's signOut). */\n signOut: () => void;\n /** Where the \"Admin console\" link points for admins. Default \"/admin/\". */\n adminHref?: string;\n /** Where an accountless member goes to apply. Default \"/join.html\". */\n applyHref?: string;\n /** What a full (non-provisional) member sees below the account header. */\n memberContent?: ComponentChildren;\n /** Resolved member-area copy. */\n copy?: ChapterCopy[\"members\"];\n /** Replace the provisional application card without replacing auth/data load. */\n renderProvisional?: (context: MemberProvisionalRenderContext) => ComponentChildren;\n}\n\n/** Data and actions supplied to a site's provisional-member render slot. */\nexport interface MemberProvisionalRenderContext {\n api: ApiFn;\n application: MemberApplication | null;\n applyHref: string;\n reload: () => Promise<void>;\n defaultContent: ComponentChildren;\n}\n\nfunction card(copy: ChapterCopy[\"members\"], kicker: string, body: ComponentChildren): ComponentChildren {\n return (\n <div className=\"card\">\n <div className=\"card-label\">{copy.provisional.cardLabel}</div>\n <div className=\"meeting-block\">\n <div className=\"meeting-kicker\">{kicker}</div>\n {body}\n </div>\n </div>\n );\n}\n\nfunction ProvisionalCard(props: {\n api: ApiFn;\n application: MemberApplication | null;\n applyHref: string;\n copy: ChapterCopy[\"members\"];\n onReschedule: () => void | Promise<void>;\n}): ComponentChildren {\n const { api, application, applyHref, copy, onReschedule } = props;\n if (!application) {\n return card(\n copy,\n copy.provisional.applicationNeeded,\n <>\n <div className=\"meeting-note\">{copy.provisional.applicationNeededBody}</div>\n <a className=\"apply-link\" href={applyHref}>\n {copy.provisional.apply}\n </a>\n </>,\n );\n }\n if (application.status === \"refunded\") {\n return card(\n copy,\n copy.provisional.refunded,\n <div className=\"meeting-note\">{copy.provisional.refundedBody}</div>,\n );\n }\n const membership = application.paid ? (\n <div className=\"meeting-note\">\n {application.renewalAt\n ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) })\n : copy.provisional.active}\n </div>\n ) : null;\n if (application.meetingAt) {\n return card(\n copy,\n copy.provisional.introductionCall,\n <>\n <div className=\"meeting-date\">{fullLabel(application.meetingAt, application.timezone)}</div>\n <div className=\"meeting-note\">{copy.provisional.calendarInvite}</div>\n {application.meetUrl ? (\n <div className=\"meeting-note\">\n <a href={application.meetUrl} target=\"_blank\" rel=\"noopener\">\n {copy.provisional.joinCall}\n </a>\n </div>\n ) : null}\n <Rescheduler\n api={api}\n applicationId={application.id}\n timezone={application.timezone}\n copy={copy.reschedule}\n onRescheduled={onReschedule}\n />\n {membership}\n </>,\n );\n }\n return card(\n copy,\n copy.provisional.bookCall,\n <>\n <div className=\"meeting-note\">{copy.provisional.bookCallBody}</div>\n <Rescheduler\n api={api}\n applicationId={application.id}\n timezone={application.timezone}\n copy={copy.reschedule}\n onRescheduled={onReschedule}\n label={copy.provisional.chooseTime}\n />\n {membership}\n </>,\n );\n}\n\n/** The signed-in member area. Loads /api/me on mount and renders the account\n * header plus the provisional application card or the full-member content. */\nexport function MembersArea(props: MembersAreaProps) {\n const inheritedCopy = useChapterCopy();\n const {\n api,\n signOut,\n adminHref = \"/admin/\",\n applyHref = \"/join.html\",\n memberContent,\n renderProvisional,\n } = props;\n const copy = props.copy ?? inheritedCopy.members;\n const [me, setMe] = useState<Me | null>(null);\n const [error, setError] = useState(false);\n\n const reload = async () => {\n try {\n setMe(await api<Me>(\"/api/me\"));\n } catch {\n setError(true);\n }\n };\n useEffect(() => {\n void reload();\n }, []);\n\n if (error) return <p className=\"meeting-note\">{copy.loadFailed}</p>;\n if (!me) return <p className=\"meeting-note\">{inheritedCopy.common.loading}</p>;\n const role = me.role || \"provisional\";\n\n return (\n <>\n <div className=\"card\">\n <div className=\"member-row\">\n <span className=\"member-email\">{me.email}</span>\n <span className={\"role-badge \" + role}>{role}</span>\n </div>\n <div className=\"account-actions\">\n <button className=\"btn secondary mini\" onClick={signOut}>\n {copy.account.signOut}\n </button>\n {role === \"admin\" ? (\n <a className=\"admin-console-link\" href={adminHref}>\n {copy.account.adminConsole}\n </a>\n ) : null}\n </div>\n </div>\n {role === \"provisional\" ? (() => {\n const defaultContent = (\n <ProvisionalCard\n api={api}\n application={me.application}\n applyHref={applyHref}\n copy={copy}\n onReschedule={reload}\n />\n );\n return renderProvisional?.({\n api,\n application: me.application,\n applyHref,\n reload,\n defaultContent,\n }) ?? defaultContent;\n })() : (\n (memberContent ?? (\n <div className=\"card\">\n <div className=\"card-label\">{copy.full.welcome}</div>\n </div>\n ))\n )}\n </>\n );\n}\n","// In-place reschedule: open a SlotPicker, rebook via /api/schedule/book (the same\n// capability the join flow uses; the application id is the credential). Ported\n// from the reference member-area Rescheduler, driven by the injected `api`.\nimport { useState } from \"preact/hooks\";\nimport { SlotPicker } from \"./slot-picker.js\";\nimport type { Slot } from \"./datetime.js\";\nimport type { ApiFn } from \"./api.js\";\nimport type { ChapterCopy } from \"../copy.js\";\nimport { useChapterCopy } from \"./copy-context.js\";\n\ninterface SlotsResponse {\n schedulingReady?: boolean;\n slots?: Slot[];\n timezone?: string;\n}\n\n/** Props for {@link Rescheduler}. */\nexport interface RescheduleProps {\n api: ApiFn;\n applicationId: string;\n timezone: string;\n /** Called after a successful (re)booking so the caller can refresh /api/me. */\n onRescheduled: () => void | Promise<void>;\n /** Link text to open the picker (e.g. \"Choose a time\" for a first booking). */\n label?: string;\n /** Resolved rescheduling copy. */\n copy?: ChapterCopy[\"members\"][\"reschedule\"];\n}\n\n/** A collapsed \"change your time\" link that expands into a {@link SlotPicker} and\n * rebooks on pick. Degrades to a message when scheduling is unavailable. */\nexport function Rescheduler(props: RescheduleProps) {\n const inheritedCopy = useChapterCopy();\n const copy = props.copy ?? inheritedCopy.members.reschedule;\n const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;\n const [open, setOpen] = useState(false);\n const [slots, setSlots] = useState<Slot[] | null>(null);\n const [tz, setTz] = useState(timezone);\n const [busy, setBusy] = useState(false);\n const [msg, setMsg] = useState<string | null>(null);\n\n const start = async () => {\n setOpen(true);\n setSlots(null);\n setMsg(null);\n try {\n const r = await api<SlotsResponse>(\"/api/schedule/slots\");\n if (r.schedulingReady === false) {\n setSlots([]);\n setMsg(copy.unavailable);\n return;\n }\n setSlots(r.slots ?? []);\n if (r.timezone) setTz(r.timezone);\n } catch {\n setSlots([]);\n setMsg(copy.loadFailed);\n }\n };\n\n const pick = async (slot: Slot) => {\n setBusy(true);\n setMsg(null);\n try {\n await api(\"/api/schedule/book\", { method: \"POST\", body: JSON.stringify({ applicationId, startAt: slot.startAt }) });\n setOpen(false);\n await onRescheduled();\n } catch (e) {\n setMsg(e instanceof Error ? e.message : copy.slotGone);\n } finally {\n setBusy(false);\n }\n };\n\n if (!open) {\n return (\n <p className=\"meeting-note\">\n <a\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n void start();\n }}\n >\n {label}\n </a>\n </p>\n );\n }\n return (\n <div className=\"msched\">\n {slots === null ? (\n <p className=\"meeting-note\">{copy.loading}</p>\n ) : slots.length === 0 ? (\n <p className=\"meeting-note\">{msg ?? copy.noTimes}</p>\n ) : (\n <SlotPicker\n slots={slots}\n timezone={tz}\n classes={{ days: \"msched-days\", day: \"msched-day\", times: \"msched-times\", time: \"msched-time\" }}\n onPick={(s) => void pick(s)}\n />\n )}\n {busy ? <p className=\"meeting-note\">{copy.rescheduling}</p> : null}\n {msg && slots && slots.length > 0 ? <p className=\"meeting-note error\">{msg}</p> : null}\n <p className=\"meeting-note\">\n <a\n href=\"#\"\n onClick={(e) => {\n e.preventDefault();\n setOpen(false);\n }}\n >\n {copy.keepTime}\n </a>\n </p>\n </div>\n );\n}\n","// The join island — the signup flow orchestrator. The SITE provides the form\n// fields (as children); this owns the flow: submit → /api/applications →\n// (paymentsReady) the Stripe PaymentStep → (schedulingReady) the SlotPicker\n// booking → confirmation. Faithful to the reference join flow, but the flow +\n// payment + booking are packaged instead of hand-rolled per site.\nimport type { ComponentChildren, JSX } from \"preact\";\nimport { useEffect, useState } from \"preact/hooks\";\nimport { PaymentStep } from \"./payment-step.js\";\nimport { fullLabel } from \"./datetime.js\";\nimport type { ChapterCopy } from \"../copy.js\";\nimport { useChapterCopy } from \"./copy-context.js\";\nimport { collectFormFields } from \"./form-fields.js\";\nimport { JoinBooking } from \"./join-booking.js\";\n\n/** The public join config (the shape `GET /api/join-config` returns). */\nexport interface JoinConfig {\n id: string;\n name: string;\n paymentsReady: boolean;\n refundPolicyText?: string;\n /** Resolved copy for the packaged join flow. */\n copy?: ChapterCopy[\"join\"];\n}\n\n/** Server-validated state used to resume a join journey after a redirect. */\nexport type JoinFlowState =\n | { step: \"form\" }\n | { step: \"payment\"; applicationId: string }\n | { step: \"paymentPending\"; applicationId: string }\n | { step: \"booking\"; applicationId: string }\n | {\n step: \"done\";\n applicationId: string;\n booked: { startAt: number; timezone: string };\n };\n\n/** Canonical flow state supplied to a site's per-step heading render slot. */\nexport interface JoinStepRenderContext {\n state: JoinFlowState;\n applicationId?: string;\n}\n\n/** Submission state supplied to a site's submit-control render slot. */\nexport interface JoinSubmitRenderContext {\n submitting: boolean;\n disabled: boolean;\n}\n\ninterface JoinResumeResponse {\n state: \"payment\" | \"paymentPending\" | \"booking\" | \"done\";\n applicationId: string;\n booked?: { startAt: number; timezone: string };\n}\n\n/** Load canonical server state for a capability-bearing application id. */\nexport async function loadJoinResume(\n applicationId: string,\n fetcher: typeof fetch = fetch,\n): Promise<JoinFlowState> {\n const res = await fetcher(`/api/join/resume?application=${encodeURIComponent(applicationId)}`);\n const data = (await res.json()) as JoinResumeResponse & { error?: string };\n if (!res.ok) throw new Error(data.error ?? \"The application could not be resumed.\");\n if (data.state === \"done\" && data.booked) {\n return { step: \"done\", applicationId: data.applicationId, booked: data.booked };\n }\n if (data.state === \"paymentPending\") {\n return { step: \"paymentPending\", applicationId: data.applicationId };\n }\n if (data.state === \"booking\") {\n return { step: \"booking\", applicationId: data.applicationId };\n }\n return { step: \"payment\", applicationId: data.applicationId };\n}\n\n/** Props for {@link JoinIsland}. */\nexport interface JoinIslandProps {\n config: JoinConfig;\n /** The site's application form fields — inputs with `name` attributes; their\n * values are collected via FormData and posted to /api/applications. */\n children: ComponentChildren;\n /** Where the confirmation links after booking. Default \"/members/\". */\n membersHref?: string;\n /** Resolved join copy. Defaults to `config.copy`, then the nearest provider. */\n copy?: ChapterCopy[\"join\"];\n /** Trusted initial state produced by the server, never raw URL parameters. */\n initialState?: JoinFlowState;\n /** Resolve redirect/query state against the server before resuming the flow. */\n loadResume?: () => Promise<JoinFlowState | null>;\n /** Render a heading or other site-owned framing above each packaged step. */\n renderStepHeader?: (context: JoinStepRenderContext) => ComponentChildren;\n /** Replace the packaged completion card while retaining flow orchestration. */\n renderDone?: (\n context: Extract<JoinFlowState, { step: \"done\" }> & { membersHref: string },\n ) => ComponentChildren;\n /** Replace the submit control without replacing the application form. */\n renderSubmit?: (context: JoinSubmitRenderContext) => ComponentChildren;\n /** Additional host validation gate for the submit action. */\n submitDisabled?: boolean;\n /** Stripe Elements presentation and site-owned payment content. */\n payment?: {\n appearance?: Record<string, unknown>;\n fonts?: readonly Record<string, unknown>[];\n renderPriceLines?: (lines: {\n standardCents: number;\n discountCents: number;\n dueTodayCents: number;\n }) => ComponentChildren;\n children?: ComponentChildren;\n };\n}\n\n/** The signup island. Renders the site's form, then drives payment (when the\n * chapter charges) and booking (when a calendar is connected) to confirmation. */\nexport function JoinIsland(props: JoinIslandProps) {\n const inheritedCopy = useChapterCopy();\n const {\n config,\n children,\n membersHref = \"/members/\",\n renderStepHeader,\n renderDone,\n renderSubmit,\n } = props;\n const copy = props.copy ?? config.copy ?? inheritedCopy.join;\n const [state, setState] = useState<JoinFlowState>(props.initialState ?? { step: \"form\" });\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect(() => {\n const resolver = props.loadResume ?? (() => {\n if (typeof window === \"undefined\") return Promise.resolve(null);\n const applicationId = new URL(window.location.href).searchParams.get(\"application\");\n return applicationId ? loadJoinResume(applicationId) : Promise.resolve(null);\n });\n let live = true;\n void resolver()\n .then((resumed) => {\n if (live && resumed) setState(resumed);\n })\n .catch(() => {\n if (live) setError(copy.form.unexpectedFailure);\n });\n return () => {\n live = false;\n };\n }, [props.loadResume]);\n\n useEffect(() => {\n if (state.step !== \"paymentPending\") return;\n let live = true;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const poll = async () => {\n try {\n const resumed = props.loadResume\n ? await props.loadResume()\n : await loadJoinResume(state.applicationId);\n if (!live || !resumed) return;\n setState(resumed);\n if (resumed.step === \"paymentPending\") timer = setTimeout(() => void poll(), 1500);\n } catch {\n if (live) timer = setTimeout(() => void poll(), 2500);\n }\n };\n timer = setTimeout(() => void poll(), 1000);\n return () => {\n live = false;\n if (timer) clearTimeout(timer);\n };\n }, [state, props.loadResume]);\n\n const submit = async (e: JSX.TargetedSubmitEvent<HTMLFormElement>) => {\n e.preventDefault();\n if (submitting || props.submitDisabled === true) return;\n setSubmitting(true);\n setError(null);\n try {\n const fields = collectFormFields(new FormData(e.currentTarget));\n fields.submissionId = crypto.randomUUID();\n const res = await fetch(\"/api/applications\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(fields),\n });\n const data = (await res.json()) as { id?: string; error?: string };\n if (!res.ok || !data.id) throw new Error(data.error ?? copy.form.submitFailed);\n setState({\n step: config.paymentsReady ? \"payment\" : \"booking\",\n applicationId: data.id,\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : copy.form.unexpectedFailure);\n } finally {\n setSubmitting(false);\n }\n };\n\n const header = renderStepHeader?.({\n state,\n ...(\"applicationId\" in state ? { applicationId: state.applicationId } : {}),\n });\n if (state.step === \"done\") {\n const body = renderDone?.({ ...state, membersHref }) ?? (\n <div className=\"join-done card\">\n <div className=\"card-label\">{copy.done.label}</div>\n <p className=\"meeting-date\">{fullLabel(state.booked.startAt, state.booked.timezone)}</p>\n <p className=\"meeting-note\">{copy.done.calendarInvite}</p>\n <a className=\"apply-link\" href={membersHref}>\n {copy.done.memberArea}\n </a>\n </div>\n );\n return <>{header}{body}</>;\n }\n if (state.step === \"booking\") {\n return (\n <>\n {header}\n <JoinBooking\n applicationId={state.applicationId}\n copy={copy.booking}\n onBooked={(booked) => {\n setState({ step: \"done\", applicationId: state.applicationId, booked });\n }}\n />\n </>\n );\n }\n if (state.step === \"payment\") {\n return (\n <>\n {header}\n <PaymentStep\n applicationId={state.applicationId}\n refundPolicyText={config.refundPolicyText ?? \"\"}\n copy={copy.payment}\n appearance={props.payment?.appearance}\n fonts={props.payment?.fonts}\n renderPriceLines={props.payment?.renderPriceLines}\n onPaid={() => setState({ step: \"booking\", applicationId: state.applicationId })}\n >\n {props.payment?.children}\n </PaymentStep>\n </>\n );\n }\n if (state.step === \"paymentPending\") {\n return (\n <>\n {header}\n <p className=\"pay-status\" role=\"status\">{copy.payment.pending}</p>\n </>\n );\n }\n const submitDisabled = submitting || props.submitDisabled === true;\n return (\n <>\n {header}\n <form className=\"join-form\" onSubmit={(e) => void submit(e)}>\n {children}\n {error ? <p className=\"join-error\">{error}</p> : null}\n {renderSubmit?.({ submitting, disabled: submitDisabled }) ?? (\n <button className=\"submit-btn\" type=\"submit\" disabled={submitDisabled}>\n {submitting ? copy.form.submitting : copy.form.submit}\n </button>\n )}\n </form>\n </>\n );\n}\n","// The join flow's payment step. Ports the reference card entry into the island:\n// dynamically loads js.stripe.com (NO @stripe/react-stripe-js dep), creates a\n// subscription server-side, and mounts a Stripe Payment Element. The refund-policy\n// checkbox is the gate — checking it starts the (money-creating) subscription\n// call. Client success is advisory only; the webhook is the\n// authoritative writer of paid state (see payments.ts).\nimport type { ComponentChildren } from \"preact\";\nimport { useRef, useState } from \"preact/hooks\";\nimport type { ChapterCopy } from \"../copy.js\";\nimport { useChapterCopy } from \"./copy-context.js\";\n\n// Minimal shapes for the dynamically-loaded js.stripe.com global.\ninterface StripeElementsApi {\n create(type: string): { mount(target: HTMLElement): void };\n}\ninterface StripeApi {\n elements(opts: {\n clientSecret: string;\n appearance?: Record<string, unknown>;\n fonts?: readonly Record<string, unknown>[];\n }): StripeElementsApi;\n confirmPayment(opts: {\n elements: StripeElementsApi;\n confirmParams?: { return_url?: string };\n redirect?: \"if_required\";\n }): Promise<{ error?: { message?: string }; paymentIntent?: { status?: string } }>;\n}\ntype StripeFactory = (publishableKey: string) => StripeApi;\n\nlet loader: Promise<StripeFactory> | null = null;\n\n/** Load js.stripe.com once and resolve the `Stripe` global (browser only). */\nfunction loadStripe(): Promise<StripeFactory> {\n const existing = (globalThis as { Stripe?: StripeFactory }).Stripe;\n if (existing) return Promise.resolve(existing);\n if (!loader) {\n loader = new Promise<StripeFactory>((resolve, reject) => {\n const s = document.createElement(\"script\");\n s.src = \"https://js.stripe.com/v3/\";\n s.onload = () => {\n const fn = (globalThis as { Stripe?: StripeFactory }).Stripe;\n if (fn) resolve(fn);\n else reject(new Error(\"stripe.js unavailable\"));\n };\n s.onerror = () => reject(new Error(\"stripe.js failed to load\"));\n document.head.appendChild(s);\n });\n }\n return loader;\n}\n\n/** Props for {@link PaymentStep}. */\nexport interface PaymentStepProps {\n /** The application to attach the subscription to (the capability). */\n applicationId: string;\n /** Refund-policy copy the applicant must acknowledge to start payment. */\n refundPolicyText: string;\n /** Called once the payment succeeds (or is processing) — advance to booking. */\n onPaid: () => void;\n /** Stripe Elements appearance passed through without Chapter reinterpretation. */\n appearance?: Record<string, unknown>;\n /** Stripe Elements custom-font descriptors. */\n fonts?: readonly Record<string, unknown>[];\n /** Resolved payment-step copy. */\n copy?: ChapterCopy[\"join\"][\"payment\"];\n /** Render the server-returned membership price breakdown. */\n renderPriceLines?: (lines: PaymentPriceLines) => ComponentChildren;\n /** Site-owned trust, security, or explanatory content. */\n children?: ComponentChildren;\n}\n\n/** Server-authoritative membership amounts supplied to the payment price slot. */\nexport interface PaymentPriceLines {\n standardCents: number;\n discountCents: number;\n dueTodayCents: number;\n}\n\ninterface SubResponse {\n clientSecret?: string;\n publishableKey?: string | null;\n lineItems?: PaymentPriceLines;\n}\n\n/** The card-entry step: acknowledge the refund policy → create the subscription\n * → mount Stripe Elements → confirm. */\nexport function PaymentStep(props: PaymentStepProps) {\n const inheritedCopy = useChapterCopy();\n const {\n applicationId,\n refundPolicyText,\n onPaid,\n appearance,\n fonts,\n renderPriceLines,\n children,\n } = props;\n const copy = props.copy ?? inheritedCopy.join.payment;\n const [status, setStatus] = useState<\"idle\" | \"loading\" | \"ready\" | \"confirming\">(\"idle\");\n const [error, setError] = useState<string | null>(null);\n const [lineItems, setLineItems] = useState<PaymentPriceLines | null>(null);\n const mountRef = useRef<HTMLDivElement | null>(null);\n const stripeRef = useRef<StripeApi | null>(null);\n const elementsRef = useRef<StripeElementsApi | null>(null);\n\n const begin = async () => {\n setStatus(\"loading\");\n setError(null);\n try {\n const res = await fetch(\"/api/payments/subscription\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ applicationId, refundPolicyAck: true }),\n });\n const data = (await res.json()) as SubResponse;\n if (!res.ok || !data.clientSecret || !data.publishableKey) throw new Error(copy.setupFailed);\n const stripe = (await loadStripe())(data.publishableKey);\n const elements = stripe.elements({\n clientSecret: data.clientSecret,\n ...(appearance ? { appearance } : {}),\n ...(fonts ? { fonts } : {}),\n });\n const element = elements.create(\"payment\");\n if (mountRef.current) element.mount(mountRef.current);\n stripeRef.current = stripe;\n elementsRef.current = elements;\n setLineItems(data.lineItems ?? null);\n setStatus(\"ready\");\n } catch (e) {\n setStatus(\"idle\");\n setError(e instanceof Error ? e.message : copy.setupFailed);\n }\n };\n\n const pay = async () => {\n const stripe = stripeRef.current;\n const elements = elementsRef.current;\n if (!stripe || !elements) return;\n setStatus(\"confirming\");\n setError(null);\n let returnUrl: string | undefined;\n if (typeof window !== \"undefined\") {\n const next = new URL(window.location.href);\n next.searchParams.set(\"application\", applicationId);\n next.searchParams.set(\"redirect_status\", \"succeeded\");\n returnUrl = next.toString();\n }\n const result = await stripe.confirmPayment({ elements, confirmParams: { return_url: returnUrl }, redirect: \"if_required\" });\n if (result.error) {\n setError(result.error.message ?? copy.incomplete);\n setStatus(\"ready\");\n return;\n }\n const paid = result.paymentIntent?.status;\n if (paid === \"succeeded\" || paid === \"processing\") onPaid();\n else {\n setError(copy.incomplete);\n setStatus(\"ready\");\n }\n };\n\n return (\n <div className=\"join-pay\">\n {/*\n The policy is a block of its own and the checkbox carries the\n affirmative sentence, so what the member agrees to is a statement of\n agreement rather than the policy text itself. A site that wants the\n checkbox labelled only by the policy sets `copy.join.payment.consent`\n to an empty string.\n */}\n <div className=\"compliance-box\">\n <p className=\"compliance-policy\">{refundPolicyText}</p>\n <label className=\"compliance-check\">\n <input\n type=\"checkbox\"\n disabled={status !== \"idle\"}\n onChange={(e) => {\n if (e.currentTarget.checked) void begin();\n }}\n />\n {copy.consent ? <span>{copy.consent}</span> : null}\n </label>\n </div>\n {status === \"loading\" ? <p className=\"pay-status\">{copy.preparing}</p> : null}\n {lineItems && renderPriceLines ? renderPriceLines(lineItems) : null}\n {children}\n <div ref={mountRef} hidden={status === \"idle\" || status === \"loading\"} />\n {status === \"ready\" || status === \"confirming\" ? (\n <button className=\"submit-btn\" disabled={status === \"confirming\"} onClick={() => void pay()}>\n {status === \"confirming\" ? copy.processing : copy.payAndContinue}\n </button>\n ) : null}\n {error ? <p className=\"pay-error\">{error}</p> : null}\n </div>\n );\n}\n","// Turning a submitted form into the JSON body /api/applications receives.\n// Split out of join.tsx so the multi-value rule is unit-testable without\n// mounting the island.\n\n/**\n * Collect a form's fields into a JSON-serializable object.\n *\n * A name that appears once yields its single value; a name that repeats yields\n * an array. Repetition is how a checkbox group (several inputs sharing one\n * `name`) expresses a multi-select, so folding it to a single value would\n * silently discard all but the last box the member ticked.\n *\n * `File` values are dropped: the application body is JSON, and a `File` would\n * serialize to `{}` and land in the row as an empty object.\n */\nexport function collectFormFields(form: FormData): Record<string, unknown> {\n const fields: Record<string, unknown> = {};\n for (const key of new Set(form.keys())) {\n const values = form.getAll(key).filter((v): v is string => typeof v === \"string\");\n if (values.length === 0) continue;\n fields[key] = values.length > 1 ? values : values[0];\n }\n return fields;\n}\n","import type { ComponentChildren } from \"preact\";\nimport { useEffect, useState } from \"preact/hooks\";\nimport type { ChapterCopy } from \"../copy.js\";\nimport type { Slot } from \"./datetime.js\";\nimport { SlotPicker } from \"./slot-picker.js\";\n\ninterface SlotsResponse {\n schedulingReady?: boolean;\n slots?: Slot[];\n timezone?: string;\n}\n\nexport function JoinBooking(props: {\n applicationId: string;\n copy: ChapterCopy[\"join\"][\"booking\"];\n onBooked: (b: { startAt: number; timezone: string }) => void;\n}): ComponentChildren {\n const { applicationId, copy, onBooked } = props;\n const [state, setState] = useState<{ slots: Slot[]; timezone: string } | null>(null);\n const [msg, setMsg] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n const [selected, setSelected] = useState<Slot | null>(null);\n\n const load = async () => {\n setMsg(null);\n try {\n const res = await fetch(\"/api/schedule/slots\");\n const data = (await res.json()) as SlotsResponse;\n if (data.schedulingReady === false || !data.slots?.length) {\n setState(null);\n setMsg(copy.unavailable);\n return;\n }\n setState({ slots: data.slots, timezone: data.timezone ?? \"UTC\" });\n } catch {\n setMsg(copy.loadFailed);\n }\n };\n useEffect(() => {\n void load();\n }, []);\n\n const book = async () => {\n if (!selected) return;\n setBusy(true);\n setMsg(null);\n try {\n const res = await fetch(\"/api/schedule/book\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ applicationId, startAt: selected.startAt }),\n });\n const data = (await res.json()) as { startAt?: number; error?: string; code?: string };\n if (res.status === 409 && data.code === \"calendar_slot_unavailable\") {\n setSelected(null);\n setMsg(copy.slotTaken);\n await load();\n return;\n }\n if (!res.ok) throw new Error(data.error ?? copy.failed);\n onBooked({ startAt: data.startAt ?? selected.startAt, timezone: state?.timezone ?? \"UTC\" });\n } catch (e) {\n setMsg(e instanceof Error ? e.message : copy.failed);\n } finally {\n setBusy(false);\n }\n };\n\n if (!state) return <p className=\"slots-status\">{msg ?? copy.loading}</p>;\n return (\n <div className=\"join-book\">\n <SlotPicker\n slots={state.slots}\n timezone={state.timezone}\n selectedStartAt={selected?.startAt}\n onPick={setSelected}\n onDayChange={() => setSelected(null)}\n />\n <div className=\"slot-confirm\" hidden={!selected}>\n <button className=\"submit-btn\" disabled={busy} onClick={() => void book()}>\n {busy ? copy.booking : copy.book}\n </button>\n {msg ? <p className=\"step2-note\">{msg}</p> : null}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState","jsx","jsxs","useState","Fragment","jsx","jsxs","useState","useEffect","useState","useState","jsx","jsxs","useState","useEffect","useState","jsx","jsxs","useState","useEffect","Fragment","jsx","jsxs","useState","useEffect"]}
|
|
@@ -210,7 +210,8 @@ var DEFAULT_JOIN_COPY = {
|
|
|
210
210
|
pending: "Confirming your payment\u2026",
|
|
211
211
|
processing: "Processing\u2026",
|
|
212
212
|
payAndContinue: "Pay and continue",
|
|
213
|
-
incomplete: "The payment did not complete. Please try again."
|
|
213
|
+
incomplete: "The payment did not complete. Please try again.",
|
|
214
|
+
consent: "I have read and agree to the pricing and refund policy above."
|
|
214
215
|
},
|
|
215
216
|
done: {
|
|
216
217
|
label: "You're booked",
|
|
@@ -408,4 +409,4 @@ export {
|
|
|
408
409
|
useChapterCopy,
|
|
409
410
|
BrandStyle
|
|
410
411
|
};
|
|
411
|
-
//# sourceMappingURL=chunk-
|
|
412
|
+
//# sourceMappingURL=chunk-WGQO4WDJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/copy-context.tsx","../src/copy-defaults-admin.ts","../src/copy-defaults.ts","../src/copy-resolve.ts","../src/brand.ts","../src/ui/brand-style.tsx"],"sourcesContent":["import { createContext, type ComponentChildren } from \"preact\";\nimport { useContext } from \"preact/hooks\";\nimport {\n DEFAULT_CHAPTER_COPY,\n resolveChapterCopy,\n type ChapterCopy,\n type ChapterCopyInput,\n} from \"../copy.js\";\n\nconst ChapterCopyContext = createContext<ChapterCopy>(DEFAULT_CHAPTER_COPY);\n\n/** Place a resolved or partial copy contract around any Chapter UI surface. */\nexport function ChapterCopyProvider(props: {\n copy?: ChapterCopy | ChapterCopyInput;\n children: ComponentChildren;\n}) {\n return (\n <ChapterCopyContext.Provider value={resolveChapterCopy(props.copy)}>\n {props.children}\n </ChapterCopyContext.Provider>\n );\n}\n\n/** Read the nearest complete Chapter copy contract. */\nexport function useChapterCopy(): ChapterCopy {\n return useContext(ChapterCopyContext);\n}\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_ADMIN_COPY: ChapterCopy[\"admin\"] = {\n auth: {\n checking: \"Checking access…\",\n notAuthorized: \"Not authorized\",\n accountNotAuthorized: \"{account} isn't on the admin list.\",\n thisAccount: \"This account\",\n signOut: \"Sign out\",\n loading: \"Loading…\",\n signInNotConfigured: \"Sign-in not configured\",\n missingPublishableKey: \"No Clerk publishable key is set for this environment yet.\",\n noWorkspaces: \"No admin workspaces are configured.\",\n signInTagline: \"Admin sign-in, invite only\",\n },\n shell: {\n adminRole: \"admin\",\n adminConsole: \"Admin Console\",\n adminName: \"Admin\",\n navigationLabel: \"Admin navigation\",\n },\n workspaces: {\n dashboard: \"Dashboard\",\n overview: \"Overview\",\n billing: \"Billing\",\n people: \"People\",\n settings: \"Settings\",\n calendar: \"Calendar\",\n email: \"Email\",\n dashboardViewsLabel: \"Dashboard views\",\n settingsViewsLabel: \"Settings views\",\n collectionsLabel: \"CRM collections\",\n },\n dashboard: {\n loading: \"Loading dashboard…\",\n loadFailed: \"Couldn't load the dashboard\",\n upcomingCalls: \"Upcoming calls\",\n callsNeedAttention: \"{count} need attention\",\n noUpcomingCalls: \"No upcoming calls.\",\n drift: \"drift\",\n open: \"Open\",\n meet: \"Meet\",\n applications: \"Applications\",\n newMembers: \"New members\",\n newMembersUnavailable: \"Appears once billing is connected.\",\n revenueAdded: \"Revenue · annual run rate added\",\n revenue: \"Revenue\",\n revenueUnavailable: \"Appears once billing is connected.\",\n pipeline: \"Pipeline\",\n pipelineLabel: \"Membership pipeline\",\n thisWeek: \"+{count} this week\",\n noChange: \"no change\",\n activeMemberships: \"Active memberships\",\n annualRunRate: \"Annual run rate\",\n testMode: \"test mode\",\n },\n billing: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load billing\",\n notConfigured: \"Billing isn't configured. No Stripe key is vaulted for this group.\",\n active: \"Active\",\n annualized: \"Annualized\",\n renewingSoon: \"Renewing within 60 days\",\n pastDue: \"Past due\",\n subscriptions: \"Subscriptions\",\n testMode: \"Stripe test mode\",\n truncated: \"Showing the first 100 subscriptions. The list is truncated.\",\n name: \"Name\",\n email: \"Email\",\n application: \"Application\",\n subscription: \"Subscription\",\n cancelling: \"cancelling\",\n amount: \"Amount\",\n renews: \"Renews\",\n },\n availability: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load availability\",\n saved: \"Saved.\",\n title: \"Booking availability\",\n days: \"Days\",\n startHour: \"Start hour (0–24)\",\n endHour: \"End hour (0–24)\",\n slotMinutes: \"Slot length (minutes)\",\n minNoticeHours: \"Minimum notice (hours)\",\n windowDays: \"Booking window (days)\",\n timezone: \"Timezone\",\n summaryTemplate: \"Event summary template\",\n save: \"Save availability\",\n dayLabels: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n },\n email: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load email config\",\n saved: \"Saved.\",\n sent: \"Sent {template} to {address}{redirected}.\",\n testFailed: \"Test send did not deliver.\",\n delivery: \"Delivery\",\n notificationAddress: \"Notification address\",\n replyTo: \"Reply-to\",\n debugInbox: \"Debug inbox\",\n debugInboxHint: \"Non-production sends redirect here.\",\n sendTest: \"Send test\",\n enabled: \"Enabled\",\n subject: \"Subject\",\n body: \"Body\",\n save: \"Save changes\",\n sendLog: \"Send log\",\n sentColumn: \"Sent\",\n templateColumn: \"Template\",\n statusColumn: \"Status\",\n toColumn: \"To\",\n failed: \"failed\",\n redirected: \"redirected\",\n delivered: \"delivered\",\n },\n meetings: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load meetings\",\n cancelConfirm: \"Cancel this call? The calendar provider notifies the attendee.\",\n newStartPrompt: \"New start time (for example, 2026-08-01 14:00):\",\n parseFailed: \"Couldn't parse that time.\",\n agenda: \"Agenda\",\n empty: \"No meetings.\",\n unknown: \"(unknown)\",\n drift: \"drift\",\n meet: \"Meet\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n network: {\n title: \"Share to a follower\",\n allowlistDescription: \"Sends the target's allowlisted fields. Its pipeline and account state remain local.\",\n sharing: \"Sharing…\",\n shareWith: \"Share with {target}\",\n shared: \"Shared with {target}.\",\n shareFailed: \"Could not share with {target}.\",\n deliveryFailed: \"Delivery failed.\",\n },\n records: {\n workflowMissing: \"This record is not linked to an application workflow.\",\n comms: \"Comms\",\n commsHistory: \"Comms history\",\n scheduling: \"Scheduling\",\n billing: \"Billing\",\n notes: \"Notes\",\n access: \"Access\",\n sharing: \"Sharing\",\n lifecycle: \"Access & lifecycle\",\n role: \"Role\",\n superAdminHint: \"Super-admin, managed in odla Studio.\",\n noAccount: \"No linked account yet. The role is set once they sign in.\",\n approve: \"Approve\",\n refund: \"Refund\",\n approved: \"Approved.\",\n refunded: \"Refund issued.\",\n roleSet: \"Role set to {role}.\",\n messageSent: \"Message sent.\",\n messageNotSent: \"Message was not sent.\",\n loadingTemplates: \"Loading templates…\",\n template: \"Template\",\n chooseTemplate: \"Choose a template…\",\n sending: \"Sending…\",\n send: \"Send\",\n noMessages: \"No messages recorded.\",\n message: \"Message\",\n newStartPrompt: \"New start time:\",\n parseFailed: \"Couldn't parse that time.\",\n cancelConfirm: \"Cancel this call?\",\n callCancelled: \"Call cancelled.\",\n callRescheduled: \"Call rescheduled.\",\n noApplication: \"No application is linked to this record.\",\n loadingMeetings: \"Loading meetings…\",\n noMeetings: \"No meetings recorded.\",\n joinMeeting: \"Join meeting\",\n openCalendar: \"Open calendar\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n};\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_COMMON_COPY: ChapterCopy[\"common\"] = {\n loading: \"Loading…\",\n saved: \"Saved.\",\n unknown: \"(unknown)\",\n};\n\nexport const DEFAULT_JOIN_COPY: ChapterCopy[\"join\"] = {\n form: {\n submit: \"Submit application\",\n submitting: \"Submitting…\",\n submitFailed: \"Your application could not be submitted.\",\n unexpectedFailure: \"Something went wrong. Please try again.\",\n },\n booking: {\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotTaken: \"That time was just taken. Here are the current openings.\",\n failed: \"The booking could not be completed.\",\n loading: \"Loading available times…\",\n book: \"Book this time\",\n booking: \"Booking…\",\n },\n payment: {\n setupFailed: \"Payment could not be set up. Please try again.\",\n preparing: \"Preparing secure payment…\",\n pending: \"Confirming your payment…\",\n processing: \"Processing…\",\n payAndContinue: \"Pay and continue\",\n incomplete: \"The payment did not complete. Please try again.\",\n consent: \"I have read and agree to the pricing and refund policy above.\",\n },\n done: {\n label: \"You're booked\",\n calendarInvite: \"A calendar invitation with the video call link is on its way to your email.\",\n memberArea: \"Go to your member area\",\n },\n};\n\nexport const DEFAULT_MEMBERS_COPY: ChapterCopy[\"members\"] = {\n loadFailed: \"Sign in is briefly unavailable. Please refresh.\",\n account: {\n signOut: \"Sign out\",\n adminConsole: \"Admin console\",\n },\n provisional: {\n cardLabel: \"Your Application\",\n applicationNeeded: \"One step remains\",\n applicationNeededBody: \"Your account is ready, and the application that completes it takes a few minutes.\",\n apply: \"Apply for membership\",\n refunded: \"Membership refunded\",\n refundedBody: \"Your fee has been refunded in full and your membership is canceled.\",\n active: \"Your membership is active.\",\n renews: \"Your membership is active and renews {date}.\",\n introductionCall: \"Your introduction call\",\n calendarInvite: \"A calendar invitation with the video call link is in your email.\",\n joinCall: \"Join the video call\",\n bookCall: \"Book your introduction call\",\n bookCallBody: \"Your application is in. Choose a time below, and a calendar invitation will reach your email.\",\n chooseTime: \"Choose a time\",\n },\n full: {\n welcome: \"Welcome back.\",\n },\n reschedule: {\n changeTime: \"Change your time\",\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotGone: \"That time is no longer available. Please pick another.\",\n loading: \"Loading available times…\",\n noTimes: \"No open times right now. Please check back soon.\",\n rescheduling: \"Rescheduling…\",\n keepTime: \"Keep my current time\",\n },\n};\n","import { DEFAULT_ADMIN_COPY } from \"./copy-defaults-admin.js\";\nimport {\n DEFAULT_COMMON_COPY,\n DEFAULT_JOIN_COPY,\n DEFAULT_MEMBERS_COPY,\n} from \"./copy-defaults.js\";\nimport type { ChapterCopy, ChapterCopyInput } from \"./copy.js\";\n\n/** Complete default language resolved beneath partial Chapter copy overrides. */\nexport const DEFAULT_CHAPTER_COPY: ChapterCopy = {\n common: DEFAULT_COMMON_COPY,\n join: DEFAULT_JOIN_COPY,\n members: DEFAULT_MEMBERS_COPY,\n admin: DEFAULT_ADMIN_COPY,\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n\nfunction mergeCopy<T>(base: T, input: unknown, path: string): T {\n if (typeof base === \"string\") {\n if (input === undefined) return base;\n if (typeof input !== \"string\") throw new Error(`${path}: must be a string`);\n return input as T;\n }\n if (Array.isArray(base)) {\n if (input === undefined) return [...base] as T;\n if (!Array.isArray(input) || !input.every((item) => typeof item === \"string\")) {\n throw new Error(`${path}: must be an array of strings`);\n }\n return [...input] as T;\n }\n if (!isObject(base)) return base;\n if (input !== undefined && !isObject(input)) throw new Error(`${path}: must be an object`);\n const overrides = input as Record<string, unknown> | undefined;\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(base)) {\n result[key] = mergeCopy(value, overrides?.[key], `${path}.${key}`);\n }\n return result as T;\n}\n\n/** Resolve a partial copy contract and validate every supplied leaf. */\nexport function resolveChapterCopy(input?: ChapterCopyInput): ChapterCopy {\n return mergeCopy(DEFAULT_CHAPTER_COPY, input, \"defineChapter.copy\");\n}\n\n/** Replace `{name}` placeholders without interpreting the resulting text as HTML. */\nexport function formatChapterCopy(\n template: string,\n values: Readonly<Record<string, string | number>>,\n): string {\n return template.replace(/\\{([a-zA-Z][a-zA-Z0-9]*)\\}/g, (match, key: string) =>\n Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match,\n );\n}\n","// Brand tokens (H4). defineChapter accepts a `brand` block; this turns it into a\n// `:root { --…: … }` CSS block, so a chapter re-skins the WHOLE UI — the\n// @odla-ai/ui components, the admin shell, and the member islands, which all read\n// --ui-* design tokens — from one config instead of hand-writing inline CSS.\n//\n// Pure string generation, so it is unit-testable and can be emitted at\n// build/SSR time into the page <head> (no flash of unstyled content), or via the\n// <BrandStyle> component from @odla-ai/chapter/ui.\nimport type { ChapterBrand, ChapterBrandTokens } from \"./types\";\n\n// A palette entry is either a direct custom property (already `--…`, e.g.\n// `--ui-accent` to retheme components) or a bare name we expose as `--<name>`\n// (e.g. `moss` → `--moss`, for a site to reference in its own CSS).\nfunction paletteVar(key: string): string {\n return key.startsWith(\"--\") ? key : `--${key}`;\n}\n\n// Strip characters that could break out of a `--var: value;` declaration or the\n// surrounding <style>. Brand config is trusted author input, so this is a\n// belt-and-suspenders guard, not a security boundary.\nfunction cleanValue(value: string): string {\n return value.replace(/[<>{};]/g, \"\").trim();\n}\n\nfunction paletteDecls(palette: Record<string, string> | undefined): string[] {\n const decls: string[] = [];\n for (const [key, value] of Object.entries(palette ?? {})) {\n if (typeof value === \"string\" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);\n }\n return decls;\n}\n\nconst TOKEN_VARS: Record<keyof ChapterBrandTokens, string> = {\n background: \"--ui-bg\",\n surface: \"--ui-surface\",\n surface2: \"--ui-surface-2\",\n text: \"--ui-text\",\n textMuted: \"--ui-text-muted\",\n textFaint: \"--ui-text-faint\",\n border: \"--ui-border\",\n borderStrong: \"--ui-border-strong\",\n accent: \"--ui-accent\",\n accentStrong: \"--ui-accent-strong\",\n accentSoft: \"--ui-accent-soft\",\n onAccent: \"--ui-on-accent\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart1: \"--ui-chart-1\",\n chart2: \"--ui-chart-2\",\n chart3: \"--ui-chart-3\",\n chart4: \"--ui-chart-4\",\n chart5: \"--ui-chart-5\",\n chart6: \"--ui-chart-6\",\n chartPositive: \"--ui-chart-pos\",\n chartNegative: \"--ui-chart-neg\",\n contentWidth: \"--ui-content-width\",\n panelRadius: \"--ui-radius-lg\",\n panelShadow: \"--ui-shadow\",\n masterDetailColumns: \"--ui-master-detail-columns\",\n masterDetailMinHeight: \"--ui-master-detail-min-height\",\n pagePadding: \"--chapter-admin-page-padding\",\n workspacePadding: \"--chapter-admin-workspace-padding\",\n};\n\nfunction semanticDecls(tokens: ChapterBrandTokens | undefined): string[] {\n return Object.entries(tokens ?? {}).flatMap(([key, value]) =>\n typeof value === \"string\" && value.trim()\n ? [`${TOKEN_VARS[key as keyof ChapterBrandTokens]}: ${cleanValue(value)};`]\n : [],\n );\n}\n\n/**\n * Build the CSS that maps a chapter's brand onto the design tokens the UI reads:\n * each `palette` entry becomes a custom property (light, and dark unless\n * `paletteDark` overrides), and `fonts` (display/body/numeral) map to\n * `--ui-font-display` / `--ui-font-sans` / `--ui-font-numeral`. The dark block is\n * emitted under both `:root[data-theme=\"dark\"]` (the odla-ui theme toggle) and\n * `@media (prefers-color-scheme: dark)`. Returns \"\" when there is nothing to\n * theme. These are brand OVERRIDES on top of a base theme — they do not replace\n * the theme layer the components need (see {@link brandTokens} usage in the docs).\n */\nexport function brandTokens(\n brand: ChapterBrand | undefined,\n options: { selector?: string } = {},\n): string {\n if (!brand) return \"\";\n const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];\n const fonts = brand.fonts;\n if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);\n if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);\n if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);\n const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];\n const invert = paletteDecls(brand.paletteInvert);\n const selector = options.selector ?? \":root\";\n const darkSelector = selector === \":root\" ? ':root[data-theme=\"dark\"]' : `${selector}[data-theme=\"dark\"]`;\n const systemSelector = selector === \":root\"\n ? ':root:not([data-theme=\"light\"])'\n : `${selector}:not([data-theme=\"light\"])`;\n\n let css = light.length ? `${selector} {\\n ${light.join(\"\\n \")}\\n}\\n` : \"\";\n if (dark.length) {\n const block = `{\\n ${dark.join(\"\\n \")}\\n}`;\n css += `${darkSelector} ${block}\\n@media (prefers-color-scheme: dark) {\\n ${systemSelector} ${block}\\n}\\n`;\n }\n if (invert.length) {\n const invertSelector = selector === \":root\"\n ? \".ui-invert\"\n : `${selector}.ui-invert, ${selector} .ui-invert`;\n css += `${invertSelector} {\\n color-scheme: dark;\\n ${invert.join(\"\\n \")}\\n}\\n`;\n }\n return css;\n}\n\n/** Structural output accepted from `@odla-ai/brand`'s pure token compiler. */\nexport interface CompiledChapterBrandTokens {\n light: Record<string, string>;\n dark: Record<string, string>;\n}\n\n/**\n * Turn a complete compiled token pair into Chapter's brand contract without\n * coupling Chapter's runtime to the brand-book/agent package. Explicit values in\n * `base` win, so a host can make final per-site adjustments after compilation.\n */\nexport function chapterBrandFromTokens(\n compiled: CompiledChapterBrandTokens,\n base: ChapterBrand = {},\n): ChapterBrand {\n return {\n ...base,\n palette: { ...compiled.light, ...base.palette },\n paletteDark: { ...compiled.dark, ...base.paletteDark },\n paletteInvert: { ...compiled.dark, ...base.paletteInvert },\n };\n}\n","// Client-side convenience for brand tokens: render the chapter's brand as a\n// <style> tag. Prefer emitting brandTokens() into the page <head> at build/SSR\n// time (no flash); use this when that isn't available (e.g. a pure SPA mount).\nimport { brandTokens } from \"../brand.js\";\nimport type { ChapterBrand } from \"../types\";\n\n/** Props for {@link BrandStyle}. */\nexport interface BrandStyleProps {\n brand: ChapterBrand | undefined;\n /** CSS selector receiving the variables. Default `:root`. */\n selector?: string;\n}\n\n/** Render a chapter's brand tokens as an inline <style> block (or nothing when\n * there is no brand to theme). */\nexport function BrandStyle(props: BrandStyleProps) {\n const css = brandTokens(props.brand, { selector: props.selector });\n if (!css) return null;\n return <style>{css}</style>;\n}\n"],"mappings":"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","names":["jsx"]}
|
|
@@ -4,18 +4,11 @@ import {
|
|
|
4
4
|
DEFAULT_CHAPTER_COPY,
|
|
5
5
|
formatChapterCopy,
|
|
6
6
|
useChapterCopy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WGQO4WDJ.js";
|
|
8
8
|
|
|
9
9
|
// src/ui/admin.tsx
|
|
10
|
-
import { useEffect as
|
|
11
|
-
import {
|
|
12
|
-
ClerkGate,
|
|
13
|
-
SignedIn,
|
|
14
|
-
SignedOut,
|
|
15
|
-
SignIn,
|
|
16
|
-
useClerkAuth,
|
|
17
|
-
clerkAppearanceFromTokens
|
|
18
|
-
} from "@odla-ai/auth-clerk";
|
|
10
|
+
import { useEffect as useEffect8, useMemo, useRef, useState as useState12 } from "preact/hooks";
|
|
11
|
+
import { ClerkGate, SignedIn, SignedOut, SignIn, useClerkAuth } from "@odla-ai/auth-clerk";
|
|
19
12
|
import { CrmClient } from "@odla-ai/crm";
|
|
20
13
|
|
|
21
14
|
// src/ui/chrome.tsx
|
|
@@ -1385,6 +1378,7 @@ function AdminTheme(props) {
|
|
|
1385
1378
|
return /* @__PURE__ */ jsx16(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs12(
|
|
1386
1379
|
ThemeScope2,
|
|
1387
1380
|
{
|
|
1381
|
+
ref: props.scopeRef,
|
|
1388
1382
|
"data-chapter-admin": "",
|
|
1389
1383
|
theme: brand?.theme,
|
|
1390
1384
|
accent: brand?.accent,
|
|
@@ -1409,6 +1403,17 @@ function signInRedirect(basePath, workspaces) {
|
|
|
1409
1403
|
return adminRouteHref(current, basePath, route, "query");
|
|
1410
1404
|
}
|
|
1411
1405
|
|
|
1406
|
+
// src/ui/admin-appearance.ts
|
|
1407
|
+
import { useEffect as useEffect7, useState as useState11 } from "preact/hooks";
|
|
1408
|
+
import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
|
|
1409
|
+
function useScopedClerkAppearance(scope) {
|
|
1410
|
+
const [appearance, setAppearance] = useState11();
|
|
1411
|
+
useEffect7(() => {
|
|
1412
|
+
setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
|
|
1413
|
+
}, [scope]);
|
|
1414
|
+
return appearance;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1412
1417
|
// src/ui/admin-auth.ts
|
|
1413
1418
|
var apiUrl = (apiBase, path) => `${apiBase.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
1414
1419
|
var DEFAULT_ADMIN_AUTH = {};
|
|
@@ -1461,8 +1466,8 @@ function Authed(props) {
|
|
|
1461
1466
|
}),
|
|
1462
1467
|
[getToken, crmBasePath]
|
|
1463
1468
|
);
|
|
1464
|
-
const [state, setState] =
|
|
1465
|
-
|
|
1469
|
+
const [state, setState] = useState12({ status: "checking" });
|
|
1470
|
+
useEffect8(() => {
|
|
1466
1471
|
let live = true;
|
|
1467
1472
|
void (async () => {
|
|
1468
1473
|
try {
|
|
@@ -1532,8 +1537,10 @@ function ChapterAdmin(props) {
|
|
|
1532
1537
|
const chrome = props.chrome ?? "embedded";
|
|
1533
1538
|
const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
|
|
1534
1539
|
const redirect = signInRedirect(basePath, workspaces);
|
|
1535
|
-
const [publishableKey, setPublishableKey] =
|
|
1536
|
-
|
|
1540
|
+
const [publishableKey, setPublishableKey] = useState12(void 0);
|
|
1541
|
+
const scopeRef = useRef(null);
|
|
1542
|
+
const appearance = useScopedClerkAppearance(scopeRef);
|
|
1543
|
+
useEffect8(() => {
|
|
1537
1544
|
let live = true;
|
|
1538
1545
|
void loadChapterAdminConfig(auth, apiBase).then((config) => live && setPublishableKey(config.publishableKey ?? null)).catch(() => live && setPublishableKey(null));
|
|
1539
1546
|
return () => {
|
|
@@ -1541,7 +1548,7 @@ function ChapterAdmin(props) {
|
|
|
1541
1548
|
};
|
|
1542
1549
|
}, [apiBase, auth]);
|
|
1543
1550
|
let content;
|
|
1544
|
-
if (publishableKey === void 0) {
|
|
1551
|
+
if (publishableKey === void 0 || publishableKey && !appearance) {
|
|
1545
1552
|
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
1546
1553
|
} else if (!publishableKey) {
|
|
1547
1554
|
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsxs13("div", { className: "card", style: S.card, children: [
|
|
@@ -1555,7 +1562,7 @@ function ChapterAdmin(props) {
|
|
|
1555
1562
|
ClerkGate,
|
|
1556
1563
|
{
|
|
1557
1564
|
publishableKey,
|
|
1558
|
-
appearance
|
|
1565
|
+
appearance,
|
|
1559
1566
|
afterSignOutUrl: "/",
|
|
1560
1567
|
children: [
|
|
1561
1568
|
/* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx17(
|
|
@@ -1586,7 +1593,7 @@ function ChapterAdmin(props) {
|
|
|
1586
1593
|
}
|
|
1587
1594
|
);
|
|
1588
1595
|
}
|
|
1589
|
-
return /* @__PURE__ */ jsx17(AdminTheme, { chapter: props.chapter, children: content });
|
|
1596
|
+
return /* @__PURE__ */ jsx17(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
|
|
1590
1597
|
}
|
|
1591
1598
|
|
|
1592
1599
|
// src/ui/admin-defaults.ts
|
|
@@ -1595,15 +1602,15 @@ function defaultAdminSections(chapter) {
|
|
|
1595
1602
|
}
|
|
1596
1603
|
|
|
1597
1604
|
// src/ui/admin-meetings.tsx
|
|
1598
|
-
import { useState as
|
|
1605
|
+
import { useState as useState13 } from "preact/hooks";
|
|
1599
1606
|
import { Button as Button6 } from "@odla-ai/ui/components";
|
|
1600
1607
|
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
1601
1608
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
1602
1609
|
function MeetingsBody({ ctx }) {
|
|
1603
1610
|
const copy = useChapterCopy().admin.meetings;
|
|
1604
1611
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
|
|
1605
|
-
const [busy, setBusy] =
|
|
1606
|
-
const [note, setNote] =
|
|
1612
|
+
const [busy, setBusy] = useState13(null);
|
|
1613
|
+
const [note, setNote] = useState13(null);
|
|
1607
1614
|
if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children: copy.loading });
|
|
1608
1615
|
if (error || !data) return /* @__PURE__ */ jsxs14(AdminNote, { children: [
|
|
1609
1616
|
copy.loadFailed,
|
|
@@ -1691,4 +1698,4 @@ export {
|
|
|
1691
1698
|
defaultAdminSections,
|
|
1692
1699
|
meetingsSection
|
|
1693
1700
|
};
|
|
1694
|
-
//# sourceMappingURL=chunk-
|
|
1701
|
+
//# sourceMappingURL=chunk-ZPAJL7KG.js.map
|