@capillarytech/creatives-library 8.0.11 → 8.0.12

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.11",
4
+ "version": "8.0.12",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -351,10 +351,11 @@ export const getUserList = () => {
351
351
  return API.get(url);// request(url, getAPICallObject('GET'));
352
352
  };
353
353
 
354
- export const fetchSchemaForEntity = ({queryParams}) => {
354
+ export const fetchSchemaForEntity = async ({queryParams}) => {
355
355
  if (queryParams.version === "v2" && queryParams.type === "LAYOUT") {
356
356
  const schema = getSchema(queryParams.layout);
357
- return schema.then((layout) => layout).catch((err) => {
357
+ // Added async/await to ensure the schema object is resolved before returning it
358
+ return await schema.then((layout) => layout).catch((err) => {
358
359
  console.error(err);
359
360
  });
360
361
  }
@@ -11,8 +11,10 @@ import {
11
11
  getTemplateInfoById,
12
12
  askAiraForLiquid,
13
13
  getLiquidTags,
14
+ fetchSchemaForEntity,
14
15
  } from '../api';
15
16
  import { mockData } from './mockData';
17
+ import getSchema from '../getSchema';
16
18
  const sampleFile = require('../../assets/line.png');
17
19
 
18
20
  let mockDecompressJsonObject;
@@ -24,6 +26,8 @@ jest.mock('@capillarytech/cap-ui-utils', () => {
24
26
  };
25
27
  });
26
28
 
29
+ jest.mock('../getSchema', () => jest.fn());
30
+
27
31
  describe('getSenderDetails -- Test with valid responses', () => {
28
32
  it('Should return correct response', () =>
29
33
  expect(getSenderDetails('WHATSAPP', -1)).toEqual(Promise.resolve()));
@@ -438,4 +442,33 @@ describe('getLiquidTags -- Test with various responses', () => {
438
442
  error: 'Internal Server Error',
439
443
  });
440
444
  });
441
- });
445
+ });
446
+
447
+ describe('fetchSchemaForEntity', () => {
448
+ it('should fetch schema for version v2 and type LAYOUT', async () => {
449
+ const queryParams = { version: 'v2', type: 'LAYOUT', layout: 'testLayout' };
450
+ const mockLayout = { id: 'layout1', name: 'Test Layout' };
451
+
452
+ getSchema.mockReturnValue(Promise.resolve(mockLayout));
453
+
454
+ const result = await fetchSchemaForEntity({ queryParams });
455
+
456
+ expect(getSchema).toHaveBeenCalledWith(queryParams.layout);
457
+ expect(result).toEqual(mockLayout);
458
+ });
459
+
460
+ it('should handle errors when fetching schema', async () => {
461
+ const queryParams = { version: 'v2', type: 'LAYOUT', layout: 'testLayout' };
462
+ const mockError = new Error('Failed to fetch schema');
463
+
464
+ getSchema.mockReturnValue(Promise.reject(mockError));
465
+
466
+ console.error = jest.fn();
467
+
468
+ const result = await fetchSchemaForEntity({ queryParams });
469
+
470
+ expect(getSchema).toHaveBeenCalledWith(queryParams.layout);
471
+ expect(result).toBeUndefined();
472
+ expect(console.error).toHaveBeenCalledWith(mockError);
473
+ });
474
+ });
@@ -98,7 +98,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
98
98
  customPopoverVisible: false,
99
99
  isDrawerVisible: false,
100
100
  translationLang: 'en',
101
- showLiquidValidation: false,
102
101
  liquidErrorMessage: {
103
102
  STANDARD_ERROR_MSG: [],
104
103
  LIQUID_ERROR_MSG: [],
@@ -494,7 +493,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
494
493
  const type = (this.props.location && this.props.location.query.type) ? this.props.location.query.type : 'full';
495
494
  const currentModule = (this.props.location && this.props.location.query.module) ? this.props.location.query.module : 'default';
496
495
  let errorData = _.cloneDeep(this.state.errorData);
497
- const stateFormData = _.cloneDeep(this.state.formData);
498
496
  if (channel && channel.toUpperCase() === SMS) {
499
497
  for (let count = 0; count < this.state.tabCount; count += 1) {
500
498
  if (_.isEmpty(errorData[count])) {
@@ -961,7 +959,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
961
959
  } else if(index === 'template-version' && data !== '') {
962
960
  errorData[index] = false;
963
961
  }
964
-
962
+ isLiquidValid = isValid; // Existing validations support for liquid enabled orgs
965
963
  // Check error for the versions
966
964
  if (!isNaN(index) || index === 'base') {
967
965
  if (errorData[index] === undefined) {
@@ -1002,7 +1000,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1002
1000
  // Adds a bypass for cases where content is initially empty in the creation flow.
1003
1001
  if(this.liquidFlow && !this.props.isEdit){
1004
1002
  errorString = "";
1005
- isValid = true;
1006
1003
  isLiquidValid = true;
1007
1004
  }
1008
1005
  }
@@ -1013,7 +1010,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1013
1010
  if (this.liquidFlow) {
1014
1011
  this.setState(
1015
1012
  (prevState) => ({
1016
- showLiquidValidation: true,
1017
1013
  liquidErrorMessage: {
1018
1014
  ...prevState.liquidErrorMessage,
1019
1015
  STANDARD_ERROR_MSG: [errorString],
@@ -1036,15 +1032,16 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1036
1032
  }
1037
1033
  });
1038
1034
  }
1035
+
1036
+ const isTemplateValid = this.liquidFlow ? isLiquidValid : isValid;
1039
1037
  //Updating the state with the error data
1040
1038
  this.setState((prevState) => ({
1041
- isFormValid: isValid,
1042
- showLiquidValidation: prevState.liquidErrorMessage?.LIQUID_ERROR_MSG?.length > 0 ? true : !isLiquidValid,
1039
+ isFormValid: isTemplateValid,
1043
1040
  liquidErrorMessage: {
1044
1041
  //Do not update the LIQUID_ERROR_MSG validation error message
1045
1042
  ...prevState.liquidErrorMessage,
1046
1043
  //update Standard error message based on the updated content
1047
- STANDARD_ERROR_MSG: isValid ? [] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
1044
+ STANDARD_ERROR_MSG: isTemplateValid ? [] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
1048
1045
  },
1049
1046
  errorData,
1050
1047
  }), () => {
@@ -1054,8 +1051,8 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1054
1051
  }
1055
1052
  });
1056
1053
 
1057
- this.props.onFormValidityChange(isValid, errorData);
1058
- return isValid;
1054
+ this.props.onFormValidityChange(isTemplateValid, errorData);
1055
+ return isTemplateValid;
1059
1056
  }
1060
1057
 
1061
1058
  indexOfEnd(targetString, string) {
@@ -1083,9 +1080,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1083
1080
  */
1084
1081
  const handleResult = (result) => {
1085
1082
  const {formatMessage} = this.props.intl;
1083
+ // Checks for errors in the result and displays them if any are found.
1086
1084
  if (result?.errors?.length > 0) {
1087
1085
  this.setState((prevState) => ({
1088
- showLiquidValidation: true,
1089
1086
  liquidErrorMessage: {
1090
1087
  ...prevState.liquidErrorMessage,
1091
1088
  LIQUID_ERROR_MSG: result?.errors?.map(error => error?.message) ?? [formatMessage(messages.somethingWentWrong)],
@@ -1096,22 +1093,22 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1096
1093
  this.props.stopValidation();
1097
1094
  return;
1098
1095
  }
1099
- else if (result?.data?.length > 0) {
1100
- const extractedLiquidTags = extractNames(result?.data);
1096
+ // Extracts the supported liquid tags from the given content and do the necessary checks.
1097
+ else {
1098
+ const extractedLiquidTags = extractNames(result?.data || []);
1101
1099
  // Extracts the supported liquid tags from the given content.
1102
1100
  const supportedLiquidTags = checkSupport(
1103
1101
  result,
1104
1102
  this.props?.metaEntities?.tagLookupMap
1105
1103
  );
1106
1104
  const unsupportedLiquidTags = extractedLiquidTags?.filter(
1107
- (tag) => !supportedLiquidTags?.includes(tag)
1105
+ (tag) => !supportedLiquidTags?.includes(tag) && !this.skipTags(tag)
1108
1106
  );
1109
1107
  if (
1110
1108
  unsupportedLiquidTags?.length > 0
1111
1109
  ) {
1112
1110
  this.setState(
1113
1111
  (prevState) => ({
1114
- showLiquidValidation: true,
1115
1112
  liquidErrorMessage: {
1116
1113
  ...prevState.liquidErrorMessage,
1117
1114
  LIQUID_ERROR_MSG: [formatMessage(messages.unsupportedTagsValidationError, {
@@ -3425,7 +3422,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3425
3422
  <CapColumn
3426
3423
  style={val.colStyle ? val.colStyle : {border : ""}}
3427
3424
  span={val.width}
3428
- className={`${this.state.showLiquidValidation && this.liquidFlow && "error-boundary"} `}
3425
+ className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow && "error-boundary"} `}
3429
3426
  >
3430
3427
  <CKEditor
3431
3428
  id={val.id}
@@ -3469,7 +3466,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
3469
3466
  isModuleFilterEnabled = this.props.isFullMode;
3470
3467
  }
3471
3468
  columns.push(
3472
- <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={`${this.state.showLiquidValidation && this.liquidFlow && "error-boundary"}`}>
3469
+ <CapColumn style={val.colStyle ? val.colStyle : {}} span={val.width} className={`${(this.state.liquidErrorMessage?.LIQUID_ERROR_MSG?.length || this.state.liquidErrorMessage?.STANDARD_ERROR_MSG?.length) && this.liquidFlow && "error-boundary"}`}>
3473
3470
  <BeeEditor
3474
3471
  uid={uuid}
3475
3472
  tokenData={beeToken}
@@ -41,6 +41,8 @@ import v2MobilePushEditReducer from './reducer';
41
41
  import * as globalActions from '../../Cap/actions';
42
42
  import { MAPP_SDK } from './constants';
43
43
  import { isEmbeddedEditOrPreview } from '../../../utils/commonUtils';
44
+ import { EMBEDDED } from '../../Whatsapp/constants';
45
+ import { OUTBOUND } from '../../../v2Components/FormBuilder/constants';
44
46
 
45
47
  const PrefixWrapper = styled.div`
46
48
  margin-right: 16px;
@@ -115,27 +117,53 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
115
117
  let selectedWeChatAccount = {};
116
118
  const queryType = String(get(this.props, 'location.query.type', ''))?.toLowerCase();
117
119
  const creativesMode = String(get(this.props, 'creativesMode', ''))?.toLowerCase();
120
+ const { Edit: EditProps, templateData, Templates, type } = this.props || {};
121
+ const { selectedWeChatAccount: editSelectedWeChatAccount, templateDetails } = EditProps || {};
122
+ const { id: selectedWeChatAccountId } = editSelectedWeChatAccount || {};
123
+ const { definition } = templateDetails || {};
124
+ const { accountId: templateAccountId } = definition || {};
125
+ const { Edit: nextEdit } = nextProps || {};
118
126
  if (isEmbeddedEditOrPreview(queryType, creativesMode)) {
119
- selectedWeChatAccount = !_.isEmpty(this.props.Edit.selectedWeChatAccount)
120
- ? this.props.Edit.selectedWeChatAccount
121
- : nextProps.Edit.selectedWeChatAccount;
122
- } else if (!_.isEmpty(this.props.Templates.selectedWeChatAccount)) {
123
- selectedWeChatAccount = this.props.Templates.selectedWeChatAccount;
124
- }
125
-
126
- if (this.props.location.query.type === 'embedded' && !nextProps.Edit.fetchingWeCrmAccounts && !_.isEqual(this.props.Edit.weCrmAccounts, nextProps.Edit.weCrmAccounts) && (!selectedWeChatAccount || _.isEmpty(selectedWeChatAccount))) {
127
- this.setMobilePushAccountOptions(nextProps.Edit.weCrmAccounts, get(this.props, "templateData.definition.accountId"));
128
- }
129
- // Check if the query type is 'embedded' and the creatives mode is either 'edit' or 'preview'. Also, ensure that the selected WeChat account ID is not equal to the account ID in the template details.
130
- // If all conditions are met, set the mobile push account options using the provided weCrmAccounts and the account ID from templateData.
131
- if (isEmbeddedEditOrPreview(queryType, creativesMode) && !_.isEqual(this.props.Edit.selectedWeChatAccount?.id, this.props.Edit.templateDetails?.definition?.accountId)) {
132
- this.setMobilePushAccountOptions(nextProps.Edit.weCrmAccounts, get(this.props, "templateData.definition.accountId"));
133
- }
134
- if (!_.isEmpty(nextProps.Edit.selectedWeChatAccount) && !_.isEqual(this.props.Edit.selectedWeChatAccount, nextProps.Edit.selectedWeChatAccount) && (this.props.location.query.type === 'embedded') && this.props.location.query.module === "loyalty") {
127
+ selectedWeChatAccount = !_.isEmpty(editSelectedWeChatAccount)
128
+ ? editSelectedWeChatAccount
129
+ : nextEdit?.selectedWeChatAccount;
130
+ } else if (!_.isEmpty(Templates?.selectedWeChatAccount)) {
131
+ selectedWeChatAccount = Templates?.selectedWeChatAccount;
132
+ }
133
+
134
+ if (queryType === EMBEDDED && !nextEdit?.fetchingWeCrmAccounts && !_.isEqual(EditProps?.weCrmAccounts, nextEdit?.weCrmAccounts) && (!selectedWeChatAccount || _.isEmpty(selectedWeChatAccount))) {
135
+ this.setMobilePushAccountOptions(nextEdit?.weCrmAccounts, templateData?.definition?.accountId);
136
+ }
137
+ // Check if the query type is 'embedded' and the creatives mode is either 'edit' or 'preview'.
138
+ // If the type is not 'outbound', ensure that the selected WeChat account ID is not equal to the account ID in the template details.
139
+ // If all conditions are met, set the mobile push account options using the provided weCrmAccounts and the account ID from templateData.
140
+ if (
141
+ isEmbeddedEditOrPreview(queryType, creativesMode) &&
142
+ type !== OUTBOUND.toLowerCase() &&
143
+ nextEdit?.weCrmAccounts &&
144
+ !EditProps?.fetchingWeCrmAccounts &&
145
+ templateData?.definition?.accountId &&
146
+ !_.isEqual(templateAccountId, templateData?.definition?.accountId)
147
+ ) {
148
+ this.setMobilePushAccountOptions(nextEdit?.weCrmAccounts, templateData?.definition?.accountId);
149
+ }
150
+ // If the type is 'outbound', ensure that the selected WeChat account ID is not equal to the account ID in the template details.
151
+ // If all conditions are met, set the mobile push account options using the provided weCrmAccounts and the template account ID.
152
+ if (
153
+ isEmbeddedEditOrPreview(queryType, creativesMode) &&
154
+ type === OUTBOUND.toLowerCase() &&
155
+ nextEdit?.weCrmAccounts &&
156
+ selectedWeChatAccountId &&
157
+ templateAccountId &&
158
+ !_.isEqual(selectedWeChatAccountId, templateAccountId)
159
+ ) {
160
+ this.setMobilePushAccountOptions(nextEdit?.weCrmAccounts, templateAccountId);
161
+ }
162
+ if (!_.isEmpty(nextEdit?.selectedWeChatAccount) && !_.isEqual(editSelectedWeChatAccount, nextEdit?.selectedWeChatAccount) && (queryType === EMBEDDED) && this.props.location.query.module === "loyalty") {
135
163
  const params = {
136
164
  name: '',
137
165
  sortBy: 'Most Recent',
138
- accountId: nextProps.Edit.selectedWeChatAccount.id,
166
+ accountId: nextEdit?.selectedWeChatAccount?.id,
139
167
  };
140
168
  this.props.actions.getMobilepushTemplatesList('mobilepush', params);
141
169
  }
@@ -164,7 +192,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
164
192
  this.setTemplateOptions(nextProps.Edit.mobilepushTemplates);
165
193
  }
166
194
  }
167
-
168
195
  this.handleEditSchemaOnPropsChange(nextProps, selectedWeChatAccount);
169
196
  if (nextProps.Edit.uploadedAssetData) {
170
197
  const formData = _.cloneDeep(this.state.formData);