@fonderie/react-courier-admin-screens 0.5.0 → 0.7.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 +507 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -3
- package/dist/index.d.ts +35 -3
- package/dist/index.js +506 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,10 +13,14 @@ function TemplateEditorScreen({
|
|
|
13
13
|
client,
|
|
14
14
|
type,
|
|
15
15
|
locale,
|
|
16
|
-
onSaved
|
|
16
|
+
onSaved,
|
|
17
|
+
system = false,
|
|
18
|
+
onDeleted,
|
|
19
|
+
onAddLocale
|
|
17
20
|
}) {
|
|
18
21
|
const { template, isLoading, error, refresh } = useTemplate(client, type, locale);
|
|
19
|
-
const { saveTemplate } = useTemplates(client);
|
|
22
|
+
const { saveTemplate, removeTemplate } = useTemplates(client);
|
|
23
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
20
24
|
const { revisions, rollback } = useTemplateRevisions(client, type, locale);
|
|
21
25
|
const [isSaving, setIsSaving] = useState(false);
|
|
22
26
|
const [saveError, setSaveError] = useState(null);
|
|
@@ -86,15 +90,42 @@ function TemplateEditorScreen({
|
|
|
86
90
|
setIsSaving(false);
|
|
87
91
|
}
|
|
88
92
|
};
|
|
93
|
+
const handleDelete = async () => {
|
|
94
|
+
const which = locale ? `the ${locale} version of "${type}"` : `"${type}"`;
|
|
95
|
+
const fallback = locale ? " People in that locale will receive the default version." : "";
|
|
96
|
+
if (!window.confirm(`Delete ${which}?${fallback}`)) return;
|
|
97
|
+
setIsDeleting(true);
|
|
98
|
+
setSaveError(null);
|
|
99
|
+
try {
|
|
100
|
+
await removeTemplate(type, locale);
|
|
101
|
+
onDeleted?.();
|
|
102
|
+
} catch (err) {
|
|
103
|
+
setSaveError(err);
|
|
104
|
+
} finally {
|
|
105
|
+
setIsDeleting(false);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
89
108
|
if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: "Loading template\u2026" });
|
|
90
109
|
if (error)
|
|
91
110
|
return /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: error.explanation });
|
|
92
111
|
return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
|
|
93
|
-
/* @__PURE__ */
|
|
112
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [
|
|
113
|
+
/* @__PURE__ */ jsx("h1", { style: styles.title, children: type }),
|
|
114
|
+
onAddLocale && !locale ? /* @__PURE__ */ jsx(
|
|
115
|
+
"button",
|
|
116
|
+
{
|
|
117
|
+
type: "button",
|
|
118
|
+
style: { ...styles.rollbackButton, marginLeft: "auto" },
|
|
119
|
+
onClick: () => onAddLocale(type),
|
|
120
|
+
children: "+ Add locale"
|
|
121
|
+
}
|
|
122
|
+
) : null
|
|
123
|
+
] }),
|
|
94
124
|
/* @__PURE__ */ jsxs("p", { style: styles.meta, children: [
|
|
95
|
-
locale ?? "
|
|
125
|
+
/* @__PURE__ */ jsx("strong", { children: locale ?? "default locale" }),
|
|
96
126
|
" \xB7 v",
|
|
97
|
-
template?.version ?? 1
|
|
127
|
+
template?.version ?? 1,
|
|
128
|
+
system ? " \xB7 built-in email: edit or roll back, it cannot be deleted" : ""
|
|
98
129
|
] }),
|
|
99
130
|
/* @__PURE__ */ jsxs("div", { style: styles.split, children: [
|
|
100
131
|
/* @__PURE__ */ jsxs("div", { style: styles.column, children: [
|
|
@@ -143,10 +174,49 @@ function TemplateEditorScreen({
|
|
|
143
174
|
),
|
|
144
175
|
"Active"
|
|
145
176
|
] }),
|
|
146
|
-
saveError
|
|
177
|
+
saveError?.reason === "VERSION_CONFLICT" ? /* @__PURE__ */ jsxs("p", { style: styles.error, role: "alert", children: [
|
|
178
|
+
"Someone changed this template since you opened it. Reload to see their change, then edit again.",
|
|
179
|
+
" ",
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
"button",
|
|
182
|
+
{
|
|
183
|
+
type: "button",
|
|
184
|
+
style: styles.rollbackButton,
|
|
185
|
+
onClick: () => {
|
|
186
|
+
setSaveError(null);
|
|
187
|
+
void refresh();
|
|
188
|
+
},
|
|
189
|
+
children: "Reload"
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
] }) : saveError ? /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: saveError.explanation }) : null,
|
|
147
193
|
/* @__PURE__ */ jsxs("div", { style: styles.saveRow, children: [
|
|
148
|
-
/* @__PURE__ */ jsx(
|
|
149
|
-
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
"button",
|
|
196
|
+
{
|
|
197
|
+
type: "submit",
|
|
198
|
+
disabled: isSaving || !dirty,
|
|
199
|
+
style: dirty ? styles.button : styles.buttonDisabled,
|
|
200
|
+
children: isSaving ? "Saving\u2026" : "Save"
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
!dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: "No changes to save" }),
|
|
204
|
+
!system ? /* @__PURE__ */ jsx(
|
|
205
|
+
"button",
|
|
206
|
+
{
|
|
207
|
+
type: "button",
|
|
208
|
+
disabled: isDeleting,
|
|
209
|
+
onClick: () => void handleDelete(),
|
|
210
|
+
style: {
|
|
211
|
+
...styles.rollbackButton,
|
|
212
|
+
height: 36,
|
|
213
|
+
marginLeft: "auto",
|
|
214
|
+
color: "var(--fonderie-danger,#e00)",
|
|
215
|
+
borderColor: "color-mix(in srgb, var(--fonderie-danger,#e00) 40%, transparent)"
|
|
216
|
+
},
|
|
217
|
+
children: isDeleting ? "Deleting\u2026" : "Delete"
|
|
218
|
+
}
|
|
219
|
+
) : null
|
|
150
220
|
] })
|
|
151
221
|
] }),
|
|
152
222
|
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: "Sample data" }),
|
|
@@ -212,13 +282,24 @@ function TemplateEditorScreen({
|
|
|
212
282
|
] });
|
|
213
283
|
}
|
|
214
284
|
var styles = {
|
|
215
|
-
container: { padding:
|
|
285
|
+
container: { padding: "8px 40px 64px", maxWidth: 1400, boxSizing: "border-box" },
|
|
216
286
|
// Two columns, always: the editor on the left, the rendered result on the
|
|
217
287
|
// right. Stacking the preview under a long form hid it below the fold.
|
|
218
|
-
split: {
|
|
288
|
+
split: {
|
|
289
|
+
display: "grid",
|
|
290
|
+
gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1fr)",
|
|
291
|
+
gap: 24,
|
|
292
|
+
alignItems: "start"
|
|
293
|
+
},
|
|
219
294
|
column: { minWidth: 0, display: "flex", flexDirection: "column" },
|
|
220
295
|
// Pinned while the form scrolls, so the preview stays in view as you edit.
|
|
221
|
-
previewColumn: {
|
|
296
|
+
previewColumn: {
|
|
297
|
+
minWidth: 0,
|
|
298
|
+
display: "flex",
|
|
299
|
+
flexDirection: "column",
|
|
300
|
+
position: "sticky",
|
|
301
|
+
top: 16
|
|
302
|
+
},
|
|
222
303
|
saveRow: { display: "flex", alignItems: "center", gap: 12, marginTop: 16 },
|
|
223
304
|
previewHeader: {
|
|
224
305
|
display: "flex",
|
|
@@ -228,6 +309,7 @@ var styles = {
|
|
|
228
309
|
},
|
|
229
310
|
previewSubject: { fontSize: 14, fontWeight: 600, margin: "8px 0" },
|
|
230
311
|
previewFrame: {
|
|
312
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))",
|
|
231
313
|
width: "100%",
|
|
232
314
|
height: 520,
|
|
233
315
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
@@ -242,60 +324,127 @@ var styles = {
|
|
|
242
324
|
fontSize: 13,
|
|
243
325
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
244
326
|
},
|
|
245
|
-
title: {
|
|
246
|
-
|
|
327
|
+
title: {
|
|
328
|
+
fontSize: 22,
|
|
329
|
+
fontWeight: 600,
|
|
330
|
+
margin: "8px 0 4px",
|
|
331
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
|
|
332
|
+
lineHeight: 1.25
|
|
333
|
+
},
|
|
334
|
+
meta: { fontSize: 13, color: "var(--fonderie-text-muted,#5c5c5c)", marginBottom: 20 },
|
|
247
335
|
status: { padding: 24, textAlign: "center", color: "var(--fonderie-text-muted,#5c5c5c)" },
|
|
248
|
-
error: {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
336
|
+
error: {
|
|
337
|
+
color: "var(--fonderie-danger,#e00)",
|
|
338
|
+
background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
|
|
339
|
+
borderRadius: 6,
|
|
340
|
+
padding: "8px 12px",
|
|
341
|
+
margin: "8px 0",
|
|
342
|
+
fontSize: 13.5
|
|
343
|
+
},
|
|
344
|
+
form: {
|
|
345
|
+
display: "flex",
|
|
346
|
+
flexDirection: "column",
|
|
347
|
+
gap: 4,
|
|
348
|
+
background: "var(--fonderie-surface,#fff)",
|
|
349
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
350
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
351
|
+
padding: "6px 20px 20px",
|
|
352
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
353
|
+
},
|
|
354
|
+
label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
|
|
355
|
+
input: {
|
|
356
|
+
height: 36,
|
|
357
|
+
boxSizing: "border-box",
|
|
358
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
359
|
+
borderRadius: 6,
|
|
360
|
+
padding: "0 10px",
|
|
361
|
+
fontSize: 13.5,
|
|
362
|
+
fontFamily: "inherit",
|
|
363
|
+
background: "var(--fonderie-surface,#fff)",
|
|
364
|
+
color: "var(--fonderie-text,#171717)"
|
|
365
|
+
},
|
|
252
366
|
textarea: {
|
|
253
367
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
254
|
-
borderRadius:
|
|
368
|
+
borderRadius: 6,
|
|
255
369
|
padding: 10,
|
|
256
|
-
fontSize:
|
|
370
|
+
fontSize: 13,
|
|
371
|
+
lineHeight: 1.55,
|
|
372
|
+
background: "var(--fonderie-surface,#fff)",
|
|
373
|
+
color: "var(--fonderie-text,#171717)",
|
|
257
374
|
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
258
375
|
},
|
|
259
376
|
checkboxLabel: { display: "flex", alignItems: "center", gap: 8, marginTop: 12, fontSize: 14 },
|
|
260
377
|
button: {
|
|
378
|
+
display: "inline-flex",
|
|
379
|
+
alignItems: "center",
|
|
380
|
+
height: 36,
|
|
381
|
+
boxSizing: "border-box",
|
|
261
382
|
backgroundColor: "var(--fonderie-text,#171717)",
|
|
262
383
|
color: "var(--fonderie-surface,#fff)",
|
|
263
|
-
padding: "
|
|
264
|
-
borderRadius:
|
|
265
|
-
border: "
|
|
266
|
-
fontSize:
|
|
384
|
+
padding: "0 16px",
|
|
385
|
+
borderRadius: 6,
|
|
386
|
+
border: "1px solid var(--fonderie-text,#171717)",
|
|
387
|
+
fontSize: 13.5,
|
|
267
388
|
fontWeight: 600,
|
|
389
|
+
fontFamily: "inherit",
|
|
268
390
|
cursor: "pointer",
|
|
269
391
|
alignSelf: "flex-start"
|
|
270
392
|
},
|
|
271
393
|
buttonDisabled: {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
394
|
+
display: "inline-flex",
|
|
395
|
+
alignItems: "center",
|
|
396
|
+
height: 36,
|
|
397
|
+
boxSizing: "border-box",
|
|
398
|
+
backgroundColor: "var(--fonderie-text,#171717)",
|
|
399
|
+
color: "var(--fonderie-surface,#fff)",
|
|
400
|
+
padding: "0 16px",
|
|
401
|
+
borderRadius: 6,
|
|
402
|
+
border: "1px solid var(--fonderie-text,#171717)",
|
|
403
|
+
fontSize: 13.5,
|
|
278
404
|
fontWeight: 600,
|
|
279
|
-
|
|
280
|
-
alignSelf: "flex-start"
|
|
405
|
+
fontFamily: "inherit",
|
|
406
|
+
alignSelf: "flex-start",
|
|
407
|
+
opacity: 0.45,
|
|
408
|
+
cursor: "not-allowed"
|
|
281
409
|
},
|
|
282
410
|
revisions: { marginTop: 32 },
|
|
283
|
-
subtitle: {
|
|
284
|
-
|
|
411
|
+
subtitle: {
|
|
412
|
+
fontSize: 15,
|
|
413
|
+
fontWeight: 600,
|
|
414
|
+
margin: "0 0 10px",
|
|
415
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)"
|
|
416
|
+
},
|
|
417
|
+
list: {
|
|
418
|
+
listStyle: "none",
|
|
419
|
+
padding: 0,
|
|
420
|
+
margin: 0,
|
|
421
|
+
background: "var(--fonderie-surface,#fff)",
|
|
422
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
423
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
424
|
+
overflow: "hidden"
|
|
425
|
+
},
|
|
285
426
|
row: {
|
|
286
427
|
display: "flex",
|
|
287
428
|
justifyContent: "space-between",
|
|
288
429
|
alignItems: "center",
|
|
289
|
-
|
|
430
|
+
gap: 12,
|
|
431
|
+
padding: "9px 16px",
|
|
290
432
|
borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)",
|
|
291
433
|
fontSize: 13
|
|
292
434
|
},
|
|
293
435
|
rollbackButton: {
|
|
294
|
-
|
|
436
|
+
display: "inline-flex",
|
|
437
|
+
alignItems: "center",
|
|
438
|
+
height: 28,
|
|
439
|
+
boxSizing: "border-box",
|
|
440
|
+
background: "var(--fonderie-surface,#fff)",
|
|
441
|
+
color: "var(--fonderie-text,#171717)",
|
|
295
442
|
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
296
|
-
borderRadius:
|
|
297
|
-
padding: "
|
|
298
|
-
fontSize: 12,
|
|
443
|
+
borderRadius: 6,
|
|
444
|
+
padding: "0 10px",
|
|
445
|
+
fontSize: 12.5,
|
|
446
|
+
fontWeight: 500,
|
|
447
|
+
fontFamily: "inherit",
|
|
299
448
|
cursor: "pointer"
|
|
300
449
|
}
|
|
301
450
|
};
|
|
@@ -303,14 +452,37 @@ var styles = {
|
|
|
303
452
|
// src/screens/TemplateListScreen.tsx
|
|
304
453
|
import { useTemplates as useTemplates2 } from "@fonderie/react-courier-admin";
|
|
305
454
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
306
|
-
function TemplateListScreen({
|
|
455
|
+
function TemplateListScreen({
|
|
456
|
+
client,
|
|
457
|
+
onSelectTemplate,
|
|
458
|
+
onCreateTemplate
|
|
459
|
+
}) {
|
|
307
460
|
const { templates, isLoading, error } = useTemplates2(client);
|
|
308
|
-
if (isLoading) return /* @__PURE__ */ jsx2("p", { style: styles2.status, children: "Loading templates\u2026" });
|
|
309
|
-
if (error)
|
|
310
|
-
return /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation });
|
|
311
461
|
return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
|
|
312
|
-
/* @__PURE__ */
|
|
313
|
-
|
|
462
|
+
/* @__PURE__ */ jsxs2(
|
|
463
|
+
"div",
|
|
464
|
+
{
|
|
465
|
+
style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 },
|
|
466
|
+
children: [
|
|
467
|
+
/* @__PURE__ */ jsx2("h1", { style: styles2.title, children: "Templates" }),
|
|
468
|
+
onCreateTemplate ? /* @__PURE__ */ jsx2(
|
|
469
|
+
"button",
|
|
470
|
+
{
|
|
471
|
+
type: "button",
|
|
472
|
+
onClick: () => onCreateTemplate({
|
|
473
|
+
locales: [
|
|
474
|
+
...new Set(templates.map((t) => t.locale).filter((l) => !!l))
|
|
475
|
+
]
|
|
476
|
+
}),
|
|
477
|
+
style: styles2.newButton,
|
|
478
|
+
children: "New template"
|
|
479
|
+
}
|
|
480
|
+
) : null
|
|
481
|
+
]
|
|
482
|
+
}
|
|
483
|
+
),
|
|
484
|
+
/* @__PURE__ */ jsx2("p", { style: styles2.hint, children: "Every email the app sends. Open one to edit its copy and preview it live." }),
|
|
485
|
+
isLoading ? /* @__PURE__ */ jsx2("p", { style: styles2.status, children: "Loading templates\u2026" }) : 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(
|
|
314
486
|
"button",
|
|
315
487
|
{
|
|
316
488
|
type: "button",
|
|
@@ -318,37 +490,315 @@ function TemplateListScreen({ client, onSelectTemplate }) {
|
|
|
318
490
|
style: styles2.rowButton,
|
|
319
491
|
children: [
|
|
320
492
|
/* @__PURE__ */ jsx2("span", { style: styles2.type, children: template.type }),
|
|
321
|
-
/* @__PURE__ */ jsx2("span", { style: styles2.locale, children: template.locale ?? "
|
|
322
|
-
/* @__PURE__ */ jsx2("span", { style:
|
|
493
|
+
/* @__PURE__ */ jsx2("span", { style: styles2.locale, children: template.locale ?? "default locale" }),
|
|
494
|
+
template.system ? /* @__PURE__ */ jsx2("span", { style: styles2.builtIn, children: "built-in" }) : null,
|
|
495
|
+
/* @__PURE__ */ jsxs2("span", { style: template.active ? styles2.active : styles2.inactive, children: [
|
|
496
|
+
/* @__PURE__ */ jsx2("span", { style: styles2.dot }),
|
|
497
|
+
template.active ? "active" : "inactive"
|
|
498
|
+
] })
|
|
323
499
|
]
|
|
324
500
|
}
|
|
325
501
|
) }, `${template.type}:${template.locale ?? "base"}`)) })
|
|
326
502
|
] });
|
|
327
503
|
}
|
|
504
|
+
var pill = {
|
|
505
|
+
display: "inline-flex",
|
|
506
|
+
alignItems: "center",
|
|
507
|
+
gap: 6,
|
|
508
|
+
borderRadius: 999,
|
|
509
|
+
padding: "2px 9px 2px 8px",
|
|
510
|
+
fontSize: 12,
|
|
511
|
+
fontWeight: 600,
|
|
512
|
+
lineHeight: 1.5,
|
|
513
|
+
minWidth: 64
|
|
514
|
+
};
|
|
328
515
|
var styles2 = {
|
|
329
|
-
container: { padding:
|
|
330
|
-
title: {
|
|
331
|
-
|
|
516
|
+
container: { padding: "32px 40px 64px", maxWidth: 1160, boxSizing: "border-box" },
|
|
517
|
+
title: {
|
|
518
|
+
fontSize: 22,
|
|
519
|
+
fontWeight: 600,
|
|
520
|
+
margin: 0,
|
|
521
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
|
|
522
|
+
lineHeight: 1.25
|
|
523
|
+
},
|
|
524
|
+
hint: { fontSize: 13.5, color: "var(--fonderie-text-muted,#5c5c5c)", margin: "4px 0 24px" },
|
|
525
|
+
status: { padding: "24px 0", color: "var(--fonderie-text-muted,#5c5c5c)", fontSize: 13.5 },
|
|
332
526
|
error: { color: "var(--fonderie-danger,#e00)", marginBottom: 12, fontSize: 14 },
|
|
333
|
-
list: {
|
|
527
|
+
list: {
|
|
528
|
+
listStyle: "none",
|
|
529
|
+
padding: 0,
|
|
530
|
+
margin: 0,
|
|
531
|
+
background: "var(--fonderie-surface,#fff)",
|
|
532
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
533
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
534
|
+
overflow: "hidden",
|
|
535
|
+
boxShadow: "var(--fonderie-shadow-card,0 2px 3px 0 rgba(0,0,0,.05))"
|
|
536
|
+
},
|
|
334
537
|
row: { borderBottom: "1px solid var(--fonderie-border-light,#f5f5f5)" },
|
|
335
538
|
rowButton: {
|
|
336
539
|
width: "100%",
|
|
337
540
|
display: "flex",
|
|
338
541
|
justifyContent: "space-between",
|
|
339
542
|
alignItems: "center",
|
|
340
|
-
|
|
543
|
+
gap: 16,
|
|
544
|
+
padding: "13px 16px",
|
|
341
545
|
background: "none",
|
|
342
546
|
border: "none",
|
|
343
547
|
cursor: "pointer",
|
|
344
|
-
textAlign: "left"
|
|
548
|
+
textAlign: "left",
|
|
549
|
+
color: "var(--fonderie-text,#171717)",
|
|
550
|
+
fontFamily: "inherit"
|
|
551
|
+
},
|
|
552
|
+
type: {
|
|
553
|
+
fontSize: 13,
|
|
554
|
+
fontWeight: 600,
|
|
555
|
+
flex: 1,
|
|
556
|
+
fontFamily: 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)'
|
|
557
|
+
},
|
|
558
|
+
locale: { fontSize: 12.5, color: "var(--fonderie-text-muted,#5c5c5c)" },
|
|
559
|
+
dot: { width: 6, height: 6, borderRadius: 999, background: "currentColor" },
|
|
560
|
+
builtIn: {
|
|
561
|
+
fontSize: 11,
|
|
562
|
+
fontWeight: 500,
|
|
563
|
+
padding: "1px 7px",
|
|
564
|
+
borderRadius: 999,
|
|
565
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
566
|
+
color: "var(--fonderie-text-muted,#5c5c5c)"
|
|
567
|
+
},
|
|
568
|
+
newButton: {
|
|
569
|
+
display: "inline-flex",
|
|
570
|
+
alignItems: "center",
|
|
571
|
+
height: 32,
|
|
572
|
+
boxSizing: "border-box",
|
|
573
|
+
borderRadius: 6,
|
|
574
|
+
padding: "0 12px",
|
|
575
|
+
fontSize: 13,
|
|
576
|
+
fontWeight: 500,
|
|
577
|
+
fontFamily: "inherit",
|
|
578
|
+
cursor: "pointer",
|
|
579
|
+
backgroundColor: "var(--fonderie-text,#171717)",
|
|
580
|
+
color: "var(--fonderie-surface,#fff)",
|
|
581
|
+
border: "1px solid var(--fonderie-text,#171717)"
|
|
345
582
|
},
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
583
|
+
active: {
|
|
584
|
+
...pill,
|
|
585
|
+
color: "var(--fonderie-accent-strong,#009767)",
|
|
586
|
+
background: "color-mix(in srgb, var(--fonderie-accent-strong,#009767) 13%, transparent)"
|
|
587
|
+
},
|
|
588
|
+
inactive: {
|
|
589
|
+
...pill,
|
|
590
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
591
|
+
background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/screens/TemplateCreateScreen.tsx
|
|
596
|
+
import { FonderieApiError } from "@fonderie/client";
|
|
597
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
598
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
599
|
+
var TYPE_RE = /^[a-z0-9][a-z0-9_-]{0,80}$/;
|
|
600
|
+
var LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
601
|
+
function TemplateCreateScreen({
|
|
602
|
+
client,
|
|
603
|
+
type,
|
|
604
|
+
locales = [],
|
|
605
|
+
onCreated
|
|
606
|
+
}) {
|
|
607
|
+
const addingLocale = type !== void 0;
|
|
608
|
+
const [newType, setNewType] = useState2("");
|
|
609
|
+
const [locale, setLocale] = useState2("");
|
|
610
|
+
const [subject, setSubject] = useState2("");
|
|
611
|
+
const [html, setHtml] = useState2("");
|
|
612
|
+
const [text, setText] = useState2("");
|
|
613
|
+
const [error, setError] = useState2(null);
|
|
614
|
+
const [busy, setBusy] = useState2(false);
|
|
615
|
+
useEffect2(() => {
|
|
616
|
+
if (!type) return;
|
|
617
|
+
client.getTemplate(type, null).then(({ result }) => {
|
|
618
|
+
setSubject(result.subject ?? "");
|
|
619
|
+
setHtml(result.html ?? "");
|
|
620
|
+
setText(result.text);
|
|
621
|
+
}).catch(() => void 0);
|
|
622
|
+
}, [client, type]);
|
|
623
|
+
const submit = async (e) => {
|
|
624
|
+
e.preventDefault();
|
|
625
|
+
setError(null);
|
|
626
|
+
const t = addingLocale ? type : newType.trim();
|
|
627
|
+
const l = locale.trim() || null;
|
|
628
|
+
if (!TYPE_RE.test(t))
|
|
629
|
+
return setError("Type: lowercase letters, digits, - and _ (e.g. weekly-digest).");
|
|
630
|
+
if (addingLocale && !l) return setError("Choose the locale to add, e.g. fr or fr-CA.");
|
|
631
|
+
if (l && !LOCALE_RE.test(l)) return setError("Locale: a language tag such as fr, es or fr-CA.");
|
|
632
|
+
if (!text.trim())
|
|
633
|
+
return setError("The plain-text body is required \u2014 it is what every email client can show.");
|
|
634
|
+
setBusy(true);
|
|
635
|
+
try {
|
|
636
|
+
const exists = await client.getTemplate(t, l).then(
|
|
637
|
+
() => true,
|
|
638
|
+
(err) => !(err instanceof FonderieApiError && err.status === 404)
|
|
639
|
+
);
|
|
640
|
+
if (exists) {
|
|
641
|
+
setError(`"${t}"${l ? ` (${l})` : ""} already exists \u2014 open it from the list to edit.`);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const input = { text, active: true };
|
|
645
|
+
if (subject) input.subject = subject;
|
|
646
|
+
if (html) input.html = html;
|
|
647
|
+
await client.setTemplate(t, input, l);
|
|
648
|
+
onCreated?.({ type: t, locale: l });
|
|
649
|
+
} catch (err) {
|
|
650
|
+
setError(err instanceof FonderieApiError ? err.explanation : String(err));
|
|
651
|
+
} finally {
|
|
652
|
+
setBusy(false);
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles3.container, children: [
|
|
656
|
+
/* @__PURE__ */ jsx3("h1", { style: styles3.title, children: addingLocale ? `Add a locale to ${type}` : "New template" }),
|
|
657
|
+
/* @__PURE__ */ jsx3("p", { style: styles3.meta, children: addingLocale ? "Starts from the default copy. People whose locale matches receive this version; everyone else keeps the default." : "For an email your app sends that has no stored copy yet. The type must match what the app sends." }),
|
|
658
|
+
/* @__PURE__ */ jsxs3("form", { style: styles3.form, onSubmit: (e) => void submit(e), children: [
|
|
659
|
+
!addingLocale ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
660
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-type", children: "Type" }),
|
|
661
|
+
/* @__PURE__ */ jsx3(
|
|
662
|
+
"input",
|
|
663
|
+
{
|
|
664
|
+
id: "new-template-type",
|
|
665
|
+
style: styles3.input,
|
|
666
|
+
value: newType,
|
|
667
|
+
placeholder: "weekly-digest",
|
|
668
|
+
autoComplete: "off",
|
|
669
|
+
spellCheck: false,
|
|
670
|
+
onChange: (e) => setNewType(e.target.value)
|
|
671
|
+
}
|
|
672
|
+
)
|
|
673
|
+
] }) : null,
|
|
674
|
+
/* @__PURE__ */ jsxs3("label", { style: styles3.label, htmlFor: "new-template-locale", children: [
|
|
675
|
+
"Locale ",
|
|
676
|
+
addingLocale ? "" : "(optional \u2014 empty is the default)"
|
|
677
|
+
] }),
|
|
678
|
+
/* @__PURE__ */ jsx3(
|
|
679
|
+
"input",
|
|
680
|
+
{
|
|
681
|
+
id: "new-template-locale",
|
|
682
|
+
style: styles3.input,
|
|
683
|
+
value: locale,
|
|
684
|
+
placeholder: "fr",
|
|
685
|
+
list: "new-template-locales",
|
|
686
|
+
autoComplete: "off",
|
|
687
|
+
spellCheck: false,
|
|
688
|
+
onChange: (e) => setLocale(e.target.value)
|
|
689
|
+
}
|
|
690
|
+
),
|
|
691
|
+
/* @__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: "Subject" }),
|
|
693
|
+
/* @__PURE__ */ jsx3(
|
|
694
|
+
"input",
|
|
695
|
+
{
|
|
696
|
+
id: "new-template-subject",
|
|
697
|
+
style: styles3.input,
|
|
698
|
+
value: subject,
|
|
699
|
+
onChange: (e) => setSubject(e.target.value)
|
|
700
|
+
}
|
|
701
|
+
),
|
|
702
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-html", children: "HTML body" }),
|
|
703
|
+
/* @__PURE__ */ jsx3(
|
|
704
|
+
"textarea",
|
|
705
|
+
{
|
|
706
|
+
id: "new-template-html",
|
|
707
|
+
style: { ...styles3.textarea, minHeight: 160 },
|
|
708
|
+
value: html,
|
|
709
|
+
onChange: (e) => setHtml(e.target.value)
|
|
710
|
+
}
|
|
711
|
+
),
|
|
712
|
+
/* @__PURE__ */ jsx3("label", { style: styles3.label, htmlFor: "new-template-text", children: "Plain-text body" }),
|
|
713
|
+
/* @__PURE__ */ jsx3(
|
|
714
|
+
"textarea",
|
|
715
|
+
{
|
|
716
|
+
id: "new-template-text",
|
|
717
|
+
style: { ...styles3.textarea, minHeight: 100 },
|
|
718
|
+
value: text,
|
|
719
|
+
onChange: (e) => setText(e.target.value)
|
|
720
|
+
}
|
|
721
|
+
),
|
|
722
|
+
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 ? "Creating\u2026" : addingLocale ? "Add locale" : "Create template" })
|
|
724
|
+
] })
|
|
725
|
+
] });
|
|
726
|
+
}
|
|
727
|
+
var MONO = 'var(--fonderie-mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace)';
|
|
728
|
+
var styles3 = {
|
|
729
|
+
container: { padding: "8px 40px 64px", maxWidth: 760, boxSizing: "border-box" },
|
|
730
|
+
title: {
|
|
731
|
+
fontSize: 22,
|
|
732
|
+
fontWeight: 600,
|
|
733
|
+
margin: "8px 0 4px",
|
|
734
|
+
letterSpacing: "var(--fonderie-tracking-display,-0.05em)",
|
|
735
|
+
lineHeight: 1.25
|
|
736
|
+
},
|
|
737
|
+
meta: {
|
|
738
|
+
fontSize: 13,
|
|
739
|
+
color: "var(--fonderie-text-muted,#5c5c5c)",
|
|
740
|
+
marginBottom: 20,
|
|
741
|
+
lineHeight: 1.5
|
|
742
|
+
},
|
|
743
|
+
form: {
|
|
744
|
+
display: "flex",
|
|
745
|
+
flexDirection: "column",
|
|
746
|
+
gap: 4,
|
|
747
|
+
background: "var(--fonderie-surface,#fff)",
|
|
748
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
749
|
+
borderRadius: "var(--fonderie-radius-lg,8px)",
|
|
750
|
+
padding: "6px 20px 20px"
|
|
751
|
+
},
|
|
752
|
+
label: { fontSize: 13, fontWeight: 500, marginTop: 14, marginBottom: 2 },
|
|
753
|
+
input: {
|
|
754
|
+
height: 36,
|
|
755
|
+
boxSizing: "border-box",
|
|
756
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
757
|
+
borderRadius: 6,
|
|
758
|
+
padding: "0 10px",
|
|
759
|
+
fontSize: 13.5,
|
|
760
|
+
fontFamily: "inherit",
|
|
761
|
+
background: "var(--fonderie-surface,#fff)",
|
|
762
|
+
color: "var(--fonderie-text,#171717)"
|
|
763
|
+
},
|
|
764
|
+
textarea: {
|
|
765
|
+
border: "1px solid var(--fonderie-border,#e0e0e0)",
|
|
766
|
+
borderRadius: 6,
|
|
767
|
+
padding: 10,
|
|
768
|
+
fontSize: 13,
|
|
769
|
+
lineHeight: 1.55,
|
|
770
|
+
background: "var(--fonderie-surface,#fff)",
|
|
771
|
+
color: "var(--fonderie-text,#171717)",
|
|
772
|
+
fontFamily: MONO
|
|
773
|
+
},
|
|
774
|
+
error: {
|
|
775
|
+
color: "var(--fonderie-danger,#e00)",
|
|
776
|
+
background: "color-mix(in srgb, var(--fonderie-danger,#e00) 8%, transparent)",
|
|
777
|
+
borderRadius: 6,
|
|
778
|
+
padding: "8px 12px",
|
|
779
|
+
margin: "8px 0",
|
|
780
|
+
fontSize: 13.5
|
|
781
|
+
},
|
|
782
|
+
button: {
|
|
783
|
+
display: "inline-flex",
|
|
784
|
+
alignItems: "center",
|
|
785
|
+
height: 36,
|
|
786
|
+
boxSizing: "border-box",
|
|
787
|
+
backgroundColor: "var(--fonderie-text,#171717)",
|
|
788
|
+
color: "var(--fonderie-surface,#fff)",
|
|
789
|
+
padding: "0 16px",
|
|
790
|
+
borderRadius: 6,
|
|
791
|
+
border: "1px solid var(--fonderie-text,#171717)",
|
|
792
|
+
fontSize: 13.5,
|
|
793
|
+
fontWeight: 600,
|
|
794
|
+
fontFamily: "inherit",
|
|
795
|
+
cursor: "pointer",
|
|
796
|
+
alignSelf: "flex-start",
|
|
797
|
+
marginTop: 16
|
|
798
|
+
}
|
|
350
799
|
};
|
|
351
800
|
export {
|
|
801
|
+
TemplateCreateScreen,
|
|
352
802
|
TemplateEditorScreen,
|
|
353
803
|
TemplateListScreen
|
|
354
804
|
};
|