@fonderie/react-courier-admin-screens 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +122 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +127 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -41,11 +41,13 @@ function TemplateEditorScreen({
|
|
|
41
41
|
system = false,
|
|
42
42
|
onDeleted,
|
|
43
43
|
onAddLocale,
|
|
44
|
+
onSelectLocale,
|
|
44
45
|
uiLocale
|
|
45
46
|
}) {
|
|
46
47
|
const t = (0, import_react.useMemo)(() => (0, import_client.createAdminT)(uiLocale), [uiLocale]);
|
|
47
48
|
const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
|
|
48
|
-
const { saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
|
|
49
|
+
const { templates, saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
|
|
50
|
+
const siblings = (0, import_client.groupTemplatesByType)(templates).find((g) => g.type === type)?.entries ?? [];
|
|
49
51
|
const [isDeleting, setIsDeleting] = (0, import_react.useState)(false);
|
|
50
52
|
const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
|
|
51
53
|
const [isSaving, setIsSaving] = (0, import_react.useState)(false);
|
|
@@ -116,6 +118,10 @@ function TemplateEditorScreen({
|
|
|
116
118
|
setIsSaving(false);
|
|
117
119
|
}
|
|
118
120
|
};
|
|
121
|
+
const switchTo = (entry) => {
|
|
122
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
123
|
+
onSelectLocale?.(entry);
|
|
124
|
+
};
|
|
119
125
|
const handleDelete = async () => {
|
|
120
126
|
const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
|
|
121
127
|
if (!window.confirm(message)) return;
|
|
@@ -134,18 +140,40 @@ function TemplateEditorScreen({
|
|
|
134
140
|
if (error)
|
|
135
141
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
136
142
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
|
|
137
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
|
|
144
|
+
onSelectLocale || onAddLocale ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.tabBar, children: [
|
|
145
|
+
onSelectLocale ? (
|
|
146
|
+
// Each tab opens another stored row, so this is navigation, not an
|
|
147
|
+
// ARIA tablist: plain buttons, the open one marked aria-current.
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": t("templates.editor.locales"), style: styles.tabs, children: siblings.map((entry) => {
|
|
149
|
+
const selected = entry.locale === (locale ?? null);
|
|
150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
151
|
+
"button",
|
|
152
|
+
{
|
|
153
|
+
type: "button",
|
|
154
|
+
"aria-current": selected ? "true" : void 0,
|
|
155
|
+
onClick: () => selected ? void 0 : switchTo(entry),
|
|
156
|
+
style: selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive,
|
|
157
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
158
|
+
children: entry.locale ?? t("templates.defaultChip")
|
|
159
|
+
},
|
|
160
|
+
entry.locale ?? ""
|
|
161
|
+
);
|
|
162
|
+
}) })
|
|
163
|
+
) : null,
|
|
164
|
+
onAddLocale ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
140
165
|
"button",
|
|
141
166
|
{
|
|
142
167
|
type: "button",
|
|
143
168
|
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
144
|
-
onClick: () =>
|
|
169
|
+
onClick: () => {
|
|
170
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
171
|
+
onAddLocale(type, { locales: (0, import_client.missingTemplateLocales)(templates, type) });
|
|
172
|
+
},
|
|
145
173
|
children: t("templates.editor.addLocale")
|
|
146
174
|
}
|
|
147
175
|
) : null
|
|
148
|
-
] }),
|
|
176
|
+
] }) : null,
|
|
149
177
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
|
|
150
178
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? t("templates.defaultLocale") }),
|
|
151
179
|
" \xB7 v",
|
|
@@ -306,8 +334,38 @@ function TemplateEditorScreen({
|
|
|
306
334
|
] })
|
|
307
335
|
] });
|
|
308
336
|
}
|
|
337
|
+
var tab = {
|
|
338
|
+
height: 32,
|
|
339
|
+
padding: "0 10px",
|
|
340
|
+
background: "none",
|
|
341
|
+
border: "none",
|
|
342
|
+
borderBottom: "2px solid transparent",
|
|
343
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
344
|
+
fontSize: 13,
|
|
345
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
346
|
+
cursor: "pointer"
|
|
347
|
+
};
|
|
309
348
|
var styles = {
|
|
310
349
|
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
350
|
+
// One tab per locale of this email, underlined like a document's tabs.
|
|
351
|
+
tabBar: {
|
|
352
|
+
display: "flex",
|
|
353
|
+
alignItems: "flex-end",
|
|
354
|
+
gap: 12,
|
|
355
|
+
borderBottom: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
356
|
+
margin: "4px 0 12px",
|
|
357
|
+
paddingBottom: 6
|
|
358
|
+
},
|
|
359
|
+
tabs: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: -7 },
|
|
360
|
+
tab,
|
|
361
|
+
tabSelected: {
|
|
362
|
+
...tab,
|
|
363
|
+
color: "var(--fonderie-text,#171717)",
|
|
364
|
+
fontWeight: 600,
|
|
365
|
+
borderBottomColor: "var(--fonderie-text,#171717)",
|
|
366
|
+
cursor: "default"
|
|
367
|
+
},
|
|
368
|
+
tabInactive: { ...tab, opacity: 0.55, textDecoration: "line-through" },
|
|
311
369
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
312
370
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
313
371
|
split: {
|
|
@@ -486,6 +544,7 @@ function TemplateListScreen({
|
|
|
486
544
|
}) {
|
|
487
545
|
const t = (0, import_client2.createAdminT)(locale);
|
|
488
546
|
const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
|
|
547
|
+
const groups = (0, import_client2.groupTemplatesByType)(templates);
|
|
489
548
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
|
|
490
549
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
491
550
|
"div",
|
|
@@ -510,23 +569,39 @@ function TemplateListScreen({
|
|
|
510
569
|
}
|
|
511
570
|
),
|
|
512
571
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: t("templates.list.hint") }),
|
|
513
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children:
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
572
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { style: styles2.row, children: [
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
574
|
+
"button",
|
|
575
|
+
{
|
|
576
|
+
type: "button",
|
|
577
|
+
onClick: () => onSelectTemplate?.(group.primary),
|
|
578
|
+
style: styles2.rowButton,
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.type, children: group.type }),
|
|
581
|
+
group.system ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.builtIn, children: t("templates.builtInBadge") }) : null,
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: group.primary.active ? styles2.active : styles2.inactive, children: [
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.dot }),
|
|
584
|
+
group.primary.active ? t("common.status.active") : t("common.status.inactive")
|
|
585
|
+
] })
|
|
586
|
+
]
|
|
587
|
+
}
|
|
588
|
+
),
|
|
589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.chips, children: group.entries.map((entry) => {
|
|
590
|
+
const name = entry.locale ?? t("templates.defaultChip");
|
|
591
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
592
|
+
"button",
|
|
593
|
+
{
|
|
594
|
+
type: "button",
|
|
595
|
+
onClick: () => onSelectTemplate?.(entry),
|
|
596
|
+
style: entry.active ? styles2.chip : styles2.chipInactive,
|
|
597
|
+
"aria-label": t("templates.list.openLocale", { type: group.type, locale: name }),
|
|
598
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
599
|
+
children: name
|
|
600
|
+
},
|
|
601
|
+
entry.locale ?? ""
|
|
602
|
+
);
|
|
603
|
+
}) })
|
|
604
|
+
] }, group.type)) })
|
|
530
605
|
] });
|
|
531
606
|
}
|
|
532
607
|
var pill = {
|
|
@@ -540,6 +615,18 @@ var pill = {
|
|
|
540
615
|
lineHeight: 1.5,
|
|
541
616
|
minWidth: 64
|
|
542
617
|
};
|
|
618
|
+
var chip = {
|
|
619
|
+
height: 24,
|
|
620
|
+
boxSizing: "border-box",
|
|
621
|
+
padding: "0 8px",
|
|
622
|
+
borderRadius: 6,
|
|
623
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
624
|
+
background: "var(--fonderie-surface,#fff)",
|
|
625
|
+
color: "var(--fonderie-text,#171717)",
|
|
626
|
+
fontSize: 12,
|
|
627
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
628
|
+
cursor: "pointer"
|
|
629
|
+
};
|
|
543
630
|
var styles2 = {
|
|
544
631
|
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
545
632
|
title: {
|
|
@@ -562,9 +649,16 @@ var styles2 = {
|
|
|
562
649
|
overflow: "hidden",
|
|
563
650
|
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
564
651
|
},
|
|
565
|
-
row: {
|
|
652
|
+
row: {
|
|
653
|
+
display: "flex",
|
|
654
|
+
alignItems: "center",
|
|
655
|
+
gap: 8,
|
|
656
|
+
paddingRight: 16,
|
|
657
|
+
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)"
|
|
658
|
+
},
|
|
566
659
|
rowButton: {
|
|
567
|
-
|
|
660
|
+
flex: 1,
|
|
661
|
+
minWidth: 0,
|
|
568
662
|
display: "flex",
|
|
569
663
|
justifyContent: "space-between",
|
|
570
664
|
alignItems: "center",
|
|
@@ -583,7 +677,9 @@ var styles2 = {
|
|
|
583
677
|
flex: 1,
|
|
584
678
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
585
679
|
},
|
|
586
|
-
|
|
680
|
+
chips: { display: "flex", flexWrap: "wrap", justifyContent: "flex-end", gap: 6 },
|
|
681
|
+
chip,
|
|
682
|
+
chipInactive: { ...chip, opacity: 0.5, textDecoration: "line-through" },
|
|
587
683
|
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
588
684
|
builtIn: {
|
|
589
685
|
fontSize: 11,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["export type {\n\tITemplateCreateScreenProps,\n\tITemplateEditorScreenProps,\n\tITemplateListScreenProps,\n} from './screens';\nexport { TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen } from './screens';\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n\tformatAdminDate,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/** Shows \"Add locale\": create a translation of this template. */\n\tonAddLocale?: (type: string) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { saveTemplate, removeTemplate } = useTemplates(client);\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>\n\t\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t\t{onAddLocale && !locale ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\tonClick={() => onAddLocale(type)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/** Receives the whole row — type AND locale, and whether it is built-in. */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{templates.map((template) => (\n\t\t\t\t\t\t<li key={`${template.type}:${template.locale ?? 'base'}`} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(template)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{template.type}</span>\n\t\t\t\t\t\t\t\t<span style={styles.locale}>{template.locale ?? t('templates.defaultLocale')}</span>\n\t\t\t\t\t\t\t\t{template.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={template.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{template.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: { borderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)' },\n\trowButton: {\n\t\twidth: '100%',\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tlocale: { fontSize: 12.5, color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["import_client","import_react_courier_admin","import_jsx_runtime","styles","import_client","import_react","import_jsx_runtime","styles"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["export type {\n\tITemplateCreateScreenProps,\n\tITemplateEditorScreenProps,\n\tITemplateListScreenProps,\n} from './screens';\nexport { TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen } from './screens';\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["import_client","import_react_courier_admin","import_jsx_runtime","styles","import_client","import_react","import_jsx_runtime","styles"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { CourierAdminClient,
|
|
2
|
+
import { CourierAdminClient, ITemplateEntry, AdminLocale } from '@fonderie/client';
|
|
3
3
|
|
|
4
4
|
interface ITemplateEditorScreenProps {
|
|
5
5
|
client: CourierAdminClient;
|
|
@@ -13,19 +13,33 @@ interface ITemplateEditorScreenProps {
|
|
|
13
13
|
system?: boolean;
|
|
14
14
|
/** Called after a delete; the template no longer exists. */
|
|
15
15
|
onDeleted?: () => void;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Shows "Add locale": create a translation of this template. `locales` are
|
|
18
|
+
* the ones the app uses elsewhere that this email lacks — suggest them first.
|
|
19
|
+
*/
|
|
20
|
+
onAddLocale?: (type: string, context: {
|
|
21
|
+
locales: string[];
|
|
22
|
+
}) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Shows a tab per locale this email exists in; receives the row to open.
|
|
25
|
+
* Unsaved edits are confirmed before switching away.
|
|
26
|
+
*/
|
|
27
|
+
onSelectLocale?: (template: ITemplateEntry) => void;
|
|
18
28
|
/**
|
|
19
29
|
* The CONSOLE's language (default English). Named `uiLocale` because
|
|
20
30
|
* `locale` here is the template's own locale.
|
|
21
31
|
*/
|
|
22
32
|
uiLocale?: AdminLocale | undefined;
|
|
23
33
|
}
|
|
24
|
-
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, uiLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
34
|
+
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, onSelectLocale, uiLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
25
35
|
|
|
26
36
|
interface ITemplateListScreenProps {
|
|
27
37
|
client: CourierAdminClient;
|
|
28
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Receives the whole row — type AND locale, and whether it is built-in. The
|
|
40
|
+
* list shows one row per email: clicking it opens the default locale, a
|
|
41
|
+
* locale chip opens that locale.
|
|
42
|
+
*/
|
|
29
43
|
onSelectTemplate?: (template: ITemplateEntry) => void;
|
|
30
44
|
/** Shows a "New template" button. Receives the locales in use, to suggest. */
|
|
31
45
|
onCreateTemplate?: (context: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { CourierAdminClient,
|
|
2
|
+
import { CourierAdminClient, ITemplateEntry, AdminLocale } from '@fonderie/client';
|
|
3
3
|
|
|
4
4
|
interface ITemplateEditorScreenProps {
|
|
5
5
|
client: CourierAdminClient;
|
|
@@ -13,19 +13,33 @@ interface ITemplateEditorScreenProps {
|
|
|
13
13
|
system?: boolean;
|
|
14
14
|
/** Called after a delete; the template no longer exists. */
|
|
15
15
|
onDeleted?: () => void;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Shows "Add locale": create a translation of this template. `locales` are
|
|
18
|
+
* the ones the app uses elsewhere that this email lacks — suggest them first.
|
|
19
|
+
*/
|
|
20
|
+
onAddLocale?: (type: string, context: {
|
|
21
|
+
locales: string[];
|
|
22
|
+
}) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Shows a tab per locale this email exists in; receives the row to open.
|
|
25
|
+
* Unsaved edits are confirmed before switching away.
|
|
26
|
+
*/
|
|
27
|
+
onSelectLocale?: (template: ITemplateEntry) => void;
|
|
18
28
|
/**
|
|
19
29
|
* The CONSOLE's language (default English). Named `uiLocale` because
|
|
20
30
|
* `locale` here is the template's own locale.
|
|
21
31
|
*/
|
|
22
32
|
uiLocale?: AdminLocale | undefined;
|
|
23
33
|
}
|
|
24
|
-
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, uiLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
34
|
+
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, onSelectLocale, uiLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
25
35
|
|
|
26
36
|
interface ITemplateListScreenProps {
|
|
27
37
|
client: CourierAdminClient;
|
|
28
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Receives the whole row — type AND locale, and whether it is built-in. The
|
|
40
|
+
* list shows one row per email: clicking it opens the default locale, a
|
|
41
|
+
* locale chip opens that locale.
|
|
42
|
+
*/
|
|
29
43
|
onSelectTemplate?: (template: ITemplateEntry) => void;
|
|
30
44
|
/** Shows a "New template" button. Receives the locales in use, to suggest. */
|
|
31
45
|
onCreateTemplate?: (context: {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/screens/TemplateEditorScreen.tsx
|
|
2
2
|
import {
|
|
3
3
|
createAdminT,
|
|
4
|
-
formatAdminDate
|
|
4
|
+
formatAdminDate,
|
|
5
|
+
groupTemplatesByType,
|
|
6
|
+
missingTemplateLocales
|
|
5
7
|
} from "@fonderie/client";
|
|
6
8
|
import {
|
|
7
9
|
useTemplate,
|
|
@@ -21,11 +23,13 @@ function TemplateEditorScreen({
|
|
|
21
23
|
system = false,
|
|
22
24
|
onDeleted,
|
|
23
25
|
onAddLocale,
|
|
26
|
+
onSelectLocale,
|
|
24
27
|
uiLocale
|
|
25
28
|
}) {
|
|
26
29
|
const t = useMemo(() => createAdminT(uiLocale), [uiLocale]);
|
|
27
30
|
const { template, isLoading, error, refresh } = useTemplate(client, type, locale);
|
|
28
|
-
const { saveTemplate, removeTemplate } = useTemplates(client);
|
|
31
|
+
const { templates, saveTemplate, removeTemplate } = useTemplates(client);
|
|
32
|
+
const siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];
|
|
29
33
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
30
34
|
const { revisions, rollback } = useTemplateRevisions(client, type, locale);
|
|
31
35
|
const [isSaving, setIsSaving] = useState(false);
|
|
@@ -96,6 +100,10 @@ function TemplateEditorScreen({
|
|
|
96
100
|
setIsSaving(false);
|
|
97
101
|
}
|
|
98
102
|
};
|
|
103
|
+
const switchTo = (entry) => {
|
|
104
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
105
|
+
onSelectLocale?.(entry);
|
|
106
|
+
};
|
|
99
107
|
const handleDelete = async () => {
|
|
100
108
|
const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
|
|
101
109
|
if (!window.confirm(message)) return;
|
|
@@ -114,18 +122,40 @@ function TemplateEditorScreen({
|
|
|
114
122
|
if (error)
|
|
115
123
|
return /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
116
124
|
return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
/* @__PURE__ */ jsx("h1", { style: styles.title, children: type }),
|
|
126
|
+
onSelectLocale || onAddLocale ? /* @__PURE__ */ jsxs("div", { style: styles.tabBar, children: [
|
|
127
|
+
onSelectLocale ? (
|
|
128
|
+
// Each tab opens another stored row, so this is navigation, not an
|
|
129
|
+
// ARIA tablist: plain buttons, the open one marked aria-current.
|
|
130
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": t("templates.editor.locales"), style: styles.tabs, children: siblings.map((entry) => {
|
|
131
|
+
const selected = entry.locale === (locale ?? null);
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
"button",
|
|
134
|
+
{
|
|
135
|
+
type: "button",
|
|
136
|
+
"aria-current": selected ? "true" : void 0,
|
|
137
|
+
onClick: () => selected ? void 0 : switchTo(entry),
|
|
138
|
+
style: selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive,
|
|
139
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
140
|
+
children: entry.locale ?? t("templates.defaultChip")
|
|
141
|
+
},
|
|
142
|
+
entry.locale ?? ""
|
|
143
|
+
);
|
|
144
|
+
}) })
|
|
145
|
+
) : null,
|
|
146
|
+
onAddLocale ? /* @__PURE__ */ jsx(
|
|
120
147
|
"button",
|
|
121
148
|
{
|
|
122
149
|
type: "button",
|
|
123
150
|
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
124
|
-
onClick: () =>
|
|
151
|
+
onClick: () => {
|
|
152
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
153
|
+
onAddLocale(type, { locales: missingTemplateLocales(templates, type) });
|
|
154
|
+
},
|
|
125
155
|
children: t("templates.editor.addLocale")
|
|
126
156
|
}
|
|
127
157
|
) : null
|
|
128
|
-
] }),
|
|
158
|
+
] }) : null,
|
|
129
159
|
/* @__PURE__ */ jsxs("p", { style: styles.meta, children: [
|
|
130
160
|
/* @__PURE__ */ jsx("strong", { children: locale ?? t("templates.defaultLocale") }),
|
|
131
161
|
" \xB7 v",
|
|
@@ -286,8 +316,38 @@ function TemplateEditorScreen({
|
|
|
286
316
|
] })
|
|
287
317
|
] });
|
|
288
318
|
}
|
|
319
|
+
var tab = {
|
|
320
|
+
height: 32,
|
|
321
|
+
padding: "0 10px",
|
|
322
|
+
background: "none",
|
|
323
|
+
border: "none",
|
|
324
|
+
borderBottom: "2px solid transparent",
|
|
325
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
326
|
+
fontSize: 13,
|
|
327
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
328
|
+
cursor: "pointer"
|
|
329
|
+
};
|
|
289
330
|
var styles = {
|
|
290
331
|
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
332
|
+
// One tab per locale of this email, underlined like a document's tabs.
|
|
333
|
+
tabBar: {
|
|
334
|
+
display: "flex",
|
|
335
|
+
alignItems: "flex-end",
|
|
336
|
+
gap: 12,
|
|
337
|
+
borderBottom: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
338
|
+
margin: "4px 0 12px",
|
|
339
|
+
paddingBottom: 6
|
|
340
|
+
},
|
|
341
|
+
tabs: { display: "flex", flexWrap: "wrap", gap: 4, marginBottom: -7 },
|
|
342
|
+
tab,
|
|
343
|
+
tabSelected: {
|
|
344
|
+
...tab,
|
|
345
|
+
color: "var(--fonderie-text,#171717)",
|
|
346
|
+
fontWeight: 600,
|
|
347
|
+
borderBottomColor: "var(--fonderie-text,#171717)",
|
|
348
|
+
cursor: "default"
|
|
349
|
+
},
|
|
350
|
+
tabInactive: { ...tab, opacity: 0.55, textDecoration: "line-through" },
|
|
291
351
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
292
352
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
293
353
|
split: {
|
|
@@ -456,7 +516,8 @@ var styles = {
|
|
|
456
516
|
|
|
457
517
|
// src/screens/TemplateListScreen.tsx
|
|
458
518
|
import {
|
|
459
|
-
createAdminT as createAdminT2
|
|
519
|
+
createAdminT as createAdminT2,
|
|
520
|
+
groupTemplatesByType as groupTemplatesByType2
|
|
460
521
|
} from "@fonderie/client";
|
|
461
522
|
import { useTemplates as useTemplates2 } from "@fonderie/react-courier-admin";
|
|
462
523
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -468,6 +529,7 @@ function TemplateListScreen({
|
|
|
468
529
|
}) {
|
|
469
530
|
const t = createAdminT2(locale);
|
|
470
531
|
const { templates, isLoading, error } = useTemplates2(client);
|
|
532
|
+
const groups = groupTemplatesByType2(templates);
|
|
471
533
|
return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
|
|
472
534
|
/* @__PURE__ */ jsxs2(
|
|
473
535
|
"div",
|
|
@@ -492,23 +554,39 @@ function TemplateListScreen({
|
|
|
492
554
|
}
|
|
493
555
|
),
|
|
494
556
|
/* @__PURE__ */ jsx2("p", { style: styles2.hint, children: t("templates.list.hint") }),
|
|
495
|
-
isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsx2("ul", { style: styles2.list, children:
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
557
|
+
isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: t("templates.list.loading") }) : error ? /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation }) : /* @__PURE__ */ jsx2("ul", { style: styles2.list, children: groups.map((group) => /* @__PURE__ */ jsxs2("li", { style: styles2.row, children: [
|
|
558
|
+
/* @__PURE__ */ jsxs2(
|
|
559
|
+
"button",
|
|
560
|
+
{
|
|
561
|
+
type: "button",
|
|
562
|
+
onClick: () => onSelectTemplate?.(group.primary),
|
|
563
|
+
style: styles2.rowButton,
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ jsx2("span", { style: styles2.type, children: group.type }),
|
|
566
|
+
group.system ? /* @__PURE__ */ jsx2("span", { style: styles2.builtIn, children: t("templates.builtInBadge") }) : null,
|
|
567
|
+
/* @__PURE__ */ jsxs2("span", { style: group.primary.active ? styles2.active : styles2.inactive, children: [
|
|
568
|
+
/* @__PURE__ */ jsx2("span", { style: styles2.dot }),
|
|
569
|
+
group.primary.active ? t("common.status.active") : t("common.status.inactive")
|
|
570
|
+
] })
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
),
|
|
574
|
+
/* @__PURE__ */ jsx2("span", { style: styles2.chips, children: group.entries.map((entry) => {
|
|
575
|
+
const name = entry.locale ?? t("templates.defaultChip");
|
|
576
|
+
return /* @__PURE__ */ jsx2(
|
|
577
|
+
"button",
|
|
578
|
+
{
|
|
579
|
+
type: "button",
|
|
580
|
+
onClick: () => onSelectTemplate?.(entry),
|
|
581
|
+
style: entry.active ? styles2.chip : styles2.chipInactive,
|
|
582
|
+
"aria-label": t("templates.list.openLocale", { type: group.type, locale: name }),
|
|
583
|
+
title: entry.active ? void 0 : t("common.status.inactive"),
|
|
584
|
+
children: name
|
|
585
|
+
},
|
|
586
|
+
entry.locale ?? ""
|
|
587
|
+
);
|
|
588
|
+
}) })
|
|
589
|
+
] }, group.type)) })
|
|
512
590
|
] });
|
|
513
591
|
}
|
|
514
592
|
var pill = {
|
|
@@ -522,6 +600,18 @@ var pill = {
|
|
|
522
600
|
lineHeight: 1.5,
|
|
523
601
|
minWidth: 64
|
|
524
602
|
};
|
|
603
|
+
var chip = {
|
|
604
|
+
height: 24,
|
|
605
|
+
boxSizing: "border-box",
|
|
606
|
+
padding: "0 8px",
|
|
607
|
+
borderRadius: 6,
|
|
608
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
609
|
+
background: "var(--fonderie-surface,#fff)",
|
|
610
|
+
color: "var(--fonderie-text,#171717)",
|
|
611
|
+
fontSize: 12,
|
|
612
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)',
|
|
613
|
+
cursor: "pointer"
|
|
614
|
+
};
|
|
525
615
|
var styles2 = {
|
|
526
616
|
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
527
617
|
title: {
|
|
@@ -544,9 +634,16 @@ var styles2 = {
|
|
|
544
634
|
overflow: "hidden",
|
|
545
635
|
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
546
636
|
},
|
|
547
|
-
row: {
|
|
637
|
+
row: {
|
|
638
|
+
display: "flex",
|
|
639
|
+
alignItems: "center",
|
|
640
|
+
gap: 8,
|
|
641
|
+
paddingRight: 16,
|
|
642
|
+
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)"
|
|
643
|
+
},
|
|
548
644
|
rowButton: {
|
|
549
|
-
|
|
645
|
+
flex: 1,
|
|
646
|
+
minWidth: 0,
|
|
550
647
|
display: "flex",
|
|
551
648
|
justifyContent: "space-between",
|
|
552
649
|
alignItems: "center",
|
|
@@ -565,7 +662,9 @@ var styles2 = {
|
|
|
565
662
|
flex: 1,
|
|
566
663
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
567
664
|
},
|
|
568
|
-
|
|
665
|
+
chips: { display: "flex", flexWrap: "wrap", justifyContent: "flex-end", gap: 6 },
|
|
666
|
+
chip,
|
|
667
|
+
chipInactive: { ...chip, opacity: 0.5, textDecoration: "line-through" },
|
|
569
668
|
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
570
669
|
builtIn: {
|
|
571
670
|
fontSize: 11,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n\tformatAdminDate,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/** Shows \"Add locale\": create a translation of this template. */\n\tonAddLocale?: (type: string) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { saveTemplate, removeTemplate } = useTemplates(client);\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>\n\t\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t\t{onAddLocale && !locale ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\tonClick={() => onAddLocale(type)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/** Receives the whole row — type AND locale, and whether it is built-in. */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{templates.map((template) => (\n\t\t\t\t\t\t<li key={`${template.type}:${template.locale ?? 'base'}`} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(template)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{template.type}</span>\n\t\t\t\t\t\t\t\t<span style={styles.locale}>{template.locale ?? t('templates.defaultLocale')}</span>\n\t\t\t\t\t\t\t\t{template.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={template.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{template.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: { borderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)' },\n\trowButton: {\n\t\twidth: '100%',\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tlocale: { fontSize: 12.5, color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":";AAAA;AAAA,EAKC;AAAA,EACA;AAAA,OACM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEP,SAAS,aAAa,WAAW,SAAS,QAAQ,gBAAgB;AAsK3C,cAUpB,YAVoB;AAlKvB,IAAM,WAAW,oBAAI,IAAI,CAAC,WAAW,aAAa,WAAW,CAAC;AAE9D,IAAM,sBAAsB;AAuBrB,SAAS,qBAAqB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AACD,GAA+B;AAG9B,QAAM,IAAI,QAAQ,MAAM,aAAa,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAC1D,QAAM,EAAE,UAAU,WAAW,OAAO,QAAQ,IAAI,YAAY,QAAQ,MAAM,MAAM;AAKhF,QAAM,EAAE,cAAc,eAAe,IAAI,aAAa,MAAM;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,EAAE,WAAW,SAAS,IAAI,qBAAqB,QAAQ,MAAM,MAAM;AACzE,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAkC,IAAI;AAExE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,IAAI;AAEzC,QAAM,EAAE,SAAS,cAAc,OAAO,cAAc,cAAc,IAAI,mBAAmB,MAAM;AAG/F,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAElE,YAAU,MAAM;AACf,QAAI,CAAC,SAAU;AACf,eAAW,SAAS,WAAW,EAAE;AACjC,YAAQ,SAAS,QAAQ,EAAE;AAC3B,YAAQ,SAAS,IAAI;AACrB,cAAU,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,MAAM,YAAY,YAAY;AACnC,QAAI,OAAgC,CAAC;AACrC,QAAI;AACH,YAAM,SAAkB,KAAK,MAAM,cAAc,IAAI;AACrD,UAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACnE,uBAAe,EAAE,kCAAkC,CAAC;AACpD;AAAA,MACD;AACA,aAAO;AACP,qBAAe,IAAI;AAAA,IACpB,QAAQ;AACP,qBAAe,EAAE,gCAAgC,CAAC;AAClD;AAAA,IACD;AACA,UAAM,SAAS,MAAM;AAAA,MACpB;AAAA,MACA,EAAE,MAAM,MAAM,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,GAAI,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,EAAG;AAAA,MACzE;AAAA,IACD;AAIA,UAAM,UAAU,OAAO,UAAU,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK;AAC/E,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAM,SAAS,EAAE,GAAG,MAAM,GAAG,OAAO,YAAY,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;AAC5E,oBAAc,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC9C;AAAA,EACD,GAAG,CAAC,eAAe,MAAM,MAAM,SAAS,MAAM,QAAQ,YAAY,CAAC,CAAC;AAOpE,QAAM,SAAS,OAAO,GAAG;AACzB,SAAO,UAAU;AACjB,QAAM,aAAa,WAAW,GAAG,IAAI,IAAI,UAAU,EAAE,KAAK;AAI1D,YAAU,MAAM;AACf,QAAI,CAAC,WAAY;AACjB,UAAM,QAAQ,WAAW,MAAM,KAAK,OAAO,QAAQ,GAAG,mBAAmB;AACzE,WAAO,MAAM,aAAa,KAAK;AAAA,EAChC,GAAG,CAAC,YAAY,SAAS,MAAM,MAAM,UAAU,CAAC;AAIhD,QAAM,QACL,CAAC,CAAC,aACD,aAAa,SAAS,WAAW,OACjC,UAAU,SAAS,QAAQ,OAC3B,SAAS,SAAS,QAClB,WAAW,SAAS;AAEtB,QAAM,eAAe,OAAO,UAAqB;AAChD,UAAM,eAAe;AACrB,gBAAY,IAAI;AAChB,iBAAa,IAAI;AACjB,QAAI;AACH,YAAM,QAA2B,EAAE,MAAM,OAAO;AAChD,UAAI,QAAS,OAAM,UAAU;AAC7B,UAAI,KAAM,OAAM,OAAO;AACvB,UAAI,UAAU,YAAY,OAAW,OAAM,YAAY,SAAS;AAChE,YAAM,aAAa,MAAM,OAAO,MAAM;AAGtC,YAAM,QAAQ;AACd,gBAAU;AAAA,IACX,SAAS,KAAK;AAEb,mBAAa,GAAuB;AAAA,IACrC,UAAE;AACD,kBAAY,KAAK;AAAA,IAClB;AAAA,EACD;AAEA,QAAM,eAAe,YAAY;AAChC,UAAM,UAAU,SACb,EAAE,wCAAwC,EAAE,MAAM,OAAO,CAAC,IAC1D,EAAE,kCAAkC,EAAE,KAAK,CAAC;AAC/C,QAAI,CAAC,OAAO,QAAQ,OAAO,EAAG;AAC9B,kBAAc,IAAI;AAClB,iBAAa,IAAI;AACjB,QAAI;AACH,YAAM,eAAe,MAAM,MAAM;AACjC,kBAAY;AAAA,IACb,SAAS,KAAK;AACb,mBAAa,GAAuB;AAAA,IACrC,UAAE;AACD,oBAAc,KAAK;AAAA,IACpB;AAAA,EACD;AAEA,MAAI,UAAW,QAAO,oBAAC,OAAE,OAAO,OAAO,QAAS,YAAE,0BAA0B,GAAE;AAC9E,MAAI;AACH,WACC,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,gBAAM,aACR;AAGF,SACC,qBAAC,SAAI,OAAO,OAAO,WAClB;AAAA,yBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,UAAU,OAAO,GAC9E;AAAA,0BAAC,QAAG,OAAO,OAAO,OAAQ,gBAAK;AAAA,MAC9B,eAAe,CAAC,SAChB;AAAA,QAAC;AAAA;AAAA,UACA,MAAK;AAAA,UACL,OAAO,EAAE,GAAG,OAAO,gBAAgB,YAAY,OAAO;AAAA,UACtD,SAAS,MAAM,YAAY,IAAI;AAAA,UAE9B,YAAE,4BAA4B;AAAA;AAAA,MAChC,IACG;AAAA,OACL;AAAA,IACA,qBAAC,OAAE,OAAO,OAAO,MAChB;AAAA,0BAAC,YAAQ,oBAAU,EAAE,yBAAyB,GAAE;AAAA,MAAS;AAAA,MAAK,UAAU,WAAW;AAAA,MAClF,SAAS,SAAM,EAAE,8BAA8B,CAAC,KAAK;AAAA,OACvD;AAAA,IAEA,qBAAC,SAAI,OAAO,OAAO,OAClB;AAAA,2BAAC,SAAI,OAAO,OAAO,QAClB;AAAA,6BAAC,UAAK,OAAO,OAAO,MAAM,UAAU,cACnC;AAAA,8BAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,oBAClC,YAAE,mBAAmB,GACvB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA;AAAA,UACnD;AAAA,UAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAClC,YAAE,oBAAoB,GACxB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,cAC/C,MAAM;AAAA;AAAA,UACP;AAAA,UAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAClC,YAAE,oBAAoB,GACxB;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACA,IAAG;AAAA,cACH,OAAO,OAAO;AAAA,cACd,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,cAC/C,MAAM;AAAA,cACN,UAAQ;AAAA;AAAA,UACT;AAAA,UAEA,qBAAC,WAAM,OAAO,OAAO,eACpB;AAAA;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,SAAS;AAAA,gBACT,UAAU,CAAC,UAAU,UAAU,MAAM,OAAO,OAAO;AAAA;AAAA,YACpD;AAAA,YACC,EAAE,yBAAyB;AAAA,aAC7B;AAAA,UAEC,WAAW,WAAW,qBACtB,qBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B;AAAA,cAAE,2BAA2B;AAAA,YAAG;AAAA,YACjC;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,OAAO,OAAO;AAAA,gBACd,SAAS,MAAM;AACd,+BAAa,IAAI;AACjB,uBAAK,QAAQ;AAAA,gBACd;AAAA,gBAEC,YAAE,eAAe;AAAA;AAAA,YACnB;AAAA,aACD,IACG,YACH,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,oBAAU,aACZ,IACG;AAAA,UAEJ,qBAAC,SAAI,OAAO,OAAO,SAClB;AAAA;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,UAAU,YAAY,CAAC;AAAA,gBACvB,OAAO,QAAQ,OAAO,SAAS,OAAO;AAAA,gBAErC,qBAAW,EAAE,eAAe,IAAI,EAAE,aAAa;AAAA;AAAA,YACjD;AAAA,YACC,CAAC,SAAS,CAAC,YACX,oBAAC,UAAK,OAAO,OAAO,MAAO,YAAE,4BAA4B,GAAE;AAAA,YAE3D,CAAC,SACD;AAAA,cAAC;AAAA;AAAA,gBACA,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,SAAS,MAAM,KAAK,aAAa;AAAA,gBACjC,OAAO;AAAA,kBACN,GAAG,OAAO;AAAA,kBACV,QAAQ;AAAA,kBACR,YAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,aAAa;AAAA,gBACd;AAAA,gBAEC,uBAAa,EAAE,iBAAiB,IAAI,EAAE,eAAe;AAAA;AAAA,YACvD,IACG;AAAA,aACL;AAAA,WACD;AAAA,QAEA,oBAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,mBAClC,YAAE,6BAA6B,GACjC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,IAAG;AAAA,YACH,OAAO,OAAO;AAAA,YACd,OAAO;AAAA,YACP,UAAU,CAAC,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,YACrD,MAAM;AAAA,YACN,YAAY;AAAA;AAAA,QACb;AAAA,QACC,eACA,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,uBACF;AAAA,SAEF;AAAA,MAEA,qBAAC,SAAI,OAAO,OAAO,eAClB;AAAA,6BAAC,SAAI,OAAO,OAAO,eAClB;AAAA,8BAAC,UAAK,OAAO,OAAO,OAAQ,YAAE,0BAA0B,GAAE;AAAA,UAC1D,oBAAC,UAAK,OAAO,OAAO,MAClB,yBAAe,EAAE,4BAA4B,IAAI,EAAE,uBAAuB,GAC5E;AAAA,WACD;AAAA,QACC,gBACA,oBAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,uBAAa,aACf;AAAA,QAEA,SAAS,WAAW,oBAAC,OAAE,OAAO,OAAO,gBAAiB,kBAAQ,SAAQ;AAAA,QACtE,SAAS;AAAA;AAAA;AAAA;AAAA,UAIT;AAAA,YAAC;AAAA;AAAA,cACA,OAAO,EAAE,+BAA+B;AAAA,cACxC,OAAO,OAAO;AAAA,cACd,SAAQ;AAAA,cACR,QAAQ,QAAQ;AAAA;AAAA,UACjB;AAAA,YACG,UACH,oBAAC,SAAI,OAAO,OAAO,aAAc,kBAAQ,MAAK,IAE9C,oBAAC,OAAE,OAAO,OAAO,MAAO,YAAE,kCAAkC,GAAE;AAAA,SAEhE;AAAA,OACD;AAAA,IAEC,UAAU,SAAS,KACnB,qBAAC,SAAI,OAAO,OAAO,WAClB;AAAA,0BAAC,QAAG,OAAO,OAAO,UAAW,YAAE,0BAA0B,GAAE;AAAA,MAC3D,oBAAC,QAAG,OAAO,OAAO,MAChB,oBAAU,IAAI,CAAC,QACf,qBAAC,QAAqB,OAAO,OAAO,KACnC;AAAA,6BAAC,UAAK;AAAA;AAAA,UACH,IAAI;AAAA,UAAQ;AAAA,UAAI,IAAI,SAAS,EAAE,+BAA+B;AAAA,UAAE;AAAA,UAAG;AAAA,UACpE,gBAAgB,IAAI,WAAW,QAAQ;AAAA,WACzC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,SAAS,MAAM,SAAS,IAAI,OAAO;AAAA,YACnC,OAAO,OAAO;AAAA,YAEb,YAAE,2BAA2B;AAAA;AAAA,QAC/B;AAAA,WAXQ,IAAI,OAYb,CACA,GACF;AAAA,OACD;AAAA,KAEF;AAEF;AAEA,IAAM,SAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,iBAAiB,UAAU,MAAM,WAAW,aAAa;AAAA;AAAA;AAAA,EAG/E,OAAO;AAAA,IACN,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,KAAK;AAAA,IACL,YAAY;AAAA,EACb;AAAA,EACA,QAAQ,EAAE,UAAU,GAAG,SAAS,QAAQ,eAAe,SAAS;AAAA;AAAA,EAEhE,eAAe;AAAA,IACd,UAAU;AAAA,IACV,SAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAU;AAAA,IACV,KAAK;AAAA,EACN;AAAA,EACA,SAAS,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,IAAI,WAAW,GAAG;AAAA,EACzE,eAAe;AAAA,IACd,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,WAAW;AAAA,EACZ;AAAA,EACA,gBAAgB,EAAE,UAAU,IAAI,YAAY,KAAK,QAAQ,QAAQ;AAAA,EACjE,cAAc;AAAA,IACb,WAAW;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,YAAY;AAAA,EACb;AAAA,EACA,aAAa;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YACC;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM,EAAE,UAAU,IAAI,OAAO,sCAAsC,cAAc,GAAG;AAAA,EACpF,QAAQ,EAAE,SAAS,IAAI,WAAW,UAAU,OAAO,qCAAqC;AAAA,EACxF,OAAO;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,EACX;AAAA,EACA,MAAM;AAAA,IACL,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,WAAW;AAAA,EACZ;AAAA,EACA,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,WAAW,IAAI,cAAc,EAAE;AAAA,EACvE,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,YACC;AAAA,EACF;AAAA,EACA,eAAe,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,WAAW,IAAI,UAAU,GAAG;AAAA,EAC5F,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,EACZ;AAAA,EACA,gBAAgB;AAAA,IACf,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AAAA,EACA,WAAW,EAAE,WAAW,GAAG;AAAA,EAC3B,UAAU;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,EAChB;AAAA,EACA,MAAM;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AAAA,EACA,KAAK;AAAA,IACJ,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AAAA,EACA,gBAAgB;AAAA,IACf,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT;AACD;;;ACliBA;AAAA,EAIC,gBAAAA;AAAA,OACM;AACP,SAAS,gBAAAC,qBAAoB;AAwB1B,SAGC,OAAAC,MAHD,QAAAC,aAAA;AAXI,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA6B;AAC5B,QAAM,IAAIH,cAAa,MAAM;AAC7B,QAAM,EAAE,WAAW,WAAW,MAAM,IAAIC,cAAa,MAAM;AAE3D,SACC,gBAAAE,MAAC,SAAI,OAAOC,QAAO,WAClB;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACA,OAAO,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,iBAAiB,KAAK,GAAG;AAAA,QAEzF;AAAA,0BAAAD,KAAC,QAAG,OAAOE,QAAO,OAAQ,YAAE,sBAAsB,GAAE;AAAA,UACnD,mBACA,gBAAAF;AAAA,YAAC;AAAA;AAAA,cACA,MAAK;AAAA,cACL,SAAS,MACR,iBAAiB;AAAA,gBAChB,SAAS;AAAA,kBACR,GAAG,IAAI,IAAI,UAAU,IAAI,CAAC,QAAQ,IAAI,MAAM,EAAE,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,CAAC;AAAA,gBAC9E;AAAA,cACD,CAAC;AAAA,cAEF,OAAOE,QAAO;AAAA,cAEb,YAAE,4BAA4B;AAAA;AAAA,UAChC,IACG;AAAA;AAAA;AAAA,IACL;AAAA,IACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,MAAO,YAAE,qBAAqB,GAAE;AAAA,IAChD,YACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,QAAS,YAAE,wBAAwB,GAAE,IACnD,QACH,gBAAAF,KAAC,OAAE,OAAOE,QAAO,OAAO,MAAK,SAC3B,gBAAM,aACR,IAEA,gBAAAF,KAAC,QAAG,OAAOE,QAAO,MAChB,oBAAU,IAAI,CAAC,aACf,gBAAAF,KAAC,QAAyD,OAAOE,QAAO,KACvE,0BAAAD;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,MAAM,mBAAmB,QAAQ;AAAA,QAC1C,OAAOC,QAAO;AAAA,QAEd;AAAA,0BAAAF,KAAC,UAAK,OAAOE,QAAO,MAAO,mBAAS,MAAK;AAAA,UACzC,gBAAAF,KAAC,UAAK,OAAOE,QAAO,QAAS,mBAAS,UAAU,EAAE,yBAAyB,GAAE;AAAA,UAC5E,SAAS,SACT,gBAAAF,KAAC,UAAK,OAAOE,QAAO,SAAU,YAAE,wBAAwB,GAAE,IACvD;AAAA,UACJ,gBAAAD,MAAC,UAAK,OAAO,SAAS,SAASC,QAAO,SAASA,QAAO,UACrD;AAAA,4BAAAF,KAAC,UAAK,OAAOE,QAAO,KAAK;AAAA,YACxB,SAAS,SAAS,EAAE,sBAAsB,IAAI,EAAE,wBAAwB;AAAA,aAC1E;AAAA;AAAA;AAAA,IACD,KAfQ,GAAG,SAAS,IAAI,IAAI,SAAS,UAAU,MAAM,EAgBtD,CACA,GACF;AAAA,KAEF;AAEF;AAIA,IAAM,OAAsB;AAAA,EAC3B,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,cAAc;AAAA,EACd,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AACX;AAEA,IAAMA,UAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,kBAAkB,UAAU,MAAM,WAAW,aAAa;AAAA,EAChF,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM,EAAE,UAAU,MAAM,OAAO,sCAAsC,QAAQ,aAAa;AAAA,EAC1F,QAAQ,EAAE,SAAS,UAAU,OAAO,sCAAsC,UAAU,KAAK;AAAA,EACzF,OAAO,EAAE,OAAO,+BAA+B,cAAc,IAAI,UAAU,GAAG;AAAA,EAC9E,MAAM;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,UAAU;AAAA,IACV,WAAW;AAAA,EACZ;AAAA,EACA,KAAK,EAAE,cAAc,iDAAiD;AAAA,EACtE,WAAW;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,YACC;AAAA,EACF;AAAA,EACA,QAAQ,EAAE,UAAU,MAAM,OAAO,qCAAqC;AAAA,EACtE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,cAAc,KAAK,YAAY,eAAe;AAAA,EAC1E,SAAS;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,OAAO;AAAA,EACR;AAAA,EACA,WAAW;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,QAAQ;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACP,GAAG;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,UAAU;AAAA,IACT,GAAG;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AACD;;;ACjLA;AAAA,EAGC;AAAA,EAEA,gBAAAC;AAAA,OACM;AAEP,SAAS,aAAAC,YAAW,YAAAC,iBAAgB;AA2FjC,SAUE,UAVF,OAAAC,MAUE,QAAAC,aAVF;AAzEH,IAAM,UAAU;AAChB,IAAM,YAAY;AAIX,SAAS,qBAAqB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,UAAU,CAAC;AAAA,EACX;AAAA,EACA;AACD,GAA+B;AAC9B,QAAM,KAAKJ,cAAa,MAAM;AAC9B,QAAM,eAAe,SAAS;AAC9B,QAAM,CAAC,SAAS,UAAU,IAAIE,UAAS,EAAE;AACzC,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,EAAE;AACnD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,EAAE;AACnC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,EAAE;AACnC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,KAAK;AAGtC,EAAAD,WAAU,MAAM;AACf,QAAI,CAAC,KAAM;AACX,WACE,YAAY,MAAM,IAAI,EACtB,KAAK,CAAC,EAAE,OAAO,MAAM;AACrB,iBAAW,OAAO,WAAW,EAAE;AAC/B,cAAQ,OAAO,QAAQ,EAAE;AACzB,cAAQ,OAAO,IAAI;AAAA,IACpB,CAAC,EACA,MAAM,MAAM,MAAS;AAAA,EACxB,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,QAAM,SAAS,OAAO,MAAiB;AACtC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,UAAM,IAAI,eAAgB,OAAkB,QAAQ,KAAK;AACzD,UAAM,IAAI,aAAa,KAAK,KAAK;AACjC,QAAI,CAAC,QAAQ,KAAK,CAAC,EAAG,QAAO,SAAS,GAAG,4BAA4B,CAAC;AACtE,QAAI,gBAAgB,CAAC,EAAG,QAAO,SAAS,GAAG,sCAAsC,CAAC;AAClF,QAAI,KAAK,CAAC,UAAU,KAAK,CAAC,EAAG,QAAO,SAAS,GAAG,8BAA8B,CAAC;AAC/E,QAAI,CAAC,KAAK,KAAK,EAAG,QAAO,SAAS,GAAG,4BAA4B,CAAC;AAClE,YAAQ,IAAI;AACZ,QAAI;AAEH,YAAM,SAAS,MAAM,OAAO,YAAY,GAAG,CAAC,EAAE;AAAA,QAC7C,MAAM;AAAA,QACN,CAAC,QAAiB,EAAE,eAAe,oBAAoB,IAAI,WAAW;AAAA,MACvE;AACA,UAAI,QAAQ;AACX;AAAA,UACC,IACG,GAAG,iCAAiC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC,IAC1D,GAAG,2BAA2B,EAAE,MAAM,EAAE,CAAC;AAAA,QAC7C;AACA;AAAA,MACD;AACA,YAAM,QAA2B,EAAE,MAAM,QAAQ,KAAK;AACtD,UAAI,QAAS,OAAM,UAAU;AAC7B,UAAI,KAAM,OAAM,OAAO;AACvB,YAAM,OAAO,YAAY,GAAG,OAAO,CAAC;AACpC,kBAAY,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAC;AAAA,IACnC,SAAS,KAAK;AACb,eAAS,eAAe,mBAAmB,IAAI,cAAc,OAAO,GAAG,CAAC;AAAA,IACzE,UAAE;AACD,cAAQ,KAAK;AAAA,IACd;AAAA,EACD;AAEA,SACC,gBAAAG,MAAC,SAAI,OAAOC,QAAO,WAClB;AAAA,oBAAAF,KAAC,QAAG,OAAOE,QAAO,OAChB,yBACE,GAAG,gCAAgC,EAAE,KAAqB,CAAC,IAC3D,GAAG,2BAA2B,GAClC;AAAA,IACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,MACf,yBAAe,GAAG,6BAA6B,IAAI,GAAG,0BAA0B,GAClF;AAAA,IACA,gBAAAD,MAAC,UAAK,OAAOC,QAAO,MAAM,UAAU,CAAC,MAAM,KAAK,OAAO,CAAC,GACtD;AAAA,OAAC,eACD,gBAAAD,MAAA,YACC;AAAA,wBAAAD,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,uBAAuB,GAC5B;AAAA,QACA,gBAAAF;AAAA,UAAC;AAAA;AAAA,YACA,IAAG;AAAA,YACH,OAAOE,QAAO;AAAA,YACd,OAAO;AAAA,YACP,aAAY;AAAA,YACZ,cAAa;AAAA,YACb,YAAY;AAAA,YACZ,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA;AAAA,QAC3C;AAAA,SACD,IACG;AAAA,MACJ,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,uBAClC,yBAAe,GAAG,yBAAyB,IAAI,GAAG,iCAAiC,GACrF;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAOE,QAAO;AAAA,UACd,OAAO;AAAA,UACP,aAAY;AAAA,UACZ,MAAK;AAAA,UACL,cAAa;AAAA,UACb,YAAY;AAAA,UACZ,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA;AAAA,MAChD;AAAA,MACA,gBAAAF,KAAC,cAAS,IAAG,wBACX,WAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,IAAI,CAAC,MAC3B,gBAAAA,KAAC,YAAe,OAAO,KAAV,CAAa,CAC1B,GACF;AAAA,MACA,gBAAAA,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,wBAClC,aAAG,mBAAmB,GACxB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAOE,QAAO;AAAA,UACd,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA;AAAA,MAC3C;AAAA,MACA,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,oBAAoB,GACzB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,EAAE,GAAGE,QAAO,UAAU,WAAW,IAAI;AAAA,UAC5C,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,MACxC;AAAA,MACA,gBAAAF,KAAC,WAAM,OAAOE,QAAO,OAAO,SAAQ,qBAClC,aAAG,oBAAoB,GACzB;AAAA,MACA,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,EAAE,GAAGE,QAAO,UAAU,WAAW,IAAI;AAAA,UAC5C,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA;AAAA,MACxC;AAAA,MACC,QACA,gBAAAF,KAAC,OAAE,OAAOE,QAAO,OAAO,MAAK,SAC3B,iBACF,IACG;AAAA,MACJ,gBAAAF,KAAC,YAAO,MAAK,UAAS,UAAU,MAAM,OAAOE,QAAO,QAClD,iBACE,GAAG,2BAA2B,IAC9B,eACC,GAAG,+BAA+B,IAClC,GAAG,4BAA4B,GACpC;AAAA,OACD;AAAA,KACD;AAEF;AAEA,IAAM,OACL;AACD,IAAMA,UAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,iBAAiB,UAAU,KAAK,WAAW,aAAa;AAAA,EAC9E,OAAO;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,UAAU;AAAA,IACV,OAAO;AAAA,IACP,cAAc;AAAA,IACd,YAAY;AAAA,EACb;AAAA,EACA,MAAM;AAAA,IACL,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,EACV;AAAA,EACA,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,WAAW,IAAI,cAAc,EAAE;AAAA,EACvE,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,YAAY;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,UAAU;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,EACZ;AACD;","names":["createAdminT","useTemplates","jsx","jsxs","styles","createAdminT","useEffect","useState","jsx","jsxs","styles"]}
|
|
1
|
+
{"version":3,"sources":["../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx","../src/screens/TemplateCreateScreen.tsx"],"sourcesContent":["import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype FonderieApiError,\n\ttype ISetTemplateInput,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tformatAdminDate,\n\tgroupTemplatesByType,\n\tmissingTemplateLocales,\n} from '@fonderie/client';\nimport {\n\tuseTemplate,\n\tuseTemplatePreview,\n\tuseTemplateRevisions,\n\tuseTemplates,\n} from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n// Implicit variables the layout injects — an operator never supplies these, so\n// offering them as fields would just be noise.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\n\nconst PREVIEW_DEBOUNCE_MS = 500;\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n\t/**\n\t * A built-in email's default-locale row (the list's `system` flag): it can\n\t * be edited and rolled back, never deleted — Delete is not offered.\n\t */\n\tsystem?: boolean;\n\t/** Called after a delete; the template no longer exists. */\n\tonDeleted?: () => void;\n\t/**\n\t * Shows \"Add locale\": create a translation of this template. `locales` are\n\t * the ones the app uses elsewhere that this email lacks — suggest them first.\n\t */\n\tonAddLocale?: (type: string, context: { locales: string[] }) => void;\n\t/**\n\t * Shows a tab per locale this email exists in; receives the row to open.\n\t * Unsaved edits are confirmed before switching away.\n\t */\n\tonSelectLocale?: (template: ITemplateEntry) => void;\n\t/**\n\t * The CONSOLE's language (default English). Named `uiLocale` because\n\t * `locale` here is the template's own locale.\n\t */\n\tuiLocale?: AdminLocale | undefined;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n\tsystem = false,\n\tonDeleted,\n\tonAddLocale,\n\tonSelectLocale,\n\tuiLocale,\n}: ITemplateEditorScreenProps) {\n\t// Memoized: `run` below depends on it, and a new translator per render would\n\t// restart the preview debounce on every render.\n\tconst t = useMemo(() => createAdminT(uiLocale), [uiLocale]);\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\t// Saves go through the list hook (which re-fetches the template list after\n\t// each write); mounting it adds a template-list fetch to this single-template\n\t// editor — acceptable for an admin dashboard. Its isLoading/error track that\n\t// list fetch, so the save action keeps local state.\n\tconst { templates, saveTemplate, removeTemplate } = useTemplates(client);\n\t// This email's locales, for the tabs. The list is already fetched for saves.\n\tconst siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];\n\tconst [isDeleting, setIsDeleting] = useState(false);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\tconst [isSaving, setIsSaving] = useState(false);\n\tconst [saveError, setSaveError] = useState<FonderieApiError | null>(null);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// The sample values, as JSON the operator can edit. Text rather than an\n\t// object so a half-typed value does not have to parse on every keystroke.\n\tconst [sampleJson, setSampleJson] = useState('{}');\n\tconst [sampleError, setSampleError] = useState<string | null>(null);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst run = useCallback(async () => {\n\t\tlet data: Record<string, unknown> = {};\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(sampleJson || '{}');\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tsetSampleError(t('templates.editor.sampleNotObject'));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdata = parsed as Record<string, unknown>;\n\t\t\tsetSampleError(null);\n\t\t} catch {\n\t\t\tsetSampleError(t('templates.editor.sampleInvalid'));\n\t\t\treturn;\n\t\t}\n\t\tconst result = await renderPreview(\n\t\t\ttype,\n\t\t\t{ text, data, ...(subject ? { subject } : {}), ...(html ? { html } : {}) },\n\t\t\tlocale,\n\t\t);\n\t\t// The server reports which variables this content uses; seed the ones\n\t\t// that have no value yet with their own name, so the render shows where\n\t\t// each lands. Never overwrite something already typed.\n\t\tconst missing = result.variables.filter((v) => !IMPLICIT.has(v) && !(v in data));\n\t\tif (missing.length > 0) {\n\t\t\tconst seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };\n\t\t\tsetSampleJson(JSON.stringify(seeded, null, 2));\n\t\t}\n\t}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);\n\n\t// The preview is always live: it renders once the template loads, then again\n\t// PREVIEW_DEBOUNCE_MS after the last edit to the content or the sample data.\n\t// Debounced, not per keystroke, so typing a paragraph costs one request.\n\t// `run` is a new function on every keystroke (it closes over the fields), so\n\t// the effect keys on the fields themselves and calls the latest `run`.\n\tconst runRef = useRef(run);\n\trunRef.current = run;\n\tconst loadedType = template ? `${type}:${locale ?? ''}` : null;\n\t// The fields ARE the trigger: each edit restarts the debounce. The effect\n\t// body reads the latest `run` through the ref, so it does not use them.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: see above\n\tuseEffect(() => {\n\t\tif (!loadedType) return;\n\t\tconst timer = setTimeout(() => void runRef.current(), PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [loadedType, subject, html, text, sampleJson]);\n\n\t// Saving what is already stored would only mint an identical version (the\n\t// server treats it as a no-op anyway), so Save waits for a real change.\n\tconst dirty =\n\t\t!!template &&\n\t\t(subject !== (template.subject ?? '') ||\n\t\t\thtml !== (template.html ?? '') ||\n\t\t\ttext !== template.text ||\n\t\t\tactive !== template.active);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsetIsSaving(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\t// The list hook refreshes its own list; this screen renders the single\n\t\t\t// template, so re-read it too.\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch (err) {\n\t\t\t// useTemplates normalizes every failure to FonderieApiError before throwing.\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsSaving(false);\n\t\t}\n\t};\n\n\tconst switchTo = (entry: ITemplateEntry) => {\n\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\tonSelectLocale?.(entry);\n\t};\n\n\tconst handleDelete = async () => {\n\t\tconst message = locale\n\t\t\t? t('templates.editor.confirmDeleteLocale', { type, locale })\n\t\t\t: t('templates.editor.confirmDelete', { type });\n\t\tif (!window.confirm(message)) return;\n\t\tsetIsDeleting(true);\n\t\tsetSaveError(null);\n\t\ttry {\n\t\t\tawait removeTemplate(type, locale);\n\t\t\tonDeleted?.();\n\t\t} catch (err) {\n\t\t\tsetSaveError(err as FonderieApiError);\n\t\t} finally {\n\t\t\tsetIsDeleting(false);\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>{t('templates.editor.loading')}</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t{onSelectLocale || onAddLocale ? (\n\t\t\t\t<div style={styles.tabBar}>\n\t\t\t\t\t{onSelectLocale ? (\n\t\t\t\t\t\t// Each tab opens another stored row, so this is navigation, not an\n\t\t\t\t\t\t// ARIA tablist: plain buttons, the open one marked aria-current.\n\t\t\t\t\t\t<nav aria-label={t('templates.editor.locales')} style={styles.tabs}>\n\t\t\t\t\t\t\t{siblings.map((entry) => {\n\t\t\t\t\t\t\t\tconst selected = entry.locale === (locale ?? null);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\taria-current={selected ? 'true' : undefined}\n\t\t\t\t\t\t\t\t\t\tonClick={() => (selected ? undefined : switchTo(entry))}\n\t\t\t\t\t\t\t\t\t\tstyle={selected ? styles.tabSelected : entry.active ? styles.tab : styles.tabInactive}\n\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{entry.locale ?? t('templates.defaultChip')}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</nav>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{onAddLocale ? (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tstyle={{ ...styles.rollbackButton, marginLeft: 'auto' }}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (dirty && !window.confirm(t('templates.editor.discardChanges'))) return;\n\t\t\t\t\t\t\t\tonAddLocale(type, { locales: missingTemplateLocales(templates, type) });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('templates.editor.addLocale')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<p style={styles.meta}>\n\t\t\t\t<strong>{locale ?? t('templates.defaultLocale')}</strong> · v{template?.version ?? 1}\n\t\t\t\t{system ? ` · ${t('templates.editor.builtInNote')}` : ''}\n\t\t\t</p>\n\n\t\t\t<div style={styles.split}>\n\t\t\t\t<div style={styles.column}>\n\t\t\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\t\t\t{t('templates.subject')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\t\t\t{t('templates.htmlBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\t\t\trows={8}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\t\t\t{t('templates.textBody')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea\n\t\t\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\t\t\trows={4}\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{t('templates.editor.active')}\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t{saveError?.reason === 'VERSION_CONFLICT' ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{t('templates.editor.conflict')}{' '}\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetSaveError(null);\n\t\t\t\t\t\t\t\t\t\tvoid refresh();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('common.reload')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : saveError ? (\n\t\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : null}\n\n\t\t\t\t\t\t<div style={styles.saveRow}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={isSaving || !dirty}\n\t\t\t\t\t\t\t\tstyle={dirty ? styles.button : styles.buttonDisabled}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? t('common.saving') : t('common.save')}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{!dirty && !isSaving && (\n\t\t\t\t\t\t\t\t<span style={styles.meta}>{t('templates.editor.noChanges')}</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{!system ? (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\tonClick={() => void handleDelete()}\n\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t...styles.rollbackButton,\n\t\t\t\t\t\t\t\t\t\theight: 36,\n\t\t\t\t\t\t\t\t\t\tmarginLeft: 'auto',\n\t\t\t\t\t\t\t\t\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\t\t\t\t\t\t\t\t\tborderColor: 'color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)',\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isDeleting ? t('common.deleting') : t('common.delete')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<label style={styles.label} htmlFor=\"template-sample\">\n\t\t\t\t\t\t{t('templates.editor.sampleData')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"template-sample\"\n\t\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\t\tvalue={sampleJson}\n\t\t\t\t\t\tonChange={(event) => setSampleJson(event.target.value)}\n\t\t\t\t\t\trows={6}\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t/>\n\t\t\t\t\t{sampleError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{sampleError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div style={styles.previewColumn}>\n\t\t\t\t\t<div style={styles.previewHeader}>\n\t\t\t\t\t\t<span style={styles.label}>{t('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? t('templates.editor.rendering') : t('templates.editor.live')}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{previewError && (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{previewError.explanation}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{preview?.subject && <p style={styles.previewSubject}>{preview.subject}</p>}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin. This is operator-authored\n\t\t\t\t\t\t// HTML and it must never execute in the dashboard's origin, which is\n\t\t\t\t\t\t// where the admin token lives.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={t('templates.editor.previewTitle')}\n\t\t\t\t\t\t\tstyle={styles.previewFrame}\n\t\t\t\t\t\t\tsandbox=\"\"\n\t\t\t\t\t\t\tsrcDoc={preview.html}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : preview ? (\n\t\t\t\t\t\t<pre style={styles.previewText}>{preview.text}</pre>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p style={styles.meta}>{t('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>{t('templates.editor.history')}</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? t('templates.editor.unknownActor')} —{' '}\n\t\t\t\t\t\t\t\t\t{formatAdminDate(rev.createdAt, uiLocale)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{t('templates.editor.rollBack')}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst tab: CSSProperties = {\n\theight: 32,\n\tpadding: '0 10px',\n\tbackground: 'none',\n\tborder: 'none',\n\tborderBottom: '2px solid transparent',\n\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\tfontSize: 13,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 1400, boxSizing: 'border-box' },\n\t// One tab per locale of this email, underlined like a document's tabs.\n\ttabBar: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'flex-end',\n\t\tgap: 12,\n\t\tborderBottom: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tmargin: '4px 0 12px',\n\t\tpaddingBottom: 6,\n\t},\n\ttabs: { display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: -7 },\n\ttab,\n\ttabSelected: {\n\t\t...tab,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontWeight: 600,\n\t\tborderBottomColor: 'var(--fonderie-text,#171717)',\n\t\tcursor: 'default',\n\t},\n\ttabInactive: { ...tab, opacity: 0.55, textDecoration: 'line-through' },\n\t// Two columns, always: the editor on the left, the rendered result on the\n\t// right. Stacking the preview under a long form hid it below the fold.\n\tsplit: {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',\n\t\tgap: 24,\n\t\talignItems: 'start',\n\t},\n\tcolumn: { minWidth: 0, display: 'flex', flexDirection: 'column' },\n\t// Pinned while the form scrolls, so the preview stays in view as you edit.\n\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tsaveRow: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 16 },\n\tpreviewHeader: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tmarginTop: 12,\n\t},\n\tpreviewSubject: { fontSize: 14, fontWeight: 600, margin: '8px 0' },\n\tpreviewFrame: {\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t\twidth: '100%',\n\t\theight: 520,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t},\n\tpreviewText: {\n\t\twhiteSpace: 'pre-wrap',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 8,\n\t\tpadding: 12,\n\t\tfontSize: 13,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: { fontSize: 13, color: 'var(--fonderie-text-muted,#5c5c5c)', marginBottom: 20 },\n\tstatus: { padding: 24, textAlign: 'center', color: 'var(--fonderie-text-muted,#5c5c5c)' },\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\tbuttonDisabled: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\talignSelf: 'flex-start',\n\t\topacity: 0.45,\n\t\tcursor: 'not-allowed',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: {\n\t\tfontSize: 15,\n\t\tfontWeight: 600,\n\t\tmargin: '0 0 10px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t},\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 12,\n\t\tpadding: '9px 16px',\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 28,\n\t\tboxSizing: 'border-box',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 12.5,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\ttype ITemplateEntry,\n\tcreateAdminT,\n\tgroupTemplatesByType,\n} from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Receives the whole row — type AND locale, and whether it is built-in. The\n\t * list shows one row per email: clicking it opens the default locale, a\n\t * locale chip opens that locale.\n\t */\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n\t/** Shows a \"New template\" button. Receives the locales in use, to suggest. */\n\tonCreateTemplate?: (context: { locales: string[] }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nexport function TemplateListScreen({\n\tclient,\n\tonSelectTemplate,\n\tonCreateTemplate,\n\tlocale,\n}: ITemplateListScreenProps) {\n\tconst t = createAdminT(locale);\n\tconst { templates, isLoading, error } = useTemplates(client);\n\tconst groups = groupTemplatesByType(templates);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<div\n\t\t\t\tstyle={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}\n\t\t\t>\n\t\t\t\t<h1 style={styles.title}>{t('templates.list.title')}</h1>\n\t\t\t\t{onCreateTemplate ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\tonCreateTemplate({\n\t\t\t\t\t\t\t\tlocales: [\n\t\t\t\t\t\t\t\t\t...new Set(templates.map((row) => row.locale).filter((l): l is string => !!l)),\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstyle={styles.newButton}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('templates.list.newTemplate')}\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t\t<p style={styles.hint}>{t('templates.list.hint')}</p>\n\t\t\t{isLoading ? (\n\t\t\t\t<p style={styles.status}>{t('templates.list.loading')}</p>\n\t\t\t) : error ? (\n\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t{error.explanation}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t{groups.map((group) => (\n\t\t\t\t\t\t<li key={group.type} style={styles.row}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(group.primary)}\n\t\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style={styles.type}>{group.type}</span>\n\t\t\t\t\t\t\t\t{group.system ? (\n\t\t\t\t\t\t\t\t\t<span style={styles.builtIn}>{t('templates.builtInBadge')}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<span style={group.primary.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t\t<span style={styles.dot} />\n\t\t\t\t\t\t\t\t\t{group.primary.active ? t('common.status.active') : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t{/* Siblings of the row button, not inside it: a button cannot hold buttons. */}\n\t\t\t\t\t\t\t<span style={styles.chips}>\n\t\t\t\t\t\t\t\t{group.entries.map((entry) => {\n\t\t\t\t\t\t\t\t\tconst name = entry.locale ?? t('templates.defaultChip');\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tkey={entry.locale ?? ''}\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(entry)}\n\t\t\t\t\t\t\t\t\t\t\tstyle={entry.active ? styles.chip : styles.chipInactive}\n\t\t\t\t\t\t\t\t\t\t\taria-label={t('templates.list.openLocale', { type: group.type, locale: name })}\n\t\t\t\t\t\t\t\t\t\t\ttitle={entry.active ? undefined : t('common.status.inactive')}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Matches @fonderie/react-admin-screens' shell look with local values: this\n// package sits below the admin screens and cannot import their blocks.\nconst pill: CSSProperties = {\n\tdisplay: 'inline-flex',\n\talignItems: 'center',\n\tgap: 6,\n\tborderRadius: 999,\n\tpadding: '2px 9px 2px 8px',\n\tfontSize: 12,\n\tfontWeight: 600,\n\tlineHeight: 1.5,\n\tminWidth: 64,\n};\n\n// One per locale the email exists in. Monospace: they are language tags.\nconst chip: CSSProperties = {\n\theight: 24,\n\tboxSizing: 'border-box',\n\tpadding: '0 8px',\n\tborderRadius: 6,\n\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\tbackground: 'var(--fonderie-surface,#fff)',\n\tcolor: 'var(--fonderie-text,#171717)',\n\tfontSize: 12,\n\tfontFamily:\n\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\tcursor: 'pointer',\n};\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '32px 40px 64px', maxWidth: 1160, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: 0,\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\thint: { fontSize: 13.5, color: 'var(--fonderie-text-muted,#5c5c5c)', margin: '4px 0 24px' },\n\tstatus: { padding: '24px 0', color: 'var(--fonderie-text-muted,#5c5c5c)', fontSize: 13.5 },\n\terror: { color: 'var(--fonderie-danger,#e00)', marginBottom: 12, fontSize: 14 },\n\tlist: {\n\t\tlistStyle: 'none',\n\t\tpadding: 0,\n\t\tmargin: 0,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\toverflow: 'hidden',\n\t\tboxShadow: 'var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))',\n\t},\n\trow: {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tgap: 8,\n\t\tpaddingRight: 16,\n\t\tborderBottom: '1px solid var(--fonderie-border-light,#f5f5f5)',\n\t},\n\trowButton: {\n\t\tflex: 1,\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tgap: 16,\n\t\tpadding: '13px 16px',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: 'inherit',\n\t},\n\ttype: {\n\t\tfontSize: 13,\n\t\tfontWeight: 600,\n\t\tflex: 1,\n\t\tfontFamily:\n\t\t\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)',\n\t},\n\tchips: { display: 'flex', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 6 },\n\tchip,\n\tchipInactive: { ...chip, opacity: 0.5, textDecoration: 'line-through' },\n\tdot: { width: 6, height: 6, borderRadius: 999, background: 'currentColor' },\n\tbuiltIn: {\n\t\tfontSize: 11,\n\t\tfontWeight: 500,\n\t\tpadding: '1px 7px',\n\t\tborderRadius: 999,\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t},\n\tnewButton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 32,\n\t\tboxSizing: 'border-box',\n\t\tborderRadius: 6,\n\t\tpadding: '0 12px',\n\t\tfontSize: 13,\n\t\tfontWeight: 500,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t},\n\tactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-accent-strong,#009767)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)',\n\t},\n\tinactive: {\n\t\t...pill,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)',\n\t},\n};\n","import {\n\ttype AdminLocale,\n\ttype CourierAdminClient,\n\tFonderieApiError,\n\ttype ISetTemplateInput,\n\tcreateAdminT,\n} from '@fonderie/client';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateCreateScreenProps {\n\tclient: CourierAdminClient;\n\t/**\n\t * Given ⇒ add a LOCALE of this existing template: the type is fixed and the\n\t * form starts from its default-locale copy. Omitted ⇒ a brand-new template\n\t * type (one the app sends that has no stored copy yet).\n\t */\n\ttype?: string;\n\t/** Locales already in use, offered as suggestions. */\n\tlocales?: string[];\n\t/** Called with what was created, so the caller can open it in the editor. */\n\tonCreated?: (created: { type: string; locale: string | null }) => void;\n\t/** The console's language. Default English. */\n\tlocale?: AdminLocale | undefined;\n}\n\nconst TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;\nconst LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;\n\n// Creating is its own screen: the editor loads an existing row, and a new\n// template or locale has none yet. Once saved, the editor takes over.\nexport function TemplateCreateScreen({\n\tclient,\n\ttype,\n\tlocales = [],\n\tonCreated,\n\tlocale,\n}: ITemplateCreateScreenProps) {\n\tconst tr = createAdminT(locale);\n\tconst addingLocale = type !== undefined;\n\tconst [newType, setNewType] = useState('');\n\tconst [targetLocale, setTargetLocale] = useState('');\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [busy, setBusy] = useState(false);\n\n\t// A new locale starts from the default copy, so translating is editing.\n\tuseEffect(() => {\n\t\tif (!type) return;\n\t\tclient\n\t\t\t.getTemplate(type, null)\n\t\t\t.then(({ result }) => {\n\t\t\t\tsetSubject(result.subject ?? '');\n\t\t\t\tsetHtml(result.html ?? '');\n\t\t\t\tsetText(result.text);\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\tconst submit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tconst t = addingLocale ? (type as string) : newType.trim();\n\t\tconst l = targetLocale.trim() || null;\n\t\tif (!TYPE_RE.test(t)) return setError(tr('templates.create.errorType'));\n\t\tif (addingLocale && !l) return setError(tr('templates.create.errorLocaleRequired'));\n\t\tif (l && !LOCALE_RE.test(l)) return setError(tr('templates.create.errorLocale'));\n\t\tif (!text.trim()) return setError(tr('templates.create.errorText'));\n\t\tsetBusy(true);\n\t\ttry {\n\t\t\t// Refuse to overwrite: creating an existing row would replace it.\n\t\t\tconst exists = await client.getTemplate(t, l).then(\n\t\t\t\t() => true,\n\t\t\t\t(err: unknown) => !(err instanceof FonderieApiError && err.status === 404),\n\t\t\t);\n\t\t\tif (exists) {\n\t\t\t\tsetError(\n\t\t\t\t\tl\n\t\t\t\t\t\t? tr('templates.create.existsLocale', { type: t, locale: l })\n\t\t\t\t\t\t: tr('templates.create.exists', { type: t }),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst input: ISetTemplateInput = { text, active: true };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tawait client.setTemplate(t, input, l);\n\t\t\tonCreated?.({ type: t, locale: l });\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof FonderieApiError ? err.explanation : String(err));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>\n\t\t\t\t{addingLocale\n\t\t\t\t\t? tr('templates.create.titleLocale', { type: type as string })\n\t\t\t\t\t: tr('templates.create.titleNew')}\n\t\t\t</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{addingLocale ? tr('templates.create.hintLocale') : tr('templates.create.hintNew')}\n\t\t\t</p>\n\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t) : null}\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\tspellCheck={false}\n\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t))}\n\t\t\t\t</datalist>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t/>\n\t\t\t\t{error ? (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{error}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t{busy\n\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t</button>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nconst MONO =\n\t'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace)';\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: '8px 40px 64px', maxWidth: 760, boxSizing: 'border-box' },\n\ttitle: {\n\t\tfontSize: 22,\n\t\tfontWeight: 600,\n\t\tmargin: '8px 0 4px',\n\t\tletterSpacing: 'var(--fonderie-tracking-display,-0.05em)',\n\t\tlineHeight: 1.25,\n\t},\n\tmeta: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text-muted,#5c5c5c)',\n\t\tmarginBottom: 20,\n\t\tlineHeight: 1.5,\n\t},\n\tform: {\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tgap: 4,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 'var(--fonderie-radius-lg,8px)',\n\t\tpadding: '6px 20px 20px',\n\t},\n\tlabel: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },\n\tinput: {\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: '0 10px',\n\t\tfontSize: 13.5,\n\t\tfontFamily: 'inherit',\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t},\n\ttextarea: {\n\t\tborder: '1px solid var(--fonderie-border,#e0e0e0)',\n\t\tborderRadius: 6,\n\t\tpadding: 10,\n\t\tfontSize: 13,\n\t\tlineHeight: 1.55,\n\t\tbackground: 'var(--fonderie-surface,#fff)',\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tfontFamily: MONO,\n\t},\n\terror: {\n\t\tcolor: 'var(--fonderie-danger,#e00)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '8px 0',\n\t\tfontSize: 13.5,\n\t},\n\tbutton: {\n\t\tdisplay: 'inline-flex',\n\t\talignItems: 'center',\n\t\theight: 36,\n\t\tboxSizing: 'border-box',\n\t\tbackgroundColor: 'var(--fonderie-text,#171717)',\n\t\tcolor: 'var(--fonderie-surface,#fff)',\n\t\tpadding: '0 16px',\n\t\tborderRadius: 6,\n\t\tborder: '1px solid var(--fonderie-text,#171717)',\n\t\tfontSize: 13.5,\n\t\tfontWeight: 600,\n\t\tfontFamily: 'inherit',\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t\tmarginTop: 16,\n\t},\n};\n"],"mappings":"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","names":["createAdminT","groupTemplatesByType","useTemplates","jsx","jsxs","styles","createAdminT","useEffect","useState","jsx","jsxs","styles"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonderie/react-courier-admin-screens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Pre-built React screens for the Fonderie courier admin API — template list, template editor with revision history and rollback.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fonderiejs",
|