@capillarytech/creatives-library 8.0.285-alpha.1 → 8.0.286
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 +83 -2
- package/utils/tagValidations.js +222 -84
- package/utils/tests/commonUtil.test.js +118 -147
- package/utils/tests/tagValidations.test.js +358 -280
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +158 -64
- 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/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/index.js +1 -0
- 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/MobilePushNew/index.js +33 -2
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +18 -4
- package/v2Containers/SmsTrai/Create/index.scss +1 -1
- package/v2Containers/SmsTrai/Edit/index.js +47 -6
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/Viber/index.js +1 -0
- package/v2Containers/Viber/index.scss +1 -1
- 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 +9 -18
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -0
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +624 -248
- 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
|
+
});
|
|
@@ -131,6 +131,7 @@ export class Creatives extends React.Component {
|
|
|
131
131
|
errorsAcknowledged: false, // Flag to track if user has acknowledged errors by clicking redirection icon
|
|
132
132
|
},
|
|
133
133
|
};
|
|
134
|
+
this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
|
|
134
135
|
this.creativesTemplateSteps = {
|
|
135
136
|
1: 'modeSelection',
|
|
136
137
|
2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
|
|
@@ -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}
|