@capillarytech/creatives-library 8.0.304 → 8.0.305-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +4 -1
- package/initialState.js +0 -2
- package/package.json +1 -1
- package/utils/common.js +12 -9
- 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/CapDeviceContent/index.js +10 -7
- 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/BeePopupEditor/index.js +9 -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 +40 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -5
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +47 -7
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +121 -4
- package/v2Containers/Email/index.js +1 -5
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +23 -70
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +29 -137
- package/v2Containers/FTP/index.js +2 -51
- package/v2Containers/FTP/messages.js +0 -4
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
- package/v2Containers/InApp/index.js +216 -120
- package/v2Containers/InApp/tests/index.test.js +17 -6
- package/v2Containers/InApp/tests/mockData.js +1 -1
- package/v2Containers/InappAdvance/index.js +6 -110
- 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
|
@@ -3201,7 +3201,6 @@ describe('HTMLEditor', () => {
|
|
|
3201
3201
|
onTagSelect={onTagSelect}
|
|
3202
3202
|
onContextChange={onContextChange}
|
|
3203
3203
|
globalActions={globalActions}
|
|
3204
|
-
isLiquidEnabled={true}
|
|
3205
3204
|
isFullMode={false}
|
|
3206
3205
|
onErrorAcknowledged={onErrorAcknowledged}
|
|
3207
3206
|
onValidationChange={onValidationChange}
|
|
@@ -3261,20 +3260,6 @@ describe('HTMLEditor', () => {
|
|
|
3261
3260
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
3262
3261
|
});
|
|
3263
3262
|
|
|
3264
|
-
it('should handle isLiquidEnabled prop', () => {
|
|
3265
|
-
render(
|
|
3266
|
-
<TestWrapper>
|
|
3267
|
-
<HTMLEditor isLiquidEnabled={true} />
|
|
3268
|
-
</TestWrapper>
|
|
3269
|
-
);
|
|
3270
|
-
|
|
3271
|
-
act(() => {
|
|
3272
|
-
jest.runAllTimers();
|
|
3273
|
-
});
|
|
3274
|
-
|
|
3275
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3276
|
-
});
|
|
3277
|
-
|
|
3278
3263
|
it('should handle isFullMode prop', () => {
|
|
3279
3264
|
render(
|
|
3280
3265
|
<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>
|
|
@@ -40,6 +40,11 @@ function BeePopupEditor(props) {
|
|
|
40
40
|
const savedCallback = useRef();
|
|
41
41
|
const beeInstanceRef = useRef(null);
|
|
42
42
|
const isInitializedRef = useRef(false);
|
|
43
|
+
const beeJsonRef = useRef(beeJson);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
beeJsonRef.current = beeJson;
|
|
47
|
+
}, [beeJson]);
|
|
43
48
|
|
|
44
49
|
const [visibleTaglist, setVisibleTaglist] = useState(false);
|
|
45
50
|
const [selectedTag, setSelectedTag] = useState({});
|
|
@@ -111,8 +116,10 @@ function BeePopupEditor(props) {
|
|
|
111
116
|
window.BeePlugin.create(tokenData, beeConfig, (instance) => {
|
|
112
117
|
beePluginInstance = instance;
|
|
113
118
|
beeInstanceRef.current = instance;
|
|
114
|
-
//
|
|
115
|
-
|
|
119
|
+
// Use ref to get the latest beeJson — the closure captures the value at effect time,
|
|
120
|
+
// but beeJsonRef.current is always up-to-date (e.g. when template data loads async)
|
|
121
|
+
const latestBeeJson = beeJsonRef.current;
|
|
122
|
+
const parseJson = typeof latestBeeJson === 'string' ? JSON.parse(latestBeeJson) : latestBeeJson;
|
|
116
123
|
beePluginInstance.start(parseJson);
|
|
117
124
|
saveBeeInstance(beePluginInstance, device);
|
|
118
125
|
isInitializedRef.current = true;
|
|
@@ -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}
|
|
@@ -1078,8 +1082,38 @@ export function SlideBoxContent(props) {
|
|
|
1078
1082
|
)}
|
|
1079
1083
|
|
|
1080
1084
|
{isCreateInApp && (
|
|
1081
|
-
|
|
1082
|
-
|
|
1085
|
+
(isFullMode && !commonUtil.hasNewEditorFlowInAppEnabled()) ||
|
|
1086
|
+
(!isFullMode && isLoyaltyModule) ||
|
|
1087
|
+
(!isFullMode && !isLoyaltyModule && !commonUtil.hasNewEditorFlowInAppEnabled()) ? (
|
|
1088
|
+
<InApp
|
|
1089
|
+
key="creatives-inapp-create"
|
|
1090
|
+
location={{ pathname: '/inapp/create', query, search: '' }}
|
|
1091
|
+
setIsLoadingContent={setIsLoadingContent}
|
|
1092
|
+
isGetFormData={isGetFormData}
|
|
1093
|
+
getFormData={getFormData}
|
|
1094
|
+
getDefaultTags={type}
|
|
1095
|
+
isFullMode={isFullMode}
|
|
1096
|
+
templateData={templateData}
|
|
1097
|
+
cap={cap}
|
|
1098
|
+
showTemplateName={showTemplateName}
|
|
1099
|
+
showLiquidErrorInFooter={showLiquidErrorInFooter}
|
|
1100
|
+
onValidationFail={onValidationFail}
|
|
1101
|
+
forwardedTags={forwardedTags}
|
|
1102
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
1103
|
+
onPreviewContentClicked={onPreviewContentClicked}
|
|
1104
|
+
onTestContentClicked={onTestContentClicked}
|
|
1105
|
+
eventContextTags={eventContextTags}
|
|
1106
|
+
onCreateComplete={onCreateComplete}
|
|
1107
|
+
handleClose={handleClose}
|
|
1108
|
+
moduleType={moduleType}
|
|
1109
|
+
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
1110
|
+
handleTestAndPreview={handleTestAndPreview}
|
|
1111
|
+
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
1112
|
+
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
1113
|
+
/>
|
|
1114
|
+
) : (
|
|
1115
|
+
<InAppWrapper
|
|
1116
|
+
key="creatives-inapp-wrapper"
|
|
1083
1117
|
date={new Date().getMilliseconds()}
|
|
1084
1118
|
setIsLoadingContent={setIsLoadingContent}
|
|
1085
1119
|
onInAppEditorTypeChange={onInAppEditorTypeChange}
|
|
@@ -1114,10 +1148,12 @@ export function SlideBoxContent(props) {
|
|
|
1114
1148
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
1115
1149
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
1116
1150
|
/>
|
|
1151
|
+
)
|
|
1117
1152
|
)}
|
|
1118
|
-
|
|
1153
|
+
|
|
1119
1154
|
{isEditInApp && (<InApp
|
|
1120
1155
|
isFullMode={isFullMode}
|
|
1156
|
+
isLoyaltyModule={isLoyaltyModule}
|
|
1121
1157
|
templateData={templateData}
|
|
1122
1158
|
getFormData={getFormData}
|
|
1123
1159
|
getDefaultTags={type}
|
|
@@ -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
|
});
|