@capillarytech/creatives-library 8.0.288 → 8.0.290-alpha.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/constants/unified.js +0 -1
- package/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +5 -8
- package/utils/commonUtils.js +4 -85
- package/utils/tagValidations.js +84 -222
- package/utils/tests/commonUtil.test.js +461 -118
- package/utils/tests/tagValidations.test.js +280 -358
- package/v2Components/ErrorInfoNote/index.js +2 -5
- package/v2Components/FormBuilder/index.js +78 -161
- package/v2Components/FormBuilder/messages.js +0 -8
- package/v2Components/HtmlEditor/HTMLEditor.js +0 -5
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -15
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
- package/v2Containers/Cap/mockData.js +0 -14
- package/v2Containers/Cap/reducer.js +3 -55
- package/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/index.js +19 -6
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +10 -62
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +12 -115
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/index.js +1 -96
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InappAdvance/index.js +2 -103
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePush/Create/index.js +6 -16
- package/v2Containers/MobilePush/Edit/index.js +6 -16
- package/v2Containers/MobilePushNew/index.js +2 -33
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/Sms/Create/index.js +31 -3
- package/v2Containers/Sms/Create/messages.js +4 -0
- package/v2Containers/Sms/Edit/index.js +29 -3
- package/v2Containers/Sms/commonMethods.js +6 -6
- package/v2Containers/SmsTrai/Edit/index.js +6 -47
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/Templates/reducer.js +3 -1
- package/v2Containers/Templates/tests/reducer.test.js +12 -0
- package/v2Containers/Viber/index.js +0 -1
- package/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
- package/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
- package/v2Containers/WebPush/Create/utils/validation.js +17 -2
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -24
- package/v2Containers/Whatsapp/index.js +9 -17
- package/v2Containers/Zalo/index.js +3 -11
|
@@ -2,11 +2,9 @@ export const expectedStateGetLiquidTagsRequest = {
|
|
|
2
2
|
fetchingLiquidTags: true,
|
|
3
3
|
fetchingSchema: true,
|
|
4
4
|
fetchingSchemaError: "",
|
|
5
|
-
liquidTags: [],
|
|
6
5
|
messages: [],
|
|
7
6
|
metaEntities: {
|
|
8
7
|
layouts: [],
|
|
9
|
-
tagLookupMap: {},
|
|
10
8
|
tags: [],
|
|
11
9
|
},
|
|
12
10
|
orgID: "",
|
|
@@ -17,11 +15,9 @@ export const expectedStateGetLiquidTagsFailure = {
|
|
|
17
15
|
fetchingLiquidTags: false,
|
|
18
16
|
fetchingSchema: true,
|
|
19
17
|
fetchingSchemaError: "",
|
|
20
|
-
liquidTags: [],
|
|
21
18
|
messages: [],
|
|
22
19
|
metaEntities: {
|
|
23
20
|
layouts: [],
|
|
24
|
-
tagLookupMap: {},
|
|
25
21
|
tags: [],
|
|
26
22
|
},
|
|
27
23
|
orgID: "",
|
|
@@ -32,11 +28,9 @@ export const expectedStateGetLiquidTagsSuccess = {
|
|
|
32
28
|
fetchingLiquidTags: false,
|
|
33
29
|
fetchingSchema: true,
|
|
34
30
|
fetchingSchemaError: "",
|
|
35
|
-
liquidTags: [],
|
|
36
31
|
messages: [],
|
|
37
32
|
metaEntities: {
|
|
38
33
|
layouts: [],
|
|
39
|
-
tagLookupMap: {},
|
|
40
34
|
tags: [],
|
|
41
35
|
},
|
|
42
36
|
orgID: "",
|
|
@@ -47,11 +41,9 @@ export const expectedStateGetSchemaForEntitySuccessTAG = {
|
|
|
47
41
|
fetchingLiquidTags: false,
|
|
48
42
|
fetchingSchema: false,
|
|
49
43
|
fetchingSchemaError: false,
|
|
50
|
-
liquidTags: [],
|
|
51
44
|
messages: [],
|
|
52
45
|
metaEntities: {
|
|
53
46
|
layouts: undefined,
|
|
54
|
-
tagLookupMap: { undefined: { definition: {} } },
|
|
55
47
|
tags: { standard: { random: "32" } },
|
|
56
48
|
},
|
|
57
49
|
orgID: "",
|
|
@@ -62,11 +54,9 @@ export const expectedStateGetSchemaForEntitySuccess = {
|
|
|
62
54
|
fetchingLiquidTags: false,
|
|
63
55
|
fetchingSchema: false,
|
|
64
56
|
fetchingSchemaError: false,
|
|
65
|
-
liquidTags: [],
|
|
66
57
|
messages: [],
|
|
67
58
|
metaEntities: {
|
|
68
59
|
layouts: undefined,
|
|
69
|
-
tagLookupMap: undefined,
|
|
70
60
|
tags: undefined,
|
|
71
61
|
},
|
|
72
62
|
orgID: "",
|
|
@@ -78,13 +68,9 @@ export const expectedForwardedTags = {
|
|
|
78
68
|
fetchingSchema: false,
|
|
79
69
|
fetchingSchemaError: '',
|
|
80
70
|
injectedTags: undefined,
|
|
81
|
-
liquidTags: [],
|
|
82
71
|
messages: [],
|
|
83
72
|
metaEntities: {
|
|
84
73
|
layouts: [],
|
|
85
|
-
tagLookupMap: {
|
|
86
|
-
|
|
87
|
-
},
|
|
88
74
|
tags: [],
|
|
89
75
|
},
|
|
90
76
|
orgID: "",
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Created by vivek on 22/5/17.
|
|
3
3
|
*/
|
|
4
|
-
import { fromJS
|
|
4
|
+
import { fromJS } from 'immutable';
|
|
5
5
|
import _ from 'lodash';
|
|
6
6
|
import * as types from './constants';
|
|
7
7
|
import initialState from '../../initialState';
|
|
8
8
|
import { FAILURE } from '../App/constants';
|
|
9
|
-
import { TAG } from '../Whatsapp/constants';
|
|
10
|
-
import { getTagMapValue, getForwardedMapValues, getLoyaltyTagsMapValue } from '../../utils/tagValidations';
|
|
11
9
|
|
|
12
10
|
function capReducer(state = fromJS(initialState.cap), action) {
|
|
13
11
|
switch (action.type) {
|
|
@@ -98,39 +96,6 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
98
96
|
return state
|
|
99
97
|
.set('fetchingLiquidTags', false);
|
|
100
98
|
case types.GET_SCHEMA_FOR_ENTITY_SUCCESS: {
|
|
101
|
-
//Process standard tags
|
|
102
|
-
const standardTagMapInitial = _.keyBy(
|
|
103
|
-
action?.data?.metaEntities?.standard,
|
|
104
|
-
item => item?.definition?.value
|
|
105
|
-
);
|
|
106
|
-
// Mapping only the `definition` object instead of the entire item, to reduce space used
|
|
107
|
-
const standardTagMap = _.mapValues(standardTagMapInitial, item => ({
|
|
108
|
-
definition: item?.definition ?? {},
|
|
109
|
-
}));
|
|
110
|
-
|
|
111
|
-
// Process custom tags
|
|
112
|
-
const customSubtags = getTagMapValue(action?.data?.metaEntities?.custom)
|
|
113
|
-
// Process extended tags
|
|
114
|
-
const extendedSubtags = getTagMapValue(action?.data?.metaEntities?.extended);
|
|
115
|
-
|
|
116
|
-
const loyaltySubTagsData = getLoyaltyTagsMapValue(action?.data?.metaEntities?.loyaltyTags);
|
|
117
|
-
|
|
118
|
-
const getExistingTagLookupMap = (state) => {
|
|
119
|
-
if (!state || !state.get) return {};
|
|
120
|
-
const tagLookupMap = state.getIn(['metaEntities', 'tagLookupMap']);
|
|
121
|
-
return state.get('metaEntities') && ImmutableMap.isMap(tagLookupMap)
|
|
122
|
-
? tagLookupMap.toJS()
|
|
123
|
-
: {};
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
// Combine all maps
|
|
127
|
-
const combinedTagMap = {
|
|
128
|
-
...standardTagMap,
|
|
129
|
-
...customSubtags,
|
|
130
|
-
...extendedSubtags,
|
|
131
|
-
...loyaltySubTagsData,
|
|
132
|
-
...getExistingTagLookupMap(state),
|
|
133
|
-
};
|
|
134
99
|
const stateMeta = state.get("metaEntities");
|
|
135
100
|
return state
|
|
136
101
|
.set('fetchingSchema', false)
|
|
@@ -138,7 +103,6 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
138
103
|
.set('metaEntities', {
|
|
139
104
|
layouts: action.data && action.entityType === 'LAYOUT' ? action.data.metaEntities : stateMeta?.layouts,
|
|
140
105
|
tags: action.data && action.entityType === 'TAG' ? action.data.metaEntities : stateMeta?.tags,
|
|
141
|
-
tagLookupMap: action?.data && action?.entityType === TAG ? combinedTagMap : stateMeta?.tagLookupMap,
|
|
142
106
|
})
|
|
143
107
|
.set('fetchingSchemaError', false);
|
|
144
108
|
}
|
|
@@ -146,7 +110,6 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
146
110
|
return state.set('metaEntities', {
|
|
147
111
|
layouts: [],
|
|
148
112
|
tags: [],
|
|
149
|
-
tagLookupMap: {},
|
|
150
113
|
});
|
|
151
114
|
// eslint-disable-next-line no-case-declarations
|
|
152
115
|
case types.HIDE_TAGS:
|
|
@@ -154,23 +117,8 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
154
117
|
metaEntities.tags.standard = _.filter(state.get('metaEntities').tags.standard, (tag) => action.tagList.indexOf(tag.definition.value) === -1);
|
|
155
118
|
metaEntities.tags.custom = _.filter(state.get('metaEntities').tags.custom, (tag) => action.tagList.indexOf(tag.name) === -1);
|
|
156
119
|
return state.setIn(['metaEntities'], metaEntities);
|
|
157
|
-
case types.SET_INJECTED_TAGS:
|
|
158
|
-
|
|
159
|
-
// Deep clone the tagLookupMap to avoid direct mutations
|
|
160
|
-
let updatedMetaEntitiesTagLookUp = _.cloneDeep(state.getIn(['metaEntities', 'tagLookupMap']));
|
|
161
|
-
const formattedInjectedTags = getForwardedMapValues(action?.injectedTags);
|
|
162
|
-
// Merge the injectedTags with the existing tagLookupMap
|
|
163
|
-
updatedMetaEntitiesTagLookUp = {
|
|
164
|
-
...formattedInjectedTags || {},
|
|
165
|
-
...updatedMetaEntitiesTagLookUp || {},
|
|
166
|
-
};
|
|
167
|
-
return state.set("injectedTags", action.injectedTags).setIn(
|
|
168
|
-
["metaEntities"],
|
|
169
|
-
fromJS({
|
|
170
|
-
...state.get("metaEntities"),
|
|
171
|
-
tagLookupMap: updatedMetaEntitiesTagLookUp
|
|
172
|
-
})
|
|
173
|
-
);
|
|
120
|
+
case types.SET_INJECTED_TAGS:
|
|
121
|
+
return state.set("injectedTags", action.injectedTags);
|
|
174
122
|
case types.GET_TOPBAR_MENU_DATA_REQUEST:
|
|
175
123
|
return state.set('topbarMenuData', fromJS({ status: 'request' }));
|
|
176
124
|
case types.GET_TOPBAR_MENU_DATA_SUCCESS:
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
expectedStateGetSchemaForEntitySuccessTAG,
|
|
22
22
|
expectedStateGetSchemaForEntitySuccess,
|
|
23
23
|
} from '../mockData';
|
|
24
|
-
import { TAG } from '../../Whatsapp/constants';
|
|
25
24
|
import { loadItem } from '../../../services/localStorageApi';
|
|
26
25
|
|
|
27
26
|
|
|
@@ -118,104 +117,3 @@ describe('should handle GET_SUPPORT_VIDEOS_CONFIG', () => {
|
|
|
118
117
|
expect(reducer(mockedInitialState, action).toJS())?.fetchingSchema?.toEqual(true);
|
|
119
118
|
});
|
|
120
119
|
});
|
|
121
|
-
|
|
122
|
-
describe('GET_SCHEMA_FOR_ENTITY_SUCCESS handler', () => {
|
|
123
|
-
it.concurrent('should handle existing tagLookupMap correctly when metaEntities and tagLookupMap exist', () => {
|
|
124
|
-
const initialStateTest = fromJS({
|
|
125
|
-
token: loadItem('token') || '',
|
|
126
|
-
orgID: loadItem('orgID') || '',
|
|
127
|
-
messages: [],
|
|
128
|
-
metaEntities: {
|
|
129
|
-
tags: [{ id: 1, name: 'tag1' }],
|
|
130
|
-
layouts: ['layout1', 'layout2'],
|
|
131
|
-
tagLookupMap: {
|
|
132
|
-
existingTag: { definition: { value: 'existing' } },
|
|
133
|
-
anotherTag: { definition: { value: 'another' } },
|
|
134
|
-
},
|
|
135
|
-
standard: [],
|
|
136
|
-
},
|
|
137
|
-
liquidTags: [],
|
|
138
|
-
fetchingLiquidTags: false,
|
|
139
|
-
fetchingSchema: false,
|
|
140
|
-
fetchingSchemaError: '',
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
let action = {
|
|
144
|
-
type: GET_SCHEMA_FOR_ENTITY_SUCCESS,
|
|
145
|
-
entityType: TAG,
|
|
146
|
-
data: {
|
|
147
|
-
metaEntities: {
|
|
148
|
-
standard: [],
|
|
149
|
-
custom: [],
|
|
150
|
-
extended: [],
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
let newState = reducer(initialStateTest, action);
|
|
156
|
-
let metaEntities = newState.get('metaEntities');
|
|
157
|
-
|
|
158
|
-
expect(metaEntities).toEqual(expect.objectContaining({
|
|
159
|
-
tagLookupMap: expect.objectContaining({
|
|
160
|
-
existingTag: expect.objectContaining({
|
|
161
|
-
definition: expect.objectContaining({
|
|
162
|
-
value: 'existing',
|
|
163
|
-
}),
|
|
164
|
-
}),
|
|
165
|
-
}),
|
|
166
|
-
}));
|
|
167
|
-
|
|
168
|
-
action = {
|
|
169
|
-
type: GET_SCHEMA_FOR_ENTITY_SUCCESS,
|
|
170
|
-
entityType: 'LAYOUT',
|
|
171
|
-
data: {
|
|
172
|
-
metaEntities: {
|
|
173
|
-
layouts: ['layout1', 'layout2'],
|
|
174
|
-
standard: [],
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
newState = reducer(initialStateTest, action);
|
|
179
|
-
metaEntities = newState.get('metaEntities');
|
|
180
|
-
expect(metaEntities).toEqual(expect.objectContaining({
|
|
181
|
-
layouts: expect.objectContaining({
|
|
182
|
-
layouts: expect.arrayContaining(['layout1', 'layout2']),
|
|
183
|
-
}),
|
|
184
|
-
}));
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it.concurrent('should handle non-existent tagLookupMap by returning empty object', () => {
|
|
188
|
-
const initialStateTest = fromJS({
|
|
189
|
-
token: loadItem('token') || '',
|
|
190
|
-
orgID: loadItem('orgID') || '',
|
|
191
|
-
messages: [],
|
|
192
|
-
metaEntities: {
|
|
193
|
-
tagLookupMap: {},
|
|
194
|
-
},
|
|
195
|
-
liquidTags: [],
|
|
196
|
-
fetchingLiquidTags: false,
|
|
197
|
-
fetchingSchema: false,
|
|
198
|
-
fetchingSchemaError: '',
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
const action = {
|
|
202
|
-
type: GET_SCHEMA_FOR_ENTITY_SUCCESS,
|
|
203
|
-
entityType: TAG,
|
|
204
|
-
data: {
|
|
205
|
-
metaEntities: {
|
|
206
|
-
standard: [],
|
|
207
|
-
custom: [],
|
|
208
|
-
extended: [],
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const newState = reducer(initialStateTest, action);
|
|
214
|
-
const metaEntities = newState.get('metaEntities');
|
|
215
|
-
|
|
216
|
-
// Updated assertions to handle plain object
|
|
217
|
-
expect(metaEntities).toBeDefined();
|
|
218
|
-
expect(metaEntities.tagLookupMap).toBeDefined();
|
|
219
|
-
expect(metaEntities.tagLookupMap).toEqual({});
|
|
220
|
-
});
|
|
221
|
-
});
|
|
@@ -53,6 +53,7 @@ function SlideBoxFooter(props) {
|
|
|
53
53
|
// Only apply validation state checks for EMAIL channel in HTML Editor mode (not BEE/DragDrop)
|
|
54
54
|
// For other channels, BEE editor, or when htmlEditorValidationState is not provided, don't disable based on validation
|
|
55
55
|
const isEmailChannel = currentChannel?.toUpperCase() === 'EMAIL';
|
|
56
|
+
const isSmsChannel = currentChannel?.toUpperCase() === 'SMS';
|
|
56
57
|
const isEditMode = slidBoxContent === 'editTemplate';
|
|
57
58
|
|
|
58
59
|
// Use selectedEmailCreateMode for accurate mode detection in create mode (emailCreateMode is mapped for backwards compatibility)
|
|
@@ -129,8 +130,9 @@ function SlideBoxFooter(props) {
|
|
|
129
130
|
const isBEEEditorModeInCreate = !isHTMLEditorMode && !isEditMode;
|
|
130
131
|
const isBEEEditorMode = isBEEEditorModeInEdit || isBEEEditorModeInCreate;
|
|
131
132
|
const hasBEEEditorErrors = isEmailChannel && isBEEEditorMode && (hasStandardErrors || hasLiquidErrors) && (!htmlEditorValidationState || !htmlEditorHasErrors);
|
|
133
|
+
const hasSmsValidationErrors = isSmsChannel && hasStandardErrors;
|
|
132
134
|
|
|
133
|
-
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
135
|
+
const shouldShowErrorInfoNote = hasBEEEditorErrors || hasSmsValidationErrors || isSupportCKEditor;
|
|
134
136
|
|
|
135
137
|
// Check for personalization tokens in title/message when anonymous user tries to save
|
|
136
138
|
const hasPersonalizationTokens = () => {
|
|
@@ -132,7 +132,6 @@ export class Creatives extends React.Component {
|
|
|
132
132
|
},
|
|
133
133
|
hasPersonalizationTokenError: false, // Track personalization token errors in form
|
|
134
134
|
};
|
|
135
|
-
this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
|
|
136
135
|
this.creativesTemplateSteps = {
|
|
137
136
|
1: 'modeSelection',
|
|
138
137
|
2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
|
|
@@ -256,12 +255,23 @@ export class Creatives extends React.Component {
|
|
|
256
255
|
};
|
|
257
256
|
|
|
258
257
|
onShowTemplates = () => {
|
|
259
|
-
this.setState({
|
|
258
|
+
this.setState({
|
|
259
|
+
slidBoxContent: 'templates',
|
|
260
|
+
showSlideBox: true,
|
|
261
|
+
isGetFormData: false,
|
|
262
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
263
|
+
isLiquidValidationError: false,
|
|
264
|
+
});
|
|
260
265
|
this.resetStep();
|
|
261
266
|
};
|
|
262
267
|
|
|
263
268
|
onChannelChange = (channel) => {
|
|
264
|
-
this.setState({
|
|
269
|
+
this.setState({
|
|
270
|
+
currentChannel: channel,
|
|
271
|
+
templateData: null,
|
|
272
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
273
|
+
isLiquidValidationError: false,
|
|
274
|
+
});
|
|
265
275
|
}
|
|
266
276
|
|
|
267
277
|
onCreateNextStep = () => {
|
|
@@ -1469,11 +1479,12 @@ export class Creatives extends React.Component {
|
|
|
1469
1479
|
}
|
|
1470
1480
|
|
|
1471
1481
|
getFormData = (template) => {
|
|
1482
|
+
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1483
|
+
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1484
|
+
this.setState({ isGetFormData: false });
|
|
1472
1485
|
if (template.validity) {
|
|
1473
1486
|
this.setState(
|
|
1474
|
-
{
|
|
1475
|
-
isGetFormData: false,
|
|
1476
|
-
},
|
|
1487
|
+
{},
|
|
1477
1488
|
() => {
|
|
1478
1489
|
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1479
1490
|
const channel = templateData.type;
|
|
@@ -1557,6 +1568,8 @@ export class Creatives extends React.Component {
|
|
|
1557
1568
|
...prevState,
|
|
1558
1569
|
templateData: undefined,
|
|
1559
1570
|
showSlideBox: false,
|
|
1571
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1572
|
+
isLiquidValidationError: false,
|
|
1560
1573
|
}), () => this.props.handleCloseCreatives(reloadTemplates));
|
|
1561
1574
|
};
|
|
1562
1575
|
|
|
@@ -26,7 +26,7 @@ import * as globalActions from '../Cap/actions';
|
|
|
26
26
|
import './_email.scss';
|
|
27
27
|
import {getMessageObject} from '../../utils/messageUtils';
|
|
28
28
|
import EmailPreview from '../../v2Components/EmailPreview';
|
|
29
|
-
import { getDecodedFileName,
|
|
29
|
+
import { getDecodedFileName, hasSupportCKEditor } from '../../utils/common';
|
|
30
30
|
import Pagination from '../../v2Components/Pagination';
|
|
31
31
|
import * as creativesContainerActions from '../CreativesContainer/actions';
|
|
32
32
|
import withCreatives from '../../hoc/withCreatives';
|
|
@@ -170,7 +170,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
170
170
|
deleteLanguage: this.deleteLanguage,
|
|
171
171
|
},
|
|
172
172
|
};
|
|
173
|
-
this.liquidFlow = hasLiquidSupportFeature();
|
|
174
173
|
}
|
|
175
174
|
componentWillMount() {
|
|
176
175
|
const formData = this.initFormData(this.props, true); //_.cloneDeep(this.state.formData);
|
|
@@ -255,7 +254,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
255
254
|
layout: 'EMAIL',
|
|
256
255
|
type: 'LAYOUT',
|
|
257
256
|
version: 'v2',
|
|
258
|
-
liquidFlow:this.liquidFlow,
|
|
259
257
|
};
|
|
260
258
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
261
259
|
window.addEventListener("message", this.handleFrameTasks);
|
|
@@ -347,7 +345,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
347
345
|
type: 'TAG',
|
|
348
346
|
context: this.props.location.query.type === 'embedded' ? this.props.location.query.module : 'default',
|
|
349
347
|
embedded: this.props.location.query.type === 'embedded' ? this.props.location.query.type : 'full',
|
|
350
|
-
liquidFlow:this.liquidFlow
|
|
351
348
|
};
|
|
352
349
|
if (this.props.getDefaultTags) {
|
|
353
350
|
query.context = this.props.getDefaultTags;
|
|
@@ -2373,7 +2370,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2373
2370
|
type: 'TAG',
|
|
2374
2371
|
context: (data || '').toLowerCase() === 'all' ? 'default' : (data || '').toLowerCase(),
|
|
2375
2372
|
embedded: 'full',
|
|
2376
|
-
liquidFlow:this.liquidFlow
|
|
2377
2373
|
};
|
|
2378
2374
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
2379
2375
|
}
|
|
@@ -15,7 +15,7 @@ import HTMLEditor from '../../../v2Components/HtmlEditor';
|
|
|
15
15
|
import CapTagListWithInput from '../../../v2Components/CapTagListWithInput';
|
|
16
16
|
import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
|
|
17
17
|
import { validateLiquidTemplateContent } from '../../../utils/commonUtils';
|
|
18
|
-
import {
|
|
18
|
+
import { isEmailUnsubscribeTagOptional } from '../../../utils/common';
|
|
19
19
|
import history from '../../../utils/history';
|
|
20
20
|
import messages from '../messages';
|
|
21
21
|
import emailMessages from '../../Email/messages';
|
|
@@ -108,13 +108,10 @@ const EmailHTMLEditor = (props) => {
|
|
|
108
108
|
standardErrors: [],
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
// Merge tag validation errors (
|
|
111
|
+
// Merge tag validation errors (missing) into apiValidationErrors so they show in ValidationErrorDisplay
|
|
112
112
|
const mergedApiValidationErrors = useMemo(() => {
|
|
113
113
|
const tagMessages = [];
|
|
114
|
-
if (tagValidationError?.
|
|
115
|
-
tagMessages.push(`Unsupported tags are: ${tagValidationError.unsupportedTags.join(', ')}`);
|
|
116
|
-
}
|
|
117
|
-
if (tagValidationError?.missingTags?.length && !isEmailUnsubscribeTagMandatory()) {
|
|
114
|
+
if (tagValidationError?.missingTags?.length && !isEmailUnsubscribeTagOptional()) {
|
|
118
115
|
tagMessages.push(`Missing tags are: ${tagValidationError.missingTags.join(', ')}`);
|
|
119
116
|
}
|
|
120
117
|
if (tagMessages.length === 0) {
|
|
@@ -190,9 +187,6 @@ const EmailHTMLEditor = (props) => {
|
|
|
190
187
|
},
|
|
191
188
|
}), [htmlContent, subject, currentOrgDetails]);
|
|
192
189
|
|
|
193
|
-
// Check if liquid support is enabled
|
|
194
|
-
const isLiquidEnabled = hasLiquidSupportFeature();
|
|
195
|
-
|
|
196
190
|
// Detect edit mode: when isEditEmail is false (create flow), never treat as edit or fetch template details
|
|
197
191
|
const hasParamsId = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
|
|
198
192
|
const currentTemplateId = isEditEmail
|
|
@@ -493,10 +487,8 @@ const EmailHTMLEditor = (props) => {
|
|
|
493
487
|
const validationResult = validateTags({
|
|
494
488
|
content,
|
|
495
489
|
tagsParam: tags,
|
|
496
|
-
injectedTagsParams: injectedTags,
|
|
497
490
|
location,
|
|
498
491
|
tagModule: getDefaultTags,
|
|
499
|
-
eventContextTags,
|
|
500
492
|
isFullMode,
|
|
501
493
|
});
|
|
502
494
|
|
|
@@ -650,7 +642,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
650
642
|
// IMPORTANT: Clear API validation errors FIRST before checking for validation errors
|
|
651
643
|
// This ensures that old API errors don't block the save when user fixes content and clicks Update again
|
|
652
644
|
// We'll re-validate with fresh API call anyway
|
|
653
|
-
if (
|
|
645
|
+
if (getLiquidTags) {
|
|
654
646
|
setApiValidationErrors({
|
|
655
647
|
liquidErrors: [],
|
|
656
648
|
standardErrors: [],
|
|
@@ -712,7 +704,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
712
704
|
// When EMAIL_UNSUBSCRIBE_TAG_MANDATORY is false: validate and require unsubscribe tag.
|
|
713
705
|
// Run for both library and full mode so liquid-enabled orgs also get the error (notification + ValidationErrorDisplay).
|
|
714
706
|
const isModuleTypeOutbound = (moduleType || '').toUpperCase() === OUTBOUND;
|
|
715
|
-
if (!
|
|
707
|
+
if (!isEmailUnsubscribeTagOptional() && isModuleTypeOutbound) {
|
|
716
708
|
const unsubscribeRegex = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g; // eslint-disable-line no-useless-escape
|
|
717
709
|
const hasUnsubscribeTag = unsubscribeRegex.test(htmlContent);
|
|
718
710
|
|
|
@@ -744,52 +736,17 @@ const EmailHTMLEditor = (props) => {
|
|
|
744
736
|
const validationResult = validateTags({
|
|
745
737
|
content: htmlContent,
|
|
746
738
|
tagsParam: tags,
|
|
747
|
-
injectedTagsParams: injectedTags,
|
|
748
739
|
location,
|
|
749
740
|
tagModule: getDefaultTags,
|
|
750
|
-
eventContextTags,
|
|
751
741
|
isFullMode,
|
|
752
742
|
});
|
|
753
743
|
|
|
754
|
-
|
|
755
|
-
if (!validationResult?.valid || (hasUnsupportedTags && !isFullMode)) {
|
|
744
|
+
if (!validationResult?.valid) {
|
|
756
745
|
setTagValidationError(validationResult);
|
|
757
|
-
//
|
|
758
|
-
// For liquid orgs, continue (extractTags API will validate)
|
|
759
|
-
if (!isLiquidEnabled) {
|
|
760
|
-
// Show notification popup like CK/BEE editor
|
|
761
|
-
const baseLanguage = get(currentOrgDetails, 'basic_details.base_language', 'en');
|
|
762
|
-
|
|
763
|
-
const contentNotValidMsg = intl.formatMessage(formBuilderMessages.contentNotValidLanguage);
|
|
764
|
-
let errorMessage = `${contentNotValidMsg} ${baseLanguage}`;
|
|
765
|
-
|
|
766
|
-
if (hasUnsupportedTags) {
|
|
767
|
-
const unsupportedTagsMsg = intl.formatMessage(formBuilderMessages.unsupportedTags);
|
|
768
|
-
errorMessage += `\n${unsupportedTagsMsg} ${validationResult?.unsupportedTags?.join(', ')}`;
|
|
769
|
-
}
|
|
770
|
-
if (validationResult?.missingTags?.length > 0) {
|
|
771
|
-
const missingTagsMsg = intl.formatMessage(formBuilderMessages.missingTags);
|
|
772
|
-
errorMessage += `\n${missingTagsMsg} ${validationResult?.missingTags?.join(', ')}`;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
const type = 'error';
|
|
776
|
-
CapNotification[type]({
|
|
777
|
-
message: `${type.toUpperCase()} ! ! ! `,
|
|
778
|
-
description: errorMessage,
|
|
779
|
-
duration: 5,
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
// Reset parent state so next click is detected as a change
|
|
783
|
-
if (onValidationFail) {
|
|
784
|
-
onValidationFail();
|
|
785
|
-
}
|
|
786
|
-
// Block save for non-liquid orgs
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
// For liquid orgs, just show warning and continue
|
|
746
|
+
// For liquid orgs, show warning and continue (extractTags API will validate)
|
|
790
747
|
}
|
|
791
748
|
// Clear tag errors if valid
|
|
792
|
-
if (tagValidationError && validationResult?.valid
|
|
749
|
+
if (tagValidationError && validationResult?.valid) {
|
|
793
750
|
setTagValidationError(null);
|
|
794
751
|
}
|
|
795
752
|
}
|
|
@@ -956,8 +913,8 @@ const EmailHTMLEditor = (props) => {
|
|
|
956
913
|
}
|
|
957
914
|
};
|
|
958
915
|
|
|
959
|
-
//
|
|
960
|
-
if (
|
|
916
|
+
// Validate first using extractTags API
|
|
917
|
+
if (getLiquidTags) {
|
|
961
918
|
// Note: API validation errors are already cleared at the start of handleSave
|
|
962
919
|
// This ensures fresh validation on every save attempt
|
|
963
920
|
|
|
@@ -998,10 +955,6 @@ const EmailHTMLEditor = (props) => {
|
|
|
998
955
|
messages: formBuilderMessages,
|
|
999
956
|
onError,
|
|
1000
957
|
onSuccess,
|
|
1001
|
-
tagLookupMap: metaEntities?.tagLookupMap,
|
|
1002
|
-
eventContextTags,
|
|
1003
|
-
isLiquidFlow: true,
|
|
1004
|
-
forwardedTags: forwardedTags || {},
|
|
1005
958
|
});
|
|
1006
959
|
} else {
|
|
1007
960
|
performSave();
|
|
@@ -1013,7 +966,6 @@ const EmailHTMLEditor = (props) => {
|
|
|
1013
966
|
injectedTags,
|
|
1014
967
|
location,
|
|
1015
968
|
getDefaultTags,
|
|
1016
|
-
eventContextTags,
|
|
1017
969
|
formatMessage,
|
|
1018
970
|
subjectError,
|
|
1019
971
|
isFullMode,
|
|
@@ -1025,11 +977,8 @@ const EmailHTMLEditor = (props) => {
|
|
|
1025
977
|
emailActions,
|
|
1026
978
|
getFormdata,
|
|
1027
979
|
isGetFormData,
|
|
1028
|
-
isLiquidEnabled,
|
|
1029
980
|
getLiquidTags,
|
|
1030
981
|
showLiquidErrorInFooter,
|
|
1031
|
-
metaEntities,
|
|
1032
|
-
forwardedTags,
|
|
1033
982
|
globalActions,
|
|
1034
983
|
intl,
|
|
1035
984
|
extractedTemplateName,
|
|
@@ -1175,7 +1124,6 @@ const EmailHTMLEditor = (props) => {
|
|
|
1175
1124
|
userLocale={intl.locale || 'en'}
|
|
1176
1125
|
moduleFilterEnabled={location?.query?.type !== EMBEDDED}
|
|
1177
1126
|
onTagContextChange={handleOnTagsContextChange}
|
|
1178
|
-
isLiquidEnabled={isLiquidEnabled}
|
|
1179
1127
|
isFullMode={isFullMode}
|
|
1180
1128
|
onErrorAcknowledged={handleErrorAcknowledged}
|
|
1181
1129
|
onValidationChange={handleValidationChange}
|