@fonderie/react-courier-admin-screens 0.7.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 +186 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -7
- package/dist/index.d.ts +30 -7
- package/dist/index.js +196 -85
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
// src/screens/TemplateEditorScreen.tsx
|
|
2
|
+
import {
|
|
3
|
+
createAdminT,
|
|
4
|
+
formatAdminDate,
|
|
5
|
+
groupTemplatesByType,
|
|
6
|
+
missingTemplateLocales
|
|
7
|
+
} from "@fonderie/client";
|
|
2
8
|
import {
|
|
3
9
|
useTemplate,
|
|
4
10
|
useTemplatePreview,
|
|
5
11
|
useTemplateRevisions,
|
|
6
12
|
useTemplates
|
|
7
13
|
} from "@fonderie/react-courier-admin";
|
|
8
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
14
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
16
|
var IMPLICIT = /* @__PURE__ */ new Set(["subject", "preheader", "brandName"]);
|
|
11
17
|
var PREVIEW_DEBOUNCE_MS = 500;
|
|
@@ -16,10 +22,14 @@ function TemplateEditorScreen({
|
|
|
16
22
|
onSaved,
|
|
17
23
|
system = false,
|
|
18
24
|
onDeleted,
|
|
19
|
-
onAddLocale
|
|
25
|
+
onAddLocale,
|
|
26
|
+
onSelectLocale,
|
|
27
|
+
uiLocale
|
|
20
28
|
}) {
|
|
29
|
+
const t = useMemo(() => createAdminT(uiLocale), [uiLocale]);
|
|
21
30
|
const { template, isLoading, error, refresh } = useTemplate(client, type, locale);
|
|
22
|
-
const { saveTemplate, removeTemplate } = useTemplates(client);
|
|
31
|
+
const { templates, saveTemplate, removeTemplate } = useTemplates(client);
|
|
32
|
+
const siblings = groupTemplatesByType(templates).find((g) => g.type === type)?.entries ?? [];
|
|
23
33
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
24
34
|
const { revisions, rollback } = useTemplateRevisions(client, type, locale);
|
|
25
35
|
const [isSaving, setIsSaving] = useState(false);
|
|
@@ -43,13 +53,13 @@ function TemplateEditorScreen({
|
|
|
43
53
|
try {
|
|
44
54
|
const parsed = JSON.parse(sampleJson || "{}");
|
|
45
55
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
46
|
-
setSampleError("
|
|
56
|
+
setSampleError(t("templates.editor.sampleNotObject"));
|
|
47
57
|
return;
|
|
48
58
|
}
|
|
49
59
|
data = parsed;
|
|
50
60
|
setSampleError(null);
|
|
51
61
|
} catch {
|
|
52
|
-
setSampleError("
|
|
62
|
+
setSampleError(t("templates.editor.sampleInvalid"));
|
|
53
63
|
return;
|
|
54
64
|
}
|
|
55
65
|
const result = await renderPreview(
|
|
@@ -62,7 +72,7 @@ function TemplateEditorScreen({
|
|
|
62
72
|
const seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };
|
|
63
73
|
setSampleJson(JSON.stringify(seeded, null, 2));
|
|
64
74
|
}
|
|
65
|
-
}, [renderPreview, type, text, subject, html, locale, sampleJson]);
|
|
75
|
+
}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);
|
|
66
76
|
const runRef = useRef(run);
|
|
67
77
|
runRef.current = run;
|
|
68
78
|
const loadedType = template ? `${type}:${locale ?? ""}` : null;
|
|
@@ -90,10 +100,13 @@ function TemplateEditorScreen({
|
|
|
90
100
|
setIsSaving(false);
|
|
91
101
|
}
|
|
92
102
|
};
|
|
103
|
+
const switchTo = (entry) => {
|
|
104
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
105
|
+
onSelectLocale?.(entry);
|
|
106
|
+
};
|
|
93
107
|
const handleDelete = async () => {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
if (!window.confirm(`Delete ${which}?${fallback}`)) return;
|
|
108
|
+
const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
|
|
109
|
+
if (!window.confirm(message)) return;
|
|
97
110
|
setIsDeleting(true);
|
|
98
111
|
setSaveError(null);
|
|
99
112
|
try {
|
|
@@ -105,32 +118,54 @@ function TemplateEditorScreen({
|
|
|
105
118
|
setIsDeleting(false);
|
|
106
119
|
}
|
|
107
120
|
};
|
|
108
|
-
if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: "
|
|
121
|
+
if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: t("templates.editor.loading") });
|
|
109
122
|
if (error)
|
|
110
123
|
return /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
111
124
|
return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
|
|
112
|
-
/* @__PURE__ */
|
|
113
|
-
|
|
114
|
-
|
|
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(
|
|
115
147
|
"button",
|
|
116
148
|
{
|
|
117
149
|
type: "button",
|
|
118
150
|
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
119
|
-
onClick: () =>
|
|
120
|
-
|
|
151
|
+
onClick: () => {
|
|
152
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
153
|
+
onAddLocale(type, { locales: missingTemplateLocales(templates, type) });
|
|
154
|
+
},
|
|
155
|
+
children: t("templates.editor.addLocale")
|
|
121
156
|
}
|
|
122
157
|
) : null
|
|
123
|
-
] }),
|
|
158
|
+
] }) : null,
|
|
124
159
|
/* @__PURE__ */ jsxs("p", { style: styles.meta, children: [
|
|
125
|
-
/* @__PURE__ */ jsx("strong", { children: locale ?? "
|
|
160
|
+
/* @__PURE__ */ jsx("strong", { children: locale ?? t("templates.defaultLocale") }),
|
|
126
161
|
" \xB7 v",
|
|
127
162
|
template?.version ?? 1,
|
|
128
|
-
system ?
|
|
163
|
+
system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
|
|
129
164
|
] }),
|
|
130
165
|
/* @__PURE__ */ jsxs("div", { style: styles.split, children: [
|
|
131
166
|
/* @__PURE__ */ jsxs("div", { style: styles.column, children: [
|
|
132
167
|
/* @__PURE__ */ jsxs("form", { style: styles.form, onSubmit: handleSubmit, children: [
|
|
133
|
-
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-subject", children: "
|
|
168
|
+
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-subject", children: t("templates.subject") }),
|
|
134
169
|
/* @__PURE__ */ jsx(
|
|
135
170
|
"input",
|
|
136
171
|
{
|
|
@@ -140,7 +175,7 @@ function TemplateEditorScreen({
|
|
|
140
175
|
onChange: (event) => setSubject(event.target.value)
|
|
141
176
|
}
|
|
142
177
|
),
|
|
143
|
-
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-html", children: "
|
|
178
|
+
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-html", children: t("templates.htmlBody") }),
|
|
144
179
|
/* @__PURE__ */ jsx(
|
|
145
180
|
"textarea",
|
|
146
181
|
{
|
|
@@ -151,7 +186,7 @@ function TemplateEditorScreen({
|
|
|
151
186
|
rows: 8
|
|
152
187
|
}
|
|
153
188
|
),
|
|
154
|
-
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-text", children: "
|
|
189
|
+
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-text", children: t("templates.textBody") }),
|
|
155
190
|
/* @__PURE__ */ jsx(
|
|
156
191
|
"textarea",
|
|
157
192
|
{
|
|
@@ -172,10 +207,10 @@ function TemplateEditorScreen({
|
|
|
172
207
|
onChange: (event) => setActive(event.target.checked)
|
|
173
208
|
}
|
|
174
209
|
),
|
|
175
|
-
"
|
|
210
|
+
t("templates.editor.active")
|
|
176
211
|
] }),
|
|
177
212
|
saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ jsxs("p", { style: styles.error, role: "alert", children: [
|
|
178
|
-
"
|
|
213
|
+
t("templates.editor.conflict"),
|
|
179
214
|
" ",
|
|
180
215
|
/* @__PURE__ */ jsx(
|
|
181
216
|
"button",
|
|
@@ -186,7 +221,7 @@ function TemplateEditorScreen({
|
|
|
186
221
|
setSaveError(null);
|
|
187
222
|
void refresh();
|
|
188
223
|
},
|
|
189
|
-
children: "
|
|
224
|
+
children: t("common.reload")
|
|
190
225
|
}
|
|
191
226
|
)
|
|
192
227
|
] }) : saveError ? /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
|
|
@@ -197,10 +232,10 @@ function TemplateEditorScreen({
|
|
|
197
232
|
type: "submit",
|
|
198
233
|
disabled: isSaving || !dirty,
|
|
199
234
|
style: dirty ? styles.button : styles.buttonDisabled,
|
|
200
|
-
children: isSaving ? "
|
|
235
|
+
children: isSaving ? t("common.saving") : t("common.save")
|
|
201
236
|
}
|
|
202
237
|
),
|
|
203
|
-
!dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: "
|
|
238
|
+
!dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: t("templates.editor.noChanges") }),
|
|
204
239
|
!system ? /* @__PURE__ */ jsx(
|
|
205
240
|
"button",
|
|
206
241
|
{
|
|
@@ -214,12 +249,12 @@ function TemplateEditorScreen({
|
|
|
214
249
|
color: "var(--fonderie-danger,#e00)",
|
|
215
250
|
borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
|
|
216
251
|
},
|
|
217
|
-
children: isDeleting ? "
|
|
252
|
+
children: isDeleting ? t("common.deleting") : t("common.delete")
|
|
218
253
|
}
|
|
219
254
|
) : null
|
|
220
255
|
] })
|
|
221
256
|
] }),
|
|
222
|
-
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: "
|
|
257
|
+
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: t("templates.editor.sampleData") }),
|
|
223
258
|
/* @__PURE__ */ jsx(
|
|
224
259
|
"textarea",
|
|
225
260
|
{
|
|
@@ -235,8 +270,8 @@ function TemplateEditorScreen({
|
|
|
235
270
|
] }),
|
|
236
271
|
/* @__PURE__ */ jsxs("div", { style: styles.previewColumn, children: [
|
|
237
272
|
/* @__PURE__ */ jsxs("div", { style: styles.previewHeader, children: [
|
|
238
|
-
/* @__PURE__ */ jsx("span", { style: styles.label, children: "
|
|
239
|
-
/* @__PURE__ */ jsx("span", { style: styles.meta, children: isPreviewing ? "
|
|
273
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: t("templates.editor.preview") }),
|
|
274
|
+
/* @__PURE__ */ jsx("span", { style: styles.meta, children: isPreviewing ? t("templates.editor.rendering") : t("templates.editor.live") })
|
|
240
275
|
] }),
|
|
241
276
|
previewError && /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: previewError.explanation }),
|
|
242
277
|
preview?.subject && /* @__PURE__ */ jsx("p", { style: styles.previewSubject, children: preview.subject }),
|
|
@@ -247,26 +282,26 @@ function TemplateEditorScreen({
|
|
|
247
282
|
/* @__PURE__ */ jsx(
|
|
248
283
|
"iframe",
|
|
249
284
|
{
|
|
250
|
-
title: "
|
|
285
|
+
title: t("templates.editor.previewTitle"),
|
|
251
286
|
style: styles.previewFrame,
|
|
252
287
|
sandbox: "",
|
|
253
288
|
srcDoc: preview.html
|
|
254
289
|
}
|
|
255
290
|
)
|
|
256
|
-
) : preview ? /* @__PURE__ */ jsx("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ jsx("p", { style: styles.meta, children: "
|
|
291
|
+
) : preview ? /* @__PURE__ */ jsx("pre", { style: styles.previewText, children: preview.text }) : /* @__PURE__ */ jsx("p", { style: styles.meta, children: t("templates.editor.nothingRendered") })
|
|
257
292
|
] })
|
|
258
293
|
] }),
|
|
259
294
|
revisions.length > 0 && /* @__PURE__ */ jsxs("div", { style: styles.revisions, children: [
|
|
260
|
-
/* @__PURE__ */ jsx("h2", { style: styles.subtitle, children: "
|
|
295
|
+
/* @__PURE__ */ jsx("h2", { style: styles.subtitle, children: t("templates.editor.history") }),
|
|
261
296
|
/* @__PURE__ */ jsx("ul", { style: styles.list, children: revisions.map((rev) => /* @__PURE__ */ jsxs("li", { style: styles.row, children: [
|
|
262
297
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
263
298
|
"v",
|
|
264
299
|
rev.version,
|
|
265
300
|
" \u2014 ",
|
|
266
|
-
rev.actor ?? "
|
|
301
|
+
rev.actor ?? t("templates.editor.unknownActor"),
|
|
267
302
|
" \u2014",
|
|
268
303
|
" ",
|
|
269
|
-
|
|
304
|
+
formatAdminDate(rev.createdAt, uiLocale)
|
|
270
305
|
] }),
|
|
271
306
|
/* @__PURE__ */ jsx(
|
|
272
307
|
"button",
|
|
@@ -274,15 +309,45 @@ function TemplateEditorScreen({
|
|
|
274
309
|
type: "button",
|
|
275
310
|
onClick: () => rollback(rev.version),
|
|
276
311
|
style: styles.rollbackButton,
|
|
277
|
-
children: "
|
|
312
|
+
children: t("templates.editor.rollBack")
|
|
278
313
|
}
|
|
279
314
|
)
|
|
280
315
|
] }, rev.version)) })
|
|
281
316
|
] })
|
|
282
317
|
] });
|
|
283
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
|
+
};
|
|
284
330
|
var styles = {
|
|
285
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" },
|
|
286
351
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
287
352
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
288
353
|
split: {
|
|
@@ -450,55 +515,78 @@ var styles = {
|
|
|
450
515
|
};
|
|
451
516
|
|
|
452
517
|
// src/screens/TemplateListScreen.tsx
|
|
518
|
+
import {
|
|
519
|
+
createAdminT as createAdminT2,
|
|
520
|
+
groupTemplatesByType as groupTemplatesByType2
|
|
521
|
+
} from "@fonderie/client";
|
|
453
522
|
import { useTemplates as useTemplates2 } from "@fonderie/react-courier-admin";
|
|
454
523
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
455
524
|
function TemplateListScreen({
|
|
456
525
|
client,
|
|
457
526
|
onSelectTemplate,
|
|
458
|
-
onCreateTemplate
|
|
527
|
+
onCreateTemplate,
|
|
528
|
+
locale
|
|
459
529
|
}) {
|
|
530
|
+
const t = createAdminT2(locale);
|
|
460
531
|
const { templates, isLoading, error } = useTemplates2(client);
|
|
532
|
+
const groups = groupTemplatesByType2(templates);
|
|
461
533
|
return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
|
|
462
534
|
/* @__PURE__ */ jsxs2(
|
|
463
535
|
"div",
|
|
464
536
|
{
|
|
465
537
|
style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
|
|
466
538
|
children: [
|
|
467
|
-
/* @__PURE__ */ jsx2("h1", { style: styles2.title, children: "
|
|
539
|
+
/* @__PURE__ */ jsx2("h1", { style: styles2.title, children: t("templates.list.title") }),
|
|
468
540
|
onCreateTemplate ? /* @__PURE__ */ jsx2(
|
|
469
541
|
"button",
|
|
470
542
|
{
|
|
471
543
|
type: "button",
|
|
472
544
|
onClick: () => onCreateTemplate({
|
|
473
545
|
locales: [
|
|
474
|
-
...new Set(templates.map((
|
|
546
|
+
...new Set(templates.map((row) => row.locale).filter((l) => !!l))
|
|
475
547
|
]
|
|
476
548
|
}),
|
|
477
549
|
style: styles2.newButton,
|
|
478
|
-
children: "
|
|
550
|
+
children: t("templates.list.newTemplate")
|
|
479
551
|
}
|
|
480
552
|
) : null
|
|
481
553
|
]
|
|
482
554
|
}
|
|
483
555
|
),
|
|
484
|
-
/* @__PURE__ */ jsx2("p", { style: styles2.hint, children: "
|
|
485
|
-
isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: "
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
556
|
+
/* @__PURE__ */ jsx2("p", { style: styles2.hint, children: t("templates.list.hint") }),
|
|
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)) })
|
|
502
590
|
] });
|
|
503
591
|
}
|
|
504
592
|
var pill = {
|
|
@@ -512,6 +600,18 @@ var pill = {
|
|
|
512
600
|
lineHeight: 1.5,
|
|
513
601
|
minWidth: 64
|
|
514
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
|
+
};
|
|
515
615
|
var styles2 = {
|
|
516
616
|
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
517
617
|
title: {
|
|
@@ -534,9 +634,16 @@ var styles2 = {
|
|
|
534
634
|
overflow: "hidden",
|
|
535
635
|
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
536
636
|
},
|
|
537
|
-
row: {
|
|
637
|
+
row: {
|
|
638
|
+
display: "flex",
|
|
639
|
+
alignItems: "center",
|
|
640
|
+
gap: 8,
|
|
641
|
+
paddingRight: 16,
|
|
642
|
+
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)"
|
|
643
|
+
},
|
|
538
644
|
rowButton: {
|
|
539
|
-
|
|
645
|
+
flex: 1,
|
|
646
|
+
minWidth: 0,
|
|
540
647
|
display: "flex",
|
|
541
648
|
justifyContent: "space-between",
|
|
542
649
|
alignItems: "center",
|
|
@@ -555,7 +662,9 @@ var styles2 = {
|
|
|
555
662
|
flex: 1,
|
|
556
663
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
557
664
|
},
|
|
558
|
-
|
|
665
|
+
chips: { display: "flex", flexWrap: "wrap", justifyContent: "flex-end", gap: 6 },
|
|
666
|
+
chip,
|
|
667
|
+
chipInactive: { ...chip, opacity: 0.5, textDecoration: "line-through" },
|
|
559
668
|
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
560
669
|
builtIn: {
|
|
561
670
|
fontSize: 11,
|
|
@@ -593,7 +702,10 @@ var styles2 = {
|
|
|
593
702
|
};
|
|
594
703
|
|
|
595
704
|
// src/screens/TemplateCreateScreen.tsx
|
|
596
|
-
import {
|
|
705
|
+
import {
|
|
706
|
+
FonderieApiError,
|
|
707
|
+
createAdminT as createAdminT3
|
|
708
|
+
} from "@fonderie/client";
|
|
597
709
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
598
710
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
599
711
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
@@ -602,11 +714,13 @@ function TemplateCreateScreen({
|
|
|
602
714
|
client,
|
|
603
715
|
type,
|
|
604
716
|
locales = [],
|
|
605
|
-
onCreated
|
|
717
|
+
onCreated,
|
|
718
|
+
locale
|
|
606
719
|
}) {
|
|
720
|
+
const tr = createAdminT3(locale);
|
|
607
721
|
const addingLocale = type !== void 0;
|
|
608
722
|
const [newType, setNewType] = useState2("");
|
|
609
|
-
const [
|
|
723
|
+
const [targetLocale, setTargetLocale] = useState2("");
|
|
610
724
|
const [subject, setSubject] = useState2("");
|
|
611
725
|
const [html, setHtml] = useState2("");
|
|
612
726
|
const [text, setText] = useState2("");
|
|
@@ -624,13 +738,11 @@ function TemplateCreateScreen({
|
|
|
624
738
|
e.preventDefault();
|
|
625
739
|
setError(null);
|
|
626
740
|
const t = addingLocale ? type : newType.trim();
|
|
627
|
-
const l =
|
|
628
|
-
if (!TYPE_RE.test(t))
|
|
629
|
-
|
|
630
|
-
if (
|
|
631
|
-
if (
|
|
632
|
-
if (!text.trim())
|
|
633
|
-
return setError("The plain-text body is required \u2014 it is what every email client can show.");
|
|
741
|
+
const l = targetLocale.trim() || null;
|
|
742
|
+
if (!TYPE_RE.test(t)) return setError(tr("templates.create.errorType"));
|
|
743
|
+
if (addingLocale && !l) return setError(tr("templates.create.errorLocaleRequired"));
|
|
744
|
+
if (l && !LOCALE_RE.test(l)) return setError(tr("templates.create.errorLocale"));
|
|
745
|
+
if (!text.trim()) return setError(tr("templates.create.errorText"));
|
|
634
746
|
setBusy(true);
|
|
635
747
|
try {
|
|
636
748
|
const exists = await client.getTemplate(t, l).then(
|
|
@@ -638,7 +750,9 @@ function TemplateCreateScreen({
|
|
|
638
750
|
(err) => !(err instanceof FonderieApiError && err.status === 404)
|
|
639
751
|
);
|
|
640
752
|
if (exists) {
|
|
641
|
-
setError(
|
|
753
|
+
setError(
|
|
754
|
+
l ? tr("templates.create.existsLocale", { type: t, locale: l }) : tr("templates.create.exists", { type: t })
|
|
755
|
+
);
|
|
642
756
|
return;
|
|
643
757
|
}
|
|
644
758
|
const input = { text, active: true };
|
|
@@ -653,11 +767,11 @@ function TemplateCreateScreen({
|
|
|
653
767
|
}
|
|
654
768
|
};
|
|
655
769
|
return /* @__PURE__ */ jsxs3("div", { style: styles3.container, children: [
|
|
656
|
-
/* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ?
|
|
657
|
-
/* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? "
|
|
770
|
+
/* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
|
|
771
|
+
/* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
|
|
658
772
|
/* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
659
773
|
!addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
660
|
-
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: "
|
|
774
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
|
|
661
775
|
/* @__PURE__ */ jsx3(
|
|
662
776
|
"input",
|
|
663
777
|
{
|
|
@@ -671,25 +785,22 @@ function TemplateCreateScreen({
|
|
|
671
785
|
}
|
|
672
786
|
)
|
|
673
787
|
] }) : null,
|
|
674
|
-
/* @__PURE__ */
|
|
675
|
-
"Locale ",
|
|
676
|
-
addingLocale ? "" : "(optional \u2014 empty is the default)"
|
|
677
|
-
] }),
|
|
788
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
|
|
678
789
|
/* @__PURE__ */ jsx3(
|
|
679
790
|
"input",
|
|
680
791
|
{
|
|
681
792
|
id: "new-template-locale",
|
|
682
793
|
style: styles3.input,
|
|
683
|
-
value:
|
|
794
|
+
value: targetLocale,
|
|
684
795
|
placeholder: "fr",
|
|
685
796
|
list: "new-template-locales",
|
|
686
797
|
autoComplete: "off",
|
|
687
798
|
spellCheck: false,
|
|
688
|
-
onChange: (e) =>
|
|
799
|
+
onChange: (e) => setTargetLocale(e.target.value)
|
|
689
800
|
}
|
|
690
801
|
),
|
|
691
802
|
/* @__PURE__ */ jsx3("datalist", { id: "new-template-locales", children: [...new Set(locales)].map((l) => /* @__PURE__ */ jsx3("option", { value: l }, l)) }),
|
|
692
|
-
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: "
|
|
803
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
|
|
693
804
|
/* @__PURE__ */ jsx3(
|
|
694
805
|
"input",
|
|
695
806
|
{
|
|
@@ -699,7 +810,7 @@ function TemplateCreateScreen({
|
|
|
699
810
|
onChange: (e) => setSubject(e.target.value)
|
|
700
811
|
}
|
|
701
812
|
),
|
|
702
|
-
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: "
|
|
813
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
|
|
703
814
|
/* @__PURE__ */ jsx3(
|
|
704
815
|
"textarea",
|
|
705
816
|
{
|
|
@@ -709,7 +820,7 @@ function TemplateCreateScreen({
|
|
|
709
820
|
onChange: (e) => setHtml(e.target.value)
|
|
710
821
|
}
|
|
711
822
|
),
|
|
712
|
-
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: "
|
|
823
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
|
|
713
824
|
/* @__PURE__ */ jsx3(
|
|
714
825
|
"textarea",
|
|
715
826
|
{
|
|
@@ -720,7 +831,7 @@ function TemplateCreateScreen({
|
|
|
720
831
|
}
|
|
721
832
|
),
|
|
722
833
|
error ? /* @__PURE__ */ jsx3("p", { style: styles3.error, role: "alert", children: error }) : null,
|
|
723
|
-
/* @__PURE__ */ jsx3("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? "
|
|
834
|
+
/* @__PURE__ */ jsx3("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? tr("templates.create.creating") : addingLocale ? tr("templates.create.submitLocale") : tr("templates.create.submitNew") })
|
|
724
835
|
] })
|
|
725
836
|
] });
|
|
726
837
|
}
|