@capillarytech/creatives-library 9.0.72 → 9.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/package.json +1 -1
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +58 -1
- package/v2Components/CapTagListWithInput/index.js +64 -35
- package/v2Components/CapTagListWithInput/index.scss +9 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/EmailPreviewContent.js +8 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +7 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +1 -0
- package/v2Components/CommonTestAndPreview/actions.js +1 -1
- package/v2Components/CommonTestAndPreview/index.js +29 -13
- package/v2Components/CommonTestAndPreview/sagas.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/EmailPreviewContent.test.js +27 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +40 -0
- package/v2Components/CommonTestAndPreview/tests/index.test.js +95 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -0
- package/v2Components/EmailMobilePreview/index.js +8 -1
- package/v2Components/EmailMobilePreview/index.scss +12 -3
- package/v2Components/EmailMobilePreview/tests/index.test.js +31 -0
- package/v2Components/EmailPreviewV2/_emailPreviewV2.scss +37 -5
- package/v2Components/EmailPreviewV2/index.js +18 -6
- package/v2Components/EmailPreviewV2/messages.js +4 -0
- package/v2Components/EmailPreviewV2/tests/index.test.js +43 -0
- package/v2Components/FormBuilder/Classic.js +134 -14
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +9 -0
- package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +11 -12
- package/v2Components/FormBuilder/Functional/tests/mpush.singlePlatform.test.js +144 -0
- package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +17 -5
- package/v2Components/FormBuilder/_formBuilder.scss +21 -0
- package/v2Components/FormBuilder/messages.js +12 -0
- package/v2Components/FormBuilder/tests/mpush.characterization.test.js +45 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +2 -1
- package/v2Containers/CommunicationFlow/Tests/constants.test.js +4 -4
- package/v2Containers/CommunicationFlow/constants.js +1 -0
- package/v2Containers/CommunicationFlow/utils/Tests/mapCommDefinitionToStepData.test.js +2 -1
- package/v2Containers/CommunicationFlow/utils/mapCommDefinitionToStepData.js +1 -0
- package/v2Containers/CreativesContainer/index.js +7 -1
- package/v2Containers/CreativesContainer/tests/index.test.js +81 -0
- package/v2Containers/Email/index.js +31 -4
- package/v2Containers/Email/initialSchema.js +30 -0
- package/v2Containers/Email/messages.js +8 -0
- package/v2Containers/Email/tests/index.test.js +107 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +172 -8
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +264 -5
- package/v2Containers/EmailWrapper/components/_emailHTMLEditor.scss +29 -0
- package/v2Containers/EmailWrapper/constants.js +5 -0
- package/v2Containers/EmailWrapper/messages.js +24 -0
- package/v2Containers/MobilePush/Create/constants.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +2 -2
- package/v2Containers/MobilePush/Create/test/contentValidity.test.js +58 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -2
- package/v2Containers/MobilePush/Edit/test/contentValidity.test.js +17 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -8
- package/v2Containers/TagList/index.js +7 -0
- package/v2Containers/TagList/tests/TagList.test.js +42 -0
|
@@ -154,6 +154,14 @@ export default defineMessages({
|
|
|
154
154
|
id: 'creatives.containersV2.Email.emailSubjectNotEmplty',
|
|
155
155
|
defaultMessage: 'Email subject cannot be empty.',
|
|
156
156
|
},
|
|
157
|
+
"Enter Pre-header Text": {
|
|
158
|
+
id: 'creatives.containersV2.Email.emailPreheader',
|
|
159
|
+
defaultMessage: 'Enter pre-header text',
|
|
160
|
+
},
|
|
161
|
+
"Pre-header": {
|
|
162
|
+
id: 'creatives.containersV2.Email.Preheader',
|
|
163
|
+
defaultMessage: 'Pre-header',
|
|
164
|
+
},
|
|
157
165
|
"Enter template name": {
|
|
158
166
|
id: 'creatives.containersV2.Email.templateNamePlaceholder',
|
|
159
167
|
defaultMessage: 'Enter template name',
|
|
@@ -377,6 +377,27 @@ describe('Email container', () => {
|
|
|
377
377
|
const addLangCol = cols.find((c) => c.id === 'add-language-button');
|
|
378
378
|
expect(addLangCol.colStyle.display).toBe('');
|
|
379
379
|
});
|
|
380
|
+
|
|
381
|
+
const getStandaloneFieldIds = (schema) => _.flatMap(
|
|
382
|
+
schema.standalone.sections,
|
|
383
|
+
(section) => _.flatMap(section.inputFields, (inputField) => _.map(inputField.cols, (col) => col.id)),
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
it('keeps both template-subject and template-preheader as standalone fields for embedded/library module', () => {
|
|
387
|
+
const wrapper = renderEmail({ location: { query: { type: 'embedded', module: 'library' } } });
|
|
388
|
+
|
|
389
|
+
const result = wrapper.instance().removeStandAlone(_.cloneDeep(emailDefinition));
|
|
390
|
+
|
|
391
|
+
expect(getStandaloneFieldIds(result)).toEqual(['template-subject', 'template-preheader']);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('drops template-name from standalone fields for embedded/library module (unchanged prior behavior)', () => {
|
|
395
|
+
const wrapper = renderEmail({ location: { query: { type: 'embedded', module: 'library' } } });
|
|
396
|
+
|
|
397
|
+
const result = wrapper.instance().removeStandAlone(_.cloneDeep(emailDefinition));
|
|
398
|
+
|
|
399
|
+
expect(getStandaloneFieldIds(result)).not.toContain('template-name');
|
|
400
|
+
});
|
|
380
401
|
});
|
|
381
402
|
|
|
382
403
|
describe('handleEdmSave (mode === "switchEditor")', () => {
|
|
@@ -717,4 +738,90 @@ describe('Email container', () => {
|
|
|
717
738
|
expect(wrapper.state('savedTemplate')).toBeNull();
|
|
718
739
|
});
|
|
719
740
|
});
|
|
741
|
+
|
|
742
|
+
describe('pre-header field', () => {
|
|
743
|
+
describe('onTagSelect', () => {
|
|
744
|
+
it('appends the tag to template-preheader when a tag is selected from the pre-header field', () => {
|
|
745
|
+
const wrapper = renderEmail();
|
|
746
|
+
wrapper.setState({ formData: { 'template-preheader': 'Hello ' } });
|
|
747
|
+
|
|
748
|
+
wrapper.instance().onTagSelect('first_name', 1, { id: 'template-preheader' });
|
|
749
|
+
|
|
750
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('Hello {{first_name}}');
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('does not touch template-subject when the tag is selected from the pre-header field', () => {
|
|
754
|
+
const wrapper = renderEmail();
|
|
755
|
+
wrapper.setState({ formData: { 'template-preheader': '', 'template-subject': 'Original subject' } });
|
|
756
|
+
|
|
757
|
+
wrapper.instance().onTagSelect('loyalty_points', 1, { id: 'template-preheader' });
|
|
758
|
+
|
|
759
|
+
expect(wrapper.state('formData')['template-subject']).toBe('Original subject');
|
|
760
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('{{loyalty_points}}');
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
describe('setEditData', () => {
|
|
765
|
+
it('populates formData["template-preheader"] from versions.base.preheader', () => {
|
|
766
|
+
const wrapper = renderEmail();
|
|
767
|
+
wrapper.setState({ schema: { containers: [] } });
|
|
768
|
+
const instance = wrapper.instance();
|
|
769
|
+
const editData = {
|
|
770
|
+
name: 'My Template',
|
|
771
|
+
versions: {
|
|
772
|
+
base: {
|
|
773
|
+
subject: 'My subject',
|
|
774
|
+
preheader: 'My preheader text',
|
|
775
|
+
activeTab: 'en',
|
|
776
|
+
selectedLanguages: ['en'],
|
|
777
|
+
en: {},
|
|
778
|
+
},
|
|
779
|
+
history: [],
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
instance.setEditData(editData);
|
|
784
|
+
|
|
785
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('My preheader text');
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it('defaults to an empty string when versions.base.preheader is absent', () => {
|
|
789
|
+
const wrapper = renderEmail();
|
|
790
|
+
wrapper.setState({ schema: { containers: [] } });
|
|
791
|
+
const instance = wrapper.instance();
|
|
792
|
+
const editData = {
|
|
793
|
+
name: 'My Template',
|
|
794
|
+
versions: {
|
|
795
|
+
base: {
|
|
796
|
+
subject: 'My subject',
|
|
797
|
+
activeTab: 'en',
|
|
798
|
+
selectedLanguages: ['en'],
|
|
799
|
+
en: {},
|
|
800
|
+
},
|
|
801
|
+
history: [],
|
|
802
|
+
},
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
instance.setEditData(editData);
|
|
806
|
+
|
|
807
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('');
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe('transformFormData', () => {
|
|
812
|
+
it('carries template-preheader into the returned base.preheader', () => {
|
|
813
|
+
const wrapper = renderEmail();
|
|
814
|
+
wrapper.setState({
|
|
815
|
+
formData: {
|
|
816
|
+
'template-preheader': 'Preview snippet text',
|
|
817
|
+
base: { activeTab: 'en', en: { 'template-content': '<p>hi</p>' } },
|
|
818
|
+
},
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
const result = wrapper.instance().transformFormData(wrapper.state('formData'));
|
|
822
|
+
|
|
823
|
+
expect(result.base.preheader).toBe('Preview snippet text');
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
});
|
|
720
827
|
});
|
|
@@ -6,7 +6,6 @@ import { injectIntl, FormattedMessage } from 'react-intl';
|
|
|
6
6
|
import isEmpty from 'lodash/isEmpty';
|
|
7
7
|
import get from 'lodash/get';
|
|
8
8
|
import _ from 'lodash';
|
|
9
|
-
import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
10
9
|
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
11
10
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
12
11
|
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
|
|
@@ -15,6 +14,9 @@ import CapDropdown from '@capillarytech/cap-ui-library/CapDropdown';
|
|
|
15
14
|
import CapMenu from '@capillarytech/cap-ui-library/CapMenu';
|
|
16
15
|
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
17
16
|
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
17
|
+
import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
|
|
18
|
+
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
19
|
+
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
18
20
|
import HTMLEditor from '../../../v2Components/HtmlEditor';
|
|
19
21
|
import CapTagListWithInput from '../../../v2Components/CapTagListWithInput';
|
|
20
22
|
import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
|
|
@@ -24,14 +26,17 @@ import { downloadHtmlFile, sanitizeFileName, getExportHtmlDisabledTooltip } from
|
|
|
24
26
|
import history from '../../../utils/history';
|
|
25
27
|
import messages from '../messages';
|
|
26
28
|
import emailMessages from '../../Email/messages';
|
|
27
|
-
import { validateTags } from '../../../utils/tagValidations';
|
|
29
|
+
import { validateTags, hasUnsubscribeTag } from '../../../utils/tagValidations';
|
|
28
30
|
import {
|
|
29
31
|
TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
|
|
30
32
|
} from '../../Whatsapp/constants';
|
|
31
33
|
import { EMAIL } from '../../CreativesContainer/constants';
|
|
32
34
|
import { OUTBOUND } from '../../../v2Components/FormBuilder/constants';
|
|
35
|
+
import { PREHEADER_EXCLUDED_TAGS } from '../constants';
|
|
33
36
|
import './_emailHTMLEditor.scss';
|
|
34
37
|
|
|
38
|
+
const { CapLabelInline } = CapLabel;
|
|
39
|
+
|
|
35
40
|
/**
|
|
36
41
|
* EmailHTMLEditor Component
|
|
37
42
|
*
|
|
@@ -111,6 +116,17 @@ const EmailHTMLEditor = (props) => {
|
|
|
111
116
|
const [savedTemplate, setSavedTemplate] = useState(null);
|
|
112
117
|
const [subject, setSubject] = useState('');
|
|
113
118
|
const [subjectError, setSubjectError] = useState('');
|
|
119
|
+
const [preheader, setPreheader] = useState('');
|
|
120
|
+
const [preheaderError, setPreheaderError] = useState('');
|
|
121
|
+
// null = show if content exists; true/false = manual "+ Pre-header"/"Remove" click.
|
|
122
|
+
const [preheaderRevealed, setPreheaderRevealed] = useState(null);
|
|
123
|
+
const isPreheaderRevealed = preheaderRevealed !== null ? preheaderRevealed : !!preheader;
|
|
124
|
+
const handleShowPreheaderClick = useCallback(() => setPreheaderRevealed(true), []);
|
|
125
|
+
const handleRemovePreheaderClick = useCallback(() => {
|
|
126
|
+
setPreheader('');
|
|
127
|
+
setPreheaderError('');
|
|
128
|
+
setPreheaderRevealed(false);
|
|
129
|
+
}, []);
|
|
114
130
|
// State for template name (extracted from template data in Edit mode)
|
|
115
131
|
const [extractedTemplateName, setExtractedTemplateName] = useState('');
|
|
116
132
|
const [tagValidationError, setTagValidationError] = useState(null);
|
|
@@ -183,6 +199,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
183
199
|
base: true,
|
|
184
200
|
},
|
|
185
201
|
'template-subject': subject || '',
|
|
202
|
+
'template-preheader': preheader || '',
|
|
186
203
|
};
|
|
187
204
|
},
|
|
188
205
|
getContentForPreview: () => htmlContent || '',
|
|
@@ -206,7 +223,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
206
223
|
html: 0, label: 0, liquid: 0, total: 0,
|
|
207
224
|
};
|
|
208
225
|
},
|
|
209
|
-
}), [htmlContent, subject, currentOrgDetails]);
|
|
226
|
+
}), [htmlContent, subject, preheader, currentOrgDetails]);
|
|
210
227
|
|
|
211
228
|
// Detect edit mode: when isEditEmail is false (create flow), never treat as edit or fetch template details
|
|
212
229
|
const hasParamsId = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
|
|
@@ -296,6 +313,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
296
313
|
lastTemplateIdRef.current = currentTemplateId;
|
|
297
314
|
setHtmlContent('');
|
|
298
315
|
setSubject('');
|
|
316
|
+
setPreheader('');
|
|
299
317
|
setExtractedTemplateName('');
|
|
300
318
|
baselineContentRef.current = null;
|
|
301
319
|
setHasContentChanged(false);
|
|
@@ -324,11 +342,13 @@ const EmailHTMLEditor = (props) => {
|
|
|
324
342
|
if (hasCompleteTemplateData && !contentInitializedRef.current) {
|
|
325
343
|
let extractedContent = '';
|
|
326
344
|
let extractedSubject = '';
|
|
345
|
+
let extractedPreheader = '';
|
|
327
346
|
let extractedName = '';
|
|
328
347
|
|
|
329
348
|
if (templateDataProp.base) {
|
|
330
349
|
extractedContent = templateDataProp.base['template-content'] || templateDataProp.base.html_content || templateDataProp.base.emailBody || '';
|
|
331
350
|
extractedSubject = templateDataProp.base.subject || templateDataProp.base.emailSubject || '';
|
|
351
|
+
extractedPreheader = templateDataProp.base.preheader || '';
|
|
332
352
|
} else if (templateDataProp.versions && templateDataProp.versions.base) {
|
|
333
353
|
const { base } = templateDataProp.versions;
|
|
334
354
|
const activeTab = base.activeTab || 'en';
|
|
@@ -341,9 +361,11 @@ const EmailHTMLEditor = (props) => {
|
|
|
341
361
|
|| '';
|
|
342
362
|
|
|
343
363
|
extractedSubject = base.subject || base.emailSubject || '';
|
|
364
|
+
extractedPreheader = base.preheader || '';
|
|
344
365
|
} else {
|
|
345
366
|
extractedContent = templateDataProp['template-content'] || templateDataProp.emailBody || templateDataProp.html_content || '';
|
|
346
367
|
extractedSubject = templateDataProp.emailSubject || templateDataProp.subject || '';
|
|
368
|
+
extractedPreheader = templateDataProp.preheader || '';
|
|
347
369
|
}
|
|
348
370
|
|
|
349
371
|
// Extract template name from templateDataProp
|
|
@@ -354,6 +376,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
354
376
|
setHtmlContent(extractedContent);
|
|
355
377
|
setLoadedHtmlContent(extractedContent);
|
|
356
378
|
setSubject(extractedSubject);
|
|
379
|
+
setPreheader(extractedPreheader);
|
|
357
380
|
setExtractedTemplateName(extractedName);
|
|
358
381
|
contentInitializedRef.current = true;
|
|
359
382
|
subjectInitializedRef.current = true;
|
|
@@ -425,6 +448,11 @@ const EmailHTMLEditor = (props) => {
|
|
|
425
448
|
|| get(templateDataFromRedux, 'versions.base.subject')
|
|
426
449
|
|| '';
|
|
427
450
|
|
|
451
|
+
const extractedPreheader = baseData.preheader
|
|
452
|
+
|| get(templateDataFromRedux, 'preheader')
|
|
453
|
+
|| get(templateDataFromRedux, 'versions.base.preheader')
|
|
454
|
+
|| '';
|
|
455
|
+
|
|
428
456
|
// Extract template name from Redux data
|
|
429
457
|
const extractedName = templateDataFromRedux.name || '';
|
|
430
458
|
|
|
@@ -438,6 +466,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
438
466
|
if (!contentInitializedRef.current || htmlContent === loadedHtmlContent) {
|
|
439
467
|
setHtmlContent(extractedContent);
|
|
440
468
|
setSubject(extractedSubject);
|
|
469
|
+
setPreheader(extractedPreheader);
|
|
441
470
|
setExtractedTemplateName(extractedName);
|
|
442
471
|
}
|
|
443
472
|
} else if (!contentInitializedRef.current) {
|
|
@@ -445,6 +474,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
445
474
|
setHtmlContent(extractedContent);
|
|
446
475
|
setLoadedHtmlContent(extractedContent);
|
|
447
476
|
setSubject(extractedSubject);
|
|
477
|
+
setPreheader(extractedPreheader);
|
|
448
478
|
setExtractedTemplateName(extractedName);
|
|
449
479
|
}
|
|
450
480
|
|
|
@@ -470,6 +500,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
470
500
|
setHtmlContent('');
|
|
471
501
|
setLoadedHtmlContent(''); // Set stable loaded content
|
|
472
502
|
setSubject('');
|
|
503
|
+
setPreheader('');
|
|
473
504
|
setExtractedTemplateName('');
|
|
474
505
|
setIsLoading(false);
|
|
475
506
|
if (setIsLoadingContent) {
|
|
@@ -676,6 +707,53 @@ const EmailHTMLEditor = (props) => {
|
|
|
676
707
|
}
|
|
677
708
|
}, [subjectError]);
|
|
678
709
|
|
|
710
|
+
// Handle tag insertion into Pre-header field
|
|
711
|
+
const handlePreheaderTagSelect = useCallback((data) => {
|
|
712
|
+
if (data) {
|
|
713
|
+
const tagToInsert = `{{${data}}}`;
|
|
714
|
+
const input = document.getElementById('template-preheader') || document.querySelector('#template-preheader input');
|
|
715
|
+
let preheaderValue = preheader || '';
|
|
716
|
+
try {
|
|
717
|
+
if (input && (typeof input.selectionStart === 'number')) {
|
|
718
|
+
const startPos = input.selectionStart;
|
|
719
|
+
const endPos = input.selectionEnd;
|
|
720
|
+
preheaderValue = `${preheaderValue.substring(0, startPos)}${tagToInsert}${preheaderValue.substring(endPos)}`;
|
|
721
|
+
setPreheader(preheaderValue);
|
|
722
|
+
try {
|
|
723
|
+
input.focus();
|
|
724
|
+
const newPos = startPos + tagToInsert.length;
|
|
725
|
+
input.selectionStart = newPos;
|
|
726
|
+
input.selectionEnd = newPos;
|
|
727
|
+
} catch (e) {
|
|
728
|
+
// Ignore focus errors
|
|
729
|
+
}
|
|
730
|
+
} else {
|
|
731
|
+
preheaderValue = `${preheaderValue}${tagToInsert}`;
|
|
732
|
+
setPreheader(preheaderValue);
|
|
733
|
+
if (input) {
|
|
734
|
+
try {
|
|
735
|
+
input.value = preheaderValue;
|
|
736
|
+
} catch (e) {
|
|
737
|
+
// Ignore value setting errors
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
} catch (e) {
|
|
742
|
+
// Fallback: safe append
|
|
743
|
+
preheaderValue = `${preheaderValue}${tagToInsert}`;
|
|
744
|
+
setPreheader(preheaderValue);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}, [preheader]);
|
|
748
|
+
|
|
749
|
+
// Handle pre-header change
|
|
750
|
+
const handlePreheaderChange = useCallback((e) => {
|
|
751
|
+
setPreheader(e.target.value);
|
|
752
|
+
if (preheaderError) {
|
|
753
|
+
setPreheaderError('');
|
|
754
|
+
}
|
|
755
|
+
}, [preheaderError]);
|
|
756
|
+
|
|
679
757
|
// Handle Save/Update with liquid validation
|
|
680
758
|
const handleSave = useCallback(() => {
|
|
681
759
|
// IMPORTANT: Clear API validation errors FIRST before checking for validation errors
|
|
@@ -704,6 +782,15 @@ const EmailHTMLEditor = (props) => {
|
|
|
704
782
|
setSubjectError('');
|
|
705
783
|
}
|
|
706
784
|
|
|
785
|
+
// 1.1. Validate Pre-header - BLOCKING: must never contain the unsubscribe tag
|
|
786
|
+
if (hasUnsubscribeTag(preheader)) {
|
|
787
|
+
setPreheaderError(formatMessage(messages.unsubscribeNotAllowedInPreheader));
|
|
788
|
+
if (onValidationFail) {
|
|
789
|
+
onValidationFail();
|
|
790
|
+
}
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
707
794
|
// 1.5. Check for validation errors (Errors = blocking, Warnings = non-blocking)
|
|
708
795
|
// Get issue counts from ref or stored state
|
|
709
796
|
let issueCounts = { errors: 0, warnings: 0, total: 0 };
|
|
@@ -835,6 +922,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
835
922
|
base: true,
|
|
836
923
|
tabKey,
|
|
837
924
|
subject: subject || '',
|
|
925
|
+
preheader: preheader || '',
|
|
838
926
|
};
|
|
839
927
|
|
|
840
928
|
const historyEntry = {
|
|
@@ -844,6 +932,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
844
932
|
base: true,
|
|
845
933
|
tabKey,
|
|
846
934
|
subject: subject || '',
|
|
935
|
+
preheader: preheader || '',
|
|
847
936
|
};
|
|
848
937
|
|
|
849
938
|
// Opened in edit mode: name from the loaded template; created in this session: the name entered for it
|
|
@@ -944,6 +1033,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
944
1033
|
secondary_templates: [{ template_data: tmpData }],
|
|
945
1034
|
};
|
|
946
1035
|
libraryPayload.base.subject = subject || '';
|
|
1036
|
+
libraryPayload.base.preheader = preheader || '';
|
|
947
1037
|
|
|
948
1038
|
if (location?.query?.module === 'library' && isGetFormData && getFormdata) {
|
|
949
1039
|
const response = {
|
|
@@ -1006,6 +1096,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
1006
1096
|
}
|
|
1007
1097
|
}, [
|
|
1008
1098
|
subject,
|
|
1099
|
+
preheader,
|
|
1009
1100
|
htmlContent,
|
|
1010
1101
|
tags,
|
|
1011
1102
|
injectedTags,
|
|
@@ -1142,7 +1233,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
1142
1233
|
<CapRow>
|
|
1143
1234
|
<CapColumn span={24}>
|
|
1144
1235
|
{/* Subject Field */}
|
|
1145
|
-
<CapColumn span={24}
|
|
1236
|
+
<CapColumn span={24} className="email-html-editor-field">
|
|
1146
1237
|
<CapTagListWithInput
|
|
1147
1238
|
inputId="template-subject"
|
|
1148
1239
|
inputValue={subject}
|
|
@@ -1150,9 +1241,13 @@ const EmailHTMLEditor = (props) => {
|
|
|
1150
1241
|
inputPlaceholder={formatMessage(messages.enterEmailSubject)}
|
|
1151
1242
|
inputRequired
|
|
1152
1243
|
inputErrorMessage={subjectError}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1244
|
+
inputProps={{
|
|
1245
|
+
prefix: (
|
|
1246
|
+
<CapLabelInline type="label2" className="email-html-editor-prefix email-html-editor-prefix-label">
|
|
1247
|
+
{formatMessage(messages.subject)}
|
|
1248
|
+
</CapLabelInline>
|
|
1249
|
+
),
|
|
1250
|
+
}}
|
|
1156
1251
|
onTagSelect={handleSubjectTagSelect}
|
|
1157
1252
|
onContextChange={handleOnTagsContextChange}
|
|
1158
1253
|
location={location}
|
|
@@ -1161,9 +1256,77 @@ const EmailHTMLEditor = (props) => {
|
|
|
1161
1256
|
selectedOfferDetails={selectedOfferDetails}
|
|
1162
1257
|
eventContextTags={eventContextTags}
|
|
1163
1258
|
waitEventContextTags={waitEventContextTags}
|
|
1164
|
-
showHeading
|
|
1259
|
+
showHeading={false}
|
|
1260
|
+
showTagList
|
|
1261
|
+
showInput
|
|
1262
|
+
singleLineLayout
|
|
1263
|
+
extraContent={!isPreheaderRevealed ? (
|
|
1264
|
+
<CapButton
|
|
1265
|
+
type="dashed"
|
|
1266
|
+
size="small"
|
|
1267
|
+
className="email-html-editor-add-preheader-btn"
|
|
1268
|
+
onClick={handleShowPreheaderClick}
|
|
1269
|
+
>
|
|
1270
|
+
{formatMessage(messages.addPreheader)}
|
|
1271
|
+
</CapButton>
|
|
1272
|
+
) : null}
|
|
1273
|
+
containerStyle={{
|
|
1274
|
+
display: 'flex',
|
|
1275
|
+
flexDirection: 'column',
|
|
1276
|
+
}}
|
|
1277
|
+
popoverPlacement="bottomRight"
|
|
1278
|
+
/>
|
|
1279
|
+
</CapColumn>
|
|
1280
|
+
|
|
1281
|
+
{/* Pre-header Field */}
|
|
1282
|
+
{isPreheaderRevealed && (
|
|
1283
|
+
<CapColumn span={24} className="email-html-editor-field">
|
|
1284
|
+
<CapTagListWithInput
|
|
1285
|
+
inputId="template-preheader"
|
|
1286
|
+
inputValue={preheader}
|
|
1287
|
+
inputOnChange={handlePreheaderChange}
|
|
1288
|
+
inputPlaceholder={formatMessage(messages.enterEmailPreheader)}
|
|
1289
|
+
inputMaxLength={85}
|
|
1290
|
+
inputErrorMessage={preheaderError}
|
|
1291
|
+
excludeTags={PREHEADER_EXCLUDED_TAGS}
|
|
1292
|
+
inputProps={{
|
|
1293
|
+
prefix: (
|
|
1294
|
+
<CapRow align="middle" type="flex" noWrap gap={4} className="email-html-editor-prefix">
|
|
1295
|
+
<CapLabelInline type="label2" className="email-html-editor-prefix-label">
|
|
1296
|
+
{formatMessage(messages.preheader)}
|
|
1297
|
+
</CapLabelInline>
|
|
1298
|
+
<CapTooltipWithInfo title={formatMessage(messages.preheaderInfoTooltip)} />
|
|
1299
|
+
</CapRow>
|
|
1300
|
+
),
|
|
1301
|
+
suffix: (
|
|
1302
|
+
<CapRow align="middle" type="flex" noWrap gap={8}>
|
|
1303
|
+
<CapLabelInline type="label1" className="email-html-editor-counter">
|
|
1304
|
+
{`${(preheader || '').length} / 85`}
|
|
1305
|
+
</CapLabelInline>
|
|
1306
|
+
<CapLabelInline
|
|
1307
|
+
type="label1"
|
|
1308
|
+
role="button"
|
|
1309
|
+
tabIndex={0}
|
|
1310
|
+
className="email-html-editor-remove-link"
|
|
1311
|
+
onClick={handleRemovePreheaderClick}
|
|
1312
|
+
>
|
|
1313
|
+
{formatMessage(messages.removePreheader)}
|
|
1314
|
+
</CapLabelInline>
|
|
1315
|
+
</CapRow>
|
|
1316
|
+
),
|
|
1317
|
+
}}
|
|
1318
|
+
onTagSelect={handlePreheaderTagSelect}
|
|
1319
|
+
onContextChange={handleOnTagsContextChange}
|
|
1320
|
+
location={location}
|
|
1321
|
+
tags={tags}
|
|
1322
|
+
injectedTags={injectedTags || {}}
|
|
1323
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
1324
|
+
eventContextTags={eventContextTags}
|
|
1325
|
+
waitEventContextTags={waitEventContextTags}
|
|
1326
|
+
showHeading={false}
|
|
1165
1327
|
showTagList
|
|
1166
1328
|
showInput
|
|
1329
|
+
singleLineLayout
|
|
1167
1330
|
containerStyle={{
|
|
1168
1331
|
display: 'flex',
|
|
1169
1332
|
flexDirection: 'column',
|
|
@@ -1171,6 +1334,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
1171
1334
|
popoverPlacement="bottomRight"
|
|
1172
1335
|
/>
|
|
1173
1336
|
</CapColumn>
|
|
1337
|
+
)}
|
|
1174
1338
|
|
|
1175
1339
|
<CapColumn span={24} className="html-editor-options-column">
|
|
1176
1340
|
<CapDropdown
|