@emailmaker/emailmaker 1.1.7 → 1.1.8
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/{emailmaker-core.0f53b24b.js → emailmaker-core.2a78033e.js} +1 -1
- package/emailmaker-esm.js +2 -2
- package/emailmaker.js +2 -2
- package/iframe/js/amp.CdTI2-VC.js +1 -0
- package/iframe/js/iframe-eblock.js +1 -1
- package/iframe/js/iframe.js +1 -1
- package/iframe/js/rules.CNqvmp_P.js +1 -1
- package/iframe/js/sanitize-html.tGUeRBWs.js +1 -0
- package/package.json +1 -1
- package/static/core/{f67b8fa2.js → 136331f7.js} +2 -2
- package/static/core/{5f66a133.js → 1f9fcb39.js} +3 -3
- package/static/core/{b330862b.js → 31f5d274.js} +2 -2
- package/static/core/{542c10a0.js → 461a4018.js} +1 -1
- package/static/core/{37323be2.js → 65824986.js} +10 -10
- package/static/core/{54c69205.js → 6eb88f06.js} +1 -1
- package/static/core/{e5c35767.js → 8147252b.js} +1 -1
- package/static/core/879b6072.js +1 -0
- package/static/core/{425f6198.js → 97e1264b.js} +2 -2
- package/static/core/9ef47c7c.js +1 -0
- package/static/core/{6f1c8ca5.js → a56dd82d.js} +2 -2
- package/static/core/{eb4e7505.js → ae2bef91.js} +1 -1
- package/static/core/{c7f99585.js → bfd98708.js} +1 -1
- package/static/core/{e1ac64f3.js → d3d60ed0.js} +4 -4
- package/static/core/fdb48e3f.js +78 -0
- package/two_step_headless_plugin.html +558 -0
- package/iframe/js/amp.BtGt-xkd.js +0 -1
- package/iframe/js/sanitize-html.BzjMWt1k.js +0 -1
- package/static/core/99f965a4.js +0 -1
- package/static/core/c19a7448.js +0 -78
- package/static/core/de149603.js +0 -1
- /package/iframe/{iframe-eblock.0f53b24b.html → iframe-eblock.2a78033e.html} +0 -0
- /package/iframe/{iframe.0f53b24b.html → iframe.2a78033e.html} +0 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ru">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Two-step flow: design → subject/preheader (headless)</title>
|
|
6
|
+
<script id="plugin-script" type="text/javascript" src="emailmaker.js?2a78033e"></script>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
font-family: system-ui, sans-serif;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
}
|
|
13
|
+
.toolbar {
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: 8px;
|
|
16
|
+
padding: 8px;
|
|
17
|
+
border-bottom: 1px solid #ddd;
|
|
18
|
+
align-items: center;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
}
|
|
21
|
+
.step-btn.active {
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
outline: 2px solid #852876;
|
|
24
|
+
}
|
|
25
|
+
#flow_log {
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 8px;
|
|
28
|
+
background: #f5f5f5;
|
|
29
|
+
border-bottom: 1px solid #ddd;
|
|
30
|
+
max-height: 180px;
|
|
31
|
+
overflow: auto;
|
|
32
|
+
white-space: pre-wrap;
|
|
33
|
+
font-size: 12px;
|
|
34
|
+
}
|
|
35
|
+
.page {
|
|
36
|
+
display: none;
|
|
37
|
+
height: calc(100vh - 260px);
|
|
38
|
+
}
|
|
39
|
+
.page.active {
|
|
40
|
+
display: block;
|
|
41
|
+
}
|
|
42
|
+
#blocks-editor {
|
|
43
|
+
padding: 0;
|
|
44
|
+
background: #fff;
|
|
45
|
+
height: 100%;
|
|
46
|
+
min-height: 400px;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
}
|
|
49
|
+
.subject-page {
|
|
50
|
+
padding: 16px;
|
|
51
|
+
max-width: 640px;
|
|
52
|
+
}
|
|
53
|
+
.subject-page label {
|
|
54
|
+
display: block;
|
|
55
|
+
margin-top: 12px;
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
}
|
|
58
|
+
.subject-page input,
|
|
59
|
+
.subject-page textarea {
|
|
60
|
+
width: 100%;
|
|
61
|
+
margin-top: 4px;
|
|
62
|
+
padding: 8px;
|
|
63
|
+
box-sizing: border-box;
|
|
64
|
+
}
|
|
65
|
+
.letter-state {
|
|
66
|
+
font-size: 12px;
|
|
67
|
+
color: #555;
|
|
68
|
+
margin-top: 8px;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<div class="toolbar">
|
|
74
|
+
<strong>Two-step editor flow</strong>
|
|
75
|
+
<button type="button" id="step_design" class="step-btn active">1. Design (full editor)</button>
|
|
76
|
+
<button type="button" id="step_subject" class="step-btn">2. Subject & preheader (headless)</button>
|
|
77
|
+
<button type="button" id="btn_clear_log">Очистить log</button>
|
|
78
|
+
</div>
|
|
79
|
+
<pre id="flow_log"></pre>
|
|
80
|
+
|
|
81
|
+
<div id="page_design" class="page active">
|
|
82
|
+
<div id="blocks-editor"></div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div id="page_subject" class="page subject-page">
|
|
86
|
+
<p>
|
|
87
|
+
Шаг 2: редактор без <code>element</code> (headless), UI — свои поля хоста.
|
|
88
|
+
Синхронизация: <code>show()</code> → <code>getEmail()</code> →
|
|
89
|
+
<code>updateJsonAndEmail</code> (subject — entity, preheader — json). Persist при каждом переходе шага.
|
|
90
|
+
</p>
|
|
91
|
+
<label for="field_subject">Subject</label>
|
|
92
|
+
<input id="field_subject" type="text" autocomplete="off" />
|
|
93
|
+
|
|
94
|
+
<label for="field_preheader">Preheader</label>
|
|
95
|
+
<textarea id="field_preheader" rows="3"></textarea>
|
|
96
|
+
|
|
97
|
+
<p class="letter-state" id="headless_status">Headless: не инициализирован</p>
|
|
98
|
+
<button type="button" id="btn_subject_next">Next (завершить шаг 2)</button>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<script async defer>
|
|
102
|
+
/**
|
|
103
|
+
* Двухшаговый флоу встраивания редактора:
|
|
104
|
+
* - Шаг 1 (design): editor.init({ element }) — полный редактор, save через updateHtmlAndEmail
|
|
105
|
+
* - Шаг 2 (headless): editor.init({ content }) без element — UI полей хоста
|
|
106
|
+
* - subject — поле сущности (letterStore.subject), preheader — в json
|
|
107
|
+
* - Шаг 2 sync: show() → getEmail() → updateJsonAndEmail
|
|
108
|
+
* - Между шагами: persist текущего шага → destroy() → новый init
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
/** Как plugin.html: id письма и шаблон с API (без inline code при старте). */
|
|
112
|
+
const DEMO_LETTER_ID = 'test_test_test_test';
|
|
113
|
+
const INITIAL_TEMPLATE_ID = 321739;
|
|
114
|
+
/** Debounce 400ms для subject/preheader на шаге 2. */
|
|
115
|
+
const FIELD_DEBOUNCE_MS = 400;
|
|
116
|
+
|
|
117
|
+
const COMMON_STYLES =
|
|
118
|
+
'{"block":{"darkBgColor":"#1c52dc"},"text":{"fontFamily":["Safe Fonts","-apple-system, \'Segoe UI\', \'Helvetica Neue\', Helvetica, Roboto, Arial, sans-serif"],"fontSize":16,"fontSizeDim":"px","lineHeight":21,"lineHeightDim":"px","color":"#5a5a5a","textAlign":"left","paddingBottom":10,"paddingBottomDim":"px"},"headings":{"fontFamily":["Safe Fonts","-apple-system, \'Segoe UI\', \'Helvetica Neue\', Helvetica, Roboto, Arial, sans-serif"],"fontSize":24,"fontSizeDim":"px","lineHeight":32,"lineHeightDim":"px","color":"#333333","textAlign":"left","paddingTop":20,"paddingTopDim":"px","paddingBottom":10,"paddingBottomDim":"px","letterSpacing":0,"letterSpacingDim":"px","bold":true,"italic":false},"links":{"color":"#1C52DC","underline":true},"buttons":{"fontFamily":["Safe Fonts","-apple-system, \'Segoe UI\', \'Helvetica Neue\', Helvetica, Roboto, Arial, sans-serif"],"fontSize":16,"fontSizeDim":"px","letterSpacing":0,"letterSpacingDim":"px","color":"#FFFFFF","bold":false,"italic":false,"backgroundColor":"#1C52DC","marginTop":10,"marginTopDim":"px","marginBottom":10,"marginBottomDim":"px","border":{"all":null,"top":null,"right":null,"bottom":null,"left":null},"borderRadius":{"all":5,"topLeft":null,"topRight":null,"bottomRight":null,"bottomLeft":null},"textAlign":"center","padding":{"all":null,"top":null,"right":null,"bottom":null,"left":null}},"block":{"backgroundColor":""}}';
|
|
119
|
+
|
|
120
|
+
/** Начальный email — как в plugin.html (тот же templateId + поля). */
|
|
121
|
+
const createInitialEmail = () => ({
|
|
122
|
+
id: DEMO_LETTER_ID,
|
|
123
|
+
author: 123,
|
|
124
|
+
isNew: false,
|
|
125
|
+
subject: 'topic',
|
|
126
|
+
preheader: '',
|
|
127
|
+
description: '',
|
|
128
|
+
project: 'undefined',
|
|
129
|
+
parent_email: '309707',
|
|
130
|
+
common_styles: COMMON_STYLES,
|
|
131
|
+
title: '',
|
|
132
|
+
date: '2024-01-12T08:42:09',
|
|
133
|
+
modified: '2024-02-16T17:21:01',
|
|
134
|
+
group: [],
|
|
135
|
+
lock: false,
|
|
136
|
+
utm_parameters: {
|
|
137
|
+
utm_content: 'ssssssss',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const buildDesignContent = () => {
|
|
142
|
+
const email = parseLetterJson();
|
|
143
|
+
// subject — entity field, preheader — из json.
|
|
144
|
+
if (letterStore.subject) {
|
|
145
|
+
email.subject = letterStore.subject;
|
|
146
|
+
}
|
|
147
|
+
delete email.code;
|
|
148
|
+
|
|
149
|
+
if (letterStore.html) {
|
|
150
|
+
return {
|
|
151
|
+
email: {
|
|
152
|
+
...email,
|
|
153
|
+
id: DEMO_LETTER_ID,
|
|
154
|
+
code: letterStore.html,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
templateId: INITIAL_TEMPLATE_ID,
|
|
161
|
+
email: {
|
|
162
|
+
...email,
|
|
163
|
+
id: DEMO_LETTER_ID,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const buildBaseConfig = () => ({
|
|
169
|
+
locale: 'en_US',
|
|
170
|
+
mode: 'pro',
|
|
171
|
+
theme: 'light',
|
|
172
|
+
previewIconEnabled: true,
|
|
173
|
+
livePreviewEnabled: true,
|
|
174
|
+
nextButtonEnabled: true,
|
|
175
|
+
mergeTags: {
|
|
176
|
+
syntax: ['{{%value%}}'],
|
|
177
|
+
items: [
|
|
178
|
+
{ label: 'System', value: 'System', children: systemfields },
|
|
179
|
+
{ label: 'User', value: 'User', children: systemfields },
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const systemfields = [
|
|
185
|
+
{ label: 'Имя получателя', value: 'ToName' },
|
|
186
|
+
{ label: 'Доп. поле «Имя»', value: 'Name' },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const getAuthToken = async () => {
|
|
190
|
+
const formdata = new FormData();
|
|
191
|
+
formdata.append('grant_type', 'client_credentials');
|
|
192
|
+
formdata.append('client_id', '****');
|
|
193
|
+
formdata.append('client_secret', '****');
|
|
194
|
+
return fetch('/api?oauth=token', { method: 'POST', body: formdata })
|
|
195
|
+
.then((r) => r.json())
|
|
196
|
+
.then((r) => r.access_token)
|
|
197
|
+
.catch(() => '');
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** In-memory store: subject + html + json (preheader в json). */
|
|
201
|
+
const letterStore = {
|
|
202
|
+
id: DEMO_LETTER_ID,
|
|
203
|
+
subject: 'topic',
|
|
204
|
+
html: '',
|
|
205
|
+
json: JSON.stringify(createInitialEmail()),
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
let editorInstance = null;
|
|
209
|
+
let currentStep = 'design';
|
|
210
|
+
let isEmailInitialized = false;
|
|
211
|
+
let isNotSaved = false;
|
|
212
|
+
let preheaderDebounceTimer = null;
|
|
213
|
+
let subjectDebounceTimer = null;
|
|
214
|
+
|
|
215
|
+
const flowLog = document.getElementById('flow_log');
|
|
216
|
+
const headlessStatus = document.getElementById('headless_status');
|
|
217
|
+
|
|
218
|
+
const appendLog = (msg) => {
|
|
219
|
+
const line = `[${new Date().toLocaleTimeString()}] ${msg}`;
|
|
220
|
+
console.log(line);
|
|
221
|
+
flowLog.textContent = `${flowLog.textContent}${flowLog.textContent ? '\n' : ''}${line}`;
|
|
222
|
+
flowLog.scrollTop = flowLog.scrollHeight;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const parseLetterJson = () => {
|
|
226
|
+
try {
|
|
227
|
+
return JSON.parse(letterStore.json || '{}');
|
|
228
|
+
} catch {
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const getHeadlessFieldValues = () => ({
|
|
234
|
+
subject: document.getElementById('field_subject').value,
|
|
235
|
+
preheader: document.getElementById('field_preheader').value,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/** Заполнить поля шага 2: subject из entity, preheader из json. */
|
|
239
|
+
const syncHeadlessFieldsFromStore = () => {
|
|
240
|
+
const email = parseLetterJson();
|
|
241
|
+
document.getElementById('field_subject').value = letterStore.subject ?? email.subject ?? '';
|
|
242
|
+
document.getElementById('field_preheader').value = email.preheader ?? '';
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const clearFieldDebounce = () => {
|
|
246
|
+
if (preheaderDebounceTimer) {
|
|
247
|
+
clearTimeout(preheaderDebounceTimer);
|
|
248
|
+
preheaderDebounceTimer = null;
|
|
249
|
+
}
|
|
250
|
+
if (subjectDebounceTimer) {
|
|
251
|
+
clearTimeout(subjectDebounceTimer);
|
|
252
|
+
subjectDebounceTimer = null;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Merge getEmail() в json + compile html.
|
|
258
|
+
* letterPatch — доп. поля entity (subject и т.д.).
|
|
259
|
+
*/
|
|
260
|
+
const updateJsonAndEmail = async (data, letterPatch = {}) => {
|
|
261
|
+
const email = parseLetterJson();
|
|
262
|
+
const safeSubject = (data.subject || letterStore.subject || '').trim() || letterStore.subject;
|
|
263
|
+
const safeData = { ...data, subject: safeSubject, id: DEMO_LETTER_ID };
|
|
264
|
+
letterStore.subject = safeSubject;
|
|
265
|
+
letterStore.json = JSON.stringify({ ...email, ...safeData, ...letterPatch });
|
|
266
|
+
if (editorInstance) {
|
|
267
|
+
const compiled = await editorInstance.compileEmail();
|
|
268
|
+
if (compiled?.compiled?.html) {
|
|
269
|
+
letterStore.html = compiled.compiled.html;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
isNotSaved = false;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/** Шаг 1: save после правок в design-редакторе. */
|
|
276
|
+
const updateHtmlAndEmail = async (data, instance = editorInstance) => {
|
|
277
|
+
if (!instance) {
|
|
278
|
+
throw new Error('editor instance missing');
|
|
279
|
+
}
|
|
280
|
+
await updateJsonAndEmail(data);
|
|
281
|
+
appendLog(
|
|
282
|
+
`updateHtmlAndEmail: html=${letterStore.html?.length ?? 0}b subject="${letterStore.subject}" preheader="${parseLetterJson().preheader ?? ''}"`,
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Шаг 2: show() → getEmail() → updateJsonAndEmail.
|
|
288
|
+
* Всегда передаём subject и preheader вместе.
|
|
289
|
+
*/
|
|
290
|
+
const updateLetterAndJson = async ({ preheader, newData = {} } = {}) => {
|
|
291
|
+
if (!editorInstance) {
|
|
292
|
+
appendLog('updateLetterAndJson: skip — no editor instance');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const email = parseLetterJson();
|
|
296
|
+
const nextPreheader = preheader !== undefined ? preheader : email.preheader;
|
|
297
|
+
const nextSubject = newData.subject ?? letterStore.subject ?? email.subject;
|
|
298
|
+
|
|
299
|
+
appendLog(
|
|
300
|
+
`updateLetterAndJson: show() subject="${nextSubject}" preheader="${nextPreheader ?? ''}"`,
|
|
301
|
+
);
|
|
302
|
+
await editorInstance.show({
|
|
303
|
+
...email,
|
|
304
|
+
id: DEMO_LETTER_ID,
|
|
305
|
+
subject: nextSubject,
|
|
306
|
+
preheader: nextPreheader,
|
|
307
|
+
code: letterStore.html,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const data = await editorInstance.getEmail();
|
|
311
|
+
await updateJsonAndEmail({ ...data, id: DEMO_LETTER_ID }, newData);
|
|
312
|
+
appendLog(
|
|
313
|
+
`updateLetterAndJson: persisted subject="${letterStore.subject}" preheader="${parseLetterJson().preheader ?? ''}" html=${letterStore.html?.length ?? 0}b`,
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const destroyEditor = async () => {
|
|
318
|
+
if (editorInstance) {
|
|
319
|
+
appendLog('destroyEditor: instance.destroy()');
|
|
320
|
+
await editorInstance.destroy();
|
|
321
|
+
editorInstance = null;
|
|
322
|
+
}
|
|
323
|
+
isEmailInitialized = false;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/** Новый DOM-узел — иначе shadow root / ELEMENT_MAP ломают повторный init на том же #blocks-editor. */
|
|
327
|
+
const replaceDesignEditorMount = () => {
|
|
328
|
+
const page = document.getElementById('page_design');
|
|
329
|
+
const old = document.getElementById('blocks-editor');
|
|
330
|
+
if (!page || !old) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const fresh = document.createElement('div');
|
|
334
|
+
fresh.id = 'blocks-editor';
|
|
335
|
+
page.replaceChild(fresh, old);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const persistDesignBeforeLeave = async () => {
|
|
339
|
+
if (!editorInstance) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const data = await editorInstance.getEmail();
|
|
344
|
+
await updateHtmlAndEmail(data);
|
|
345
|
+
appendLog(
|
|
346
|
+
`persistDesignBeforeLeave: subject="${letterStore.subject}" preheader="${parseLetterJson().preheader ?? ''}"`,
|
|
347
|
+
);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
const message = error && error.message ? error.message : String(error);
|
|
350
|
+
appendLog(`persistDesignBeforeLeave failed: ${message}`);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/** Перед уходом с шага 2: flush debounce + updateLetterAndJson с полями формы. */
|
|
355
|
+
const persistHeadlessBeforeLeave = async () => {
|
|
356
|
+
clearFieldDebounce();
|
|
357
|
+
if (!editorInstance) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const { subject, preheader } = getHeadlessFieldValues();
|
|
362
|
+
await updateLetterAndJson({ preheader, newData: { subject } });
|
|
363
|
+
appendLog(
|
|
364
|
+
`persistHeadlessBeforeLeave: subject="${letterStore.subject}" preheader="${parseLetterJson().preheader ?? ''}"`,
|
|
365
|
+
);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
const message = error && error.message ? error.message : String(error);
|
|
368
|
+
appendLog(`persistHeadlessBeforeLeave failed: ${message}`);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const initDesignEditor = async () => {
|
|
373
|
+
await destroyEditor();
|
|
374
|
+
replaceDesignEditorMount();
|
|
375
|
+
appendLog('initDesignEditor: editor.init({ element: #blocks-editor })');
|
|
376
|
+
|
|
377
|
+
editorInstance = await emailmaker.init({
|
|
378
|
+
getAuthToken,
|
|
379
|
+
element: '#blocks-editor',
|
|
380
|
+
project: undefined,
|
|
381
|
+
previewIconEnabled: true,
|
|
382
|
+
livePreviewEnabled: true,
|
|
383
|
+
nextButtonEnabled: true,
|
|
384
|
+
content: buildDesignContent(),
|
|
385
|
+
config: buildBaseConfig(),
|
|
386
|
+
handleReadEmailRevisions: () => [],
|
|
387
|
+
handleEmailInit: async (data) => {
|
|
388
|
+
appendLog(`ON_EMAIL_INIT (design) preheader=${data?.preheader ?? ''} html=${data?.html?.length ?? 0}b`);
|
|
389
|
+
isEmailInitialized = true;
|
|
390
|
+
},
|
|
391
|
+
handleEmailChanged: () => {
|
|
392
|
+
isNotSaved = true;
|
|
393
|
+
},
|
|
394
|
+
handleSaveButtonClick: async () => {
|
|
395
|
+
appendLog('handleSaveButtonClick: analytics hook');
|
|
396
|
+
},
|
|
397
|
+
handleNextButtonClick: async (data) => {
|
|
398
|
+
appendLog('handleNextButtonClick: save design → go to step 2');
|
|
399
|
+
await updateHtmlAndEmail(data);
|
|
400
|
+
await goToStep('subject');
|
|
401
|
+
},
|
|
402
|
+
handleEmailAutosave: async ({ value }) => {
|
|
403
|
+
if (!isEmailInitialized) {
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
const compiled = await editorInstance?.compileEmail();
|
|
407
|
+
if (!compiled?.compiled?.html) {
|
|
408
|
+
return value;
|
|
409
|
+
}
|
|
410
|
+
appendLog('handleEmailAutosave (design)');
|
|
411
|
+
await updateHtmlAndEmail(value);
|
|
412
|
+
return value;
|
|
413
|
+
},
|
|
414
|
+
handleSaveEmail: async ({ value }) => {
|
|
415
|
+
appendLog('handleSaveEmail (design)');
|
|
416
|
+
await updateHtmlAndEmail(value);
|
|
417
|
+
return value;
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
try {
|
|
422
|
+
const compiled = await editorInstance.compileEmail();
|
|
423
|
+
appendLog(
|
|
424
|
+
`initDesignEditor done: compile html=${compiled?.compiled?.html?.length ?? 0}b`,
|
|
425
|
+
);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
const message = error && error.message ? error.message : String(error);
|
|
428
|
+
appendLog(`initDesignEditor compile check failed: ${message}`);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const initHeadlessEditor = async () => {
|
|
433
|
+
await destroyEditor();
|
|
434
|
+
appendLog('initHeadlessEditor: editor.init({ NO element }) — headless');
|
|
435
|
+
|
|
436
|
+
// Headless init: subject из store, preheader из json, code из html.
|
|
437
|
+
const email = parseLetterJson();
|
|
438
|
+
const subject = letterStore.subject;
|
|
439
|
+
const preheader = email.preheader ?? '';
|
|
440
|
+
const code = letterStore.html;
|
|
441
|
+
|
|
442
|
+
editorInstance = await emailmaker.init({
|
|
443
|
+
getAuthToken,
|
|
444
|
+
config: { locale: 'en_US' },
|
|
445
|
+
content: {
|
|
446
|
+
email: {
|
|
447
|
+
...email,
|
|
448
|
+
id: DEMO_LETTER_ID,
|
|
449
|
+
subject,
|
|
450
|
+
code,
|
|
451
|
+
preheader,
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
handleReadEmailRevisions: () => [],
|
|
455
|
+
handleEmailInit: async () => {
|
|
456
|
+
appendLog('handleEmailInit (headless): emailInitialized');
|
|
457
|
+
isEmailInitialized = true;
|
|
458
|
+
headlessStatus.textContent = 'Headless: initialized';
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
syncHeadlessFieldsFromStore();
|
|
463
|
+
appendLog(
|
|
464
|
+
`initHeadlessEditor: fields subject="${letterStore.subject}" preheader="${parseLetterJson().preheader ?? ''}"`,
|
|
465
|
+
);
|
|
466
|
+
headlessStatus.textContent = isEmailInitialized ? 'Headless: initialized' : 'Headless: waiting init…';
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const goToStep = async (step) => {
|
|
470
|
+
if (step === currentStep) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
// Persist текущего шага перед destroy (оба направления).
|
|
474
|
+
if (currentStep === 'design') {
|
|
475
|
+
await persistDesignBeforeLeave();
|
|
476
|
+
} else if (currentStep === 'subject') {
|
|
477
|
+
await persistHeadlessBeforeLeave();
|
|
478
|
+
}
|
|
479
|
+
currentStep = step;
|
|
480
|
+
document.getElementById('page_design').classList.toggle('active', step === 'design');
|
|
481
|
+
document.getElementById('page_subject').classList.toggle('active', step === 'subject');
|
|
482
|
+
document.getElementById('step_design').classList.toggle('active', step === 'design');
|
|
483
|
+
document.getElementById('step_subject').classList.toggle('active', step === 'subject');
|
|
484
|
+
|
|
485
|
+
if (step === 'design') {
|
|
486
|
+
await initDesignEditor();
|
|
487
|
+
} else {
|
|
488
|
+
await initHeadlessEditor();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
/** Preheader input → debounce → { preheader, newData: { subject } }. */
|
|
493
|
+
const schedulePreheaderSync = () => {
|
|
494
|
+
if (preheaderDebounceTimer) {
|
|
495
|
+
clearTimeout(preheaderDebounceTimer);
|
|
496
|
+
}
|
|
497
|
+
preheaderDebounceTimer = setTimeout(() => {
|
|
498
|
+
const { subject, preheader } = getHeadlessFieldValues();
|
|
499
|
+
appendLog(`debounce ${FIELD_DEBOUNCE_MS}ms → updateLetterAndJson (preheader+subject)`);
|
|
500
|
+
void updateLetterAndJson({ preheader, newData: { subject } });
|
|
501
|
+
}, FIELD_DEBOUNCE_MS);
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
/** Subject input → debounce → updateLetterAndJson с актуальным preheader. */
|
|
505
|
+
const scheduleSubjectSync = () => {
|
|
506
|
+
if (subjectDebounceTimer) {
|
|
507
|
+
clearTimeout(subjectDebounceTimer);
|
|
508
|
+
}
|
|
509
|
+
subjectDebounceTimer = setTimeout(() => {
|
|
510
|
+
const { subject, preheader } = getHeadlessFieldValues();
|
|
511
|
+
appendLog(`debounce ${FIELD_DEBOUNCE_MS}ms → updateLetterAndJson (subject+preheader)`);
|
|
512
|
+
void updateLetterAndJson({ preheader, newData: { subject } });
|
|
513
|
+
}, FIELD_DEBOUNCE_MS);
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const onSubjectFieldBlur = () => {
|
|
517
|
+
clearFieldDebounce();
|
|
518
|
+
const { subject, preheader } = getHeadlessFieldValues();
|
|
519
|
+
appendLog('subject blur → updateLetterAndJson (subject+preheader)');
|
|
520
|
+
void updateLetterAndJson({ preheader, newData: { subject } });
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
document.getElementById('step_design').addEventListener('click', () => {
|
|
524
|
+
void goToStep('design');
|
|
525
|
+
});
|
|
526
|
+
document.getElementById('step_subject').addEventListener('click', () => {
|
|
527
|
+
void goToStep('subject');
|
|
528
|
+
});
|
|
529
|
+
document.getElementById('btn_clear_log').addEventListener('click', () => {
|
|
530
|
+
flowLog.textContent = '';
|
|
531
|
+
});
|
|
532
|
+
document.getElementById('field_preheader').addEventListener('input', schedulePreheaderSync);
|
|
533
|
+
document.getElementById('field_subject').addEventListener('input', scheduleSubjectSync);
|
|
534
|
+
document.getElementById('field_subject').addEventListener('blur', onSubjectFieldBlur);
|
|
535
|
+
document.getElementById('btn_subject_next').addEventListener('click', async () => {
|
|
536
|
+
clearFieldDebounce();
|
|
537
|
+
const { subject, preheader } = getHeadlessFieldValues();
|
|
538
|
+
appendLog('step 2 Next: updateLetterAndJson + finish');
|
|
539
|
+
await updateLetterAndJson({ preheader, newData: { subject } });
|
|
540
|
+
appendLog(
|
|
541
|
+
`DONE letter: subject="${letterStore.subject}", preheader="${parseLetterJson().preheader ?? ''}", html=${letterStore.html?.length}b`,
|
|
542
|
+
);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
(async () => {
|
|
546
|
+
try {
|
|
547
|
+
appendLog('=== two-step flow demo START (step 1: design) ===');
|
|
548
|
+
appendLog(`letter id=${DEMO_LETTER_ID}, templateId=${INITIAL_TEMPLATE_ID} (no inline code)`);
|
|
549
|
+
await initDesignEditor();
|
|
550
|
+
} catch (error) {
|
|
551
|
+
const message = error && error.message ? error.message : String(error);
|
|
552
|
+
appendLog(`FATAL: ${message}`);
|
|
553
|
+
console.error(error);
|
|
554
|
+
}
|
|
555
|
+
})();
|
|
556
|
+
</script>
|
|
557
|
+
</body>
|
|
558
|
+
</html>
|