@odla-ai/chapter 0.22.0 → 0.22.1

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.
@@ -1,5 +1,5 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
1
+ import * as preact from 'preact';
2
+ import { ComponentChildren, JSX } from 'preact';
3
3
  import { a as ChapterBrand } from '../../types-CgWUQ2MH.js';
4
4
  import '@odla-ai/crm';
5
5
 
@@ -54,7 +54,7 @@ interface SlotPickerProps<T extends Slot = Slot> {
54
54
  /** A day-chips + time-grid slot picker. Presentational and `onPick`-driven: it
55
55
  * owns only the active-day selection; the caller owns slot data, the chosen
56
56
  * slot, and booking. Shared by the join booking step and the member reschedule. */
57
- declare function SlotPicker<T extends Slot = Slot>(props: SlotPickerProps<T>): react.JSX.Element;
57
+ declare function SlotPicker<T extends Slot = Slot>(props: SlotPickerProps<T>): preact.JSX.Element;
58
58
 
59
59
  /** Issue an authenticated request and parse the JSON response. Throws on a
60
60
  * non-2xx status (the islands catch and surface a friendly message). */
@@ -70,11 +70,11 @@ interface MembersAreaProps {
70
70
  /** Where an accountless member goes to apply. Default "/join.html". */
71
71
  applyHref?: string;
72
72
  /** What a full (non-provisional) member sees below the account header. */
73
- memberContent?: ReactNode;
73
+ memberContent?: ComponentChildren;
74
74
  }
75
75
  /** The signed-in member area. Loads /api/me on mount and renders the account
76
76
  * header plus the provisional application card or the full-member content. */
77
- declare function MembersArea(props: MembersAreaProps): react.JSX.Element;
77
+ declare function MembersArea(props: MembersAreaProps): preact.JSX.Element;
78
78
 
79
79
  /** Props for {@link Rescheduler}. */
80
80
  interface RescheduleProps {
@@ -88,7 +88,7 @@ interface RescheduleProps {
88
88
  }
89
89
  /** A collapsed "change your time" link that expands into a {@link SlotPicker} and
90
90
  * rebooks on pick. Degrades to a message when scheduling is unavailable. */
91
- declare function Rescheduler(props: RescheduleProps): react.JSX.Element;
91
+ declare function Rescheduler(props: RescheduleProps): preact.JSX.Element;
92
92
 
93
93
  /** The public join config (the shape `GET /api/join-config` returns). */
94
94
  interface JoinConfig {
@@ -102,13 +102,13 @@ interface JoinIslandProps {
102
102
  config: JoinConfig;
103
103
  /** The site's application form fields — inputs with `name` attributes; their
104
104
  * values are collected via FormData and posted to /api/applications. */
105
- children: ReactNode;
105
+ children: ComponentChildren;
106
106
  /** Where the confirmation links after booking. Default "/members/". */
107
107
  membersHref?: string;
108
108
  }
109
109
  /** The signup island. Renders the site's form, then drives payment (when the
110
110
  * chapter charges) and booking (when a calendar is connected) to confirmation. */
111
- declare function JoinIsland(props: JoinIslandProps): react.JSX.Element;
111
+ declare function JoinIsland(props: JoinIslandProps): JSX.Element;
112
112
 
113
113
  /** Props for {@link PaymentStep}. */
114
114
  interface PaymentStepProps {
@@ -121,7 +121,7 @@ interface PaymentStepProps {
121
121
  }
122
122
  /** The card-entry step: acknowledge the refund policy → create the subscription
123
123
  * → mount Stripe Elements → confirm. */
124
- declare function PaymentStep(props: PaymentStepProps): react.JSX.Element;
124
+ declare function PaymentStep(props: PaymentStepProps): preact.JSX.Element;
125
125
 
126
126
  /** Props for {@link BrandStyle}. */
127
127
  interface BrandStyleProps {
@@ -131,6 +131,6 @@ interface BrandStyleProps {
131
131
  }
132
132
  /** Render a chapter's brand tokens as an inline <style> block (or nothing when
133
133
  * there is no brand to theme). */
134
- declare function BrandStyle(props: BrandStyleProps): react.JSX.Element | null;
134
+ declare function BrandStyle(props: BrandStyleProps): preact.JSX.Element | null;
135
135
 
136
136
  export { type ApiFn, BrandStyle, type BrandStyleProps, type JoinConfig, JoinIsland, type JoinIslandProps, MembersArea, type MembersAreaProps, PaymentStep, type PaymentStepProps, type RescheduleProps, Rescheduler, type Slot, SlotPicker, type SlotPickerClasses, type SlotPickerProps, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, timeLabel, tzShort };
@@ -12,10 +12,10 @@ import {
12
12
  groupSlotsByDay,
13
13
  timeLabel,
14
14
  tzShort
15
- } from "../../chunk-ILGUJXSB.js";
15
+ } from "../../chunk-4COPR4SY.js";
16
16
  import {
17
17
  BrandStyle
18
- } from "../../chunk-VZPBTTO3.js";
18
+ } from "../../chunk-3QKGKUTX.js";
19
19
  export {
20
20
  BrandStyle,
21
21
  JoinIsland,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/chapter",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "A leader/follower foundation for branded membership sites: shared CRM, admin, auth, payments, booking, and explicit record delivery from one defineChapter config.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://odla.ai/docs/packages/chapter",
@@ -70,16 +70,20 @@
70
70
  "prepublishOnly": "npm run build && npm test"
71
71
  },
72
72
  "dependencies": {
73
- "@odla-ai/crm": "^0.3.0",
73
+ "@odla-ai/crm": "^0.3.1",
74
74
  "jose": "^6.2.3"
75
75
  },
76
76
  "peerDependencies": {
77
+ "@odla-ai/auth-clerk": ">=0.4.1 <1.0.0",
77
78
  "@odla-ai/calendar": ">=0.2.0 <1.0.0",
78
- "@odla-ai/db": ">=0.6.0 <1.0.0",
79
- "@odla-ai/ui": ">=0.12.0 <1.0.0",
80
- "react": ">=18"
79
+ "@odla-ai/db": ">=0.6.7 <1.0.0",
80
+ "@odla-ai/ui": ">=0.12.1 <1.0.0",
81
+ "preact": ">=10.29.7 <11"
81
82
  },
82
83
  "peerDependenciesMeta": {
84
+ "@odla-ai/auth-clerk": {
85
+ "optional": true
86
+ },
83
87
  "@odla-ai/calendar": {
84
88
  "optional": true
85
89
  },
@@ -88,9 +92,6 @@
88
92
  },
89
93
  "@odla-ai/ui": {
90
94
  "optional": true
91
- },
92
- "react": {
93
- "optional": true
94
95
  }
95
96
  },
96
97
  "devDependencies": {
@@ -99,10 +100,9 @@
99
100
  "@odla-ai/db": "*",
100
101
  "@odla-ai/ui": "*",
101
102
  "@types/node": "^26.1.0",
102
- "@types/react": "^19.2.7",
103
103
  "@vitest/coverage-v8": "^4.1.9",
104
- "react": "^19.2.7",
105
- "react-dom": "^19.2.7",
104
+ "preact": "10.29.7",
105
+ "preact-render-to-string": "6.7.0",
106
106
  "tsup": "^8.3.0",
107
107
  "typescript": "^6.0.3",
108
108
  "vitest": "^4.1.9"
@@ -18,12 +18,12 @@ Chapter supplies:
18
18
  - the validated `defineChapter()` engine and generated schema/rules/seed;
19
19
  - the Cloudflare Worker API through `chapterWorker()`;
20
20
  - the join, payment, booking, member, admin, CRM, and network behavior;
21
- - member-facing React components and the complete Clerk-gated admin console;
21
+ - member-facing Preact components and the complete Clerk-gated admin console;
22
22
  - the provisioning descriptor consumed by `@odla-ai/cli`.
23
23
 
24
24
  Chapter does **not** generate a website. The host still owns:
25
25
 
26
- - `package.json`, Vite/React or Vite/Preact, HTML entries, routing, and CSS;
26
+ - `package.json`, Vite/Preact, HTML entries, routing, and CSS;
27
27
  - public pages, navigation, copy, imagery, SEO, legal text, and the join form;
28
28
  - the member auth wrapper that gives `MembersArea` an authenticated `api`;
29
29
  - Wrangler configuration, observability wrapping, provider setup, and tests.
@@ -115,31 +115,30 @@ npm init -y
115
115
  npx @odla-ai/cli@0.17.1 setup
116
116
  ```
117
117
 
118
- Verify every release target exists, then install the exact known-good React
118
+ Verify every release target exists, then install the exact known-good Preact
119
119
  host matrix. Do not copy these commands into a pre-release branch where
120
- `@odla-ai/chapter@0.22.0` is not yet on npm.
120
+ `@odla-ai/chapter@0.22.1` is not yet on npm.
121
121
 
122
122
  ```sh
123
- npm view @odla-ai/chapter@0.22.0 version
123
+ npm view @odla-ai/chapter@0.22.1 version
124
124
  npm install --save-exact \
125
- @odla-ai/chapter@0.22.0 @odla-ai/ui@0.12.0 \
126
- @odla-ai/crm@0.3.0 @odla-ai/db@0.6.6 \
125
+ @odla-ai/chapter@0.22.1 @odla-ai/ui@0.12.1 \
126
+ @odla-ai/crm@0.3.1 @odla-ai/db@0.6.7 \
127
127
  @odla-ai/calendar@0.2.0 @odla-ai/email@0.3.1 \
128
- @odla-ai/auth-clerk@0.4.0 @odla-ai/o11y@2.2.2 \
129
- jose@6.2.3 react@19.2.7 react-dom@19.2.7
128
+ @odla-ai/auth-clerk@0.4.1 @odla-ai/o11y@2.2.2 \
129
+ jose@6.2.3 preact@10.29.7
130
130
  npm install --save-dev --save-exact \
131
131
  @odla-ai/cli@0.17.1 @odla-ai/security@0.3.1 \
132
132
  @cloudflare/workers-types@4.20260702.1 \
133
- @types/react@19.2.17 @types/react-dom@19.2.3 \
134
- @vitejs/plugin-react@6.0.3 typescript@6.0.3 \
133
+ typescript@6.0.3 \
135
134
  vite@8.1.4 vitest@4.1.10 wrangler@4.107.0
136
135
  npx odla-ai capabilities --json
137
136
  ```
138
137
 
139
138
  The parent `README.md` repeats this version matrix. This runbook standardizes on
140
- React; a Preact host needs its own exact, tested compatibility matrix. Install
141
- auth-clerk explicitly: it is deliberately not a Chapter peer because
142
- worker-only and member-only consumers do not need it.
139
+ Preact. Install auth-clerk explicitly when adopting the admin entry; it is an
140
+ optional Chapter peer because worker-only and member-only consumers do not need
141
+ it.
143
142
 
144
143
  Required host scripts:
145
144
 
@@ -1 +0,0 @@
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"],"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 to React; 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 \"react\";\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// React, driven by the injected `api` (no Clerk SDK dependency).\nimport { useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Rescheduler } from \"./reschedule.js\";\nimport { fmtDate, fullLabel } from \"./datetime.js\";\nimport type { ApiFn } from \"./api.js\";\nimport type { MemberApplication } from \"../session.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?: ReactNode;\n}\n\nfunction card(kicker: string, body: ReactNode): ReactNode {\n return (\n <div className=\"card\">\n <div className=\"card-label\">Your Application</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 onReschedule: () => void | Promise<void>;\n}): ReactNode {\n const { api, application, applyHref, onReschedule } = props;\n if (!application) {\n return card(\n \"One step remains\",\n <>\n <div className=\"meeting-note\">Your account is ready, and the application that completes it takes a few minutes.</div>\n <a className=\"apply-link\" href={applyHref}>\n Apply for membership\n </a>\n </>,\n );\n }\n if (application.status === \"refunded\") {\n return card(\"Membership refunded\", <div className=\"meeting-note\">Your fee has been refunded in full and your membership is canceled.</div>);\n }\n const membership = application.paid ? (\n <div className=\"meeting-note\">Your membership is active{application.renewalAt ? ` and renews ${fmtDate(application.renewalAt)}` : \"\"}.</div>\n ) : null;\n if (application.meetingAt) {\n return card(\n \"Your introduction call\",\n <>\n <div className=\"meeting-date\">{fullLabel(application.meetingAt, application.timezone)}</div>\n <div className=\"meeting-note\">A calendar invitation with the video call link is in your email.</div>\n {application.meetUrl ? (\n <div className=\"meeting-note\">\n <a href={application.meetUrl} target=\"_blank\" rel=\"noopener\">\n Join the video call\n </a>\n </div>\n ) : null}\n <Rescheduler api={api} applicationId={application.id} timezone={application.timezone} onRescheduled={onReschedule} />\n {membership}\n </>,\n );\n }\n return card(\n \"Book your introduction call\",\n <>\n <div className=\"meeting-note\">Your application is in. Choose a time below, and a calendar invitation will reach your email.</div>\n <Rescheduler api={api} applicationId={application.id} timezone={application.timezone} onRescheduled={onReschedule} label=\"Choose a time\" />\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 { api, signOut, adminHref = \"/admin/\", applyHref = \"/join.html\", memberContent } = props;\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\">Sign in is briefly unavailable. Please refresh.</p>;\n if (!me) return <p className=\"meeting-note\">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 Sign out\n </button>\n {role === \"admin\" ? (\n <a className=\"admin-console-link\" href={adminHref}>\n Admin console\n </a>\n ) : null}\n </div>\n </div>\n {role === \"provisional\" ? (\n <ProvisionalCard api={api} application={me.application} applyHref={applyHref} onReschedule={reload} />\n ) : (\n (memberContent ?? (\n <div className=\"card\">\n <div className=\"card-label\">Welcome back.</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 to React, driven by the injected `api`.\nimport { useState } from \"react\";\nimport { SlotPicker } from \"./slot-picker.js\";\nimport type { Slot } from \"./datetime.js\";\nimport type { ApiFn } from \"./api.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}\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 { api, applicationId, timezone, onRescheduled, label = \"Change your time\" } = 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(\"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\");\n return;\n }\n setSlots(r.slots ?? []);\n if (r.timezone) setTz(r.timezone);\n } catch {\n setSlots([]);\n setMsg(\"Times are briefly unavailable. Please try again.\");\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 : \"That time is no longer available. Please pick another.\");\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\">Loading available times…</p>\n ) : slots.length === 0 ? (\n <p className=\"meeting-note\">{msg ?? \"No open times right now. Please check back soon.\"}</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\">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 Keep my current time\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 { useEffect, useState } from \"react\";\nimport type { FormEvent, ReactNode } from \"react\";\nimport { SlotPicker } from \"./slot-picker.js\";\nimport { PaymentStep } from \"./payment-step.js\";\nimport { fullLabel } from \"./datetime.js\";\nimport type { Slot } from \"./datetime.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}\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: ReactNode;\n /** Where the confirmation links after booking. Default \"/members/\". */\n membersHref?: string;\n}\n\ntype Step = \"form\" | \"pay\" | \"book\" | \"done\";\n\ninterface SlotsResponse {\n schedulingReady?: boolean;\n slots?: Slot[];\n timezone?: string;\n}\n\nfunction JoinBooking(props: { applicationId: string; onBooked: (b: { startAt: number; timezone: string }) => void }): ReactNode {\n const { applicationId, 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(\"Scheduling is briefly unavailable — we'll reach out by email to arrange your call.\");\n return;\n }\n setState({ slots: data.slots, timezone: data.timezone ?? \"UTC\" });\n } catch {\n setMsg(\"Times are briefly unavailable. Please try again.\");\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(\"That time was just taken. Here are the current openings.\");\n await load();\n return;\n }\n if (!res.ok) throw new Error(data.error ?? \"The booking could not be completed.\");\n onBooked({ startAt: data.startAt ?? selected.startAt, timezone: state?.timezone ?? \"UTC\" });\n } catch (e) {\n setMsg(e instanceof Error ? e.message : \"The booking could not be completed.\");\n } finally {\n setBusy(false);\n }\n };\n\n if (!state) return <p className=\"slots-status\">{msg ?? \"Loading available times…\"}</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 ? \"Booking…\" : \"Book this time\"}\n </button>\n {msg ? <p className=\"step2-note\">{msg}</p> : null}\n </div>\n </div>\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 { config, children, membersHref = \"/members/\" } = props;\n const [step, setStep] = useState<Step>(\"form\");\n const [applicationId, setApplicationId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const [booked, setBooked] = useState<{ startAt: number; timezone: string } | null>(null);\n\n const submit = async (e: FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n setSubmitting(true);\n setError(null);\n try {\n const fields: Record<string, unknown> = {};\n for (const [k, v] of new FormData(e.currentTarget).entries()) fields[k] = v;\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 ?? \"Your application could not be submitted.\");\n setApplicationId(data.id);\n setStep(config.paymentsReady ? \"pay\" : \"book\");\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Something went wrong. Please try again.\");\n } finally {\n setSubmitting(false);\n }\n };\n\n if (step === \"done\" && booked) {\n return (\n <div className=\"join-done card\">\n <div className=\"card-label\">You're booked</div>\n <p className=\"meeting-date\">{fullLabel(booked.startAt, booked.timezone)}</p>\n <p className=\"meeting-note\">A calendar invitation with the video call link is on its way to your email.</p>\n <a className=\"apply-link\" href={membersHref}>\n Go to your member area\n </a>\n </div>\n );\n }\n if (step === \"book\" && applicationId) {\n return (\n <JoinBooking\n applicationId={applicationId}\n onBooked={(b) => {\n setBooked(b);\n setStep(\"done\");\n }}\n />\n );\n }\n if (step === \"pay\" && applicationId) {\n return <PaymentStep applicationId={applicationId} refundPolicyText={config.refundPolicyText ?? \"\"} onPaid={() => setStep(\"book\")} />;\n }\n return (\n <form className=\"join-form\" onSubmit={(e) => void submit(e)}>\n {children}\n {error ? <p className=\"join-error\">{error}</p> : null}\n <button className=\"submit-btn\" type=\"submit\" disabled={submitting}>\n {submitting ? \"Submitting…\" : \"Submit application\"}\n </button>\n </form>\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 { useRef, useState } from \"react\";\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: { clientSecret: string }): 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}\n\ninterface SubResponse {\n clientSecret?: string;\n publishableKey?: string | null;\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 { applicationId, refundPolicyText, onPaid } = props;\n const [status, setStatus] = useState<\"idle\" | \"loading\" | \"ready\" | \"confirming\">(\"idle\");\n const [error, setError] = useState<string | 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(\"Payment could not be set up. Please try again.\");\n const stripe = (await loadStripe())(data.publishableKey);\n const elements = stripe.elements({ clientSecret: data.clientSecret });\n const element = elements.create(\"payment\");\n if (mountRef.current) element.mount(mountRef.current);\n stripeRef.current = stripe;\n elementsRef.current = elements;\n setStatus(\"ready\");\n } catch (e) {\n setStatus(\"idle\");\n setError(e instanceof Error ? e.message : \"Payment could not be set up. Please try again.\");\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 const returnUrl =\n typeof window !== \"undefined\" ? `${window.location.origin}${window.location.pathname}?redirect_status=succeeded` : undefined;\n const result = await stripe.confirmPayment({ elements, confirmParams: { return_url: returnUrl }, redirect: \"if_required\" });\n if (result.error) {\n setError(result.error.message ?? \"The payment could not be completed.\");\n setStatus(\"ready\");\n return;\n }\n const paid = result.paymentIntent?.status;\n if (paid === \"succeeded\" || paid === \"processing\") onPaid();\n else {\n setError(\"The payment did not complete. Please try again.\");\n setStatus(\"ready\");\n }\n };\n\n return (\n <div className=\"join-pay\">\n <label className=\"compliance-box\">\n <input\n type=\"checkbox\"\n disabled={status !== \"idle\"}\n onChange={(e) => {\n if (e.currentTarget.checked) void begin();\n }}\n />\n <span>{refundPolicyText}</span>\n </label>\n {status === \"loading\" ? <p className=\"pay-status\">Preparing secure payment…</p> : null}\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\" ? \"Processing…\" : \"Pay and continue\"}\n </button>\n ) : null}\n {error ? <p className=\"pay-error\">{error}</p> : null}\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","jsx","jsxs","useState","useEffect"]}