@fonderie/react-courier-admin-screens 0.8.0 → 0.9.0

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