@odla-ai/chapter 0.22.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -26
- package/dist/{chunk-4COPR4SY.js → chunk-GMGTJIY3.js} +292 -97
- package/dist/chunk-GMGTJIY3.js.map +1 -0
- package/dist/chunk-WGQO4WDJ.js +412 -0
- package/dist/chunk-WGQO4WDJ.js.map +1 -0
- package/dist/{chunk-C4XQSRW7.js → chunk-ZPAJL7KG.js} +225 -190
- package/dist/chunk-ZPAJL7KG.js.map +1 -0
- package/dist/{types-CgWUQ2MH.d.ts → copy-context-Dp5hvj5W.d.ts} +85 -20
- package/dist/index.cjs +340 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -24
- package/dist/index.d.ts +111 -24
- package/dist/index.js +340 -9
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +5 -2
- package/dist/ui/admin/index.js +8 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +11 -5
- package/dist/ui/member/index.d.ts +118 -2
- package/dist/ui/member/index.js +10 -4
- package/dist/worker/index.cjs +274 -213
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +74 -19
- package/dist/worker/index.d.ts +74 -19
- package/dist/worker/index.js +274 -213
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/adopt-existing.md +23 -5
- package/runbooks/greenfield.md +85 -4
- package/dist/chunk-3QKGKUTX.js +0 -91
- package/dist/chunk-3QKGKUTX.js.map +0 -1
- package/dist/chunk-4COPR4SY.js.map +0 -1
- package/dist/chunk-C4XQSRW7.js.map +0 -1
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BrandStyle
|
|
3
|
-
|
|
2
|
+
BrandStyle,
|
|
3
|
+
ChapterCopyProvider,
|
|
4
|
+
DEFAULT_CHAPTER_COPY,
|
|
5
|
+
formatChapterCopy,
|
|
6
|
+
useChapterCopy
|
|
7
|
+
} from "./chunk-WGQO4WDJ.js";
|
|
4
8
|
|
|
5
9
|
// src/ui/admin.tsx
|
|
6
|
-
import { useEffect as
|
|
7
|
-
import {
|
|
8
|
-
ClerkGate,
|
|
9
|
-
SignedIn,
|
|
10
|
-
SignedOut,
|
|
11
|
-
SignIn,
|
|
12
|
-
useClerkAuth,
|
|
13
|
-
clerkAppearanceFromTokens
|
|
14
|
-
} from "@odla-ai/auth-clerk";
|
|
10
|
+
import { useEffect as useEffect8, useMemo, useRef, useState as useState12 } from "preact/hooks";
|
|
11
|
+
import { ClerkGate, SignedIn, SignedOut, SignIn, useClerkAuth } from "@odla-ai/auth-clerk";
|
|
15
12
|
import { CrmClient } from "@odla-ai/crm";
|
|
16
13
|
|
|
17
14
|
// src/ui/chrome.tsx
|
|
18
15
|
import { useCallback, useEffect as useEffect2, useState as useState2 } from "preact/hooks";
|
|
19
|
-
import { Tabs, TopBar, TopBarLink } from "@odla-ai/ui/components";
|
|
16
|
+
import { Tabs, ThemeScope, TopBar, TopBarLink } from "@odla-ai/ui/components";
|
|
20
17
|
|
|
21
18
|
// src/ui/admin-layout.tsx
|
|
22
19
|
import { useEffect, useState } from "preact/hooks";
|
|
@@ -301,6 +298,7 @@ function AdminShell({
|
|
|
301
298
|
renderAccountMenu,
|
|
302
299
|
renderWorkspaceNav
|
|
303
300
|
}) {
|
|
301
|
+
const copy = useChapterCopy().admin;
|
|
304
302
|
const normalizeRoute = useCallback((candidate) => {
|
|
305
303
|
if (candidate.viewId) return candidate;
|
|
306
304
|
const workspace = workspaces.find((item) => item.id === candidate.workspaceId);
|
|
@@ -371,10 +369,12 @@ function AdminShell({
|
|
|
371
369
|
signOut
|
|
372
370
|
};
|
|
373
371
|
const identity = renderAccountMenu?.(accountProps) ?? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
374
|
-
/* @__PURE__ */ jsx3("span", { style: { ...S.role, color: "inherit", borderColor: "currentColor", background: "transparent" }, children:
|
|
372
|
+
/* @__PURE__ */ jsx3("span", { style: { ...S.role, color: "inherit", borderColor: "currentColor", background: "transparent" }, children: copy.shell.adminRole }),
|
|
375
373
|
email ? /* @__PURE__ */ jsx3("span", { style: { opacity: 0.72, fontSize: 12 }, children: email }) : null,
|
|
376
|
-
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children:
|
|
374
|
+
/* @__PURE__ */ jsx3("button", { className: "btn secondary mini", onClick: signOut, children: copy.auth.signOut })
|
|
377
375
|
] });
|
|
376
|
+
const activeContent = active?.render(context);
|
|
377
|
+
const workspaceContent = active?.accent ? /* @__PURE__ */ jsx3(ThemeScope, { accent: active.accent, children: activeContent }) : activeContent;
|
|
378
378
|
if (chrome === "topbar") {
|
|
379
379
|
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
380
380
|
/* @__PURE__ */ jsxs3(TopBar, { children: [
|
|
@@ -382,13 +382,13 @@ function AdminShell({
|
|
|
382
382
|
/* @__PURE__ */ jsx3("span", { style: S.badgeSm, children: badgeText(brand) }),
|
|
383
383
|
brandDisplay(brand)
|
|
384
384
|
] }),
|
|
385
|
-
/* @__PURE__ */ jsx3("nav", { className: "topbar-nav", "aria-label":
|
|
385
|
+
/* @__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
386
|
/* @__PURE__ */ jsx3("span", { className: "topbar-spacer" }),
|
|
387
387
|
/* @__PURE__ */ jsx3("div", { className: "topbar-actions", children: identity })
|
|
388
388
|
] }),
|
|
389
389
|
/* @__PURE__ */ jsxs3("main", { className: "shell-main", style: S.main, children: [
|
|
390
390
|
/* @__PURE__ */ jsx3(ThemeWarning, {}),
|
|
391
|
-
|
|
391
|
+
workspaceContent
|
|
392
392
|
] })
|
|
393
393
|
] });
|
|
394
394
|
}
|
|
@@ -397,7 +397,7 @@ function AdminShell({
|
|
|
397
397
|
/* @__PURE__ */ jsx3("span", { style: S.badgeSm, children: badgeText(brand) }),
|
|
398
398
|
brandDisplay(brand)
|
|
399
399
|
] }),
|
|
400
|
-
/* @__PURE__ */ jsx3("span", { style: { opacity: 0.68 }, children:
|
|
400
|
+
/* @__PURE__ */ jsx3("span", { style: { opacity: 0.68 }, children: copy.shell.adminConsole }),
|
|
401
401
|
/* @__PURE__ */ jsx3("span", { style: { flex: 1 } }),
|
|
402
402
|
/* @__PURE__ */ jsx3("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: identity })
|
|
403
403
|
] }) : null;
|
|
@@ -407,12 +407,12 @@ function AdminShell({
|
|
|
407
407
|
/* @__PURE__ */ jsx3(ThemeWarning, {}),
|
|
408
408
|
renderWorkspaceNav ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
409
409
|
/* @__PURE__ */ jsx3("div", { style: S.workspaceTabs, children: renderWorkspaceNav({ workspaces, route, navigate, href }) }),
|
|
410
|
-
|
|
410
|
+
workspaceContent
|
|
411
411
|
] }) : /* @__PURE__ */ jsx3(
|
|
412
412
|
Tabs,
|
|
413
413
|
{
|
|
414
414
|
style: S.workspaceTabs,
|
|
415
|
-
ariaLabel:
|
|
415
|
+
ariaLabel: copy.shell.navigationLabel,
|
|
416
416
|
value: route.workspaceId,
|
|
417
417
|
mount: "active",
|
|
418
418
|
onValueChange: (workspaceId) => setRoute(normalizeRoute({ workspaceId })),
|
|
@@ -420,7 +420,7 @@ function AdminShell({
|
|
|
420
420
|
value: workspace.id,
|
|
421
421
|
label: workspace.label,
|
|
422
422
|
href: href(workspace.id),
|
|
423
|
-
panel: workspace.id === route.workspaceId ? workspace.render(context) : null
|
|
423
|
+
panel: workspace.id === route.workspaceId ? workspace.accent ? /* @__PURE__ */ jsx3(ThemeScope, { accent: workspace.accent, children: workspace.render(context) }) : workspace.render(context) : null
|
|
424
424
|
}))
|
|
425
425
|
}
|
|
426
426
|
)
|
|
@@ -467,17 +467,17 @@ function useAdminResource(getToken, path) {
|
|
|
467
467
|
|
|
468
468
|
// src/ui/admin-availability.tsx
|
|
469
469
|
import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
470
|
-
var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
471
470
|
function AvailabilityBody({ ctx }) {
|
|
471
|
+
const copy = useChapterCopy().admin.availability;
|
|
472
472
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/scheduling");
|
|
473
473
|
const [draft, setDraft] = useState4(null);
|
|
474
474
|
const [busy, setBusy] = useState4(false);
|
|
475
475
|
const [note, setNote] = useState4(null);
|
|
476
476
|
const [errors, setErrors] = useState4({});
|
|
477
477
|
const cfg = draft ?? data?.scheduling ?? null;
|
|
478
|
-
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children:
|
|
478
|
+
if (loading) return /* @__PURE__ */ jsx4(AdminNote, { children: copy.loading });
|
|
479
479
|
if (error || !cfg) return /* @__PURE__ */ jsxs4(AdminNote, { children: [
|
|
480
|
-
|
|
480
|
+
copy.loadFailed,
|
|
481
481
|
error ? `: ${error}` : "",
|
|
482
482
|
"."
|
|
483
483
|
] });
|
|
@@ -499,7 +499,7 @@ function AvailabilityBody({ ctx }) {
|
|
|
499
499
|
}
|
|
500
500
|
setDraft(null);
|
|
501
501
|
refresh();
|
|
502
|
-
setNote(
|
|
502
|
+
setNote(copy.saved);
|
|
503
503
|
} catch (e) {
|
|
504
504
|
setNote(e instanceof Error ? e.message : String(e));
|
|
505
505
|
} finally {
|
|
@@ -507,18 +507,18 @@ function AvailabilityBody({ ctx }) {
|
|
|
507
507
|
}
|
|
508
508
|
};
|
|
509
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]) }) }) });
|
|
510
|
-
return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title:
|
|
511
|
-
/* @__PURE__ */ jsx4(Field, { label:
|
|
510
|
+
return /* @__PURE__ */ jsx4(AdminPage, { children: /* @__PURE__ */ jsxs4(Panel, { title: copy.title, actions: note ? /* @__PURE__ */ jsx4("span", { className: "muted", children: note }) : void 0, children: [
|
|
511
|
+
/* @__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
512
|
/* @__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:
|
|
513
|
+
numField("startHour", copy.startHour),
|
|
514
|
+
numField("endHour", copy.endHour),
|
|
515
|
+
numField("slotMinutes", copy.slotMinutes),
|
|
516
|
+
numField("minNoticeHours", copy.minNoticeHours),
|
|
517
|
+
numField("windowDays", copy.windowDays),
|
|
518
|
+
/* @__PURE__ */ jsx4(Field, { label: copy.timezone, error: errors.timezone, children: /* @__PURE__ */ jsx4(Input, { value: cfg.timezone, onInput: (e) => edit({ timezone: e.target.value }) }) })
|
|
519
519
|
] }),
|
|
520
|
-
/* @__PURE__ */ jsx4(Field, { label:
|
|
521
|
-
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children:
|
|
520
|
+
/* @__PURE__ */ jsx4(Field, { label: copy.summaryTemplate, error: errors.summaryTemplate, children: /* @__PURE__ */ jsx4(Input, { value: cfg.summaryTemplate, onInput: (e) => edit({ summaryTemplate: e.target.value }) }) }),
|
|
521
|
+
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsx4(Button, { disabled: !draft || busy, onClick: () => void save(), children: copy.save }) })
|
|
522
522
|
] }) });
|
|
523
523
|
}
|
|
524
524
|
function availabilitySection(options = {}) {
|
|
@@ -531,35 +531,36 @@ import { DataTable, StatBand } from "@odla-ai/ui/components";
|
|
|
531
531
|
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
532
532
|
var money = (cents) => `$${(cents / 100).toLocaleString()}`;
|
|
533
533
|
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:
|
|
534
|
+
var columns = (copy) => [
|
|
535
|
+
{ key: "name", header: copy.name, cell: (r) => r.name, sortAs: "string" },
|
|
536
|
+
{ key: "email", header: copy.email, cell: (r) => r.email },
|
|
537
|
+
{ key: "applicationStatus", header: copy.application, cell: (r) => r.applicationStatus },
|
|
538
|
+
{ key: "subscriptionStatus", header: copy.subscription, cell: (r) => r.subscriptionStatus ? `${r.subscriptionStatus}${r.cancelAtPeriodEnd ? ` \xB7 ${copy.cancelling}` : ""}` : "\u2014" },
|
|
539
|
+
{ key: "amountCents", header: copy.amount, cell: (r) => `${money(r.amountCents)}/${r.interval === "month" ? "mo" : "yr"}`, sortAs: "number", sortValue: (r) => r.amountCents },
|
|
540
|
+
{ key: "renewalAt", header: copy.renews, cell: (r) => date(r.renewalAt), sortAs: "number", sortValue: (r) => r.renewalAt ?? 0 }
|
|
541
541
|
];
|
|
542
542
|
function BillingBody({ ctx }) {
|
|
543
|
+
const copy = useChapterCopy().admin.billing;
|
|
543
544
|
const { data, loading, error } = useAdminResource(ctx.getToken, "/api/admin/billing");
|
|
544
|
-
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children:
|
|
545
|
+
if (loading) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.loading });
|
|
545
546
|
if (error || !data) return /* @__PURE__ */ jsxs5(AdminNote, { children: [
|
|
546
|
-
|
|
547
|
+
copy.loadFailed,
|
|
547
548
|
error ? `: ${error}` : "",
|
|
548
549
|
"."
|
|
549
550
|
] });
|
|
550
|
-
if (!data.billingReady) return /* @__PURE__ */ jsx5(AdminNote, { children:
|
|
551
|
+
if (!data.billingReady) return /* @__PURE__ */ jsx5(AdminNote, { children: copy.notConfigured });
|
|
551
552
|
const s = data.summary;
|
|
552
553
|
const stats = s ? [
|
|
553
|
-
{ value: s.activeCount, label:
|
|
554
|
-
{ value: money(s.annualizedCents), label:
|
|
555
|
-
{ value: s.renewingSoonCount, label:
|
|
556
|
-
{ value: s.pastDueCount, label:
|
|
554
|
+
{ value: s.activeCount, label: copy.active },
|
|
555
|
+
{ value: money(s.annualizedCents), label: copy.annualized },
|
|
556
|
+
{ value: s.renewingSoonCount, label: copy.renewingSoon },
|
|
557
|
+
{ value: s.pastDueCount, label: copy.pastDue }
|
|
557
558
|
] : [];
|
|
558
559
|
return /* @__PURE__ */ jsxs5(AdminPage, { children: [
|
|
559
560
|
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:
|
|
561
|
+
/* @__PURE__ */ jsxs5(Panel, { title: copy.subscriptions, actions: data.testMode ? /* @__PURE__ */ jsx5("span", { className: "badge", children: copy.testMode }) : void 0, children: [
|
|
562
|
+
data.truncated ? /* @__PURE__ */ jsx5("p", { className: "badge", children: copy.truncated }) : null,
|
|
563
|
+
/* @__PURE__ */ jsx5(DataTable, { rows: data.rows, columns: columns(copy), rowKey: (r) => r.id })
|
|
563
564
|
] })
|
|
564
565
|
] });
|
|
565
566
|
}
|
|
@@ -575,20 +576,21 @@ var number = (value) => Math.round(value).toLocaleString();
|
|
|
575
576
|
var money2 = (cents) => `$${Math.round(cents / 100).toLocaleString()}`;
|
|
576
577
|
var when = (time) => new Date(time).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
577
578
|
function DashboardBody({ ctx }) {
|
|
579
|
+
const copy = useChapterCopy().admin.dashboard;
|
|
578
580
|
const { data, loading, error } = useAdminResource(
|
|
579
581
|
ctx.getToken,
|
|
580
582
|
"/api/admin/dashboard"
|
|
581
583
|
);
|
|
582
|
-
if (loading) return /* @__PURE__ */ jsx6(AdminNote, { children:
|
|
584
|
+
if (loading) return /* @__PURE__ */ jsx6(AdminNote, { children: copy.loading });
|
|
583
585
|
if (error || !data) return /* @__PURE__ */ jsxs6(AdminNote, { children: [
|
|
584
|
-
|
|
586
|
+
copy.loadFailed,
|
|
585
587
|
error ? `: ${error}` : "",
|
|
586
588
|
"."
|
|
587
589
|
] });
|
|
588
590
|
const pipelineSteps = Object.entries(data.pipeline).filter(([stage]) => !["declined", "refunded"].includes(stage)).map(([stage, count]) => ({
|
|
589
591
|
icon: /* @__PURE__ */ jsx6("span", { style: { fontSize: 22, fontWeight: 700 }, children: count }),
|
|
590
592
|
title: stage.replaceAll("_", " "),
|
|
591
|
-
description: data.pipelineDelta[stage] ?
|
|
593
|
+
description: data.pipelineDelta[stage] ? formatChapterCopy(copy.thisWeek, { count: data.pipelineDelta[stage] }) : copy.noChange
|
|
592
594
|
}));
|
|
593
595
|
const exceptions = ["declined", "refunded"].filter((stage) => stage in data.pipeline).map((stage) => `${stage} ${data.pipeline[stage]}`).join(" \xB7 ");
|
|
594
596
|
return /* @__PURE__ */ jsxs6(AdminPage, { children: [
|
|
@@ -596,54 +598,54 @@ function DashboardBody({ ctx }) {
|
|
|
596
598
|
Panel,
|
|
597
599
|
{
|
|
598
600
|
title: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
599
|
-
|
|
601
|
+
copy.upcomingCalls,
|
|
600
602
|
" ",
|
|
601
603
|
/* @__PURE__ */ jsxs6("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
602
604
|
"\xB7 ",
|
|
603
605
|
data.timezone
|
|
604
606
|
] })
|
|
605
607
|
] }),
|
|
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: [
|
|
608
|
+
actions: data.calls.needsAttention ? /* @__PURE__ */ jsx6("span", { className: "badge", children: formatChapterCopy(copy.callsNeedAttention, { count: data.calls.needsAttention }) }) : void 0,
|
|
609
|
+
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
610
|
/* @__PURE__ */ jsx6("strong", { style: { minWidth: 170 }, children: when(meeting.startAt) }),
|
|
612
611
|
/* @__PURE__ */ jsxs6("span", { style: { flex: 1 }, children: [
|
|
613
612
|
meeting.name,
|
|
614
613
|
meeting.email ? ` \xB7 ${meeting.email}` : ""
|
|
615
614
|
] }),
|
|
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:
|
|
615
|
+
meeting.drift && meeting.drift !== "none" ? /* @__PURE__ */ jsx6("span", { className: "badge", children: copy.drift }) : null,
|
|
616
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx6("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.open }) : null,
|
|
617
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx6("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null
|
|
619
618
|
] }, meeting.id)) })
|
|
620
619
|
}
|
|
621
620
|
),
|
|
622
621
|
/* @__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:
|
|
622
|
+
/* @__PURE__ */ jsx6(MetricWidget, { label: copy.applications, data: data.applicationsSeries, format: number }),
|
|
623
|
+
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
624
|
] }),
|
|
626
625
|
data.revenueSeries && data.revenue.billingReady ? /* @__PURE__ */ jsxs6("div", { children: [
|
|
627
626
|
/* @__PURE__ */ jsx6(
|
|
628
627
|
MetricWidget,
|
|
629
628
|
{
|
|
630
|
-
label:
|
|
629
|
+
label: copy.revenueAdded,
|
|
631
630
|
data: data.revenueSeries,
|
|
632
631
|
format: money2,
|
|
633
632
|
size: "large"
|
|
634
633
|
}
|
|
635
634
|
),
|
|
636
635
|
/* @__PURE__ */ jsxs6("p", { className: "muted", children: [
|
|
637
|
-
|
|
636
|
+
copy.activeMemberships,
|
|
637
|
+
": ",
|
|
638
638
|
data.revenue.activeCount ?? 0,
|
|
639
|
-
" \xB7
|
|
639
|
+
" \xB7 ",
|
|
640
|
+
copy.annualRunRate,
|
|
641
|
+
":",
|
|
640
642
|
" ",
|
|
641
643
|
money2(data.revenue.annualRunRateCents ?? 0),
|
|
642
|
-
data.revenue.testMode ?
|
|
644
|
+
data.revenue.testMode ? ` \xB7 ${copy.testMode}` : ""
|
|
643
645
|
] })
|
|
644
|
-
] }) : /* @__PURE__ */ jsx6(Panel, { title:
|
|
645
|
-
/* @__PURE__ */ jsxs6(Panel, { title:
|
|
646
|
-
/* @__PURE__ */ jsx6(StepPipeline, { ariaLabel:
|
|
646
|
+
] }) : /* @__PURE__ */ jsx6(Panel, { title: copy.revenue, children: /* @__PURE__ */ jsx6("p", { className: "muted", children: copy.revenueUnavailable }) }),
|
|
647
|
+
/* @__PURE__ */ jsxs6(Panel, { title: copy.pipeline, children: [
|
|
648
|
+
/* @__PURE__ */ jsx6(StepPipeline, { ariaLabel: copy.pipelineLabel, steps: pipelineSteps }),
|
|
647
649
|
exceptions ? /* @__PURE__ */ jsx6("p", { className: "muted", style: { margin: 0 }, children: exceptions }) : null
|
|
648
650
|
] })
|
|
649
651
|
] });
|
|
@@ -657,22 +659,23 @@ function dashboardSection(options = {}) {
|
|
|
657
659
|
import { useState as useState5 } from "preact/hooks";
|
|
658
660
|
import { Button as Button2, Checkbox as Checkbox2, DataTable as DataTable2, Field as Field2, Input as Input2, Textarea } from "@odla-ai/ui/components";
|
|
659
661
|
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
660
|
-
var
|
|
661
|
-
{ key: "sentAt", header:
|
|
662
|
-
{ key: "template", header:
|
|
663
|
-
{ key: "to", header:
|
|
664
|
-
{ key: "status", header:
|
|
662
|
+
var logColumns = (copy) => [
|
|
663
|
+
{ key: "sentAt", header: copy.sentColumn, cell: (r) => new Date(r.sentAt).toLocaleString(), sortAs: "number", sortValue: (r) => r.sentAt },
|
|
664
|
+
{ key: "template", header: copy.templateColumn, cell: (r) => r.template },
|
|
665
|
+
{ key: "to", header: copy.toColumn, cell: (r) => r.to },
|
|
666
|
+
{ 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
667
|
];
|
|
666
668
|
function EmailBody({ ctx }) {
|
|
669
|
+
const copy = useChapterCopy().admin.email;
|
|
667
670
|
const cfg = useAdminResource(ctx.getToken, "/api/admin/group/email");
|
|
668
671
|
const log = useAdminResource(ctx.getToken, "/api/admin/email/log");
|
|
669
672
|
const [draft, setDraft] = useState5(null);
|
|
670
673
|
const [busy, setBusy] = useState5(null);
|
|
671
674
|
const [note, setNote] = useState5(null);
|
|
672
675
|
const data = draft ?? cfg.data;
|
|
673
|
-
if (cfg.loading) return /* @__PURE__ */ jsx7(AdminNote, { children:
|
|
676
|
+
if (cfg.loading) return /* @__PURE__ */ jsx7(AdminNote, { children: copy.loading });
|
|
674
677
|
if (cfg.error || !data) return /* @__PURE__ */ jsxs7(AdminNote, { children: [
|
|
675
|
-
|
|
678
|
+
copy.loadFailed,
|
|
676
679
|
cfg.error ? `: ${cfg.error}` : "",
|
|
677
680
|
"."
|
|
678
681
|
] });
|
|
@@ -693,18 +696,22 @@ function EmailBody({ ctx }) {
|
|
|
693
696
|
await adminFetch(ctx.getToken, "/api/admin/group/email", { method: "PUT", body: JSON.stringify(data) });
|
|
694
697
|
setDraft(null);
|
|
695
698
|
cfg.refresh();
|
|
696
|
-
setNote(
|
|
699
|
+
setNote(copy.saved);
|
|
697
700
|
});
|
|
698
701
|
const test = (template) => run(`test:${template}`, async () => {
|
|
699
702
|
const r = await adminFetch(ctx.getToken, "/api/admin/email/test", { method: "POST", body: JSON.stringify({ template }) });
|
|
700
|
-
setNote(r.ok ?
|
|
703
|
+
setNote(r.ok ? formatChapterCopy(copy.sent, {
|
|
704
|
+
template,
|
|
705
|
+
address: r.to,
|
|
706
|
+
redirected: r.redirected ? ` (${copy.redirected})` : ""
|
|
707
|
+
}) : copy.testFailed);
|
|
701
708
|
log.refresh();
|
|
702
709
|
});
|
|
703
710
|
return /* @__PURE__ */ jsxs7(AdminPage, { children: [
|
|
704
711
|
/* @__PURE__ */ jsxs7(
|
|
705
712
|
Panel,
|
|
706
713
|
{
|
|
707
|
-
title:
|
|
714
|
+
title: copy.delivery,
|
|
708
715
|
actions: /* @__PURE__ */ jsxs7("span", { className: "muted", children: [
|
|
709
716
|
data.envName,
|
|
710
717
|
" \xB7 ",
|
|
@@ -712,22 +719,22 @@ function EmailBody({ ctx }) {
|
|
|
712
719
|
data.fromEmail ? ` \xB7 ${data.fromEmail}` : ""
|
|
713
720
|
] }),
|
|
714
721
|
children: [
|
|
715
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
716
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
717
|
-
/* @__PURE__ */ jsx7(Field2, { label:
|
|
722
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.notificationAddress, children: /* @__PURE__ */ jsx7(Input2, { value: data.notificationEmail, onInput: (e) => edit({ notificationEmail: e.target.value }) }) }),
|
|
723
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.replyTo, children: /* @__PURE__ */ jsx7(Input2, { value: data.replyTo, onInput: (e) => edit({ replyTo: e.target.value }) }) }),
|
|
724
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.debugInbox, hint: copy.debugInboxHint, children: /* @__PURE__ */ jsx7(Input2, { value: data.debugEmail, onInput: (e) => edit({ debugEmail: e.target.value }) }) })
|
|
718
725
|
]
|
|
719
726
|
}
|
|
720
727
|
),
|
|
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:
|
|
728
|
+
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: [
|
|
729
|
+
/* @__PURE__ */ jsx7(Checkbox2, { label: copy.enabled, checked: t.enabled, onChange: (e) => editTemplate(key, { enabled: e.target.checked }) }),
|
|
730
|
+
/* @__PURE__ */ jsx7(Field2, { label: copy.subject, children: /* @__PURE__ */ jsx7(Input2, { value: t.subject, onInput: (e) => editTemplate(key, { subject: e.target.value }) }) }),
|
|
731
|
+
/* @__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
732
|
] }, key)),
|
|
726
733
|
/* @__PURE__ */ jsxs7("div", { style: { display: "flex", gap: 12, alignItems: "center" }, children: [
|
|
727
|
-
/* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children:
|
|
734
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !draft || busy === "save", onClick: () => void save(), children: copy.save }),
|
|
728
735
|
note ? /* @__PURE__ */ jsx7("span", { className: "muted", children: note }) : null
|
|
729
736
|
] }),
|
|
730
|
-
/* @__PURE__ */ jsx7(Panel, { title:
|
|
737
|
+
/* @__PURE__ */ jsx7(Panel, { title: copy.sendLog, children: /* @__PURE__ */ jsx7(DataTable2, { rows: log.data?.sends ?? [], columns: logColumns(copy), rowKey: (r) => r.id }) })
|
|
731
738
|
] });
|
|
732
739
|
}
|
|
733
740
|
function emailSection(options = {}) {
|
|
@@ -743,6 +750,7 @@ import { useEffect as useEffect4, useState as useState6 } from "preact/hooks";
|
|
|
743
750
|
import { Button as Button3, Field as Field3, Select } from "@odla-ai/ui/components";
|
|
744
751
|
import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
745
752
|
function RecordActions({ getToken, record, roles, onChanged }) {
|
|
753
|
+
const copy = useChapterCopy().admin.records;
|
|
746
754
|
const userId = typeof record.clerkUserId === "string" ? record.clerkUserId : null;
|
|
747
755
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
748
756
|
const [access, setAccess] = useState6(null);
|
|
@@ -772,13 +780,13 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
772
780
|
setBusy(null);
|
|
773
781
|
}
|
|
774
782
|
};
|
|
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" }),
|
|
783
|
+
const setRole = (role) => act("role", () => adminFetch(getToken, "/api/admin/people/role", { method: "POST", body: JSON.stringify({ userId, role }) }), formatChapterCopy(copy.roleSet, { role }));
|
|
784
|
+
const approve = () => act("approve", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/approve`, { method: "POST" }), copy.approved);
|
|
785
|
+
const refund = () => act("refund", () => adminFetch(getToken, `/api/admin/applications/${applicationId}/refund`, { method: "POST" }), copy.refunded);
|
|
778
786
|
if (!userId && !applicationId) return null;
|
|
779
787
|
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:
|
|
788
|
+
/* @__PURE__ */ jsx8("h3", { style: { margin: 0, fontSize: 16 }, children: copy.lifecycle }),
|
|
789
|
+
userId ? /* @__PURE__ */ jsx8(Field3, { label: copy.role, hint: access?.superAdmin ? copy.superAdminHint : void 0, children: /* @__PURE__ */ jsx8(
|
|
782
790
|
Select,
|
|
783
791
|
{
|
|
784
792
|
value: access?.role ?? "",
|
|
@@ -786,10 +794,10 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
786
794
|
options: roles.map((r) => ({ value: r, label: r })),
|
|
787
795
|
onChange: (e) => void setRole(e.target.value)
|
|
788
796
|
}
|
|
789
|
-
) }) : /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children:
|
|
797
|
+
) }) : /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children: copy.noAccount }),
|
|
790
798
|
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:
|
|
799
|
+
/* @__PURE__ */ jsx8(Button3, { variant: "secondary", mini: true, disabled: busy === "approve", onClick: () => void approve(), children: copy.approve }),
|
|
800
|
+
/* @__PURE__ */ jsx8(Button3, { variant: "danger", mini: true, disabled: busy === "refund", onClick: () => void refund(), children: copy.refund })
|
|
793
801
|
] }) : null,
|
|
794
802
|
note ? /* @__PURE__ */ jsx8("p", { className: "muted", style: { margin: 0 }, children: note }) : null
|
|
795
803
|
] });
|
|
@@ -799,6 +807,7 @@ function RecordActions({ getToken, record, roles, onChanged }) {
|
|
|
799
807
|
import { useState as useState7 } from "preact/hooks";
|
|
800
808
|
import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
801
809
|
function NetworkShareActions(props) {
|
|
810
|
+
const copy = useChapterCopy().admin.network;
|
|
802
811
|
const { recordId, recordType, getToken } = props;
|
|
803
812
|
const targets = useAdminResource(getToken, "/api/admin/network/targets");
|
|
804
813
|
const [busy, setBusy] = useState7(null);
|
|
@@ -814,19 +823,19 @@ function NetworkShareActions(props) {
|
|
|
814
823
|
body: JSON.stringify({ recordId, targetIds: [target.id] })
|
|
815
824
|
});
|
|
816
825
|
const delivery = result.results[0];
|
|
817
|
-
setMessage(delivery?.ok ?
|
|
826
|
+
setMessage(delivery?.ok ? formatChapterCopy(copy.shared, { target: target.name }) : delivery?.error ?? formatChapterCopy(copy.shareFailed, { target: target.name }));
|
|
818
827
|
} catch (err) {
|
|
819
|
-
setMessage(err instanceof Error ? err.message :
|
|
828
|
+
setMessage(err instanceof Error ? err.message : copy.deliveryFailed);
|
|
820
829
|
} finally {
|
|
821
830
|
setBusy(null);
|
|
822
831
|
}
|
|
823
832
|
};
|
|
824
833
|
return /* @__PURE__ */ jsxs9("section", { className: "panel", style: { marginTop: 14, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
825
834
|
/* @__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:
|
|
835
|
+
/* @__PURE__ */ jsx9("h3", { style: { margin: 0, fontSize: 16 }, children: copy.title }),
|
|
836
|
+
/* @__PURE__ */ jsx9("p", { className: "muted", style: { margin: "4px 0 0", fontSize: 13 }, children: copy.allowlistDescription })
|
|
828
837
|
] }),
|
|
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 ?
|
|
838
|
+
/* @__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
839
|
message ? /* @__PURE__ */ jsx9("p", { role: "status", style: { margin: 0, fontSize: 13 }, children: message }) : null
|
|
831
840
|
] });
|
|
832
841
|
}
|
|
@@ -877,6 +886,7 @@ import { useCallback as useCallback4, useEffect as useEffect6, useState as useSt
|
|
|
877
886
|
import { Button as Button4, Field as Field4, Select as Select2 } from "@odla-ai/ui/components";
|
|
878
887
|
import { jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
879
888
|
function RecordCommsPanel(props) {
|
|
889
|
+
const copy = useChapterCopy().admin.records;
|
|
880
890
|
const { client, recordId, getToken } = props;
|
|
881
891
|
const config = useAdminResource(getToken, "/api/admin/group/email");
|
|
882
892
|
const templates = Object.entries(config.data?.emailTemplates ?? {}).filter(([, value]) => value.enabled);
|
|
@@ -889,32 +899,33 @@ function RecordCommsPanel(props) {
|
|
|
889
899
|
setNote(null);
|
|
890
900
|
try {
|
|
891
901
|
const result = await client.sendEmail(recordId, { templateId: template });
|
|
892
|
-
setNote(result.sent ?
|
|
902
|
+
setNote(result.sent ? copy.messageSent : result.reason ?? copy.messageNotSent);
|
|
893
903
|
} catch (failure) {
|
|
894
904
|
setNote(failure instanceof Error ? failure.message : String(failure));
|
|
895
905
|
} finally {
|
|
896
906
|
setBusy(false);
|
|
897
907
|
}
|
|
898
908
|
};
|
|
899
|
-
if (config.loading) return /* @__PURE__ */ jsx11("p", { className: "muted", children:
|
|
909
|
+
if (config.loading) return /* @__PURE__ */ jsx11("p", { className: "muted", children: copy.loadingTemplates });
|
|
900
910
|
if (config.error) return /* @__PURE__ */ jsx11("p", { role: "alert", className: "muted", children: config.error });
|
|
901
911
|
return /* @__PURE__ */ jsxs10("div", { style: { display: "grid", gap: 14, maxWidth: 560 }, children: [
|
|
902
|
-
/* @__PURE__ */ jsx11(Field4, { label:
|
|
912
|
+
/* @__PURE__ */ jsx11(Field4, { label: copy.template, children: /* @__PURE__ */ jsx11(
|
|
903
913
|
Select2,
|
|
904
914
|
{
|
|
905
915
|
value: template,
|
|
906
916
|
onChange: (event) => setTemplate(event.currentTarget.value),
|
|
907
917
|
options: [
|
|
908
|
-
{ value: "", label:
|
|
918
|
+
{ value: "", label: copy.chooseTemplate },
|
|
909
919
|
...templates.map(([id, value]) => ({ value: id, label: value.subject || id }))
|
|
910
920
|
]
|
|
911
921
|
}
|
|
912
922
|
) }),
|
|
913
|
-
/* @__PURE__ */ jsx11("div", { children: /* @__PURE__ */ jsx11(Button4, { disabled: !template || busy, onClick: () => void send(), children: busy ?
|
|
923
|
+
/* @__PURE__ */ jsx11("div", { children: /* @__PURE__ */ jsx11(Button4, { disabled: !template || busy, onClick: () => void send(), children: busy ? copy.sending : copy.send }) }),
|
|
914
924
|
note ? /* @__PURE__ */ jsx11("p", { role: "status", className: "muted", children: note }) : null
|
|
915
925
|
] });
|
|
916
926
|
}
|
|
917
927
|
function RecordCommsHistory(props) {
|
|
928
|
+
const copy = useChapterCopy().admin.records;
|
|
918
929
|
const { client, recordId } = props;
|
|
919
930
|
const [rows, setRows] = useState9([]);
|
|
920
931
|
const [error, setError] = useState9(null);
|
|
@@ -929,9 +940,9 @@ function RecordCommsHistory(props) {
|
|
|
929
940
|
}, [client, recordId]);
|
|
930
941
|
useEffect6(() => void load(), [load]);
|
|
931
942
|
if (error) return /* @__PURE__ */ jsx11("p", { role: "alert", className: "muted", children: error });
|
|
932
|
-
if (rows.length === 0) return /* @__PURE__ */ jsx11("p", { className: "muted", children:
|
|
943
|
+
if (rows.length === 0) return /* @__PURE__ */ jsx11("p", { className: "muted", children: copy.noMessages });
|
|
933
944
|
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"] ??
|
|
945
|
+
/* @__PURE__ */ jsx11("strong", { children: String(row["templateId"] ?? row["template"] ?? copy.message) }),
|
|
935
946
|
/* @__PURE__ */ jsxs10("div", { className: "muted", children: [
|
|
936
947
|
row["to"] ? String(row["to"]) : "",
|
|
937
948
|
row["sentAt"] ? ` \xB7 ${new Date(Number(row["sentAt"])).toLocaleString()}` : ""
|
|
@@ -944,6 +955,7 @@ import { useState as useState10 } from "preact/hooks";
|
|
|
944
955
|
import { Button as Button5 } from "@odla-ai/ui/components";
|
|
945
956
|
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
946
957
|
function RecordSchedulingPanel(props) {
|
|
958
|
+
const copy = useChapterCopy().admin.records;
|
|
947
959
|
const { getToken, applicationId } = props;
|
|
948
960
|
const resource = useAdminResource(
|
|
949
961
|
getToken,
|
|
@@ -955,32 +967,32 @@ function RecordSchedulingPanel(props) {
|
|
|
955
967
|
const run = async (meeting, action) => {
|
|
956
968
|
let body;
|
|
957
969
|
if (action === "reschedule") {
|
|
958
|
-
const input = globalThis.prompt?.(
|
|
970
|
+
const input = globalThis.prompt?.(copy.newStartPrompt);
|
|
959
971
|
if (!input) return;
|
|
960
972
|
const startAt = Date.parse(input);
|
|
961
973
|
if (!Number.isFinite(startAt)) {
|
|
962
|
-
setNote(
|
|
974
|
+
setNote(copy.parseFailed);
|
|
963
975
|
return;
|
|
964
976
|
}
|
|
965
977
|
body = JSON.stringify({ startAt });
|
|
966
|
-
} else if (!globalThis.confirm?.(
|
|
978
|
+
} else if (!globalThis.confirm?.(copy.cancelConfirm)) {
|
|
967
979
|
return;
|
|
968
980
|
}
|
|
969
981
|
setBusy(meeting.id);
|
|
970
982
|
try {
|
|
971
983
|
await adminFetch(getToken, `/api/admin/meetings/${meeting.id}/${action}`, { method: "POST", body });
|
|
972
984
|
resource.refresh();
|
|
973
|
-
setNote(action === "cancel" ?
|
|
985
|
+
setNote(action === "cancel" ? copy.callCancelled : copy.callRescheduled);
|
|
974
986
|
} catch (failure) {
|
|
975
987
|
setNote(failure instanceof Error ? failure.message : String(failure));
|
|
976
988
|
} finally {
|
|
977
989
|
setBusy(null);
|
|
978
990
|
}
|
|
979
991
|
};
|
|
980
|
-
if (!applicationId) return /* @__PURE__ */ jsx12("p", { className: "muted", children:
|
|
981
|
-
if (resource.loading) return /* @__PURE__ */ jsx12("p", { className: "muted", children:
|
|
992
|
+
if (!applicationId) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.noApplication });
|
|
993
|
+
if (resource.loading) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.loadingMeetings });
|
|
982
994
|
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:
|
|
995
|
+
if (meetings.length === 0) return /* @__PURE__ */ jsx12("p", { className: "muted", children: copy.noMeetings });
|
|
984
996
|
return /* @__PURE__ */ jsxs11("div", { style: { display: "grid", gap: 12 }, children: [
|
|
985
997
|
meetings.map((meeting) => /* @__PURE__ */ jsxs11("section", { className: "panel", children: [
|
|
986
998
|
/* @__PURE__ */ jsx12("strong", { children: new Date(meeting.startAt).toLocaleString() }),
|
|
@@ -990,11 +1002,11 @@ function RecordSchedulingPanel(props) {
|
|
|
990
1002
|
resource.data?.timezone
|
|
991
1003
|
] }),
|
|
992
1004
|
/* @__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:
|
|
1005
|
+
meeting.meetUrl ? /* @__PURE__ */ jsx12("a", { href: meeting.meetUrl, target: "_blank", rel: "noreferrer", children: copy.joinMeeting }) : null,
|
|
1006
|
+
meeting.htmlLink ? /* @__PURE__ */ jsx12("a", { href: meeting.htmlLink, target: "_blank", rel: "noreferrer", children: copy.openCalendar }) : null,
|
|
995
1007
|
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:
|
|
1008
|
+
/* @__PURE__ */ jsx12(Button5, { variant: "secondary", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "reschedule"), children: copy.reschedule }),
|
|
1009
|
+
/* @__PURE__ */ jsx12(Button5, { variant: "danger", mini: true, disabled: busy === meeting.id, onClick: () => void run(meeting, "cancel"), children: copy.cancel })
|
|
998
1010
|
] }) : null
|
|
999
1011
|
] })
|
|
1000
1012
|
] }, meeting.id)),
|
|
@@ -1004,12 +1016,12 @@ function RecordSchedulingPanel(props) {
|
|
|
1004
1016
|
|
|
1005
1017
|
// src/ui/admin-people-workspace.tsx
|
|
1006
1018
|
import { jsx as jsx13 } from "preact/jsx-runtime";
|
|
1007
|
-
function applicationLifecycleAdapter(ctx) {
|
|
1019
|
+
function applicationLifecycleAdapter(ctx, copy = DEFAULT_CHAPTER_COPY.admin.records) {
|
|
1008
1020
|
return {
|
|
1009
1021
|
transitionStage: async ({ record, to }) => {
|
|
1010
1022
|
const applicationId = typeof record.fields?.applicationId === "string" ? record.fields.applicationId : null;
|
|
1011
1023
|
if (!applicationId) {
|
|
1012
|
-
throw new Error(
|
|
1024
|
+
throw new Error(copy.workflowMissing);
|
|
1013
1025
|
}
|
|
1014
1026
|
const path = `/api/admin/applications/${encodeURIComponent(applicationId)}`;
|
|
1015
1027
|
if (to === "approved") {
|
|
@@ -1026,6 +1038,7 @@ function applicationLifecycleAdapter(ctx) {
|
|
|
1026
1038
|
};
|
|
1027
1039
|
}
|
|
1028
1040
|
function PeopleBody(props) {
|
|
1041
|
+
const copy = useChapterCopy().admin.records;
|
|
1029
1042
|
const {
|
|
1030
1043
|
crm,
|
|
1031
1044
|
client,
|
|
@@ -1045,7 +1058,7 @@ function PeopleBody(props) {
|
|
|
1045
1058
|
} = props;
|
|
1046
1059
|
const def = crm.type(type);
|
|
1047
1060
|
const recordId = ctx.route?.recordId ?? null;
|
|
1048
|
-
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx) : void 0;
|
|
1061
|
+
const builtInLifecycle = lifecycle ? applicationLifecycleAdapter(ctx, copy) : void 0;
|
|
1049
1062
|
const resolvedLifecycle = typeof lifecycleAdapter === "function" ? lifecycleAdapter(ctx) : lifecycleAdapter ?? builtInLifecycle;
|
|
1050
1063
|
const workspaceClient = client;
|
|
1051
1064
|
return /* @__PURE__ */ jsx13(AdminPage, { children: /* @__PURE__ */ jsx13(
|
|
@@ -1081,35 +1094,35 @@ function PeopleBody(props) {
|
|
|
1081
1094
|
if (def.facets?.email !== false) {
|
|
1082
1095
|
tabs.push({
|
|
1083
1096
|
id: "comms",
|
|
1084
|
-
label:
|
|
1097
|
+
label: copy.comms,
|
|
1085
1098
|
panel: /* @__PURE__ */ jsx13(RecordCommsPanel, { client, recordId: record.id, getToken: ctx.getToken })
|
|
1086
1099
|
});
|
|
1087
1100
|
tabs.push({
|
|
1088
1101
|
id: "history",
|
|
1089
|
-
label:
|
|
1102
|
+
label: copy.commsHistory,
|
|
1090
1103
|
panel: /* @__PURE__ */ jsx13(RecordCommsHistory, { client, recordId: record.id })
|
|
1091
1104
|
});
|
|
1092
1105
|
}
|
|
1093
1106
|
if (lifecycle) {
|
|
1094
1107
|
tabs.push({
|
|
1095
1108
|
id: "scheduling",
|
|
1096
|
-
label:
|
|
1109
|
+
label: copy.scheduling,
|
|
1097
1110
|
panel: /* @__PURE__ */ jsx13(RecordSchedulingPanel, { getToken: ctx.getToken, applicationId })
|
|
1098
1111
|
});
|
|
1099
|
-
tabs.push({ id: "billing", label:
|
|
1112
|
+
tabs.push({ id: "billing", label: copy.billing, panel: /* @__PURE__ */ jsx13(BillingCard, { record }) });
|
|
1100
1113
|
} else {
|
|
1101
1114
|
add("billing");
|
|
1102
1115
|
}
|
|
1103
1116
|
tabs.push({
|
|
1104
1117
|
id: "notes",
|
|
1105
|
-
label:
|
|
1118
|
+
label: copy.notes,
|
|
1106
1119
|
panel: /* @__PURE__ */ jsx13(RecordActivityPanel, { client, recordId: record.id })
|
|
1107
1120
|
});
|
|
1108
1121
|
add("connections");
|
|
1109
1122
|
if (lifecycle) {
|
|
1110
1123
|
tabs.push({
|
|
1111
1124
|
id: "access",
|
|
1112
|
-
label:
|
|
1125
|
+
label: copy.access,
|
|
1113
1126
|
panel: /* @__PURE__ */ jsx13(
|
|
1114
1127
|
RecordActions,
|
|
1115
1128
|
{
|
|
@@ -1128,7 +1141,7 @@ function PeopleBody(props) {
|
|
|
1128
1141
|
if (networkSharing) {
|
|
1129
1142
|
tabs.push({
|
|
1130
1143
|
id: "sharing",
|
|
1131
|
-
label:
|
|
1144
|
+
label: copy.sharing,
|
|
1132
1145
|
panel: /* @__PURE__ */ jsx13(NetworkShareActions, { recordId: record.id, recordType: record.type, getToken: ctx.getToken })
|
|
1133
1146
|
});
|
|
1134
1147
|
}
|
|
@@ -1245,26 +1258,27 @@ var adminViewContext = (ctx, viewId, active) => {
|
|
|
1245
1258
|
href: (target) => ctx.href(targetFromView(target))
|
|
1246
1259
|
};
|
|
1247
1260
|
};
|
|
1248
|
-
function DashboardWorkspace({ ctx }) {
|
|
1261
|
+
function DashboardWorkspace({ chapter, ctx }) {
|
|
1262
|
+
const copy = chapter.copy.admin;
|
|
1249
1263
|
const active = ctx.route.viewId === "billing" ? "billing" : "overview";
|
|
1250
|
-
const overview = dashboardSection({ id: "overview", label:
|
|
1251
|
-
const billing = billingSection();
|
|
1264
|
+
const overview = dashboardSection({ id: "overview", label: copy.workspaces.overview });
|
|
1265
|
+
const billing = billingSection({ label: copy.workspaces.billing });
|
|
1252
1266
|
return /* @__PURE__ */ jsx15(
|
|
1253
1267
|
Tabs2,
|
|
1254
1268
|
{
|
|
1255
|
-
ariaLabel:
|
|
1269
|
+
ariaLabel: copy.workspaces.dashboardViewsLabel,
|
|
1256
1270
|
value: active,
|
|
1257
1271
|
mount: "active",
|
|
1258
1272
|
items: [
|
|
1259
1273
|
{
|
|
1260
1274
|
value: "overview",
|
|
1261
|
-
label:
|
|
1275
|
+
label: copy.workspaces.overview,
|
|
1262
1276
|
href: ctx.href({ viewId: "overview", recordId: null, detailTab: null }),
|
|
1263
1277
|
panel: overview.render(adminViewContext(ctx, "overview", active === "overview"))
|
|
1264
1278
|
},
|
|
1265
1279
|
{
|
|
1266
1280
|
value: "billing",
|
|
1267
|
-
label:
|
|
1281
|
+
label: copy.workspaces.billing,
|
|
1268
1282
|
href: ctx.href({ viewId: "billing", recordId: null, detailTab: null }),
|
|
1269
1283
|
panel: billing.render(adminViewContext(ctx, "billing", active === "billing"))
|
|
1270
1284
|
}
|
|
@@ -1272,26 +1286,27 @@ function DashboardWorkspace({ ctx }) {
|
|
|
1272
1286
|
}
|
|
1273
1287
|
);
|
|
1274
1288
|
}
|
|
1275
|
-
function SettingsWorkspace({ ctx }) {
|
|
1289
|
+
function SettingsWorkspace({ chapter, ctx }) {
|
|
1290
|
+
const copy = chapter.copy.admin;
|
|
1276
1291
|
const active = ctx.route.viewId === "email" ? "email" : "calendar";
|
|
1277
|
-
const calendar = availabilitySection({ id: "calendar", label:
|
|
1278
|
-
const email = emailSection();
|
|
1292
|
+
const calendar = availabilitySection({ id: "calendar", label: copy.workspaces.calendar });
|
|
1293
|
+
const email = emailSection({ label: copy.workspaces.email });
|
|
1279
1294
|
return /* @__PURE__ */ jsx15(
|
|
1280
1295
|
Tabs2,
|
|
1281
1296
|
{
|
|
1282
|
-
ariaLabel:
|
|
1297
|
+
ariaLabel: copy.workspaces.settingsViewsLabel,
|
|
1283
1298
|
value: active,
|
|
1284
1299
|
mount: "active",
|
|
1285
1300
|
items: [
|
|
1286
1301
|
{
|
|
1287
1302
|
value: "calendar",
|
|
1288
|
-
label:
|
|
1303
|
+
label: copy.workspaces.calendar,
|
|
1289
1304
|
href: ctx.href({ viewId: "calendar", recordId: null, detailTab: null }),
|
|
1290
1305
|
panel: calendar.render(adminViewContext(ctx, "calendar", active === "calendar"))
|
|
1291
1306
|
},
|
|
1292
1307
|
{
|
|
1293
1308
|
value: "email",
|
|
1294
|
-
label:
|
|
1309
|
+
label: copy.workspaces.email,
|
|
1295
1310
|
href: ctx.href({ viewId: "email", recordId: null, detailTab: null }),
|
|
1296
1311
|
panel: email.render(adminViewContext(ctx, "email", active === "email"))
|
|
1297
1312
|
}
|
|
@@ -1307,7 +1322,7 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
1307
1322
|
return /* @__PURE__ */ jsx15(
|
|
1308
1323
|
Tabs2,
|
|
1309
1324
|
{
|
|
1310
|
-
ariaLabel:
|
|
1325
|
+
ariaLabel: chapter.copy.admin.workspaces.collectionsLabel,
|
|
1311
1326
|
value: active,
|
|
1312
1327
|
variant: "pill",
|
|
1313
1328
|
mount: "active",
|
|
@@ -1330,9 +1345,10 @@ function PeopleWorkspace({ chapter, ctx }) {
|
|
|
1330
1345
|
);
|
|
1331
1346
|
}
|
|
1332
1347
|
function defaultAdminWorkspaces(chapter) {
|
|
1348
|
+
const copy = chapter.copy.admin;
|
|
1333
1349
|
const people = {
|
|
1334
1350
|
id: "people",
|
|
1335
|
-
label:
|
|
1351
|
+
label: copy.workspaces.people,
|
|
1336
1352
|
defaultViewId: Object.keys(chapter.crm.config.types)[0] ?? "person",
|
|
1337
1353
|
render: (ctx) => /* @__PURE__ */ jsx15(PeopleWorkspace, { chapter, ctx })
|
|
1338
1354
|
};
|
|
@@ -1340,37 +1356,39 @@ function defaultAdminWorkspaces(chapter) {
|
|
|
1340
1356
|
return [
|
|
1341
1357
|
{
|
|
1342
1358
|
id: "dashboard",
|
|
1343
|
-
label:
|
|
1359
|
+
label: copy.workspaces.dashboard,
|
|
1344
1360
|
defaultViewId: "overview",
|
|
1345
|
-
render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { ctx })
|
|
1361
|
+
render: (ctx) => /* @__PURE__ */ jsx15(DashboardWorkspace, { chapter, ctx })
|
|
1346
1362
|
},
|
|
1347
1363
|
people,
|
|
1348
1364
|
{
|
|
1349
1365
|
id: "settings",
|
|
1350
|
-
label:
|
|
1366
|
+
label: copy.workspaces.settings,
|
|
1351
1367
|
defaultViewId: "calendar",
|
|
1352
|
-
render: (ctx) => /* @__PURE__ */ jsx15(SettingsWorkspace, { ctx })
|
|
1368
|
+
render: (ctx) => /* @__PURE__ */ jsx15(SettingsWorkspace, { chapter, ctx })
|
|
1353
1369
|
}
|
|
1354
1370
|
];
|
|
1355
1371
|
}
|
|
1356
1372
|
|
|
1357
1373
|
// src/ui/admin-frame.tsx
|
|
1358
|
-
import { ThemeScope } from "@odla-ai/ui/components";
|
|
1374
|
+
import { ThemeScope as ThemeScope2 } from "@odla-ai/ui/components";
|
|
1359
1375
|
import { jsx as jsx16, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
1360
1376
|
function AdminTheme(props) {
|
|
1361
1377
|
const brand = props.chapter?.brand;
|
|
1362
|
-
return /* @__PURE__ */ jsxs12(
|
|
1363
|
-
|
|
1378
|
+
return /* @__PURE__ */ jsx16(ChapterCopyProvider, { copy: props.chapter?.copy, children: /* @__PURE__ */ jsxs12(
|
|
1379
|
+
ThemeScope2,
|
|
1364
1380
|
{
|
|
1381
|
+
ref: props.scopeRef,
|
|
1365
1382
|
"data-chapter-admin": "",
|
|
1366
1383
|
theme: brand?.theme,
|
|
1384
|
+
accent: brand?.accent,
|
|
1367
1385
|
colorScheme: brand?.colorScheme ?? "light",
|
|
1368
1386
|
children: [
|
|
1369
1387
|
/* @__PURE__ */ jsx16(BrandStyle, { brand, selector: "[data-chapter-admin]" }),
|
|
1370
1388
|
props.children
|
|
1371
1389
|
]
|
|
1372
1390
|
}
|
|
1373
|
-
);
|
|
1391
|
+
) });
|
|
1374
1392
|
}
|
|
1375
1393
|
function signInRedirect(basePath, workspaces) {
|
|
1376
1394
|
if (typeof window === "undefined") return basePath;
|
|
@@ -1385,6 +1403,17 @@ function signInRedirect(basePath, workspaces) {
|
|
|
1385
1403
|
return adminRouteHref(current, basePath, route, "query");
|
|
1386
1404
|
}
|
|
1387
1405
|
|
|
1406
|
+
// src/ui/admin-appearance.ts
|
|
1407
|
+
import { useEffect as useEffect7, useState as useState11 } from "preact/hooks";
|
|
1408
|
+
import { clerkAppearanceFromTokens } from "@odla-ai/auth-clerk";
|
|
1409
|
+
function useScopedClerkAppearance(scope) {
|
|
1410
|
+
const [appearance, setAppearance] = useState11();
|
|
1411
|
+
useEffect7(() => {
|
|
1412
|
+
setAppearance(clerkAppearanceFromTokens(scope.current ?? void 0));
|
|
1413
|
+
}, [scope]);
|
|
1414
|
+
return appearance;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1388
1417
|
// src/ui/admin-auth.ts
|
|
1389
1418
|
var apiUrl = (apiBase, path) => `${apiBase.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
1390
1419
|
var DEFAULT_ADMIN_AUTH = {};
|
|
@@ -1412,6 +1441,7 @@ async function loadChapterAdminUser(adapter, apiBase, getToken) {
|
|
|
1412
1441
|
// src/ui/admin.tsx
|
|
1413
1442
|
import { jsx as jsx17, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
1414
1443
|
function Authed(props) {
|
|
1444
|
+
const copy = useChapterCopy().admin;
|
|
1415
1445
|
const {
|
|
1416
1446
|
workspaces,
|
|
1417
1447
|
basePath,
|
|
@@ -1436,8 +1466,8 @@ function Authed(props) {
|
|
|
1436
1466
|
}),
|
|
1437
1467
|
[getToken, crmBasePath]
|
|
1438
1468
|
);
|
|
1439
|
-
const [state, setState] =
|
|
1440
|
-
|
|
1469
|
+
const [state, setState] = useState12({ status: "checking" });
|
|
1470
|
+
useEffect8(() => {
|
|
1441
1471
|
let live = true;
|
|
1442
1472
|
void (async () => {
|
|
1443
1473
|
try {
|
|
@@ -1456,15 +1486,14 @@ function Authed(props) {
|
|
|
1456
1486
|
live = false;
|
|
1457
1487
|
};
|
|
1458
1488
|
}, [getToken, apiBase, auth]);
|
|
1459
|
-
if (state.status === "checking") return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1489
|
+
if (state.status === "checking") return /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.auth.checking }) });
|
|
1460
1490
|
if (state.status === "denied") {
|
|
1461
1491
|
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" })
|
|
1492
|
+
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: copy.auth.notAuthorized }),
|
|
1493
|
+
/* @__PURE__ */ jsx17("p", { style: S.muted, children: formatChapterCopy(copy.auth.accountNotAuthorized, {
|
|
1494
|
+
account: state.email ?? copy.auth.thisAccount
|
|
1495
|
+
}) }),
|
|
1496
|
+
/* @__PURE__ */ jsx17("button", { className: "btn secondary", onClick: () => signOut(), children: copy.auth.signOut })
|
|
1468
1497
|
] }) });
|
|
1469
1498
|
}
|
|
1470
1499
|
return /* @__PURE__ */ jsx17(
|
|
@@ -1487,6 +1516,7 @@ function Authed(props) {
|
|
|
1487
1516
|
);
|
|
1488
1517
|
}
|
|
1489
1518
|
function ChapterAdmin(props) {
|
|
1519
|
+
const copy = props.chapter?.copy ?? DEFAULT_CHAPTER_COPY;
|
|
1490
1520
|
const defaults = useMemo(
|
|
1491
1521
|
() => props.chapter ? defaultAdminWorkspaces(props.chapter) : [],
|
|
1492
1522
|
[props.chapter]
|
|
@@ -1497,7 +1527,7 @@ function ChapterAdmin(props) {
|
|
|
1497
1527
|
}, [props.workspaces, props.sections, defaults]);
|
|
1498
1528
|
const basePath = props.basePath ?? "/admin";
|
|
1499
1529
|
const brand = props.brand ?? {
|
|
1500
|
-
name: props.chapter?.name ??
|
|
1530
|
+
name: props.chapter?.name ?? copy.admin.shell.adminName,
|
|
1501
1531
|
badge: props.chapter?.brand.badge,
|
|
1502
1532
|
wordmark: props.chapter?.brand.wordmark
|
|
1503
1533
|
};
|
|
@@ -1507,8 +1537,10 @@ function ChapterAdmin(props) {
|
|
|
1507
1537
|
const chrome = props.chrome ?? "embedded";
|
|
1508
1538
|
const auth = props.auth ?? DEFAULT_ADMIN_AUTH;
|
|
1509
1539
|
const redirect = signInRedirect(basePath, workspaces);
|
|
1510
|
-
const [publishableKey, setPublishableKey] =
|
|
1511
|
-
|
|
1540
|
+
const [publishableKey, setPublishableKey] = useState12(void 0);
|
|
1541
|
+
const scopeRef = useRef(null);
|
|
1542
|
+
const appearance = useScopedClerkAppearance(scopeRef);
|
|
1543
|
+
useEffect8(() => {
|
|
1512
1544
|
let live = true;
|
|
1513
1545
|
void loadChapterAdminConfig(auth, apiBase).then((config) => live && setPublishableKey(config.publishableKey ?? null)).catch(() => live && setPublishableKey(null));
|
|
1514
1546
|
return () => {
|
|
@@ -1516,24 +1548,24 @@ function ChapterAdmin(props) {
|
|
|
1516
1548
|
};
|
|
1517
1549
|
}, [apiBase, auth]);
|
|
1518
1550
|
let content;
|
|
1519
|
-
if (publishableKey === void 0) {
|
|
1520
|
-
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1551
|
+
if (publishableKey === void 0 || publishableKey && !appearance) {
|
|
1552
|
+
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.loading }) });
|
|
1521
1553
|
} else if (!publishableKey) {
|
|
1522
1554
|
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:
|
|
1555
|
+
/* @__PURE__ */ jsx17("h2", { style: { marginTop: 0 }, children: copy.admin.auth.signInNotConfigured }),
|
|
1556
|
+
/* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.missingPublishableKey })
|
|
1525
1557
|
] }) });
|
|
1526
1558
|
} else if (workspaces.length === 0) {
|
|
1527
|
-
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children:
|
|
1559
|
+
content = /* @__PURE__ */ jsx17(Gate, { brand, children: /* @__PURE__ */ jsx17("p", { style: S.muted, children: copy.admin.auth.noWorkspaces }) });
|
|
1528
1560
|
} else {
|
|
1529
1561
|
content = /* @__PURE__ */ jsxs13(
|
|
1530
1562
|
ClerkGate,
|
|
1531
1563
|
{
|
|
1532
1564
|
publishableKey,
|
|
1533
|
-
appearance
|
|
1565
|
+
appearance,
|
|
1534
1566
|
afterSignOutUrl: "/",
|
|
1535
1567
|
children: [
|
|
1536
|
-
/* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline:
|
|
1568
|
+
/* @__PURE__ */ jsx17(SignedOut, { children: /* @__PURE__ */ jsx17(Gate, { brand, tagline: copy.admin.auth.signInTagline, children: /* @__PURE__ */ jsx17(
|
|
1537
1569
|
SignIn,
|
|
1538
1570
|
{
|
|
1539
1571
|
routing: "hash",
|
|
@@ -1561,7 +1593,7 @@ function ChapterAdmin(props) {
|
|
|
1561
1593
|
}
|
|
1562
1594
|
);
|
|
1563
1595
|
}
|
|
1564
|
-
return /* @__PURE__ */ jsx17(AdminTheme, { chapter: props.chapter, children: content });
|
|
1596
|
+
return /* @__PURE__ */ jsx17(AdminTheme, { chapter: props.chapter, scopeRef, children: content });
|
|
1565
1597
|
}
|
|
1566
1598
|
|
|
1567
1599
|
// src/ui/admin-defaults.ts
|
|
@@ -1570,17 +1602,18 @@ function defaultAdminSections(chapter) {
|
|
|
1570
1602
|
}
|
|
1571
1603
|
|
|
1572
1604
|
// src/ui/admin-meetings.tsx
|
|
1573
|
-
import { useState as
|
|
1605
|
+
import { useState as useState13 } from "preact/hooks";
|
|
1574
1606
|
import { Button as Button6 } from "@odla-ai/ui/components";
|
|
1575
1607
|
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
1576
1608
|
var when2 = (t) => new Date(t).toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
1577
1609
|
function MeetingsBody({ ctx }) {
|
|
1610
|
+
const copy = useChapterCopy().admin.meetings;
|
|
1578
1611
|
const { data, loading, error, refresh } = useAdminResource(ctx.getToken, "/api/admin/meetings?all=1");
|
|
1579
|
-
const [busy, setBusy] =
|
|
1580
|
-
const [note, setNote] =
|
|
1581
|
-
if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children:
|
|
1612
|
+
const [busy, setBusy] = useState13(null);
|
|
1613
|
+
const [note, setNote] = useState13(null);
|
|
1614
|
+
if (loading) return /* @__PURE__ */ jsx18(AdminNote, { children: copy.loading });
|
|
1582
1615
|
if (error || !data) return /* @__PURE__ */ jsxs14(AdminNote, { children: [
|
|
1583
|
-
|
|
1616
|
+
copy.loadFailed,
|
|
1584
1617
|
error ? `: ${error}` : "",
|
|
1585
1618
|
"."
|
|
1586
1619
|
] });
|
|
@@ -1597,37 +1630,39 @@ function MeetingsBody({ ctx }) {
|
|
|
1597
1630
|
}
|
|
1598
1631
|
};
|
|
1599
1632
|
const cancel = (id) => {
|
|
1600
|
-
if (!globalThis.confirm?.(
|
|
1633
|
+
if (!globalThis.confirm?.(copy.cancelConfirm)) return;
|
|
1601
1634
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/cancel`, { method: "POST" }));
|
|
1602
1635
|
};
|
|
1603
1636
|
const reschedule = (id) => {
|
|
1604
|
-
const input = globalThis.prompt?.(
|
|
1637
|
+
const input = globalThis.prompt?.(copy.newStartPrompt);
|
|
1605
1638
|
if (!input) return;
|
|
1606
1639
|
const startAt = Date.parse(input);
|
|
1607
1640
|
if (!Number.isFinite(startAt)) {
|
|
1608
|
-
setNote(
|
|
1641
|
+
setNote(copy.parseFailed);
|
|
1609
1642
|
return;
|
|
1610
1643
|
}
|
|
1611
1644
|
void run(id, () => adminFetch(ctx.getToken, `/api/admin/meetings/${id}/reschedule`, { method: "POST", body: JSON.stringify({ startAt }) }));
|
|
1612
1645
|
};
|
|
1613
1646
|
return /* @__PURE__ */ jsx18(AdminPage, { children: /* @__PURE__ */ jsx18(Panel, { title: /* @__PURE__ */ jsxs14(Fragment4, { children: [
|
|
1614
|
-
|
|
1647
|
+
copy.agenda,
|
|
1648
|
+
" ",
|
|
1615
1649
|
/* @__PURE__ */ jsxs14("span", { className: "muted", style: { fontWeight: 400 }, children: [
|
|
1616
1650
|
"\xB7 ",
|
|
1617
1651
|
data.timezone
|
|
1618
1652
|
] })
|
|
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:
|
|
1653
|
+
] }), 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
1654
|
/* @__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}` :
|
|
1655
|
+
/* @__PURE__ */ jsx18("span", { style: { flex: 1 }, children: m.applicant ? `${m.applicant.name} \xB7 ${m.applicant.email}` : copy.unknown }),
|
|
1622
1656
|
/* @__PURE__ */ jsx18("span", { className: "badge", children: m.status }),
|
|
1623
1657
|
m.drift && m.drift !== "none" ? /* @__PURE__ */ jsxs14("span", { className: "badge", children: [
|
|
1624
|
-
|
|
1658
|
+
copy.drift,
|
|
1659
|
+
": ",
|
|
1625
1660
|
m.drift
|
|
1626
1661
|
] }) : null,
|
|
1627
|
-
m.meetUrl ? /* @__PURE__ */ jsx18("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children:
|
|
1662
|
+
m.meetUrl ? /* @__PURE__ */ jsx18("a", { href: m.meetUrl, target: "_blank", rel: "noreferrer", children: copy.meet }) : null,
|
|
1628
1663
|
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:
|
|
1664
|
+
/* @__PURE__ */ jsx18(Button6, { variant: "secondary", mini: true, disabled: busy === m.id, onClick: () => reschedule(m.id), children: copy.reschedule }),
|
|
1665
|
+
/* @__PURE__ */ jsx18(Button6, { variant: "danger", mini: true, disabled: busy === m.id, onClick: () => cancel(m.id), children: copy.cancel })
|
|
1631
1666
|
] }) : null
|
|
1632
1667
|
] }, m.id)) }) }) });
|
|
1633
1668
|
}
|
|
@@ -1663,4 +1698,4 @@ export {
|
|
|
1663
1698
|
defaultAdminSections,
|
|
1664
1699
|
meetingsSection
|
|
1665
1700
|
};
|
|
1666
|
-
//# sourceMappingURL=chunk-
|
|
1701
|
+
//# sourceMappingURL=chunk-ZPAJL7KG.js.map
|