@fonderie/react-courier-admin-screens 0.6.0 → 0.8.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
@@ -20,12 +20,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ TemplateCreateScreen: () => TemplateCreateScreen,
23
24
  TemplateEditorScreen: () => TemplateEditorScreen,
24
25
  TemplateListScreen: () => TemplateListScreen
25
26
  });
26
27
  module.exports = __toCommonJS(index_exports);
27
28
 
28
29
  // src/screens/TemplateEditorScreen.tsx
30
+ var import_client = require("@fonderie/client");
29
31
  var import_react_courier_admin = require("@fonderie/react-courier-admin");
30
32
  var import_react = require("react");
31
33
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -35,10 +37,16 @@ function TemplateEditorScreen({
35
37
  client,
36
38
  type,
37
39
  locale,
38
- onSaved
40
+ onSaved,
41
+ system = false,
42
+ onDeleted,
43
+ onAddLocale,
44
+ uiLocale
39
45
  }) {
46
+ const t = (0, import_react.useMemo)(() => (0, import_client.createAdminT)(uiLocale), [uiLocale]);
40
47
  const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
41
- const { saveTemplate } = (0, import_react_courier_admin.useTemplates)(client);
48
+ const { saveTemplate, removeTemplate } = (0, import_react_courier_admin.useTemplates)(client);
49
+ const [isDeleting, setIsDeleting] = (0, import_react.useState)(false);
42
50
  const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
43
51
  const [isSaving, setIsSaving] = (0, import_react.useState)(false);
44
52
  const [saveError, setSaveError] = (0, import_react.useState)(null);
@@ -61,13 +69,13 @@ function TemplateEditorScreen({
61
69
  try {
62
70
  const parsed = JSON.parse(sampleJson || "{}");
63
71
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
64
- setSampleError("Sample data must be a JSON object.");
72
+ setSampleError(t("templates.editor.sampleNotObject"));
65
73
  return;
66
74
  }
67
75
  data = parsed;
68
76
  setSampleError(null);
69
77
  } catch {
70
- setSampleError("Sample data is not valid JSON.");
78
+ setSampleError(t("templates.editor.sampleInvalid"));
71
79
  return;
72
80
  }
73
81
  const result = await renderPreview(
@@ -80,7 +88,7 @@ function TemplateEditorScreen({
80
88
  const seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };
81
89
  setSampleJson(JSON.stringify(seeded, null, 2));
82
90
  }
83
- }, [renderPreview, type, text, subject, html, locale, sampleJson]);
91
+ }, [renderPreview, type, text, subject, html, locale, sampleJson, t]);
84
92
  const runRef = (0, import_react.useRef)(run);
85
93
  runRef.current = run;
86
94
  const loadedType = template ? `${type}:${locale ?? ""}` : null;
@@ -108,20 +116,46 @@ function TemplateEditorScreen({
108
116
  setIsSaving(false);
109
117
  }
110
118
  };
111
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: "Loading template\u2026" });
119
+ const handleDelete = async () => {
120
+ const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
121
+ if (!window.confirm(message)) return;
122
+ setIsDeleting(true);
123
+ setSaveError(null);
124
+ try {
125
+ await removeTemplate(type, locale);
126
+ onDeleted?.();
127
+ } catch (err) {
128
+ setSaveError(err);
129
+ } finally {
130
+ setIsDeleting(false);
131
+ }
132
+ };
133
+ if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: t("templates.editor.loading") });
112
134
  if (error)
113
135
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
114
136
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
115
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
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)(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ style: { ...styles.rollbackButton, marginLeft: "auto" },
144
+ onClick: () => onAddLocale(type),
145
+ children: t("templates.editor.addLocale")
146
+ }
147
+ ) : null
148
+ ] }),
116
149
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
117
- locale ?? "base",
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? t("templates.defaultLocale") }),
118
151
  " \xB7 v",
119
- template?.version ?? 1
152
+ template?.version ?? 1,
153
+ system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
120
154
  ] }),
121
155
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.split, children: [
122
156
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.column, children: [
123
157
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { style: styles.form, onSubmit: handleSubmit, children: [
124
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-subject", children: "Subject" }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-subject", children: t("templates.subject") }),
125
159
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
126
160
  "input",
127
161
  {
@@ -131,7 +165,7 @@ function TemplateEditorScreen({
131
165
  onChange: (event) => setSubject(event.target.value)
132
166
  }
133
167
  ),
134
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-html", children: "HTML body" }),
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-html", children: t("templates.htmlBody") }),
135
169
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
136
170
  "textarea",
137
171
  {
@@ -142,7 +176,7 @@ function TemplateEditorScreen({
142
176
  rows: 8
143
177
  }
144
178
  ),
145
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-text", children: "Plain-text body" }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-text", children: t("templates.textBody") }),
146
180
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
147
181
  "textarea",
148
182
  {
@@ -163,9 +197,24 @@ function TemplateEditorScreen({
163
197
  onChange: (event) => setActive(event.target.checked)
164
198
  }
165
199
  ),
166
- "Active"
200
+ t("templates.editor.active")
167
201
  ] }),
168
- saveError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }),
202
+ saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.error, role: "alert", children: [
203
+ t("templates.editor.conflict"),
204
+ " ",
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
206
+ "button",
207
+ {
208
+ type: "button",
209
+ style: styles.rollbackButton,
210
+ onClick: () => {
211
+ setSaveError(null);
212
+ void refresh();
213
+ },
214
+ children: t("common.reload")
215
+ }
216
+ )
217
+ ] }) : saveError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
169
218
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.saveRow, children: [
170
219
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
171
220
  "button",
@@ -173,13 +222,29 @@ function TemplateEditorScreen({
173
222
  type: "submit",
174
223
  disabled: isSaving || !dirty,
175
224
  style: dirty ? styles.button : styles.buttonDisabled,
176
- children: isSaving ? "Saving\u2026" : "Save"
225
+ children: isSaving ? t("common.saving") : t("common.save")
177
226
  }
178
227
  ),
179
- !dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: "No changes to save" })
228
+ !dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: t("templates.editor.noChanges") }),
229
+ !system ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
230
+ "button",
231
+ {
232
+ type: "button",
233
+ disabled: isDeleting,
234
+ onClick: () => void handleDelete(),
235
+ style: {
236
+ ...styles.rollbackButton,
237
+ height: 36,
238
+ marginLeft: "auto",
239
+ color: "var(--fonderie-danger,#e00)",
240
+ borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
241
+ },
242
+ children: isDeleting ? t("common.deleting") : t("common.delete")
243
+ }
244
+ ) : null
180
245
  ] })
181
246
  ] }),
182
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-sample", children: "Sample data" }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-sample", children: t("templates.editor.sampleData") }),
183
248
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
184
249
  "textarea",
185
250
  {
@@ -195,8 +260,8 @@ function TemplateEditorScreen({
195
260
  ] }),
196
261
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.previewColumn, children: [
197
262
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.previewHeader, children: [
198
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.label, children: "Preview" }),
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: isPreviewing ? "Rendering\u2026" : "Live" })
263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.label, children: t("templates.editor.preview") }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: isPreviewing ? t("templates.editor.rendering") : t("templates.editor.live") })
200
265
  ] }),
201
266
  previewError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: previewError.explanation }),
202
267
  preview?.subject && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.previewSubject, children: preview.subject }),
@@ -207,26 +272,26 @@ function TemplateEditorScreen({
207
272
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
273
  "iframe",
209
274
  {
210
- title: "Template preview",
275
+ title: t("templates.editor.previewTitle"),
211
276
  style: styles.previewFrame,
212
277
  sandbox: "",
213
278
  srcDoc: preview.html
214
279
  }
215
280
  )
216
- ) : preview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.meta, children: "Nothing rendered yet." })
281
+ ) : preview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.meta, children: t("templates.editor.nothingRendered") })
217
282
  ] })
218
283
  ] }),
219
284
  revisions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.revisions, children: [
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.subtitle, children: "History" }),
285
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.subtitle, children: t("templates.editor.history") }),
221
286
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.list, children: revisions.map((rev) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: styles.row, children: [
222
287
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
223
288
  "v",
224
289
  rev.version,
225
290
  " \u2014 ",
226
- rev.actor ?? "unknown",
291
+ rev.actor ?? t("templates.editor.unknownActor"),
227
292
  " \u2014",
228
293
  " ",
229
- new Date(rev.createdAt).toLocaleString()
294
+ (0, import_client.formatAdminDate)(rev.createdAt, uiLocale)
230
295
  ] }),
231
296
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
232
297
  "button",
@@ -234,7 +299,7 @@ function TemplateEditorScreen({
234
299
  type: "button",
235
300
  onClick: () => rollback(rev.version),
236
301
  style: styles.rollbackButton,
237
- children: "Roll back"
302
+ children: t("templates.editor.rollBack")
238
303
  }
239
304
  )
240
305
  ] }, rev.version)) })
@@ -410,14 +475,42 @@ var styles = {
410
475
  };
411
476
 
412
477
  // src/screens/TemplateListScreen.tsx
478
+ var import_client2 = require("@fonderie/client");
413
479
  var import_react_courier_admin2 = require("@fonderie/react-courier-admin");
414
480
  var import_jsx_runtime2 = require("react/jsx-runtime");
415
- function TemplateListScreen({ client, onSelectTemplate }) {
481
+ function TemplateListScreen({
482
+ client,
483
+ onSelectTemplate,
484
+ onCreateTemplate,
485
+ locale
486
+ }) {
487
+ const t = (0, import_client2.createAdminT)(locale);
416
488
  const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
417
489
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
418
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: "Templates" }),
419
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: "Every email the app sends. Open one to edit its copy and preview it live." }),
420
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: "Loading templates\u2026" }) : 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)(
490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
491
+ "div",
492
+ {
493
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
494
+ children: [
495
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: t("templates.list.title") }),
496
+ onCreateTemplate ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
497
+ "button",
498
+ {
499
+ type: "button",
500
+ onClick: () => onCreateTemplate({
501
+ locales: [
502
+ ...new Set(templates.map((row) => row.locale).filter((l) => !!l))
503
+ ]
504
+ }),
505
+ style: styles2.newButton,
506
+ children: t("templates.list.newTemplate")
507
+ }
508
+ ) : null
509
+ ]
510
+ }
511
+ ),
512
+ /* @__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)(
421
514
  "button",
422
515
  {
423
516
  type: "button",
@@ -425,10 +518,11 @@ function TemplateListScreen({ client, onSelectTemplate }) {
425
518
  style: styles2.rowButton,
426
519
  children: [
427
520
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.type, children: template.type }),
428
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.locale, children: template.locale ?? "default locale" }),
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,
429
523
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: template.active ? styles2.active : styles2.inactive, children: [
430
524
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.dot }),
431
- template.active ? "active" : "inactive"
525
+ template.active ? t("common.status.active") : t("common.status.inactive")
432
526
  ] })
433
527
  ]
434
528
  }
@@ -491,6 +585,29 @@ var styles2 = {
491
585
  },
492
586
  locale: { fontSize: 12.5, color: "var(--fonderie-text-muted,#5c5c5c)" },
493
587
  dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
588
+ builtIn: {
589
+ fontSize: 11,
590
+ fontWeight: 500,
591
+ padding: "1px 7px",
592
+ borderRadius: 999,
593
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
594
+ color: "var(--fonderie-text-muted,#5c5c5c)"
595
+ },
596
+ newButton: {
597
+ display: "inline-flex",
598
+ alignItems: "center",
599
+ height: 32,
600
+ boxSizing: "border-box",
601
+ borderRadius: 6,
602
+ padding: "0 12px",
603
+ fontSize: 13,
604
+ fontWeight: 500,
605
+ fontFamily: "inherit",
606
+ cursor: "pointer",
607
+ backgroundColor: "var(--fonderie-text,#171717)",
608
+ color: "var(--fonderie-surface,#fff)",
609
+ border: "1px solid var(--fonderie-text,#171717)"
610
+ },
494
611
  active: {
495
612
  ...pill,
496
613
  color: "var(--fonderie-accent-strong,#009767)",
@@ -502,8 +619,214 @@ var styles2 = {
502
619
  background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
503
620
  }
504
621
  };
622
+
623
+ // src/screens/TemplateCreateScreen.tsx
624
+ var import_client3 = require("@fonderie/client");
625
+ var import_react2 = require("react");
626
+ var import_jsx_runtime3 = require("react/jsx-runtime");
627
+ var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
628
+ var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
629
+ function TemplateCreateScreen({
630
+ client,
631
+ type,
632
+ locales = [],
633
+ onCreated,
634
+ locale
635
+ }) {
636
+ const tr = (0, import_client3.createAdminT)(locale);
637
+ const addingLocale = type !== void 0;
638
+ const [newType, setNewType] = (0, import_react2.useState)("");
639
+ const [targetLocale, setTargetLocale] = (0, import_react2.useState)("");
640
+ const [subject, setSubject] = (0, import_react2.useState)("");
641
+ const [html, setHtml] = (0, import_react2.useState)("");
642
+ const [text, setText] = (0, import_react2.useState)("");
643
+ const [error, setError] = (0, import_react2.useState)(null);
644
+ const [busy, setBusy] = (0, import_react2.useState)(false);
645
+ (0, import_react2.useEffect)(() => {
646
+ if (!type) return;
647
+ client.getTemplate(type, null).then(({ result }) => {
648
+ setSubject(result.subject ?? "");
649
+ setHtml(result.html ?? "");
650
+ setText(result.text);
651
+ }).catch(() => void 0);
652
+ }, [client, type]);
653
+ const submit = async (e) => {
654
+ e.preventDefault();
655
+ setError(null);
656
+ const t = addingLocale ? type : newType.trim();
657
+ const l = targetLocale.trim() || null;
658
+ if (!TYPE_RE.test(t)) return setError(tr("templates.create.errorType"));
659
+ if (addingLocale && !l) return setError(tr("templates.create.errorLocaleRequired"));
660
+ if (l && !LOCALE_RE.test(l)) return setError(tr("templates.create.errorLocale"));
661
+ if (!text.trim()) return setError(tr("templates.create.errorText"));
662
+ setBusy(true);
663
+ try {
664
+ const exists = await client.getTemplate(t, l).then(
665
+ () => true,
666
+ (err) => !(err instanceof import_client3.FonderieApiError && err.status === 404)
667
+ );
668
+ if (exists) {
669
+ setError(
670
+ l ? tr("templates.create.existsLocale", { type: t, locale: l }) : tr("templates.create.exists", { type: t })
671
+ );
672
+ return;
673
+ }
674
+ const input = { text, active: true };
675
+ if (subject) input.subject = subject;
676
+ if (html) input.html = html;
677
+ await client.setTemplate(t, input, l);
678
+ onCreated?.({ type: t, locale: l });
679
+ } catch (err) {
680
+ setError(err instanceof import_client3.FonderieApiError ? err.explanation : String(err));
681
+ } finally {
682
+ setBusy(false);
683
+ }
684
+ };
685
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
687
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
689
+ !addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
692
+ "input",
693
+ {
694
+ id: "new-template-type",
695
+ style: styles3.input,
696
+ value: newType,
697
+ placeholder: "weekly-digest",
698
+ autoComplete: "off",
699
+ spellCheck: false,
700
+ onChange: (e) => setNewType(e.target.value)
701
+ }
702
+ )
703
+ ] }) : null,
704
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
706
+ "input",
707
+ {
708
+ id: "new-template-locale",
709
+ style: styles3.input,
710
+ value: targetLocale,
711
+ placeholder: "fr",
712
+ list: "new-template-locales",
713
+ autoComplete: "off",
714
+ spellCheck: false,
715
+ onChange: (e) => setTargetLocale(e.target.value)
716
+ }
717
+ ),
718
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: l }, l)) }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
721
+ "input",
722
+ {
723
+ id: "new-template-subject",
724
+ style: styles3.input,
725
+ value: subject,
726
+ onChange: (e) => setSubject(e.target.value)
727
+ }
728
+ ),
729
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
731
+ "textarea",
732
+ {
733
+ id: "new-template-html",
734
+ style: { ...styles3.textarea, minHeight: 160 },
735
+ value: html,
736
+ onChange: (e) => setHtml(e.target.value)
737
+ }
738
+ ),
739
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
741
+ "textarea",
742
+ {
743
+ id: "new-template-text",
744
+ style: { ...styles3.textarea, minHeight: 100 },
745
+ value: text,
746
+ onChange: (e) => setText(e.target.value)
747
+ }
748
+ ),
749
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
750
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
751
+ ] })
752
+ ] });
753
+ }
754
+ var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
755
+ var styles3 = {
756
+ container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
757
+ title: {
758
+ fontSize: 22,
759
+ fontWeight: 600,
760
+ margin: "8px 0 4px",
761
+ letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
762
+ lineHeight: 1.25
763
+ },
764
+ meta: {
765
+ fontSize: 13,
766
+ color: "var(--fonderie-text-muted,#5c5c5c)",
767
+ marginBottom: 20,
768
+ lineHeight: 1.5
769
+ },
770
+ form: {
771
+ display: "flex",
772
+ flexDirection: "column",
773
+ gap: 4,
774
+ background: "var(--fonderie-surface,#fff)",
775
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
776
+ borderRadius: "var(--fonderie-radius-lg,8px)",
777
+ padding: "6px 20px 20px"
778
+ },
779
+ label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
780
+ input: {
781
+ height: 36,
782
+ boxSizing: "border-box",
783
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
784
+ borderRadius: 6,
785
+ padding: "0 10px",
786
+ fontSize: 13.5,
787
+ fontFamily: "inherit",
788
+ background: "var(--fonderie-surface,#fff)",
789
+ color: "var(--fonderie-text,#171717)"
790
+ },
791
+ textarea: {
792
+ border: "1px solid var(--fonderie-border,#e0e0e0)",
793
+ borderRadius: 6,
794
+ padding: 10,
795
+ fontSize: 13,
796
+ lineHeight: 1.55,
797
+ background: "var(--fonderie-surface,#fff)",
798
+ color: "var(--fonderie-text,#171717)",
799
+ fontFamily: MONO
800
+ },
801
+ error: {
802
+ color: "var(--fonderie-danger,#e00)",
803
+ background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
804
+ borderRadius: 6,
805
+ padding: "8px 12px",
806
+ margin: "8px 0",
807
+ fontSize: 13.5
808
+ },
809
+ button: {
810
+ display: "inline-flex",
811
+ alignItems: "center",
812
+ height: 36,
813
+ boxSizing: "border-box",
814
+ backgroundColor: "var(--fonderie-text,#171717)",
815
+ color: "var(--fonderie-surface,#fff)",
816
+ padding: "0 16px",
817
+ borderRadius: 6,
818
+ border: "1px solid var(--fonderie-text,#171717)",
819
+ fontSize: 13.5,
820
+ fontWeight: 600,
821
+ fontFamily: "inherit",
822
+ cursor: "pointer",
823
+ alignSelf: "flex-start",
824
+ marginTop: 16
825
+ }
826
+ };
505
827
  // Annotate the CommonJS export names for ESM import in node:
506
828
  0 && (module.exports = {
829
+ TemplateCreateScreen,
507
830
  TemplateEditorScreen,
508
831
  TemplateListScreen
509
832
  });