@odla-ai/chapter 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -41
- package/dist/chunk-NTZSXAFT.js +411 -0
- package/dist/chunk-NTZSXAFT.js.map +1 -0
- package/dist/{chunk-ILGUJXSB.js → chunk-OYXNI6VR.js} +275 -93
- package/dist/chunk-OYXNI6VR.js.map +1 -0
- package/dist/{chunk-IWW5VAAC.js → chunk-VCC4S6JU.js} +230 -202
- package/dist/chunk-VCC4S6JU.js.map +1 -0
- package/dist/{types-CgWUQ2MH.d.ts → copy-context-pDogTR4x.d.ts} +78 -20
- package/dist/index.cjs +322 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -24
- package/dist/index.d.ts +90 -24
- package/dist/index.js +322 -3
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +31 -28
- package/dist/ui/admin/index.js +8 -3
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +11 -5
- package/dist/ui/member/index.d.ts +128 -12
- package/dist/ui/member/index.js +10 -4
- package/dist/worker/index.cjs +180 -130
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +67 -19
- package/dist/worker/index.d.ts +67 -19
- package/dist/worker/index.js +180 -130
- package/dist/worker/index.js.map +1 -1
- package/package.json +11 -11
- package/runbooks/adopt-existing.md +23 -5
- package/runbooks/greenfield.md +95 -15
- package/dist/chunk-ILGUJXSB.js.map +0 -1
- package/dist/chunk-IWW5VAAC.js.map +0 -1
- package/dist/chunk-VZPBTTO3.js +0 -91
- package/dist/chunk-VZPBTTO3.js.map +0 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BrandStyle
|
|
3
|
-
|
|
2
|
+
BrandStyle,
|
|
3
|
+
ChapterCopyProvider,
|
|
4
|
+
DEFAULT_CHAPTER_COPY,
|
|
5
|
+
formatChapterCopy,
|
|
6
|
+
useChapterCopy
|
|
7
|
+
} from "./chunk-NTZSXAFT.js";
|
|
4
8
|
|
|
5
9
|
// src/ui/admin.tsx
|
|
6
|
-
import { useEffect as useEffect7, useMemo, useState as useState11 } from "
|
|
10
|
+
import { useEffect as useEffect7, useMemo, useState as useState11 } from "preact/hooks";
|
|
7
11
|
import {
|
|
8
12
|
ClerkGate,
|
|
9
13
|
SignedIn,
|
|
@@ -15,12 +19,12 @@ import {
|
|
|
15
19
|
import { CrmClient } from "@odla-ai/crm";
|
|
16
20
|
|
|
17
21
|
// src/ui/chrome.tsx
|
|
18
|
-
import { useCallback, useEffect as useEffect2, useState as useState2 } from "
|
|
19
|
-
import { Tabs, TopBar, TopBarLink } from "@odla-ai/ui/components";
|
|
22
|
+
import { useCallback, useEffect as useEffect2, useState as useState2 } from "preact/hooks";
|
|
23
|
+
import { Tabs, ThemeScope, TopBar, TopBarLink } from "@odla-ai/ui/components";
|
|
20
24
|
|
|
21
25
|
// src/ui/admin-layout.tsx
|
|
22
|
-
import { useEffect, useState } from "
|
|
23
|
-
import { jsx, jsxs } from "
|
|
26
|
+
import { useEffect, useState } from "preact/hooks";
|
|
27
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
24
28
|
var PAGE = {
|
|
25
29
|
maxWidth: "var(--ui-content-width, 1120px)",
|
|
26
30
|
margin: "0 auto",
|
|
@@ -84,7 +88,7 @@ function AdminNote({ children }) {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
// src/ui/admin-brand.tsx
|
|
87
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "
|
|
91
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
88
92
|
var badgeText = (brand) => brand.badge ?? brand.name.slice(0, 3).toUpperCase();
|
|
89
93
|
var brandDisplay = (brand) => {
|
|
90
94
|
const display = brand.wordmark ?? brand.name;
|
|
@@ -269,7 +273,7 @@ function adminSectionHref(current, basePath, id, routing = "fragment") {
|
|
|
269
273
|
}
|
|
270
274
|
|
|
271
275
|
// src/ui/chrome.tsx
|
|
272
|
-
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "
|
|
276
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
273
277
|
var mergeRoute = (current, target) => {
|
|
274
278
|
if (typeof target === "string") return { workspaceId: target };
|
|
275
279
|
const changingWorkspace = target.workspaceId != null && target.workspaceId !== current.workspaceId;
|
|
@@ -301,6 +305,7 @@ function AdminShell({
|
|
|
301
305
|
renderAccountMenu,
|
|
302
306
|
renderWorkspaceNav
|
|
303
307
|
}) {
|
|
308
|
+
const copy = useChapterCopy().admin;
|
|
304
309
|
const normalizeRoute = useCallback((candidate) => {
|
|
305
310
|
if (candidate.viewId) return candidate;
|
|
306
311
|
const workspace = workspaces.find((item) => item.id === candidate.workspaceId);
|
|
@@ -371,10 +376,12 @@ function AdminShell({
|
|
|
371
376
|
signOut
|
|
372
377
|
};
|
|
373
378
|
const identity = renderAccountMenu?.(accountProps) ?? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
374
|
-
/* @__PURE__ */ jsx3("span", { style: { ...S.role, color: "inherit", borderColor: "currentColor", background: "transparent" }, children:
|
|
379
|
+
/* @__PURE__ */ jsx3("span", { style: { ...S.role, color: "inherit", borderColor: "currentColor", background: "transparent" }, children: copy.shell.adminRole }),
|
|
375
380
|
email ? /* @__PURE__ */ jsx3("span", { style: { opacity: 0.72, fontSize: 12 }, children: email }) : null,
|
|
376
|
-
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children:
|
|
381
|
+
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children: copy.auth.signOut })
|
|
377
382
|
] });
|
|
383
|
+
const activeContent = active?.render(context);
|
|
384
|
+
const workspaceContent = active?.accent ? /* @__PURE__ */ jsx3(ThemeScope, { accent: active.accent, children: activeContent }) : activeContent;
|
|
378
385
|
if (chrome === "topbar") {
|
|
379
386
|
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
380
387
|
/* @__PURE__ */ jsxs3(TopBar, { children: [
|
|
@@ -382,13 +389,13 @@ function AdminShell({
|
|
|
382
389
|
/* @__PURE__ */ jsx3("span", { style: S.badgeSm, children: badgeText(brand) }),
|
|
383
390
|
brandDisplay(brand)
|
|
384
391
|
] }),
|
|
385
|
-
/* @__PURE__ */ jsx3("nav", { className: "topbar-nav", "aria-label":
|
|
392
|
+
/* @__PURE__ */ jsx3("nav", { className: "topbar-nav", "aria-label": copy.shell.navigationLabel, children: workspaces.map((workspace) => /* @__PURE__ */ jsx3(TopBarLink, { active: workspace.id === route.workspaceId, onClick: () => navigate(workspace.id), children: workspace.label }, workspace.id)) }),
|
|
386
393
|
/* @__PURE__ */ jsx3("span", { className: "topbar-spacer" }),
|
|
387
394
|
/* @__PURE__ */ jsx3("div", { className: "topbar-actions", children: identity })
|
|
388
395
|
] }),
|
|
389
396
|
/* @__PURE__ */ jsxs3("main", { className: "shell-main", style: S.main, children: [
|
|
390
397
|
/* @__PURE__ */ jsx3(ThemeWarning, {}),
|
|
391
|
-
|
|
398
|
+
workspaceContent
|
|
392
399
|
] })
|
|
393
400
|
] });
|
|
394
401
|
}
|
|
@@ -397,7 +404,7 @@ function AdminShell({
|
|
|
397
404
|
/* @__PURE__ */ jsx3("span", { style: S.badgeSm, children: badgeText(brand) }),
|
|
398
405
|
brandDisplay(brand)
|
|
399
406
|
] }),
|
|
400
|
-
/* @__PURE__ */ jsx3("span", { style: { opacity: 0.68 }, children:
|
|
407
|
+
/* @__PURE__ */ jsx3("span", { style: { opacity: 0.68 }, children: copy.shell.adminConsole }),
|
|
401
408
|
/* @__PURE__ */ jsx3("span", { style: { flex: 1 } }),
|
|
402
409
|
/* @__PURE__ */ jsx3("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: identity })
|
|
403
410
|
] }) : null;
|
|
@@ -407,12 +414,12 @@ function AdminShell({
|
|
|
407
414
|
/* @__PURE__ */ jsx3(ThemeWarning, {}),
|
|
408
415
|
renderWorkspaceNav ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
409
416
|
/* @__PURE__ */ jsx3("div", { style: S.workspaceTabs, children: renderWorkspaceNav({ workspaces, route, navigate, href }) }),
|
|
410
|
-
|
|
417
|
+
workspaceContent
|
|
411
418
|
] }) : /* @__PURE__ */ jsx3(
|
|
412
419
|
Tabs,
|
|
413
420
|
{
|
|
414
421
|
style: S.workspaceTabs,
|
|
415
|
-
ariaLabel:
|
|
422
|
+
ariaLabel: copy.shell.navigationLabel,
|
|
416
423
|
value: route.workspaceId,
|
|
417
424
|
mount: "active",
|
|
418
425
|
onValueChange: (workspaceId) => setRoute(normalizeRoute({ workspaceId })),
|
|
@@ -420,7 +427,7 @@ function AdminShell({
|
|
|
420
427
|
value: workspace.id,
|
|
421
428
|
label: workspace.label,
|
|
422
429
|
href: href(workspace.id),
|
|
423
|
-
panel: workspace.id === route.workspaceId ? workspace.render(context) : null
|
|
430
|
+
panel: workspace.id === route.workspaceId ? workspace.accent ? /* @__PURE__ */ jsx3(ThemeScope, { accent: workspace.accent, children: workspace.render(context) }) : workspace.render(context) : null
|
|
424
431
|
}))
|
|
425
432
|
}
|
|
426
433
|
)
|
|
@@ -432,11 +439,11 @@ function AdminShell({
|
|
|
432
439
|
import { Tabs as Tabs2 } from "@odla-ai/ui/components";
|
|
433
440
|
|
|
434
441
|
// src/ui/admin-availability.tsx
|
|
435
|
-
import { useState as useState4 } from "
|
|
442
|
+
import { useState as useState4 } from "preact/hooks";
|
|
436
443
|
import { Button, Checkbox, Field, Input } from "@odla-ai/ui/components";
|
|
437
444
|
|
|
438
445
|
// src/ui/admin-api.ts
|
|
439
|
-
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "
|
|
446
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useState as useState3 } from "preact/hooks";
|
|
440
447
|
async function adminFetch(getToken, path, init) {
|
|
441
448
|
const token = await getToken();
|
|
442
449
|
const headers = new Headers(init?.headers);
|
|
@@ -466,18 +473,18 @@ function useAdminResource(getToken, path) {
|
|
|
466
473
|
}
|
|
467
474
|
|
|
468
475
|
// src/ui/admin-availability.tsx
|
|
469
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "
|
|
470
|
-
var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
476
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
471
477
|
function AvailabilityBody({ ctx }) {
|
|
478
|
+
const copy = useChapterCopy().admin.availability;
|
|
472
479
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
|
|
473
480
|
const [draft, setDraft] = useState4(null);
|
|
474
481
|
const [busy, setBusy] = useState4(false);
|
|
475
482
|
const [note, setNote] = useState4(null);
|
|
476
483
|
const [errors, setErrors] = useState4({});
|
|
477
484
|
const cfg = draft ?? data?.scheduling ?? null;
|
|
478
|
-
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children:
|
|
485
|
+
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children: copy.loading });
|
|
479
486
|
if (error || !cfg) return /* @__PURE__ */ jsxs4(AdminNote, { children: [
|
|
480
|
-
|
|
487
|
+
copy.loadFailed,
|
|
481
488
|
error ? `: ${error}` : "",
|
|
482
489
|
"."
|
|
483
490
|
] });
|
|
@@ -499,26 +506,26 @@ function AvailabilityBody({ ctx }) {
|
|
|
499
506
|
}
|
|
500
507
|
setDraft(null);
|
|
501
508
|
refresh();
|
|
502
|
-
setNote(
|
|
509
|
+
setNote(copy.saved);
|
|
503
510
|
} catch (e) {
|
|
504
511
|
setNote(e instanceof Error ? e.message : String(e));
|
|
505
512
|
} finally {
|
|
506
513
|
setBusy(false);
|
|
507
514
|
}
|
|
508
515
|
};
|
|
509
|
-
const numField = (key, label) => /* @__PURE__ */ jsx4(Field, { label, error: errors[key], children: /* @__PURE__ */ jsx4(Input, { type: "number", value: cfg[key],
|
|
510
|
-
return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title:
|
|
511
|
-
/* @__PURE__ */ jsx4(Field, { label:
|
|
516
|
+
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]) }) }) });
|
|
517
|
+
return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx4("span", { className: "muted", children: note }) : void 0, children: [
|
|
518
|
+
/* @__PURE__ */ jsx4(Field, { label: copy.days, error: errors.days, children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 14, flexWrap: "wrap" }, children: copy.dayLabels.map((d, i) => /* @__PURE__ */ jsx4(Checkbox, { label: d, checked: cfg.days.includes(i), onChange: () => toggleDay(i) }, d)) }) }),
|
|
512
519
|
/* @__PURE__ */ jsxs4("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 14 }, children: [
|
|
513
|
-
numField("startHour",
|
|
514
|
-
numField("endHour",
|
|
515
|
-
numField("slotMinutes",
|
|
516
|
-
numField("minNoticeHours",
|
|
517
|
-
numField("windowDays",
|
|
518
|
-
/* @__PURE__ */ jsx4(Field, { label:
|
|
520
|
+
numField("startHour", copy.startHour),
|
|
521
|
+
numField("endHour", copy.endHour),
|
|
522
|
+
numField("slotMinutes", copy.slotMinutes),
|
|
523
|
+
numField("minNoticeHours", copy.minNoticeHours),
|
|
524
|
+
numField("windowDays", copy.windowDays),
|
|
525
|
+
/* @__PURE__ */ jsx4(Field, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx4(Input, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
519
526
|
] }),
|
|
520
|
-
/* @__PURE__ */ jsx4(Field, { label:
|
|
521
|
-
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children:
|
|
527
|
+
/* @__PURE__ */ jsx4(Field, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx4(Input, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
528
|
+
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
522
529
|
] }) });
|
|
523
530
|
}
|
|
524
531
|
function availabilitySection(options = {}) {
|
|
@@ -528,38 +535,39 @@ function availabilitySection(options = {}) {
|
|
|
528
535
|
|
|
529
536
|
// src/ui/admin-billing.tsx
|
|
530
537
|
import { DataTable, StatBand } from "@odla-ai/ui/components";
|
|
531
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "
|
|
538
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
532
539
|
var money = (cents) => `$${(cents / 100).toLocaleString()}`;
|
|
533
540
|
var date = (t) => t ? new Date(t).toLocaleDateString([], { dateStyle: "medium" }) : "\u2014";
|
|
534
|
-
var
|
|
535
|
-
{ key: "name", header:
|
|
536
|
-
{ key: "email", header:
|
|
537
|
-
{ key: "applicationStatus", header:
|
|
538
|
-
{ key: "subscriptionStatus", header:
|
|
539
|
-
{ key: "amountCents", header:
|
|
540
|
-
{ key: "renewalAt", header:
|
|
541
|
+
var columns = (copy) => [
|
|
542
|
+
{ key: "name", header: copy.name, cell: (r) => r.name, sortAs: "string" },
|
|
543
|
+
{ key: "email", header: copy.email, cell: (r) => r.email },
|
|
544
|
+
{ key: "applicationStatus", header: copy.application, cell: (r) => r.applicationStatus },
|
|
545
|
+
{ key: "subscriptionStatus", header: copy.subscription, cell: (r) => r.subscriptionStatus ? `${r.subscriptionStatus}${r.cancelAtPeriodEnd ? ` \xB7 ${copy.cancelling}` : ""}` : "\u2014" },
|
|
546
|
+
{ key: "amountCents", header: copy.amount, cell: (r) => `${money(r.amountCents)}/${r.interval === "month" ? "mo" : "yr"}`, sortAs: "number", sortValue: (r) => r.amountCents },
|
|
547
|
+
{ key: "renewalAt", header: copy.renews, cell: (r) => date(r.renewalAt), sortAs: "number", sortValue: (r) => r.renewalAt ?? 0 }
|
|
541
548
|
];
|
|
542
549
|
function BillingBody({ ctx }) {
|
|
550
|
+
const copy = useChapterCopy().admin.billing;
|
|
543
551
|
const { data, loading, error } = useAdminResource(ctx.getToken, "/api/admin/billing");
|
|
544
|
-
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children:
|
|
552
|
+
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.loading });
|
|
545
553
|
if (error || !data) return /* @__PURE__ */ jsxs5(AdminNote, { children: [
|
|
546
|
-
|
|
554
|
+
copy.loadFailed,
|
|
547
555
|
error ? `: ${error}` : "",
|
|
548
556
|
"."
|
|
549
557
|
] });
|
|
550
|
-
if (!data.billingReady) return /* @__PURE__ */ jsx5(AdminNote, { children:
|
|
558
|
+
if (!data.billingReady) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.notConfigured });
|
|
551
559
|
const s = data.summary;
|
|
552
560
|
const stats = s ? [
|
|
553
|
-
{ value: s.activeCount, label:
|
|
554
|
-
{ value: money(s.annualizedCents), label:
|
|
555
|
-
{ value: s.renewingSoonCount, label:
|
|
556
|
-
{ value: s.pastDueCount, label:
|
|
561
|
+
{ value: s.activeCount, label: copy.active },
|
|
562
|
+
{ value: money(s.annualizedCents), label: copy.annualized },
|
|
563
|
+
{ value: s.renewingSoonCount, label: copy.renewingSoon },
|
|
564
|
+
{ value: s.pastDueCount, label: copy.pastDue }
|
|
557
565
|
] : [];
|
|
558
566
|
return /* @__PURE__ */ jsxs5(AdminPage, { children: [
|
|
559
567
|
s ? /* @__PURE__ */ jsx5(StatBand, { stats }) : null,
|
|
560
|
-
/* @__PURE__ */ jsxs5(Panel, { title:
|
|
561
|
-
data.truncated ? /* @__PURE__ */ jsx5("p", { className: "badge", children:
|
|
562
|
-
/* @__PURE__ */ jsx5(DataTable, { rows: data.rows, columns:
|
|
568
|
+
/* @__PURE__ */ jsxs5(Panel, { title: copy.subscriptions, actions: data.testMode ? /* @__PURE__ */ jsx5("span", { className: "badge", children: copy.testMode }) : void 0, children: [
|
|
569
|
+
data.truncated ? /* @__PURE__ */ jsx5("p", { className: "badge", children: copy.truncated }) : null,
|
|
570
|
+
/* @__PURE__ */ jsx5(DataTable, { rows: data.rows, columns: columns(copy), rowKey: (r) => r.id })
|
|
563
571
|
] })
|
|
564
572
|
] });
|
|
565
573
|
}
|
|
@@ -570,25 +578,26 @@ function billingSection(options = {}) {
|
|
|
570
578
|
|
|
571
579
|
// src/ui/admin-dashboard.tsx
|
|
572
580
|
import { MetricWidget, StepPipeline } from "@odla-ai/ui/components";
|
|
573
|
-
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "
|
|
581
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
574
582
|
var number = (value) => Math.round(value).toLocaleString();
|
|
575
583
|
var money2 = (cents) => `$${Math.round(cents / 100).toLocaleString()}`;
|
|
576
584
|
var when = (time) => new Date(time).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
577
585
|
function DashboardBody({ ctx }) {
|
|
586
|
+
const copy = useChapterCopy().admin.dashboard;
|
|
578
587
|
const { data, loading, error } = useAdminResource(
|
|
579
588
|
ctx.getToken,
|
|
580
589
|
"/api/admin/dashboard"
|
|
581
590
|
);
|
|
582
|
-
if (loading) return /* @__PURE__ */ jsx6(AdminNote, { children:
|
|
591
|
+
if (loading) return /* @__PURE__ */ jsx6(AdminNote, { children: copy.loading });
|
|
583
592
|
if (error || !data) return /* @__PURE__ */ jsxs6(AdminNote, { children: [
|
|
584
|
-
|
|
593
|
+
copy.loadFailed,
|
|
585
594
|
error ? `: ${error}` : "",
|
|
586
595
|
"."
|
|
587
596
|
] });
|
|
588
597
|
const pipelineSteps = Object.entries(data.pipeline).filter(([stage]) => !["declined", "refunded"].includes(stage)).map(([stage, count]) => ({
|
|
589
598
|
icon: /* @__PURE__ */ jsx6("span", { style: { fontSize: 22, fontWeight: 700 }, children: count }),
|
|
590
599
|
title: stage.replaceAll("_", " "),
|
|
591
|
-
description: data.pipelineDelta[stage] ?
|
|
600
|
+
description: data.pipelineDelta[stage] ? formatChapterCopy(copy.thisWeek, { count: data.pipelineDelta[stage] }) : copy.noChange
|
|
592
601
|
}));
|
|
593
602
|
const exceptions = ["declined", "refunded"].filter((stage) => stage in data.pipeline).map((stage) => `${stage} ${data.pipeline[stage]}`).join(" \xB7 ");
|
|
594
603
|
return /* @__PURE__ */ jsxs6(AdminPage, { children: [
|
|
@@ -596,54 +605,54 @@ function DashboardBody({ ctx }) {
|
|
|
596
605
|
Panel,
|
|
597
606
|
{
|
|
598
607
|
title: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
599
|
-
|
|
608
|
+
copy.upcomingCalls,
|
|
600
609
|
" ",
|
|
601
610
|
/* @__PURE__ */ jsxs6("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
602
611
|
"\xB7 ",
|
|
603
612
|
data.timezone
|
|
604
613
|
] })
|
|
605
614
|
] }),
|
|
606
|
-
actions: data.calls.needsAttention ? /* @__PURE__ */
|
|
607
|
-
|
|
608
|
-
" need attention"
|
|
609
|
-
] }) : void 0,
|
|
610
|
-
children: data.agenda.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: "No upcoming calls." }) : /* @__PURE__ */ jsx6("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.agenda.map((meeting) => /* @__PURE__ */ jsxs6("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
615
|
+
actions: data.calls.needsAttention ? /* @__PURE__ */ jsx6("span", { className: "badge", children: formatChapterCopy(copy.callsNeedAttention, { count: data.calls.needsAttention }) }) : void 0,
|
|
616
|
+
children: data.agenda.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: copy.noUpcomingCalls }) : /* @__PURE__ */ jsx6("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: data.agenda.map((meeting) => /* @__PURE__ */ jsxs6("li", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
611
617
|
/* @__PURE__ */ jsx6("strong", { style: { minWidth: 170 }, children: when(meeting.startAt) }),
|
|
612
618
|
/* @__PURE__ */ jsxs6("span", { style: { flex: 1 }, children: [
|
|
613
619
|
meeting.name,
|
|
614
620
|
meeting.email ? ` \xB7 ${meeting.email}` : ""
|
|
615
621
|
] }),
|
|
616
|
-
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */ jsx6("span", { className: "badge", children:
|
|
617
|
-
meeting.htmlLink ? /* @__PURE__ */ jsx6("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children:
|
|
618
|
-
meeting.meetUrl ? /* @__PURE__ */ jsx6("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children:
|
|
622
|
+
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */ jsx6("span", { className: "badge", children: copy.drift }) : null,
|
|
623
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx6("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.open }) : null,
|
|
624
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx6("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null
|
|
619
625
|
] }, meeting.id)) })
|
|
620
626
|
}
|
|
621
627
|
),
|
|
622
628
|
/* @__PURE__ */ jsxs6("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 18 }, children: [
|
|
623
|
-
/* @__PURE__ */ jsx6(MetricWidget, { label:
|
|
624
|
-
data.membersSeries ? /* @__PURE__ */ jsx6(MetricWidget, { label:
|
|
629
|
+
/* @__PURE__ */ jsx6(MetricWidget, { label: copy.applications, data: data.applicationsSeries, format: number }),
|
|
630
|
+
data.membersSeries ? /* @__PURE__ */ jsx6(MetricWidget, { label: copy.newMembers, data: data.membersSeries, format: number }) : /* @__PURE__ */ jsx6(Panel, { title: copy.newMembers, children: /* @__PURE__ */ jsx6("p", { className: "muted", children: copy.newMembersUnavailable }) })
|
|
625
631
|
] }),
|
|
626
632
|
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */ jsxs6("div", { children: [
|
|
627
633
|
/* @__PURE__ */ jsx6(
|
|
628
634
|
MetricWidget,
|
|
629
635
|
{
|
|
630
|
-
label:
|
|
636
|
+
label: copy.revenueAdded,
|
|
631
637
|
data: data.revenueSeries,
|
|
632
638
|
format: money2,
|
|
633
639
|
size: "large"
|
|
634
640
|
}
|
|
635
641
|
),
|
|
636
642
|
/* @__PURE__ */ jsxs6("p", { className: "muted", children: [
|
|
637
|
-
|
|
643
|
+
copy.activeMemberships,
|
|
644
|
+
": ",
|
|
638
645
|
data.revenue.activeCount ?? 0,
|
|
639
|
-
" \xB7
|
|
646
|
+
" \xB7 ",
|
|
647
|
+
copy.annualRunRate,
|
|
648
|
+
":",
|
|
640
649
|
" ",
|
|
641
650
|
money2(data.revenue.annualRunRateCents ?? 0),
|
|
642
|
-
data.revenue.testMode ?
|
|
651
|
+
data.revenue.testMode ? ` \xB7 ${copy.testMode}` : ""
|
|
643
652
|
] })
|
|
644
|
-
] }) : /* @__PURE__ */ jsx6(Panel, { title:
|
|
645
|
-
/* @__PURE__ */ jsxs6(Panel, { title:
|
|
646
|
-
/* @__PURE__ */ jsx6(StepPipeline, { ariaLabel:
|
|
653
|
+
] }) : /* @__PURE__ */ jsx6(Panel, { title: copy.revenue, children: /* @__PURE__ */ jsx6("p", { className: "muted", children: copy.revenueUnavailable }) }),
|
|
654
|
+
/* @__PURE__ */ jsxs6(Panel, { title: copy.pipeline, children: [
|
|
655
|
+
/* @__PURE__ */ jsx6(StepPipeline, { ariaLabel: copy.pipelineLabel, steps: pipelineSteps }),
|
|
647
656
|
exceptions ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: exceptions }) : null
|
|
648
657
|
] })
|
|
649
658
|
] });
|
|
@@ -654,25 +663,26 @@ function dashboardSection(options = {}) {
|
|
|
654
663
|
}
|
|
655
664
|
|
|
656
665
|
// src/ui/admin-email.tsx
|
|
657
|
-
import { useState as useState5 } from "
|
|
666
|
+
import { useState as useState5 } from "preact/hooks";
|
|
658
667
|
import { Button as Button2, Checkbox as Checkbox2, DataTable as DataTable2, Field as Field2, Input as Input2, Textarea } from "@odla-ai/ui/components";
|
|
659
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "
|
|
660
|
-
var
|
|
661
|
-
{ key: "sentAt", header:
|
|
662
|
-
{ key: "template", header:
|
|
663
|
-
{ key: "to", header:
|
|
664
|
-
{ key: "status", header:
|
|
668
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
669
|
+
var logColumns = (copy) => [
|
|
670
|
+
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
671
|
+
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
672
|
+
{ key: "to", header: copy.toColumn, cell: (r) => r.to },
|
|
673
|
+
{ key: "status", header: copy.statusColumn, cell: (r) => r.error ? /* @__PURE__ */ jsx7("span", { className: "badge", children: copy.failed }) : r.redirected ? /* @__PURE__ */ jsx7("span", { className: "badge", children: copy.redirected }) : copy.delivered }
|
|
665
674
|
];
|
|
666
675
|
function EmailBody({ ctx }) {
|
|
676
|
+
const copy = useChapterCopy().admin.email;
|
|
667
677
|
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
668
678
|
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
669
679
|
const [draft, setDraft] = useState5(null);
|
|
670
680
|
const [busy, setBusy] = useState5(null);
|
|
671
681
|
const [note, setNote] = useState5(null);
|
|
672
682
|
const data = draft ?? cfg.data;
|
|
673
|
-
if (cfg.loading) return /* @__PURE__ */ jsx7(AdminNote, { children:
|
|
683
|
+
if (cfg.loading) return /* @__PURE__ */ jsx7(AdminNote, { children: copy.loading });
|
|
674
684
|
if (cfg.error || !data) return /* @__PURE__ */ jsxs7(AdminNote, { children: [
|
|
675
|
-
|
|
685
|
+
copy.loadFailed,
|
|
676
686
|
cfg.error ? `: ${cfg.error}` : "",
|
|
677
687
|
"."
|
|
678
688
|
] });
|
|
@@ -693,18 +703,22 @@ function EmailBody({ ctx }) {
|
|
|
693
703
|
await adminFetch(ctx.getToken, "/api/admin/group/email", { method: "PUT", body: JSON.stringify(data) });
|
|
694
704
|
setDraft(null);
|
|
695
705
|
cfg.refresh();
|
|
696
|
-
setNote(
|
|
706
|
+
setNote(copy.saved);
|
|
697
707
|
});
|
|
698
708
|
const test = (template) => run(`test:${template}`, async () => {
|
|
699
709
|
const r = await adminFetch(ctx.getToken, "/api/admin/email/test", { method: "POST", body: JSON.stringify({ template }) });
|
|
700
|
-
setNote(r.ok ?
|
|
710
|
+
setNote(r.ok ? formatChapterCopy(copy.sent, {
|
|
711
|
+
template,
|
|
712
|
+
address: r.to,
|
|
713
|
+
redirected: r.redirected ? ` (${copy.redirected})` : ""
|
|
714
|
+
}) : copy.testFailed);
|
|
701
715
|
log.refresh();
|
|
702
716
|
});
|
|
703
717
|
return /* @__PURE__ */ jsxs7(AdminPage, { children: [
|
|
704
718
|
/* @__PURE__ */ jsxs7(
|
|
705
719
|
Panel,
|
|
706
720
|
{
|
|
707
|
-
title:
|
|
721
|
+
title: copy.delivery,
|
|
708
722
|
actions: /* @__PURE__ */ jsxs7("span", { className: "muted", children: [
|
|
709
723
|
data.envName,
|
|
710
724
|
" \xB7 ",
|
|
@@ -712,22 +726,22 @@ function EmailBody({ ctx }) {
|
|
|
712
726
|
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
713
727
|
] }),
|
|
714
728
|
children: [
|
|
715
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
716
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
717
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
729
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx7(Input2, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
730
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.replyTo, children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
731
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
718
732
|
]
|
|
719
733
|
}
|
|
720
734
|
),
|
|
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:
|
|
722
|
-
/* @__PURE__ */ jsx7(Checkbox2, { label:
|
|
723
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
724
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
735
|
+
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: copy.sendTest }), children: [
|
|
736
|
+
/* @__PURE__ */ jsx7(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
737
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.subject, children: /* @__PURE__ */ jsx7(Input2, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
738
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.body, children: /* @__PURE__ */ jsx7(Textarea, { prose: true, rows: 5, value: t.text, onInput: (e) => editTemplate(key, { text: e.target.value }) }) })
|
|
725
739
|
] }, key)),
|
|
726
740
|
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
727
|
-
/* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children:
|
|
741
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
728
742
|
note ? /* @__PURE__ */ jsx7("span", { className: "muted", children: note }) : null
|
|
729
743
|
] }),
|
|
730
|
-
/* @__PURE__ */ jsx7(Panel, { title:
|
|
744
|
+
/* @__PURE__ */ jsx7(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx7(DataTable2, { rows: log.data?.sends ?? [], columns: logColumns(copy), rowKey: (r) => r.id }) })
|
|
731
745
|
] });
|
|
732
746
|
}
|
|
733
747
|
function emailSection(options = {}) {
|
|
@@ -739,10 +753,11 @@ function emailSection(options = {}) {
|
|
|
739
753
|
import { BillingCard, CrmWorkspace } from "@odla-ai/crm/ui";
|
|
740
754
|
|
|
741
755
|
// src/ui/admin-record-actions.tsx
|
|
742
|
-
import { useEffect as useEffect4, useState as useState6 } from "
|
|
756
|
+
import { useEffect as useEffect4, useState as useState6 } from "preact/hooks";
|
|
743
757
|
import { Button as Button3, Field as Field3, Select } from "@odla-ai/ui/components";
|
|
744
|
-
import { jsx as jsx8, jsxs as jsxs8 } from "
|
|
758
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
745
759
|
function RecordActions({ getToken, record, roles, onChanged }) {
|
|
760
|
+
const copy = useChapterCopy().admin.records;
|
|
746
761
|
const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
|
|
747
762
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
748
763
|
const [access, setAccess] = useState6(null);
|
|
@@ -772,13 +787,13 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
772
787
|
setBusy(null);
|
|
773
788
|
}
|
|
774
789
|
};
|
|
775
|
-
const setRole = (role) => act("role", () => adminFetch(getToken, "/api/admin/people/role", { method: "POST", body: JSON.stringify({ userId, role }) }),
|
|
776
|
-
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }),
|
|
777
|
-
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }),
|
|
790
|
+
const setRole = (role) => act("role", () => adminFetch(getToken, "/api/admin/people/role", { method: "POST", body: JSON.stringify({ userId, role }) }), formatChapterCopy(copy.roleSet, { role }));
|
|
791
|
+
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }), copy.approved);
|
|
792
|
+
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
|
|
778
793
|
if (!userId && !applicationId) return null;
|
|
779
794
|
return /* @__PURE__ */ jsxs8("section", { className: "panel", style: { display: "flex", flexDirection: "column", gap: 14, marginTop: 16 }, children: [
|
|
780
|
-
/* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: 16 }, children:
|
|
781
|
-
userId ? /* @__PURE__ */ jsx8(Field3, { label:
|
|
795
|
+
/* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
|
|
796
|
+
userId ? /* @__PURE__ */ jsx8(Field3, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx8(
|
|
782
797
|
Select,
|
|
783
798
|
{
|
|
784
799
|
value: access?.role ?? "",
|
|
@@ -786,19 +801,20 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
786
801
|
options: roles.map((r) => ({ value: r, label: r })),
|
|
787
802
|
onChange: (e) => void setRole(e.target.value)
|
|
788
803
|
}
|
|
789
|
-
) }) : /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children:
|
|
804
|
+
) }) : /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
|
|
790
805
|
applicationId ? /* @__PURE__ */ jsxs8("div", { style: { display: "flex", gap: 8 }, children: [
|
|
791
|
-
/* @__PURE__ */ jsx8(Button3, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children:
|
|
792
|
-
/* @__PURE__ */ jsx8(Button3, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children:
|
|
806
|
+
/* @__PURE__ */ jsx8(Button3, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
|
|
807
|
+
/* @__PURE__ */ jsx8(Button3, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
|
|
793
808
|
] }) : null,
|
|
794
809
|
note ? /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children: note }) : null
|
|
795
810
|
] });
|
|
796
811
|
}
|
|
797
812
|
|
|
798
813
|
// src/ui/admin-network.tsx
|
|
799
|
-
import { useState as useState7 } from "
|
|
800
|
-
import { jsx as jsx9, jsxs as jsxs9 } from "
|
|
814
|
+
import { useState as useState7 } from "preact/hooks";
|
|
815
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
801
816
|
function NetworkShareActions(props) {
|
|
817
|
+
const copy = useChapterCopy().admin.network;
|
|
802
818
|
const { recordId, recordType, getToken } = props;
|
|
803
819
|
const targets = useAdminResource(getToken, "/api/admin/network/targets");
|
|
804
820
|
const [busy, setBusy] = useState7(null);
|
|
@@ -814,27 +830,27 @@ function NetworkShareActions(props) {
|
|
|
814
830
|
body: JSON.stringify({ recordId, targetIds: [target.id] })
|
|
815
831
|
});
|
|
816
832
|
const delivery = result.results[0];
|
|
817
|
-
setMessage(delivery?.ok ?
|
|
833
|
+
setMessage(delivery?.ok ? formatChapterCopy(copy.shared, { target: target.name }) : delivery?.error ?? formatChapterCopy(copy.shareFailed, { target: target.name }));
|
|
818
834
|
} catch (err) {
|
|
819
|
-
setMessage(err instanceof Error ? err.message :
|
|
835
|
+
setMessage(err instanceof Error ? err.message : copy.deliveryFailed);
|
|
820
836
|
} finally {
|
|
821
837
|
setBusy(null);
|
|
822
838
|
}
|
|
823
839
|
};
|
|
824
840
|
return /* @__PURE__ */ jsxs9("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
825
841
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
826
|
-
/* @__PURE__ */ jsx9("h3", { style: { margin: 0, fontSize: 16 }, children:
|
|
827
|
-
/* @__PURE__ */ jsx9("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children:
|
|
842
|
+
/* @__PURE__ */ jsx9("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
|
|
843
|
+
/* @__PURE__ */ jsx9("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
|
|
828
844
|
] }),
|
|
829
|
-
/* @__PURE__ */ jsx9("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx9("button", { className: "btn secondary mini", disabled: busy !== null, onClick: () => void push(target), children: busy === target.id ?
|
|
845
|
+
/* @__PURE__ */ jsx9("div", { style: { display: "flex", flexWrap: "wrap", gap: 8 }, children: compatible.map((target) => /* @__PURE__ */ jsx9("button", { className: "btn secondary mini", disabled: busy !== null, onClick: () => void push(target), children: busy === target.id ? copy.sharing : formatChapterCopy(copy.shareWith, { target: target.name }) }, target.id)) }),
|
|
830
846
|
message ? /* @__PURE__ */ jsx9("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
|
|
831
847
|
] });
|
|
832
848
|
}
|
|
833
849
|
|
|
834
850
|
// src/ui/admin-record-activity.tsx
|
|
835
|
-
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "
|
|
851
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useState as useState8 } from "preact/hooks";
|
|
836
852
|
import { ActivityFeed } from "@odla-ai/crm/ui";
|
|
837
|
-
import { jsx as jsx10 } from "
|
|
853
|
+
import { jsx as jsx10 } from "preact/jsx-runtime";
|
|
838
854
|
function RecordActivityPanel(props) {
|
|
839
855
|
const { client, recordId } = props;
|
|
840
856
|
const [activities, setActivities] = useState8([]);
|
|
@@ -873,10 +889,11 @@ function RecordActivityPanel(props) {
|
|
|
873
889
|
}
|
|
874
890
|
|
|
875
891
|
// src/ui/admin-record-comms.tsx
|
|
876
|
-
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState9 } from "
|
|
892
|
+
import { useCallback as useCallback4, useEffect as useEffect6, useState as useState9 } from "preact/hooks";
|
|
877
893
|
import { Button as Button4, Field as Field4, Select as Select2 } from "@odla-ai/ui/components";
|
|
878
|
-
import { jsx as jsx11, jsxs as jsxs10 } from "
|
|
894
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
879
895
|
function RecordCommsPanel(props) {
|
|
896
|
+
const copy = useChapterCopy().admin.records;
|
|
880
897
|
const { client, recordId, getToken } = props;
|
|
881
898
|
const config = useAdminResource(getToken, "/api/admin/group/email");
|
|
882
899
|
const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
|
|
@@ -889,32 +906,33 @@ function RecordCommsPanel(props) {
|
|
|
889
906
|
setNote(null);
|
|
890
907
|
try {
|
|
891
908
|
const result = await client.sendEmail(recordId, { templateId: template });
|
|
892
|
-
setNote(result.sent ?
|
|
909
|
+
setNote(result.sent ? copy.messageSent : result.reason ?? copy.messageNotSent);
|
|
893
910
|
} catch (failure) {
|
|
894
911
|
setNote(failure instanceof Error ? failure.message : String(failure));
|
|
895
912
|
} finally {
|
|
896
913
|
setBusy(false);
|
|
897
914
|
}
|
|
898
915
|
};
|
|
899
|
-
if (config.loading) return /* @__PURE__ */ jsx11("p", { className: "muted", children:
|
|
916
|
+
if (config.loading) return /* @__PURE__ */ jsx11("p", { className: "muted", children: copy.loadingTemplates });
|
|
900
917
|
if (config.error) return /* @__PURE__ */ jsx11("p", { role: "alert", className: "muted", children: config.error });
|
|
901
918
|
return /* @__PURE__ */ jsxs10("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
|
|
902
|
-
/* @__PURE__ */ jsx11(Field4, { label:
|
|
919
|
+
/* @__PURE__ */ jsx11(Field4, { label: copy.template, children: /* @__PURE__ */ jsx11(
|
|
903
920
|
Select2,
|
|
904
921
|
{
|
|
905
922
|
value: template,
|
|
906
923
|
onChange: (event) => setTemplate(event.currentTarget.value),
|
|
907
924
|
options: [
|
|
908
|
-
{ value: "", label:
|
|
925
|
+
{ value: "", label: copy.chooseTemplate },
|
|
909
926
|
...templates.map(([id, value]) => ({ value: id, label: value.subject || id }))
|
|
910
927
|
]
|
|
911
928
|
}
|
|
912
929
|
) }),
|
|
913
|
-
/* @__PURE__ */ jsx11("div", { children: /* @__PURE__ */ jsx11(Button4, { disabled: !template || busy, onClick: () => void send(), children: busy ?
|
|
930
|
+
/* @__PURE__ */ jsx11("div", { children: /* @__PURE__ */ jsx11(Button4, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
|
|
914
931
|
note ? /* @__PURE__ */ jsx11("p", { role: "status", className: "muted", children: note }) : null
|
|
915
932
|
] });
|
|
916
933
|
}
|
|
917
934
|
function RecordCommsHistory(props) {
|
|
935
|
+
const copy = useChapterCopy().admin.records;
|
|
918
936
|
const { client, recordId } = props;
|
|
919
937
|
const [rows, setRows] = useState9([]);
|
|
920
938
|
const [error, setError] = useState9(null);
|
|
@@ -929,9 +947,9 @@ function RecordCommsHistory(props) {
|
|
|
929
947
|
}, [client, recordId]);
|
|
930
948
|
useEffect6(() => void load(), [load]);
|
|
931
949
|
if (error) return /* @__PURE__ */ jsx11("p", { role: "alert", className: "muted", children: error });
|
|
932
|
-
if (rows.length === 0) return /* @__PURE__ */ jsx11("p", { className: "muted", children:
|
|
950
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx11("p", { className: "muted", children: copy.noMessages });
|
|
933
951
|
return /* @__PURE__ */ jsx11("ul", { style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 10 }, children: rows.map((row, index) => /* @__PURE__ */ jsxs10("li", { className: "panel", children: [
|
|
934
|
-
/* @__PURE__ */ jsx11("strong", { children: String(row["templateId"] ?? row["template"] ??
|
|
952
|
+
/* @__PURE__ */ jsx11("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
|
|
935
953
|
/* @__PURE__ */ jsxs10("div", { className: "muted", children: [
|
|
936
954
|
row["to"] ? String(row["to"]) : "",
|
|
937
955
|
row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
|
|
@@ -940,10 +958,11 @@ function RecordCommsHistory(props) {
|
|
|
940
958
|
}
|
|
941
959
|
|
|
942
960
|
// src/ui/admin-record-scheduling.tsx
|
|
943
|
-
import { useState as useState10 } from "
|
|
961
|
+
import { useState as useState10 } from "preact/hooks";
|
|
944
962
|
import { Button as Button5 } from "@odla-ai/ui/components";
|
|
945
|
-
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "
|
|
963
|
+
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
946
964
|
function RecordSchedulingPanel(props) {
|
|
965
|
+
const copy = useChapterCopy().admin.records;
|
|
947
966
|
const { getToken, applicationId } = props;
|
|
948
967
|
const resource = useAdminResource(
|
|
949
968
|
getToken,
|
|
@@ -955,32 +974,32 @@ function RecordSchedulingPanel(props) {
|
|
|
955
974
|
const run = async (meeting, action) => {
|
|
956
975
|
let body;
|
|
957
976
|
if (action === "reschedule") {
|
|
958
|
-
const input = globalThis.prompt?.(
|
|
977
|
+
const input = globalThis.prompt?.(copy.newStartPrompt);
|
|
959
978
|
if (!input) return;
|
|
960
979
|
const startAt = Date.parse(input);
|
|
961
980
|
if (!Number.isFinite(startAt)) {
|
|
962
|
-
setNote(
|
|
981
|
+
setNote(copy.parseFailed);
|
|
963
982
|
return;
|
|
964
983
|
}
|
|
965
984
|
body = JSON.stringify({ startAt });
|
|
966
|
-
} else if (!globalThis.confirm?.(
|
|
985
|
+
} else if (!globalThis.confirm?.(copy.cancelConfirm)) {
|
|
967
986
|
return;
|
|
968
987
|
}
|
|
969
988
|
setBusy(meeting.id);
|
|
970
989
|
try {
|
|
971
990
|
await adminFetch(getToken, `/api/admin/meetings/${meeting.id}/${action}`, { method: "POST", body });
|
|
972
991
|
resource.refresh();
|
|
973
|
-
setNote(action === "cancel" ?
|
|
992
|
+
setNote(action === "cancel" ? copy.callCancelled : copy.callRescheduled);
|
|
974
993
|
} catch (failure) {
|
|
975
994
|
setNote(failure instanceof Error ? failure.message : String(failure));
|
|
976
995
|
} finally {
|
|
977
996
|
setBusy(null);
|
|
978
997
|
}
|
|
979
998
|
};
|
|
980
|
-
if (!applicationId) return /* @__PURE__ */ jsx12("p", { className: "muted", children:
|
|
981
|
-
if (resource.loading) return /* @__PURE__ */ jsx12("p", { className: "muted", children:
|
|
999
|
+
if (!applicationId) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.noApplication });
|
|
1000
|
+
if (resource.loading) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.loadingMeetings });
|
|
982
1001
|
if (resource.error) return /* @__PURE__ */ jsx12("p", { role: "alert", className: "muted", children: resource.error });
|
|
983
|
-
if (meetings.length === 0) return /* @__PURE__ */ jsx12("p", { className: "muted", children:
|
|
1002
|
+
if (meetings.length === 0) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.noMeetings });
|
|
984
1003
|
return /* @__PURE__ */ jsxs11("div", { style: { display: "grid", gap: 12 }, children: [
|
|
985
1004
|
meetings.map((meeting) => /* @__PURE__ */ jsxs11("section", { className: "panel", children: [
|
|
986
1005
|
/* @__PURE__ */ jsx12("strong", { children: new Date(meeting.startAt).toLocaleString() }),
|
|
@@ -990,11 +1009,11 @@ function RecordSchedulingPanel(props) {
|
|
|
990
1009
|
resource.data?.timezone
|
|
991
1010
|
] }),
|
|
992
1011
|
/* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
|
|
993
|
-
meeting.meetUrl ? /* @__PURE__ */ jsx12("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children:
|
|
994
|
-
meeting.htmlLink ? /* @__PURE__ */ jsx12("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children:
|
|
1012
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx12("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
|
|
1013
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx12("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
|
|
995
1014
|
meeting.status === "scheduled" ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
996
|
-
/* @__PURE__ */ jsx12(Button5, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children:
|
|
997
|
-
/* @__PURE__ */ jsx12(Button5, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children:
|
|
1015
|
+
/* @__PURE__ */ jsx12(Button5, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
|
|
1016
|
+
/* @__PURE__ */ jsx12(Button5, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
|
|
998
1017
|
] }) : null
|
|
999
1018
|
] })
|
|
1000
1019
|
] }, meeting.id)),
|
|
@@ -1003,13 +1022,13 @@ function RecordSchedulingPanel(props) {
|
|
|
1003
1022
|
}
|
|
1004
1023
|
|
|
1005
1024
|
// src/ui/admin-people-workspace.tsx
|
|
1006
|
-
import { jsx as jsx13 } from "
|
|
1007
|
-
function applicationLifecycleAdapter(ctx) {
|
|
1025
|
+
import { jsx as jsx13 } from "preact/jsx-runtime";
|
|
1026
|
+
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
1008
1027
|
return {
|
|
1009
1028
|
transitionStage: async ({ record, to }) => {
|
|
1010
1029
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
1011
1030
|
if (!applicationId) {
|
|
1012
|
-
throw new Error(
|
|
1031
|
+
throw new Error(copy.workflowMissing);
|
|
1013
1032
|
}
|
|
1014
1033
|
const path = `/api/admin/applications/${encodeURIComponent(applicationId)}`;
|
|
1015
1034
|
if (to === "approved") {
|
|
@@ -1026,6 +1045,7 @@ function applicationLifecycleAdapter(ctx) {
|
|
|
1026
1045
|
};
|
|
1027
1046
|
}
|
|
1028
1047
|
function PeopleBody(props) {
|
|
1048
|
+
const copy = useChapterCopy().admin.records;
|
|
1029
1049
|
const {
|
|
1030
1050
|
crm,
|
|
1031
1051
|
client,
|
|
@@ -1045,7 +1065,7 @@ function PeopleBody(props) {
|
|
|
1045
1065
|
} = props;
|
|
1046
1066
|
const def = crm.type(type);
|
|
1047
1067
|
const recordId = ctx.route?.recordId ?? null;
|
|
1048
|
-
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx) : void 0;
|
|
1068
|
+
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
|
|
1049
1069
|
const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
|
|
1050
1070
|
const workspaceClient = client;
|
|
1051
1071
|
return /* @__PURE__ */ jsx13(AdminPage, { children: /* @__PURE__ */ jsx13(
|
|
@@ -1081,35 +1101,35 @@ function PeopleBody(props) {
|
|
|
1081
1101
|
if (def.facets?.email !== false) {
|
|
1082
1102
|
tabs.push({
|
|
1083
1103
|
id: "comms",
|
|
1084
|
-
label:
|
|
1104
|
+
label: copy.comms,
|
|
1085
1105
|
panel: /* @__PURE__ */ jsx13(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
|
|
1086
1106
|
});
|
|
1087
1107
|
tabs.push({
|
|
1088
1108
|
id: "history",
|
|
1089
|
-
label:
|
|
1109
|
+
label: copy.commsHistory,
|
|
1090
1110
|
panel: /* @__PURE__ */ jsx13(RecordCommsHistory, { client, recordId: record.id })
|
|
1091
1111
|
});
|
|
1092
1112
|
}
|
|
1093
1113
|
if (lifecycle) {
|
|
1094
1114
|
tabs.push({
|
|
1095
1115
|
id: "scheduling",
|
|
1096
|
-
label:
|
|
1116
|
+
label: copy.scheduling,
|
|
1097
1117
|
panel: /* @__PURE__ */ jsx13(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
|
|
1098
1118
|
});
|
|
1099
|
-
tabs.push({ id: "billing", label:
|
|
1119
|
+
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsx13(BillingCard, { record }) });
|
|
1100
1120
|
} else {
|
|
1101
1121
|
add("billing");
|
|
1102
1122
|
}
|
|
1103
1123
|
tabs.push({
|
|
1104
1124
|
id: "notes",
|
|
1105
|
-
label:
|
|
1125
|
+
label: copy.notes,
|
|
1106
1126
|
panel: /* @__PURE__ */ jsx13(RecordActivityPanel, { client, recordId: record.id })
|
|
1107
1127
|
});
|
|
1108
1128
|
add("connections");
|
|
1109
1129
|
if (lifecycle) {
|
|
1110
1130
|
tabs.push({
|
|
1111
1131
|
id: "access",
|
|
1112
|
-
label:
|
|
1132
|
+
label: copy.access,
|
|
1113
1133
|
panel: /* @__PURE__ */ jsx13(
|
|
1114
1134
|
RecordActions,
|
|
1115
1135
|
{
|
|
@@ -1128,7 +1148,7 @@ function PeopleBody(props) {
|
|
|
1128
1148
|
if (networkSharing) {
|
|
1129
1149
|
tabs.push({
|
|
1130
1150
|
id: "sharing",
|
|
1131
|
-
label:
|
|
1151
|
+
label: copy.sharing,
|
|
1132
1152
|
panel: /* @__PURE__ */ jsx13(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
|
|
1133
1153
|
});
|
|
1134
1154
|
}
|
|
@@ -1139,7 +1159,7 @@ function PeopleBody(props) {
|
|
|
1139
1159
|
}
|
|
1140
1160
|
|
|
1141
1161
|
// src/ui/admin-people.tsx
|
|
1142
|
-
import { jsx as jsx14 } from "
|
|
1162
|
+
import { jsx as jsx14 } from "preact/jsx-runtime";
|
|
1143
1163
|
var DEFAULT_ROLES = ["provisional", "member", "admin"];
|
|
1144
1164
|
function collectionSection(options) {
|
|
1145
1165
|
const { crm, type } = options;
|
|
@@ -1224,7 +1244,7 @@ function peopleSection(options) {
|
|
|
1224
1244
|
}
|
|
1225
1245
|
|
|
1226
1246
|
// src/ui/admin-workspaces.tsx
|
|
1227
|
-
import { jsx as jsx15 } from "
|
|
1247
|
+
import { jsx as jsx15 } from "preact/jsx-runtime";
|
|
1228
1248
|
var adminViewContext = (ctx, viewId, active) => {
|
|
1229
1249
|
const route = {
|
|
1230
1250
|
workspaceId: ctx.route.workspaceId,
|
|
@@ -1245,26 +1265,27 @@ var adminViewContext = (ctx, viewId, active) => {
|
|
|
1245
1265
|
href: (target) => ctx.href(targetFromView(target))
|
|
1246
1266
|
};
|
|
1247
1267
|
};
|
|
1248
|
-
function DashboardWorkspace({ ctx }) {
|
|
1268
|
+
function DashboardWorkspace({ chapter, ctx }) {
|
|
1269
|
+
const copy = chapter.copy.admin;
|
|
1249
1270
|
const active = ctx.route.viewId === "billing" ? "billing" : "overview";
|
|
1250
|
-
const overview = dashboardSection({ id: "overview", label:
|
|
1251
|
-
const billing = billingSection();
|
|
1271
|
+
const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
|
|
1272
|
+
const billing = billingSection({ label: copy.workspaces.billing });
|
|
1252
1273
|
return /* @__PURE__ */ jsx15(
|
|
1253
1274
|
Tabs2,
|
|
1254
1275
|
{
|
|
1255
|
-
ariaLabel:
|
|
1276
|
+
ariaLabel: copy.workspaces.dashboardViewsLabel,
|
|
1256
1277
|
value: active,
|
|
1257
1278
|
mount: "active",
|
|
1258
1279
|
items: [
|
|
1259
1280
|
{
|
|
1260
1281
|
value: "overview",
|
|
1261
|
-
label:
|
|
1282
|
+
label: copy.workspaces.overview,
|
|
1262
1283
|
href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
|
|
1263
1284
|
panel: overview.render(adminViewContext(ctx, "overview", active === "overview"))
|
|
1264
1285
|
},
|
|
1265
1286
|
{
|
|
1266
1287
|
value: "billing",
|
|
1267
|
-
label:
|
|
1288
|
+
label: copy.workspaces.billing,
|
|
1268
1289
|
href: ctx.href({ viewId: "billing", recordId: null, detailTab: null }),
|
|
1269
1290
|
panel: billing.render(adminViewContext(ctx, "billing", active === "billing"))
|
|
1270
1291
|
}
|
|
@@ -1272,26 +1293,27 @@ function DashboardWorkspace({ ctx }) {
|
|
|
1272
1293
|
}
|
|
1273
1294
|
);
|
|
1274
1295
|
}
|
|
1275
|
-
function SettingsWorkspace({ ctx }) {
|
|
1296
|
+
function SettingsWorkspace({ chapter, ctx }) {
|
|
1297
|
+
const copy = chapter.copy.admin;
|
|
1276
1298
|
const active = ctx.route.viewId === "email" ? "email" : "calendar";
|
|
1277
|
-
const calendar = availabilitySection({ id: "calendar", label:
|
|
1278
|
-
const email = emailSection();
|
|
1299
|
+
const calendar = availabilitySection({ id: "calendar", label: copy.workspaces.calendar });
|
|
1300
|
+
const email = emailSection({ label: copy.workspaces.email });
|
|
1279
1301
|
return /* @__PURE__ */ jsx15(
|
|
1280
1302
|
Tabs2,
|
|
1281
1303
|
{
|
|
1282
|
-
ariaLabel:
|
|
1304
|
+
ariaLabel: copy.workspaces.settingsViewsLabel,
|
|
1283
1305
|
value: active,
|
|
1284
1306
|
mount: "active",
|
|
1285
1307
|
items: [
|
|
1286
1308
|
{
|
|
1287
1309
|
value: "calendar",
|
|
1288
|
-
label:
|
|
1310
|
+
label: copy.workspaces.calendar,
|
|
1289
1311
|
href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }),
|
|
1290
1312
|
panel: calendar.render(adminViewContext(ctx, "calendar", active === "calendar"))
|
|
1291
1313
|
},
|
|
1292
1314
|
{
|
|
1293
1315
|
value: "email",
|
|
1294
|
-
label:
|
|
1316
|
+
label: copy.workspaces.email,
|
|
1295
1317
|
href: ctx.href({ viewId: "email", recordId: null, detailTab: null }),
|
|
1296
1318
|
panel: email.render(adminViewContext(ctx, "email", active === "email"))
|
|
1297
1319
|
}
|
|
@@ -1307,7 +1329,7 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
1307
1329
|
return /* @__PURE__ */ jsx15(
|
|
1308
1330
|
Tabs2,
|
|
1309
1331
|
{
|
|
1310
|
-
ariaLabel:
|
|
1332
|
+
ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
|
|
1311
1333
|
value: active,
|
|
1312
1334
|
variant: "pill",
|
|
1313
1335
|
mount: "active",
|
|
@@ -1330,9 +1352,10 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
1330
1352
|
);
|
|
1331
1353
|
}
|
|
1332
1354
|
function defaultAdminWorkspaces(chapter) {
|
|
1355
|
+
const copy = chapter.copy.admin;
|
|
1333
1356
|
const people = {
|
|
1334
1357
|
id: "people",
|
|
1335
|
-
label:
|
|
1358
|
+
label: copy.workspaces.people,
|
|
1336
1359
|
defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
|
|
1337
1360
|
render: (ctx) => /* @__PURE__ */ jsx15(PeopleWorkspace, { chapter, ctx })
|
|
1338
1361
|
};
|
|
@@ -1340,37 +1363,38 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
1340
1363
|
return [
|
|
1341
1364
|
{
|
|
1342
1365
|
id: "dashboard",
|
|
1343
|
-
label:
|
|
1366
|
+
label: copy.workspaces.dashboard,
|
|
1344
1367
|
defaultViewId: "overview",
|
|
1345
|
-
render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { ctx })
|
|
1368
|
+
render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { chapter, ctx })
|
|
1346
1369
|
},
|
|
1347
1370
|
people,
|
|
1348
1371
|
{
|
|
1349
1372
|
id: "settings",
|
|
1350
|
-
label:
|
|
1373
|
+
label: copy.workspaces.settings,
|
|
1351
1374
|
defaultViewId: "calendar",
|
|
1352
|
-
render: (ctx) => /* @__PURE__ */ jsx15(SettingsWorkspace, { ctx })
|
|
1375
|
+
render: (ctx) => /* @__PURE__ */ jsx15(SettingsWorkspace, { chapter, ctx })
|
|
1353
1376
|
}
|
|
1354
1377
|
];
|
|
1355
1378
|
}
|
|
1356
1379
|
|
|
1357
1380
|
// src/ui/admin-frame.tsx
|
|
1358
|
-
import { ThemeScope } from "@odla-ai/ui/components";
|
|
1359
|
-
import { jsx as jsx16, jsxs as jsxs12 } from "
|
|
1381
|
+
import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
|
|
1382
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
1360
1383
|
function AdminTheme(props) {
|
|
1361
1384
|
const brand = props.chapter?.brand;
|
|
1362
|
-
return /* @__PURE__ */ jsxs12(
|
|
1363
|
-
|
|
1385
|
+
return /* @__PURE__ */ jsx16(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs12(
|
|
1386
|
+
ThemeScope2,
|
|
1364
1387
|
{
|
|
1365
1388
|
"data-chapter-admin": "",
|
|
1366
1389
|
theme: brand?.theme,
|
|
1390
|
+
accent: brand?.accent,
|
|
1367
1391
|
colorScheme: brand?.colorScheme ?? "light",
|
|
1368
1392
|
children: [
|
|
1369
1393
|
/* @__PURE__ */ jsx16(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
|
|
1370
1394
|
props.children
|
|
1371
1395
|
]
|
|
1372
1396
|
}
|
|
1373
|
-
);
|
|
1397
|
+
) });
|
|
1374
1398
|
}
|
|
1375
1399
|
function signInRedirect(basePath, workspaces) {
|
|
1376
1400
|
if (typeof window === "undefined") return basePath;
|
|
@@ -1410,8 +1434,9 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
|
|
|
1410
1434
|
}
|
|
1411
1435
|
|
|
1412
1436
|
// src/ui/admin.tsx
|
|
1413
|
-
import { jsx as jsx17, jsxs as jsxs13 } from "
|
|
1437
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
1414
1438
|
function Authed(props) {
|
|
1439
|
+
const copy = useChapterCopy().admin;
|
|
1415
1440
|
const {
|
|
1416
1441
|
workspaces,
|
|
1417
1442
|
basePath,
|
|
@@ -1456,15 +1481,14 @@ function Authed(props) {
|
|
|
1456
1481
|
live = false;
|
|
1457
1482
|
};
|
|
1458
1483
|
}, [getToken, apiBase, auth]);
|
|
1459
|
-
if (state.status === "checking") return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1484
|
+
if (state.status === "checking") return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.auth.checking }) });
|
|
1460
1485
|
if (state.status === "denied") {
|
|
1461
1486
|
return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsxs13("div", { className: "card", style: S.card, children: [
|
|
1462
|
-
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children:
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
state.email
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
/* @__PURE__ */ jsx17("button", { className: "btn secondary", onClick: () => signOut(), children: "Sign out" })
|
|
1487
|
+
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
|
|
1488
|
+
/* @__PURE__ */ jsx17("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
|
|
1489
|
+
account: state.email ?? copy.auth.thisAccount
|
|
1490
|
+
}) }),
|
|
1491
|
+
/* @__PURE__ */ jsx17("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
|
|
1468
1492
|
] }) });
|
|
1469
1493
|
}
|
|
1470
1494
|
return /* @__PURE__ */ jsx17(
|
|
@@ -1487,6 +1511,7 @@ function Authed(props) {
|
|
|
1487
1511
|
);
|
|
1488
1512
|
}
|
|
1489
1513
|
function ChapterAdmin(props) {
|
|
1514
|
+
const copy = props.chapter?.copy ?? DEFAULT_CHAPTER_COPY;
|
|
1490
1515
|
const defaults = useMemo(
|
|
1491
1516
|
() => props.chapter ? defaultAdminWorkspaces(props.chapter) : [],
|
|
1492
1517
|
[props.chapter]
|
|
@@ -1497,7 +1522,7 @@ function ChapterAdmin(props) {
|
|
|
1497
1522
|
}, [props.workspaces, props.sections, defaults]);
|
|
1498
1523
|
const basePath = props.basePath ?? "/admin";
|
|
1499
1524
|
const brand = props.brand ?? {
|
|
1500
|
-
name: props.chapter?.name ??
|
|
1525
|
+
name: props.chapter?.name ?? copy.admin.shell.adminName,
|
|
1501
1526
|
badge: props.chapter?.brand.badge,
|
|
1502
1527
|
wordmark: props.chapter?.brand.wordmark
|
|
1503
1528
|
};
|
|
@@ -1517,14 +1542,14 @@ function ChapterAdmin(props) {
|
|
|
1517
1542
|
}, [apiBase, auth]);
|
|
1518
1543
|
let content;
|
|
1519
1544
|
if (publishableKey === void 0) {
|
|
1520
|
-
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1545
|
+
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
1521
1546
|
} else if (!publishableKey) {
|
|
1522
1547
|
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsxs13("div", { className: "card", style: S.card, children: [
|
|
1523
|
-
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children:
|
|
1524
|
-
/* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1548
|
+
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
|
|
1549
|
+
/* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
|
|
1525
1550
|
] }) });
|
|
1526
1551
|
} else if (workspaces.length === 0) {
|
|
1527
|
-
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1552
|
+
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
|
|
1528
1553
|
} else {
|
|
1529
1554
|
content = /* @__PURE__ */ jsxs13(
|
|
1530
1555
|
ClerkGate,
|
|
@@ -1533,7 +1558,7 @@ function ChapterAdmin(props) {
|
|
|
1533
1558
|
appearance: clerkAppearanceFromTokens(),
|
|
1534
1559
|
afterSignOutUrl: "/",
|
|
1535
1560
|
children: [
|
|
1536
|
-
/* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline:
|
|
1561
|
+
/* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx17(
|
|
1537
1562
|
SignIn,
|
|
1538
1563
|
{
|
|
1539
1564
|
routing: "hash",
|
|
@@ -1570,17 +1595,18 @@ function defaultAdminSections(chapter) {
|
|
|
1570
1595
|
}
|
|
1571
1596
|
|
|
1572
1597
|
// src/ui/admin-meetings.tsx
|
|
1573
|
-
import { useState as useState12 } from "
|
|
1598
|
+
import { useState as useState12 } from "preact/hooks";
|
|
1574
1599
|
import { Button as Button6 } from "@odla-ai/ui/components";
|
|
1575
|
-
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs14 } from "
|
|
1600
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
1576
1601
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
1577
1602
|
function MeetingsBody({ ctx }) {
|
|
1603
|
+
const copy = useChapterCopy().admin.meetings;
|
|
1578
1604
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
|
|
1579
1605
|
const [busy, setBusy] = useState12(null);
|
|
1580
1606
|
const [note, setNote] = useState12(null);
|
|
1581
|
-
if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children:
|
|
1607
|
+
if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children: copy.loading });
|
|
1582
1608
|
if (error || !data) return /* @__PURE__ */ jsxs14(AdminNote, { children: [
|
|
1583
|
-
|
|
1609
|
+
copy.loadFailed,
|
|
1584
1610
|
error ? `: ${error}` : "",
|
|
1585
1611
|
"."
|
|
1586
1612
|
] });
|
|
@@ -1597,37 +1623,39 @@ function MeetingsBody({ ctx }) {
|
|
|
1597
1623
|
}
|
|
1598
1624
|
};
|
|
1599
1625
|
const cancel = (id) => {
|
|
1600
|
-
if (!globalThis.confirm?.(
|
|
1626
|
+
if (!globalThis.confirm?.(copy.cancelConfirm)) return;
|
|
1601
1627
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/cancel`, { method: "POST" }));
|
|
1602
1628
|
};
|
|
1603
1629
|
const reschedule = (id) => {
|
|
1604
|
-
const input = globalThis.prompt?.(
|
|
1630
|
+
const input = globalThis.prompt?.(copy.newStartPrompt);
|
|
1605
1631
|
if (!input) return;
|
|
1606
1632
|
const startAt = Date.parse(input);
|
|
1607
1633
|
if (!Number.isFinite(startAt)) {
|
|
1608
|
-
setNote(
|
|
1634
|
+
setNote(copy.parseFailed);
|
|
1609
1635
|
return;
|
|
1610
1636
|
}
|
|
1611
1637
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
1612
1638
|
};
|
|
1613
1639
|
return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsx18(Panel, { title: /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
1614
|
-
|
|
1640
|
+
copy.agenda,
|
|
1641
|
+
" ",
|
|
1615
1642
|
/* @__PURE__ */ jsxs14("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
1616
1643
|
"\xB7 ",
|
|
1617
1644
|
data.timezone
|
|
1618
1645
|
] })
|
|
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:
|
|
1646
|
+
] }), 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: copy.empty }) : /* @__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
1647
|
/* @__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}` :
|
|
1648
|
+
/* @__PURE__ */ jsx18("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
|
|
1622
1649
|
/* @__PURE__ */ jsx18("span", { className: "badge", children: m.status }),
|
|
1623
1650
|
m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs14("span", { className: "badge", children: [
|
|
1624
|
-
|
|
1651
|
+
copy.drift,
|
|
1652
|
+
": ",
|
|
1625
1653
|
m.drift
|
|
1626
1654
|
] }) : null,
|
|
1627
|
-
m.meetUrl ? /* @__PURE__ */ jsx18("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children:
|
|
1655
|
+
m.meetUrl ? /* @__PURE__ */ jsx18("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
1628
1656
|
m.status === "scheduled" ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
1629
|
-
/* @__PURE__ */ jsx18(Button6, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children:
|
|
1630
|
-
/* @__PURE__ */ jsx18(Button6, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children:
|
|
1657
|
+
/* @__PURE__ */ jsx18(Button6, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
1658
|
+
/* @__PURE__ */ jsx18(Button6, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
1631
1659
|
] }) : null
|
|
1632
1660
|
] }, m.id)) }) }) });
|
|
1633
1661
|
}
|
|
@@ -1663,4 +1691,4 @@ export {
|
|
|
1663
1691
|
defaultAdminSections,
|
|
1664
1692
|
meetingsSection
|
|
1665
1693
|
};
|
|
1666
|
-
//# sourceMappingURL=chunk-
|
|
1694
|
+
//# sourceMappingURL=chunk-VCC4S6JU.js.map
|