@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 +21 -0
- package/README.md +66 -0
- package/dist/index.cjs +251 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +223 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
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":"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","names":["import_react_courier_admin","import_jsx_runtime","styles"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|