@odla-ai/chapter 0.4.0 → 0.5.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/dist/ui/index.js CHANGED
@@ -212,8 +212,69 @@ function ChapterAdmin(props) {
212
212
  ] });
213
213
  }
214
214
 
215
+ // src/ui/admin-people.tsx
216
+ import { useState as useState3 } from "react";
217
+ import { CrmList, RecordPanel, useCrmQuery, useCrmRecord } from "@odla-ai/crm/ui";
218
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
219
+ function PeopleBody(props) {
220
+ const { crm, client, type } = props;
221
+ const hookClient = client;
222
+ const query = useCrmQuery(hookClient, type);
223
+ const [openId, setOpenId] = useState3(null);
224
+ const record = useCrmRecord(hookClient, openId);
225
+ const [saving, setSaving] = useState3(false);
226
+ const saveFields = async (input) => {
227
+ if (!openId) return;
228
+ setSaving(true);
229
+ try {
230
+ await client.updateRecord(openId, { input });
231
+ record.refresh();
232
+ query.refresh();
233
+ } finally {
234
+ setSaving(false);
235
+ }
236
+ };
237
+ const moveStage = async (to) => {
238
+ if (!openId) return;
239
+ await client.setStage(openId, to);
240
+ record.refresh();
241
+ query.refresh();
242
+ };
243
+ const addTag = async (tag) => {
244
+ if (openId) {
245
+ await client.addTag(openId, tag);
246
+ record.refresh();
247
+ }
248
+ };
249
+ const removeTag = async (tag) => {
250
+ if (openId) {
251
+ await client.removeTag(openId, tag);
252
+ record.refresh();
253
+ }
254
+ };
255
+ return /* @__PURE__ */ jsxs3("div", { className: "wrap", children: [
256
+ /* @__PURE__ */ jsx3(CrmList, { crm, type, query, onOpenRecord: (r) => setOpenId(r.id) }),
257
+ record.detail ? /* @__PURE__ */ jsx3(
258
+ RecordPanel,
259
+ {
260
+ crm,
261
+ detail: record.detail,
262
+ onSaveFields: saveFields,
263
+ onMoveStage: moveStage,
264
+ onAddTag: addTag,
265
+ onRemoveTag: removeTag,
266
+ saving
267
+ }
268
+ ) : null
269
+ ] });
270
+ }
271
+ function peopleSection(options) {
272
+ const { crm, id = "people", label = "People", type = "person" } = options;
273
+ return { id, label, render: (ctx) => /* @__PURE__ */ jsx3(PeopleBody, { crm, client: ctx.client, type }) };
274
+ }
275
+
215
276
  // src/ui/slot-picker.tsx
216
- import { useMemo as useMemo2, useState as useState3 } from "react";
277
+ import { useMemo as useMemo2, useState as useState4 } from "react";
217
278
 
218
279
  // src/ui/datetime.ts
219
280
  function tzShort(tz) {
@@ -264,7 +325,7 @@ function groupSlotsByDay(slots, tz) {
264
325
  }
265
326
 
266
327
  // src/ui/slot-picker.tsx
267
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
328
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
268
329
  var DEFAULT_CLASSES = {
269
330
  days: "slot-days",
270
331
  day: "slot-day",
@@ -275,13 +336,13 @@ function SlotPicker(props) {
275
336
  const { slots, timezone, selectedStartAt, onPick, onDayChange, classes = DEFAULT_CLASSES } = props;
276
337
  const byDay = useMemo2(() => groupSlotsByDay(slots, timezone), [slots, timezone]);
277
338
  const dayKeys = [...byDay.keys()];
278
- const [activeDay, setActiveDay] = useState3(dayKeys[0]);
339
+ const [activeDay, setActiveDay] = useState4(dayKeys[0]);
279
340
  const day = activeDay !== void 0 && byDay.has(activeDay) ? activeDay : dayKeys[0];
280
341
  const times = (day !== void 0 ? byDay.get(day) : void 0) ?? [];
281
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
282
- /* @__PURE__ */ jsx3("div", { className: classes.days, children: dayKeys.map((key) => {
342
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
343
+ /* @__PURE__ */ jsx4("div", { className: classes.days, children: dayKeys.map((key) => {
283
344
  const first = byDay.get(key)?.[0];
284
- return /* @__PURE__ */ jsx3(
345
+ return /* @__PURE__ */ jsx4(
285
346
  "button",
286
347
  {
287
348
  type: "button",
@@ -296,7 +357,7 @@ function SlotPicker(props) {
296
357
  key
297
358
  );
298
359
  }) }),
299
- /* @__PURE__ */ jsx3("div", { className: classes.times, children: times.map((s) => /* @__PURE__ */ jsx3(
360
+ /* @__PURE__ */ jsx4("div", { className: classes.times, children: times.map((s) => /* @__PURE__ */ jsx4(
300
361
  "button",
301
362
  {
302
363
  type: "button",
@@ -311,18 +372,18 @@ function SlotPicker(props) {
311
372
  }
312
373
 
313
374
  // src/ui/members.tsx
314
- import { useEffect as useEffect3, useState as useState5 } from "react";
375
+ import { useEffect as useEffect3, useState as useState6 } from "react";
315
376
 
316
377
  // src/ui/reschedule.tsx
317
- import { useState as useState4 } from "react";
318
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
378
+ import { useState as useState5 } from "react";
379
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
319
380
  function Rescheduler(props) {
320
381
  const { api, applicationId, timezone, onRescheduled, label = "Change your time" } = props;
321
- const [open, setOpen] = useState4(false);
322
- const [slots, setSlots] = useState4(null);
323
- const [tz, setTz] = useState4(timezone);
324
- const [busy, setBusy] = useState4(false);
325
- const [msg, setMsg] = useState4(null);
382
+ const [open, setOpen] = useState5(false);
383
+ const [slots, setSlots] = useState5(null);
384
+ const [tz, setTz] = useState5(timezone);
385
+ const [busy, setBusy] = useState5(false);
386
+ const [msg, setMsg] = useState5(null);
326
387
  const start = async () => {
327
388
  setOpen(true);
328
389
  setSlots(null);
@@ -355,7 +416,7 @@ function Rescheduler(props) {
355
416
  }
356
417
  };
357
418
  if (!open) {
358
- return /* @__PURE__ */ jsx4("p", { className: "meeting-note", children: /* @__PURE__ */ jsx4(
419
+ return /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: /* @__PURE__ */ jsx5(
359
420
  "a",
360
421
  {
361
422
  href: "#",
@@ -367,8 +428,8 @@ function Rescheduler(props) {
367
428
  }
368
429
  ) });
369
430
  }
370
- return /* @__PURE__ */ jsxs4("div", { className: "msched", children: [
371
- slots === null ? /* @__PURE__ */ jsx4("p", { className: "meeting-note", children: "Loading available times\u2026" }) : slots.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "meeting-note", children: msg ?? "No open times right now. Please check back soon." }) : /* @__PURE__ */ jsx4(
431
+ return /* @__PURE__ */ jsxs5("div", { className: "msched", children: [
432
+ slots === null ? /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: "Loading available times\u2026" }) : slots.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: msg ?? "No open times right now. Please check back soon." }) : /* @__PURE__ */ jsx5(
372
433
  SlotPicker,
373
434
  {
374
435
  slots,
@@ -377,9 +438,9 @@ function Rescheduler(props) {
377
438
  onPick: (s) => void pick(s)
378
439
  }
379
440
  ),
380
- busy ? /* @__PURE__ */ jsx4("p", { className: "meeting-note", children: "Rescheduling\u2026" }) : null,
381
- msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx4("p", { className: "meeting-note error", children: msg }) : null,
382
- /* @__PURE__ */ jsx4("p", { className: "meeting-note", children: /* @__PURE__ */ jsx4(
441
+ busy ? /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: "Rescheduling\u2026" }) : null,
442
+ msg && slots && slots.length > 0 ? /* @__PURE__ */ jsx5("p", { className: "meeting-note error", children: msg }) : null,
443
+ /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: /* @__PURE__ */ jsx5(
383
444
  "a",
384
445
  {
385
446
  href: "#",
@@ -394,12 +455,12 @@ function Rescheduler(props) {
394
455
  }
395
456
 
396
457
  // src/ui/members.tsx
397
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
458
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
398
459
  function card(kicker, body) {
399
- return /* @__PURE__ */ jsxs5("div", { className: "card", children: [
400
- /* @__PURE__ */ jsx5("div", { className: "card-label", children: "Your Application" }),
401
- /* @__PURE__ */ jsxs5("div", { className: "meeting-block", children: [
402
- /* @__PURE__ */ jsx5("div", { className: "meeting-kicker", children: kicker }),
460
+ return /* @__PURE__ */ jsxs6("div", { className: "card", children: [
461
+ /* @__PURE__ */ jsx6("div", { className: "card-label", children: "Your Application" }),
462
+ /* @__PURE__ */ jsxs6("div", { className: "meeting-block", children: [
463
+ /* @__PURE__ */ jsx6("div", { className: "meeting-kicker", children: kicker }),
403
464
  body
404
465
  ] })
405
466
  ] });
@@ -409,16 +470,16 @@ function ProvisionalCard(props) {
409
470
  if (!application) {
410
471
  return card(
411
472
  "One step remains",
412
- /* @__PURE__ */ jsxs5(Fragment3, { children: [
413
- /* @__PURE__ */ jsx5("div", { className: "meeting-note", children: "Your account is ready, and the application that completes it takes a few minutes." }),
414
- /* @__PURE__ */ jsx5("a", { className: "apply-link", href: applyHref, children: "Apply for membership" })
473
+ /* @__PURE__ */ jsxs6(Fragment3, { children: [
474
+ /* @__PURE__ */ jsx6("div", { className: "meeting-note", children: "Your account is ready, and the application that completes it takes a few minutes." }),
475
+ /* @__PURE__ */ jsx6("a", { className: "apply-link", href: applyHref, children: "Apply for membership" })
415
476
  ] })
416
477
  );
417
478
  }
418
479
  if (application.status === "refunded") {
419
- return card("Membership refunded", /* @__PURE__ */ jsx5("div", { className: "meeting-note", children: "Your fee has been refunded in full and your membership is canceled." }));
480
+ return card("Membership refunded", /* @__PURE__ */ jsx6("div", { className: "meeting-note", children: "Your fee has been refunded in full and your membership is canceled." }));
420
481
  }
421
- const membership = application.paid ? /* @__PURE__ */ jsxs5("div", { className: "meeting-note", children: [
482
+ const membership = application.paid ? /* @__PURE__ */ jsxs6("div", { className: "meeting-note", children: [
422
483
  "Your membership is active",
423
484
  application.renewalAt ? ` and renews ${fmtDate(application.renewalAt)}` : "",
424
485
  "."
@@ -426,28 +487,28 @@ function ProvisionalCard(props) {
426
487
  if (application.meetingAt) {
427
488
  return card(
428
489
  "Your introduction call",
429
- /* @__PURE__ */ jsxs5(Fragment3, { children: [
430
- /* @__PURE__ */ jsx5("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
431
- /* @__PURE__ */ jsx5("div", { className: "meeting-note", children: "A calendar invitation with the video call link is in your email." }),
432
- application.meetUrl ? /* @__PURE__ */ jsx5("div", { className: "meeting-note", children: /* @__PURE__ */ jsx5("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: "Join the video call" }) }) : null,
433
- /* @__PURE__ */ jsx5(Rescheduler, { api, applicationId: application.id, timezone: application.timezone, onRescheduled: onReschedule }),
490
+ /* @__PURE__ */ jsxs6(Fragment3, { children: [
491
+ /* @__PURE__ */ jsx6("div", { className: "meeting-date", children: fullLabel(application.meetingAt, application.timezone) }),
492
+ /* @__PURE__ */ jsx6("div", { className: "meeting-note", children: "A calendar invitation with the video call link is in your email." }),
493
+ application.meetUrl ? /* @__PURE__ */ jsx6("div", { className: "meeting-note", children: /* @__PURE__ */ jsx6("a", { href: application.meetUrl, target: "_blank", rel: "noopener", children: "Join the video call" }) }) : null,
494
+ /* @__PURE__ */ jsx6(Rescheduler, { api, applicationId: application.id, timezone: application.timezone, onRescheduled: onReschedule }),
434
495
  membership
435
496
  ] })
436
497
  );
437
498
  }
438
499
  return card(
439
500
  "Book your introduction call",
440
- /* @__PURE__ */ jsxs5(Fragment3, { children: [
441
- /* @__PURE__ */ jsx5("div", { className: "meeting-note", children: "Your application is in. Choose a time below, and a calendar invitation will reach your email." }),
442
- /* @__PURE__ */ jsx5(Rescheduler, { api, applicationId: application.id, timezone: application.timezone, onRescheduled: onReschedule, label: "Choose a time" }),
501
+ /* @__PURE__ */ jsxs6(Fragment3, { children: [
502
+ /* @__PURE__ */ jsx6("div", { className: "meeting-note", children: "Your application is in. Choose a time below, and a calendar invitation will reach your email." }),
503
+ /* @__PURE__ */ jsx6(Rescheduler, { api, applicationId: application.id, timezone: application.timezone, onRescheduled: onReschedule, label: "Choose a time" }),
443
504
  membership
444
505
  ] })
445
506
  );
446
507
  }
447
508
  function MembersArea(props) {
448
509
  const { api, signOut, adminHref = "/admin/", applyHref = "/join.html", memberContent } = props;
449
- const [me, setMe] = useState5(null);
450
- const [error, setError] = useState5(false);
510
+ const [me, setMe] = useState6(null);
511
+ const [error, setError] = useState6(false);
451
512
  const reload = async () => {
452
513
  try {
453
514
  setMe(await api("/api/me"));
@@ -458,30 +519,30 @@ function MembersArea(props) {
458
519
  useEffect3(() => {
459
520
  void reload();
460
521
  }, []);
461
- if (error) return /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: "Sign in is briefly unavailable. Please refresh." });
462
- if (!me) return /* @__PURE__ */ jsx5("p", { className: "meeting-note", children: "Loading\u2026" });
522
+ if (error) return /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: "Sign in is briefly unavailable. Please refresh." });
523
+ if (!me) return /* @__PURE__ */ jsx6("p", { className: "meeting-note", children: "Loading\u2026" });
463
524
  const role = me.role || "provisional";
464
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
465
- /* @__PURE__ */ jsxs5("div", { className: "card", children: [
466
- /* @__PURE__ */ jsxs5("div", { className: "member-row", children: [
467
- /* @__PURE__ */ jsx5("span", { className: "member-email", children: me.email }),
468
- /* @__PURE__ */ jsx5("span", { className: "role-badge " + role, children: role })
525
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
526
+ /* @__PURE__ */ jsxs6("div", { className: "card", children: [
527
+ /* @__PURE__ */ jsxs6("div", { className: "member-row", children: [
528
+ /* @__PURE__ */ jsx6("span", { className: "member-email", children: me.email }),
529
+ /* @__PURE__ */ jsx6("span", { className: "role-badge " + role, children: role })
469
530
  ] }),
470
- /* @__PURE__ */ jsxs5("div", { className: "account-actions", children: [
471
- /* @__PURE__ */ jsx5("button", { className: "btn secondary mini", onClick: signOut, children: "Sign out" }),
472
- role === "admin" ? /* @__PURE__ */ jsx5("a", { className: "admin-console-link", href: adminHref, children: "Admin console" }) : null
531
+ /* @__PURE__ */ jsxs6("div", { className: "account-actions", children: [
532
+ /* @__PURE__ */ jsx6("button", { className: "btn secondary mini", onClick: signOut, children: "Sign out" }),
533
+ role === "admin" ? /* @__PURE__ */ jsx6("a", { className: "admin-console-link", href: adminHref, children: "Admin console" }) : null
473
534
  ] })
474
535
  ] }),
475
- role === "provisional" ? /* @__PURE__ */ jsx5(ProvisionalCard, { api, application: me.application, applyHref, onReschedule: reload }) : memberContent ?? /* @__PURE__ */ jsx5("div", { className: "card", children: /* @__PURE__ */ jsx5("div", { className: "card-label", children: "Welcome back." }) })
536
+ role === "provisional" ? /* @__PURE__ */ jsx6(ProvisionalCard, { api, application: me.application, applyHref, onReschedule: reload }) : memberContent ?? /* @__PURE__ */ jsx6("div", { className: "card", children: /* @__PURE__ */ jsx6("div", { className: "card-label", children: "Welcome back." }) })
476
537
  ] });
477
538
  }
478
539
 
479
540
  // src/ui/join.tsx
480
- import { useEffect as useEffect4, useState as useState7 } from "react";
541
+ import { useEffect as useEffect4, useState as useState8 } from "react";
481
542
 
482
543
  // src/ui/payment-step.tsx
483
- import { useRef, useState as useState6 } from "react";
484
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
544
+ import { useRef, useState as useState7 } from "react";
545
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
485
546
  var loader = null;
486
547
  function loadStripe() {
487
548
  const existing = globalThis.Stripe;
@@ -503,8 +564,8 @@ function loadStripe() {
503
564
  }
504
565
  function PaymentStep(props) {
505
566
  const { applicationId, refundPolicyText, onPaid } = props;
506
- const [status, setStatus] = useState6("idle");
507
- const [error, setError] = useState6(null);
567
+ const [status, setStatus] = useState7("idle");
568
+ const [error, setError] = useState7(null);
508
569
  const mountRef = useRef(null);
509
570
  const stripeRef = useRef(null);
510
571
  const elementsRef = useRef(null);
@@ -551,9 +612,9 @@ function PaymentStep(props) {
551
612
  setStatus("ready");
552
613
  }
553
614
  };
554
- return /* @__PURE__ */ jsxs6("div", { className: "join-pay", children: [
555
- /* @__PURE__ */ jsxs6("label", { className: "compliance-box", children: [
556
- /* @__PURE__ */ jsx6(
615
+ return /* @__PURE__ */ jsxs7("div", { className: "join-pay", children: [
616
+ /* @__PURE__ */ jsxs7("label", { className: "compliance-box", children: [
617
+ /* @__PURE__ */ jsx7(
557
618
  "input",
558
619
  {
559
620
  type: "checkbox",
@@ -563,23 +624,23 @@ function PaymentStep(props) {
563
624
  }
564
625
  }
565
626
  ),
566
- /* @__PURE__ */ jsx6("span", { children: refundPolicyText })
627
+ /* @__PURE__ */ jsx7("span", { children: refundPolicyText })
567
628
  ] }),
568
- status === "loading" ? /* @__PURE__ */ jsx6("p", { className: "pay-status", children: "Preparing secure payment\u2026" }) : null,
569
- /* @__PURE__ */ jsx6("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
570
- status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx6("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? "Processing\u2026" : "Pay and continue" }) : null,
571
- error ? /* @__PURE__ */ jsx6("p", { className: "pay-error", children: error }) : null
629
+ status === "loading" ? /* @__PURE__ */ jsx7("p", { className: "pay-status", children: "Preparing secure payment\u2026" }) : null,
630
+ /* @__PURE__ */ jsx7("div", { ref: mountRef, hidden: status === "idle" || status === "loading" }),
631
+ status === "ready" || status === "confirming" ? /* @__PURE__ */ jsx7("button", { className: "submit-btn", disabled: status === "confirming", onClick: () => void pay(), children: status === "confirming" ? "Processing\u2026" : "Pay and continue" }) : null,
632
+ error ? /* @__PURE__ */ jsx7("p", { className: "pay-error", children: error }) : null
572
633
  ] });
573
634
  }
574
635
 
575
636
  // src/ui/join.tsx
576
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
637
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
577
638
  function JoinBooking(props) {
578
639
  const { applicationId, onBooked } = props;
579
- const [state, setState] = useState7(null);
580
- const [msg, setMsg] = useState7(null);
581
- const [busy, setBusy] = useState7(false);
582
- const [selected, setSelected] = useState7(null);
640
+ const [state, setState] = useState8(null);
641
+ const [msg, setMsg] = useState8(null);
642
+ const [busy, setBusy] = useState8(false);
643
+ const [selected, setSelected] = useState8(null);
583
644
  const load = async () => {
584
645
  setMsg(null);
585
646
  try {
@@ -623,9 +684,9 @@ function JoinBooking(props) {
623
684
  setBusy(false);
624
685
  }
625
686
  };
626
- if (!state) return /* @__PURE__ */ jsx7("p", { className: "slots-status", children: msg ?? "Loading available times\u2026" });
627
- return /* @__PURE__ */ jsxs7("div", { className: "join-book", children: [
628
- /* @__PURE__ */ jsx7(
687
+ if (!state) return /* @__PURE__ */ jsx8("p", { className: "slots-status", children: msg ?? "Loading available times\u2026" });
688
+ return /* @__PURE__ */ jsxs8("div", { className: "join-book", children: [
689
+ /* @__PURE__ */ jsx8(
629
690
  SlotPicker,
630
691
  {
631
692
  slots: state.slots,
@@ -635,19 +696,19 @@ function JoinBooking(props) {
635
696
  onDayChange: () => setSelected(null)
636
697
  }
637
698
  ),
638
- /* @__PURE__ */ jsxs7("div", { className: "slot-confirm", hidden: !selected, children: [
639
- /* @__PURE__ */ jsx7("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? "Booking\u2026" : "Book this time" }),
640
- msg ? /* @__PURE__ */ jsx7("p", { className: "step2-note", children: msg }) : null
699
+ /* @__PURE__ */ jsxs8("div", { className: "slot-confirm", hidden: !selected, children: [
700
+ /* @__PURE__ */ jsx8("button", { className: "submit-btn", disabled: busy, onClick: () => void book(), children: busy ? "Booking\u2026" : "Book this time" }),
701
+ msg ? /* @__PURE__ */ jsx8("p", { className: "step2-note", children: msg }) : null
641
702
  ] })
642
703
  ] });
643
704
  }
644
705
  function JoinIsland(props) {
645
706
  const { config, children, membersHref = "/members/" } = props;
646
- const [step, setStep] = useState7("form");
647
- const [applicationId, setApplicationId] = useState7(null);
648
- const [error, setError] = useState7(null);
649
- const [submitting, setSubmitting] = useState7(false);
650
- const [booked, setBooked] = useState7(null);
707
+ const [step, setStep] = useState8("form");
708
+ const [applicationId, setApplicationId] = useState8(null);
709
+ const [error, setError] = useState8(null);
710
+ const [submitting, setSubmitting] = useState8(false);
711
+ const [booked, setBooked] = useState8(null);
651
712
  const submit = async (e) => {
652
713
  e.preventDefault();
653
714
  setSubmitting(true);
@@ -672,15 +733,15 @@ function JoinIsland(props) {
672
733
  }
673
734
  };
674
735
  if (step === "done" && booked) {
675
- return /* @__PURE__ */ jsxs7("div", { className: "join-done card", children: [
676
- /* @__PURE__ */ jsx7("div", { className: "card-label", children: "You're booked" }),
677
- /* @__PURE__ */ jsx7("p", { className: "meeting-date", children: fullLabel(booked.startAt, booked.timezone) }),
678
- /* @__PURE__ */ jsx7("p", { className: "meeting-note", children: "A calendar invitation with the video call link is on its way to your email." }),
679
- /* @__PURE__ */ jsx7("a", { className: "apply-link", href: membersHref, children: "Go to your member area" })
736
+ return /* @__PURE__ */ jsxs8("div", { className: "join-done card", children: [
737
+ /* @__PURE__ */ jsx8("div", { className: "card-label", children: "You're booked" }),
738
+ /* @__PURE__ */ jsx8("p", { className: "meeting-date", children: fullLabel(booked.startAt, booked.timezone) }),
739
+ /* @__PURE__ */ jsx8("p", { className: "meeting-note", children: "A calendar invitation with the video call link is on its way to your email." }),
740
+ /* @__PURE__ */ jsx8("a", { className: "apply-link", href: membersHref, children: "Go to your member area" })
680
741
  ] });
681
742
  }
682
743
  if (step === "book" && applicationId) {
683
- return /* @__PURE__ */ jsx7(
744
+ return /* @__PURE__ */ jsx8(
684
745
  JoinBooking,
685
746
  {
686
747
  applicationId,
@@ -692,15 +753,47 @@ function JoinIsland(props) {
692
753
  );
693
754
  }
694
755
  if (step === "pay" && applicationId) {
695
- return /* @__PURE__ */ jsx7(PaymentStep, { applicationId, refundPolicyText: config.refundPolicyText ?? "", onPaid: () => setStep("book") });
756
+ return /* @__PURE__ */ jsx8(PaymentStep, { applicationId, refundPolicyText: config.refundPolicyText ?? "", onPaid: () => setStep("book") });
696
757
  }
697
- return /* @__PURE__ */ jsxs7("form", { className: "join-form", onSubmit: (e) => void submit(e), children: [
758
+ return /* @__PURE__ */ jsxs8("form", { className: "join-form", onSubmit: (e) => void submit(e), children: [
698
759
  children,
699
- error ? /* @__PURE__ */ jsx7("p", { className: "join-error", children: error }) : null,
700
- /* @__PURE__ */ jsx7("button", { className: "submit-btn", type: "submit", disabled: submitting, children: submitting ? "Submitting\u2026" : "Submit application" })
760
+ error ? /* @__PURE__ */ jsx8("p", { className: "join-error", children: error }) : null,
761
+ /* @__PURE__ */ jsx8("button", { className: "submit-btn", type: "submit", disabled: submitting, children: submitting ? "Submitting\u2026" : "Submit application" })
701
762
  ] });
702
763
  }
764
+
765
+ // src/brand.ts
766
+ function paletteVar(key) {
767
+ return key.startsWith("--") ? key : `--${key}`;
768
+ }
769
+ function cleanValue(value) {
770
+ return value.replace(/[<>{};]/g, "").trim();
771
+ }
772
+ function brandTokens(brand) {
773
+ if (!brand) return "";
774
+ const decls = [];
775
+ for (const [key, value] of Object.entries(brand.palette ?? {})) {
776
+ if (typeof value === "string" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);
777
+ }
778
+ const fonts = brand.fonts;
779
+ if (fonts?.display) decls.push(`--ui-font-display: ${cleanValue(fonts.display)};`);
780
+ if (fonts?.body) decls.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);
781
+ if (fonts?.numeral) decls.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);
782
+ return decls.length ? `:root {
783
+ ${decls.join("\n ")}
784
+ }
785
+ ` : "";
786
+ }
787
+
788
+ // src/ui/brand-style.tsx
789
+ import { jsx as jsx9 } from "react/jsx-runtime";
790
+ function BrandStyle(props) {
791
+ const css = brandTokens(props.brand);
792
+ if (!css) return null;
793
+ return /* @__PURE__ */ jsx9("style", { children: css });
794
+ }
703
795
  export {
796
+ BrandStyle,
704
797
  ChapterAdmin,
705
798
  JoinIsland,
706
799
  MembersArea,
@@ -713,6 +806,7 @@ export {
713
806
  fmtMoney,
714
807
  fullLabel,
715
808
  groupSlotsByDay,
809
+ peopleSection,
716
810
  timeLabel,
717
811
  tzShort
718
812
  };