@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.
Files changed (79) hide show
  1. package/constants/unified.js +1 -5
  2. package/initialState.js +2 -0
  3. package/package.json +1 -1
  4. package/services/api.js +0 -17
  5. package/services/tests/api.test.js +0 -85
  6. package/utils/common.js +8 -5
  7. package/utils/commonUtils.js +93 -46
  8. package/utils/tagValidations.js +223 -83
  9. package/utils/tests/commonUtil.test.js +124 -316
  10. package/utils/tests/tagValidations.test.js +358 -441
  11. package/v2Components/CommonTestAndPreview/SendTestMessage.js +49 -78
  12. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -134
  13. package/v2Components/CommonTestAndPreview/actions.js +0 -10
  14. package/v2Components/CommonTestAndPreview/constants.js +1 -15
  15. package/v2Components/CommonTestAndPreview/index.js +19 -80
  16. package/v2Components/CommonTestAndPreview/messages.js +0 -94
  17. package/v2Components/CommonTestAndPreview/reducer.js +0 -10
  18. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +0 -53
  19. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
  20. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -36
  21. package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
  22. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +0 -377
  23. package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
  24. package/v2Components/ErrorInfoNote/index.js +5 -2
  25. package/v2Components/FormBuilder/index.js +203 -137
  26. package/v2Components/FormBuilder/messages.js +8 -0
  27. package/v2Components/HtmlEditor/HTMLEditor.js +5 -0
  28. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +15 -0
  30. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +2 -1
  31. package/v2Containers/Cap/mockData.js +14 -0
  32. package/v2Containers/Cap/reducer.js +55 -3
  33. package/v2Containers/Cap/tests/reducer.test.js +102 -0
  34. package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -5
  35. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -13
  36. package/v2Containers/CreativesContainer/constants.js +0 -6
  37. package/v2Containers/CreativesContainer/index.js +7 -47
  38. package/v2Containers/Email/index.js +5 -1
  39. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +70 -23
  40. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +120 -20
  41. package/v2Containers/FTP/index.js +51 -2
  42. package/v2Containers/FTP/messages.js +4 -0
  43. package/v2Containers/InApp/index.js +107 -35
  44. package/v2Containers/InApp/tests/index.test.js +6 -17
  45. package/v2Containers/InappAdvance/index.js +112 -4
  46. package/v2Containers/InappAdvance/tests/index.test.js +0 -2
  47. package/v2Containers/Line/Container/Text/index.js +1 -0
  48. package/v2Containers/MobilePush/Create/index.js +19 -59
  49. package/v2Containers/MobilePush/Edit/index.js +20 -48
  50. package/v2Containers/MobilePushNew/index.js +32 -12
  51. package/v2Containers/MobilepushWrapper/index.js +1 -3
  52. package/v2Containers/Rcs/index.js +37 -12
  53. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1276 -1408
  54. package/v2Containers/Sms/Create/index.js +3 -39
  55. package/v2Containers/Sms/Create/messages.js +0 -4
  56. package/v2Containers/Sms/Edit/index.js +3 -35
  57. package/v2Containers/Sms/commonMethods.js +6 -3
  58. package/v2Containers/SmsTrai/Edit/index.js +47 -11
  59. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +294 -327
  60. package/v2Containers/SmsWrapper/index.js +0 -2
  61. package/v2Containers/TemplatesV2/index.js +13 -28
  62. package/v2Containers/Viber/index.js +1 -0
  63. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +3 -1
  64. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +7 -0
  65. package/v2Containers/WebPush/Create/index.js +2 -2
  66. package/v2Containers/WebPush/Create/utils/validation.js +8 -17
  67. package/v2Containers/WebPush/Create/utils/validation.test.js +24 -44
  68. package/v2Containers/Whatsapp/index.js +17 -9
  69. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +4872 -5246
  70. package/v2Containers/Zalo/index.js +11 -3
  71. package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
  72. package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -284
  73. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -72
  74. package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
  75. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -657
  76. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +0 -172
  77. package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -466
  78. package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
  79. package/v2Containers/Sms/tests/commonMethods.test.js +0 -122
@@ -229,6 +229,7 @@ const defaultProps = {
229
229
  channel: 'EMAIL',
230
230
  userLocale: 'en',
231
231
  moduleFilterEnabled: true,
232
+ isLiquidEnabled: false,
232
233
  isFullMode: true,
233
234
  onErrorAcknowledged: jest.fn(),
234
235
  onValidationChange: jest.fn(),
@@ -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
- return state.set("injectedTags", action.injectedTags);
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-create-new"
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, EMAIL, SMS, EDIT_TEMPLATE, MOBILE_PUSH } from './constants';
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 === EDIT_TEMPLATE
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 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;
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 || hasSmsValidationErrors || hasMobilePushValidationErrors || isSupportCKEditor;
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
  }