@capillarytech/creatives-library 8.0.309 → 8.0.310
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 -5
- package/initialState.js +2 -0
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/common.js +8 -5
- package/utils/commonUtils.js +93 -46
- package/utils/tagValidations.js +223 -83
- package/utils/tests/commonUtil.test.js +124 -316
- package/utils/tests/tagValidations.test.js +358 -441
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -15
- package/v2Components/CommonTestAndPreview/index.js +19 -80
- package/v2Components/CommonTestAndPreview/messages.js +0 -94
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/ErrorInfoNote/index.js +5 -2
- package/v2Components/FormBuilder/index.js +203 -137
- 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/SlideBoxContent.js +1 -5
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
- package/v2Containers/CreativesContainer/constants.js +0 -6
- package/v2Containers/CreativesContainer/index.js +7 -47
- package/v2Containers/Email/index.js +5 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +120 -20
- package/v2Containers/FTP/index.js +51 -2
- package/v2Containers/FTP/messages.js +4 -0
- package/v2Containers/InApp/index.js +107 -35
- package/v2Containers/InApp/tests/index.test.js +6 -17
- package/v2Containers/InappAdvance/index.js +112 -4
- package/v2Containers/InappAdvance/tests/index.test.js +0 -2
- package/v2Containers/Line/Container/Text/index.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +19 -59
- package/v2Containers/MobilePush/Edit/index.js +20 -48
- package/v2Containers/MobilePushNew/index.js +32 -12
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +37 -12
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
- package/v2Containers/Sms/Create/index.js +3 -39
- package/v2Containers/Sms/Create/messages.js +0 -4
- package/v2Containers/Sms/Edit/index.js +3 -35
- package/v2Containers/Sms/commonMethods.js +6 -3
- package/v2Containers/SmsTrai/Edit/index.js +47 -11
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +294 -327
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/TemplatesV2/index.js +13 -28
- 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 +8 -17
- package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
- package/v2Containers/Whatsapp/index.js +17 -9
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
- package/v2Containers/Zalo/index.js +11 -3
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
|
@@ -3202,6 +3202,7 @@ describe('HTMLEditor', () => {
|
|
|
3202
3202
|
onTagSelect={onTagSelect}
|
|
3203
3203
|
onContextChange={onContextChange}
|
|
3204
3204
|
globalActions={globalActions}
|
|
3205
|
+
isLiquidEnabled={true}
|
|
3205
3206
|
isFullMode={false}
|
|
3206
3207
|
onErrorAcknowledged={onErrorAcknowledged}
|
|
3207
3208
|
onValidationChange={onValidationChange}
|
|
@@ -3261,6 +3262,20 @@ describe('HTMLEditor', () => {
|
|
|
3261
3262
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3262
3263
|
});
|
|
3263
3264
|
|
|
3265
|
+
it('should handle isLiquidEnabled prop', () => {
|
|
3266
|
+
render(
|
|
3267
|
+
<TestWrapper>
|
|
3268
|
+
<HTMLEditor isLiquidEnabled={true} />
|
|
3269
|
+
</TestWrapper>
|
|
3270
|
+
);
|
|
3271
|
+
|
|
3272
|
+
act(() => {
|
|
3273
|
+
jest.runAllTimers();
|
|
3274
|
+
});
|
|
3275
|
+
|
|
3276
|
+
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3277
|
+
});
|
|
3278
|
+
|
|
3264
3279
|
it('should handle isFullMode prop', () => {
|
|
3265
3280
|
render(
|
|
3266
3281
|
<TestWrapper>
|
|
@@ -69,7 +69,7 @@ const CodeEditorPaneComponent = ({
|
|
|
69
69
|
}) => {
|
|
70
70
|
const context = useEditorContext();
|
|
71
71
|
const {
|
|
72
|
-
content, validation, variant,
|
|
72
|
+
content, validation, variant, isLiquidEnabled,
|
|
73
73
|
} = context || {};
|
|
74
74
|
const { content: contentValue, updateContent } = content || {};
|
|
75
75
|
const editorRef = useRef(null);
|
|
@@ -298,6 +298,7 @@ const CodeEditorPaneComponent = ({
|
|
|
298
298
|
<ValidationErrorDisplay
|
|
299
299
|
validation={validation}
|
|
300
300
|
onErrorClick={onErrorClick}
|
|
301
|
+
isLiquidEnabled={isLiquidEnabled}
|
|
301
302
|
className="code-editor-pane__validation"
|
|
302
303
|
/>
|
|
303
304
|
</div>
|
|
@@ -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
|
+
});
|
|
@@ -566,7 +566,6 @@ export function SlideBoxContent(props) {
|
|
|
566
566
|
handleTestAndPreview={handleTestAndPreview}
|
|
567
567
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
568
568
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
569
|
-
onValidationFail={onValidationFail}
|
|
570
569
|
/>
|
|
571
570
|
)}
|
|
572
571
|
{isEditFTP && (
|
|
@@ -633,7 +632,6 @@ export function SlideBoxContent(props) {
|
|
|
633
632
|
getLiquidTags={getLiquidTags}
|
|
634
633
|
getDefaultTags={type}
|
|
635
634
|
isFullMode={isFullMode}
|
|
636
|
-
onValidationFail={onValidationFail}
|
|
637
635
|
forwardedTags={forwardedTags}
|
|
638
636
|
selectedOfferDetails={selectedOfferDetails}
|
|
639
637
|
onPreviewContentClicked={onPreviewContentClicked}
|
|
@@ -782,7 +780,6 @@ export function SlideBoxContent(props) {
|
|
|
782
780
|
<MobliPushEdit
|
|
783
781
|
getFormLibraryData={getFormData}
|
|
784
782
|
setIsLoadingContent={setIsLoadingContent}
|
|
785
|
-
getLiquidTags={getLiquidTags}
|
|
786
783
|
location={{
|
|
787
784
|
pathname: `/mobilepush/edit/`,
|
|
788
785
|
query,
|
|
@@ -855,7 +852,6 @@ export function SlideBoxContent(props) {
|
|
|
855
852
|
mobilePushCreateMode={mobilePushCreateMode}
|
|
856
853
|
isGetFormData={isGetFormData}
|
|
857
854
|
getFormData={getFormData}
|
|
858
|
-
getLiquidTags={getLiquidTags}
|
|
859
855
|
templateData={templateData}
|
|
860
856
|
type={type}
|
|
861
857
|
step={templateStep}
|
|
@@ -884,7 +880,7 @@ export function SlideBoxContent(props) {
|
|
|
884
880
|
/>
|
|
885
881
|
) : (
|
|
886
882
|
<MobilePushNew
|
|
887
|
-
key="creatives-mobilepush-
|
|
883
|
+
key="creatives-mobilepush-wrapper"
|
|
888
884
|
date={new Date().getMilliseconds()}
|
|
889
885
|
setIsLoadingContent={setIsLoadingContent}
|
|
890
886
|
onMobilepushModeChange={onMobilepushModeChange}
|
|
@@ -6,7 +6,7 @@ import CapError from '@capillarytech/cap-ui-library/CapError';
|
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import messages from './messages';
|
|
8
8
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
9
|
-
import { PREVIEW
|
|
9
|
+
import { PREVIEW } from './constants';
|
|
10
10
|
import { EMAIL_CREATE_MODES } from '../EmailWrapper/constants';
|
|
11
11
|
import { hasSupportCKEditor } from '../../utils/common';
|
|
12
12
|
import { getMessageForDevice, getTitleForDevice } from '../../utils/commonUtils';
|
|
@@ -15,7 +15,7 @@ function getFullModeSaveBtn(slidBoxContent, isCreatingTemplate) {
|
|
|
15
15
|
if (isCreatingTemplate) {
|
|
16
16
|
return <FormattedMessage {...messages.creativesTemplatesDone} />;
|
|
17
17
|
}
|
|
18
|
-
return slidBoxContent ===
|
|
18
|
+
return slidBoxContent === "editTemplate"
|
|
19
19
|
? <FormattedMessage {...messages.creativesTemplatesUpdate} />
|
|
20
20
|
: <FormattedMessage {...messages.creativesTemplatesSaveFullMode} />;
|
|
21
21
|
}
|
|
@@ -52,13 +52,8 @@ function SlideBoxFooter(props) {
|
|
|
52
52
|
// Calculate if buttons should be disabled
|
|
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
|
-
const isEmailChannel = currentChannel?.toUpperCase() === EMAIL;
|
|
56
|
-
const
|
|
57
|
-
// Use templateData.type in library/edit so footer shows when editing a mobile push template (currentChannel may not be set to template channel)
|
|
58
|
-
const isMobilePushChannel =
|
|
59
|
-
currentChannel?.toUpperCase() === MOBILE_PUSH ||
|
|
60
|
-
(templateData?.type && templateData.type.toUpperCase() === MOBILE_PUSH);
|
|
61
|
-
const isEditMode = slidBoxContent === EDIT_TEMPLATE;
|
|
55
|
+
const isEmailChannel = currentChannel?.toUpperCase() === 'EMAIL';
|
|
56
|
+
const isEditMode = slidBoxContent === 'editTemplate';
|
|
62
57
|
|
|
63
58
|
// Use selectedEmailCreateMode for accurate mode detection in create mode (emailCreateMode is mapped for backwards compatibility)
|
|
64
59
|
// In edit mode: htmlEditorValidationState is initialized as {} but only updated by HTML Editor
|
|
@@ -134,11 +129,8 @@ function SlideBoxFooter(props) {
|
|
|
134
129
|
const isBEEEditorModeInCreate = !isHTMLEditorMode && !isEditMode;
|
|
135
130
|
const isBEEEditorMode = isBEEEditorModeInEdit || isBEEEditorModeInCreate;
|
|
136
131
|
const hasBEEEditorErrors = isEmailChannel && isBEEEditorMode && (hasStandardErrors || hasLiquidErrors) && (!htmlEditorValidationState || !htmlEditorHasErrors);
|
|
137
|
-
const hasSmsValidationErrors = isSmsChannel && (hasStandardErrors || hasLiquidErrors);
|
|
138
|
-
// Mobile Push OLD: footer only for extractTags/Aira liquid errors, not standard tag errors
|
|
139
|
-
const hasMobilePushValidationErrors = isMobilePushChannel && hasLiquidErrors;
|
|
140
132
|
|
|
141
|
-
const shouldShowErrorInfoNote = hasBEEEditorErrors ||
|
|
133
|
+
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
142
134
|
|
|
143
135
|
// Check for personalization tokens in title/message when anonymous user tries to save
|
|
144
136
|
const hasPersonalizationTokens = () => {
|
|
@@ -52,12 +52,6 @@ export const GENERIC = "GENERIC";
|
|
|
52
52
|
export const LIQUID_ERROR_MSG = "LIQUID_ERROR_MSG";
|
|
53
53
|
export const STANDARD_ERROR_MSG = "STANDARD_ERROR_MSG";
|
|
54
54
|
export const COMMON_CHANNELS = ['sms', 'email', 'wechat', 'mobilepush', 'webpush', 'line', 'viber', 'facebook', 'call_task', 'ftp', 'assets'];
|
|
55
|
-
|
|
56
|
-
/** Normalized channel forms (e.g. from camelCase) that do not match pane keys; maps to canonical pane key for hide/disable. */
|
|
57
|
-
export const NORMALIZED_CHANNEL_ALIASES = {
|
|
58
|
-
we_chat: WECHAT.toLowerCase(),
|
|
59
|
-
m_push: MOBILE_PUSH.toLowerCase(),
|
|
60
|
-
};
|
|
61
55
|
export const MIXED = "MIXED";
|
|
62
56
|
export const VISITOR = "VISITOR";
|
|
63
57
|
export const ALLOWED_CHANNELS_FOR_ANONYMOUS = ['mobilepush', 'webpush'];
|
|
@@ -69,23 +69,6 @@ import {
|
|
|
69
69
|
import { MANUAL_CAROUSEL } from '../MobilePushNew/constants';
|
|
70
70
|
import { BIG_HTML } from '../InApp/constants';
|
|
71
71
|
|
|
72
|
-
/**
|
|
73
|
-
* Returns true if value is "deep empty": no errors present.
|
|
74
|
-
* - null/undefined: empty
|
|
75
|
-
* - string: empty if length === 0
|
|
76
|
-
* - array: empty if length === 0
|
|
77
|
-
* - plain object (e.g. { android: [], ios: [], generic: [] }): empty only if every value is deep-empty
|
|
78
|
-
*/
|
|
79
|
-
function isDeepEmpty(value) {
|
|
80
|
-
if (value == null) return true;
|
|
81
|
-
if (typeof value === 'string') return value.length === 0;
|
|
82
|
-
if (Array.isArray(value)) return value.length === 0;
|
|
83
|
-
if (typeof value === 'object') {
|
|
84
|
-
return Object.values(value).every(isDeepEmpty);
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
72
|
const classPrefix = 'add-creatives-section';
|
|
90
73
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
91
74
|
|
|
@@ -149,6 +132,7 @@ export class Creatives extends React.Component {
|
|
|
149
132
|
},
|
|
150
133
|
hasPersonalizationTokenError: false, // Track personalization token errors in form
|
|
151
134
|
};
|
|
135
|
+
this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
|
|
152
136
|
this.creativesTemplateSteps = {
|
|
153
137
|
1: 'modeSelection',
|
|
154
138
|
2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
|
|
@@ -272,23 +256,12 @@ export class Creatives extends React.Component {
|
|
|
272
256
|
};
|
|
273
257
|
|
|
274
258
|
onShowTemplates = () => {
|
|
275
|
-
this.setState({
|
|
276
|
-
slidBoxContent: 'templates',
|
|
277
|
-
showSlideBox: true,
|
|
278
|
-
isGetFormData: false,
|
|
279
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
280
|
-
isLiquidValidationError: false,
|
|
281
|
-
});
|
|
259
|
+
this.setState({ slidBoxContent: 'templates', showSlideBox: true, isGetFormData: false });
|
|
282
260
|
this.resetStep();
|
|
283
261
|
};
|
|
284
262
|
|
|
285
263
|
onChannelChange = (channel) => {
|
|
286
|
-
this.setState({
|
|
287
|
-
currentChannel: channel,
|
|
288
|
-
templateData: null,
|
|
289
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
290
|
-
isLiquidValidationError: false,
|
|
291
|
-
});
|
|
264
|
+
this.setState({ currentChannel: channel, templateData: null });
|
|
292
265
|
}
|
|
293
266
|
|
|
294
267
|
onCreateNextStep = () => {
|
|
@@ -1496,12 +1469,11 @@ export class Creatives extends React.Component {
|
|
|
1496
1469
|
}
|
|
1497
1470
|
|
|
1498
1471
|
getFormData = (template) => {
|
|
1499
|
-
// Always reset isGetFormData so the child does not re-send form data on every re-render
|
|
1500
|
-
// (e.g. when user fixes validation error by typing, we must not auto-close the slidebox)
|
|
1501
|
-
this.setState({ isGetFormData: false });
|
|
1502
1472
|
if (template.validity) {
|
|
1503
1473
|
this.setState(
|
|
1504
|
-
{
|
|
1474
|
+
{
|
|
1475
|
+
isGetFormData: false,
|
|
1476
|
+
},
|
|
1505
1477
|
() => {
|
|
1506
1478
|
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1507
1479
|
const channel = templateData.type;
|
|
@@ -1585,8 +1557,6 @@ export class Creatives extends React.Component {
|
|
|
1585
1557
|
...prevState,
|
|
1586
1558
|
templateData: undefined,
|
|
1587
1559
|
showSlideBox: false,
|
|
1588
|
-
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1589
|
-
isLiquidValidationError: false,
|
|
1590
1560
|
}), () => this.props.handleCloseCreatives(reloadTemplates));
|
|
1591
1561
|
};
|
|
1592
1562
|
|
|
@@ -1794,18 +1764,8 @@ export class Creatives extends React.Component {
|
|
|
1794
1764
|
}
|
|
1795
1765
|
|
|
1796
1766
|
showLiquidErrorInFooter = (errorMessagesFromFormBuilder, currentFormBuilderTab) => {
|
|
1797
|
-
const liquidMsgs = get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, []);
|
|
1798
|
-
const standardMsgs = get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, []);
|
|
1799
|
-
const hasLiquid = !isDeepEmpty(liquidMsgs);
|
|
1800
|
-
const hasStandard = !isDeepEmpty(standardMsgs);
|
|
1801
|
-
const isLiquidValidationError = hasLiquid || hasStandard;
|
|
1802
|
-
// Don't overwrite existing liquid error with empty only for Mobile Push OLD (FormBuilder/clear calls empty there); SMS/others clear on input change
|
|
1803
|
-
const isMobilePush = this.state.currentChannel?.toUpperCase() === constants.MOBILE_PUSH;
|
|
1804
|
-
if (!hasLiquid && !hasStandard && this.state.isLiquidValidationError && isMobilePush) {
|
|
1805
|
-
return;
|
|
1806
|
-
}
|
|
1807
1767
|
this.setState({
|
|
1808
|
-
isLiquidValidationError,
|
|
1768
|
+
isLiquidValidationError: !isEmpty(get(errorMessagesFromFormBuilder, constants.LIQUID_ERROR_MSG, [])) || !isEmpty(get(errorMessagesFromFormBuilder, constants.STANDARD_ERROR_MSG, [])),
|
|
1809
1769
|
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1810
1770
|
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1811
1771
|
});
|
|
@@ -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
|
}
|