@fonderie/react-courier-admin-screens 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fonderie, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @fonderie/react-courier-admin-screens
2
+
3
+ Pre-built React screens for the Fonderie courier admin API —
4
+ `TemplateListScreen` and `TemplateEditorScreen` — built on
5
+ [`@fonderie/react-courier-admin`](https://github.com/fonderie-js/sdk/tree/main/packages/react-courier-admin)
6
+ hooks. Plain HTML elements, inline styles you can override, zero UI library
7
+ dependency.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install @fonderie/react-courier-admin-screens
13
+ ```
14
+
15
+ ## Use
16
+
17
+ ```tsx
18
+ import { CourierAdminClient } from '@fonderie/client';
19
+ import { TemplateListScreen, TemplateEditorScreen } from '@fonderie/react-courier-admin-screens';
20
+
21
+ const admin = new CourierAdminClient({
22
+ baseUrl: 'https://api.example.com/v1',
23
+ adminToken: process.env.FONDERIE_ADMIN_TOKEN!,
24
+ });
25
+
26
+ function Templates() {
27
+ return (
28
+ <TemplateListScreen
29
+ client={admin}
30
+ onSelectTemplate={(t) => setRoute(`edit/${t.type}/${t.locale ?? ''}`)}
31
+ />
32
+ );
33
+ }
34
+
35
+ function EditTemplate({ type, locale }: { type: string; locale?: string }) {
36
+ return <TemplateEditorScreen client={admin} type={type} locale={locale} onSaved={() => setRoute('list')} />;
37
+ }
38
+ ```
39
+
40
+ `TemplateListScreen` lists every template with its locale and active state.
41
+ `TemplateEditorScreen` is a subject/HTML/plain-text form with a revision
42
+ history and one-click rollback, using the loaded template's version as
43
+ optimistic-concurrency `ifVersion` on save. Need just the state management
44
+ without the markup? Use `@fonderie/react-courier-admin`'s hooks directly.
45
+
46
+ ## Why this exists
47
+
48
+ You've shipped this plumbing before — auth, teams, billing, messaging —
49
+ and the next project will ask for it again. Fonderie packages it once:
50
+ plain TypeScript modules for
51
+ [`@fonderie/core`](https://github.com/fonderie-js/sdk/tree/main/packages/core),
52
+ PostgreSQL-backed, self-hosted, MIT. No external control plane, no
53
+ per-seat anything. Register the modules you need; skip the ones you don't.
54
+
55
+ **This package owns** pre-built React admin UI for courier's templates, so
56
+ you don't have to build a template editor from scratch. Swap it for your own
57
+ design system whenever you outgrow it — `@fonderie/react-courier-admin`'s
58
+ hooks work standalone.
59
+
60
+ Browse the whole set at
61
+ [fonderie-js/sdk](https://github.com/fonderie-js/sdk) · follow
62
+ [@fonderiejs](https://x.com/fonderiejs)
63
+
64
+ ## License
65
+
66
+ MIT © Fonderie, Inc.
package/dist/index.cjs ADDED
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TemplateEditorScreen: () => TemplateEditorScreen,
24
+ TemplateListScreen: () => TemplateListScreen
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/screens/TemplateEditorScreen.tsx
29
+ var import_react_courier_admin = require("@fonderie/react-courier-admin");
30
+ var import_react = require("react");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ function TemplateEditorScreen({
33
+ client,
34
+ type,
35
+ locale,
36
+ onSaved
37
+ }) {
38
+ const { template, isLoading, error, refresh } = (0, import_react_courier_admin.useTemplate)(client, type, locale);
39
+ const { saveTemplate, isLoading: isSaving, error: saveError } = (0, import_react_courier_admin.useSaveTemplate)(client);
40
+ const { revisions, rollback } = (0, import_react_courier_admin.useTemplateRevisions)(client, type, locale);
41
+ const [subject, setSubject] = (0, import_react.useState)("");
42
+ const [html, setHtml] = (0, import_react.useState)("");
43
+ const [text, setText] = (0, import_react.useState)("");
44
+ const [active, setActive] = (0, import_react.useState)(true);
45
+ (0, import_react.useEffect)(() => {
46
+ if (!template) return;
47
+ setSubject(template.subject ?? "");
48
+ setHtml(template.html ?? "");
49
+ setText(template.text);
50
+ setActive(template.active);
51
+ }, [template]);
52
+ const handleSubmit = async (event) => {
53
+ event.preventDefault();
54
+ try {
55
+ const input = { text, active };
56
+ if (subject) input.subject = subject;
57
+ if (html) input.html = html;
58
+ if (template?.version !== void 0) input.ifVersion = template.version;
59
+ await saveTemplate(type, input, locale);
60
+ await refresh();
61
+ onSaved?.();
62
+ } catch {
63
+ }
64
+ };
65
+ if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: "Loading template\u2026" });
66
+ if (error)
67
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: error.explanation });
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.container, children: [
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { style: styles.title, children: type }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: styles.meta, children: [
71
+ locale ?? "base",
72
+ " \xB7 v",
73
+ template?.version ?? 1
74
+ ] }),
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { style: styles.form, onSubmit: handleSubmit, children: [
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-subject", children: "Subject" }),
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
78
+ "input",
79
+ {
80
+ id: "template-subject",
81
+ style: styles.input,
82
+ value: subject,
83
+ onChange: (event) => setSubject(event.target.value)
84
+ }
85
+ ),
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-html", children: "HTML body" }),
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
88
+ "textarea",
89
+ {
90
+ id: "template-html",
91
+ style: styles.textarea,
92
+ value: html,
93
+ onChange: (event) => setHtml(event.target.value),
94
+ rows: 8
95
+ }
96
+ ),
97
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { style: styles.label, htmlFor: "template-text", children: "Plain-text body" }),
98
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
99
+ "textarea",
100
+ {
101
+ id: "template-text",
102
+ style: styles.textarea,
103
+ value: text,
104
+ onChange: (event) => setText(event.target.value),
105
+ rows: 4,
106
+ required: true
107
+ }
108
+ ),
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.checkboxLabel, children: [
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
111
+ "input",
112
+ {
113
+ type: "checkbox",
114
+ checked: active,
115
+ onChange: (event) => setActive(event.target.checked)
116
+ }
117
+ ),
118
+ "Active"
119
+ ] }),
120
+ saveError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.error, role: "alert", children: saveError.explanation }),
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", disabled: isSaving, style: styles.button, children: isSaving ? "Saving\u2026" : "Save" })
122
+ ] }),
123
+ revisions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.revisions, children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.subtitle, children: "History" }),
125
+ /* @__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: [
126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
127
+ "v",
128
+ rev.version,
129
+ " \u2014 ",
130
+ rev.actor ?? "unknown",
131
+ " \u2014",
132
+ " ",
133
+ new Date(rev.createdAt).toLocaleString()
134
+ ] }),
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
136
+ "button",
137
+ {
138
+ type: "button",
139
+ onClick: () => rollback(rev.version),
140
+ style: styles.rollbackButton,
141
+ children: "Roll back"
142
+ }
143
+ )
144
+ ] }, rev.version)) })
145
+ ] })
146
+ ] });
147
+ }
148
+ var styles = {
149
+ container: { padding: 24, maxWidth: 640 },
150
+ title: { fontSize: 24, fontWeight: 700 },
151
+ meta: { fontSize: 13, color: "#666", marginBottom: 16 },
152
+ status: { padding: 24, textAlign: "center", color: "#666" },
153
+ error: { color: "#e11d48", marginBottom: 12, fontSize: 14 },
154
+ form: { display: "flex", flexDirection: "column", gap: 4 },
155
+ label: { fontSize: 13, fontWeight: 600, marginTop: 12 },
156
+ input: { border: "1px solid #ddd", borderRadius: 8, padding: 10, fontSize: 14 },
157
+ textarea: {
158
+ border: "1px solid #ddd",
159
+ borderRadius: 8,
160
+ padding: 10,
161
+ fontSize: 14,
162
+ fontFamily: "monospace"
163
+ },
164
+ checkboxLabel: { display: "flex", alignItems: "center", gap: 8, marginTop: 12, fontSize: 14 },
165
+ button: {
166
+ marginTop: 16,
167
+ backgroundColor: "#000",
168
+ color: "#fff",
169
+ padding: "10px 20px",
170
+ borderRadius: 8,
171
+ border: "none",
172
+ fontSize: 14,
173
+ fontWeight: 600,
174
+ cursor: "pointer",
175
+ alignSelf: "flex-start"
176
+ },
177
+ revisions: { marginTop: 32 },
178
+ subtitle: { fontSize: 16, fontWeight: 600, marginBottom: 8 },
179
+ list: { listStyle: "none", padding: 0, margin: 0 },
180
+ row: {
181
+ display: "flex",
182
+ justifyContent: "space-between",
183
+ alignItems: "center",
184
+ padding: "8px 0",
185
+ borderBottom: "1px solid #eee",
186
+ fontSize: 13
187
+ },
188
+ rollbackButton: {
189
+ background: "none",
190
+ border: "1px solid #ddd",
191
+ borderRadius: 8,
192
+ padding: "4px 10px",
193
+ fontSize: 12,
194
+ cursor: "pointer"
195
+ }
196
+ };
197
+
198
+ // src/screens/TemplateListScreen.tsx
199
+ var import_react_courier_admin2 = require("@fonderie/react-courier-admin");
200
+ var import_jsx_runtime2 = require("react/jsx-runtime");
201
+ function TemplateListScreen({ client, onSelectTemplate }) {
202
+ const { templates, isLoading, error } = (0, import_react_courier_admin2.useTemplates)(client);
203
+ if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.status, children: "Loading templates\u2026" });
204
+ if (error)
205
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: styles2.error, role: "alert", children: error.explanation });
206
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles2.container, children: [
207
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: styles2.title, children: "Templates" }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: styles2.row, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
209
+ "button",
210
+ {
211
+ type: "button",
212
+ onClick: () => onSelectTemplate?.(template),
213
+ style: styles2.rowButton,
214
+ children: [
215
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.type, children: template.type }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles2.locale, children: template.locale ?? "base" }),
217
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: template.active ? styles2.active : styles2.inactive, children: template.active ? "Active" : "Inactive" })
218
+ ]
219
+ }
220
+ ) }, `${template.type}:${template.locale ?? "base"}`)) })
221
+ ] });
222
+ }
223
+ var styles2 = {
224
+ container: { padding: 24, maxWidth: 640 },
225
+ title: { fontSize: 24, fontWeight: 700, marginBottom: 16 },
226
+ status: { padding: 24, textAlign: "center", color: "#666" },
227
+ error: { color: "#e11d48", marginBottom: 12, fontSize: 14 },
228
+ list: { listStyle: "none", padding: 0, margin: 0 },
229
+ row: { borderBottom: "1px solid #eee" },
230
+ rowButton: {
231
+ width: "100%",
232
+ display: "flex",
233
+ justifyContent: "space-between",
234
+ alignItems: "center",
235
+ padding: "12px 0",
236
+ background: "none",
237
+ border: "none",
238
+ cursor: "pointer",
239
+ textAlign: "left"
240
+ },
241
+ type: { fontSize: 14, fontWeight: 600, flex: 1 },
242
+ locale: { fontSize: 13, color: "#666", marginRight: 16 },
243
+ active: { fontSize: 12, color: "#16a34a" },
244
+ inactive: { fontSize: 12, color: "#999" }
245
+ };
246
+ // Annotate the CommonJS export names for ESM import in node:
247
+ 0 && (module.exports = {
248
+ TemplateEditorScreen,
249
+ TemplateListScreen
250
+ });
251
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx"],"sourcesContent":["export type { ITemplateEditorScreenProps, ITemplateListScreenProps } from './screens';\nexport { TemplateEditorScreen, TemplateListScreen } from './screens';\n","import type { CourierAdminClient, ISetTemplateInput } from '@fonderie/client';\nimport { useSaveTemplate, useTemplate, useTemplateRevisions } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n}: ITemplateEditorScreenProps) {\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\tconst { saveTemplate, isLoading: isSaving, error: saveError } = useSaveTemplate(client);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch {\n\t\t\t// Surfaced via `saveError` from useSaveTemplate.\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>Loading template…</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{locale ?? 'base'} · v{template?.version ?? 1}\n\t\t\t</p>\n\n\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\tSubject\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\tHTML body\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\trows={8}\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\tPlain-text body\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\trows={4}\n\t\t\t\t\trequired\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t/>\n\t\t\t\t\tActive\n\t\t\t\t</label>\n\n\t\t\t\t{saveError && (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\n\t\t\t\t<button type=\"submit\" disabled={isSaving} style={styles.button}>\n\t\t\t\t\t{isSaving ? 'Saving…' : 'Save'}\n\t\t\t\t</button>\n\t\t\t</form>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>History</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? 'unknown'} —{' '}\n\t\t\t\t\t\t\t\t\t{new Date(rev.createdAt).toLocaleString()}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tRoll back\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: 24, maxWidth: 640 },\n\ttitle: { fontSize: 24, fontWeight: 700 },\n\tmeta: { fontSize: 13, color: '#666', marginBottom: 16 },\n\tstatus: { padding: 24, textAlign: 'center', color: '#666' },\n\terror: { color: '#e11d48', marginBottom: 12, fontSize: 14 },\n\tform: { display: 'flex', flexDirection: 'column', gap: 4 },\n\tlabel: { fontSize: 13, fontWeight: 600, marginTop: 12 },\n\tinput: { border: '1px solid #ddd', borderRadius: 8, padding: 10, fontSize: 14 },\n\ttextarea: {\n\t\tborder: '1px solid #ddd',\n\t\tborderRadius: 8,\n\t\tpadding: 10,\n\t\tfontSize: 14,\n\t\tfontFamily: 'monospace',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tmarginTop: 16,\n\t\tbackgroundColor: '#000',\n\t\tcolor: '#fff',\n\t\tpadding: '10px 20px',\n\t\tborderRadius: 8,\n\t\tborder: 'none',\n\t\tfontSize: 14,\n\t\tfontWeight: 600,\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: { fontSize: 16, fontWeight: 600, marginBottom: 8 },\n\tlist: { listStyle: 'none', padding: 0, margin: 0 },\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tpadding: '8px 0',\n\t\tborderBottom: '1px solid #eee',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tbackground: 'none',\n\t\tborder: '1px solid #ddd',\n\t\tborderRadius: 8,\n\t\tpadding: '4px 10px',\n\t\tfontSize: 12,\n\t\tcursor: 'pointer',\n\t},\n};\n","import type { CourierAdminClient, ITemplateEntry } from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n}\n\nexport function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps) {\n\tconst { templates, isLoading, error } = useTemplates(client);\n\n\tif (isLoading) return <p style={styles.status}>Loading templates…</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>Templates</h1>\n\t\t\t<ul style={styles.list}>\n\t\t\t\t{templates.map((template) => (\n\t\t\t\t\t<li key={`${template.type}:${template.locale ?? 'base'}`} style={styles.row}>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(template)}\n\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span style={styles.type}>{template.type}</span>\n\t\t\t\t\t\t\t<span style={styles.locale}>{template.locale ?? 'base'}</span>\n\t\t\t\t\t\t\t<span style={template.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t{template.active ? 'Active' : 'Inactive'}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</li>\n\t\t\t\t))}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: 24, maxWidth: 640 },\n\ttitle: { fontSize: 24, fontWeight: 700, marginBottom: 16 },\n\tstatus: { padding: 24, textAlign: 'center', color: '#666' },\n\terror: { color: '#e11d48', marginBottom: 12, fontSize: 14 },\n\tlist: { listStyle: 'none', padding: 0, margin: 0 },\n\trow: { borderBottom: '1px solid #eee' },\n\trowButton: {\n\t\twidth: '100%',\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tpadding: '12px 0',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t},\n\ttype: { fontSize: 14, fontWeight: 600, flex: 1 },\n\tlocale: { fontSize: 13, color: '#666', marginRight: 16 },\n\tactive: { fontSize: 12, color: '#16a34a' },\n\tinactive: { fontSize: 12, color: '#999' },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,iCAAmE;AAEnE,mBAAoC;AA+Cb;AAtChB,SAAS,qBAAqB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA+B;AAC9B,QAAM,EAAE,UAAU,WAAW,OAAO,QAAQ,QAAI,wCAAY,QAAQ,MAAM,MAAM;AAChF,QAAM,EAAE,cAAc,WAAW,UAAU,OAAO,UAAU,QAAI,4CAAgB,MAAM;AACtF,QAAM,EAAE,WAAW,SAAS,QAAI,iDAAqB,QAAQ,MAAM,MAAM;AAEzE,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,EAAE;AACzC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,EAAE;AACnC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,EAAE;AACnC,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,IAAI;AAEzC,8BAAU,MAAM;AACf,QAAI,CAAC,SAAU;AACf,eAAW,SAAS,WAAW,EAAE;AACjC,YAAQ,SAAS,QAAQ,EAAE;AAC3B,YAAQ,SAAS,IAAI;AACrB,cAAU,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,eAAe,OAAO,UAAqB;AAChD,UAAM,eAAe;AACrB,QAAI;AACH,YAAM,QAA2B,EAAE,MAAM,OAAO;AAChD,UAAI,QAAS,OAAM,UAAU;AAC7B,UAAI,KAAM,OAAM,OAAO;AACvB,UAAI,UAAU,YAAY,OAAW,OAAM,YAAY,SAAS;AAChE,YAAM,aAAa,MAAM,OAAO,MAAM;AACtC,YAAM,QAAQ;AACd,gBAAU;AAAA,IACX,QAAQ;AAAA,IAER;AAAA,EACD;AAEA,MAAI,UAAW,QAAO,4CAAC,OAAE,OAAO,OAAO,QAAQ,oCAAiB;AAChE,MAAI;AACH,WACC,4CAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,gBAAM,aACR;AAGF,SACC,6CAAC,SAAI,OAAO,OAAO,WAClB;AAAA,gDAAC,QAAG,OAAO,OAAO,OAAQ,gBAAK;AAAA,IAC/B,6CAAC,OAAE,OAAO,OAAO,MACf;AAAA,gBAAU;AAAA,MAAO;AAAA,MAAK,UAAU,WAAW;AAAA,OAC7C;AAAA,IAEA,6CAAC,UAAK,OAAO,OAAO,MAAM,UAAU,cACnC;AAAA,kDAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,oBAAmB,qBAEvD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,OAAO;AAAA,UACd,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA;AAAA,MACnD;AAAA,MAEA,4CAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAAgB,uBAEpD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,OAAO;AAAA,UACd,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,UAC/C,MAAM;AAAA;AAAA,MACP;AAAA,MAEA,4CAAC,WAAM,OAAO,OAAO,OAAO,SAAQ,iBAAgB,6BAEpD;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,OAAO;AAAA,UACd,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,KAAK;AAAA,UAC/C,MAAM;AAAA,UACN,UAAQ;AAAA;AAAA,MACT;AAAA,MAEA,6CAAC,WAAM,OAAO,OAAO,eACpB;AAAA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,SAAS;AAAA,YACT,UAAU,CAAC,UAAU,UAAU,MAAM,OAAO,OAAO;AAAA;AAAA,QACpD;AAAA,QAAE;AAAA,SAEH;AAAA,MAEC,aACA,4CAAC,OAAE,OAAO,OAAO,OAAO,MAAK,SAC3B,oBAAU,aACZ;AAAA,MAGD,4CAAC,YAAO,MAAK,UAAS,UAAU,UAAU,OAAO,OAAO,QACtD,qBAAW,iBAAY,QACzB;AAAA,OACD;AAAA,IAEC,UAAU,SAAS,KACnB,6CAAC,SAAI,OAAO,OAAO,WAClB;AAAA,kDAAC,QAAG,OAAO,OAAO,UAAU,qBAAO;AAAA,MACnC,4CAAC,QAAG,OAAO,OAAO,MAChB,oBAAU,IAAI,CAAC,QACf,6CAAC,QAAqB,OAAO,OAAO,KACnC;AAAA,qDAAC,UAAK;AAAA;AAAA,UACH,IAAI;AAAA,UAAQ;AAAA,UAAI,IAAI,SAAS;AAAA,UAAU;AAAA,UAAG;AAAA,UAC3C,IAAI,KAAK,IAAI,SAAS,EAAE,eAAe;AAAA,WACzC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,SAAS,MAAM,SAAS,IAAI,OAAO;AAAA,YACnC,OAAO,OAAO;AAAA,YACd;AAAA;AAAA,QAED;AAAA,WAXQ,IAAI,OAYb,CACA,GACF;AAAA,OACD;AAAA,KAEF;AAEF;AAEA,IAAM,SAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,IAAI,UAAU,IAAI;AAAA,EACxC,OAAO,EAAE,UAAU,IAAI,YAAY,IAAI;AAAA,EACvC,MAAM,EAAE,UAAU,IAAI,OAAO,QAAQ,cAAc,GAAG;AAAA,EACtD,QAAQ,EAAE,SAAS,IAAI,WAAW,UAAU,OAAO,OAAO;AAAA,EAC1D,OAAO,EAAE,OAAO,WAAW,cAAc,IAAI,UAAU,GAAG;AAAA,EAC1D,MAAM,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE;AAAA,EACzD,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,WAAW,GAAG;AAAA,EACtD,OAAO,EAAE,QAAQ,kBAAkB,cAAc,GAAG,SAAS,IAAI,UAAU,GAAG;AAAA,EAC9E,UAAU;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,EACb;AAAA,EACA,eAAe,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,WAAW,IAAI,UAAU,GAAG;AAAA,EAC5F,QAAQ;AAAA,IACP,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,WAAW;AAAA,EACZ;AAAA,EACA,WAAW,EAAE,WAAW,GAAG;AAAA,EAC3B,UAAU,EAAE,UAAU,IAAI,YAAY,KAAK,cAAc,EAAE;AAAA,EAC3D,MAAM,EAAE,WAAW,QAAQ,SAAS,GAAG,QAAQ,EAAE;AAAA,EACjD,KAAK;AAAA,IACJ,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,EACX;AAAA,EACA,gBAAgB;AAAA,IACf,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,QAAQ;AAAA,EACT;AACD;;;AChMA,IAAAA,8BAA6B;AAWN,IAAAC,sBAAA;AAHhB,SAAS,mBAAmB,EAAE,QAAQ,iBAAiB,GAA6B;AAC1F,QAAM,EAAE,WAAW,WAAW,MAAM,QAAI,0CAAa,MAAM;AAE3D,MAAI,UAAW,QAAO,6CAAC,OAAE,OAAOC,QAAO,QAAQ,qCAAkB;AACjE,MAAI;AACH,WACC,6CAAC,OAAE,OAAOA,QAAO,OAAO,MAAK,SAC3B,gBAAM,aACR;AAGF,SACC,8CAAC,SAAI,OAAOA,QAAO,WAClB;AAAA,iDAAC,QAAG,OAAOA,QAAO,OAAO,uBAAS;AAAA,IAClC,6CAAC,QAAG,OAAOA,QAAO,MAChB,oBAAU,IAAI,CAAC,aACf,6CAAC,QAAyD,OAAOA,QAAO,KACvE;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,MAAM,mBAAmB,QAAQ;AAAA,QAC1C,OAAOA,QAAO;AAAA,QAEd;AAAA,uDAAC,UAAK,OAAOA,QAAO,MAAO,mBAAS,MAAK;AAAA,UACzC,6CAAC,UAAK,OAAOA,QAAO,QAAS,mBAAS,UAAU,QAAO;AAAA,UACvD,6CAAC,UAAK,OAAO,SAAS,SAASA,QAAO,SAASA,QAAO,UACpD,mBAAS,SAAS,WAAW,YAC/B;AAAA;AAAA;AAAA,IACD,KAXQ,GAAG,SAAS,IAAI,IAAI,SAAS,UAAU,MAAM,EAYtD,CACA,GACF;AAAA,KACD;AAEF;AAEA,IAAMA,UAAwC;AAAA,EAC7C,WAAW,EAAE,SAAS,IAAI,UAAU,IAAI;AAAA,EACxC,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,cAAc,GAAG;AAAA,EACzD,QAAQ,EAAE,SAAS,IAAI,WAAW,UAAU,OAAO,OAAO;AAAA,EAC1D,OAAO,EAAE,OAAO,WAAW,cAAc,IAAI,UAAU,GAAG;AAAA,EAC1D,MAAM,EAAE,WAAW,QAAQ,SAAS,GAAG,QAAQ,EAAE;AAAA,EACjD,KAAK,EAAE,cAAc,iBAAiB;AAAA,EACtC,WAAW;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,EACZ;AAAA,EACA,MAAM,EAAE,UAAU,IAAI,YAAY,KAAK,MAAM,EAAE;AAAA,EAC/C,QAAQ,EAAE,UAAU,IAAI,OAAO,QAAQ,aAAa,GAAG;AAAA,EACvD,QAAQ,EAAE,UAAU,IAAI,OAAO,UAAU;AAAA,EACzC,UAAU,EAAE,UAAU,IAAI,OAAO,OAAO;AACzC;","names":["import_react_courier_admin","import_jsx_runtime","styles"]}
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { CourierAdminClient, ITemplateEntry } from '@fonderie/client';
3
+
4
+ interface ITemplateEditorScreenProps {
5
+ client: CourierAdminClient;
6
+ type: string;
7
+ locale?: string | null;
8
+ onSaved?: () => void;
9
+ }
10
+ declare function TemplateEditorScreen({ client, type, locale, onSaved, }: ITemplateEditorScreenProps): react.JSX.Element;
11
+
12
+ interface ITemplateListScreenProps {
13
+ client: CourierAdminClient;
14
+ onSelectTemplate?: (template: ITemplateEntry) => void;
15
+ }
16
+ declare function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps): react.JSX.Element;
17
+
18
+ export { type ITemplateEditorScreenProps, type ITemplateListScreenProps, TemplateEditorScreen, TemplateListScreen };
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { CourierAdminClient, ITemplateEntry } from '@fonderie/client';
3
+
4
+ interface ITemplateEditorScreenProps {
5
+ client: CourierAdminClient;
6
+ type: string;
7
+ locale?: string | null;
8
+ onSaved?: () => void;
9
+ }
10
+ declare function TemplateEditorScreen({ client, type, locale, onSaved, }: ITemplateEditorScreenProps): react.JSX.Element;
11
+
12
+ interface ITemplateListScreenProps {
13
+ client: CourierAdminClient;
14
+ onSelectTemplate?: (template: ITemplateEntry) => void;
15
+ }
16
+ declare function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps): react.JSX.Element;
17
+
18
+ export { type ITemplateEditorScreenProps, type ITemplateListScreenProps, TemplateEditorScreen, TemplateListScreen };
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ // src/screens/TemplateEditorScreen.tsx
2
+ import { useSaveTemplate, useTemplate, useTemplateRevisions } from "@fonderie/react-courier-admin";
3
+ import { useEffect, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function TemplateEditorScreen({
6
+ client,
7
+ type,
8
+ locale,
9
+ onSaved
10
+ }) {
11
+ const { template, isLoading, error, refresh } = useTemplate(client, type, locale);
12
+ const { saveTemplate, isLoading: isSaving, error: saveError } = useSaveTemplate(client);
13
+ const { revisions, rollback } = useTemplateRevisions(client, type, locale);
14
+ const [subject, setSubject] = useState("");
15
+ const [html, setHtml] = useState("");
16
+ const [text, setText] = useState("");
17
+ const [active, setActive] = useState(true);
18
+ useEffect(() => {
19
+ if (!template) return;
20
+ setSubject(template.subject ?? "");
21
+ setHtml(template.html ?? "");
22
+ setText(template.text);
23
+ setActive(template.active);
24
+ }, [template]);
25
+ const handleSubmit = async (event) => {
26
+ event.preventDefault();
27
+ try {
28
+ const input = { text, active };
29
+ if (subject) input.subject = subject;
30
+ if (html) input.html = html;
31
+ if (template?.version !== void 0) input.ifVersion = template.version;
32
+ await saveTemplate(type, input, locale);
33
+ await refresh();
34
+ onSaved?.();
35
+ } catch {
36
+ }
37
+ };
38
+ if (isLoading) return /* @__PURE__ */ jsx("p", { style: styles.status, children: "Loading template\u2026" });
39
+ if (error)
40
+ return /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: error.explanation });
41
+ return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
42
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: type }),
43
+ /* @__PURE__ */ jsxs("p", { style: styles.meta, children: [
44
+ locale ?? "base",
45
+ " \xB7 v",
46
+ template?.version ?? 1
47
+ ] }),
48
+ /* @__PURE__ */ jsxs("form", { style: styles.form, onSubmit: handleSubmit, children: [
49
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-subject", children: "Subject" }),
50
+ /* @__PURE__ */ jsx(
51
+ "input",
52
+ {
53
+ id: "template-subject",
54
+ style: styles.input,
55
+ value: subject,
56
+ onChange: (event) => setSubject(event.target.value)
57
+ }
58
+ ),
59
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-html", children: "HTML body" }),
60
+ /* @__PURE__ */ jsx(
61
+ "textarea",
62
+ {
63
+ id: "template-html",
64
+ style: styles.textarea,
65
+ value: html,
66
+ onChange: (event) => setHtml(event.target.value),
67
+ rows: 8
68
+ }
69
+ ),
70
+ /* @__PURE__ */ jsx("label", { style: styles.label, htmlFor: "template-text", children: "Plain-text body" }),
71
+ /* @__PURE__ */ jsx(
72
+ "textarea",
73
+ {
74
+ id: "template-text",
75
+ style: styles.textarea,
76
+ value: text,
77
+ onChange: (event) => setText(event.target.value),
78
+ rows: 4,
79
+ required: true
80
+ }
81
+ ),
82
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxLabel, children: [
83
+ /* @__PURE__ */ jsx(
84
+ "input",
85
+ {
86
+ type: "checkbox",
87
+ checked: active,
88
+ onChange: (event) => setActive(event.target.checked)
89
+ }
90
+ ),
91
+ "Active"
92
+ ] }),
93
+ saveError && /* @__PURE__ */ jsx("p", { style: styles.error, role: "alert", children: saveError.explanation }),
94
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSaving, style: styles.button, children: isSaving ? "Saving\u2026" : "Save" })
95
+ ] }),
96
+ revisions.length > 0 && /* @__PURE__ */ jsxs("div", { style: styles.revisions, children: [
97
+ /* @__PURE__ */ jsx("h2", { style: styles.subtitle, children: "History" }),
98
+ /* @__PURE__ */ jsx("ul", { style: styles.list, children: revisions.map((rev) => /* @__PURE__ */ jsxs("li", { style: styles.row, children: [
99
+ /* @__PURE__ */ jsxs("span", { children: [
100
+ "v",
101
+ rev.version,
102
+ " \u2014 ",
103
+ rev.actor ?? "unknown",
104
+ " \u2014",
105
+ " ",
106
+ new Date(rev.createdAt).toLocaleString()
107
+ ] }),
108
+ /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ onClick: () => rollback(rev.version),
113
+ style: styles.rollbackButton,
114
+ children: "Roll back"
115
+ }
116
+ )
117
+ ] }, rev.version)) })
118
+ ] })
119
+ ] });
120
+ }
121
+ var styles = {
122
+ container: { padding: 24, maxWidth: 640 },
123
+ title: { fontSize: 24, fontWeight: 700 },
124
+ meta: { fontSize: 13, color: "#666", marginBottom: 16 },
125
+ status: { padding: 24, textAlign: "center", color: "#666" },
126
+ error: { color: "#e11d48", marginBottom: 12, fontSize: 14 },
127
+ form: { display: "flex", flexDirection: "column", gap: 4 },
128
+ label: { fontSize: 13, fontWeight: 600, marginTop: 12 },
129
+ input: { border: "1px solid #ddd", borderRadius: 8, padding: 10, fontSize: 14 },
130
+ textarea: {
131
+ border: "1px solid #ddd",
132
+ borderRadius: 8,
133
+ padding: 10,
134
+ fontSize: 14,
135
+ fontFamily: "monospace"
136
+ },
137
+ checkboxLabel: { display: "flex", alignItems: "center", gap: 8, marginTop: 12, fontSize: 14 },
138
+ button: {
139
+ marginTop: 16,
140
+ backgroundColor: "#000",
141
+ color: "#fff",
142
+ padding: "10px 20px",
143
+ borderRadius: 8,
144
+ border: "none",
145
+ fontSize: 14,
146
+ fontWeight: 600,
147
+ cursor: "pointer",
148
+ alignSelf: "flex-start"
149
+ },
150
+ revisions: { marginTop: 32 },
151
+ subtitle: { fontSize: 16, fontWeight: 600, marginBottom: 8 },
152
+ list: { listStyle: "none", padding: 0, margin: 0 },
153
+ row: {
154
+ display: "flex",
155
+ justifyContent: "space-between",
156
+ alignItems: "center",
157
+ padding: "8px 0",
158
+ borderBottom: "1px solid #eee",
159
+ fontSize: 13
160
+ },
161
+ rollbackButton: {
162
+ background: "none",
163
+ border: "1px solid #ddd",
164
+ borderRadius: 8,
165
+ padding: "4px 10px",
166
+ fontSize: 12,
167
+ cursor: "pointer"
168
+ }
169
+ };
170
+
171
+ // src/screens/TemplateListScreen.tsx
172
+ import { useTemplates } from "@fonderie/react-courier-admin";
173
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
174
+ function TemplateListScreen({ client, onSelectTemplate }) {
175
+ const { templates, isLoading, error } = useTemplates(client);
176
+ if (isLoading) return /* @__PURE__ */ jsx2("p", { style: styles2.status, children: "Loading templates\u2026" });
177
+ if (error)
178
+ return /* @__PURE__ */ jsx2("p", { style: styles2.error, role: "alert", children: error.explanation });
179
+ return /* @__PURE__ */ jsxs2("div", { style: styles2.container, children: [
180
+ /* @__PURE__ */ jsx2("h1", { style: styles2.title, children: "Templates" }),
181
+ /* @__PURE__ */ jsx2("ul", { style: styles2.list, children: templates.map((template) => /* @__PURE__ */ jsx2("li", { style: styles2.row, children: /* @__PURE__ */ jsxs2(
182
+ "button",
183
+ {
184
+ type: "button",
185
+ onClick: () => onSelectTemplate?.(template),
186
+ style: styles2.rowButton,
187
+ children: [
188
+ /* @__PURE__ */ jsx2("span", { style: styles2.type, children: template.type }),
189
+ /* @__PURE__ */ jsx2("span", { style: styles2.locale, children: template.locale ?? "base" }),
190
+ /* @__PURE__ */ jsx2("span", { style: template.active ? styles2.active : styles2.inactive, children: template.active ? "Active" : "Inactive" })
191
+ ]
192
+ }
193
+ ) }, `${template.type}:${template.locale ?? "base"}`)) })
194
+ ] });
195
+ }
196
+ var styles2 = {
197
+ container: { padding: 24, maxWidth: 640 },
198
+ title: { fontSize: 24, fontWeight: 700, marginBottom: 16 },
199
+ status: { padding: 24, textAlign: "center", color: "#666" },
200
+ error: { color: "#e11d48", marginBottom: 12, fontSize: 14 },
201
+ list: { listStyle: "none", padding: 0, margin: 0 },
202
+ row: { borderBottom: "1px solid #eee" },
203
+ rowButton: {
204
+ width: "100%",
205
+ display: "flex",
206
+ justifyContent: "space-between",
207
+ alignItems: "center",
208
+ padding: "12px 0",
209
+ background: "none",
210
+ border: "none",
211
+ cursor: "pointer",
212
+ textAlign: "left"
213
+ },
214
+ type: { fontSize: 14, fontWeight: 600, flex: 1 },
215
+ locale: { fontSize: 13, color: "#666", marginRight: 16 },
216
+ active: { fontSize: 12, color: "#16a34a" },
217
+ inactive: { fontSize: 12, color: "#999" }
218
+ };
219
+ export {
220
+ TemplateEditorScreen,
221
+ TemplateListScreen
222
+ };
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/screens/TemplateEditorScreen.tsx","../src/screens/TemplateListScreen.tsx"],"sourcesContent":["import type { CourierAdminClient, ISetTemplateInput } from '@fonderie/client';\nimport { useSaveTemplate, useTemplate, useTemplateRevisions } from '@fonderie/react-courier-admin';\nimport type { CSSProperties, FormEvent } from 'react';\nimport { useEffect, useState } from 'react';\n\nexport interface ITemplateEditorScreenProps {\n\tclient: CourierAdminClient;\n\ttype: string;\n\tlocale?: string | null;\n\tonSaved?: () => void;\n}\n\nexport function TemplateEditorScreen({\n\tclient,\n\ttype,\n\tlocale,\n\tonSaved,\n}: ITemplateEditorScreenProps) {\n\tconst { template, isLoading, error, refresh } = useTemplate(client, type, locale);\n\tconst { saveTemplate, isLoading: isSaving, error: saveError } = useSaveTemplate(client);\n\tconst { revisions, rollback } = useTemplateRevisions(client, type, locale);\n\n\tconst [subject, setSubject] = useState('');\n\tconst [html, setHtml] = useState('');\n\tconst [text, setText] = useState('');\n\tconst [active, setActive] = useState(true);\n\n\tuseEffect(() => {\n\t\tif (!template) return;\n\t\tsetSubject(template.subject ?? '');\n\t\tsetHtml(template.html ?? '');\n\t\tsetText(template.text);\n\t\tsetActive(template.active);\n\t}, [template]);\n\n\tconst handleSubmit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\ttry {\n\t\t\tconst input: ISetTemplateInput = { text, active };\n\t\t\tif (subject) input.subject = subject;\n\t\t\tif (html) input.html = html;\n\t\t\tif (template?.version !== undefined) input.ifVersion = template.version;\n\t\t\tawait saveTemplate(type, input, locale);\n\t\t\tawait refresh();\n\t\t\tonSaved?.();\n\t\t} catch {\n\t\t\t// Surfaced via `saveError` from useSaveTemplate.\n\t\t}\n\t};\n\n\tif (isLoading) return <p style={styles.status}>Loading template…</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>{type}</h1>\n\t\t\t<p style={styles.meta}>\n\t\t\t\t{locale ?? 'base'} · v{template?.version ?? 1}\n\t\t\t</p>\n\n\t\t\t<form style={styles.form} onSubmit={handleSubmit}>\n\t\t\t\t<label style={styles.label} htmlFor=\"template-subject\">\n\t\t\t\t\tSubject\n\t\t\t\t</label>\n\t\t\t\t<input\n\t\t\t\t\tid=\"template-subject\"\n\t\t\t\t\tstyle={styles.input}\n\t\t\t\t\tvalue={subject}\n\t\t\t\t\tonChange={(event) => setSubject(event.target.value)}\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.label} htmlFor=\"template-html\">\n\t\t\t\t\tHTML body\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"template-html\"\n\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\tvalue={html}\n\t\t\t\t\tonChange={(event) => setHtml(event.target.value)}\n\t\t\t\t\trows={8}\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.label} htmlFor=\"template-text\">\n\t\t\t\t\tPlain-text body\n\t\t\t\t</label>\n\t\t\t\t<textarea\n\t\t\t\t\tid=\"template-text\"\n\t\t\t\t\tstyle={styles.textarea}\n\t\t\t\t\tvalue={text}\n\t\t\t\t\tonChange={(event) => setText(event.target.value)}\n\t\t\t\t\trows={4}\n\t\t\t\t\trequired\n\t\t\t\t/>\n\n\t\t\t\t<label style={styles.checkboxLabel}>\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\tchecked={active}\n\t\t\t\t\t\tonChange={(event) => setActive(event.target.checked)}\n\t\t\t\t\t/>\n\t\t\t\t\tActive\n\t\t\t\t</label>\n\n\t\t\t\t{saveError && (\n\t\t\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t\t\t{saveError.explanation}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\n\t\t\t\t<button type=\"submit\" disabled={isSaving} style={styles.button}>\n\t\t\t\t\t{isSaving ? 'Saving…' : 'Save'}\n\t\t\t\t</button>\n\t\t\t</form>\n\n\t\t\t{revisions.length > 0 && (\n\t\t\t\t<div style={styles.revisions}>\n\t\t\t\t\t<h2 style={styles.subtitle}>History</h2>\n\t\t\t\t\t<ul style={styles.list}>\n\t\t\t\t\t\t{revisions.map((rev) => (\n\t\t\t\t\t\t\t<li key={rev.version} style={styles.row}>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\tv{rev.version} — {rev.actor ?? 'unknown'} —{' '}\n\t\t\t\t\t\t\t\t\t{new Date(rev.createdAt).toLocaleString()}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => rollback(rev.version)}\n\t\t\t\t\t\t\t\t\tstyle={styles.rollbackButton}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tRoll back\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: 24, maxWidth: 640 },\n\ttitle: { fontSize: 24, fontWeight: 700 },\n\tmeta: { fontSize: 13, color: '#666', marginBottom: 16 },\n\tstatus: { padding: 24, textAlign: 'center', color: '#666' },\n\terror: { color: '#e11d48', marginBottom: 12, fontSize: 14 },\n\tform: { display: 'flex', flexDirection: 'column', gap: 4 },\n\tlabel: { fontSize: 13, fontWeight: 600, marginTop: 12 },\n\tinput: { border: '1px solid #ddd', borderRadius: 8, padding: 10, fontSize: 14 },\n\ttextarea: {\n\t\tborder: '1px solid #ddd',\n\t\tborderRadius: 8,\n\t\tpadding: 10,\n\t\tfontSize: 14,\n\t\tfontFamily: 'monospace',\n\t},\n\tcheckboxLabel: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 14 },\n\tbutton: {\n\t\tmarginTop: 16,\n\t\tbackgroundColor: '#000',\n\t\tcolor: '#fff',\n\t\tpadding: '10px 20px',\n\t\tborderRadius: 8,\n\t\tborder: 'none',\n\t\tfontSize: 14,\n\t\tfontWeight: 600,\n\t\tcursor: 'pointer',\n\t\talignSelf: 'flex-start',\n\t},\n\trevisions: { marginTop: 32 },\n\tsubtitle: { fontSize: 16, fontWeight: 600, marginBottom: 8 },\n\tlist: { listStyle: 'none', padding: 0, margin: 0 },\n\trow: {\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tpadding: '8px 0',\n\t\tborderBottom: '1px solid #eee',\n\t\tfontSize: 13,\n\t},\n\trollbackButton: {\n\t\tbackground: 'none',\n\t\tborder: '1px solid #ddd',\n\t\tborderRadius: 8,\n\t\tpadding: '4px 10px',\n\t\tfontSize: 12,\n\t\tcursor: 'pointer',\n\t},\n};\n","import type { CourierAdminClient, ITemplateEntry } from '@fonderie/client';\nimport { useTemplates } from '@fonderie/react-courier-admin';\nimport type { CSSProperties } from 'react';\n\nexport interface ITemplateListScreenProps {\n\tclient: CourierAdminClient;\n\tonSelectTemplate?: (template: ITemplateEntry) => void;\n}\n\nexport function TemplateListScreen({ client, onSelectTemplate }: ITemplateListScreenProps) {\n\tconst { templates, isLoading, error } = useTemplates(client);\n\n\tif (isLoading) return <p style={styles.status}>Loading templates…</p>;\n\tif (error)\n\t\treturn (\n\t\t\t<p style={styles.error} role=\"alert\">\n\t\t\t\t{error.explanation}\n\t\t\t</p>\n\t\t);\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h1 style={styles.title}>Templates</h1>\n\t\t\t<ul style={styles.list}>\n\t\t\t\t{templates.map((template) => (\n\t\t\t\t\t<li key={`${template.type}:${template.locale ?? 'base'}`} style={styles.row}>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelectTemplate?.(template)}\n\t\t\t\t\t\t\tstyle={styles.rowButton}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span style={styles.type}>{template.type}</span>\n\t\t\t\t\t\t\t<span style={styles.locale}>{template.locale ?? 'base'}</span>\n\t\t\t\t\t\t\t<span style={template.active ? styles.active : styles.inactive}>\n\t\t\t\t\t\t\t\t{template.active ? 'Active' : 'Inactive'}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</li>\n\t\t\t\t))}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n\nconst styles: Record<string, CSSProperties> = {\n\tcontainer: { padding: 24, maxWidth: 640 },\n\ttitle: { fontSize: 24, fontWeight: 700, marginBottom: 16 },\n\tstatus: { padding: 24, textAlign: 'center', color: '#666' },\n\terror: { color: '#e11d48', marginBottom: 12, fontSize: 14 },\n\tlist: { listStyle: 'none', padding: 0, margin: 0 },\n\trow: { borderBottom: '1px solid #eee' },\n\trowButton: {\n\t\twidth: '100%',\n\t\tdisplay: 'flex',\n\t\tjustifyContent: 'space-between',\n\t\talignItems: 'center',\n\t\tpadding: '12px 0',\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\ttextAlign: 'left',\n\t},\n\ttype: { fontSize: 14, fontWeight: 600, flex: 1 },\n\tlocale: { fontSize: 13, color: '#666', marginRight: 16 },\n\tactive: { fontSize: 12, color: '#16a34a' },\n\tinactive: { fontSize: 12, color: '#999' },\n};\n"],"mappings":"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","names":["jsx","jsxs","styles"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@fonderie/react-courier-admin-screens",
3
+ "version": "0.1.0",
4
+ "description": "Pre-built React screens for the Fonderie courier admin API — template list, template editor with revision history and rollback.",
5
+ "keywords": [
6
+ "fonderie-js",
7
+ "react",
8
+ "courier",
9
+ "templates",
10
+ "admin",
11
+ "screens",
12
+ "ui",
13
+ "typescript"
14
+ ],
15
+ "license": "MIT",
16
+ "type": "module",
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.cjs"
25
+ }
26
+ },
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "dev": "tsup --watch",
33
+ "typecheck": "tsc --noEmit",
34
+ "lint": "biome lint src",
35
+ "format": "biome format --write src",
36
+ "check": "biome check --write src"
37
+ },
38
+ "dependencies": {
39
+ "@fonderie/client": "*",
40
+ "@fonderie/react-courier-admin": "*"
41
+ },
42
+ "peerDependencies": {
43
+ "react": ">=18.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^25.6.0",
47
+ "@types/react": "^18.0.0",
48
+ "react": "^18.0.0",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "^6.0.3"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "LICENSE",
58
+ "README.md"
59
+ ],
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/fonderiejs/fonderie.git",
63
+ "directory": "packages/react-courier-admin-screens"
64
+ },
65
+ "homepage": "https://github.com/fonderiejs/fonderie/tree/main/packages/react-courier-admin-screens#readme",
66
+ "bugs": {
67
+ "url": "https://github.com/fonderiejs/fonderie/issues"
68
+ }
69
+ }