@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.
- package/README.md +92 -25
- package/dist/{chunk-3JG5X2LT.js → chunk-3QKGKUTX.js} +14 -3
- package/dist/chunk-3QKGKUTX.js.map +1 -0
- package/dist/{chunk-ILGUJXSB.js → chunk-4COPR4SY.js} +11 -11
- package/dist/chunk-4COPR4SY.js.map +1 -0
- package/dist/{chunk-AOTWFOH4.js → chunk-C4XQSRW7.js} +308 -123
- package/dist/chunk-C4XQSRW7.js.map +1 -0
- package/dist/index.cjs +12 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/{types-CXDiDj_s.d.ts → types-CgWUQ2MH.d.ts} +14 -0
- package/dist/ui/admin/index.d.ts +94 -28
- package/dist/ui/admin/index.js +8 -2
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +9 -3
- package/dist/ui/member/index.d.ts +11 -11
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.d.cts +14 -0
- package/dist/worker/index.d.ts +14 -0
- package/package.json +11 -11
- package/runbooks/adopt-existing.md +13 -4
- package/runbooks/greenfield.md +13 -14
- package/dist/chunk-3JG5X2LT.js.map +0 -1
- package/dist/chunk-AOTWFOH4.js.map +0 -1
- package/dist/chunk-ILGUJXSB.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BrandStyle
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3QKGKUTX.js";
|
|
4
4
|
|
|
5
5
|
// src/ui/admin.tsx
|
|
6
|
-
import { useEffect as useEffect7, useMemo, useState as useState11 } from "
|
|
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 "
|
|
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 "
|
|
24
|
-
import { jsx, jsxs } from "
|
|
25
|
-
var PAGE = {
|
|
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 "
|
|
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: {
|
|
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 "
|
|
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 = "
|
|
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")
|
|
293
|
-
|
|
294
|
-
|
|
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) =>
|
|
298
|
-
|
|
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 = {
|
|
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
|
|
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(
|
|
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__ */
|
|
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
|
-
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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],
|
|
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,
|
|
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,
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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,
|
|
666
|
-
/* @__PURE__ */ jsx7(Field2, { label: "Reply-to", children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo,
|
|
667
|
-
/* @__PURE__ */ jsx7(Field2, { label: "Debug inbox", hint: "Non-prod sends redirect here.", children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail,
|
|
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,
|
|
674
|
-
/* @__PURE__ */ jsx7(Field2, { label: "Body", children: /* @__PURE__ */ jsx7(Textarea, { prose: true, rows: 5, value: t.text,
|
|
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 "
|
|
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 "
|
|
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 "
|
|
753
|
-
import { jsx as jsx9, jsxs as jsxs9 } from "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
960
|
-
|
|
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 {
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
-
|
|
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
|
-
{
|
|
1341
|
+
{
|
|
1342
|
+
id: "dashboard",
|
|
1343
|
+
label: "Dashboard",
|
|
1344
|
+
defaultViewId: "overview",
|
|
1345
|
+
render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { ctx })
|
|
1346
|
+
},
|
|
1212
1347
|
people,
|
|
1213
|
-
{
|
|
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 {
|
|
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__ */
|
|
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
|
-
|
|
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
|
|
1262
|
-
const
|
|
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:
|
|
1268
|
-
email: body.email
|
|
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__ */
|
|
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__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1467
|
+
/* @__PURE__ */ jsx17("button", { className: "btn secondary", onClick: () => signOut(), children: "Sign out" })
|
|
1287
1468
|
] }) });
|
|
1288
1469
|
}
|
|
1289
|
-
return /* @__PURE__ */
|
|
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 ?? "
|
|
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
|
|
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__ */
|
|
1520
|
+
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: "Loading\u2026" }) });
|
|
1342
1521
|
} else if (!publishableKey) {
|
|
1343
|
-
content = /* @__PURE__ */
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1529
|
+
content = /* @__PURE__ */ jsxs13(
|
|
1351
1530
|
ClerkGate,
|
|
1352
1531
|
{
|
|
1353
1532
|
publishableKey,
|
|
1354
1533
|
appearance: clerkAppearanceFromTokens(),
|
|
1355
1534
|
afterSignOutUrl: "/",
|
|
1356
1535
|
children: [
|
|
1357
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
-
|
|
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__ */
|
|
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 "
|
|
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
|
|
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__ */
|
|
1400
|
-
if (error || !data) return /* @__PURE__ */
|
|
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__ */
|
|
1613
|
+
return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsx18(Panel, { title: /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
1432
1614
|
"Agenda ",
|
|
1433
|
-
/* @__PURE__ */
|
|
1615
|
+
/* @__PURE__ */ jsxs14("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
1434
1616
|
"\xB7 ",
|
|
1435
1617
|
data.timezone
|
|
1436
1618
|
] })
|
|
1437
|
-
] }), actions: note ? /* @__PURE__ */
|
|
1438
|
-
/* @__PURE__ */
|
|
1439
|
-
/* @__PURE__ */
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
m.drift && m.drift !== "none" ? /* @__PURE__ */
|
|
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__ */
|
|
1446
|
-
m.status === "scheduled" ? /* @__PURE__ */
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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-
|
|
1666
|
+
//# sourceMappingURL=chunk-C4XQSRW7.js.map
|