@capillarytech/creatives-library 8.0.290-alpha.3 → 8.0.290
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 +1 -0
- package/initialState.js +2 -0
- package/package.json +1 -1
- package/utils/common.js +8 -5
- package/utils/commonUtils.js +85 -4
- package/utils/tagValidations.js +222 -84
- package/utils/tests/commonUtil.test.js +124 -147
- package/utils/tests/tagValidations.test.js +358 -280
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +33 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +397 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +35 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/TECH_DETAILING_DELIVERY_SETTINGS.md +725 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +92 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +243 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +111 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +91 -0
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +33 -1
- package/v2Components/CommonTestAndPreview/actions.js +20 -0
- package/v2Components/CommonTestAndPreview/constants.js +10 -0
- package/v2Components/CommonTestAndPreview/index.js +133 -15
- package/v2Components/CommonTestAndPreview/reducer.js +47 -0
- package/v2Components/CommonTestAndPreview/sagas.js +60 -0
- package/v2Components/CommonTestAndPreview/selectors.js +51 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +782 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +200 -0
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +235 -0
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +127 -0
- package/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -0
- package/v2Components/CommonTestAndPreview/tests/index.test.js +214 -1
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +145 -0
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +162 -84
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
- package/v2Components/TestAndPreviewSlidebox/index.js +14 -0
- package/v2Containers/Cap/mockData.js +14 -0
- package/v2Containers/Cap/reducer.js +55 -3
- package/v2Containers/Cap/tests/reducer.test.js +102 -0
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -3
- package/v2Containers/CreativesContainer/index.js +6 -19
- package/v2Containers/Email/index.js +5 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +62 -10
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +115 -12
- package/v2Containers/FTP/index.js +51 -2
- package/v2Containers/FTP/messages.js +4 -0
- package/v2Containers/InApp/index.js +96 -1
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +103 -2
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +16 -6
- package/v2Containers/MobilePush/Edit/index.js +16 -6
- package/v2Containers/MobilePushNew/index.js +33 -2
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +667 -16
- package/v2Containers/Sms/Create/index.js +3 -35
- package/v2Containers/Sms/Create/messages.js +0 -4
- package/v2Containers/Sms/Edit/index.js +3 -33
- package/v2Containers/Sms/commonMethods.js +6 -6
- package/v2Containers/SmsTrai/Edit/index.js +47 -6
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +147 -6
- package/v2Containers/Viber/index.js +1 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
- package/v2Containers/WebPush/Create/index.js +2 -2
- package/v2Containers/WebPush/Create/utils/validation.js +2 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -0
- package/v2Containers/Whatsapp/index.js +18 -10
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +25849 -3524
- package/v2Containers/Zalo/index.js +11 -3
|
@@ -2,9 +2,11 @@ export const expectedStateGetLiquidTagsRequest = {
|
|
|
2
2
|
fetchingLiquidTags: true,
|
|
3
3
|
fetchingSchema: true,
|
|
4
4
|
fetchingSchemaError: "",
|
|
5
|
+
liquidTags: [],
|
|
5
6
|
messages: [],
|
|
6
7
|
metaEntities: {
|
|
7
8
|
layouts: [],
|
|
9
|
+
tagLookupMap: {},
|
|
8
10
|
tags: [],
|
|
9
11
|
},
|
|
10
12
|
orgID: "",
|
|
@@ -15,9 +17,11 @@ export const expectedStateGetLiquidTagsFailure = {
|
|
|
15
17
|
fetchingLiquidTags: false,
|
|
16
18
|
fetchingSchema: true,
|
|
17
19
|
fetchingSchemaError: "",
|
|
20
|
+
liquidTags: [],
|
|
18
21
|
messages: [],
|
|
19
22
|
metaEntities: {
|
|
20
23
|
layouts: [],
|
|
24
|
+
tagLookupMap: {},
|
|
21
25
|
tags: [],
|
|
22
26
|
},
|
|
23
27
|
orgID: "",
|
|
@@ -28,9 +32,11 @@ export const expectedStateGetLiquidTagsSuccess = {
|
|
|
28
32
|
fetchingLiquidTags: false,
|
|
29
33
|
fetchingSchema: true,
|
|
30
34
|
fetchingSchemaError: "",
|
|
35
|
+
liquidTags: [],
|
|
31
36
|
messages: [],
|
|
32
37
|
metaEntities: {
|
|
33
38
|
layouts: [],
|
|
39
|
+
tagLookupMap: {},
|
|
34
40
|
tags: [],
|
|
35
41
|
},
|
|
36
42
|
orgID: "",
|
|
@@ -41,9 +47,11 @@ export const expectedStateGetSchemaForEntitySuccessTAG = {
|
|
|
41
47
|
fetchingLiquidTags: false,
|
|
42
48
|
fetchingSchema: false,
|
|
43
49
|
fetchingSchemaError: false,
|
|
50
|
+
liquidTags: [],
|
|
44
51
|
messages: [],
|
|
45
52
|
metaEntities: {
|
|
46
53
|
layouts: undefined,
|
|
54
|
+
tagLookupMap: { undefined: { definition: {} } },
|
|
47
55
|
tags: { standard: { random: "32" } },
|
|
48
56
|
},
|
|
49
57
|
orgID: "",
|
|
@@ -54,9 +62,11 @@ export const expectedStateGetSchemaForEntitySuccess = {
|
|
|
54
62
|
fetchingLiquidTags: false,
|
|
55
63
|
fetchingSchema: false,
|
|
56
64
|
fetchingSchemaError: false,
|
|
65
|
+
liquidTags: [],
|
|
57
66
|
messages: [],
|
|
58
67
|
metaEntities: {
|
|
59
68
|
layouts: undefined,
|
|
69
|
+
tagLookupMap: undefined,
|
|
60
70
|
tags: undefined,
|
|
61
71
|
},
|
|
62
72
|
orgID: "",
|
|
@@ -68,9 +78,13 @@ export const expectedForwardedTags = {
|
|
|
68
78
|
fetchingSchema: false,
|
|
69
79
|
fetchingSchemaError: '',
|
|
70
80
|
injectedTags: undefined,
|
|
81
|
+
liquidTags: [],
|
|
71
82
|
messages: [],
|
|
72
83
|
metaEntities: {
|
|
73
84
|
layouts: [],
|
|
85
|
+
tagLookupMap: {
|
|
86
|
+
|
|
87
|
+
},
|
|
74
88
|
tags: [],
|
|
75
89
|
},
|
|
76
90
|
orgID: "",
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Created by vivek on 22/5/17.
|
|
3
3
|
*/
|
|
4
|
-
import { fromJS } from 'immutable';
|
|
4
|
+
import { fromJS, Map as ImmutableMap } 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';
|
|
9
11
|
|
|
10
12
|
function capReducer(state = fromJS(initialState.cap), action) {
|
|
11
13
|
switch (action.type) {
|
|
@@ -96,6 +98,39 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
96
98
|
return state
|
|
97
99
|
.set('fetchingLiquidTags', false);
|
|
98
100
|
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
|
+
};
|
|
99
134
|
const stateMeta = state.get("metaEntities");
|
|
100
135
|
return state
|
|
101
136
|
.set('fetchingSchema', false)
|
|
@@ -103,6 +138,7 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
103
138
|
.set('metaEntities', {
|
|
104
139
|
layouts: action.data && action.entityType === 'LAYOUT' ? action.data.metaEntities : stateMeta?.layouts,
|
|
105
140
|
tags: action.data && action.entityType === 'TAG' ? action.data.metaEntities : stateMeta?.tags,
|
|
141
|
+
tagLookupMap: action?.data && action?.entityType === TAG ? combinedTagMap : stateMeta?.tagLookupMap,
|
|
106
142
|
})
|
|
107
143
|
.set('fetchingSchemaError', false);
|
|
108
144
|
}
|
|
@@ -110,6 +146,7 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
110
146
|
return state.set('metaEntities', {
|
|
111
147
|
layouts: [],
|
|
112
148
|
tags: [],
|
|
149
|
+
tagLookupMap: {},
|
|
113
150
|
});
|
|
114
151
|
// eslint-disable-next-line no-case-declarations
|
|
115
152
|
case types.HIDE_TAGS:
|
|
@@ -117,8 +154,23 @@ function capReducer(state = fromJS(initialState.cap), action) {
|
|
|
117
154
|
metaEntities.tags.standard = _.filter(state.get('metaEntities').tags.standard, (tag) => action.tagList.indexOf(tag.definition.value) === -1);
|
|
118
155
|
metaEntities.tags.custom = _.filter(state.get('metaEntities').tags.custom, (tag) => action.tagList.indexOf(tag.name) === -1);
|
|
119
156
|
return state.setIn(['metaEntities'], metaEntities);
|
|
120
|
-
case types.SET_INJECTED_TAGS:
|
|
121
|
-
|
|
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
|
+
);
|
|
122
174
|
case types.GET_TOPBAR_MENU_DATA_REQUEST:
|
|
123
175
|
return state.set('topbarMenuData', fromJS({ status: 'request' }));
|
|
124
176
|
case types.GET_TOPBAR_MENU_DATA_SUCCESS:
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
expectedStateGetSchemaForEntitySuccessTAG,
|
|
22
22
|
expectedStateGetSchemaForEntitySuccess,
|
|
23
23
|
} from '../mockData';
|
|
24
|
+
import { TAG } from '../../Whatsapp/constants';
|
|
24
25
|
import { loadItem } from '../../../services/localStorageApi';
|
|
25
26
|
|
|
26
27
|
|
|
@@ -117,3 +118,104 @@ describe('should handle GET_SUPPORT_VIDEOS_CONFIG', () => {
|
|
|
117
118
|
expect(reducer(mockedInitialState, action).toJS())?.fetchingSchema?.toEqual(true);
|
|
118
119
|
});
|
|
119
120
|
});
|
|
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,7 +53,6 @@ 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';
|
|
57
56
|
const isEditMode = slidBoxContent === 'editTemplate';
|
|
58
57
|
|
|
59
58
|
// Use selectedEmailCreateMode for accurate mode detection in create mode (emailCreateMode is mapped for backwards compatibility)
|
|
@@ -130,9 +129,8 @@ function SlideBoxFooter(props) {
|
|
|
130
129
|
const isBEEEditorModeInCreate = !isHTMLEditorMode && !isEditMode;
|
|
131
130
|
const isBEEEditorMode = isBEEEditorModeInEdit || isBEEEditorModeInCreate;
|
|
132
131
|
const hasBEEEditorErrors = isEmailChannel && isBEEEditorMode && (hasStandardErrors || hasLiquidErrors) && (!htmlEditorValidationState || !htmlEditorHasErrors);
|
|
133
|
-
const hasSmsValidationErrors = isSmsChannel && hasStandardErrors;
|
|
134
132
|
|
|
135
|
-
const shouldShowErrorInfoNote = hasBEEEditorErrors ||
|
|
133
|
+
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
136
134
|
|
|
137
135
|
// Check for personalization tokens in title/message when anonymous user tries to save
|
|
138
136
|
const hasPersonalizationTokens = () => {
|
|
@@ -132,6 +132,7 @@ 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());
|
|
135
136
|
this.creativesTemplateSteps = {
|
|
136
137
|
1: 'modeSelection',
|
|
137
138
|
2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
|
|
@@ -255,23 +256,12 @@ export class Creatives extends React.Component {
|
|
|
255
256
|
};
|
|
256
257
|
|
|
257
258
|
onShowTemplates = () => {
|
|
258
|
-
this.setState({
|
|
259
|
-
slidBoxContent: 'templates',
|
|
260
|
-
showSlideBox: true,
|
|
261
|
-
isGetFormData: false,
|
|
262
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
263
|
-
isLiquidValidationError: false,
|
|
264
|
-
});
|
|
259
|
+
this.setState({ slidBoxContent: 'templates', showSlideBox: true, isGetFormData: false });
|
|
265
260
|
this.resetStep();
|
|
266
261
|
};
|
|
267
262
|
|
|
268
263
|
onChannelChange = (channel) => {
|
|
269
|
-
this.setState({
|
|
270
|
-
currentChannel: channel,
|
|
271
|
-
templateData: null,
|
|
272
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
273
|
-
isLiquidValidationError: false,
|
|
274
|
-
});
|
|
264
|
+
this.setState({ currentChannel: channel, templateData: null });
|
|
275
265
|
}
|
|
276
266
|
|
|
277
267
|
onCreateNextStep = () => {
|
|
@@ -1479,12 +1469,11 @@ export class Creatives extends React.Component {
|
|
|
1479
1469
|
}
|
|
1480
1470
|
|
|
1481
1471
|
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 });
|
|
1485
1472
|
if (template.validity) {
|
|
1486
1473
|
this.setState(
|
|
1487
|
-
{
|
|
1474
|
+
{
|
|
1475
|
+
isGetFormData: false,
|
|
1476
|
+
},
|
|
1488
1477
|
() => {
|
|
1489
1478
|
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1490
1479
|
const channel = templateData.type;
|
|
@@ -1568,8 +1557,6 @@ export class Creatives extends React.Component {
|
|
|
1568
1557
|
...prevState,
|
|
1569
1558
|
templateData: undefined,
|
|
1570
1559
|
showSlideBox: false,
|
|
1571
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1572
|
-
isLiquidValidationError: false,
|
|
1573
1560
|
}), () => this.props.handleCloseCreatives(reloadTemplates));
|
|
1574
1561
|
};
|
|
1575
1562
|
|
|
@@ -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, hasSupportCKEditor } from '../../utils/common';
|
|
29
|
+
import { getDecodedFileName, hasLiquidSupportFeature, 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,6 +170,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
170
170
|
deleteLanguage: this.deleteLanguage,
|
|
171
171
|
},
|
|
172
172
|
};
|
|
173
|
+
this.liquidFlow = hasLiquidSupportFeature();
|
|
173
174
|
}
|
|
174
175
|
componentWillMount() {
|
|
175
176
|
const formData = this.initFormData(this.props, true); //_.cloneDeep(this.state.formData);
|
|
@@ -254,6 +255,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
254
255
|
layout: 'EMAIL',
|
|
255
256
|
type: 'LAYOUT',
|
|
256
257
|
version: 'v2',
|
|
258
|
+
liquidFlow:this.liquidFlow,
|
|
257
259
|
};
|
|
258
260
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
259
261
|
window.addEventListener("message", this.handleFrameTasks);
|
|
@@ -345,6 +347,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
345
347
|
type: 'TAG',
|
|
346
348
|
context: this.props.location.query.type === 'embedded' ? this.props.location.query.module : 'default',
|
|
347
349
|
embedded: this.props.location.query.type === 'embedded' ? this.props.location.query.type : 'full',
|
|
350
|
+
liquidFlow:this.liquidFlow
|
|
348
351
|
};
|
|
349
352
|
if (this.props.getDefaultTags) {
|
|
350
353
|
query.context = this.props.getDefaultTags;
|
|
@@ -2370,6 +2373,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2370
2373
|
type: 'TAG',
|
|
2371
2374
|
context: (data || '').toLowerCase() === 'all' ? 'default' : (data || '').toLowerCase(),
|
|
2372
2375
|
embedded: 'full',
|
|
2376
|
+
liquidFlow:this.liquidFlow
|
|
2373
2377
|
};
|
|
2374
2378
|
this.props.globalActions.fetchSchemaForEntity(query);
|
|
2375
2379
|
}
|
|
@@ -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 { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../../utils/common';
|
|
19
19
|
import history from '../../../utils/history';
|
|
20
20
|
import messages from '../messages';
|
|
21
21
|
import emailMessages from '../../Email/messages';
|
|
@@ -108,10 +108,13 @@ const EmailHTMLEditor = (props) => {
|
|
|
108
108
|
standardErrors: [],
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
// Merge tag validation errors (missing) into apiValidationErrors so they show in ValidationErrorDisplay
|
|
111
|
+
// Merge tag validation errors (unsupported/missing) into apiValidationErrors so they show in ValidationErrorDisplay
|
|
112
112
|
const mergedApiValidationErrors = useMemo(() => {
|
|
113
113
|
const tagMessages = [];
|
|
114
|
-
if (tagValidationError?.
|
|
114
|
+
if (tagValidationError?.unsupportedTags?.length) {
|
|
115
|
+
tagMessages.push(`Unsupported tags are: ${tagValidationError.unsupportedTags.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
if (tagValidationError?.missingTags?.length && !isEmailUnsubscribeTagMandatory()) {
|
|
115
118
|
tagMessages.push(`Missing tags are: ${tagValidationError.missingTags.join(', ')}`);
|
|
116
119
|
}
|
|
117
120
|
if (tagMessages.length === 0) {
|
|
@@ -187,6 +190,9 @@ const EmailHTMLEditor = (props) => {
|
|
|
187
190
|
},
|
|
188
191
|
}), [htmlContent, subject, currentOrgDetails]);
|
|
189
192
|
|
|
193
|
+
// Check if liquid support is enabled
|
|
194
|
+
const isLiquidEnabled = hasLiquidSupportFeature();
|
|
195
|
+
|
|
190
196
|
// Detect edit mode: when isEditEmail is false (create flow), never treat as edit or fetch template details
|
|
191
197
|
const hasParamsId = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
|
|
192
198
|
const currentTemplateId = isEditEmail
|
|
@@ -487,8 +493,10 @@ const EmailHTMLEditor = (props) => {
|
|
|
487
493
|
const validationResult = validateTags({
|
|
488
494
|
content,
|
|
489
495
|
tagsParam: tags,
|
|
496
|
+
injectedTagsParams: injectedTags,
|
|
490
497
|
location,
|
|
491
498
|
tagModule: getDefaultTags,
|
|
499
|
+
eventContextTags,
|
|
492
500
|
isFullMode,
|
|
493
501
|
});
|
|
494
502
|
|
|
@@ -642,7 +650,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
642
650
|
// IMPORTANT: Clear API validation errors FIRST before checking for validation errors
|
|
643
651
|
// This ensures that old API errors don't block the save when user fixes content and clicks Update again
|
|
644
652
|
// We'll re-validate with fresh API call anyway
|
|
645
|
-
if (getLiquidTags) {
|
|
653
|
+
if (isLiquidEnabled && getLiquidTags) {
|
|
646
654
|
setApiValidationErrors({
|
|
647
655
|
liquidErrors: [],
|
|
648
656
|
standardErrors: [],
|
|
@@ -704,7 +712,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
704
712
|
// When EMAIL_UNSUBSCRIBE_TAG_MANDATORY is false: validate and require unsubscribe tag.
|
|
705
713
|
// Run for both library and full mode so liquid-enabled orgs also get the error (notification + ValidationErrorDisplay).
|
|
706
714
|
const isModuleTypeOutbound = (moduleType || '').toUpperCase() === OUTBOUND;
|
|
707
|
-
if (!
|
|
715
|
+
if (!isEmailUnsubscribeTagMandatory() && isModuleTypeOutbound) {
|
|
708
716
|
const unsubscribeRegex = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g; // eslint-disable-line no-useless-escape
|
|
709
717
|
const hasUnsubscribeTag = unsubscribeRegex.test(htmlContent);
|
|
710
718
|
|
|
@@ -736,17 +744,52 @@ const EmailHTMLEditor = (props) => {
|
|
|
736
744
|
const validationResult = validateTags({
|
|
737
745
|
content: htmlContent,
|
|
738
746
|
tagsParam: tags,
|
|
747
|
+
injectedTagsParams: injectedTags,
|
|
739
748
|
location,
|
|
740
749
|
tagModule: getDefaultTags,
|
|
750
|
+
eventContextTags,
|
|
741
751
|
isFullMode,
|
|
742
752
|
});
|
|
743
753
|
|
|
744
|
-
|
|
754
|
+
const hasUnsupportedTags = validationResult?.unsupportedTags?.length > 0;
|
|
755
|
+
if (!validationResult?.valid || (hasUnsupportedTags && !isFullMode)) {
|
|
745
756
|
setTagValidationError(validationResult);
|
|
746
|
-
// For liquid orgs,
|
|
757
|
+
// IMPORTANT: For non-liquid orgs, block save (like CK/BEE editor)
|
|
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
|
|
747
790
|
}
|
|
748
791
|
// Clear tag errors if valid
|
|
749
|
-
if (tagValidationError && validationResult?.valid) {
|
|
792
|
+
if (tagValidationError && validationResult?.valid && !hasUnsupportedTags) {
|
|
750
793
|
setTagValidationError(null);
|
|
751
794
|
}
|
|
752
795
|
}
|
|
@@ -913,8 +956,8 @@ const EmailHTMLEditor = (props) => {
|
|
|
913
956
|
}
|
|
914
957
|
};
|
|
915
958
|
|
|
916
|
-
//
|
|
917
|
-
if (getLiquidTags) {
|
|
959
|
+
// If liquid enabled, validate first using extractTags API
|
|
960
|
+
if (isLiquidEnabled && getLiquidTags) {
|
|
918
961
|
// Note: API validation errors are already cleared at the start of handleSave
|
|
919
962
|
// This ensures fresh validation on every save attempt
|
|
920
963
|
|
|
@@ -955,6 +998,10 @@ const EmailHTMLEditor = (props) => {
|
|
|
955
998
|
messages: formBuilderMessages,
|
|
956
999
|
onError,
|
|
957
1000
|
onSuccess,
|
|
1001
|
+
tagLookupMap: metaEntities?.tagLookupMap,
|
|
1002
|
+
eventContextTags,
|
|
1003
|
+
isLiquidFlow: true,
|
|
1004
|
+
forwardedTags: forwardedTags || {},
|
|
958
1005
|
});
|
|
959
1006
|
} else {
|
|
960
1007
|
performSave();
|
|
@@ -966,6 +1013,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
966
1013
|
injectedTags,
|
|
967
1014
|
location,
|
|
968
1015
|
getDefaultTags,
|
|
1016
|
+
eventContextTags,
|
|
969
1017
|
formatMessage,
|
|
970
1018
|
subjectError,
|
|
971
1019
|
isFullMode,
|
|
@@ -977,8 +1025,11 @@ const EmailHTMLEditor = (props) => {
|
|
|
977
1025
|
emailActions,
|
|
978
1026
|
getFormdata,
|
|
979
1027
|
isGetFormData,
|
|
1028
|
+
isLiquidEnabled,
|
|
980
1029
|
getLiquidTags,
|
|
981
1030
|
showLiquidErrorInFooter,
|
|
1031
|
+
metaEntities,
|
|
1032
|
+
forwardedTags,
|
|
982
1033
|
globalActions,
|
|
983
1034
|
intl,
|
|
984
1035
|
extractedTemplateName,
|
|
@@ -1124,6 +1175,7 @@ const EmailHTMLEditor = (props) => {
|
|
|
1124
1175
|
userLocale={intl.locale || 'en'}
|
|
1125
1176
|
moduleFilterEnabled={location?.query?.type !== EMBEDDED}
|
|
1126
1177
|
onTagContextChange={handleOnTagsContextChange}
|
|
1178
|
+
isLiquidEnabled={isLiquidEnabled}
|
|
1127
1179
|
isFullMode={isFullMode}
|
|
1128
1180
|
onErrorAcknowledged={handleErrorAcknowledged}
|
|
1129
1181
|
onValidationChange={handleValidationChange}
|