@fonderie/react-courier-admin-screens 0.9.0 → 0.10.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 +159 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -718,10 +718,13 @@ var styles2 = {
|
|
|
718
718
|
|
|
719
719
|
// src/screens/TemplateCreateScreen.tsx
|
|
720
720
|
var import_client3 = require("@fonderie/client");
|
|
721
|
+
var import_react_courier_admin3 = require("@fonderie/react-courier-admin");
|
|
721
722
|
var import_react2 = require("react");
|
|
722
723
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
723
724
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
724
725
|
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
726
|
+
var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
727
|
+
var PREVIEW_DEBOUNCE_MS2 = 500;
|
|
725
728
|
function TemplateCreateScreen({
|
|
726
729
|
client,
|
|
727
730
|
type,
|
|
@@ -738,14 +741,48 @@ function TemplateCreateScreen({
|
|
|
738
741
|
const [text, setText] = (0, import_react2.useState)("");
|
|
739
742
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
740
743
|
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
744
|
+
const [source, setSource] = (0, import_react2.useState)(
|
|
745
|
+
null
|
|
746
|
+
);
|
|
747
|
+
const { preview, isPreviewing, error: previewError, renderPreview } = (0, import_react_courier_admin3.useTemplatePreview)(client);
|
|
748
|
+
const variables = (0, import_react2.useRef)([]);
|
|
741
749
|
(0, import_react2.useEffect)(() => {
|
|
742
750
|
if (!type) return;
|
|
743
751
|
client.getTemplate(type, null).then(({ result }) => {
|
|
744
752
|
setSubject(result.subject ?? "");
|
|
745
753
|
setHtml(result.html ?? "");
|
|
746
754
|
setText(result.text);
|
|
755
|
+
setSource({ subject: result.subject ?? "", html: result.html ?? "", text: result.text });
|
|
747
756
|
}).catch(() => void 0);
|
|
748
757
|
}, [client, type]);
|
|
758
|
+
const previewType = addingLocale ? type : newType.trim();
|
|
759
|
+
const previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;
|
|
760
|
+
(0, import_react2.useEffect)(() => {
|
|
761
|
+
if (!TYPE_RE.test(previewType) || !text.trim()) return;
|
|
762
|
+
const timer = setTimeout(async () => {
|
|
763
|
+
const input = { text, ...subject ? { subject } : {}, ...html ? { html } : {} };
|
|
764
|
+
const render = () => renderPreview(
|
|
765
|
+
previewType,
|
|
766
|
+
{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },
|
|
767
|
+
previewLocale
|
|
768
|
+
);
|
|
769
|
+
try {
|
|
770
|
+
const result = await render();
|
|
771
|
+
const used = result.variables.filter((v) => !IMPLICIT2.has(v));
|
|
772
|
+
if (used.some((v) => !variables.current.includes(v))) {
|
|
773
|
+
variables.current = used;
|
|
774
|
+
await render();
|
|
775
|
+
}
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
}, PREVIEW_DEBOUNCE_MS2);
|
|
779
|
+
return () => clearTimeout(timer);
|
|
780
|
+
}, [renderPreview, previewType, previewLocale, subject, html, text]);
|
|
781
|
+
const untranslated = source ? [
|
|
782
|
+
subject && subject === source.subject ? tr("templates.subject") : null,
|
|
783
|
+
html && html === source.html ? tr("templates.htmlBody") : null,
|
|
784
|
+
text === source.text ? tr("templates.textBody") : null
|
|
785
|
+
].filter((f) => f !== null) : [];
|
|
749
786
|
const submit = async (e) => {
|
|
750
787
|
e.preventDefault();
|
|
751
788
|
setError(null);
|
|
@@ -781,75 +818,140 @@ function TemplateCreateScreen({
|
|
|
781
818
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
|
|
782
819
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
|
|
783
820
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
|
|
784
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("
|
|
785
|
-
|
|
786
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
821
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.split, children: [
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
823
|
+
!addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
824
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
826
|
+
"input",
|
|
827
|
+
{
|
|
828
|
+
id: "new-template-type",
|
|
829
|
+
style: styles3.input,
|
|
830
|
+
value: newType,
|
|
831
|
+
placeholder: "weekly-digest",
|
|
832
|
+
autoComplete: "off",
|
|
833
|
+
spellCheck: false,
|
|
834
|
+
onChange: (e) => setNewType(e.target.value)
|
|
835
|
+
}
|
|
836
|
+
)
|
|
837
|
+
] }) : null,
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
|
|
787
839
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
788
840
|
"input",
|
|
789
841
|
{
|
|
790
|
-
id: "new-template-
|
|
842
|
+
id: "new-template-locale",
|
|
791
843
|
style: styles3.input,
|
|
792
|
-
value:
|
|
793
|
-
placeholder: "
|
|
844
|
+
value: targetLocale,
|
|
845
|
+
placeholder: "fr",
|
|
846
|
+
list: "new-template-locales",
|
|
794
847
|
autoComplete: "off",
|
|
795
848
|
spellCheck: false,
|
|
796
|
-
onChange: (e) =>
|
|
849
|
+
onChange: (e) => setTargetLocale(e.target.value)
|
|
797
850
|
}
|
|
798
|
-
)
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
),
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
851
|
+
),
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
|
|
854
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
855
|
+
"input",
|
|
856
|
+
{
|
|
857
|
+
id: "new-template-subject",
|
|
858
|
+
style: styles3.input,
|
|
859
|
+
value: subject,
|
|
860
|
+
onChange: (e) => setSubject(e.target.value)
|
|
861
|
+
}
|
|
862
|
+
),
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
865
|
+
"textarea",
|
|
866
|
+
{
|
|
867
|
+
id: "new-template-html",
|
|
868
|
+
style: { ...styles3.textarea, minHeight: 160 },
|
|
869
|
+
value: html,
|
|
870
|
+
onChange: (e) => setHtml(e.target.value)
|
|
871
|
+
}
|
|
872
|
+
),
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
875
|
+
"textarea",
|
|
876
|
+
{
|
|
877
|
+
id: "new-template-text",
|
|
878
|
+
style: { ...styles3.textarea, minHeight: 100 },
|
|
879
|
+
value: text,
|
|
880
|
+
onChange: (e) => setText(e.target.value)
|
|
881
|
+
}
|
|
882
|
+
),
|
|
883
|
+
untranslated.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.notice, children: tr("templates.create.untranslated", { fields: untranslated.join(", ") }) }) : null,
|
|
884
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
|
|
886
|
+
] }),
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewColumn, children: [
|
|
888
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.previewHeader, children: [
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.label, children: tr("templates.editor.preview") }),
|
|
890
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles3.meta, children: isPreviewing ? tr("templates.editor.rendering") : tr("templates.editor.live") })
|
|
891
|
+
] }),
|
|
892
|
+
previewError ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: previewError.explanation }) : null,
|
|
893
|
+
preview?.subject ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.previewSubject, children: preview.subject }) : null,
|
|
894
|
+
preview?.html ? (
|
|
895
|
+
// sandbox="" — no scripts, no same-origin: operator-authored HTML must
|
|
896
|
+
// never execute in the console's origin.
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
898
|
+
"iframe",
|
|
899
|
+
{
|
|
900
|
+
title: tr("templates.editor.previewTitle"),
|
|
901
|
+
style: styles3.previewFrame,
|
|
902
|
+
sandbox: "",
|
|
903
|
+
srcDoc: preview.html
|
|
904
|
+
}
|
|
905
|
+
)
|
|
906
|
+
) : preview ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { style: styles3.previewText, children: preview.text }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: tr("templates.editor.nothingRendered") })
|
|
907
|
+
] })
|
|
847
908
|
] })
|
|
848
909
|
] });
|
|
849
910
|
}
|
|
850
911
|
var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
|
|
851
912
|
var styles3 = {
|
|
852
|
-
container: { padding: "8px 40px 64px", maxWidth:
|
|
913
|
+
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
914
|
+
// The editor's layout: the form on the left, the render pinned on the right.
|
|
915
|
+
split: {
|
|
916
|
+
display: "grid",
|
|
917
|
+
gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
|
|
918
|
+
gap: 24,
|
|
919
|
+
alignItems: "start"
|
|
920
|
+
},
|
|
921
|
+
previewColumn: {
|
|
922
|
+
minWidth: 0,
|
|
923
|
+
display: "flex",
|
|
924
|
+
flexDirection: "column",
|
|
925
|
+
position: "sticky",
|
|
926
|
+
top: 16
|
|
927
|
+
},
|
|
928
|
+
previewHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
929
|
+
previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
|
|
930
|
+
previewFrame: {
|
|
931
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
|
|
932
|
+
width: "100%",
|
|
933
|
+
height: 520,
|
|
934
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
935
|
+
borderRadius: 8,
|
|
936
|
+
background: "var(--fonderie-surface,#fff)"
|
|
937
|
+
},
|
|
938
|
+
previewText: {
|
|
939
|
+
whiteSpace: "pre-wrap",
|
|
940
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
941
|
+
borderRadius: 8,
|
|
942
|
+
padding: 12,
|
|
943
|
+
fontSize: 13,
|
|
944
|
+
fontFamily: MONO
|
|
945
|
+
},
|
|
946
|
+
// The console's warning token (the shell's amber), readable in both themes.
|
|
947
|
+
notice: {
|
|
948
|
+
fontSize: 13,
|
|
949
|
+
color: "var(--fonderie-text,#171717)",
|
|
950
|
+
background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
|
|
951
|
+
borderRadius: 6,
|
|
952
|
+
padding: "8px 12px",
|
|
953
|
+
margin: "12px 0 0"
|
|
954
|
+
},
|
|
853
955
|
title: {
|
|
854
956
|
fontSize: 22,
|
|
855
957
|
fontWeight: 600,
|
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\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"]}
|
|
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 { useTemplatePreview } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useRef, 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// Variables the layout injects — never sample values, as in the editor.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\nconst PREVIEW_DEBOUNCE_MS = 500;\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\t// The default copy this locale started from, to flag fields not yet translated.\n\tconst [source, setSource] = useState<{ subject: string; html: string; text: string } | null>(\n\t\tnull,\n\t);\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// Each variable renders as its own name, so the preview shows where it lands.\n\t// Learned from the server's answer, not re-parsed here.\n\tconst variables = useRef<string[]>([]);\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\tsetSource({ subject: result.subject ?? '', html: result.html ?? '', text: result.text });\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\t// Live preview, as in the editor: translating is where it matters most — a\n\t// half-translated email looks finished in a textarea and wrong in the render.\n\tconst previewType = addingLocale ? (type as string) : newType.trim();\n\tconst previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;\n\tuseEffect(() => {\n\t\tif (!TYPE_RE.test(previewType) || !text.trim()) return;\n\t\tconst timer = setTimeout(async () => {\n\t\t\tconst input = { text, ...(subject ? { subject } : {}), ...(html ? { html } : {}) };\n\t\t\tconst render = () =>\n\t\t\t\trenderPreview(\n\t\t\t\t\tpreviewType,\n\t\t\t\t\t{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },\n\t\t\t\t\tpreviewLocale,\n\t\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await render();\n\t\t\t\tconst used = result.variables.filter((v) => !IMPLICIT.has(v));\n\t\t\t\t// A variable not seen before rendered empty: render once more with it.\n\t\t\t\tif (used.some((v) => !variables.current.includes(v))) {\n\t\t\t\t\tvariables.current = used;\n\t\t\t\t\tawait render();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Shown through previewError.\n\t\t\t}\n\t\t}, PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [renderPreview, previewType, previewLocale, subject, html, text]);\n\n\tconst untranslated = source\n\t\t? [\n\t\t\t\tsubject && subject === source.subject ? tr('templates.subject') : null,\n\t\t\t\thtml && html === source.html ? tr('templates.htmlBody') : null,\n\t\t\t\ttext === source.text ? tr('templates.textBody') : null,\n\t\t\t].filter((f): f is string => f !== null)\n\t\t: [];\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<div style={styles.split}>\n\t\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</datalist>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t{untranslated.length > 0 ? (\n\t\t\t\t\t\t<p style={styles.notice}>\n\t\t\t\t\t\t\t{tr('templates.create.untranslated', { fields: untranslated.join(', ') })}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{error ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t\t{busy\n\t\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\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}>{tr('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? tr('templates.editor.rendering') : tr('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) : null}\n\t\t\t\t\t{preview?.subject ? <p style={styles.previewSubject}>{preview.subject}</p> : null}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin: operator-authored HTML must\n\t\t\t\t\t\t// never execute in the console's origin.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={tr('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}>{tr('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\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: 1400, boxSizing: 'border-box' },\n\t// The editor's layout: the form on the left, the render pinned on the right.\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\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tpreviewHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' },\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: MONO,\n\t},\n\t// The console's warning token (the shell's amber), readable in both themes.\n\tnotice: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '12px 0 0',\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: {\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_courier_admin","import_react","import_jsx_runtime","IMPLICIT","PREVIEW_DEBOUNCE_MS","styles"]}
|
package/dist/index.js
CHANGED
|
@@ -706,10 +706,13 @@ import {
|
|
|
706
706
|
FonderieApiError,
|
|
707
707
|
createAdminT as createAdminT3
|
|
708
708
|
} from "@fonderie/client";
|
|
709
|
-
import {
|
|
709
|
+
import { useTemplatePreview as useTemplatePreview2 } from "@fonderie/react-courier-admin";
|
|
710
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
710
711
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
711
712
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
712
713
|
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
714
|
+
var IMPLICIT2 = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
715
|
+
var PREVIEW_DEBOUNCE_MS2 = 500;
|
|
713
716
|
function TemplateCreateScreen({
|
|
714
717
|
client,
|
|
715
718
|
type,
|
|
@@ -726,14 +729,48 @@ function TemplateCreateScreen({
|
|
|
726
729
|
const [text, setText] = useState2("");
|
|
727
730
|
const [error, setError] = useState2(null);
|
|
728
731
|
const [busy, setBusy] = useState2(false);
|
|
732
|
+
const [source, setSource] = useState2(
|
|
733
|
+
null
|
|
734
|
+
);
|
|
735
|
+
const { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview2(client);
|
|
736
|
+
const variables = useRef2([]);
|
|
729
737
|
useEffect2(() => {
|
|
730
738
|
if (!type) return;
|
|
731
739
|
client.getTemplate(type, null).then(({ result }) => {
|
|
732
740
|
setSubject(result.subject ?? "");
|
|
733
741
|
setHtml(result.html ?? "");
|
|
734
742
|
setText(result.text);
|
|
743
|
+
setSource({ subject: result.subject ?? "", html: result.html ?? "", text: result.text });
|
|
735
744
|
}).catch(() => void 0);
|
|
736
745
|
}, [client, type]);
|
|
746
|
+
const previewType = addingLocale ? type : newType.trim();
|
|
747
|
+
const previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;
|
|
748
|
+
useEffect2(() => {
|
|
749
|
+
if (!TYPE_RE.test(previewType) || !text.trim()) return;
|
|
750
|
+
const timer = setTimeout(async () => {
|
|
751
|
+
const input = { text, ...subject ? { subject } : {}, ...html ? { html } : {} };
|
|
752
|
+
const render = () => renderPreview(
|
|
753
|
+
previewType,
|
|
754
|
+
{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },
|
|
755
|
+
previewLocale
|
|
756
|
+
);
|
|
757
|
+
try {
|
|
758
|
+
const result = await render();
|
|
759
|
+
const used = result.variables.filter((v) => !IMPLICIT2.has(v));
|
|
760
|
+
if (used.some((v) => !variables.current.includes(v))) {
|
|
761
|
+
variables.current = used;
|
|
762
|
+
await render();
|
|
763
|
+
}
|
|
764
|
+
} catch {
|
|
765
|
+
}
|
|
766
|
+
}, PREVIEW_DEBOUNCE_MS2);
|
|
767
|
+
return () => clearTimeout(timer);
|
|
768
|
+
}, [renderPreview, previewType, previewLocale, subject, html, text]);
|
|
769
|
+
const untranslated = source ? [
|
|
770
|
+
subject && subject === source.subject ? tr("templates.subject") : null,
|
|
771
|
+
html && html === source.html ? tr("templates.htmlBody") : null,
|
|
772
|
+
text === source.text ? tr("templates.textBody") : null
|
|
773
|
+
].filter((f) => f !== null) : [];
|
|
737
774
|
const submit = async (e) => {
|
|
738
775
|
e.preventDefault();
|
|
739
776
|
setError(null);
|
|
@@ -769,75 +806,140 @@ function TemplateCreateScreen({
|
|
|
769
806
|
return /* @__PURE__ */ jsxs3("div", { style: styles3.container, children: [
|
|
770
807
|
/* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
|
|
771
808
|
/* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
|
|
772
|
-
/* @__PURE__ */ jsxs3("
|
|
773
|
-
|
|
774
|
-
/* @__PURE__ */
|
|
809
|
+
/* @__PURE__ */ jsxs3("div", { style: styles3.split, children: [
|
|
810
|
+
/* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
811
|
+
!addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
812
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
|
|
813
|
+
/* @__PURE__ */ jsx3(
|
|
814
|
+
"input",
|
|
815
|
+
{
|
|
816
|
+
id: "new-template-type",
|
|
817
|
+
style: styles3.input,
|
|
818
|
+
value: newType,
|
|
819
|
+
placeholder: "weekly-digest",
|
|
820
|
+
autoComplete: "off",
|
|
821
|
+
spellCheck: false,
|
|
822
|
+
onChange: (e) => setNewType(e.target.value)
|
|
823
|
+
}
|
|
824
|
+
)
|
|
825
|
+
] }) : null,
|
|
826
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
|
|
775
827
|
/* @__PURE__ */ jsx3(
|
|
776
828
|
"input",
|
|
777
829
|
{
|
|
778
|
-
id: "new-template-
|
|
830
|
+
id: "new-template-locale",
|
|
779
831
|
style: styles3.input,
|
|
780
|
-
value:
|
|
781
|
-
placeholder: "
|
|
832
|
+
value: targetLocale,
|
|
833
|
+
placeholder: "fr",
|
|
834
|
+
list: "new-template-locales",
|
|
782
835
|
autoComplete: "off",
|
|
783
836
|
spellCheck: false,
|
|
784
|
-
onChange: (e) =>
|
|
837
|
+
onChange: (e) => setTargetLocale(e.target.value)
|
|
785
838
|
}
|
|
786
|
-
)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ jsx3("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ jsx3("option", { value: l }, l)) }),
|
|
841
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
|
|
842
|
+
/* @__PURE__ */ jsx3(
|
|
843
|
+
"input",
|
|
844
|
+
{
|
|
845
|
+
id: "new-template-subject",
|
|
846
|
+
style: styles3.input,
|
|
847
|
+
value: subject,
|
|
848
|
+
onChange: (e) => setSubject(e.target.value)
|
|
849
|
+
}
|
|
850
|
+
),
|
|
851
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
|
|
852
|
+
/* @__PURE__ */ jsx3(
|
|
853
|
+
"textarea",
|
|
854
|
+
{
|
|
855
|
+
id: "new-template-html",
|
|
856
|
+
style: { ...styles3.textarea, minHeight: 160 },
|
|
857
|
+
value: html,
|
|
858
|
+
onChange: (e) => setHtml(e.target.value)
|
|
859
|
+
}
|
|
860
|
+
),
|
|
861
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
|
|
862
|
+
/* @__PURE__ */ jsx3(
|
|
863
|
+
"textarea",
|
|
864
|
+
{
|
|
865
|
+
id: "new-template-text",
|
|
866
|
+
style: { ...styles3.textarea, minHeight: 100 },
|
|
867
|
+
value: text,
|
|
868
|
+
onChange: (e) => setText(e.target.value)
|
|
869
|
+
}
|
|
870
|
+
),
|
|
871
|
+
untranslated.length > 0 ? /* @__PURE__ */ jsx3("p", { style: styles3.notice, children: tr("templates.create.untranslated", { fields: untranslated.join(", ") }) }) : null,
|
|
872
|
+
error ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: error }) : null,
|
|
873
|
+
/* @__PURE__ */ jsx3("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
|
|
874
|
+
] }),
|
|
875
|
+
/* @__PURE__ */ jsxs3("div", { style: styles3.previewColumn, children: [
|
|
876
|
+
/* @__PURE__ */ jsxs3("div", { style: styles3.previewHeader, children: [
|
|
877
|
+
/* @__PURE__ */ jsx3("span", { style: styles3.label, children: tr("templates.editor.preview") }),
|
|
878
|
+
/* @__PURE__ */ jsx3("span", { style: styles3.meta, children: isPreviewing ? tr("templates.editor.rendering") : tr("templates.editor.live") })
|
|
879
|
+
] }),
|
|
880
|
+
previewError ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: previewError.explanation }) : null,
|
|
881
|
+
preview?.subject ? /* @__PURE__ */ jsx3("p", { style: styles3.previewSubject, children: preview.subject }) : null,
|
|
882
|
+
preview?.html ? (
|
|
883
|
+
// sandbox="" — no scripts, no same-origin: operator-authored HTML must
|
|
884
|
+
// never execute in the console's origin.
|
|
885
|
+
/* @__PURE__ */ jsx3(
|
|
886
|
+
"iframe",
|
|
887
|
+
{
|
|
888
|
+
title: tr("templates.editor.previewTitle"),
|
|
889
|
+
style: styles3.previewFrame,
|
|
890
|
+
sandbox: "",
|
|
891
|
+
srcDoc: preview.html
|
|
892
|
+
}
|
|
893
|
+
)
|
|
894
|
+
) : preview ? /* @__PURE__ */ jsx3("pre", { style: styles3.previewText, children: preview.text }) : /* @__PURE__ */ jsx3("p", { style: styles3.meta, children: tr("templates.editor.nothingRendered") })
|
|
895
|
+
] })
|
|
835
896
|
] })
|
|
836
897
|
] });
|
|
837
898
|
}
|
|
838
899
|
var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
|
|
839
900
|
var styles3 = {
|
|
840
|
-
container: { padding: "8px 40px 64px", maxWidth:
|
|
901
|
+
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
902
|
+
// The editor's layout: the form on the left, the render pinned on the right.
|
|
903
|
+
split: {
|
|
904
|
+
display: "grid",
|
|
905
|
+
gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
|
|
906
|
+
gap: 24,
|
|
907
|
+
alignItems: "start"
|
|
908
|
+
},
|
|
909
|
+
previewColumn: {
|
|
910
|
+
minWidth: 0,
|
|
911
|
+
display: "flex",
|
|
912
|
+
flexDirection: "column",
|
|
913
|
+
position: "sticky",
|
|
914
|
+
top: 16
|
|
915
|
+
},
|
|
916
|
+
previewHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
917
|
+
previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
|
|
918
|
+
previewFrame: {
|
|
919
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
|
|
920
|
+
width: "100%",
|
|
921
|
+
height: 520,
|
|
922
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
923
|
+
borderRadius: 8,
|
|
924
|
+
background: "var(--fonderie-surface,#fff)"
|
|
925
|
+
},
|
|
926
|
+
previewText: {
|
|
927
|
+
whiteSpace: "pre-wrap",
|
|
928
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
929
|
+
borderRadius: 8,
|
|
930
|
+
padding: 12,
|
|
931
|
+
fontSize: 13,
|
|
932
|
+
fontFamily: MONO
|
|
933
|
+
},
|
|
934
|
+
// The console's warning token (the shell's amber), readable in both themes.
|
|
935
|
+
notice: {
|
|
936
|
+
fontSize: 13,
|
|
937
|
+
color: "var(--fonderie-text,#171717)",
|
|
938
|
+
background: "color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)",
|
|
939
|
+
borderRadius: 6,
|
|
940
|
+
padding: "8px 12px",
|
|
941
|
+
margin: "12px 0 0"
|
|
942
|
+
},
|
|
841
943
|
title: {
|
|
842
944
|
fontSize: 22,
|
|
843
945
|
fontWeight: 600,
|
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\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"]}
|
|
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 { useTemplatePreview } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useRef, 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// Variables the layout injects — never sample values, as in the editor.\nconst IMPLICIT = new Set(['subject', 'preheader', 'brandName']);\nconst PREVIEW_DEBOUNCE_MS = 500;\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\t// The default copy this locale started from, to flag fields not yet translated.\n\tconst [source, setSource] = useState<{ subject: string; html: string; text: string } | null>(\n\t\tnull,\n\t);\n\tconst { preview, isPreviewing, error: previewError, renderPreview } = useTemplatePreview(client);\n\t// Each variable renders as its own name, so the preview shows where it lands.\n\t// Learned from the server's answer, not re-parsed here.\n\tconst variables = useRef<string[]>([]);\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\tsetSource({ subject: result.subject ?? '', html: result.html ?? '', text: result.text });\n\t\t\t})\n\t\t\t.catch(() => undefined);\n\t}, [client, type]);\n\n\t// Live preview, as in the editor: translating is where it matters most — a\n\t// half-translated email looks finished in a textarea and wrong in the render.\n\tconst previewType = addingLocale ? (type as string) : newType.trim();\n\tconst previewLocale = LOCALE_RE.test(targetLocale.trim()) ? targetLocale.trim() : null;\n\tuseEffect(() => {\n\t\tif (!TYPE_RE.test(previewType) || !text.trim()) return;\n\t\tconst timer = setTimeout(async () => {\n\t\t\tconst input = { text, ...(subject ? { subject } : {}), ...(html ? { html } : {}) };\n\t\t\tconst render = () =>\n\t\t\t\trenderPreview(\n\t\t\t\t\tpreviewType,\n\t\t\t\t\t{ ...input, data: Object.fromEntries(variables.current.map((v) => [v, v])) },\n\t\t\t\t\tpreviewLocale,\n\t\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst result = await render();\n\t\t\t\tconst used = result.variables.filter((v) => !IMPLICIT.has(v));\n\t\t\t\t// A variable not seen before rendered empty: render once more with it.\n\t\t\t\tif (used.some((v) => !variables.current.includes(v))) {\n\t\t\t\t\tvariables.current = used;\n\t\t\t\t\tawait render();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Shown through previewError.\n\t\t\t}\n\t\t}, PREVIEW_DEBOUNCE_MS);\n\t\treturn () => clearTimeout(timer);\n\t}, [renderPreview, previewType, previewLocale, subject, html, text]);\n\n\tconst untranslated = source\n\t\t? [\n\t\t\t\tsubject && subject === source.subject ? tr('templates.subject') : null,\n\t\t\t\thtml && html === source.html ? tr('templates.htmlBody') : null,\n\t\t\t\ttext === source.text ? tr('templates.textBody') : null,\n\t\t\t].filter((f): f is string => f !== null)\n\t\t: [];\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<div style={styles.split}>\n\t\t\t\t<form style={styles.form} onSubmit={(e) => void submit(e)}>\n\t\t\t\t\t{!addingLocale ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-type\">\n\t\t\t\t\t\t\t\t{tr('templates.create.type')}\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tid=\"new-template-type\"\n\t\t\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\t\t\tvalue={newType}\n\t\t\t\t\t\t\t\tplaceholder=\"weekly-digest\"\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tonChange={(e) => setNewType(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-locale\">\n\t\t\t\t\t\t{addingLocale ? tr('templates.create.locale') : tr('templates.create.localeOptional')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-locale\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={targetLocale}\n\t\t\t\t\t\tplaceholder=\"fr\"\n\t\t\t\t\t\tlist=\"new-template-locales\"\n\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\tonChange={(e) => setTargetLocale(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<datalist id=\"new-template-locales\">\n\t\t\t\t\t\t{[...new Set(locales)].map((l) => (\n\t\t\t\t\t\t\t<option key={l} value={l} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</datalist>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-subject\">\n\t\t\t\t\t\t{tr('templates.subject')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<input\n\t\t\t\t\t\tid=\"new-template-subject\"\n\t\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\t\tvalue={subject}\n\t\t\t\t\t\tonChange={(e) => setSubject(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-html\">\n\t\t\t\t\t\t{tr('templates.htmlBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-html\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 160 }}\n\t\t\t\t\t\tvalue={html}\n\t\t\t\t\t\tonChange={(e) => setHtml(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<label style={styles.label} htmlFor=\"new-template-text\">\n\t\t\t\t\t\t{tr('templates.textBody')}\n\t\t\t\t\t</label>\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tid=\"new-template-text\"\n\t\t\t\t\t\tstyle={{ ...styles.textarea, minHeight: 100 }}\n\t\t\t\t\t\tvalue={text}\n\t\t\t\t\t\tonChange={(e) => setText(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t{untranslated.length > 0 ? (\n\t\t\t\t\t\t<p style={styles.notice}>\n\t\t\t\t\t\t\t{tr('templates.create.untranslated', { fields: untranslated.join(', ') })}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{error ? (\n\t\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<button type=\"submit\" disabled={busy} style={styles.button}>\n\t\t\t\t\t\t{busy\n\t\t\t\t\t\t\t? tr('templates.create.creating')\n\t\t\t\t\t\t\t: addingLocale\n\t\t\t\t\t\t\t\t? tr('templates.create.submitLocale')\n\t\t\t\t\t\t\t\t: tr('templates.create.submitNew')}\n\t\t\t\t\t</button>\n\t\t\t\t</form>\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}>{tr('templates.editor.preview')}</span>\n\t\t\t\t\t\t<span style={styles.meta}>\n\t\t\t\t\t\t\t{isPreviewing ? tr('templates.editor.rendering') : tr('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) : null}\n\t\t\t\t\t{preview?.subject ? <p style={styles.previewSubject}>{preview.subject}</p> : null}\n\t\t\t\t\t{preview?.html ? (\n\t\t\t\t\t\t// sandbox=\"\" — no scripts, no same-origin: operator-authored HTML must\n\t\t\t\t\t\t// never execute in the console's origin.\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\ttitle={tr('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}>{tr('templates.editor.nothingRendered')}</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\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: 1400, boxSizing: 'border-box' },\n\t// The editor's layout: the form on the left, the render pinned on the right.\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\tpreviewColumn: {\n\t\tminWidth: 0,\n\t\tdisplay: 'flex',\n\t\tflexDirection: 'column',\n\t\tposition: 'sticky',\n\t\ttop: 16,\n\t},\n\tpreviewHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' },\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: MONO,\n\t},\n\t// The console's warning token (the shell's amber), readable in both themes.\n\tnotice: {\n\t\tfontSize: 13,\n\t\tcolor: 'var(--fonderie-text,#171717)',\n\t\tbackground: 'color-mix(in srgb, var(--fonderie-warning,#f5a623) 16%, transparent)',\n\t\tborderRadius: 6,\n\t\tpadding: '8px 12px',\n\t\tmargin: '12px 0 0',\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: {\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","useTemplatePreview","useEffect","useRef","useState","jsx","jsxs","IMPLICIT","PREVIEW_DEBOUNCE_MS","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.10.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",
|