@odla-ai/chapter 0.22.1 → 0.23.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 +99 -26
- package/dist/chunk-NTZSXAFT.js +411 -0
- package/dist/chunk-NTZSXAFT.js.map +1 -0
- package/dist/{chunk-4COPR4SY.js → chunk-OYXNI6VR.js} +266 -84
- package/dist/chunk-OYXNI6VR.js.map +1 -0
- package/dist/{chunk-C4XQSRW7.js → chunk-VCC4S6JU.js} +199 -171
- package/dist/chunk-VCC4S6JU.js.map +1 -0
- package/dist/{types-CgWUQ2MH.d.ts → copy-context-pDogTR4x.d.ts} +78 -20
- package/dist/index.cjs +322 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -24
- package/dist/index.d.ts +90 -24
- package/dist/index.js +322 -3
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +5 -2
- package/dist/ui/admin/index.js +8 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +11 -5
- package/dist/ui/member/index.d.ts +118 -2
- package/dist/ui/member/index.js +10 -4
- package/dist/worker/index.cjs +180 -130
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +67 -19
- package/dist/worker/index.d.ts +67 -19
- package/dist/worker/index.js +180 -130
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/adopt-existing.md +23 -5
- package/runbooks/greenfield.md +85 -4
- package/dist/chunk-3QKGKUTX.js +0 -91
- package/dist/chunk-3QKGKUTX.js.map +0 -1
- package/dist/chunk-4COPR4SY.js.map +0 -1
- package/dist/chunk-C4XQSRW7.js.map +0 -1
|
@@ -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 },\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"]}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatChapterCopy,
|
|
3
|
+
useChapterCopy
|
|
4
|
+
} from "./chunk-NTZSXAFT.js";
|
|
5
|
+
|
|
1
6
|
// src/ui/slot-picker.tsx
|
|
2
7
|
import { useMemo, useState } from "preact/hooks";
|
|
3
8
|
|
|
@@ -103,7 +108,9 @@ import { useEffect, useState as useState3 } from "preact/hooks";
|
|
|
103
108
|
import { useState as useState2 } from "preact/hooks";
|
|
104
109
|
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
105
110
|
function Rescheduler(props) {
|
|
106
|
-
const
|
|
111
|
+
const inheritedCopy = useChapterCopy();
|
|
112
|
+
const copy = props.copy ?? inheritedCopy.members.reschedule;
|
|
113
|
+
const { api, applicationId, timezone, onRescheduled, label = copy.changeTime } = props;
|
|
107
114
|
const [open, setOpen] = useState2(false);
|
|
108
115
|
const [slots, setSlots] = useState2(null);
|
|
109
116
|
const [tz, setTz] = useState2(timezone);
|
|
@@ -117,14 +124,14 @@ function Rescheduler(props) {
|
|
|
117
124
|
const r = await api("/api/schedule/slots");
|
|
118
125
|
if (r.schedulingReady === false) {
|
|
119
126
|
setSlots([]);
|
|
120
|
-
setMsg(
|
|
127
|
+
setMsg(copy.unavailable);
|
|
121
128
|
return;
|
|
122
129
|
}
|
|
123
130
|
setSlots(r.slots ?? []);
|
|
124
131
|
if (r.timezone) setTz(r.timezone);
|
|
125
132
|
} catch {
|
|
126
133
|
setSlots([]);
|
|
127
|
-
setMsg(
|
|
134
|
+
setMsg(copy.loadFailed);
|
|
128
135
|
}
|
|
129
136
|
};
|
|
130
137
|
const pick = async (slot) => {
|
|
@@ -135,7 +142,7 @@ function Rescheduler(props) {
|
|
|
135
142
|
setOpen(false);
|
|
136
143
|
await onRescheduled();
|
|
137
144
|
} catch (e) {
|
|
138
|
-
setMsg(e instanceof Error ? e.message :
|
|
145
|
+
setMsg(e instanceof Error ? e.message : copy.slotGone);
|
|
139
146
|
} finally {
|
|
140
147
|
setBusy(false);
|
|
141
148
|
}
|
|
@@ -154,7 +161,7 @@ function Rescheduler(props) {
|
|
|
154
161
|
) });
|
|
155
162
|
}
|
|
156
163
|
return /* @__PURE__ */ jsxs2("div", { className: "msched", children: [
|
|
157
|
-
slots === null ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children:
|
|
164
|
+
slots === null ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: copy.loading }) : slots.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: msg ?? copy.noTimes }) : /* @__PURE__ */ jsx2(
|
|
158
165
|
SlotPicker,
|
|
159
166
|
{
|
|
160
167
|
slots,
|
|
@@ -163,7 +170,7 @@ function Rescheduler(props) {
|
|
|
163
170
|
onPick: (s) => void pick(s)
|
|
164
171
|
}
|
|
165
172
|
),
|
|
166
|
-
busy ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children:
|
|
173
|
+
busy ? /* @__PURE__ */ jsx2("p", { className: "meeting-note", children: copy.rescheduling }) : null,
|
|
167
174
|
msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx2("p", { className: "meeting-note error", children: msg }) : null,
|
|
168
175
|
/* @__PURE__ */ jsx2("p", { className: "meeting-note", children: /* @__PURE__ */ jsx2(
|
|
169
176
|
"a",
|
|
@@ -173,7 +180,7 @@ function Rescheduler(props) {
|
|
|
173
180
|
e.preventDefault();
|
|
174
181
|
setOpen(false);
|
|
175
182
|
},
|
|
176
|
-
children:
|
|
183
|
+
children: copy.keepTime
|
|
177
184
|
}
|
|
178
185
|
) })
|
|
179
186
|
] });
|
|
@@ -181,9 +188,9 @@ function Rescheduler(props) {
|
|
|
181
188
|
|
|
182
189
|
// src/ui/members.tsx
|
|
183
190
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
184
|
-
function card(kicker, body) {
|
|
191
|
+
function card(copy, kicker, body) {
|
|
185
192
|
return /* @__PURE__ */ jsxs3("div", { className: "card", children: [
|
|
186
|
-
/* @__PURE__ */ jsx3("div", { className: "card-label", children:
|
|
193
|
+
/* @__PURE__ */ jsx3("div", { className: "card-label", children: copy.provisional.cardLabel }),
|
|
187
194
|
/* @__PURE__ */ jsxs3("div", { className: "meeting-block", children: [
|
|
188
195
|
/* @__PURE__ */ jsx3("div", { className: "meeting-kicker", children: kicker }),
|
|
189
196
|
body
|
|
@@ -191,47 +198,78 @@ function card(kicker, body) {
|
|
|
191
198
|
] });
|
|
192
199
|
}
|
|
193
200
|
function ProvisionalCard(props) {
|
|
194
|
-
const { api, application, applyHref, onReschedule } = props;
|
|
201
|
+
const { api, application, applyHref, copy, onReschedule } = props;
|
|
195
202
|
if (!application) {
|
|
196
203
|
return card(
|
|
197
|
-
|
|
204
|
+
copy,
|
|
205
|
+
copy.provisional.applicationNeeded,
|
|
198
206
|
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
199
|
-
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children:
|
|
200
|
-
/* @__PURE__ */ jsx3("a", { className: "apply-link", href: applyHref, children:
|
|
207
|
+
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children: copy.provisional.applicationNeededBody }),
|
|
208
|
+
/* @__PURE__ */ jsx3("a", { className: "apply-link", href: applyHref, children: copy.provisional.apply })
|
|
201
209
|
] })
|
|
202
210
|
);
|
|
203
211
|
}
|
|
204
212
|
if (application.status === "refunded") {
|
|
205
|
-
return card(
|
|
213
|
+
return card(
|
|
214
|
+
copy,
|
|
215
|
+
copy.provisional.refunded,
|
|
216
|
+
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children: copy.provisional.refundedBody })
|
|
217
|
+
);
|
|
206
218
|
}
|
|
207
|
-
const membership = application.paid ? /* @__PURE__ */
|
|
208
|
-
"Your membership is active",
|
|
209
|
-
application.renewalAt ? ` and renews ${fmtDate(application.renewalAt)}` : "",
|
|
210
|
-
"."
|
|
211
|
-
] }) : null;
|
|
219
|
+
const membership = application.paid ? /* @__PURE__ */ jsx3("div", { className: "meeting-note", children: application.renewalAt ? formatChapterCopy(copy.provisional.renews, { date: fmtDate(application.renewalAt) }) : copy.provisional.active }) : null;
|
|
212
220
|
if (application.meetingAt) {
|
|
213
221
|
return card(
|
|
214
|
-
|
|
222
|
+
copy,
|
|
223
|
+
copy.provisional.introductionCall,
|
|
215
224
|
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
216
225
|
/* @__PURE__ */ jsx3("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
|
|
217
|
-
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children:
|
|
218
|
-
application.meetUrl ? /* @__PURE__ */ jsx3("div", { className: "meeting-note", children: /* @__PURE__ */ jsx3("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children:
|
|
219
|
-
/* @__PURE__ */ jsx3(
|
|
226
|
+
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children: copy.provisional.calendarInvite }),
|
|
227
|
+
application.meetUrl ? /* @__PURE__ */ jsx3("div", { className: "meeting-note", children: /* @__PURE__ */ jsx3("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: copy.provisional.joinCall }) }) : null,
|
|
228
|
+
/* @__PURE__ */ jsx3(
|
|
229
|
+
Rescheduler,
|
|
230
|
+
{
|
|
231
|
+
api,
|
|
232
|
+
applicationId: application.id,
|
|
233
|
+
timezone: application.timezone,
|
|
234
|
+
copy: copy.reschedule,
|
|
235
|
+
onRescheduled: onReschedule
|
|
236
|
+
}
|
|
237
|
+
),
|
|
220
238
|
membership
|
|
221
239
|
] })
|
|
222
240
|
);
|
|
223
241
|
}
|
|
224
242
|
return card(
|
|
225
|
-
|
|
243
|
+
copy,
|
|
244
|
+
copy.provisional.bookCall,
|
|
226
245
|
/* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
227
|
-
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children:
|
|
228
|
-
/* @__PURE__ */ jsx3(
|
|
246
|
+
/* @__PURE__ */ jsx3("div", { className: "meeting-note", children: copy.provisional.bookCallBody }),
|
|
247
|
+
/* @__PURE__ */ jsx3(
|
|
248
|
+
Rescheduler,
|
|
249
|
+
{
|
|
250
|
+
api,
|
|
251
|
+
applicationId: application.id,
|
|
252
|
+
timezone: application.timezone,
|
|
253
|
+
copy: copy.reschedule,
|
|
254
|
+
onRescheduled: onReschedule,
|
|
255
|
+
label: copy.provisional.chooseTime
|
|
256
|
+
}
|
|
257
|
+
),
|
|
229
258
|
membership
|
|
230
259
|
] })
|
|
231
260
|
);
|
|
232
261
|
}
|
|
233
262
|
function MembersArea(props) {
|
|
234
|
-
const
|
|
263
|
+
const inheritedCopy = useChapterCopy();
|
|
264
|
+
const {
|
|
265
|
+
api,
|
|
266
|
+
signOut,
|
|
267
|
+
adminHref = "/admin/",
|
|
268
|
+
applyHref = "/join.html",
|
|
269
|
+
memberContent,
|
|
270
|
+
renderProvisional
|
|
271
|
+
} = props;
|
|
272
|
+
const copy = props.copy ?? inheritedCopy.members;
|
|
235
273
|
const [me, setMe] = useState3(null);
|
|
236
274
|
const [error, setError] = useState3(false);
|
|
237
275
|
const reload = async () => {
|
|
@@ -244,8 +282,8 @@ function MembersArea(props) {
|
|
|
244
282
|
useEffect(() => {
|
|
245
283
|
void reload();
|
|
246
284
|
}, []);
|
|
247
|
-
if (error) return /* @__PURE__ */ jsx3("p", { className: "meeting-note", children:
|
|
248
|
-
if (!me) return /* @__PURE__ */ jsx3("p", { className: "meeting-note", children:
|
|
285
|
+
if (error) return /* @__PURE__ */ jsx3("p", { className: "meeting-note", children: copy.loadFailed });
|
|
286
|
+
if (!me) return /* @__PURE__ */ jsx3("p", { className: "meeting-note", children: inheritedCopy.common.loading });
|
|
249
287
|
const role = me.role || "provisional";
|
|
250
288
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
251
289
|
/* @__PURE__ */ jsxs3("div", { className: "card", children: [
|
|
@@ -254,16 +292,34 @@ function MembersArea(props) {
|
|
|
254
292
|
/* @__PURE__ */ jsx3("span", { className: "role-badge " + role, children: role })
|
|
255
293
|
] }),
|
|
256
294
|
/* @__PURE__ */ jsxs3("div", { className: "account-actions", children: [
|
|
257
|
-
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children:
|
|
258
|
-
role === "admin" ? /* @__PURE__ */ jsx3("a", { className: "admin-console-link", href: adminHref, children:
|
|
295
|
+
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children: copy.account.signOut }),
|
|
296
|
+
role === "admin" ? /* @__PURE__ */ jsx3("a", { className: "admin-console-link", href: adminHref, children: copy.account.adminConsole }) : null
|
|
259
297
|
] })
|
|
260
298
|
] }),
|
|
261
|
-
role === "provisional" ?
|
|
299
|
+
role === "provisional" ? (() => {
|
|
300
|
+
const defaultContent = /* @__PURE__ */ jsx3(
|
|
301
|
+
ProvisionalCard,
|
|
302
|
+
{
|
|
303
|
+
api,
|
|
304
|
+
application: me.application,
|
|
305
|
+
applyHref,
|
|
306
|
+
copy,
|
|
307
|
+
onReschedule: reload
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
return renderProvisional?.({
|
|
311
|
+
api,
|
|
312
|
+
application: me.application,
|
|
313
|
+
applyHref,
|
|
314
|
+
reload,
|
|
315
|
+
defaultContent
|
|
316
|
+
}) ?? defaultContent;
|
|
317
|
+
})() : memberContent ?? /* @__PURE__ */ jsx3("div", { className: "card", children: /* @__PURE__ */ jsx3("div", { className: "card-label", children: copy.full.welcome }) })
|
|
262
318
|
] });
|
|
263
319
|
}
|
|
264
320
|
|
|
265
321
|
// src/ui/join.tsx
|
|
266
|
-
import { useEffect as
|
|
322
|
+
import { useEffect as useEffect3, useState as useState6 } from "preact/hooks";
|
|
267
323
|
|
|
268
324
|
// src/ui/payment-step.tsx
|
|
269
325
|
import { useRef, useState as useState4 } from "preact/hooks";
|
|
@@ -288,9 +344,20 @@ function loadStripe() {
|
|
|
288
344
|
return loader;
|
|
289
345
|
}
|
|
290
346
|
function PaymentStep(props) {
|
|
291
|
-
const
|
|
347
|
+
const inheritedCopy = useChapterCopy();
|
|
348
|
+
const {
|
|
349
|
+
applicationId,
|
|
350
|
+
refundPolicyText,
|
|
351
|
+
onPaid,
|
|
352
|
+
appearance,
|
|
353
|
+
fonts,
|
|
354
|
+
renderPriceLines,
|
|
355
|
+
children
|
|
356
|
+
} = props;
|
|
357
|
+
const copy = props.copy ?? inheritedCopy.join.payment;
|
|
292
358
|
const [status, setStatus] = useState4("idle");
|
|
293
359
|
const [error, setError] = useState4(null);
|
|
360
|
+
const [lineItems, setLineItems] = useState4(null);
|
|
294
361
|
const mountRef = useRef(null);
|
|
295
362
|
const stripeRef = useRef(null);
|
|
296
363
|
const elementsRef = useRef(null);
|
|
@@ -304,17 +371,22 @@ function PaymentStep(props) {
|
|
|
304
371
|
body: JSON.stringify({ applicationId, refundPolicyAck: true })
|
|
305
372
|
});
|
|
306
373
|
const data = await res.json();
|
|
307
|
-
if (!res.ok || !data.clientSecret || !data.publishableKey) throw new Error(
|
|
374
|
+
if (!res.ok || !data.clientSecret || !data.publishableKey) throw new Error(copy.setupFailed);
|
|
308
375
|
const stripe = (await loadStripe())(data.publishableKey);
|
|
309
|
-
const elements = stripe.elements({
|
|
376
|
+
const elements = stripe.elements({
|
|
377
|
+
clientSecret: data.clientSecret,
|
|
378
|
+
...appearance ? { appearance } : {},
|
|
379
|
+
...fonts ? { fonts } : {}
|
|
380
|
+
});
|
|
310
381
|
const element = elements.create("payment");
|
|
311
382
|
if (mountRef.current) element.mount(mountRef.current);
|
|
312
383
|
stripeRef.current = stripe;
|
|
313
384
|
elementsRef.current = elements;
|
|
385
|
+
setLineItems(data.lineItems ?? null);
|
|
314
386
|
setStatus("ready");
|
|
315
387
|
} catch (e) {
|
|
316
388
|
setStatus("idle");
|
|
317
|
-
setError(e instanceof Error ? e.message :
|
|
389
|
+
setError(e instanceof Error ? e.message : copy.setupFailed);
|
|
318
390
|
}
|
|
319
391
|
};
|
|
320
392
|
const pay = async () => {
|
|
@@ -323,17 +395,23 @@ function PaymentStep(props) {
|
|
|
323
395
|
if (!stripe || !elements) return;
|
|
324
396
|
setStatus("confirming");
|
|
325
397
|
setError(null);
|
|
326
|
-
|
|
398
|
+
let returnUrl;
|
|
399
|
+
if (typeof window !== "undefined") {
|
|
400
|
+
const next = new URL(window.location.href);
|
|
401
|
+
next.searchParams.set("application", applicationId);
|
|
402
|
+
next.searchParams.set("redirect_status", "succeeded");
|
|
403
|
+
returnUrl = next.toString();
|
|
404
|
+
}
|
|
327
405
|
const result = await stripe.confirmPayment({ elements, confirmParams: { return_url: returnUrl }, redirect: "if_required" });
|
|
328
406
|
if (result.error) {
|
|
329
|
-
setError(result.error.message ??
|
|
407
|
+
setError(result.error.message ?? copy.incomplete);
|
|
330
408
|
setStatus("ready");
|
|
331
409
|
return;
|
|
332
410
|
}
|
|
333
411
|
const paid = result.paymentIntent?.status;
|
|
334
412
|
if (paid === "succeeded" || paid === "processing") onPaid();
|
|
335
413
|
else {
|
|
336
|
-
setError(
|
|
414
|
+
setError(copy.incomplete);
|
|
337
415
|
setStatus("ready");
|
|
338
416
|
}
|
|
339
417
|
};
|
|
@@ -351,17 +429,20 @@ function PaymentStep(props) {
|
|
|
351
429
|
),
|
|
352
430
|
/* @__PURE__ */ jsx4("span", { children: refundPolicyText })
|
|
353
431
|
] }),
|
|
354
|
-
status === "loading" ? /* @__PURE__ */ jsx4("p", { className: "pay-status", children:
|
|
432
|
+
status === "loading" ? /* @__PURE__ */ jsx4("p", { className: "pay-status", children: copy.preparing }) : null,
|
|
433
|
+
lineItems && renderPriceLines ? renderPriceLines(lineItems) : null,
|
|
434
|
+
children,
|
|
355
435
|
/* @__PURE__ */ jsx4("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
|
|
356
|
-
status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx4("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ?
|
|
436
|
+
status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx4("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? copy.processing : copy.payAndContinue }) : null,
|
|
357
437
|
error ? /* @__PURE__ */ jsx4("p", { className: "pay-error", children: error }) : null
|
|
358
438
|
] });
|
|
359
439
|
}
|
|
360
440
|
|
|
361
|
-
// src/ui/join.tsx
|
|
441
|
+
// src/ui/join-booking.tsx
|
|
442
|
+
import { useEffect as useEffect2, useState as useState5 } from "preact/hooks";
|
|
362
443
|
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
363
444
|
function JoinBooking(props) {
|
|
364
|
-
const { applicationId, onBooked } = props;
|
|
445
|
+
const { applicationId, copy, onBooked } = props;
|
|
365
446
|
const [state, setState] = useState5(null);
|
|
366
447
|
const [msg, setMsg] = useState5(null);
|
|
367
448
|
const [busy, setBusy] = useState5(false);
|
|
@@ -373,12 +454,12 @@ function JoinBooking(props) {
|
|
|
373
454
|
const data = await res.json();
|
|
374
455
|
if (data.schedulingReady === false || !data.slots?.length) {
|
|
375
456
|
setState(null);
|
|
376
|
-
setMsg(
|
|
457
|
+
setMsg(copy.unavailable);
|
|
377
458
|
return;
|
|
378
459
|
}
|
|
379
460
|
setState({ slots: data.slots, timezone: data.timezone ?? "UTC" });
|
|
380
461
|
} catch {
|
|
381
|
-
setMsg(
|
|
462
|
+
setMsg(copy.loadFailed);
|
|
382
463
|
}
|
|
383
464
|
};
|
|
384
465
|
useEffect2(() => {
|
|
@@ -397,19 +478,19 @@ function JoinBooking(props) {
|
|
|
397
478
|
const data = await res.json();
|
|
398
479
|
if (res.status === 409 && data.code === "calendar_slot_unavailable") {
|
|
399
480
|
setSelected(null);
|
|
400
|
-
setMsg(
|
|
481
|
+
setMsg(copy.slotTaken);
|
|
401
482
|
await load();
|
|
402
483
|
return;
|
|
403
484
|
}
|
|
404
|
-
if (!res.ok) throw new Error(data.error ??
|
|
485
|
+
if (!res.ok) throw new Error(data.error ?? copy.failed);
|
|
405
486
|
onBooked({ startAt: data.startAt ?? selected.startAt, timezone: state?.timezone ?? "UTC" });
|
|
406
487
|
} catch (e) {
|
|
407
|
-
setMsg(e instanceof Error ? e.message :
|
|
488
|
+
setMsg(e instanceof Error ? e.message : copy.failed);
|
|
408
489
|
} finally {
|
|
409
490
|
setBusy(false);
|
|
410
491
|
}
|
|
411
492
|
};
|
|
412
|
-
if (!state) return /* @__PURE__ */ jsx5("p", { className: "slots-status", children: msg ??
|
|
493
|
+
if (!state) return /* @__PURE__ */ jsx5("p", { className: "slots-status", children: msg ?? copy.loading });
|
|
413
494
|
return /* @__PURE__ */ jsxs5("div", { className: "join-book", children: [
|
|
414
495
|
/* @__PURE__ */ jsx5(
|
|
415
496
|
SlotPicker,
|
|
@@ -422,20 +503,82 @@ function JoinBooking(props) {
|
|
|
422
503
|
}
|
|
423
504
|
),
|
|
424
505
|
/* @__PURE__ */ jsxs5("div", { className: "slot-confirm", hidden: !selected, children: [
|
|
425
|
-
/* @__PURE__ */ jsx5("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ?
|
|
506
|
+
/* @__PURE__ */ jsx5("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? copy.booking : copy.book }),
|
|
426
507
|
msg ? /* @__PURE__ */ jsx5("p", { className: "step2-note", children: msg }) : null
|
|
427
508
|
] })
|
|
428
509
|
] });
|
|
429
510
|
}
|
|
511
|
+
|
|
512
|
+
// src/ui/join.tsx
|
|
513
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
514
|
+
async function loadJoinResume(applicationId, fetcher = fetch) {
|
|
515
|
+
const res = await fetcher(`/api/join/resume?application=${encodeURIComponent(applicationId)}`);
|
|
516
|
+
const data = await res.json();
|
|
517
|
+
if (!res.ok) throw new Error(data.error ?? "The application could not be resumed.");
|
|
518
|
+
if (data.state === "done" && data.booked) {
|
|
519
|
+
return { step: "done", applicationId: data.applicationId, booked: data.booked };
|
|
520
|
+
}
|
|
521
|
+
if (data.state === "paymentPending") {
|
|
522
|
+
return { step: "paymentPending", applicationId: data.applicationId };
|
|
523
|
+
}
|
|
524
|
+
if (data.state === "booking") {
|
|
525
|
+
return { step: "booking", applicationId: data.applicationId };
|
|
526
|
+
}
|
|
527
|
+
return { step: "payment", applicationId: data.applicationId };
|
|
528
|
+
}
|
|
430
529
|
function JoinIsland(props) {
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
530
|
+
const inheritedCopy = useChapterCopy();
|
|
531
|
+
const {
|
|
532
|
+
config,
|
|
533
|
+
children,
|
|
534
|
+
membersHref = "/members/",
|
|
535
|
+
renderStepHeader,
|
|
536
|
+
renderDone,
|
|
537
|
+
renderSubmit
|
|
538
|
+
} = props;
|
|
539
|
+
const copy = props.copy ?? config.copy ?? inheritedCopy.join;
|
|
540
|
+
const [state, setState] = useState6(props.initialState ?? { step: "form" });
|
|
541
|
+
const [error, setError] = useState6(null);
|
|
542
|
+
const [submitting, setSubmitting] = useState6(false);
|
|
543
|
+
useEffect3(() => {
|
|
544
|
+
const resolver = props.loadResume ?? (() => {
|
|
545
|
+
if (typeof window === "undefined") return Promise.resolve(null);
|
|
546
|
+
const applicationId = new URL(window.location.href).searchParams.get("application");
|
|
547
|
+
return applicationId ? loadJoinResume(applicationId) : Promise.resolve(null);
|
|
548
|
+
});
|
|
549
|
+
let live = true;
|
|
550
|
+
void resolver().then((resumed) => {
|
|
551
|
+
if (live && resumed) setState(resumed);
|
|
552
|
+
}).catch(() => {
|
|
553
|
+
if (live) setError(copy.form.unexpectedFailure);
|
|
554
|
+
});
|
|
555
|
+
return () => {
|
|
556
|
+
live = false;
|
|
557
|
+
};
|
|
558
|
+
}, [props.loadResume]);
|
|
559
|
+
useEffect3(() => {
|
|
560
|
+
if (state.step !== "paymentPending") return;
|
|
561
|
+
let live = true;
|
|
562
|
+
let timer;
|
|
563
|
+
const poll = async () => {
|
|
564
|
+
try {
|
|
565
|
+
const resumed = props.loadResume ? await props.loadResume() : await loadJoinResume(state.applicationId);
|
|
566
|
+
if (!live || !resumed) return;
|
|
567
|
+
setState(resumed);
|
|
568
|
+
if (resumed.step === "paymentPending") timer = setTimeout(() => void poll(), 1500);
|
|
569
|
+
} catch {
|
|
570
|
+
if (live) timer = setTimeout(() => void poll(), 2500);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
timer = setTimeout(() => void poll(), 1e3);
|
|
574
|
+
return () => {
|
|
575
|
+
live = false;
|
|
576
|
+
if (timer) clearTimeout(timer);
|
|
577
|
+
};
|
|
578
|
+
}, [state, props.loadResume]);
|
|
437
579
|
const submit = async (e) => {
|
|
438
580
|
e.preventDefault();
|
|
581
|
+
if (submitting || props.submitDisabled === true) return;
|
|
439
582
|
setSubmitting(true);
|
|
440
583
|
setError(null);
|
|
441
584
|
try {
|
|
@@ -448,42 +591,80 @@ function JoinIsland(props) {
|
|
|
448
591
|
body: JSON.stringify(fields)
|
|
449
592
|
});
|
|
450
593
|
const data = await res.json();
|
|
451
|
-
if (!res.ok || !data.id) throw new Error(data.error ??
|
|
452
|
-
|
|
453
|
-
|
|
594
|
+
if (!res.ok || !data.id) throw new Error(data.error ?? copy.form.submitFailed);
|
|
595
|
+
setState({
|
|
596
|
+
step: config.paymentsReady ? "payment" : "booking",
|
|
597
|
+
applicationId: data.id
|
|
598
|
+
});
|
|
454
599
|
} catch (err) {
|
|
455
|
-
setError(err instanceof Error ? err.message :
|
|
600
|
+
setError(err instanceof Error ? err.message : copy.form.unexpectedFailure);
|
|
456
601
|
} finally {
|
|
457
602
|
setSubmitting(false);
|
|
458
603
|
}
|
|
459
604
|
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
605
|
+
const header = renderStepHeader?.({
|
|
606
|
+
state,
|
|
607
|
+
..."applicationId" in state ? { applicationId: state.applicationId } : {}
|
|
608
|
+
});
|
|
609
|
+
if (state.step === "done") {
|
|
610
|
+
const body = renderDone?.({ ...state, membersHref }) ?? /* @__PURE__ */ jsxs6("div", { className: "join-done card", children: [
|
|
611
|
+
/* @__PURE__ */ jsx6("div", { className: "card-label", children: copy.done.label }),
|
|
612
|
+
/* @__PURE__ */ jsx6("p", { className: "meeting-date", children: fullLabel(state.booked.startAt, state.booked.timezone) }),
|
|
613
|
+
/* @__PURE__ */ jsx6("p", { className: "meeting-note", children: copy.done.calendarInvite }),
|
|
614
|
+
/* @__PURE__ */ jsx6("a", { className: "apply-link", href: membersHref, children: copy.done.memberArea })
|
|
615
|
+
] });
|
|
616
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
617
|
+
header,
|
|
618
|
+
body
|
|
466
619
|
] });
|
|
467
620
|
}
|
|
468
|
-
if (step === "
|
|
469
|
-
return /* @__PURE__ */
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
621
|
+
if (state.step === "booking") {
|
|
622
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
623
|
+
header,
|
|
624
|
+
/* @__PURE__ */ jsx6(
|
|
625
|
+
JoinBooking,
|
|
626
|
+
{
|
|
627
|
+
applicationId: state.applicationId,
|
|
628
|
+
copy: copy.booking,
|
|
629
|
+
onBooked: (booked) => {
|
|
630
|
+
setState({ step: "done", applicationId: state.applicationId, booked });
|
|
631
|
+
}
|
|
476
632
|
}
|
|
477
|
-
|
|
478
|
-
);
|
|
633
|
+
)
|
|
634
|
+
] });
|
|
635
|
+
}
|
|
636
|
+
if (state.step === "payment") {
|
|
637
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
638
|
+
header,
|
|
639
|
+
/* @__PURE__ */ jsx6(
|
|
640
|
+
PaymentStep,
|
|
641
|
+
{
|
|
642
|
+
applicationId: state.applicationId,
|
|
643
|
+
refundPolicyText: config.refundPolicyText ?? "",
|
|
644
|
+
copy: copy.payment,
|
|
645
|
+
appearance: props.payment?.appearance,
|
|
646
|
+
fonts: props.payment?.fonts,
|
|
647
|
+
renderPriceLines: props.payment?.renderPriceLines,
|
|
648
|
+
onPaid: () => setState({ step: "booking", applicationId: state.applicationId }),
|
|
649
|
+
children: props.payment?.children
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
] });
|
|
479
653
|
}
|
|
480
|
-
if (step === "
|
|
481
|
-
return /* @__PURE__ */
|
|
654
|
+
if (state.step === "paymentPending") {
|
|
655
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
656
|
+
header,
|
|
657
|
+
/* @__PURE__ */ jsx6("p", { className: "pay-status", role: "status", children: copy.payment.pending })
|
|
658
|
+
] });
|
|
482
659
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
/* @__PURE__ */
|
|
660
|
+
const submitDisabled = submitting || props.submitDisabled === true;
|
|
661
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
662
|
+
header,
|
|
663
|
+
/* @__PURE__ */ jsxs6("form", { className: "join-form", onSubmit: (e) => void submit(e), children: [
|
|
664
|
+
children,
|
|
665
|
+
error ? /* @__PURE__ */ jsx6("p", { className: "join-error", children: error }) : null,
|
|
666
|
+
renderSubmit?.({ submitting, disabled: submitDisabled }) ?? /* @__PURE__ */ jsx6("button", { className: "submit-btn", type: "submit", disabled: submitDisabled, children: submitting ? copy.form.submitting : copy.form.submit })
|
|
667
|
+
] })
|
|
487
668
|
] });
|
|
488
669
|
}
|
|
489
670
|
|
|
@@ -500,6 +681,7 @@ export {
|
|
|
500
681
|
Rescheduler,
|
|
501
682
|
MembersArea,
|
|
502
683
|
PaymentStep,
|
|
684
|
+
loadJoinResume,
|
|
503
685
|
JoinIsland
|
|
504
686
|
};
|
|
505
|
-
//# sourceMappingURL=chunk-
|
|
687
|
+
//# sourceMappingURL=chunk-OYXNI6VR.js.map
|