@capillarytech/creatives-library 8.0.340-beta.0.4 → 8.0.340-beta.0.6

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 (67) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +20 -0
  4. package/services/tests/api.test.js +59 -0
  5. package/utils/common.js +6 -0
  6. package/utils/test-utils.js +2 -2
  7. package/utils/tests/v2Common.test.js +46 -1
  8. package/utils/v2common.js +18 -0
  9. package/v2Components/CapTagList/index.js +5 -6
  10. package/v2Components/CapTagListWithInput/index.js +1 -1
  11. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +18 -6
  12. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +27 -0
  13. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WhatsAppPreviewContent.test.js +48 -0
  14. package/v2Components/TemplatePreview/_templatePreview.scss +22 -1
  15. package/v2Components/TemplatePreview/index.js +21 -9
  16. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +1 -0
  17. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +9 -0
  18. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +28 -20
  19. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +24 -16
  20. package/v2Containers/CreativesContainer/SlideBoxContent.js +16 -5
  21. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
  22. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -4
  23. package/v2Containers/CreativesContainer/index.js +14 -1
  24. package/v2Containers/CreativesContainer/messages.js +4 -0
  25. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -4
  26. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +4 -4
  27. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +3 -0
  28. package/v2Containers/Email/reducer.js +12 -3
  29. package/v2Containers/Email/sagas.js +9 -4
  30. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
  31. package/v2Containers/Email/tests/reducer.test.js +47 -0
  32. package/v2Containers/Email/tests/sagas.test.js +146 -6
  33. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +8 -1
  34. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  35. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +7 -0
  36. package/v2Containers/MobilePush/Create/index.js +1 -1
  37. package/v2Containers/MobilePush/Edit/index.js +1 -1
  38. package/v2Containers/Sms/Create/index.js +3 -0
  39. package/v2Containers/Sms/SCHEMA_FORMBUILDER_MAP.md +1 -1
  40. package/v2Containers/Templates/ChannelTypeIllustration.js +23 -6
  41. package/v2Containers/Templates/_templates.scss +155 -24
  42. package/v2Containers/Templates/actions.js +44 -0
  43. package/v2Containers/Templates/constants.js +31 -0
  44. package/v2Containers/Templates/index.js +400 -59
  45. package/v2Containers/Templates/messages.js +96 -0
  46. package/v2Containers/Templates/reducer.js +84 -1
  47. package/v2Containers/Templates/sagas.js +64 -0
  48. package/v2Containers/Templates/selectors.js +12 -0
  49. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +12 -0
  50. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1166 -1112
  51. package/v2Containers/Templates/tests/index.test.js +6 -0
  52. package/v2Containers/Templates/tests/reducer.test.js +178 -0
  53. package/v2Containers/Templates/tests/sagas.test.js +390 -8
  54. package/v2Containers/Templates/tests/selector.test.js +32 -0
  55. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -1
  56. package/v2Containers/Viber/constants.js +8 -0
  57. package/v2Containers/Viber/index.js +5 -0
  58. package/v2Containers/Viber/messages.js +4 -0
  59. package/v2Containers/Viber/reducer.js +26 -3
  60. package/v2Containers/Viber/sagas.js +50 -8
  61. package/v2Containers/Viber/tests/index.test.js +80 -0
  62. package/v2Containers/Viber/tests/reducer.test.js +297 -0
  63. package/v2Containers/Viber/tests/saga.test.js +412 -40
  64. package/v2Containers/Whatsapp/constants.js +8 -0
  65. package/v2Containers/Whatsapp/index.js +145 -5
  66. package/v2Containers/Whatsapp/index.scss +12 -0
  67. package/v2Containers/Whatsapp/messages.js +16 -0
@@ -52,6 +52,7 @@ export const EXTENDED_TAG = 'ExtendedTagMessage';
52
52
  export const BADGES_UI_ENABLED = 'BADGES_UI_ENABLED';
53
53
  export const JP_LOCALE_HIDE_FEATURE = 'JP_LOCALE_HIDE_FEATURE';
54
54
  export const ENABLE_WECHAT = 'ENABLE_WECHAT';
55
+ export const ENABLE_CREATIVES_ARCHIVAL = 'ENABLE_CREATIVES_ARCHIVAL';
55
56
  export const ENABLE_WEBPUSH = 'ENABLE_WEBPUSH';
56
57
  export const ENABLE_CUSTOMER_BARCODE_TAG = 'ENABLE_CUSTOMER_BARCODE_TAG';
57
58
  export const EMAIL_UNSUBSCRIBE_TAG_MANDATORY = 'EMAIL_UNSUBSCRIBE_TAG_MANDATORY';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.340-beta.0.4",
4
+ "version": "8.0.340-beta.0.6",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -361,6 +361,26 @@ export const deleteTemplate = ({channel, id}) => {
361
361
  //return API.deleteResource(url);
362
362
  };
363
363
 
364
+ export const archiveTemplate = ({ channel, id }) => {
365
+ const url = `${API_ENDPOINT}/templates/archive/${id}/${channel}`;
366
+ return request(url, getAPICallObject('PUT'));
367
+ };
368
+
369
+ export const unarchiveTemplate = ({ channel, id }) => {
370
+ const url = `${API_ENDPOINT}/templates/unarchive/${id}/${channel}`;
371
+ return request(url, getAPICallObject('PUT'));
372
+ };
373
+
374
+ export const bulkArchiveTemplates = ({ channel, ids }) => {
375
+ const url = `${API_ENDPOINT}/templates/archive/bulk/${channel}`;
376
+ return request(url, getAPICallObject('PUT', { ids }));
377
+ };
378
+
379
+ export const bulkUnarchiveTemplates = ({ channel, ids }) => {
380
+ const url = `${API_ENDPOINT}/templates/unarchive/bulk/${channel}`;
381
+ return request(url, getAPICallObject('PUT', { ids }));
382
+ };
383
+
364
384
  export const deleteRcsTemplate = ({ templateName }) => {
365
385
  const url = `${API_ENDPOINT}/templates/${templateName}/RCS`;
366
386
  return request(url, getAPICallObject('DELETE'))
@@ -1086,3 +1086,62 @@ describe('createTestCustomer', () => {
1086
1086
  expect(lastCall[1].body).toBe(JSON.stringify(payload));
1087
1087
  });
1088
1088
  });
1089
+
1090
+ import {
1091
+ archiveTemplate,
1092
+ unarchiveTemplate,
1093
+ bulkArchiveTemplates,
1094
+ bulkUnarchiveTemplates,
1095
+ } from '../api';
1096
+
1097
+ describe('archiveTemplate', () => {
1098
+ beforeEach(() => {
1099
+ global.fetch = jest.fn().mockReturnValue(Promise.resolve({ json: () => Promise.resolve({}) }));
1100
+ });
1101
+
1102
+ it('should call PUT on archive endpoint', () => {
1103
+ archiveTemplate({ channel: 'EMAIL', id: 'id123' });
1104
+ expect(global.fetch).toHaveBeenCalled();
1105
+ const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1106
+ expect(lastCall[1].method).toBe('PUT');
1107
+ });
1108
+ });
1109
+
1110
+ describe('unarchiveTemplate', () => {
1111
+ beforeEach(() => {
1112
+ global.fetch = jest.fn().mockReturnValue(Promise.resolve({ json: () => Promise.resolve({}) }));
1113
+ });
1114
+
1115
+ it('should call PUT on unarchive endpoint', () => {
1116
+ unarchiveTemplate({ channel: 'EMAIL', id: 'id123' });
1117
+ expect(global.fetch).toHaveBeenCalled();
1118
+ const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1119
+ expect(lastCall[1].method).toBe('PUT');
1120
+ });
1121
+ });
1122
+
1123
+ describe('bulkArchiveTemplates', () => {
1124
+ beforeEach(() => {
1125
+ global.fetch = jest.fn().mockReturnValue(Promise.resolve({ json: () => Promise.resolve({}) }));
1126
+ });
1127
+
1128
+ it('should call PUT on bulk archive endpoint with ids', () => {
1129
+ bulkArchiveTemplates({ channel: 'EMAIL', ids: ['id1', 'id2'] });
1130
+ expect(global.fetch).toHaveBeenCalled();
1131
+ const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1132
+ expect(lastCall[1].method).toBe('PUT');
1133
+ });
1134
+ });
1135
+
1136
+ describe('bulkUnarchiveTemplates', () => {
1137
+ beforeEach(() => {
1138
+ global.fetch = jest.fn().mockReturnValue(Promise.resolve({ json: () => Promise.resolve({}) }));
1139
+ });
1140
+
1141
+ it('should call PUT on bulk unarchive endpoint with ids', () => {
1142
+ bulkUnarchiveTemplates({ channel: 'EMAIL', ids: ['id1', 'id2'] });
1143
+ expect(global.fetch).toHaveBeenCalled();
1144
+ const lastCall = global.fetch.mock.calls[global.fetch.mock.calls.length - 1];
1145
+ expect(lastCall[1].method).toBe('PUT');
1146
+ });
1147
+ });
package/utils/common.js CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  ENABLE_NEW_MPUSH,
28
28
  ENABLE_NEW_EDITOR_FLOW_INAPP,
29
29
  SUPPORT_ENGAGEMENT_MODULE,
30
+ ENABLE_CREATIVES_ARCHIVAL,
30
31
  } from '../constants/unified';
31
32
  import { apiMessageFormatHandler } from './commonUtils';
32
33
 
@@ -122,6 +123,11 @@ export const hasWechatFeatureEnabled = Auth.hasFeatureAccess.bind(
122
123
  ENABLE_WECHAT,
123
124
  );
124
125
 
126
+ export const hasCreativesArchivalEnabled = Auth.hasFeatureAccess.bind(
127
+ null,
128
+ ENABLE_CREATIVES_ARCHIVAL,
129
+ );
130
+
125
131
  export const hasWebPushFeatureEnabled = Auth.hasFeatureAccess.bind(
126
132
  null,
127
133
  ENABLE_WEBPUSH,
@@ -57,7 +57,7 @@ function getVisibleDropdown() {
57
57
  return dropdowns[0];
58
58
  }
59
59
 
60
- async function findVisibleSelectOption(optionText) {
60
+ async function findVisibleSelectOption(optionText, { timeout = 1000 } = {}) {
61
61
  return waitFor(() => {
62
62
  const dropdowns = Array.from(document.querySelectorAll('.ant-select-dropdown'))
63
63
  .filter((el) => !el.classList.contains('ant-select-dropdown-hidden'));
@@ -67,7 +67,7 @@ async function findVisibleSelectOption(optionText) {
67
67
  if (match) return match;
68
68
  }
69
69
  throw new Error(`Visible select option with text "${optionText}" not found`);
70
- });
70
+ }, { timeout });
71
71
  }
72
72
 
73
73
  // re-export everything
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { getObjFromQueryParams } from '../v2common';
2
+ import { shallow } from 'enzyme';
3
+ import { getObjFromQueryParams, buildTemplateNameDescription } from '../v2common';
3
4
 
4
5
  describe('Test v2common container', () => {
5
6
  it('test getObjFromQueryParams', () => {
@@ -9,3 +10,47 @@ describe('Test v2common container', () => {
9
10
  });
10
11
  });
11
12
  });
13
+
14
+ describe('buildTemplateNameDescription', () => {
15
+ it('returns undefined when templateName is empty string', () => {
16
+ expect(buildTemplateNameDescription('Template name', '')).toBeUndefined();
17
+ });
18
+
19
+ it('returns undefined when templateName is null', () => {
20
+ expect(buildTemplateNameDescription('Template name', null)).toBeUndefined();
21
+ });
22
+
23
+ it('returns undefined when templateName is undefined', () => {
24
+ expect(buildTemplateNameDescription('Template name', undefined)).toBeUndefined();
25
+ });
26
+
27
+ it('renders a span wrapper when templateName is provided', () => {
28
+ const result = buildTemplateNameDescription('Template name', 'Welcome 01');
29
+ const wrapper = shallow(result);
30
+ expect(wrapper.type()).toBe('span');
31
+ });
32
+
33
+ it('renders label text in the first CapLabelInline with correct class', () => {
34
+ const result = buildTemplateNameDescription('Template name', 'Welcome 01');
35
+ const wrapper = shallow(result);
36
+ const firstLabel = wrapper.childAt(0);
37
+ expect(firstLabel.prop('className')).toBe('notification-template-label');
38
+ expect(firstLabel.prop('children')).toBe('Template name');
39
+ });
40
+
41
+ it('renders template name in the second CapLabelInline with correct class', () => {
42
+ const result = buildTemplateNameDescription('Template name', 'Welcome 01');
43
+ const wrapper = shallow(result);
44
+ const secondLabel = wrapper.childAt(1);
45
+ expect(secondLabel.prop('className')).toBe('notification-template-name');
46
+ expect(secondLabel.prop('children')).toBe('Welcome 01');
47
+ });
48
+
49
+ it('both CapLabelInline elements have type="label1"', () => {
50
+ const result = buildTemplateNameDescription('Template name', 'Welcome 01');
51
+ const wrapper = shallow(result);
52
+ wrapper.children().forEach((node) => {
53
+ expect(node.prop('type')).toBe('label1');
54
+ });
55
+ });
56
+ });
package/utils/v2common.js CHANGED
@@ -1,3 +1,21 @@
1
+ import React from 'react';
2
+ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
3
+
4
+ /**
5
+ * Builds a JSX description for template archive/unarchive notifications.
6
+ * @param {string} labelText - Localised label (e.g. "Template name ")
7
+ * @param {string} templateName - The template name to display
8
+ */
9
+ export const buildTemplateNameDescription = (labelText, templateName) => (
10
+ templateName
11
+ ? (
12
+ <span>
13
+ <CapLabel.CapLabelInline type="label1" className="notification-template-label">{labelText}</CapLabel.CapLabelInline>
14
+ <CapLabel.CapLabelInline type="label1" className="notification-template-name">{templateName}</CapLabel.CapLabelInline>
15
+ </span>
16
+ )
17
+ : undefined
18
+ );
1
19
 
2
20
  // returns query items as obj when query string is passed
3
21
  /**
@@ -79,7 +79,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
79
79
 
80
80
  componentDidUpdate(prevProps, prevState) {
81
81
  if (this.props.tags !== prevProps.tags || this.state.searchValue !== prevState.searchValue) {
82
- const temp = this.renderTags(this.props.tags, this.state.searchValue);
82
+ const temp = this.renderTags(this.props.tags);
83
83
  this.setState({
84
84
  tagsList: temp,
85
85
  });
@@ -136,14 +136,13 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
136
136
  const tagNameWithoutUnderscore = tagName.replace(/_/g, " ");
137
137
  const searchStringLower = _.toLower(value);
138
138
  if (_.has(val, "subtags")) {
139
- if (
140
- val?.name
139
+ const temp = this.getSearchedExpandedKeys(val?.subtags, value, nodeKey);
140
+ const nodeMatches = val?.name
141
141
  && (tagName.includes(searchStringLower)
142
- || tagNameWithoutUnderscore.includes(searchStringLower))
143
- ) {
142
+ || tagNameWithoutUnderscore.includes(searchStringLower));
143
+ if (nodeMatches || temp.length > 0) {
144
144
  list.push(nodeKey);
145
145
  }
146
- const temp = this.getSearchedExpandedKeys(val?.subtags, value, nodeKey);
147
146
  list = list.concat(temp);
148
147
  } else if (
149
148
  val?.name
@@ -57,7 +57,7 @@ export const CapTagListWithInput = (props) => {
57
57
 
58
58
  return (
59
59
  <CapColumn style={containerStyle}>
60
- <CapRow align="middle" type="flex">
60
+ <CapRow align="middle" type="flex" noWrap>
61
61
  {showHeading && headingText && (
62
62
  <CapHeading type={headingType} style={headingStyle}>
63
63
  {headingText}
@@ -18,7 +18,7 @@ import CapRow from '@capillarytech/cap-ui-library/CapRow';
18
18
  import { ANDROID, IOS } from '../constants';
19
19
  import messages from '../messages';
20
20
  import { getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../../v2Containers/Whatsapp/utils';
21
- import { QUICK_REPLY, PHONE_NUMBER, WHATSAPP_CATEGORIES } from '../../../v2Containers/Whatsapp/constants';
21
+ import { QUICK_REPLY, PHONE_NUMBER, WHATSAPP_CATEGORIES, TEMPLATE_VARIABLE_REGEX } from '../../../v2Containers/Whatsapp/constants';
22
22
  import videoPlay from '../../../assets/videoPlay.svg';
23
23
  import whatsappImageEmptyPreview from '../../TemplatePreview/assets/images/empty_image_preview.svg';
24
24
  import whatsappVideoEmptyPreview from '../../TemplatePreview/assets/images/empty_video_preview.svg';
@@ -27,6 +27,8 @@ import whatsappVideoEmptyPreview from '../../TemplatePreview/assets/images/empty
27
27
  const whatsappMobileAndroid = require('../../../assets/whatsapp_android.png');
28
28
  const whatsappMobileIos = require('../../../assets/whatsapp_ios.png');
29
29
 
30
+ const { CapLabelInline } = CapLabel;
31
+
30
32
  const WhatsAppPreviewContent = ({
31
33
  content,
32
34
  device,
@@ -215,11 +217,21 @@ const WhatsAppPreviewContent = ({
215
217
 
216
218
  {/* Image Preview */}
217
219
  {content?.whatsappImageSrc && (
218
- <CapImage
219
- src={content.whatsappImageSrc}
220
- className="whatsapp-image"
221
- alt={formatMessage(messages.previewGenerated)}
222
- />
220
+ TEMPLATE_VARIABLE_REGEX.test(content.whatsappImageSrc) ? (
221
+ <CapRow className="whatsapp-image-url-placeholder">
222
+ <CapIcon type="picture" size="s" className="whatsapp-image-url-placeholder-icon" />
223
+ <CapLabelInline type="label2" className="whatsapp-image-url-placeholder-text">
224
+ {content.whatsappImageSrc}
225
+ </CapLabelInline>
226
+ </CapRow>
227
+ ) : (
228
+ <CapImage
229
+ src={content.whatsappImageSrc}
230
+ className="whatsapp-image"
231
+ alt={formatMessage(messages.previewGenerated)}
232
+ onError={(e) => { e.target.src = whatsappImageEmptyPreview; }}
233
+ />
234
+ )
223
235
  )}
224
236
 
225
237
  {/* Video Preview */}
@@ -941,6 +941,33 @@
941
941
  }
942
942
  }
943
943
 
944
+ .whatsapp-image-url-placeholder {
945
+ display: flex;
946
+ flex-direction: column;
947
+ align-items: center;
948
+ justify-content: center;
949
+ gap: $CAP_SPACE_08;
950
+ width: 100%;
951
+ min-height: 5rem;
952
+ max-height: 8.786rem;
953
+ margin-bottom: $CAP_SPACE_04;
954
+ background-color: $CAP_G09;
955
+ border-radius: $CAP_SPACE_04;
956
+ padding: $CAP_SPACE_08;
957
+
958
+ .whatsapp-image-url-placeholder-icon {
959
+ color: $CAP_G05;
960
+ font-size: 1.5rem;
961
+ }
962
+
963
+ .whatsapp-image-url-placeholder-text {
964
+ font-size: $FONT_SIZE_S;
965
+ color: $CAP_G04;
966
+ text-align: center;
967
+ word-break: break-all;
968
+ }
969
+ }
970
+
944
971
  .whatsapp-image {
945
972
  width: 100%;
946
973
  max-height: 8.786rem;
@@ -26,6 +26,7 @@ jest.mock('../../../../v2Containers/Whatsapp/constants', () => ({
26
26
  WHATSAPP_CATEGORIES: {
27
27
  authentication: 'authentication',
28
28
  },
29
+ TEMPLATE_VARIABLE_REGEX: /\{\{.*?\}\}/,
29
30
  }));
30
31
 
31
32
  // Convert messages object to format expected by IntlProvider
@@ -238,6 +239,53 @@ describe('WhatsAppPreviewContent', () => {
238
239
  expect(image.getAttribute('src')).toBe('https://image.url');
239
240
  });
240
241
 
242
+ it('should render placeholder when whatsappImageSrc is a template variable', () => {
243
+ const props = {
244
+ ...defaultProps,
245
+ content: {
246
+ templateMsg: 'Message',
247
+ whatsappImageSrc: '{{imageUrl}}',
248
+ },
249
+ };
250
+
251
+ const { container } = render(
252
+ <TestWrapper>
253
+ <ComponentToRender {...props} />
254
+ </TestWrapper>
255
+ );
256
+
257
+ expect(container.querySelector('.whatsapp-image-url-placeholder')).toBeTruthy();
258
+ expect(container.querySelector('.whatsapp-image-url-placeholder-icon')).toBeTruthy();
259
+ expect(container.querySelector('.whatsapp-image-url-placeholder-text').textContent).toBe('{{imageUrl}}');
260
+ expect(container.querySelector('img.whatsapp-image')).toBeFalsy();
261
+ });
262
+
263
+ it('should fall back to empty preview image when image fails to load', () => {
264
+ const props = {
265
+ ...defaultProps,
266
+ content: {
267
+ templateMsg: 'Message',
268
+ whatsappImageSrc: 'https://broken.url/image.jpg',
269
+ },
270
+ };
271
+
272
+ const { container } = render(
273
+ <TestWrapper>
274
+ <ComponentToRender {...props} />
275
+ </TestWrapper>
276
+ );
277
+
278
+ const image = container.querySelector('img.whatsapp-image');
279
+ expect(image).toBeTruthy();
280
+ expect(image.getAttribute('src')).toBe('https://broken.url/image.jpg');
281
+
282
+ // Trigger onError to swap src to fallback
283
+ const errorEvent = new Event('error');
284
+ image.dispatchEvent(errorEvent);
285
+
286
+ expect(image.getAttribute('src')).not.toBe('https://broken.url/image.jpg');
287
+ });
288
+
241
289
  it('should render video preview when whatsappVideoPreviewImg is present', () => {
242
290
  const props = {
243
291
  ...defaultProps,
@@ -816,6 +816,27 @@
816
816
  }
817
817
  }
818
818
 
819
+ .whatsapp-image-url-placeholder {
820
+ display: flex;
821
+ flex-direction: column;
822
+ align-items: center;
823
+ justify-content: center;
824
+ background-color: $CAP_G09;
825
+ border-radius: $CAP_SPACE_04;
826
+ width: 100%;
827
+ min-height: 5rem;
828
+ max-height: 8.786rem;
829
+ margin-bottom: $CAP_SPACE_04;
830
+ .whatsapp-image-url-placeholder-text {
831
+ font-size: 0.786rem;
832
+ color: $CAP_G04;
833
+ margin-top: $CAP_SPACE_04;
834
+ text-align: center;
835
+ word-break: break-all;
836
+ padding: 0 $CAP_SPACE_04;
837
+ }
838
+ }
839
+
819
840
  .msg-container-carousel {
820
841
  display: flex;
821
842
  .scroll-container {
@@ -972,7 +993,7 @@
972
993
  }
973
994
 
974
995
  .character-count-col{
975
- margin-top: 8px;
996
+ margin: $CAP_SPACE_12 0;
976
997
  text-align: center;
977
998
  width: 100%;
978
999
  .optout-tag-heading{
@@ -48,7 +48,7 @@ import { TEMPLATE, IMAGE_CAROUSEL, IMAGE, STICKER, TEXT, IMAGE_MAP, VIDEO } from
48
48
  import CapFacebookPreview from '../../v2Containers/CapFacebookPreview';
49
49
  import WhatsappStatusContainer from '../WhatsappStatusContainer';
50
50
  import { getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../v2Containers/Whatsapp/utils';
51
- import { QUICK_REPLY, WHATSAPP_CATEGORIES, PHONE_NUMBER } from '../../v2Containers/Whatsapp/constants';
51
+ import { QUICK_REPLY, WHATSAPP_CATEGORIES, PHONE_NUMBER, TEMPLATE_VARIABLE_REGEX } from '../../v2Containers/Whatsapp/constants';
52
52
  import { RCS_BUTTON_TYPES, LEFT, HORIZONTAL, VERTICAL, RIGHT} from '../../v2Containers/Rcs/constants';
53
53
  import { ANDROID, INAPP_MESSAGE_LAYOUT_TYPES } from '../../v2Containers/InApp/constants';
54
54
  import { CAROUSEL } from '../../v2Containers/MobilePushNew/constants';
@@ -66,6 +66,8 @@ const lineVideoPlaceholder = require('../../assets/rich-video-placeholder.svg');
66
66
  const androidPushMessagePhone = require('./assets/images/Android_With_date_and_time.svg');
67
67
  const iPhonePushMessagePhone = require('./assets/images/iOS_With_date_and_time.svg');
68
68
 
69
+ const { CapLabelInline } = CapLabel;
70
+
69
71
  export class TemplatePreview extends React.Component { // eslint-disable-line react/prefer-stateless-function
70
72
  onPreviewContentClicked = (channel) => {
71
73
  const IOSContent = (this.props.templateDataRaw && this.props.templateDataRaw.versions.base.IOS) ||
@@ -659,8 +661,8 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
659
661
  </CapRow>
660
662
  </CapRow>
661
663
  {!this.props.showCount && (
662
- <CapRow>
663
- <CapColumn className="character-count-col" span={22}>
664
+ <CapRow type='flex' justify='center'>
665
+ <CapRow type='flex' align='middle' justify='center' className="character-count-col">
664
666
  {(channel || "").toLowerCase() === "sms" && (
665
667
  <CapHeading type="h3">
666
668
  <FormattedMessage
@@ -685,7 +687,7 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
685
687
  />
686
688
  </CapHeading>
687
689
  )}
688
- </CapColumn>
690
+ </CapRow>
689
691
  </CapRow>
690
692
  )}
691
693
  </CapRow>
@@ -1195,11 +1197,21 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1195
1197
  {content?.showUrlPreview
1196
1198
  && renderUrlPreview(content?.metaTagsDetails)}
1197
1199
  {content?.whatsappImageSrc && (
1198
- <CapImage
1199
- src={content.whatsappImageSrc}
1200
- className="whatsapp-image"
1201
- alt={formatMessage(messages.previewGenerated)}
1202
- />
1200
+ TEMPLATE_VARIABLE_REGEX.test(content.whatsappImageSrc) ? (
1201
+ <CapRow className="whatsapp-image-url-placeholder">
1202
+ <CapIcon type="picture" size="s" className="whatsapp-image-url-placeholder-icon" />
1203
+ <CapLabelInline type="label2" className="whatsapp-image-url-placeholder-text">
1204
+ {content.whatsappImageSrc}
1205
+ </CapLabelInline>
1206
+ </CapRow>
1207
+ ) : (
1208
+ <CapImage
1209
+ src={content.whatsappImageSrc}
1210
+ className="whatsapp-image"
1211
+ alt={formatMessage(messages.previewGenerated)}
1212
+ onError={(e) => { e.target.src = whatsappImageEmptyPreview; }}
1213
+ />
1214
+ )
1203
1215
  )}
1204
1216
  {content?.whatsappVideoPreviewImg && (
1205
1217
  <CapTooltip
@@ -42,6 +42,7 @@ exports[`Test Templates container Should render correct preview component for wh
42
42
  <CapImage
43
43
  alt="Preview is being generated"
44
44
  className="whatsapp-image"
45
+ onError={[Function]}
45
46
  src="https://crm-nightly-new-fileservice.s3.amazonaws.com/intouch_creative_assets/c9edc114-923b-4ac7-82d0-d6682213.jpg"
46
47
  />
47
48
  You have received {{1}} points