@fonderie/react-courier-admin-screens 0.6.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 +328 -9
- 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 +327 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -6,13 +6,45 @@ interface ITemplateEditorScreenProps {
|
|
|
6
6
|
type: string;
|
|
7
7
|
locale?: string | null;
|
|
8
8
|
onSaved?: () => void;
|
|
9
|
+
/**
|
|
10
|
+
* A built-in email's default-locale row (the list's `system` flag): it can
|
|
11
|
+
* be edited and rolled back, never deleted — Delete is not offered.
|
|
12
|
+
*/
|
|
13
|
+
system?: boolean;
|
|
14
|
+
/** Called after a delete; the template no longer exists. */
|
|
15
|
+
onDeleted?: () => void;
|
|
16
|
+
/** Shows "Add locale": create a translation of this template. */
|
|
17
|
+
onAddLocale?: (type: string) => void;
|
|
9
18
|
}
|
|
10
|
-
declare function TemplateEditorScreen({ client, type, locale, onSaved, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
19
|
+
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
11
20
|
|
|
12
21
|
interface ITemplateListScreenProps {
|
|
13
22
|
client: CourierAdminClient;
|
|
23
|
+
/** Receives the whole row — type AND locale, and whether it is built-in. */
|
|
14
24
|
onSelectTemplate?: (template: ITemplateEntry) => void;
|
|
25
|
+
/** Shows a "New template" button. Receives the locales in use, to suggest. */
|
|
26
|
+
onCreateTemplate?: (context: {
|
|
27
|
+
locales: string[];
|
|
28
|
+
}) => void;
|
|
15
29
|
}
|
|
16
|
-
declare function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps): react.JSX.Element;
|
|
30
|
+
declare function TemplateListScreen({ client, onSelectTemplate, onCreateTemplate, }: ITemplateListScreenProps): react.JSX.Element;
|
|
17
31
|
|
|
18
|
-
|
|
32
|
+
interface ITemplateCreateScreenProps {
|
|
33
|
+
client: CourierAdminClient;
|
|
34
|
+
/**
|
|
35
|
+
* Given ⇒ add a LOCALE of this existing template: the type is fixed and the
|
|
36
|
+
* form starts from its default-locale copy. Omitted ⇒ a brand-new template
|
|
37
|
+
* type (one the app sends that has no stored copy yet).
|
|
38
|
+
*/
|
|
39
|
+
type?: string;
|
|
40
|
+
/** Locales already in use, offered as suggestions. */
|
|
41
|
+
locales?: string[];
|
|
42
|
+
/** Called with what was created, so the caller can open it in the editor. */
|
|
43
|
+
onCreated?: (created: {
|
|
44
|
+
type: string;
|
|
45
|
+
locale: string | null;
|
|
46
|
+
}) => void;
|
|
47
|
+
}
|
|
48
|
+
declare function TemplateCreateScreen({ client, type, locales, onCreated, }: ITemplateCreateScreenProps): react.JSX.Element;
|
|
49
|
+
|
|
50
|
+
export { type ITemplateCreateScreenProps, type ITemplateEditorScreenProps, type ITemplateListScreenProps, TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,13 +6,45 @@ interface ITemplateEditorScreenProps {
|
|
|
6
6
|
type: string;
|
|
7
7
|
locale?: string | null;
|
|
8
8
|
onSaved?: () => void;
|
|
9
|
+
/**
|
|
10
|
+
* A built-in email's default-locale row (the list's `system` flag): it can
|
|
11
|
+
* be edited and rolled back, never deleted — Delete is not offered.
|
|
12
|
+
*/
|
|
13
|
+
system?: boolean;
|
|
14
|
+
/** Called after a delete; the template no longer exists. */
|
|
15
|
+
onDeleted?: () => void;
|
|
16
|
+
/** Shows "Add locale": create a translation of this template. */
|
|
17
|
+
onAddLocale?: (type: string) => void;
|
|
9
18
|
}
|
|
10
|
-
declare function TemplateEditorScreen({ client, type, locale, onSaved, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
19
|
+
declare function TemplateEditorScreen({ client, type, locale, onSaved, system, onDeleted, onAddLocale, }: ITemplateEditorScreenProps): react.JSX.Element;
|
|
11
20
|
|
|
12
21
|
interface ITemplateListScreenProps {
|
|
13
22
|
client: CourierAdminClient;
|
|
23
|
+
/** Receives the whole row — type AND locale, and whether it is built-in. */
|
|
14
24
|
onSelectTemplate?: (template: ITemplateEntry) => void;
|
|
25
|
+
/** Shows a "New template" button. Receives the locales in use, to suggest. */
|
|
26
|
+
onCreateTemplate?: (context: {
|
|
27
|
+
locales: string[];
|
|
28
|
+
}) => void;
|
|
15
29
|
}
|
|
16
|
-
declare function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps): react.JSX.Element;
|
|
30
|
+
declare function TemplateListScreen({ client, onSelectTemplate, onCreateTemplate, }: ITemplateListScreenProps): react.JSX.Element;
|
|
17
31
|
|
|
18
|
-
|
|
32
|
+
interface ITemplateCreateScreenProps {
|
|
33
|
+
client: CourierAdminClient;
|
|
34
|
+
/**
|
|
35
|
+
* Given ⇒ add a LOCALE of this existing template: the type is fixed and the
|
|
36
|
+
* form starts from its default-locale copy. Omitted ⇒ a brand-new template
|
|
37
|
+
* type (one the app sends that has no stored copy yet).
|
|
38
|
+
*/
|
|
39
|
+
type?: string;
|
|
40
|
+
/** Locales already in use, offered as suggestions. */
|
|
41
|
+
locales?: string[];
|
|
42
|
+
/** Called with what was created, so the caller can open it in the editor. */
|
|
43
|
+
onCreated?: (created: {
|
|
44
|
+
type: string;
|
|
45
|
+
locale: string | null;
|
|
46
|
+
}) => void;
|
|
47
|
+
}
|
|
48
|
+
declare function TemplateCreateScreen({ client, type, locales, onCreated, }: ITemplateCreateScreenProps): react.JSX.Element;
|
|
49
|
+
|
|
50
|
+
export { type ITemplateCreateScreenProps, type ITemplateEditorScreenProps, type ITemplateListScreenProps, TemplateCreateScreen, TemplateEditorScreen, TemplateListScreen };
|
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,7 +174,22 @@ 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
194
|
/* @__PURE__ */ jsx(
|
|
149
195
|
"button",
|
|
@@ -154,7 +200,23 @@ function TemplateEditorScreen({
|
|
|
154
200
|
children: isSaving ? "Saving\u2026" : "Save"
|
|
155
201
|
}
|
|
156
202
|
),
|
|
157
|
-
!dirty && !isSaving && /* @__PURE__ */ jsx("span", { style: styles.meta, children: "No changes to save" })
|
|
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
|
|
158
220
|
] })
|
|
159
221
|
] }),
|
|
160
222
|
/* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-sample", children: "Sample data" }),
|
|
@@ -390,10 +452,35 @@ var styles = {
|
|
|
390
452
|
// src/screens/TemplateListScreen.tsx
|
|
391
453
|
import { useTemplates as useTemplates2 } from "@fonderie/react-courier-admin";
|
|
392
454
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
393
|
-
function TemplateListScreen({
|
|
455
|
+
function TemplateListScreen({
|
|
456
|
+
client,
|
|
457
|
+
onSelectTemplate,
|
|
458
|
+
onCreateTemplate
|
|
459
|
+
}) {
|
|
394
460
|
const { templates, isLoading, error } = useTemplates2(client);
|
|
395
461
|
return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
|
|
396
|
-
/* @__PURE__ */
|
|
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
|
+
),
|
|
397
484
|
/* @__PURE__ */ jsx2("p", { style: styles2.hint, children: "Every email the app sends. Open one to edit its copy and preview it live." }),
|
|
398
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(
|
|
399
486
|
"button",
|
|
@@ -404,6 +491,7 @@ function TemplateListScreen({ client, onSelectTemplate }) {
|
|
|
404
491
|
children: [
|
|
405
492
|
/* @__PURE__ */ jsx2("span", { style: styles2.type, children: template.type }),
|
|
406
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,
|
|
407
495
|
/* @__PURE__ */ jsxs2("span", { style: template.active ? styles2.active : styles2.inactive, children: [
|
|
408
496
|
/* @__PURE__ */ jsx2("span", { style: styles2.dot }),
|
|
409
497
|
template.active ? "active" : "inactive"
|
|
@@ -469,6 +557,29 @@ var styles2 = {
|
|
|
469
557
|
},
|
|
470
558
|
locale: { fontSize: 12.5, color: "var(--fonderie-text-muted,#5c5c5c)" },
|
|
471
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)"
|
|
582
|
+
},
|
|
472
583
|
active: {
|
|
473
584
|
...pill,
|
|
474
585
|
color: "var(--fonderie-accent-strong,#009767)",
|
|
@@ -480,7 +591,214 @@ var styles2 = {
|
|
|
480
591
|
background: "color-mix(in srgb, var(--fonderie-text-muted,#5c5c5c) 10%, transparent)"
|
|
481
592
|
}
|
|
482
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
|
+
}
|
|
799
|
+
};
|
|
483
800
|
export {
|
|
801
|
+
TemplateCreateScreen,
|
|
484
802
|
TemplateEditorScreen,
|
|
485
803
|
TemplateListScreen
|
|
486
804
|
};
|