@odla-ai/chapter 0.21.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,9 +1,9 @@
1
1
  import {
2
2
  BrandStyle
3
- } from "./chunk-3JG5X2LT.js";
3
+ } from "./chunk-3QKGKUTX.js";
4
4
 
5
5
  // src/ui/admin.tsx
6
- import { useEffect as useEffect7, useMemo, useState as useState11 } from "react";
6
+ import { useEffect as useEffect7, useMemo, useState as useState11 } from "preact/hooks";
7
7
  import {
8
8
  ClerkGate,
9
9
  SignedIn,
@@ -13,16 +13,22 @@ import {
13
13
  clerkAppearanceFromTokens
14
14
  } from "@odla-ai/auth-clerk";
15
15
  import { CrmClient } from "@odla-ai/crm";
16
- import { ThemeScope } from "@odla-ai/ui/components";
17
16
 
18
17
  // src/ui/chrome.tsx
19
- import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
18
+ import { useCallback, useEffect as useEffect2, useState as useState2 } from "preact/hooks";
20
19
  import { Tabs, TopBar, TopBarLink } from "@odla-ai/ui/components";
21
20
 
22
21
  // src/ui/admin-layout.tsx
23
- import { useEffect, useState } from "react";
24
- import { jsx, jsxs } from "react/jsx-runtime";
25
- var PAGE = { maxWidth: 1120, margin: "0 auto", padding: "28px 20px 64px", display: "flex", flexDirection: "column", gap: 20 };
22
+ import { useEffect, useState } from "preact/hooks";
23
+ import { jsx, jsxs } from "preact/jsx-runtime";
24
+ var PAGE = {
25
+ maxWidth: "var(--ui-content-width, 1120px)",
26
+ margin: "0 auto",
27
+ padding: "var(--chapter-admin-page-padding, 28px 20px 64px)",
28
+ display: "flex",
29
+ flexDirection: "column",
30
+ gap: 20
31
+ };
26
32
  var HEAD = { display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 16, flexWrap: "wrap" };
27
33
  function AdminPage({ children }) {
28
34
  return /* @__PURE__ */ jsx("div", { style: PAGE, children });
@@ -78,7 +84,7 @@ function AdminNote({ children }) {
78
84
  }
79
85
 
80
86
  // src/ui/admin-brand.tsx
81
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
87
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
82
88
  var badgeText = (brand) => brand.badge ?? brand.name.slice(0, 3).toUpperCase();
83
89
  var brandDisplay = (brand) => {
84
90
  const display = brand.wordmark ?? brand.name;
@@ -155,7 +161,11 @@ var S = {
155
161
  fontSize: 22,
156
162
  textDecoration: "none"
157
163
  },
158
- workspaceTabs: { maxWidth: 1160, margin: "0 auto", padding: "18px 20px 0" }
164
+ workspaceTabs: {
165
+ maxWidth: "var(--ui-content-width, 1160px)",
166
+ margin: "0 auto",
167
+ padding: "var(--chapter-admin-workspace-padding, 18px 20px 0)"
168
+ }
159
169
  };
160
170
  function Gate(props) {
161
171
  const { brand, tagline, children } = props;
@@ -259,7 +269,7 @@ function adminSectionHref(current, basePath, id, routing = "fragment") {
259
269
  }
260
270
 
261
271
  // src/ui/chrome.tsx
262
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
272
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
263
273
  var mergeRoute = (current, target) => {
264
274
  if (typeof target === "string") return { workspaceId: target };
265
275
  const changingWorkspace = target.workspaceId != null && target.workspaceId !== current.workspaceId;
@@ -284,28 +294,51 @@ function AdminShell({
284
294
  getToken,
285
295
  signOut,
286
296
  email,
297
+ currentUser,
287
298
  routing = "fragment",
288
- chrome = "editorial",
289
- renderHeader
299
+ chrome = "embedded",
300
+ renderHeader,
301
+ renderAccountMenu,
302
+ renderWorkspaceNav
290
303
  }) {
304
+ const normalizeRoute = useCallback((candidate) => {
305
+ if (candidate.viewId) return candidate;
306
+ const workspace = workspaces.find((item) => item.id === candidate.workspaceId);
307
+ return workspace?.defaultViewId ? { ...candidate, viewId: workspace.defaultViewId } : candidate;
308
+ }, [workspaces]);
291
309
  const routeFromLocation = useCallback(() => {
292
- if (typeof window === "undefined") return { workspaceId: workspaces[0]?.id ?? "" };
293
- return adminRouteFromUrl(new URL(window.location.href), basePath, workspaces.map((workspace) => workspace.id));
294
- }, [workspaces, basePath]);
310
+ if (typeof window === "undefined") {
311
+ return normalizeRoute({ workspaceId: workspaces[0]?.id ?? "" });
312
+ }
313
+ return normalizeRoute(
314
+ adminRouteFromUrl(new URL(window.location.href), basePath, workspaces.map((workspace) => workspace.id))
315
+ );
316
+ }, [workspaces, basePath, normalizeRoute]);
295
317
  const [route, setRoute] = useState2(routeFromLocation);
296
318
  const href = useCallback(
297
- (target) => typeof window === "undefined" ? `#${mergeRoute(route, target).workspaceId}` : adminRouteHref(new URL(window.location.href), basePath, mergeRoute(route, target), routing),
298
- [route, basePath, routing]
319
+ (target) => {
320
+ const next = normalizeRoute(mergeRoute(route, target));
321
+ if (typeof window === "undefined") {
322
+ return `#${[
323
+ next.workspaceId,
324
+ next.viewId,
325
+ next.recordId,
326
+ next.detailTab
327
+ ].filter((part) => Boolean(part)).map(encodeURIComponent).join("/")}`;
328
+ }
329
+ return adminRouteHref(new URL(window.location.href), basePath, next, routing);
330
+ },
331
+ [route, basePath, routing, normalizeRoute]
299
332
  );
300
333
  const navigate = useCallback(
301
334
  (target) => {
302
- const next = mergeRoute(route, target);
335
+ const next = normalizeRoute(mergeRoute(route, target));
303
336
  if (typeof window !== "undefined") {
304
337
  window.history.pushState(null, "", adminRouteHref(new URL(window.location.href), basePath, next, routing));
305
338
  }
306
339
  setRoute(next);
307
340
  },
308
- [route, basePath, routing]
341
+ [route, basePath, routing, normalizeRoute]
309
342
  );
310
343
  useEffect2(() => {
311
344
  const sync = () => setRoute(routeFromLocation());
@@ -322,9 +355,22 @@ function AdminShell({
322
355
  const actual = `${window.location.pathname}${window.location.search}${window.location.hash}`;
323
356
  if (canonical !== actual) window.history.replaceState(null, "", canonical);
324
357
  }, []);
325
- const context = { client, getToken, route, navigate, href };
358
+ const context = {
359
+ client,
360
+ getToken,
361
+ ...currentUser ? { currentUser } : {},
362
+ route,
363
+ navigate,
364
+ href
365
+ };
326
366
  const active = workspaces.find((workspace) => workspace.id === route.workspaceId) ?? workspaces[0];
327
- const identity = /* @__PURE__ */ jsxs3(Fragment, { children: [
367
+ const accountProps = {
368
+ brand,
369
+ email,
370
+ ...currentUser ? { currentUser } : {},
371
+ signOut
372
+ };
373
+ const identity = renderAccountMenu?.(accountProps) ?? /* @__PURE__ */ jsxs3(Fragment, { children: [
328
374
  /* @__PURE__ */ jsx3("span", { style: { ...S.role, color: "inherit", borderColor: "currentColor", background: "transparent" }, children: "admin" }),
329
375
  email ? /* @__PURE__ */ jsx3("span", { style: { opacity: 0.72, fontSize: 12 }, children: email }) : null,
330
376
  /* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children: "Sign out" })
@@ -346,7 +392,7 @@ function AdminShell({
346
392
  ] })
347
393
  ] });
348
394
  }
349
- const header = renderHeader ? renderHeader({ brand, email, signOut }) : chrome === "editorial" ? /* @__PURE__ */ jsxs3("header", { style: S.masthead, children: [
395
+ const header = renderHeader ? renderHeader(accountProps) : chrome === "standalone" || chrome === "editorial" ? /* @__PURE__ */ jsxs3("header", { style: S.masthead, children: [
350
396
  /* @__PURE__ */ jsxs3("a", { href: "/", style: S.mastheadBrand, children: [
351
397
  /* @__PURE__ */ jsx3("span", { style: S.badgeSm, children: badgeText(brand) }),
352
398
  brandDisplay(brand)
@@ -359,18 +405,22 @@ function AdminShell({
359
405
  header,
360
406
  /* @__PURE__ */ jsxs3("main", { className: "shell-main", style: S.main, children: [
361
407
  /* @__PURE__ */ jsx3(ThemeWarning, {}),
362
- /* @__PURE__ */ jsx3(
408
+ renderWorkspaceNav ? /* @__PURE__ */ jsxs3(Fragment, { children: [
409
+ /* @__PURE__ */ jsx3("div", { style: S.workspaceTabs, children: renderWorkspaceNav({ workspaces, route, navigate, href }) }),
410
+ active?.render(context)
411
+ ] }) : /* @__PURE__ */ jsx3(
363
412
  Tabs,
364
413
  {
365
414
  style: S.workspaceTabs,
366
415
  ariaLabel: "Admin workspaces",
367
416
  value: route.workspaceId,
368
- onValueChange: (workspaceId) => setRoute({ workspaceId }),
417
+ mount: "active",
418
+ onValueChange: (workspaceId) => setRoute(normalizeRoute({ workspaceId })),
369
419
  items: workspaces.map((workspace) => ({
370
420
  value: workspace.id,
371
421
  label: workspace.label,
372
422
  href: href(workspace.id),
373
- panel: workspace.render(context)
423
+ panel: workspace.id === route.workspaceId ? workspace.render(context) : null
374
424
  }))
375
425
  }
376
426
  )
@@ -382,11 +432,11 @@ function AdminShell({
382
432
  import { Tabs as Tabs2 } from "@odla-ai/ui/components";
383
433
 
384
434
  // src/ui/admin-availability.tsx
385
- import { useState as useState4 } from "react";
435
+ import { useState as useState4 } from "preact/hooks";
386
436
  import { Button, Checkbox, Field, Input } from "@odla-ai/ui/components";
387
437
 
388
438
  // src/ui/admin-api.ts
389
- import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "react";
439
+ import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "preact/hooks";
390
440
  async function adminFetch(getToken, path, init) {
391
441
  const token = await getToken();
392
442
  const headers = new Headers(init?.headers);
@@ -416,7 +466,7 @@ function useAdminResource(getToken, path) {
416
466
  }
417
467
 
418
468
  // src/ui/admin-availability.tsx
419
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
469
+ import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
420
470
  var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
421
471
  function AvailabilityBody({ ctx }) {
422
472
  const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
@@ -456,7 +506,7 @@ function AvailabilityBody({ ctx }) {
456
506
  setBusy(false);
457
507
  }
458
508
  };
459
- const numField = (key, label) => /* @__PURE__ */ jsx4(Field, { label, error: errors[key], children: /* @__PURE__ */ jsx4(Input, { type: "number", value: cfg[key], onChange: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
509
+ const numField = (key, label) => /* @__PURE__ */ jsx4(Field, { label, error: errors[key], children: /* @__PURE__ */ jsx4(Input, { type: "number", value: cfg[key], onInput: (e) => edit({ [key]: num(e.target.value, cfg[key]) }) }) });
460
510
  return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title: "Booking availability", actions: note ? /* @__PURE__ */ jsx4("span", { className: "muted", children: note }) : void 0, children: [
461
511
  /* @__PURE__ */ jsx4(Field, { label: "Days", error: errors.days, children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: DAYS.map((d, i) => /* @__PURE__ */ jsx4(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
462
512
  /* @__PURE__ */ jsxs4("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
@@ -465,9 +515,9 @@ function AvailabilityBody({ ctx }) {
465
515
  numField("slotMinutes", "Slot length (minutes)"),
466
516
  numField("minNoticeHours", "Minimum notice (hours)"),
467
517
  numField("windowDays", "Booking window (days)"),
468
- /* @__PURE__ */ jsx4(Field, { label: "Timezone", error: errors.timezone, children: /* @__PURE__ */ jsx4(Input, { value: cfg.timezone, onChange: (e) => edit({ timezone: e.target.value }) }) })
518
+ /* @__PURE__ */ jsx4(Field, { label: "Timezone", error: errors.timezone, children: /* @__PURE__ */ jsx4(Input, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
469
519
  ] }),
470
- /* @__PURE__ */ jsx4(Field, { label: "Event summary template", error: errors.summaryTemplate, children: /* @__PURE__ */ jsx4(Input, { value: cfg.summaryTemplate, onChange: (e) => edit({ summaryTemplate: e.target.value }) }) }),
520
+ /* @__PURE__ */ jsx4(Field, { label: "Event summary template", error: errors.summaryTemplate, children: /* @__PURE__ */ jsx4(Input, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
471
521
  /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children: "Save availability" }) })
472
522
  ] }) });
473
523
  }
@@ -478,7 +528,7 @@ function availabilitySection(options = {}) {
478
528
 
479
529
  // src/ui/admin-billing.tsx
480
530
  import { DataTable, StatBand } from "@odla-ai/ui/components";
481
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
531
+ import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
482
532
  var money = (cents) => `$${(cents / 100).toLocaleString()}`;
483
533
  var date = (t) => t ? new Date(t).toLocaleDateString([], { dateStyle: "medium" }) : "\u2014";
484
534
  var COLUMNS = [
@@ -520,7 +570,7 @@ function billingSection(options = {}) {
520
570
 
521
571
  // src/ui/admin-dashboard.tsx
522
572
  import { MetricWidget, StepPipeline } from "@odla-ai/ui/components";
523
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
573
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
524
574
  var number = (value) => Math.round(value).toLocaleString();
525
575
  var money2 = (cents) => `$${Math.round(cents / 100).toLocaleString()}`;
526
576
  var when = (time) => new Date(time).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
@@ -604,9 +654,9 @@ function dashboardSection(options = {}) {
604
654
  }
605
655
 
606
656
  // src/ui/admin-email.tsx
607
- import { useState as useState5 } from "react";
657
+ import { useState as useState5 } from "preact/hooks";
608
658
  import { Button as Button2, Checkbox as Checkbox2, DataTable as DataTable2, Field as Field2, Input as Input2, Textarea } from "@odla-ai/ui/components";
609
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
659
+ import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
610
660
  var LOG_COLUMNS = [
611
661
  { key: "sentAt", header: "Sent", cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
612
662
  { key: "template", header: "Template", cell: (r) => r.template },
@@ -662,16 +712,16 @@ function EmailBody({ ctx }) {
662
712
  data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
663
713
  ] }),
664
714
  children: [
665
- /* @__PURE__ */ jsx7(Field2, { label: "Notification address", children: /* @__PURE__ */ jsx7(Input2, { value: data.notificationEmail, onChange: (e) => edit({ notificationEmail: e.target.value }) }) }),
666
- /* @__PURE__ */ jsx7(Field2, { label: "Reply-to", children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo, onChange: (e) => edit({ replyTo: e.target.value }) }) }),
667
- /* @__PURE__ */ jsx7(Field2, { label: "Debug inbox", hint: "Non-prod sends redirect here.", children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail, onChange: (e) => edit({ debugEmail: e.target.value }) }) })
715
+ /* @__PURE__ */ jsx7(Field2, { label: "Notification address", children: /* @__PURE__ */ jsx7(Input2, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
716
+ /* @__PURE__ */ jsx7(Field2, { label: "Reply-to", children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
717
+ /* @__PURE__ */ jsx7(Field2, { label: "Debug inbox", hint: "Non-prod sends redirect here.", children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
668
718
  ]
669
719
  }
670
720
  ),
671
721
  Object.entries(data.emailTemplates).map(([key, t]) => /* @__PURE__ */ jsxs7(Panel, { title: key, actions: /* @__PURE__ */ jsx7(Button2, { variant: "secondary", mini: true, disabled: busy === `test:${key}`, onClick: () => void test(key), children: "Send test" }), children: [
672
722
  /* @__PURE__ */ jsx7(Checkbox2, { label: "Enabled", checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
673
- /* @__PURE__ */ jsx7(Field2, { label: "Subject", children: /* @__PURE__ */ jsx7(Input2, { value: t.subject, onChange: (e) => editTemplate(key, { subject: e.target.value }) }) }),
674
- /* @__PURE__ */ jsx7(Field2, { label: "Body", children: /* @__PURE__ */ jsx7(Textarea, { prose: true, rows: 5, value: t.text, onChange: (e) => editTemplate(key, { text: e.target.value }) }) })
723
+ /* @__PURE__ */ jsx7(Field2, { label: "Subject", children: /* @__PURE__ */ jsx7(Input2, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
724
+ /* @__PURE__ */ jsx7(Field2, { label: "Body", children: /* @__PURE__ */ jsx7(Textarea, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
675
725
  ] }, key)),
676
726
  /* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
677
727
  /* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children: "Save changes" }),
@@ -685,16 +735,13 @@ function emailSection(options = {}) {
685
735
  return { id, label, render: (ctx) => /* @__PURE__ */ jsx7(EmailBody, { ctx }) };
686
736
  }
687
737
 
688
- // src/ui/admin-people.tsx
689
- import { BillingCard } from "@odla-ai/crm/ui";
690
- import {
691
- CrmWorkspace
692
- } from "@odla-ai/crm/ui";
738
+ // src/ui/admin-people-workspace.tsx
739
+ import { BillingCard, CrmWorkspace } from "@odla-ai/crm/ui";
693
740
 
694
741
  // src/ui/admin-record-actions.tsx
695
- import { useEffect as useEffect4, useState as useState6 } from "react";
742
+ import { useEffect as useEffect4, useState as useState6 } from "preact/hooks";
696
743
  import { Button as Button3, Field as Field3, Select } from "@odla-ai/ui/components";
697
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
744
+ import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
698
745
  function RecordActions({ getToken, record, roles, onChanged }) {
699
746
  const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
700
747
  const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
@@ -749,8 +796,8 @@ function RecordActions({ getToken, record, roles, onChanged }) {
749
796
  }
750
797
 
751
798
  // src/ui/admin-network.tsx
752
- import { useState as useState7 } from "react";
753
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
799
+ import { useState as useState7 } from "preact/hooks";
800
+ import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
754
801
  function NetworkShareActions(props) {
755
802
  const { recordId, recordType, getToken } = props;
756
803
  const targets = useAdminResource(getToken, "/api/admin/network/targets");
@@ -785,9 +832,9 @@ function NetworkShareActions(props) {
785
832
  }
786
833
 
787
834
  // src/ui/admin-record-activity.tsx
788
- import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "react";
835
+ import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "preact/hooks";
789
836
  import { ActivityFeed } from "@odla-ai/crm/ui";
790
- import { jsx as jsx10 } from "react/jsx-runtime";
837
+ import { jsx as jsx10 } from "preact/jsx-runtime";
791
838
  function RecordActivityPanel(props) {
792
839
  const { client, recordId } = props;
793
840
  const [activities, setActivities] = useState8([]);
@@ -826,9 +873,9 @@ function RecordActivityPanel(props) {
826
873
  }
827
874
 
828
875
  // src/ui/admin-record-comms.tsx
829
- import { useCallback as useCallback4, useEffect as useEffect6, useState as useState9 } from "react";
876
+ import { useCallback as useCallback4, useEffect as useEffect6, useState as useState9 } from "preact/hooks";
830
877
  import { Button as Button4, Field as Field4, Select as Select2 } from "@odla-ai/ui/components";
831
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
878
+ import { jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
832
879
  function RecordCommsPanel(props) {
833
880
  const { client, recordId, getToken } = props;
834
881
  const config = useAdminResource(getToken, "/api/admin/group/email");
@@ -893,9 +940,9 @@ function RecordCommsHistory(props) {
893
940
  }
894
941
 
895
942
  // src/ui/admin-record-scheduling.tsx
896
- import { useState as useState10 } from "react";
943
+ import { useState as useState10 } from "preact/hooks";
897
944
  import { Button as Button5 } from "@odla-ai/ui/components";
898
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
945
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "preact/jsx-runtime";
899
946
  function RecordSchedulingPanel(props) {
900
947
  const { getToken, applicationId } = props;
901
948
  const resource = useAdminResource(
@@ -955,13 +1002,51 @@ function RecordSchedulingPanel(props) {
955
1002
  ] });
956
1003
  }
957
1004
 
958
- // src/ui/admin-people.tsx
959
- import { jsx as jsx13 } from "react/jsx-runtime";
960
- var DEFAULT_ROLES = ["provisional", "member", "admin"];
1005
+ // src/ui/admin-people-workspace.tsx
1006
+ import { jsx as jsx13 } from "preact/jsx-runtime";
1007
+ function applicationLifecycleAdapter(ctx) {
1008
+ return {
1009
+ transitionStage: async ({ record, to }) => {
1010
+ const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
1011
+ if (!applicationId) {
1012
+ throw new Error("This record is not linked to an application workflow.");
1013
+ }
1014
+ const path = `/api/admin/applications/${encodeURIComponent(applicationId)}`;
1015
+ if (to === "approved") {
1016
+ await adminFetch(ctx.getToken, `${path}/approve`, { method: "POST" });
1017
+ } else if (to === "refunded") {
1018
+ await adminFetch(ctx.getToken, `${path}/refund`, { method: "POST" });
1019
+ } else {
1020
+ await adminFetch(ctx.getToken, path, {
1021
+ method: "PATCH",
1022
+ body: JSON.stringify({ status: to })
1023
+ });
1024
+ }
1025
+ }
1026
+ };
1027
+ }
961
1028
  function PeopleBody(props) {
962
- const { crm, client, type, ctx, roles, lifecycle, networkSharing, extendRecordTabs } = props;
1029
+ const {
1030
+ crm,
1031
+ client,
1032
+ type,
1033
+ ctx,
1034
+ roles,
1035
+ lifecycle,
1036
+ networkSharing,
1037
+ lifecycleAdapter,
1038
+ requireLifecycleAdapter,
1039
+ renderSummary,
1040
+ renderMaster,
1041
+ renderDetailHeader,
1042
+ renderEmptyDetail,
1043
+ hrefForRecord,
1044
+ extendRecordTabs
1045
+ } = props;
963
1046
  const def = crm.type(type);
964
1047
  const recordId = ctx.route?.recordId ?? null;
1048
+ const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx) : void 0;
1049
+ const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
965
1050
  const workspaceClient = client;
966
1051
  return /* @__PURE__ */ jsx13(AdminPage, { children: /* @__PURE__ */ jsx13(
967
1052
  CrmWorkspace,
@@ -973,7 +1058,15 @@ function PeopleBody(props) {
973
1058
  detailTab: ctx.route?.detailTab,
974
1059
  defaultDetailTab: "profile",
975
1060
  onRecordIdChange: (id) => ctx.navigate({ recordId: id, detailTab: null }),
1061
+ hrefForRecord: (record) => hrefForRecord?.(record, ctx) ?? ctx.href({ recordId: record.id, detailTab: "profile" }),
1062
+ hrefForList: ctx.href({ recordId: null, detailTab: null }),
976
1063
  hrefForDetailTab: (detailTab) => ctx.href({ detailTab }),
1064
+ lifecycle: resolvedLifecycle,
1065
+ requireLifecycleAdapter: requireLifecycleAdapter ?? lifecycle,
1066
+ renderSummary: renderSummary ? (context) => renderSummary(context, ctx) : void 0,
1067
+ renderMaster: renderMaster ? (context) => renderMaster(context, ctx) : void 0,
1068
+ renderDetailHeader: renderDetailHeader ? (context) => renderDetailHeader(context, ctx) : void 0,
1069
+ renderEmptyDetail: renderEmptyDetail ? (context) => renderEmptyDetail(context, ctx) : void 0,
977
1070
  extendRecordTabs: (base, context) => {
978
1071
  const record = context.detail.record;
979
1072
  const applicationId = typeof record.fields?.applicationId === "string" ? String(record.fields.applicationId) : null;
@@ -1028,7 +1121,8 @@ function PeopleBody(props) {
1028
1121
  context.refreshList();
1029
1122
  }
1030
1123
  }
1031
- )
1124
+ ),
1125
+ visible: () => ctx.currentUser?.superAdmin === true
1032
1126
  });
1033
1127
  }
1034
1128
  if (networkSharing) {
@@ -1043,6 +1137,10 @@ function PeopleBody(props) {
1043
1137
  }
1044
1138
  ) });
1045
1139
  }
1140
+
1141
+ // src/ui/admin-people.tsx
1142
+ import { jsx as jsx14 } from "preact/jsx-runtime";
1143
+ var DEFAULT_ROLES = ["provisional", "member", "admin"];
1046
1144
  function collectionSection(options) {
1047
1145
  const { crm, type } = options;
1048
1146
  let fallbackLabel = type;
@@ -1056,12 +1154,19 @@ function collectionSection(options) {
1056
1154
  lifecycle = type === "person",
1057
1155
  roles = DEFAULT_ROLES,
1058
1156
  networkSharing = false,
1157
+ lifecycleAdapter,
1158
+ requireLifecycleAdapter,
1159
+ renderSummary,
1160
+ renderMaster,
1161
+ renderDetailHeader,
1162
+ renderEmptyDetail,
1163
+ hrefForRecord,
1059
1164
  extendRecordTabs
1060
1165
  } = options;
1061
1166
  return {
1062
1167
  id,
1063
1168
  label,
1064
- render: (ctx) => /* @__PURE__ */ jsx13(
1169
+ render: (ctx) => /* @__PURE__ */ jsx14(
1065
1170
  PeopleBody,
1066
1171
  {
1067
1172
  crm,
@@ -1071,6 +1176,13 @@ function collectionSection(options) {
1071
1176
  roles,
1072
1177
  lifecycle,
1073
1178
  networkSharing,
1179
+ lifecycleAdapter,
1180
+ requireLifecycleAdapter,
1181
+ renderSummary,
1182
+ renderMaster,
1183
+ renderDetailHeader,
1184
+ renderEmptyDetail,
1185
+ hrefForRecord,
1074
1186
  extendRecordTabs
1075
1187
  }
1076
1188
  )
@@ -1084,6 +1196,13 @@ function peopleSection(options) {
1084
1196
  type = "person",
1085
1197
  roles,
1086
1198
  networkSharing,
1199
+ lifecycleAdapter,
1200
+ requireLifecycleAdapter,
1201
+ renderSummary,
1202
+ renderMaster,
1203
+ renderDetailHeader,
1204
+ renderEmptyDetail,
1205
+ hrefForRecord,
1087
1206
  extendRecordTabs
1088
1207
  } = options;
1089
1208
  return collectionSection({
@@ -1093,12 +1212,19 @@ function peopleSection(options) {
1093
1212
  label,
1094
1213
  ...roles ? { roles } : {},
1095
1214
  ...networkSharing != null ? { networkSharing } : {},
1215
+ ...lifecycleAdapter ? { lifecycleAdapter } : {},
1216
+ ...requireLifecycleAdapter != null ? { requireLifecycleAdapter } : {},
1217
+ ...renderSummary ? { renderSummary } : {},
1218
+ ...renderMaster ? { renderMaster } : {},
1219
+ ...renderDetailHeader ? { renderDetailHeader } : {},
1220
+ ...renderEmptyDetail ? { renderEmptyDetail } : {},
1221
+ ...hrefForRecord ? { hrefForRecord } : {},
1096
1222
  ...extendRecordTabs ? { extendRecordTabs } : {}
1097
1223
  });
1098
1224
  }
1099
1225
 
1100
1226
  // src/ui/admin-workspaces.tsx
1101
- import { jsx as jsx14 } from "react/jsx-runtime";
1227
+ import { jsx as jsx15 } from "preact/jsx-runtime";
1102
1228
  var adminViewContext = (ctx, viewId, active) => {
1103
1229
  const route = {
1104
1230
  workspaceId: ctx.route.workspaceId,
@@ -1123,11 +1249,12 @@ function DashboardWorkspace({ ctx }) {
1123
1249
  const active = ctx.route.viewId === "billing" ? "billing" : "overview";
1124
1250
  const overview = dashboardSection({ id: "overview", label: "Overview" });
1125
1251
  const billing = billingSection();
1126
- return /* @__PURE__ */ jsx14(
1252
+ return /* @__PURE__ */ jsx15(
1127
1253
  Tabs2,
1128
1254
  {
1129
1255
  ariaLabel: "Dashboard views",
1130
1256
  value: active,
1257
+ mount: "active",
1131
1258
  items: [
1132
1259
  {
1133
1260
  value: "overview",
@@ -1149,11 +1276,12 @@ function SettingsWorkspace({ ctx }) {
1149
1276
  const active = ctx.route.viewId === "email" ? "email" : "calendar";
1150
1277
  const calendar = availabilitySection({ id: "calendar", label: "Calendar" });
1151
1278
  const email = emailSection();
1152
- return /* @__PURE__ */ jsx14(
1279
+ return /* @__PURE__ */ jsx15(
1153
1280
  Tabs2,
1154
1281
  {
1155
1282
  ariaLabel: "Settings views",
1156
1283
  value: active,
1284
+ mount: "active",
1157
1285
  items: [
1158
1286
  {
1159
1287
  value: "calendar",
@@ -1176,12 +1304,13 @@ function PeopleWorkspace({ chapter, ctx }) {
1176
1304
  const fallback = types[0]?.[0] ?? "person";
1177
1305
  const active = types.some(([type]) => type === ctx.route.viewId) ? ctx.route.viewId : fallback;
1178
1306
  const networkSharing = chapter.network.targets.length > 0;
1179
- return /* @__PURE__ */ jsx14(
1307
+ return /* @__PURE__ */ jsx15(
1180
1308
  Tabs2,
1181
1309
  {
1182
1310
  ariaLabel: "CRM collections",
1183
1311
  value: active,
1184
1312
  variant: "pill",
1313
+ mount: "active",
1185
1314
  items: types.map(([type, def]) => {
1186
1315
  const section = collectionSection({
1187
1316
  crm: chapter.crm,
@@ -1204,18 +1333,30 @@ function defaultAdminWorkspaces(chapter) {
1204
1333
  const people = {
1205
1334
  id: "people",
1206
1335
  label: "People",
1207
- render: (ctx) => /* @__PURE__ */ jsx14(PeopleWorkspace, { chapter, ctx })
1336
+ defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
1337
+ render: (ctx) => /* @__PURE__ */ jsx15(PeopleWorkspace, { chapter, ctx })
1208
1338
  };
1209
1339
  if (chapter.mode === "hub") return [people];
1210
1340
  return [
1211
- { id: "dashboard", label: "Dashboard", render: (ctx) => /* @__PURE__ */ jsx14(DashboardWorkspace, { ctx }) },
1341
+ {
1342
+ id: "dashboard",
1343
+ label: "Dashboard",
1344
+ defaultViewId: "overview",
1345
+ render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { ctx })
1346
+ },
1212
1347
  people,
1213
- { id: "settings", label: "Settings", render: (ctx) => /* @__PURE__ */ jsx14(SettingsWorkspace, { ctx }) }
1348
+ {
1349
+ id: "settings",
1350
+ label: "Settings",
1351
+ defaultViewId: "calendar",
1352
+ render: (ctx) => /* @__PURE__ */ jsx15(SettingsWorkspace, { ctx })
1353
+ }
1214
1354
  ];
1215
1355
  }
1216
1356
 
1217
- // src/ui/admin.tsx
1218
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1357
+ // src/ui/admin-frame.tsx
1358
+ import { ThemeScope } from "@odla-ai/ui/components";
1359
+ import { jsx as jsx16, jsxs as jsxs12 } from "preact/jsx-runtime";
1219
1360
  function AdminTheme(props) {
1220
1361
  const brand = props.chapter?.brand;
1221
1362
  return /* @__PURE__ */ jsxs12(
@@ -1225,12 +1366,51 @@ function AdminTheme(props) {
1225
1366
  theme: brand?.theme,
1226
1367
  colorScheme: brand?.colorScheme ?? "light",
1227
1368
  children: [
1228
- /* @__PURE__ */ jsx15(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
1369
+ /* @__PURE__ */ jsx16(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
1229
1370
  props.children
1230
1371
  ]
1231
1372
  }
1232
1373
  );
1233
1374
  }
1375
+ function signInRedirect(basePath, workspaces) {
1376
+ if (typeof window === "undefined") return basePath;
1377
+ const current = new URL(window.location.href);
1378
+ const inbound = adminRouteFromUrl(
1379
+ current,
1380
+ basePath,
1381
+ workspaces.map((workspace2) => workspace2.id)
1382
+ );
1383
+ const workspace = workspaces.find((item) => item.id === inbound.workspaceId);
1384
+ const route = inbound.viewId || !workspace?.defaultViewId ? inbound : { ...inbound, viewId: workspace.defaultViewId };
1385
+ return adminRouteHref(current, basePath, route, "query");
1386
+ }
1387
+
1388
+ // src/ui/admin-auth.ts
1389
+ var apiUrl = (apiBase, path) => `${apiBase.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
1390
+ var DEFAULT_ADMIN_AUTH = {};
1391
+ async function loadChapterAdminConfig(adapter, apiBase) {
1392
+ if (adapter.loadConfig) return adapter.loadConfig({ apiBase });
1393
+ const response = await fetch(apiUrl(apiBase, adapter.configPath ?? "/api/config"));
1394
+ const body = await response.json();
1395
+ if (adapter.mapConfig) return adapter.mapConfig(body);
1396
+ return {
1397
+ publishableKey: typeof body.clerkPublishableKey === "string" ? body.clerkPublishableKey : null
1398
+ };
1399
+ }
1400
+ async function loadChapterAdminUser(adapter, apiBase, getToken) {
1401
+ if (adapter.loadCurrentUser) {
1402
+ return adapter.loadCurrentUser({ apiBase, getToken });
1403
+ }
1404
+ const token = await getToken();
1405
+ const response = await fetch(apiUrl(apiBase, adapter.currentUserPath ?? "/api/me"), {
1406
+ headers: token ? { authorization: `Bearer ${token}` } : {}
1407
+ });
1408
+ const body = await response.json().catch(() => ({}));
1409
+ return adapter.mapCurrentUser?.(body) ?? body;
1410
+ }
1411
+
1412
+ // src/ui/admin.tsx
1413
+ import { jsx as jsx17, jsxs as jsxs13 } from "preact/jsx-runtime";
1234
1414
  function Authed(props) {
1235
1415
  const {
1236
1416
  workspaces,
@@ -1240,7 +1420,10 @@ function Authed(props) {
1240
1420
  apiBase,
1241
1421
  routing,
1242
1422
  chrome,
1243
- renderHeader
1423
+ auth,
1424
+ renderHeader,
1425
+ renderAccountMenu,
1426
+ renderWorkspaceNav
1244
1427
  } = props;
1245
1428
  const { getToken, signOut } = useClerkAuth();
1246
1429
  const client = useMemo(
@@ -1258,14 +1441,12 @@ function Authed(props) {
1258
1441
  let live = true;
1259
1442
  void (async () => {
1260
1443
  try {
1261
- const token = await getToken();
1262
- const response = await fetch(`${apiBase}/api/me`, {
1263
- headers: token ? { authorization: `Bearer ${token}` } : {}
1264
- });
1265
- const body = await response.json().catch(() => ({}));
1444
+ const body = await loadChapterAdminUser(auth, apiBase, getToken);
1445
+ const authorized = auth.isAuthorized ? auth.isAuthorized(body) : body.authorized === true;
1266
1446
  if (live) setState({
1267
- status: body.authorized ? "ok" : "denied",
1268
- email: body.email ?? null
1447
+ status: authorized ? "ok" : "denied",
1448
+ email: typeof body.email === "string" ? body.email : null,
1449
+ user: body
1269
1450
  });
1270
1451
  } catch {
1271
1452
  if (live) setState({ status: "denied" });
@@ -1274,19 +1455,19 @@ function Authed(props) {
1274
1455
  return () => {
1275
1456
  live = false;
1276
1457
  };
1277
- }, [getToken, apiBase]);
1278
- if (state.status === "checking") return /* @__PURE__ */ jsx15(Gate, { brand, children: /* @__PURE__ */ jsx15("p", { style: S.muted, children: "Checking access\u2026" }) });
1458
+ }, [getToken, apiBase, auth]);
1459
+ if (state.status === "checking") return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: "Checking access\u2026" }) });
1279
1460
  if (state.status === "denied") {
1280
- return /* @__PURE__ */ jsx15(Gate, { brand, children: /* @__PURE__ */ jsxs12("div", { className: "card", style: S.card, children: [
1281
- /* @__PURE__ */ jsx15("h2", { style: { marginTop: 0 }, children: "Not authorized" }),
1282
- /* @__PURE__ */ jsxs12("p", { style: S.muted, children: [
1461
+ return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsxs13("div", { className: "card", style: S.card, children: [
1462
+ /* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: "Not authorized" }),
1463
+ /* @__PURE__ */ jsxs13("p", { style: S.muted, children: [
1283
1464
  state.email ? `${state.email} isn't` : "This account isn't",
1284
1465
  " on the admin list."
1285
1466
  ] }),
1286
- /* @__PURE__ */ jsx15("button", { className: "btn secondary", onClick: () => signOut(), children: "Sign out" })
1467
+ /* @__PURE__ */ jsx17("button", { className: "btn secondary", onClick: () => signOut(), children: "Sign out" })
1287
1468
  ] }) });
1288
1469
  }
1289
- return /* @__PURE__ */ jsx15(
1470
+ return /* @__PURE__ */ jsx17(
1290
1471
  AdminShell,
1291
1472
  {
1292
1473
  workspaces,
@@ -1296,18 +1477,15 @@ function Authed(props) {
1296
1477
  getToken,
1297
1478
  signOut,
1298
1479
  email: state.email ?? null,
1480
+ currentUser: state.user,
1299
1481
  routing,
1300
1482
  chrome,
1301
- renderHeader
1483
+ renderHeader,
1484
+ renderAccountMenu,
1485
+ renderWorkspaceNav
1302
1486
  }
1303
1487
  );
1304
1488
  }
1305
- var signInRedirect = (basePath, workspaces) => {
1306
- if (typeof window === "undefined") return basePath;
1307
- const current = new URL(window.location.href);
1308
- const route = adminRouteFromUrl(current, basePath, workspaces.map((workspace) => workspace.id));
1309
- return adminRouteHref(current, basePath, route, "query");
1310
- };
1311
1489
  function ChapterAdmin(props) {
1312
1490
  const defaults = useMemo(
1313
1491
  () => props.chapter ? defaultAdminWorkspaces(props.chapter) : [],
@@ -1326,35 +1504,36 @@ function ChapterAdmin(props) {
1326
1504
  const crmBasePath = props.crmBasePath ?? "/api/crm";
1327
1505
  const apiBase = props.apiBase ?? "";
1328
1506
  const routing = props.routing ?? "fragment";
1329
- const chrome = props.chrome ?? "editorial";
1507
+ const chrome = props.chrome ?? "embedded";
1508
+ const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
1330
1509
  const redirect = signInRedirect(basePath, workspaces);
1331
1510
  const [publishableKey, setPublishableKey] = useState11(void 0);
1332
1511
  useEffect7(() => {
1333
1512
  let live = true;
1334
- void fetch(`${apiBase}/api/config`).then((response) => response.json()).then((body) => live && setPublishableKey(body.clerkPublishableKey ?? null)).catch(() => live && setPublishableKey(null));
1513
+ void loadChapterAdminConfig(auth, apiBase).then((config) => live && setPublishableKey(config.publishableKey ?? null)).catch(() => live && setPublishableKey(null));
1335
1514
  return () => {
1336
1515
  live = false;
1337
1516
  };
1338
- }, [apiBase]);
1517
+ }, [apiBase, auth]);
1339
1518
  let content;
1340
1519
  if (publishableKey === void 0) {
1341
- content = /* @__PURE__ */ jsx15(Gate, { brand, children: /* @__PURE__ */ jsx15("p", { style: S.muted, children: "Loading\u2026" }) });
1520
+ content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: "Loading\u2026" }) });
1342
1521
  } else if (!publishableKey) {
1343
- content = /* @__PURE__ */ jsx15(Gate, { brand, children: /* @__PURE__ */ jsxs12("div", { className: "card", style: S.card, children: [
1344
- /* @__PURE__ */ jsx15("h2", { style: { marginTop: 0 }, children: "Sign-in not configured" }),
1345
- /* @__PURE__ */ jsx15("p", { style: S.muted, children: "No Clerk publishable key is set for this environment yet." })
1522
+ content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsxs13("div", { className: "card", style: S.card, children: [
1523
+ /* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: "Sign-in not configured" }),
1524
+ /* @__PURE__ */ jsx17("p", { style: S.muted, children: "No Clerk publishable key is set for this environment yet." })
1346
1525
  ] }) });
1347
1526
  } else if (workspaces.length === 0) {
1348
- content = /* @__PURE__ */ jsx15(Gate, { brand, children: /* @__PURE__ */ jsx15("p", { style: S.muted, children: "No admin workspaces are configured." }) });
1527
+ content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: "No admin workspaces are configured." }) });
1349
1528
  } else {
1350
- content = /* @__PURE__ */ jsxs12(
1529
+ content = /* @__PURE__ */ jsxs13(
1351
1530
  ClerkGate,
1352
1531
  {
1353
1532
  publishableKey,
1354
1533
  appearance: clerkAppearanceFromTokens(),
1355
1534
  afterSignOutUrl: "/",
1356
1535
  children: [
1357
- /* @__PURE__ */ jsx15(SignedOut, { children: /* @__PURE__ */ jsx15(Gate, { brand, tagline: "Admin sign-in \u2014 invite only", children: /* @__PURE__ */ jsx15(
1536
+ /* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline: "Admin sign-in \u2014 invite only", children: /* @__PURE__ */ jsx17(
1358
1537
  SignIn,
1359
1538
  {
1360
1539
  routing: "hash",
@@ -1362,7 +1541,7 @@ function ChapterAdmin(props) {
1362
1541
  signUpForceRedirectUrl: redirect
1363
1542
  }
1364
1543
  ) }) }),
1365
- /* @__PURE__ */ jsx15(SignedIn, { children: /* @__PURE__ */ jsx15(
1544
+ /* @__PURE__ */ jsx17(SignedIn, { children: /* @__PURE__ */ jsx17(
1366
1545
  Authed,
1367
1546
  {
1368
1547
  workspaces,
@@ -1372,14 +1551,17 @@ function ChapterAdmin(props) {
1372
1551
  apiBase,
1373
1552
  routing,
1374
1553
  chrome,
1375
- renderHeader: props.renderHeader
1554
+ auth,
1555
+ renderHeader: props.renderHeader,
1556
+ renderAccountMenu: props.renderAccountMenu,
1557
+ renderWorkspaceNav: props.renderWorkspaceNav
1376
1558
  }
1377
1559
  ) })
1378
1560
  ]
1379
1561
  }
1380
1562
  );
1381
1563
  }
1382
- return /* @__PURE__ */ jsx15(AdminTheme, { chapter: props.chapter, children: content });
1564
+ return /* @__PURE__ */ jsx17(AdminTheme, { chapter: props.chapter, children: content });
1383
1565
  }
1384
1566
 
1385
1567
  // src/ui/admin-defaults.ts
@@ -1388,16 +1570,16 @@ function defaultAdminSections(chapter) {
1388
1570
  }
1389
1571
 
1390
1572
  // src/ui/admin-meetings.tsx
1391
- import { useState as useState12 } from "react";
1573
+ import { useState as useState12 } from "preact/hooks";
1392
1574
  import { Button as Button6 } from "@odla-ai/ui/components";
1393
- import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1575
+ import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs14 } from "preact/jsx-runtime";
1394
1576
  var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
1395
1577
  function MeetingsBody({ ctx }) {
1396
1578
  const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
1397
1579
  const [busy, setBusy] = useState12(null);
1398
1580
  const [note, setNote] = useState12(null);
1399
- if (loading) return /* @__PURE__ */ jsx16(AdminNote, { children: "Loading\u2026" });
1400
- if (error || !data) return /* @__PURE__ */ jsxs13(AdminNote, { children: [
1581
+ if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children: "Loading\u2026" });
1582
+ if (error || !data) return /* @__PURE__ */ jsxs14(AdminNote, { children: [
1401
1583
  "Couldn\u2019t load meetings",
1402
1584
  error ? `: ${error}` : "",
1403
1585
  "."
@@ -1428,30 +1610,30 @@ function MeetingsBody({ ctx }) {
1428
1610
  }
1429
1611
  void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
1430
1612
  };
1431
- return /* @__PURE__ */ jsx16(AdminPage, { children: /* @__PURE__ */ jsx16(Panel, { title: /* @__PURE__ */ jsxs13(Fragment4, { children: [
1613
+ return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsx18(Panel, { title: /* @__PURE__ */ jsxs14(Fragment4, { children: [
1432
1614
  "Agenda ",
1433
- /* @__PURE__ */ jsxs13("span", { className: "muted", style: { fontWeight: 400 }, children: [
1615
+ /* @__PURE__ */ jsxs14("span", { className: "muted", style: { fontWeight: 400 }, children: [
1434
1616
  "\xB7 ",
1435
1617
  data.timezone
1436
1618
  ] })
1437
- ] }), actions: note ? /* @__PURE__ */ jsx16("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx16("p", { className: "muted", style: { margin: 0 }, children: "No meetings." }) : /* @__PURE__ */ jsx16("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs13("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
1438
- /* @__PURE__ */ jsx16("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
1439
- /* @__PURE__ */ jsx16("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : "(unknown)" }),
1440
- /* @__PURE__ */ jsx16("span", { className: "badge", children: m.status }),
1441
- m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs13("span", { className: "badge", children: [
1619
+ ] }), actions: note ? /* @__PURE__ */ jsx18("span", { className: "muted", children: note }) : void 0, children: data.meetings.length === 0 ? /* @__PURE__ */ jsx18("p", { className: "muted", style: { margin: 0 }, children: "No meetings." }) : /* @__PURE__ */ jsx18("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 12 }, children: data.meetings.map((m) => /* @__PURE__ */ jsxs14("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap", opacity: m.status === "cancelled" ? 0.55 : 1 }, children: [
1620
+ /* @__PURE__ */ jsx18("strong", { style: { minWidth: 170 }, children: when2(m.startAt) }),
1621
+ /* @__PURE__ */ jsx18("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : "(unknown)" }),
1622
+ /* @__PURE__ */ jsx18("span", { className: "badge", children: m.status }),
1623
+ m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs14("span", { className: "badge", children: [
1442
1624
  "drift: ",
1443
1625
  m.drift
1444
1626
  ] }) : null,
1445
- m.meetUrl ? /* @__PURE__ */ jsx16("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: "Meet" }) : null,
1446
- m.status === "scheduled" ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
1447
- /* @__PURE__ */ jsx16(Button6, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: "Reschedule" }),
1448
- /* @__PURE__ */ jsx16(Button6, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: "Cancel" })
1627
+ m.meetUrl ? /* @__PURE__ */ jsx18("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: "Meet" }) : null,
1628
+ m.status === "scheduled" ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
1629
+ /* @__PURE__ */ jsx18(Button6, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: "Reschedule" }),
1630
+ /* @__PURE__ */ jsx18(Button6, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: "Cancel" })
1449
1631
  ] }) : null
1450
1632
  ] }, m.id)) }) }) });
1451
1633
  }
1452
1634
  function meetingsSection(options = {}) {
1453
1635
  const { id = "meetings", label = "Meetings" } = options;
1454
- return { id, label, render: (ctx) => /* @__PURE__ */ jsx16(MeetingsBody, { ctx }) };
1636
+ return { id, label, render: (ctx) => /* @__PURE__ */ jsx18(MeetingsBody, { ctx }) };
1455
1637
  }
1456
1638
 
1457
1639
  export {
@@ -1471,11 +1653,14 @@ export {
1471
1653
  emailSection,
1472
1654
  RecordActions,
1473
1655
  NetworkShareActions,
1656
+ applicationLifecycleAdapter,
1474
1657
  collectionSection,
1475
1658
  peopleSection,
1476
1659
  defaultAdminWorkspaces,
1660
+ loadChapterAdminConfig,
1661
+ loadChapterAdminUser,
1477
1662
  ChapterAdmin,
1478
1663
  defaultAdminSections,
1479
1664
  meetingsSection
1480
1665
  };
1481
- //# sourceMappingURL=chunk-AOTWFOH4.js.map
1666
+ //# sourceMappingURL=chunk-C4XQSRW7.js.map