@capillarytech/creatives-library 8.0.308 → 8.0.309
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 +5 -1
- package/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +5 -8
- package/utils/commonUtils.js +36 -93
- package/utils/tagValidations.js +83 -223
- package/utils/tests/commonUtil.test.js +147 -124
- package/utils/tests/tagValidations.test.js +441 -358
- package/v2Components/ErrorInfoNote/index.js +2 -5
- package/v2Components/FormBuilder/index.js +137 -203
- 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/SlideBoxContent.js +5 -1
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -5
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +47 -7
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +23 -70
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -120
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/index.js +35 -107
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InappAdvance/index.js +4 -112
- package/v2Containers/InappAdvance/tests/index.test.js +2 -0
- package/v2Containers/Line/Container/Text/index.js +0 -1
- package/v2Containers/MobilePush/Create/index.js +59 -19
- package/v2Containers/MobilePush/Edit/index.js +48 -20
- package/v2Containers/MobilePushNew/index.js +12 -32
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/index.js +12 -37
- package/v2Containers/Sms/Create/index.js +39 -3
- package/v2Containers/Sms/Create/messages.js +4 -0
- package/v2Containers/Sms/Edit/index.js +35 -3
- package/v2Containers/Sms/commonMethods.js +3 -6
- package/v2Containers/Sms/tests/commonMethods.test.js +122 -0
- package/v2Containers/SmsTrai/Edit/index.js +11 -47
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/SmsWrapper/index.js +2 -0
- package/v2Containers/TemplatesV2/index.js +28 -13
- package/v2Containers/Viber/index.js +0 -1
- 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/utils/validation.js +17 -8
- package/v2Containers/WebPush/Create/utils/validation.test.js +44 -24
- package/v2Containers/Whatsapp/index.js +9 -17
- package/v2Containers/Zalo/index.js +3 -11
|
@@ -102,7 +102,6 @@ const HTMLEditor = forwardRef(({
|
|
|
102
102
|
onTagSelect = null,
|
|
103
103
|
onContextChange = null,
|
|
104
104
|
globalActions = null,
|
|
105
|
-
isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
|
|
106
105
|
isFullMode = true, // Full mode vs library mode - controls layout and visibility
|
|
107
106
|
onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
|
|
108
107
|
onValidationChange = null, // Callback when validation state changes (for parent to track errors)
|
|
@@ -574,7 +573,6 @@ const HTMLEditor = forwardRef(({
|
|
|
574
573
|
content,
|
|
575
574
|
layout,
|
|
576
575
|
validation,
|
|
577
|
-
isLiquidEnabled,
|
|
578
576
|
editorRef: getActiveEditorRef(),
|
|
579
577
|
handleLabelInsert,
|
|
580
578
|
handleSave,
|
|
@@ -594,7 +592,6 @@ const HTMLEditor = forwardRef(({
|
|
|
594
592
|
content,
|
|
595
593
|
layout,
|
|
596
594
|
validation,
|
|
597
|
-
isLiquidEnabled,
|
|
598
595
|
getActiveEditorRef,
|
|
599
596
|
handleLabelInsert,
|
|
600
597
|
handleSave,
|
|
@@ -783,7 +780,6 @@ HTMLEditor.propTypes = {
|
|
|
783
780
|
onTagSelect: PropTypes.func,
|
|
784
781
|
onContextChange: PropTypes.func, // Deprecated: use globalActions instead
|
|
785
782
|
globalActions: PropTypes.object,
|
|
786
|
-
isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
|
|
787
783
|
isFullMode: PropTypes.bool, // Full mode vs library mode
|
|
788
784
|
onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
|
|
789
785
|
onValidationChange: PropTypes.func, // Callback when validation state changes
|
|
@@ -817,7 +813,6 @@ HTMLEditor.defaultProps = {
|
|
|
817
813
|
onTagSelect: null,
|
|
818
814
|
onContextChange: null,
|
|
819
815
|
globalActions: null, // Redux actions for API calls
|
|
820
|
-
isLiquidEnabled: false,
|
|
821
816
|
isFullMode: true, // Default to full mode
|
|
822
817
|
onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
|
|
823
818
|
onValidationChange: null, // Callback when validation state changes
|
|
@@ -3202,7 +3202,6 @@ describe('HTMLEditor', () => {
|
|
|
3202
3202
|
onTagSelect={onTagSelect}
|
|
3203
3203
|
onContextChange={onContextChange}
|
|
3204
3204
|
globalActions={globalActions}
|
|
3205
|
-
isLiquidEnabled={true}
|
|
3206
3205
|
isFullMode={false}
|
|
3207
3206
|
onErrorAcknowledged={onErrorAcknowledged}
|
|
3208
3207
|
onValidationChange={onValidationChange}
|
|
@@ -3262,20 +3261,6 @@ describe('HTMLEditor', () => {
|
|
|
3262
3261
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3263
3262
|
});
|
|
3264
3263
|
|
|
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
|
-
|
|
3279
3264
|
it('should handle isFullMode prop', () => {
|
|
3280
3265
|
render(
|
|
3281
3266
|
<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,
|
|
73
73
|
} = context || {};
|
|
74
74
|
const { content: contentValue, updateContent } = content || {};
|
|
75
75
|
const editorRef = useRef(null);
|
|
@@ -298,7 +298,6 @@ const CodeEditorPaneComponent = ({
|
|
|
298
298
|
<ValidationErrorDisplay
|
|
299
299
|
validation={validation}
|
|
300
300
|
onErrorClick={onErrorClick}
|
|
301
|
-
isLiquidEnabled={isLiquidEnabled}
|
|
302
301
|
className="code-editor-pane__validation"
|
|
303
302
|
/>
|
|
304
303
|
</div>
|
|
@@ -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
|
-
});
|
|
@@ -566,6 +566,7 @@ export function SlideBoxContent(props) {
|
|
|
566
566
|
handleTestAndPreview={handleTestAndPreview}
|
|
567
567
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
568
568
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
569
|
+
onValidationFail={onValidationFail}
|
|
569
570
|
/>
|
|
570
571
|
)}
|
|
571
572
|
{isEditFTP && (
|
|
@@ -632,6 +633,7 @@ export function SlideBoxContent(props) {
|
|
|
632
633
|
getLiquidTags={getLiquidTags}
|
|
633
634
|
getDefaultTags={type}
|
|
634
635
|
isFullMode={isFullMode}
|
|
636
|
+
onValidationFail={onValidationFail}
|
|
635
637
|
forwardedTags={forwardedTags}
|
|
636
638
|
selectedOfferDetails={selectedOfferDetails}
|
|
637
639
|
onPreviewContentClicked={onPreviewContentClicked}
|
|
@@ -780,6 +782,7 @@ export function SlideBoxContent(props) {
|
|
|
780
782
|
<MobliPushEdit
|
|
781
783
|
getFormLibraryData={getFormData}
|
|
782
784
|
setIsLoadingContent={setIsLoadingContent}
|
|
785
|
+
getLiquidTags={getLiquidTags}
|
|
783
786
|
location={{
|
|
784
787
|
pathname: `/mobilepush/edit/`,
|
|
785
788
|
query,
|
|
@@ -852,6 +855,7 @@ export function SlideBoxContent(props) {
|
|
|
852
855
|
mobilePushCreateMode={mobilePushCreateMode}
|
|
853
856
|
isGetFormData={isGetFormData}
|
|
854
857
|
getFormData={getFormData}
|
|
858
|
+
getLiquidTags={getLiquidTags}
|
|
855
859
|
templateData={templateData}
|
|
856
860
|
type={type}
|
|
857
861
|
step={templateStep}
|
|
@@ -880,7 +884,7 @@ export function SlideBoxContent(props) {
|
|
|
880
884
|
/>
|
|
881
885
|
) : (
|
|
882
886
|
<MobilePushNew
|
|
883
|
-
key="creatives-mobilepush-
|
|
887
|
+
key="creatives-mobilepush-create-new"
|
|
884
888
|
date={new Date().getMilliseconds()}
|
|
885
889
|
setIsLoadingContent={setIsLoadingContent}
|
|
886
890
|
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 } from './constants';
|
|
9
|
+
import { PREVIEW, EMAIL, SMS, EDIT_TEMPLATE, MOBILE_PUSH } 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 === EDIT_TEMPLATE
|
|
19
19
|
? <FormattedMessage {...messages.creativesTemplatesUpdate} />
|
|
20
20
|
: <FormattedMessage {...messages.creativesTemplatesSaveFullMode} />;
|
|
21
21
|
}
|
|
@@ -52,8 +52,13 @@ 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() ===
|
|
56
|
-
const
|
|
55
|
+
const isEmailChannel = currentChannel?.toUpperCase() === EMAIL;
|
|
56
|
+
const isSmsChannel = currentChannel?.toUpperCase() === SMS;
|
|
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;
|
|
57
62
|
|
|
58
63
|
// Use selectedEmailCreateMode for accurate mode detection in create mode (emailCreateMode is mapped for backwards compatibility)
|
|
59
64
|
// In edit mode: htmlEditorValidationState is initialized as {} but only updated by HTML Editor
|
|
@@ -129,8 +134,11 @@ function SlideBoxFooter(props) {
|
|
|
129
134
|
const isBEEEditorModeInCreate = !isHTMLEditorMode && !isEditMode;
|
|
130
135
|
const isBEEEditorMode = isBEEEditorModeInEdit || isBEEEditorModeInCreate;
|
|
131
136
|
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;
|
|
132
140
|
|
|
133
|
-
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
141
|
+
const shouldShowErrorInfoNote = hasBEEEditorErrors || hasSmsValidationErrors || hasMobilePushValidationErrors || isSupportCKEditor;
|
|
134
142
|
|
|
135
143
|
// Check for personalization tokens in title/message when anonymous user tries to save
|
|
136
144
|
const hasPersonalizationTokens = () => {
|
|
@@ -52,6 +52,12 @@ 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
|
+
};
|
|
55
61
|
export const MIXED = "MIXED";
|
|
56
62
|
export const VISITOR = "VISITOR";
|
|
57
63
|
export const ALLOWED_CHANNELS_FOR_ANONYMOUS = ['mobilepush', 'webpush'];
|
|
@@ -69,6 +69,23 @@ 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
|
+
|
|
72
89
|
const classPrefix = 'add-creatives-section';
|
|
73
90
|
const CREATIVES_CONTAINER = 'creativesContainer';
|
|
74
91
|
|
|
@@ -132,7 +149,6 @@ export class Creatives extends React.Component {
|
|
|
132
149
|
},
|
|
133
150
|
hasPersonalizationTokenError: false, // Track personalization token errors in form
|
|
134
151
|
};
|
|
135
|
-
this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
|
|
136
152
|
this.creativesTemplateSteps = {
|
|
137
153
|
1: 'modeSelection',
|
|
138
154
|
2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
|
|
@@ -256,12 +272,23 @@ export class Creatives extends React.Component {
|
|
|
256
272
|
};
|
|
257
273
|
|
|
258
274
|
onShowTemplates = () => {
|
|
259
|
-
this.setState({
|
|
275
|
+
this.setState({
|
|
276
|
+
slidBoxContent: 'templates',
|
|
277
|
+
showSlideBox: true,
|
|
278
|
+
isGetFormData: false,
|
|
279
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
280
|
+
isLiquidValidationError: false,
|
|
281
|
+
});
|
|
260
282
|
this.resetStep();
|
|
261
283
|
};
|
|
262
284
|
|
|
263
285
|
onChannelChange = (channel) => {
|
|
264
|
-
this.setState({
|
|
286
|
+
this.setState({
|
|
287
|
+
currentChannel: channel,
|
|
288
|
+
templateData: null,
|
|
289
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
290
|
+
isLiquidValidationError: false,
|
|
291
|
+
});
|
|
265
292
|
}
|
|
266
293
|
|
|
267
294
|
onCreateNextStep = () => {
|
|
@@ -1469,11 +1496,12 @@ export class Creatives extends React.Component {
|
|
|
1469
1496
|
}
|
|
1470
1497
|
|
|
1471
1498
|
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 });
|
|
1472
1502
|
if (template.validity) {
|
|
1473
1503
|
this.setState(
|
|
1474
|
-
{
|
|
1475
|
-
isGetFormData: false,
|
|
1476
|
-
},
|
|
1504
|
+
{},
|
|
1477
1505
|
() => {
|
|
1478
1506
|
const templateData = this.state.templateData ? this.state.templateData : template; //select existing or create new content
|
|
1479
1507
|
const channel = templateData.type;
|
|
@@ -1557,6 +1585,8 @@ export class Creatives extends React.Component {
|
|
|
1557
1585
|
...prevState,
|
|
1558
1586
|
templateData: undefined,
|
|
1559
1587
|
showSlideBox: false,
|
|
1588
|
+
liquidErrorMessage: { STANDARD_ERROR_MSG: [], LIQUID_ERROR_MSG: [] },
|
|
1589
|
+
isLiquidValidationError: false,
|
|
1560
1590
|
}), () => this.props.handleCloseCreatives(reloadTemplates));
|
|
1561
1591
|
};
|
|
1562
1592
|
|
|
@@ -1764,8 +1794,18 @@ export class Creatives extends React.Component {
|
|
|
1764
1794
|
}
|
|
1765
1795
|
|
|
1766
1796
|
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
|
+
}
|
|
1767
1807
|
this.setState({
|
|
1768
|
-
isLiquidValidationError
|
|
1808
|
+
isLiquidValidationError,
|
|
1769
1809
|
liquidErrorMessage: errorMessagesFromFormBuilder,
|
|
1770
1810
|
activeFormBuilderTab: currentFormBuilderTab === 1 ? constants.ANDROID : (currentFormBuilderTab === 2 ? constants.IOS : null), // Update activeFormBuilderTab, default to 1 if undefined
|
|
1771
1811
|
});
|
|
@@ -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
|
}
|