@capillarytech/creatives-library 8.0.285 → 8.0.287-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.
Files changed (42) hide show
  1. package/constants/unified.js +0 -1
  2. package/initialState.js +0 -2
  3. package/package.json +1 -1
  4. package/utils/common.js +5 -8
  5. package/utils/commonUtils.js +2 -83
  6. package/utils/tagValidations.js +84 -222
  7. package/utils/tests/commonUtil.test.js +147 -118
  8. package/utils/tests/tagValidations.test.js +280 -358
  9. package/v2Components/ErrorInfoNote/index.js +2 -5
  10. package/v2Components/FormBuilder/index.js +64 -158
  11. package/v2Components/FormBuilder/messages.js +0 -8
  12. package/v2Components/HtmlEditor/HTMLEditor.js +0 -5
  13. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
  14. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +0 -15
  15. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +1 -2
  16. package/v2Containers/Cap/mockData.js +0 -14
  17. package/v2Containers/Cap/reducer.js +3 -55
  18. package/v2Containers/Cap/tests/reducer.test.js +0 -102
  19. package/v2Containers/CreativesContainer/index.js +0 -1
  20. package/v2Containers/Email/index.js +1 -5
  21. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +10 -62
  22. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +12 -115
  23. package/v2Containers/FTP/index.js +2 -51
  24. package/v2Containers/FTP/messages.js +0 -4
  25. package/v2Containers/InApp/index.js +1 -96
  26. package/v2Containers/InApp/tests/index.test.js +17 -6
  27. package/v2Containers/InappAdvance/index.js +2 -103
  28. package/v2Containers/Line/Container/Text/index.js +0 -1
  29. package/v2Containers/MobilePushNew/index.js +2 -33
  30. package/v2Containers/Rcs/index.js +12 -37
  31. package/v2Containers/SmsTrai/Create/index.scss +1 -1
  32. package/v2Containers/SmsTrai/Edit/index.js +6 -47
  33. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
  34. package/v2Containers/Viber/index.js +0 -1
  35. package/v2Containers/Viber/index.scss +1 -1
  36. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -3
  37. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  38. package/v2Containers/WebPush/Create/index.js +2 -2
  39. package/v2Containers/WebPush/Create/utils/validation.js +18 -9
  40. package/v2Containers/WebPush/Create/utils/validation.test.js +0 -24
  41. package/v2Containers/Whatsapp/index.js +9 -17
  42. package/v2Containers/Zalo/index.js +3 -11
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * Created by vivek on 22/5/17.
3
3
  */
4
- import { fromJS, Map as ImmutableMap } from 'immutable';
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
- });
@@ -131,7 +131,6 @@ export class Creatives extends React.Component {
131
131
  errorsAcknowledged: false, // Flag to track if user has acknowledged errors by clicking redirection icon
132
132
  },
133
133
  };
134
- this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
135
134
  this.creativesTemplateSteps = {
136
135
  1: 'modeSelection',
137
136
  2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
@@ -26,7 +26,7 @@ import * as globalActions from '../Cap/actions';
26
26
  import './_email.scss';
27
27
  import {getMessageObject} from '../../utils/messageUtils';
28
28
  import EmailPreview from '../../v2Components/EmailPreview';
29
- import { getDecodedFileName, hasLiquidSupportFeature, hasSupportCKEditor } from '../../utils/common';
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
  }
@@ -15,7 +15,7 @@ import HTMLEditor from '../../../v2Components/HtmlEditor';
15
15
  import CapTagListWithInput from '../../../v2Components/CapTagListWithInput';
16
16
  import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
17
17
  import { validateLiquidTemplateContent } from '../../../utils/commonUtils';
18
- import { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../../utils/common';
18
+ import { isEmailUnsubscribeTagOptional } from '../../../utils/common';
19
19
  import history from '../../../utils/history';
20
20
  import messages from '../messages';
21
21
  import emailMessages from '../../Email/messages';
@@ -108,13 +108,10 @@ const EmailHTMLEditor = (props) => {
108
108
  standardErrors: [],
109
109
  });
110
110
 
111
- // Merge tag validation errors (unsupported/missing) into apiValidationErrors so they show in ValidationErrorDisplay
111
+ // Merge tag validation errors (missing) into apiValidationErrors so they show in ValidationErrorDisplay
112
112
  const mergedApiValidationErrors = useMemo(() => {
113
113
  const tagMessages = [];
114
- if (tagValidationError?.unsupportedTags?.length) {
115
- tagMessages.push(`Unsupported tags are: ${tagValidationError.unsupportedTags.join(', ')}`);
116
- }
117
- if (tagValidationError?.missingTags?.length && !isEmailUnsubscribeTagMandatory()) {
114
+ if (tagValidationError?.missingTags?.length && !isEmailUnsubscribeTagOptional()) {
118
115
  tagMessages.push(`Missing tags are: ${tagValidationError.missingTags.join(', ')}`);
119
116
  }
120
117
  if (tagMessages.length === 0) {
@@ -190,9 +187,6 @@ const EmailHTMLEditor = (props) => {
190
187
  },
191
188
  }), [htmlContent, subject, currentOrgDetails]);
192
189
 
193
- // Check if liquid support is enabled
194
- const isLiquidEnabled = hasLiquidSupportFeature();
195
-
196
190
  // Detect edit mode: when isEditEmail is false (create flow), never treat as edit or fetch template details
197
191
  const hasParamsId = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
198
192
  const currentTemplateId = isEditEmail
@@ -493,10 +487,8 @@ const EmailHTMLEditor = (props) => {
493
487
  const validationResult = validateTags({
494
488
  content,
495
489
  tagsParam: tags,
496
- injectedTagsParams: injectedTags,
497
490
  location,
498
491
  tagModule: getDefaultTags,
499
- eventContextTags,
500
492
  isFullMode,
501
493
  });
502
494
 
@@ -650,7 +642,7 @@ const EmailHTMLEditor = (props) => {
650
642
  // IMPORTANT: Clear API validation errors FIRST before checking for validation errors
651
643
  // This ensures that old API errors don't block the save when user fixes content and clicks Update again
652
644
  // We'll re-validate with fresh API call anyway
653
- if (isLiquidEnabled && getLiquidTags) {
645
+ if (getLiquidTags) {
654
646
  setApiValidationErrors({
655
647
  liquidErrors: [],
656
648
  standardErrors: [],
@@ -712,7 +704,7 @@ const EmailHTMLEditor = (props) => {
712
704
  // When EMAIL_UNSUBSCRIBE_TAG_MANDATORY is false: validate and require unsubscribe tag.
713
705
  // Run for both library and full mode so liquid-enabled orgs also get the error (notification + ValidationErrorDisplay).
714
706
  const isModuleTypeOutbound = (moduleType || '').toUpperCase() === OUTBOUND;
715
- if (!isEmailUnsubscribeTagMandatory() && isModuleTypeOutbound) {
707
+ if (!isEmailUnsubscribeTagOptional() && isModuleTypeOutbound) {
716
708
  const unsubscribeRegex = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g; // eslint-disable-line no-useless-escape
717
709
  const hasUnsubscribeTag = unsubscribeRegex.test(htmlContent);
718
710
 
@@ -744,52 +736,17 @@ const EmailHTMLEditor = (props) => {
744
736
  const validationResult = validateTags({
745
737
  content: htmlContent,
746
738
  tagsParam: tags,
747
- injectedTagsParams: injectedTags,
748
739
  location,
749
740
  tagModule: getDefaultTags,
750
- eventContextTags,
751
741
  isFullMode,
752
742
  });
753
743
 
754
- const hasUnsupportedTags = validationResult?.unsupportedTags?.length > 0;
755
- if (!validationResult?.valid || (hasUnsupportedTags && !isFullMode)) {
744
+ if (!validationResult?.valid) {
756
745
  setTagValidationError(validationResult);
757
- // IMPORTANT: For non-liquid orgs, block save (like CK/BEE editor)
758
- // For liquid orgs, continue (extractTags API will validate)
759
- if (!isLiquidEnabled) {
760
- // Show notification popup like CK/BEE editor
761
- const baseLanguage = get(currentOrgDetails, 'basic_details.base_language', 'en');
762
-
763
- const contentNotValidMsg = intl.formatMessage(formBuilderMessages.contentNotValidLanguage);
764
- let errorMessage = `${contentNotValidMsg} ${baseLanguage}`;
765
-
766
- if (hasUnsupportedTags) {
767
- const unsupportedTagsMsg = intl.formatMessage(formBuilderMessages.unsupportedTags);
768
- errorMessage += `\n${unsupportedTagsMsg} ${validationResult?.unsupportedTags?.join(', ')}`;
769
- }
770
- if (validationResult?.missingTags?.length > 0) {
771
- const missingTagsMsg = intl.formatMessage(formBuilderMessages.missingTags);
772
- errorMessage += `\n${missingTagsMsg} ${validationResult?.missingTags?.join(', ')}`;
773
- }
774
-
775
- const type = 'error';
776
- CapNotification[type]({
777
- message: `${type.toUpperCase()} ! ! ! `,
778
- description: errorMessage,
779
- duration: 5,
780
- });
781
-
782
- // Reset parent state so next click is detected as a change
783
- if (onValidationFail) {
784
- onValidationFail();
785
- }
786
- // Block save for non-liquid orgs
787
- return;
788
- }
789
- // For liquid orgs, just show warning and continue
746
+ // For liquid orgs, show warning and continue (extractTags API will validate)
790
747
  }
791
748
  // Clear tag errors if valid
792
- if (tagValidationError && validationResult?.valid && !hasUnsupportedTags) {
749
+ if (tagValidationError && validationResult?.valid) {
793
750
  setTagValidationError(null);
794
751
  }
795
752
  }
@@ -956,8 +913,8 @@ const EmailHTMLEditor = (props) => {
956
913
  }
957
914
  };
958
915
 
959
- // If liquid enabled, validate first using extractTags API
960
- if (isLiquidEnabled && getLiquidTags) {
916
+ // Validate first using extractTags API
917
+ if (getLiquidTags) {
961
918
  // Note: API validation errors are already cleared at the start of handleSave
962
919
  // This ensures fresh validation on every save attempt
963
920
 
@@ -998,10 +955,6 @@ const EmailHTMLEditor = (props) => {
998
955
  messages: formBuilderMessages,
999
956
  onError,
1000
957
  onSuccess,
1001
- tagLookupMap: metaEntities?.tagLookupMap,
1002
- eventContextTags,
1003
- isLiquidFlow: true,
1004
- forwardedTags: forwardedTags || {},
1005
958
  });
1006
959
  } else {
1007
960
  performSave();
@@ -1013,7 +966,6 @@ const EmailHTMLEditor = (props) => {
1013
966
  injectedTags,
1014
967
  location,
1015
968
  getDefaultTags,
1016
- eventContextTags,
1017
969
  formatMessage,
1018
970
  subjectError,
1019
971
  isFullMode,
@@ -1025,11 +977,8 @@ const EmailHTMLEditor = (props) => {
1025
977
  emailActions,
1026
978
  getFormdata,
1027
979
  isGetFormData,
1028
- isLiquidEnabled,
1029
980
  getLiquidTags,
1030
981
  showLiquidErrorInFooter,
1031
- metaEntities,
1032
- forwardedTags,
1033
982
  globalActions,
1034
983
  intl,
1035
984
  extractedTemplateName,
@@ -1175,7 +1124,6 @@ const EmailHTMLEditor = (props) => {
1175
1124
  userLocale={intl.locale || 'en'}
1176
1125
  moduleFilterEnabled={location?.query?.type !== EMBEDDED}
1177
1126
  onTagContextChange={handleOnTagsContextChange}
1178
- isLiquidEnabled={isLiquidEnabled}
1179
1127
  isFullMode={isFullMode}
1180
1128
  onErrorAcknowledged={handleErrorAcknowledged}
1181
1129
  onValidationChange={handleValidationChange}