@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.cjs
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
29
|
// src/screens/TemplateEditorScreen.tsx
|
|
30
|
+
var import_client = require("@fonderie/client");
|
|
30
31
|
var import_react_courier_admin = require("@fonderie/react-courier-admin");
|
|
31
32
|
var import_react = require("react");
|
|
32
33
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -39,10 +40,14 @@ function TemplateEditorScreen({
|
|
|
39
40
|
onSaved,
|
|
40
41
|
system = false,
|
|
41
42
|
onDeleted,
|
|
42
|
-
onAddLocale
|
|
43
|
+
onAddLocale,
|
|
44
|
+
onSelectLocale,
|
|
45
|
+
uiLocale
|
|
43
46
|
}) {
|
|
47
|
+
const t = (0, import_react.useMemo)(() => (0, import_client.createAdminT)(uiLocale), [uiLocale]);
|
|
44
48
|
const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
|
|
45
|
-
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 ?? [];
|
|
46
51
|
const [isDeleting, setIsDeleting] = (0, import_react.useState)(false);
|
|
47
52
|
const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
|
|
48
53
|
const [isSaving, setIsSaving] = (0, import_react.useState)(false);
|
|
@@ -66,13 +71,13 @@ function TemplateEditorScreen({
|
|
|
66
71
|
try {
|
|
67
72
|
const parsed = JSON.parse(sampleJson || "{}");
|
|
68
73
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
69
|
-
setSampleError("
|
|
74
|
+
setSampleError(t("templates.editor.sampleNotObject"));
|
|
70
75
|
return;
|
|
71
76
|
}
|
|
72
77
|
data = parsed;
|
|
73
78
|
setSampleError(null);
|
|
74
79
|
} catch {
|
|
75
|
-
setSampleError("
|
|
80
|
+
setSampleError(t("templates.editor.sampleInvalid"));
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
78
83
|
const result = await renderPreview(
|
|
@@ -85,7 +90,7 @@ function TemplateEditorScreen({
|
|
|
85
90
|
const seeded = { ...data, ...Object.fromEntries(missing.map((v) => [v, v])) };
|
|
86
91
|
setSampleJson(JSON.stringify(seeded, null, 2));
|
|
87
92
|
}
|
|
88
|
-
}, [renderPreview, type, text, subject, html, locale, sampleJson]);
|
|
93
|
+
}, [renderPreview, type, text, subject, html, locale, sampleJson, t]);
|
|
89
94
|
const runRef = (0, import_react.useRef)(run);
|
|
90
95
|
runRef.current = run;
|
|
91
96
|
const loadedType = template ? `${type}:${locale ?? ""}` : null;
|
|
@@ -113,10 +118,13 @@ function TemplateEditorScreen({
|
|
|
113
118
|
setIsSaving(false);
|
|
114
119
|
}
|
|
115
120
|
};
|
|
121
|
+
const switchTo = (entry) => {
|
|
122
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
123
|
+
onSelectLocale?.(entry);
|
|
124
|
+
};
|
|
116
125
|
const handleDelete = async () => {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
if (!window.confirm(`Delete ${which}?${fallback}`)) return;
|
|
126
|
+
const message = locale ? t("templates.editor.confirmDeleteLocale", { type, locale }) : t("templates.editor.confirmDelete", { type });
|
|
127
|
+
if (!window.confirm(message)) return;
|
|
120
128
|
setIsDeleting(true);
|
|
121
129
|
setSaveError(null);
|
|
122
130
|
try {
|
|
@@ -128,32 +136,54 @@ function TemplateEditorScreen({
|
|
|
128
136
|
setIsDeleting(false);
|
|
129
137
|
}
|
|
130
138
|
};
|
|
131
|
-
if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: "
|
|
139
|
+
if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: t("templates.editor.loading") });
|
|
132
140
|
if (error)
|
|
133
141
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
134
142
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
|
|
135
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
136
|
-
|
|
137
|
-
|
|
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)(
|
|
138
165
|
"button",
|
|
139
166
|
{
|
|
140
167
|
type: "button",
|
|
141
168
|
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
142
|
-
onClick: () =>
|
|
143
|
-
|
|
169
|
+
onClick: () => {
|
|
170
|
+
if (dirty && !window.confirm(t("templates.editor.discardChanges"))) return;
|
|
171
|
+
onAddLocale(type, { locales: (0, import_client.missingTemplateLocales)(templates, type) });
|
|
172
|
+
},
|
|
173
|
+
children: t("templates.editor.addLocale")
|
|
144
174
|
}
|
|
145
175
|
) : null
|
|
146
|
-
] }),
|
|
176
|
+
] }) : null,
|
|
147
177
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
|
|
148
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? "
|
|
178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: locale ?? t("templates.defaultLocale") }),
|
|
149
179
|
" \xB7 v",
|
|
150
180
|
template?.version ?? 1,
|
|
151
|
-
system ?
|
|
181
|
+
system ? ` \xB7 ${t("templates.editor.builtInNote")}` : ""
|
|
152
182
|
] }),
|
|
153
183
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.split, children: [
|
|
154
184
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.column, children: [
|
|
155
185
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { style: styles.form, onSubmit: handleSubmit, children: [
|
|
156
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-subject", children: "
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-subject", children: t("templates.subject") }),
|
|
157
187
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
158
188
|
"input",
|
|
159
189
|
{
|
|
@@ -163,7 +193,7 @@ function TemplateEditorScreen({
|
|
|
163
193
|
onChange: (event) => setSubject(event.target.value)
|
|
164
194
|
}
|
|
165
195
|
),
|
|
166
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-html", children: "
|
|
196
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-html", children: t("templates.htmlBody") }),
|
|
167
197
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
168
198
|
"textarea",
|
|
169
199
|
{
|
|
@@ -174,7 +204,7 @@ function TemplateEditorScreen({
|
|
|
174
204
|
rows: 8
|
|
175
205
|
}
|
|
176
206
|
),
|
|
177
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-text", children: "
|
|
207
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-text", children: t("templates.textBody") }),
|
|
178
208
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
179
209
|
"textarea",
|
|
180
210
|
{
|
|
@@ -195,10 +225,10 @@ function TemplateEditorScreen({
|
|
|
195
225
|
onChange: (event) => setActive(event.target.checked)
|
|
196
226
|
}
|
|
197
227
|
),
|
|
198
|
-
"
|
|
228
|
+
t("templates.editor.active")
|
|
199
229
|
] }),
|
|
200
230
|
saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.error, role: "alert", children: [
|
|
201
|
-
"
|
|
231
|
+
t("templates.editor.conflict"),
|
|
202
232
|
" ",
|
|
203
233
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
204
234
|
"button",
|
|
@@ -209,7 +239,7 @@ function TemplateEditorScreen({
|
|
|
209
239
|
setSaveError(null);
|
|
210
240
|
void refresh();
|
|
211
241
|
},
|
|
212
|
-
children: "
|
|
242
|
+
children: t("common.reload")
|
|
213
243
|
}
|
|
214
244
|
)
|
|
215
245
|
] }) : saveError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
|
|
@@ -220,10 +250,10 @@ function TemplateEditorScreen({
|
|
|
220
250
|
type: "submit",
|
|
221
251
|
disabled: isSaving || !dirty,
|
|
222
252
|
style: dirty ? styles.button : styles.buttonDisabled,
|
|
223
|
-
children: isSaving ? "
|
|
253
|
+
children: isSaving ? t("common.saving") : t("common.save")
|
|
224
254
|
}
|
|
225
255
|
),
|
|
226
|
-
!dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: "
|
|
256
|
+
!dirty && !isSaving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: t("templates.editor.noChanges") }),
|
|
227
257
|
!system ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
228
258
|
"button",
|
|
229
259
|
{
|
|
@@ -237,12 +267,12 @@ function TemplateEditorScreen({
|
|
|
237
267
|
color: "var(--fonderie-danger,#e00)",
|
|
238
268
|
borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
|
|
239
269
|
},
|
|
240
|
-
children: isDeleting ? "
|
|
270
|
+
children: isDeleting ? t("common.deleting") : t("common.delete")
|
|
241
271
|
}
|
|
242
272
|
) : null
|
|
243
273
|
] })
|
|
244
274
|
] }),
|
|
245
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-sample", children: "
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-sample", children: t("templates.editor.sampleData") }),
|
|
246
276
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
247
277
|
"textarea",
|
|
248
278
|
{
|
|
@@ -258,8 +288,8 @@ function TemplateEditorScreen({
|
|
|
258
288
|
] }),
|
|
259
289
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.previewColumn, children: [
|
|
260
290
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.previewHeader, children: [
|
|
261
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.label, children: "
|
|
262
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: isPreviewing ? "
|
|
291
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.label, children: t("templates.editor.preview") }),
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.meta, children: isPreviewing ? t("templates.editor.rendering") : t("templates.editor.live") })
|
|
263
293
|
] }),
|
|
264
294
|
previewError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: previewError.explanation }),
|
|
265
295
|
preview?.subject && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.previewSubject, children: preview.subject }),
|
|
@@ -270,26 +300,26 @@ function TemplateEditorScreen({
|
|
|
270
300
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
271
301
|
"iframe",
|
|
272
302
|
{
|
|
273
|
-
title: "
|
|
303
|
+
title: t("templates.editor.previewTitle"),
|
|
274
304
|
style: styles.previewFrame,
|
|
275
305
|
sandbox: "",
|
|
276
306
|
srcDoc: preview.html
|
|
277
307
|
}
|
|
278
308
|
)
|
|
279
|
-
) : 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: "
|
|
309
|
+
) : 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") })
|
|
280
310
|
] })
|
|
281
311
|
] }),
|
|
282
312
|
revisions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.revisions, children: [
|
|
283
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.subtitle, children: "
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.subtitle, children: t("templates.editor.history") }),
|
|
284
314
|
/* @__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: [
|
|
285
315
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
286
316
|
"v",
|
|
287
317
|
rev.version,
|
|
288
318
|
" \u2014 ",
|
|
289
|
-
rev.actor ?? "
|
|
319
|
+
rev.actor ?? t("templates.editor.unknownActor"),
|
|
290
320
|
" \u2014",
|
|
291
321
|
" ",
|
|
292
|
-
|
|
322
|
+
(0, import_client.formatAdminDate)(rev.createdAt, uiLocale)
|
|
293
323
|
] }),
|
|
294
324
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
295
325
|
"button",
|
|
@@ -297,15 +327,45 @@ function TemplateEditorScreen({
|
|
|
297
327
|
type: "button",
|
|
298
328
|
onClick: () => rollback(rev.version),
|
|
299
329
|
style: styles.rollbackButton,
|
|
300
|
-
children: "
|
|
330
|
+
children: t("templates.editor.rollBack")
|
|
301
331
|
}
|
|
302
332
|
)
|
|
303
333
|
] }, rev.version)) })
|
|
304
334
|
] })
|
|
305
335
|
] });
|
|
306
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
|
+
};
|
|
307
348
|
var styles = {
|
|
308
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" },
|
|
309
369
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
310
370
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
311
371
|
split: {
|
|
@@ -473,55 +533,75 @@ var styles = {
|
|
|
473
533
|
};
|
|
474
534
|
|
|
475
535
|
// src/screens/TemplateListScreen.tsx
|
|
536
|
+
var import_client2 = require("@fonderie/client");
|
|
476
537
|
var import_react_courier_admin2 = require("@fonderie/react-courier-admin");
|
|
477
538
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
478
539
|
function TemplateListScreen({
|
|
479
540
|
client,
|
|
480
541
|
onSelectTemplate,
|
|
481
|
-
onCreateTemplate
|
|
542
|
+
onCreateTemplate,
|
|
543
|
+
locale
|
|
482
544
|
}) {
|
|
545
|
+
const t = (0, import_client2.createAdminT)(locale);
|
|
483
546
|
const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
|
|
547
|
+
const groups = (0, import_client2.groupTemplatesByType)(templates);
|
|
484
548
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
|
|
485
549
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
486
550
|
"div",
|
|
487
551
|
{
|
|
488
552
|
style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
|
|
489
553
|
children: [
|
|
490
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: "
|
|
554
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: t("templates.list.title") }),
|
|
491
555
|
onCreateTemplate ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
492
556
|
"button",
|
|
493
557
|
{
|
|
494
558
|
type: "button",
|
|
495
559
|
onClick: () => onCreateTemplate({
|
|
496
560
|
locales: [
|
|
497
|
-
...new Set(templates.map((
|
|
561
|
+
...new Set(templates.map((row) => row.locale).filter((l) => !!l))
|
|
498
562
|
]
|
|
499
563
|
}),
|
|
500
564
|
style: styles2.newButton,
|
|
501
|
-
children: "
|
|
565
|
+
children: t("templates.list.newTemplate")
|
|
502
566
|
}
|
|
503
567
|
) : null
|
|
504
568
|
]
|
|
505
569
|
}
|
|
506
570
|
),
|
|
507
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: "
|
|
508
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: "
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
571
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.hint, children: t("templates.list.hint") }),
|
|
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)) })
|
|
525
605
|
] });
|
|
526
606
|
}
|
|
527
607
|
var pill = {
|
|
@@ -535,6 +615,18 @@ var pill = {
|
|
|
535
615
|
lineHeight: 1.5,
|
|
536
616
|
minWidth: 64
|
|
537
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
|
+
};
|
|
538
630
|
var styles2 = {
|
|
539
631
|
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
540
632
|
title: {
|
|
@@ -557,9 +649,16 @@ var styles2 = {
|
|
|
557
649
|
overflow: "hidden",
|
|
558
650
|
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
559
651
|
},
|
|
560
|
-
row: {
|
|
652
|
+
row: {
|
|
653
|
+
display: "flex",
|
|
654
|
+
alignItems: "center",
|
|
655
|
+
gap: 8,
|
|
656
|
+
paddingRight: 16,
|
|
657
|
+
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)"
|
|
658
|
+
},
|
|
561
659
|
rowButton: {
|
|
562
|
-
|
|
660
|
+
flex: 1,
|
|
661
|
+
minWidth: 0,
|
|
563
662
|
display: "flex",
|
|
564
663
|
justifyContent: "space-between",
|
|
565
664
|
alignItems: "center",
|
|
@@ -578,7 +677,9 @@ var styles2 = {
|
|
|
578
677
|
flex: 1,
|
|
579
678
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
580
679
|
},
|
|
581
|
-
|
|
680
|
+
chips: { display: "flex", flexWrap: "wrap", justifyContent: "flex-end", gap: 6 },
|
|
681
|
+
chip,
|
|
682
|
+
chipInactive: { ...chip, opacity: 0.5, textDecoration: "line-through" },
|
|
582
683
|
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
583
684
|
builtIn: {
|
|
584
685
|
fontSize: 11,
|
|
@@ -616,7 +717,7 @@ var styles2 = {
|
|
|
616
717
|
};
|
|
617
718
|
|
|
618
719
|
// src/screens/TemplateCreateScreen.tsx
|
|
619
|
-
var
|
|
720
|
+
var import_client3 = require("@fonderie/client");
|
|
620
721
|
var import_react2 = require("react");
|
|
621
722
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
622
723
|
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
@@ -625,11 +726,13 @@ function TemplateCreateScreen({
|
|
|
625
726
|
client,
|
|
626
727
|
type,
|
|
627
728
|
locales = [],
|
|
628
|
-
onCreated
|
|
729
|
+
onCreated,
|
|
730
|
+
locale
|
|
629
731
|
}) {
|
|
732
|
+
const tr = (0, import_client3.createAdminT)(locale);
|
|
630
733
|
const addingLocale = type !== void 0;
|
|
631
734
|
const [newType, setNewType] = (0, import_react2.useState)("");
|
|
632
|
-
const [
|
|
735
|
+
const [targetLocale, setTargetLocale] = (0, import_react2.useState)("");
|
|
633
736
|
const [subject, setSubject] = (0, import_react2.useState)("");
|
|
634
737
|
const [html, setHtml] = (0, import_react2.useState)("");
|
|
635
738
|
const [text, setText] = (0, import_react2.useState)("");
|
|
@@ -647,21 +750,21 @@ function TemplateCreateScreen({
|
|
|
647
750
|
e.preventDefault();
|
|
648
751
|
setError(null);
|
|
649
752
|
const t = addingLocale ? type : newType.trim();
|
|
650
|
-
const l =
|
|
651
|
-
if (!TYPE_RE.test(t))
|
|
652
|
-
|
|
653
|
-
if (
|
|
654
|
-
if (
|
|
655
|
-
if (!text.trim())
|
|
656
|
-
return setError("The plain-text body is required \u2014 it is what every email client can show.");
|
|
753
|
+
const l = targetLocale.trim() || null;
|
|
754
|
+
if (!TYPE_RE.test(t)) return setError(tr("templates.create.errorType"));
|
|
755
|
+
if (addingLocale && !l) return setError(tr("templates.create.errorLocaleRequired"));
|
|
756
|
+
if (l && !LOCALE_RE.test(l)) return setError(tr("templates.create.errorLocale"));
|
|
757
|
+
if (!text.trim()) return setError(tr("templates.create.errorText"));
|
|
657
758
|
setBusy(true);
|
|
658
759
|
try {
|
|
659
760
|
const exists = await client.getTemplate(t, l).then(
|
|
660
761
|
() => true,
|
|
661
|
-
(err) => !(err instanceof
|
|
762
|
+
(err) => !(err instanceof import_client3.FonderieApiError && err.status === 404)
|
|
662
763
|
);
|
|
663
764
|
if (exists) {
|
|
664
|
-
setError(
|
|
765
|
+
setError(
|
|
766
|
+
l ? tr("templates.create.existsLocale", { type: t, locale: l }) : tr("templates.create.exists", { type: t })
|
|
767
|
+
);
|
|
665
768
|
return;
|
|
666
769
|
}
|
|
667
770
|
const input = { text, active: true };
|
|
@@ -670,17 +773,17 @@ function TemplateCreateScreen({
|
|
|
670
773
|
await client.setTemplate(t, input, l);
|
|
671
774
|
onCreated?.({ type: t, locale: l });
|
|
672
775
|
} catch (err) {
|
|
673
|
-
setError(err instanceof
|
|
776
|
+
setError(err instanceof import_client3.FonderieApiError ? err.explanation : String(err));
|
|
674
777
|
} finally {
|
|
675
778
|
setBusy(false);
|
|
676
779
|
}
|
|
677
780
|
};
|
|
678
781
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles3.container, children: [
|
|
679
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ?
|
|
680
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? "
|
|
782
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles3.title, children: addingLocale ? tr("templates.create.titleLocale", { type }) : tr("templates.create.titleNew") }),
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.meta, children: addingLocale ? tr("templates.create.hintLocale") : tr("templates.create.hintNew") }),
|
|
681
784
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
682
785
|
!addingLocale ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
683
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: "
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-type", children: tr("templates.create.type") }),
|
|
684
787
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
685
788
|
"input",
|
|
686
789
|
{
|
|
@@ -694,25 +797,22 @@ function TemplateCreateScreen({
|
|
|
694
797
|
}
|
|
695
798
|
)
|
|
696
799
|
] }) : null,
|
|
697
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
698
|
-
"Locale ",
|
|
699
|
-
addingLocale ? "" : "(optional \u2014 empty is the default)"
|
|
700
|
-
] }),
|
|
800
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-locale", children: addingLocale ? tr("templates.create.locale") : tr("templates.create.localeOptional") }),
|
|
701
801
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
702
802
|
"input",
|
|
703
803
|
{
|
|
704
804
|
id: "new-template-locale",
|
|
705
805
|
style: styles3.input,
|
|
706
|
-
value:
|
|
806
|
+
value: targetLocale,
|
|
707
807
|
placeholder: "fr",
|
|
708
808
|
list: "new-template-locales",
|
|
709
809
|
autoComplete: "off",
|
|
710
810
|
spellCheck: false,
|
|
711
|
-
onChange: (e) =>
|
|
811
|
+
onChange: (e) => setTargetLocale(e.target.value)
|
|
712
812
|
}
|
|
713
813
|
),
|
|
714
814
|
/* @__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)) }),
|
|
715
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: "
|
|
815
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-subject", children: tr("templates.subject") }),
|
|
716
816
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
717
817
|
"input",
|
|
718
818
|
{
|
|
@@ -722,7 +822,7 @@ function TemplateCreateScreen({
|
|
|
722
822
|
onChange: (e) => setSubject(e.target.value)
|
|
723
823
|
}
|
|
724
824
|
),
|
|
725
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: "
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-html", children: tr("templates.htmlBody") }),
|
|
726
826
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
727
827
|
"textarea",
|
|
728
828
|
{
|
|
@@ -732,7 +832,7 @@ function TemplateCreateScreen({
|
|
|
732
832
|
onChange: (e) => setHtml(e.target.value)
|
|
733
833
|
}
|
|
734
834
|
),
|
|
735
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: "
|
|
835
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: styles3.label, htmlFor: "new-template-text", children: tr("templates.textBody") }),
|
|
736
836
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
737
837
|
"textarea",
|
|
738
838
|
{
|
|
@@ -743,7 +843,7 @@ function TemplateCreateScreen({
|
|
|
743
843
|
}
|
|
744
844
|
),
|
|
745
845
|
error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles3.error, role: "alert", children: error }) : null,
|
|
746
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", disabled: busy, style: styles3.button, children: busy ? "
|
|
846
|
+
/* @__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") })
|
|
747
847
|
] })
|
|
748
848
|
] });
|
|
749
849
|
}
|