@singleton-sd/post-kit-editor 0.4.0 → 0.5.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/README.md +52 -14
- package/dist/email-template-editor.d.ts +5 -3
- package/dist/email-template-editor.js +100 -2
- package/dist/email-template-editor.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/save-send/SaveSendBar.d.ts +10 -0
- package/dist/save-send/SaveSendBar.js +38 -0
- package/dist/save-send/SaveSendBar.js.map +1 -0
- package/dist/save-send/dirty.d.ts +3 -0
- package/dist/save-send/dirty.js +27 -0
- package/dist/save-send/dirty.js.map +1 -0
- package/dist/save-send/recipient.d.ts +1 -0
- package/dist/save-send/recipient.js +15 -0
- package/dist/save-send/recipient.js.map +1 -0
- package/dist/save-send/types.d.ts +21 -0
- package/dist/save-send/types.js +27 -0
- package/dist/save-send/types.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
React admin editor component for [PostKit](../../README.md) email templates. It
|
|
4
4
|
edits the three Git-backed source files (`template.json`, `metadata.json`,
|
|
5
|
-
`preview.json`) **in memory
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
unmounts unless the host implements those callbacks (save/send-test UI lands in
|
|
9
|
-
a follow-up issue).
|
|
5
|
+
`preview.json`) **in memory**, with Save and optional Send-test controls that
|
|
6
|
+
call **consumer-supplied** callbacks. The package never writes to disk, Git, or
|
|
7
|
+
the network, and never accepts API keys or other credentials as props.
|
|
10
8
|
|
|
11
9
|
## Installation
|
|
12
10
|
|
|
@@ -23,18 +21,49 @@ pnpm add react@^18.3.1 react-dom@^18.3.1
|
|
|
23
21
|
## Usage
|
|
24
22
|
|
|
25
23
|
```tsx
|
|
26
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
EmailTemplateEditor,
|
|
26
|
+
type TemplateSourceFiles,
|
|
27
|
+
type SerializedTemplateSource,
|
|
28
|
+
} from '@singleton-sd/post-kit-editor';
|
|
27
29
|
|
|
28
30
|
export function TemplateAdminPage({ template }: { template: TemplateSourceFiles }) {
|
|
29
31
|
return (
|
|
30
32
|
<EmailTemplateEditor
|
|
31
33
|
template={template}
|
|
32
34
|
availableVariables={[{ name: 'name', description: 'Recipient display name' }]}
|
|
33
|
-
onSave={async (files) => {
|
|
34
|
-
|
|
35
|
+
onSave={async (serialized, files) => {
|
|
36
|
+
// Commit serialized.templateJson / metadataJson / previewJson to the
|
|
37
|
+
// consumer repository (e.g. via the app's own server endpoint).
|
|
38
|
+
const res = await fetch('/api/templates', {
|
|
39
|
+
method: 'PUT',
|
|
40
|
+
body: JSON.stringify({ serialized, key: files.metadata.key }),
|
|
41
|
+
});
|
|
42
|
+
// fetch() resolves for HTTP 4xx/5xx — return failure so the editor
|
|
43
|
+
// keeps dirty state and does not treat the rejection as success.
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
return { ok: false, message: 'Save failed.' };
|
|
46
|
+
}
|
|
47
|
+
}}
|
|
48
|
+
onSendTest={async (serialized, _files, recipient) => {
|
|
49
|
+
// Browser → your trusted server only. The server uses
|
|
50
|
+
// @singleton-sd/post-kit-client with POSTKIT_API_KEY from Azure Key
|
|
51
|
+
// Vault `ssd-global-kv-prod-ae` (production). Local `.env` is for
|
|
52
|
+
// development only — never embed a long-lived PostKit API key in
|
|
53
|
+
// browser code.
|
|
54
|
+
const res = await fetch('/api/templates/send-test', {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({ serialized, recipient }),
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
return { ok: false, message: 'Test send failed.' };
|
|
61
|
+
}
|
|
62
|
+
}}
|
|
63
|
+
onDirtyChange={(dirty) => {
|
|
64
|
+
// Optional: guard in-app navigation while dirty.
|
|
35
65
|
}}
|
|
36
66
|
onPreviewRendered={(html) => {
|
|
37
|
-
// Optional: host can offer “open preview in new tab”
|
|
38
67
|
console.log('preview bytes', html.length);
|
|
39
68
|
}}
|
|
40
69
|
className="tenant-theme"
|
|
@@ -45,10 +74,19 @@ export function TemplateAdminPage({ template }: { template: TemplateSourceFiles
|
|
|
45
74
|
|
|
46
75
|
## Persistence is consumer-supplied
|
|
47
76
|
|
|
48
|
-
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
Save serializes the working state with `serializeTemplateSource()` and passes
|
|
78
|
+
both the `SerializedTemplateSource` strings and the structured
|
|
79
|
+
`TemplateSourceFiles` to `onSave`. The editor shows pending / success / failure
|
|
80
|
+
feedback and re-enables the control in every outcome. Rejected promises become
|
|
81
|
+
failure messages (no unhandled rejections).
|
|
82
|
+
|
|
83
|
+
Send-test chrome appears **only** when `onSendTest` is provided. The editor
|
|
84
|
+
validates a non-empty, plausible recipient address, then invokes the callback
|
|
85
|
+
with the same serialized payload. Test delivery must go through the consumer's
|
|
86
|
+
trusted server (typically `@singleton-sd/post-kit-client` server-side).
|
|
87
|
+
|
|
88
|
+
Optional `onDirtyChange` fires when working state diverges from the seeded
|
|
89
|
+
`template` prop and resets after a successful save.
|
|
52
90
|
|
|
53
91
|
## Preview data (synthetic only)
|
|
54
92
|
|
|
@@ -93,7 +131,7 @@ const html = renderToStaticMarkup(<EmailTemplateEditor template={template} onSav
|
|
|
93
131
|
```
|
|
94
132
|
|
|
95
133
|
Specs live next to the code as `src/**/*.spec.tsx`. Behaviour that needs
|
|
96
|
-
interaction is factored into pure functions (preview rows,
|
|
134
|
+
interaction is factored into pure functions (preview rows, save/send helpers)
|
|
97
135
|
that can be tested without a DOM.
|
|
98
136
|
|
|
99
137
|
## Development
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { type SaveResult, type SendTestResult, type SerializedTemplateSource } from './save-send/types';
|
|
1
2
|
import type { TemplateSourceFiles, TemplateVariable } from './types';
|
|
2
3
|
export declare const EDITOR_CLASS_PREFIX = "pk-editor-";
|
|
3
4
|
export interface EmailTemplateEditorProps {
|
|
4
5
|
template: TemplateSourceFiles;
|
|
5
6
|
availableVariables?: TemplateVariable[];
|
|
6
|
-
onSave: (files: TemplateSourceFiles) => Promise<void> | void;
|
|
7
|
-
onSendTest?: (files: TemplateSourceFiles, recipient: string) => Promise<void> | void;
|
|
7
|
+
onSave: (serialized: SerializedTemplateSource, files: TemplateSourceFiles) => Promise<SaveResult | void> | SaveResult | void;
|
|
8
|
+
onSendTest?: (serialized: SerializedTemplateSource, files: TemplateSourceFiles, recipient: string) => Promise<SendTestResult | void> | SendTestResult | void;
|
|
9
|
+
onDirtyChange?: (dirty: boolean) => void;
|
|
8
10
|
onPreviewRendered?: (html: string) => void;
|
|
9
11
|
className?: string;
|
|
10
12
|
}
|
|
11
|
-
export declare function EmailTemplateEditor({ template, availableVariables, onPreviewRendered, className, }: EmailTemplateEditorProps): JSX.Element;
|
|
13
|
+
export declare function EmailTemplateEditor({ template, availableVariables, onSave, onSendTest, onDirtyChange, onPreviewRendered, className, }: EmailTemplateEditorProps): JSX.Element;
|
|
@@ -9,11 +9,41 @@ const insertion_target_1 = require("./insertion-target");
|
|
|
9
9
|
const MetadataPanel_1 = require("./metadata/MetadataPanel");
|
|
10
10
|
const PreviewDataEditor_1 = require("./preview/PreviewDataEditor");
|
|
11
11
|
const PreviewPane_1 = require("./preview/PreviewPane");
|
|
12
|
+
const dirty_1 = require("./save-send/dirty");
|
|
13
|
+
const SaveSendBar_1 = require("./save-send/SaveSendBar");
|
|
14
|
+
const types_1 = require("./save-send/types");
|
|
12
15
|
const VariableCatalogue_1 = require("./variables/VariableCatalogue");
|
|
13
16
|
const working_files_1 = require("./working-files");
|
|
14
17
|
exports.EDITOR_CLASS_PREFIX = 'pk-editor-';
|
|
15
|
-
function EmailTemplateEditor({ template, availableVariables, onPreviewRendered, className, }) {
|
|
18
|
+
function EmailTemplateEditor({ template, availableVariables, onSave, onSendTest, onDirtyChange, onPreviewRendered, className, }) {
|
|
16
19
|
const [workingFiles, setWorkingFiles] = (0, react_1.useState)(template);
|
|
20
|
+
const [seedFiles, setSeedFiles] = (0, react_1.useState)(template);
|
|
21
|
+
const [saveFeedback, setSaveFeedback] = (0, react_1.useState)({ status: 'idle' });
|
|
22
|
+
const [sendFeedback, setSendFeedback] = (0, react_1.useState)({ status: 'idle' });
|
|
23
|
+
const onDirtyChangeRef = (0, react_1.useRef)(onDirtyChange);
|
|
24
|
+
onDirtyChangeRef.current = onDirtyChange;
|
|
25
|
+
const lastDirtyRef = (0, react_1.useRef)(null);
|
|
26
|
+
const templateGenerationRef = (0, react_1.useRef)(0);
|
|
27
|
+
const seedFilesRef = (0, react_1.useRef)(seedFiles);
|
|
28
|
+
seedFilesRef.current = seedFiles;
|
|
29
|
+
(0, react_1.useEffect)(() => {
|
|
30
|
+
if (!(0, dirty_1.isWorkingDirty)(seedFilesRef.current, template)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
templateGenerationRef.current += 1;
|
|
34
|
+
setSaveFeedback({ status: 'idle' });
|
|
35
|
+
setSendFeedback({ status: 'idle' });
|
|
36
|
+
setWorkingFiles(template);
|
|
37
|
+
setSeedFiles(template);
|
|
38
|
+
}, [template]);
|
|
39
|
+
(0, react_1.useEffect)(() => {
|
|
40
|
+
const dirty = (0, dirty_1.isWorkingDirty)(seedFiles, workingFiles);
|
|
41
|
+
if (lastDirtyRef.current === dirty) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
lastDirtyRef.current = dirty;
|
|
45
|
+
onDirtyChangeRef.current?.(dirty);
|
|
46
|
+
}, [seedFiles, workingFiles]);
|
|
17
47
|
const handleDocumentChange = (0, react_1.useCallback)((document) => {
|
|
18
48
|
setWorkingFiles((current) => (0, working_files_1.withDocument)(current, document));
|
|
19
49
|
}, []);
|
|
@@ -26,7 +56,75 @@ function EmailTemplateEditor({ template, availableVariables, onPreviewRendered,
|
|
|
26
56
|
const handlePreviewDataChange = (0, react_1.useCallback)((previewData) => {
|
|
27
57
|
setWorkingFiles((current) => (0, working_files_1.withPreviewData)(current, previewData));
|
|
28
58
|
}, []);
|
|
59
|
+
const busy = saveFeedback.status === 'pending' || sendFeedback.status === 'pending';
|
|
60
|
+
const handleSave = (0, react_1.useCallback)(() => {
|
|
61
|
+
if (busy) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let payload;
|
|
65
|
+
try {
|
|
66
|
+
payload = (0, types_1.buildSavePayload)(workingFiles);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
setSaveFeedback({
|
|
70
|
+
status: 'failure',
|
|
71
|
+
message: error instanceof Error ? error.message : String(error),
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const generation = templateGenerationRef.current;
|
|
76
|
+
setSaveFeedback({ status: 'pending' });
|
|
77
|
+
void (async () => {
|
|
78
|
+
const result = await (0, types_1.invokeConsumerAction)(() => onSave(payload.serialized, payload.files));
|
|
79
|
+
if (generation !== templateGenerationRef.current) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (result.ok) {
|
|
83
|
+
setSeedFiles(payload.files);
|
|
84
|
+
setSaveFeedback({ status: 'success', message: result.message });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
setSaveFeedback({
|
|
88
|
+
status: 'failure',
|
|
89
|
+
message: result.message ?? 'Save failed.',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
}, [busy, onSave, workingFiles]);
|
|
94
|
+
const handleSendTest = (0, react_1.useCallback)((recipient) => {
|
|
95
|
+
if (busy || !onSendTest) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let payload;
|
|
99
|
+
try {
|
|
100
|
+
payload = (0, types_1.buildSavePayload)(workingFiles);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
setSendFeedback({
|
|
104
|
+
status: 'failure',
|
|
105
|
+
message: error instanceof Error ? error.message : String(error),
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const generation = templateGenerationRef.current;
|
|
110
|
+
setSendFeedback({ status: 'pending' });
|
|
111
|
+
void (async () => {
|
|
112
|
+
const result = await (0, types_1.invokeConsumerAction)(() => onSendTest(payload.serialized, payload.files, recipient));
|
|
113
|
+
if (generation !== templateGenerationRef.current) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (result.ok) {
|
|
117
|
+
setSendFeedback({ status: 'success', message: result.message });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
setSendFeedback({
|
|
121
|
+
status: 'failure',
|
|
122
|
+
message: result.message ?? 'Send test failed.',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
}, [busy, onSendTest, workingFiles]);
|
|
29
127
|
const rootClassName = [`${exports.EDITOR_CLASS_PREFIX}root`, className].filter(Boolean).join(' ');
|
|
30
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: rootClassName, "data-testid": `${exports.EDITOR_CLASS_PREFIX}root`, children: (0, jsx_runtime_1.jsx)(insertion_target_1.InsertionTargetProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: `${exports.EDITOR_CLASS_PREFIX}layout`, "data-testid": `${exports.EDITOR_CLASS_PREFIX}layout`, children: [(0, jsx_runtime_1.jsxs)("aside", { className: `${exports.EDITOR_CLASS_PREFIX}sidebar`, children: [(0, jsx_runtime_1.jsx)(MetadataPanel_1.MetadataPanel, { metadata: workingFiles.metadata, previewData: workingFiles.previewData, onChange: handleMetadataChange }), (0, jsx_runtime_1.jsx)(VariableCatalogue_1.VariableCatalogue, { availableVariables: availableVariables, metadataVariables: workingFiles.metadata.variables, onMetadataVariablesChange: handleMetadataVariablesChange }), (0, jsx_runtime_1.jsx)(PreviewDataEditor_1.PreviewDataEditor, { declaredVariables: workingFiles.metadata.variables, previewData: workingFiles.previewData, onChange: handlePreviewDataChange })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${exports.EDITOR_CLASS_PREFIX}main`, children: [(0, jsx_runtime_1.jsx)(EmailBuilderCanvas_1.EmailBuilderCanvas, { document: workingFiles.templateJson, onChange: handleDocumentChange }), (0, jsx_runtime_1.jsx)(PreviewPane_1.PreviewPane, { files: workingFiles, onPreviewRendered: onPreviewRendered })] })] }) }) }));
|
|
128
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: rootClassName, "data-testid": `${exports.EDITOR_CLASS_PREFIX}root`, children: (0, jsx_runtime_1.jsx)(insertion_target_1.InsertionTargetProvider, { children: (0, jsx_runtime_1.jsxs)("div", { className: `${exports.EDITOR_CLASS_PREFIX}layout`, "data-testid": `${exports.EDITOR_CLASS_PREFIX}layout`, children: [(0, jsx_runtime_1.jsxs)("aside", { className: `${exports.EDITOR_CLASS_PREFIX}sidebar`, children: [(0, jsx_runtime_1.jsx)(SaveSendBar_1.SaveSendBar, { saveFeedback: saveFeedback, sendFeedback: sendFeedback, busy: busy, showSendTest: typeof onSendTest === 'function', onSave: handleSave, onSendTest: handleSendTest }), (0, jsx_runtime_1.jsx)(MetadataPanel_1.MetadataPanel, { metadata: workingFiles.metadata, previewData: workingFiles.previewData, onChange: handleMetadataChange }), (0, jsx_runtime_1.jsx)(VariableCatalogue_1.VariableCatalogue, { availableVariables: availableVariables, metadataVariables: workingFiles.metadata.variables, onMetadataVariablesChange: handleMetadataVariablesChange }), (0, jsx_runtime_1.jsx)(PreviewDataEditor_1.PreviewDataEditor, { declaredVariables: workingFiles.metadata.variables, previewData: workingFiles.previewData, onChange: handlePreviewDataChange })] }), (0, jsx_runtime_1.jsxs)("div", { className: `${exports.EDITOR_CLASS_PREFIX}main`, children: [(0, jsx_runtime_1.jsx)(EmailBuilderCanvas_1.EmailBuilderCanvas, { document: workingFiles.templateJson, onChange: handleDocumentChange }), (0, jsx_runtime_1.jsx)(PreviewPane_1.PreviewPane, { files: workingFiles, onPreviewRendered: onPreviewRendered })] })] }) }) }));
|
|
31
129
|
}
|
|
32
130
|
//# sourceMappingURL=email-template-editor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-template-editor.js","sourceRoot":"","sources":["../src/email-template-editor.tsx"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"email-template-editor.js","sourceRoot":"","sources":["../src/email-template-editor.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { type TemplateSourceFiles, type TemplateVariable, type EmailBuilderDocum
|
|
|
2
2
|
export { EDITOR_CLASS_PREFIX, EmailTemplateEditor, type EmailTemplateEditorProps, } from './email-template-editor';
|
|
3
3
|
export { EmailBuilderCanvas, type EmailBuilderCanvasProps } from './canvas/EmailBuilderCanvas';
|
|
4
4
|
export { loadTemplateSource, serializeTemplateSource, TemplateSourceError } from './serialization';
|
|
5
|
+
export type { SerializedTemplateSource, SaveResult, SendTestResult } from './save-send/types';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ActionFeedback } from './types';
|
|
2
|
+
export interface SaveSendBarProps {
|
|
3
|
+
saveFeedback: ActionFeedback;
|
|
4
|
+
sendFeedback: ActionFeedback;
|
|
5
|
+
busy: boolean;
|
|
6
|
+
showSendTest: boolean;
|
|
7
|
+
onSave: () => void;
|
|
8
|
+
onSendTest: (recipient: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function SaveSendBar({ saveFeedback, sendFeedback, busy, showSendTest, onSave, onSendTest, }: SaveSendBarProps): JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SaveSendBar = SaveSendBar;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const email_template_editor_1 = require("../email-template-editor");
|
|
7
|
+
const recipient_1 = require("./recipient");
|
|
8
|
+
function SaveSendBar({ saveFeedback, sendFeedback, busy, showSendTest, onSave, onSendTest, }) {
|
|
9
|
+
const p = email_template_editor_1.EDITOR_CLASS_PREFIX;
|
|
10
|
+
const recipientId = (0, react_1.useId)();
|
|
11
|
+
const [recipient, setRecipient] = (0, react_1.useState)('');
|
|
12
|
+
const [recipientError, setRecipientError] = (0, react_1.useState)(null);
|
|
13
|
+
const handleSend = (event) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
const error = (0, recipient_1.validateSendTestRecipient)(recipient);
|
|
16
|
+
if (error) {
|
|
17
|
+
setRecipientError(error);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
setRecipientError(null);
|
|
21
|
+
onSendTest(recipient.trim());
|
|
22
|
+
};
|
|
23
|
+
return ((0, jsx_runtime_1.jsxs)("section", { className: `${p}save-send`, "data-testid": `${p}save-send`, children: [(0, jsx_runtime_1.jsx)("h2", { className: `${p}save-send-heading`, children: showSendTest ? 'Save & send test' : 'Save' }), (0, jsx_runtime_1.jsxs)("div", { className: `${p}save-send-actions`, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: `${p}save-button`, "data-testid": `${p}save`, disabled: busy, onClick: onSave, children: saveFeedback.status === 'pending' ? 'Saving…' : 'Save' }), (0, jsx_runtime_1.jsx)(ActionStatusMessage, { feedback: saveFeedback, testId: `${p}save-status`, successLabel: "Saved." })] }), showSendTest ? ((0, jsx_runtime_1.jsxs)("form", { className: `${p}send-test`, "data-testid": `${p}send-test`, onSubmit: handleSend, children: [(0, jsx_runtime_1.jsx)("label", { className: `${p}send-test-label`, htmlFor: recipientId, children: "Send test to" }), (0, jsx_runtime_1.jsx)("input", { id: recipientId, className: `${p}send-test-input`, type: "email", value: recipient, disabled: busy, autoComplete: "off", spellCheck: false, placeholder: "you@example.com", "data-testid": `${p}send-test-recipient`, onChange: (event) => {
|
|
24
|
+
setRecipient(event.target.value);
|
|
25
|
+
setRecipientError(null);
|
|
26
|
+
} }), (0, jsx_runtime_1.jsx)("button", { type: "submit", className: `${p}send-test-button`, "data-testid": `${p}send-test-submit`, disabled: busy, children: sendFeedback.status === 'pending' ? 'Sending…' : 'Send test' }), recipientError ? ((0, jsx_runtime_1.jsx)("p", { className: `${p}send-test-error`, "data-testid": `${p}send-test-invalid`, role: "status", children: recipientError })) : null, (0, jsx_runtime_1.jsx)(ActionStatusMessage, { feedback: sendFeedback, testId: `${p}send-test-status`, successLabel: "Test send completed." })] })) : null] }));
|
|
27
|
+
}
|
|
28
|
+
function ActionStatusMessage({ feedback, testId, successLabel, }) {
|
|
29
|
+
const p = email_template_editor_1.EDITOR_CLASS_PREFIX;
|
|
30
|
+
if (feedback.status === 'idle' || feedback.status === 'pending') {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const text = feedback.status === 'success'
|
|
34
|
+
? (feedback.message ?? successLabel)
|
|
35
|
+
: (feedback.message ?? 'Something went wrong.');
|
|
36
|
+
return ((0, jsx_runtime_1.jsx)("p", { className: `${p}action-status ${p}action-status-${feedback.status}`, "data-testid": testId, role: feedback.status === 'failure' ? 'alert' : 'status', children: text }));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=SaveSendBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SaveSendBar.js","sourceRoot":"","sources":["../../src/save-send/SaveSendBar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stableStringify = stableStringify;
|
|
4
|
+
exports.isWorkingDirty = isWorkingDirty;
|
|
5
|
+
function stableStringify(value) {
|
|
6
|
+
return JSON.stringify(sortKeys(value));
|
|
7
|
+
}
|
|
8
|
+
function sortKeys(value) {
|
|
9
|
+
if (value === null || typeof value !== 'object') {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value.map(sortKeys);
|
|
14
|
+
}
|
|
15
|
+
const record = value;
|
|
16
|
+
const sorted = {};
|
|
17
|
+
for (const key of Object.keys(record).sort()) {
|
|
18
|
+
sorted[key] = sortKeys(record[key]);
|
|
19
|
+
}
|
|
20
|
+
return sorted;
|
|
21
|
+
}
|
|
22
|
+
function isWorkingDirty(seed, working) {
|
|
23
|
+
return (stableStringify(seed.templateJson) !== stableStringify(working.templateJson) ||
|
|
24
|
+
stableStringify(seed.metadata) !== stableStringify(working.metadata) ||
|
|
25
|
+
stableStringify(seed.previewData) !== stableStringify(working.previewData));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=dirty.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dirty.js","sourceRoot":"","sources":["../../src/save-send/dirty.ts"],"names":[],"mappings":";;AAMA,0CAEC;AAqBD,wCAMC;AA7BD,SAAgB,eAAe,CAAC,KAAc;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,SAAgB,cAAc,CAAC,IAAyB,EAAE,OAA4B;IACpF,OAAO,CACL,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,eAAe,CAAC,OAAO,CAAC,YAAY,CAAC;QAC5E,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;QACpE,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC,CAC3E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateSendTestRecipient(recipient: string): string | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateSendTestRecipient = validateSendTestRecipient;
|
|
4
|
+
const BASIC_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5
|
+
function validateSendTestRecipient(recipient) {
|
|
6
|
+
const trimmed = recipient.trim();
|
|
7
|
+
if (!trimmed) {
|
|
8
|
+
return 'Enter a recipient email address.';
|
|
9
|
+
}
|
|
10
|
+
if (!BASIC_EMAIL.test(trimmed)) {
|
|
11
|
+
return 'Enter a valid email address.';
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=recipient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient.js","sourceRoot":"","sources":["../../src/save-send/recipient.ts"],"names":[],"mappings":";;AAMA,8DASC;AAXD,MAAM,WAAW,GAAG,4BAA4B,CAAC;AAEjD,SAAgB,yBAAyB,CAAC,SAAiB;IACzD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,kCAAkC,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO,8BAA8B,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TemplateSourceFiles } from '../types';
|
|
2
|
+
import { serializeTemplateSource } from '../serialization';
|
|
3
|
+
export type SerializedTemplateSource = ReturnType<typeof serializeTemplateSource>;
|
|
4
|
+
export interface SaveResult {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SendTestResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
message?: string;
|
|
11
|
+
}
|
|
12
|
+
export type ActionStatus = 'idle' | 'pending' | 'success' | 'failure';
|
|
13
|
+
export interface ActionFeedback {
|
|
14
|
+
status: ActionStatus;
|
|
15
|
+
message?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function invokeConsumerAction(run: () => Promise<SaveResult | SendTestResult | void> | SaveResult | SendTestResult | void): Promise<SaveResult | SendTestResult>;
|
|
18
|
+
export declare function buildSavePayload(files: TemplateSourceFiles): {
|
|
19
|
+
serialized: SerializedTemplateSource;
|
|
20
|
+
files: TemplateSourceFiles;
|
|
21
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.invokeConsumerAction = invokeConsumerAction;
|
|
4
|
+
exports.buildSavePayload = buildSavePayload;
|
|
5
|
+
const serialization_1 = require("../serialization");
|
|
6
|
+
async function invokeConsumerAction(run) {
|
|
7
|
+
try {
|
|
8
|
+
const result = await run();
|
|
9
|
+
if (result && typeof result === 'object' && 'ok' in result) {
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
return { ok: true };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
message: error instanceof Error ? error.message : String(error),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function buildSavePayload(files) {
|
|
22
|
+
return {
|
|
23
|
+
serialized: (0, serialization_1.serializeTemplateSource)(files),
|
|
24
|
+
files,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/save-send/types.ts"],"names":[],"mappings":";;AA8BA,oDAeC;AAGD,4CAQC;AAvDD,oDAA2D;AA6BpD,KAAK,UAAU,oBAAoB,CACxC,GAA2F;IAE3F,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;QAC3B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YAC3D,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE,CAAC;IACJ,CAAC;AACH,CAAC;AAGD,SAAgB,gBAAgB,CAAC,KAA0B;IAIzD,OAAO;QACL,UAAU,EAAE,IAAA,uCAAuB,EAAC,KAAK,CAAC;QAC1C,KAAK;KACN,CAAC;AACJ,CAAC"}
|