@capillarytech/creatives-library 8.0.256 → 8.0.258

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 (154) hide show
  1. package/constants/unified.js +1 -0
  2. package/package.json +1 -1
  3. package/services/api.js +5 -0
  4. package/translations/en.json +4 -3
  5. package/utils/common.js +6 -0
  6. package/utils/imageUrlUpload.js +141 -0
  7. package/utils/tests/transformerUtils.test.js +297 -0
  8. package/utils/transformerUtils.js +40 -0
  9. package/v2Components/CapImageUpload/constants.js +2 -0
  10. package/v2Components/CapImageUpload/index.js +65 -16
  11. package/v2Components/CapImageUpload/index.scss +4 -1
  12. package/v2Components/CapImageUpload/messages.js +5 -1
  13. package/v2Components/CapImageUrlUpload/constants.js +26 -0
  14. package/v2Components/CapImageUrlUpload/index.js +365 -0
  15. package/v2Components/CapImageUrlUpload/index.scss +35 -0
  16. package/v2Components/CapImageUrlUpload/messages.js +47 -0
  17. package/v2Containers/App/constants.js +5 -0
  18. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +4 -3
  19. package/v2Containers/CreativesContainer/SlideBoxContent.js +57 -2
  20. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -0
  21. package/v2Containers/CreativesContainer/constants.js +3 -0
  22. package/v2Containers/CreativesContainer/index.js +168 -0
  23. package/v2Containers/CreativesContainer/messages.js +4 -0
  24. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +210 -0
  25. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +304 -0
  26. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +36 -12
  27. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +8 -6
  28. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +100 -75
  29. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +72 -54
  30. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +250 -178
  31. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +16 -12
  32. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +48 -36
  33. package/v2Containers/Templates/ChannelTypeIllustration.js +13 -1
  34. package/v2Containers/Templates/_templates.scss +205 -0
  35. package/v2Containers/Templates/actions.js +2 -1
  36. package/v2Containers/Templates/constants.js +1 -0
  37. package/v2Containers/Templates/index.js +274 -34
  38. package/v2Containers/Templates/messages.js +24 -0
  39. package/v2Containers/Templates/reducer.js +2 -0
  40. package/v2Containers/Templates/tests/index.test.js +10 -0
  41. package/v2Containers/TemplatesV2/index.js +15 -7
  42. package/v2Containers/TemplatesV2/messages.js +4 -0
  43. package/v2Containers/WebPush/Create/components/BrandIconSection.js +108 -0
  44. package/v2Containers/WebPush/Create/components/ButtonForm.js +172 -0
  45. package/v2Containers/WebPush/Create/components/ButtonItem.js +101 -0
  46. package/v2Containers/WebPush/Create/components/ButtonList.js +145 -0
  47. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +164 -0
  48. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +463 -0
  49. package/v2Containers/WebPush/Create/components/FormActions.js +54 -0
  50. package/v2Containers/WebPush/Create/components/FormActions.test.js +163 -0
  51. package/v2Containers/WebPush/Create/components/MediaSection.js +142 -0
  52. package/v2Containers/WebPush/Create/components/MediaSection.test.js +341 -0
  53. package/v2Containers/WebPush/Create/components/MessageSection.js +103 -0
  54. package/v2Containers/WebPush/Create/components/MessageSection.test.js +268 -0
  55. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +87 -0
  56. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +210 -0
  57. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +54 -0
  58. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +143 -0
  59. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +86 -0
  60. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +16 -0
  61. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +41 -0
  62. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +54 -0
  63. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +37 -0
  64. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +21 -0
  65. package/v2Containers/WebPush/Create/components/_buttons.scss +246 -0
  66. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +554 -0
  67. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +607 -0
  68. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +633 -0
  69. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +666 -0
  70. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +74 -0
  71. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +78 -0
  72. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +138 -0
  73. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +406 -0
  74. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +30 -0
  75. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +151 -0
  76. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +104 -0
  77. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +538 -0
  78. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +122 -0
  79. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +633 -0
  80. package/v2Containers/WebPush/Create/index.js +1148 -0
  81. package/v2Containers/WebPush/Create/index.scss +134 -0
  82. package/v2Containers/WebPush/Create/messages.js +203 -0
  83. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +228 -0
  84. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +294 -0
  85. package/v2Containers/WebPush/Create/preview/PreviewContent.js +90 -0
  86. package/v2Containers/WebPush/Create/preview/PreviewControls.js +305 -0
  87. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +23 -0
  88. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +155 -0
  89. package/v2Containers/WebPush/Create/preview/assets/Light.svg +53 -0
  90. package/v2Containers/WebPush/Create/preview/assets/Top.svg +5 -0
  91. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +9 -0
  92. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +9 -0
  93. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  94. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  95. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +106 -0
  96. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +26 -0
  97. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +9 -0
  98. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +9 -0
  99. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +18 -0
  100. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +29 -0
  101. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +9 -0
  102. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +9 -0
  103. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +47 -0
  104. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +141 -0
  105. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +45 -0
  106. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +68 -0
  107. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +61 -0
  108. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +99 -0
  109. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +733 -0
  110. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +571 -0
  111. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +81 -0
  112. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +81 -0
  113. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +50 -0
  114. package/v2Containers/WebPush/Create/preview/constants.js +637 -0
  115. package/v2Containers/WebPush/Create/preview/notification-container.scss +79 -0
  116. package/v2Containers/WebPush/Create/preview/preview.scss +351 -0
  117. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +370 -0
  118. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +12 -0
  119. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +12 -0
  120. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +12 -0
  121. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +47 -0
  122. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +11 -0
  123. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +11 -0
  124. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +11 -0
  125. package/v2Containers/WebPush/Create/preview/styles/_base.scss +207 -0
  126. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +153 -0
  127. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +107 -0
  128. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +101 -0
  129. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +229 -0
  130. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +909 -0
  131. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +1081 -0
  132. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +723 -0
  133. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +1327 -0
  134. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +131 -0
  135. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +112 -0
  136. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +144 -0
  137. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +129 -0
  138. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +96 -0
  139. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +396 -0
  140. package/v2Containers/WebPush/Create/utils/previewUtils.js +89 -0
  141. package/v2Containers/WebPush/Create/utils/urlValidation.js +115 -0
  142. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +449 -0
  143. package/v2Containers/WebPush/Create/utils/validation.js +75 -0
  144. package/v2Containers/WebPush/Create/utils/validation.test.js +283 -0
  145. package/v2Containers/WebPush/actions.js +60 -0
  146. package/v2Containers/WebPush/constants.js +132 -0
  147. package/v2Containers/WebPush/index.js +2 -0
  148. package/v2Containers/WebPush/reducer.js +104 -0
  149. package/v2Containers/WebPush/sagas.js +119 -0
  150. package/v2Containers/WebPush/selectors.js +65 -0
  151. package/v2Containers/WebPush/tests/reducer.test.js +863 -0
  152. package/v2Containers/WebPush/tests/sagas.test.js +566 -0
  153. package/v2Containers/WebPush/tests/selectors.test.js +960 -0
  154. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +1272 -734
@@ -51,6 +51,7 @@ export const EXTENDED_TAG = 'ExtendedTagMessage';
51
51
  export const BADGES_UI_ENABLED = 'BADGES_UI_ENABLED';
52
52
  export const JP_LOCALE_HIDE_FEATURE = 'JP_LOCALE_HIDE_FEATURE';
53
53
  export const ENABLE_WECHAT = 'ENABLE_WECHAT';
54
+ export const ENABLE_WEBPUSH = 'ENABLE_WEBPUSH';
54
55
  export const ENABLE_CUSTOMER_BARCODE_TAG = 'ENABLE_CUSTOMER_BARCODE_TAG';
55
56
  export const EMAIL_UNSUBSCRIBE_TAG_MANDATORY = 'EMAIL_UNSUBSCRIBE_TAG_MANDATORY';
56
57
  export const ENABLE_AI_SUGGESTIONS = 'ENABLE_AI_SUGGESTIONS';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.256",
4
+ "version": "8.0.258",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -287,6 +287,11 @@ export const createMobilePushTemplateV2 = (template) => {
287
287
  return request(url, getAPICallObject('POST', template));
288
288
  };
289
289
 
290
+ export const createWebPushTemplate = (template) => {
291
+ const url = `${API_ENDPOINT}/templates/WEBPUSH`;
292
+ return request(url, getAPICallObject('POST', template));
293
+ };
294
+
290
295
  export const duplicateTemplate = ({id, channel}) => {
291
296
  const url = `${API_ENDPOINT}/templates/duplicate/${id}/${channel}`;
292
297
  return request(url, getAPICallObject('GET'));
@@ -165,7 +165,7 @@
165
165
  "creatives.componentsV2.CapDocumentUpload.imageDimenstionDescription": "Dimensions upto: {width}px x {height}px",
166
166
  "creatives.componentsV2.CapDocumentUpload.or": "OR",
167
167
  "creatives.componentsV2.CapDocumentUpload.uploadComputer": "Select from computer",
168
- "creatives.componentsV2.CapDocumentUpload.whatsappDocSize": "Size upto: {size}",
168
+ "creatives.componentsV2.CapDocumentUpload.whatsappDocSize": "Size up to: {size}",
169
169
  "creatives.componentsV2.CapImageUpload.aspectRatio": "Aspect ratio: 1:1",
170
170
  "creatives.componentsV2.CapImageUpload.dragAndDrop": "Drag and drop image here",
171
171
  "creatives.componentsV2.CapImageUpload.format": "Format: JPEG, JPG, PNG",
@@ -173,13 +173,13 @@
173
173
  "creatives.componentsV2.CapImageUpload.imageErrorDesc": "Please upload the image with allowed file extension, size, dimension and aspect ratio",
174
174
  "creatives.componentsV2.CapImageUpload.imageGallery": "Gallery",
175
175
  "creatives.componentsV2.CapImageUpload.imageReUpload": "Reupload",
176
- "creatives.componentsV2.CapImageUpload.imageSize": "Size upto: 2MB",
176
+ "creatives.componentsV2.CapImageUpload.imageSize": "Size up to: 2MB",
177
177
  "creatives.componentsV2.CapImageUpload.or": "OR",
178
178
  "creatives.componentsV2.CapImageUpload.uploadComputer": "Select from computer",
179
179
  "creatives.componentsV2.CapImageUpload.uploadGallery": "Gallery",
180
180
  "creatives.componentsV2.CapImageUpload.uploadImageDescription": "The relevant image that complements the message context.",
181
181
  "creatives.componentsV2.CapImageUpload.whatsappAspectRatio": "Max aspect ratio: 1.91:1",
182
- "creatives.componentsV2.CapImageUpload.whatsappImageSize": "Size upto: 5MB",
182
+ "creatives.componentsV2.CapImageUpload.whatsappImageSize": "Size up to: 5MB",
183
183
  "creatives.componentsV2.CapTagList.Cancel": "Cancel",
184
184
  "creatives.componentsV2.CapTagList.Ok": "Ok",
185
185
  "creatives.componentsV2.CapTagList.all": "All",
@@ -2022,6 +2022,7 @@
2022
2022
  "creatives.containersV2.Whatsapp.vietnamese": "Vietnamese",
2023
2023
  "creatives.containersV2.Whatsapp.whatsappCreateNotification": "{name} has been sent for approval",
2024
2024
  "creatives.containersV2.Whatsapp.zulu": "Zulu",
2025
+ "creatives.containersV2.WebPush.addLabels": "Add labels",
2025
2026
  "creatives.containersV2.addLabels": "Add labels",
2026
2027
  "creatives.containersV2.appName": "App Name",
2027
2028
  "creatives.containersV2.applyNow": "Apply now",
package/utils/common.js CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  EMAIL_UNSUBSCRIBE_TAG_MANDATORY,
22
22
  BADGES_ISSUE,
23
23
  ENABLE_WECHAT,
24
+ ENABLE_WEBPUSH,
24
25
  LIQUID_SUPPORT,
25
26
  ENABLE_NEW_MPUSH
26
27
  } from '../constants/unified';
@@ -116,6 +117,11 @@ export const hasWechatFeatureEnabled = Auth.hasFeatureAccess.bind(
116
117
  ENABLE_WECHAT,
117
118
  );
118
119
 
120
+ export const hasWebPushFeatureEnabled = Auth.hasFeatureAccess.bind(
121
+ null,
122
+ ENABLE_WEBPUSH,
123
+ );
124
+
119
125
  export const hasCustomerBarcodeFeatureEnabled = Auth.hasFeatureAccess.bind(
120
126
  null,
121
127
  ENABLE_CUSTOMER_BARCODE_TAG,
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Utility functions for uploading images from URLs
3
+ *
4
+ * NOTE: CORS-limited; will be replaced with backend implementation.
5
+ * Flow currently hidden (not removed) from frontend.
6
+ */
7
+
8
+ import {
9
+ DEFAULT_ALLOWED_CONTENT_TYPES,
10
+ MIME_TYPE_TO_EXTENSION,
11
+ DEFAULT_IMAGE_EXTENSION,
12
+ } from '../v2Components/CapImageUrlUpload/constants';
13
+
14
+ /**
15
+ * Fetches an image from a URL
16
+ *
17
+ * @param {string} url - The image URL to fetch
18
+ * @returns {Promise<Response>} - The fetch response object
19
+ * @throws {Error} - If the fetch fails (network/CORS error)
20
+ */
21
+ export const fetchImageFromUrl = async (url) => {
22
+ const trimmedUrl = url?.trim() || '';
23
+
24
+ if (!trimmedUrl) {
25
+ throw new Error('URL is required');
26
+ }
27
+
28
+ // CORS-limited: fails for images without proper CORS headers
29
+ const response = await fetch(trimmedUrl, {
30
+ method: 'GET',
31
+ redirect: 'follow',
32
+ mode: 'cors',
33
+ });
34
+
35
+ if (!response.ok) {
36
+ throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
37
+ }
38
+
39
+ return response;
40
+ };
41
+
42
+ /**
43
+ * Helper function to upload image from URL
44
+ * Fetches image, validates content type and size, converts to File, and uploads via uploadAsset
45
+ *
46
+ * @param {string} url - The image URL to upload
47
+ * @param {Function} formatMessage - React Intl formatMessage function
48
+ * @param {Object} messages - React Intl messages object
49
+ * @param {Function} uploadAssetFn - Function to upload the asset (file, type, fileParams)
50
+ * @param {string} fileNamePrefix - Prefix for the generated file name
51
+ * @param {number} maxSize - Maximum file size in bytes
52
+ * @param {string[]} allowedContentTypes - Array of allowed MIME types (defaults to DEFAULT_ALLOWED_CONTENT_TYPES)
53
+ * @returns {Promise<{success: boolean, error: string}>} - Result object with success status and error message
54
+ *
55
+ * @example
56
+ * const result = await uploadImageFromUrlHelper(
57
+ * 'https://example.com/image.jpg',
58
+ * formatMessage,
59
+ * messages,
60
+ * uploadAsset,
61
+ * 'my-image',
62
+ * 5000000,
63
+ * ['image/jpeg', 'image/png']
64
+ * );
65
+ */
66
+ export const uploadImageFromUrlHelper = async (
67
+ url,
68
+ formatMessage,
69
+ messages,
70
+ uploadAssetFn,
71
+ fileNamePrefix,
72
+ maxSize,
73
+ allowedContentTypes = DEFAULT_ALLOWED_CONTENT_TYPES,
74
+ ) => {
75
+ const trimmedUrl = url?.trim() || '';
76
+
77
+ try {
78
+ const response = await fetchImageFromUrl(trimmedUrl);
79
+
80
+ // Validate Content-Type
81
+ const contentType = response.headers?.get('Content-Type') || '';
82
+ const normalizedContentType = contentType.split(';')[0].toLowerCase().trim();
83
+
84
+ if (!allowedContentTypes.includes(normalizedContentType)) {
85
+ return {
86
+ success: false,
87
+ error: formatMessage(messages.imageTypeInvalid),
88
+ };
89
+ }
90
+
91
+ const blob = await response.blob();
92
+
93
+ if (blob.size > maxSize) {
94
+ return {
95
+ success: false,
96
+ error: formatMessage(messages.imageSizeInvalid),
97
+ };
98
+ }
99
+
100
+ // Load image to get dimensions and verify validity
101
+ return new Promise((resolve) => {
102
+ const img = new Image();
103
+ const objectUrl = URL.createObjectURL(blob);
104
+
105
+ img.onload = () => {
106
+ const extension = MIME_TYPE_TO_EXTENSION[normalizedContentType] || DEFAULT_IMAGE_EXTENSION;
107
+ const fileName = `${fileNamePrefix}.${extension}`;
108
+ const file = new File([blob], fileName, { type: blob.type });
109
+ const fileParams = {
110
+ width: img.width,
111
+ height: img.height,
112
+ error: false,
113
+ };
114
+
115
+ uploadAssetFn(file, 'image', fileParams);
116
+ URL.revokeObjectURL(objectUrl);
117
+
118
+ resolve({
119
+ success: true,
120
+ error: '',
121
+ });
122
+ };
123
+
124
+ img.onerror = () => {
125
+ URL.revokeObjectURL(objectUrl);
126
+ resolve({
127
+ success: false,
128
+ error: formatMessage(messages.imageLoadError),
129
+ });
130
+ };
131
+
132
+ img.src = objectUrl;
133
+ });
134
+ } catch (error) {
135
+ return {
136
+ success: false,
137
+ error: formatMessage(messages.imageLoadError),
138
+ };
139
+ }
140
+ };
141
+
@@ -15,6 +15,7 @@ import {
15
15
  RCS,
16
16
  LINE,
17
17
  VIBER,
18
+ WEBPUSH,
18
19
  } from "../../v2Containers/CreativesContainer/constants";
19
20
 
20
21
  describe("transformerUtils", () => {
@@ -1182,6 +1183,302 @@ describe("transformerUtils", () => {
1182
1183
  });
1183
1184
  });
1184
1185
 
1186
+ describe("WebPush payload transformation", () => {
1187
+ it("should transform WebPush data correctly with minimal data", () => {
1188
+ const mockData = {
1189
+ channel: WEBPUSH,
1190
+ messageSubject: "test webpush notification"
1191
+ };
1192
+
1193
+ const result = transformChannelPayload(mockData);
1194
+
1195
+ expect(result.centralCommsPayload).toBeDefined();
1196
+ expect(result.centralCommsPayload.channel).toEqual(WEBPUSH);
1197
+ expect(result.centralCommsPayload.webpushMessageContent.messageSubject).toEqual(
1198
+ "test webpush notification"
1199
+ );
1200
+ expect(result.centralCommsPayload.clientName).toEqual("VENENO");
1201
+ });
1202
+
1203
+ it("should transform WebPush data with all options", () => {
1204
+ const mockData = {
1205
+ channel: WEBPUSH,
1206
+ messageSubject: "test webpush notification",
1207
+ messageContent: {
1208
+ content: {
1209
+ accountId: 12345,
1210
+ content: {
1211
+ title: "WebPush Title",
1212
+ message: "WebPush Message",
1213
+ iconImageUrl: "https://example.com/icon.png",
1214
+ cta: {
1215
+ actionLink: "https://example.com/action",
1216
+ actionText: "Click Here"
1217
+ },
1218
+ expandableDetails: {
1219
+ style: "BIG_TEXT",
1220
+ message: "Expanded Message"
1221
+ }
1222
+ },
1223
+ offers: [
1224
+ { id: "offer-1", title: "Offer 1" },
1225
+ { id: "offer-2", title: "Offer 2" }
1226
+ ]
1227
+ }
1228
+ }
1229
+ };
1230
+
1231
+ const options = {
1232
+ loyaltyMetaData: {
1233
+ actionId: "action-123",
1234
+ ouId: 456,
1235
+ clientName: "TestClient",
1236
+ module: "TestModule",
1237
+ transformedMessageDetails: {
1238
+ webpushDeliverySettings: {
1239
+ sendDate: "2023-01-01",
1240
+ priority: "HIGH"
1241
+ }
1242
+ }
1243
+ }
1244
+ };
1245
+
1246
+ const result = transformChannelPayload(mockData, options);
1247
+
1248
+ // Check base payload
1249
+ expect(result.centralCommsPayload.sourceEntityId).toEqual("action-123");
1250
+ expect(result.centralCommsPayload.ouId).toEqual(456);
1251
+ expect(result.centralCommsPayload.clientName).toEqual("TestClient");
1252
+ expect(result.centralCommsPayload.module).toEqual("TestModule");
1253
+
1254
+ // Check WebPush-specific content
1255
+ expect(result.centralCommsPayload.webpushMessageContent.channel).toEqual(WEBPUSH);
1256
+ expect(result.centralCommsPayload.webpushMessageContent.accountId).toEqual(12345);
1257
+ expect(result.centralCommsPayload.webpushMessageContent.messageSubject).toEqual(
1258
+ "test webpush notification"
1259
+ );
1260
+ expect(result.centralCommsPayload.webpushMessageContent.content.title).toEqual(
1261
+ "WebPush Title"
1262
+ );
1263
+ expect(result.centralCommsPayload.webpushMessageContent.content.message).toEqual(
1264
+ "WebPush Message"
1265
+ );
1266
+ expect(result.centralCommsPayload.webpushMessageContent.content.iconImageUrl).toEqual(
1267
+ "https://example.com/icon.png"
1268
+ );
1269
+ expect(result.centralCommsPayload.webpushMessageContent.content.cta).toEqual({
1270
+ actionLink: "https://example.com/action",
1271
+ actionText: "Click Here"
1272
+ });
1273
+ expect(result.centralCommsPayload.webpushMessageContent.content.expandableDetails).toEqual({
1274
+ style: "BIG_TEXT",
1275
+ message: "Expanded Message"
1276
+ });
1277
+ expect(result.centralCommsPayload.webpushMessageContent.offers).toEqual([
1278
+ { id: "offer-1", title: "Offer 1" },
1279
+ { id: "offer-2", title: "Offer 2" }
1280
+ ]);
1281
+
1282
+ // Check delivery settings
1283
+ expect(result.centralCommsPayload.webpushDeliverySettings).toEqual({
1284
+ sendDate: "2023-01-01",
1285
+ priority: "HIGH"
1286
+ });
1287
+ });
1288
+
1289
+ it("should handle missing messageContent structure", () => {
1290
+ const mockData = {
1291
+ channel: WEBPUSH,
1292
+ messageSubject: "test webpush notification"
1293
+ };
1294
+
1295
+ const result = transformChannelPayload(mockData);
1296
+
1297
+ expect(result.centralCommsPayload.webpushMessageContent.accountId).toEqual(0);
1298
+ expect(result.centralCommsPayload.webpushMessageContent.content.title).toEqual("");
1299
+ expect(result.centralCommsPayload.webpushMessageContent.content.message).toEqual("");
1300
+ expect(result.centralCommsPayload.webpushMessageContent.offers).toEqual([]);
1301
+ });
1302
+
1303
+ it("should handle missing content properties", () => {
1304
+ const mockData = {
1305
+ channel: WEBPUSH,
1306
+ messageSubject: "test webpush notification",
1307
+ messageContent: {
1308
+ content: {
1309
+ accountId: 12345,
1310
+ content: {}
1311
+ }
1312
+ }
1313
+ };
1314
+
1315
+ const result = transformChannelPayload(mockData);
1316
+
1317
+ expect(result.centralCommsPayload.webpushMessageContent.accountId).toEqual(12345);
1318
+ expect(result.centralCommsPayload.webpushMessageContent.content.title).toEqual("");
1319
+ expect(result.centralCommsPayload.webpushMessageContent.content.message).toEqual("");
1320
+ expect(result.centralCommsPayload.webpushMessageContent.content.iconImageUrl).toBeUndefined();
1321
+ expect(result.centralCommsPayload.webpushMessageContent.content.cta).toBeUndefined();
1322
+ expect(result.centralCommsPayload.webpushMessageContent.content.expandableDetails).toBeUndefined();
1323
+ });
1324
+
1325
+ it("should conditionally include iconImageUrl only when present", () => {
1326
+ const mockDataWithoutIcon = {
1327
+ channel: WEBPUSH,
1328
+ messageSubject: "test",
1329
+ messageContent: {
1330
+ content: {
1331
+ content: {
1332
+ title: "Title",
1333
+ message: "Message"
1334
+ }
1335
+ }
1336
+ }
1337
+ };
1338
+
1339
+ const resultWithoutIcon = transformChannelPayload(mockDataWithoutIcon);
1340
+ expect(resultWithoutIcon.centralCommsPayload.webpushMessageContent.content.iconImageUrl).toBeUndefined();
1341
+
1342
+ const mockDataWithIcon = {
1343
+ channel: WEBPUSH,
1344
+ messageSubject: "test",
1345
+ messageContent: {
1346
+ content: {
1347
+ content: {
1348
+ title: "Title",
1349
+ message: "Message",
1350
+ iconImageUrl: "https://example.com/icon.png"
1351
+ }
1352
+ }
1353
+ }
1354
+ };
1355
+
1356
+ const resultWithIcon = transformChannelPayload(mockDataWithIcon);
1357
+ expect(resultWithIcon.centralCommsPayload.webpushMessageContent.content.iconImageUrl).toEqual(
1358
+ "https://example.com/icon.png"
1359
+ );
1360
+ });
1361
+
1362
+ it("should conditionally include cta only when present", () => {
1363
+ const mockDataWithoutCta = {
1364
+ channel: WEBPUSH,
1365
+ messageSubject: "test",
1366
+ messageContent: {
1367
+ content: {
1368
+ content: {
1369
+ title: "Title",
1370
+ message: "Message"
1371
+ }
1372
+ }
1373
+ }
1374
+ };
1375
+
1376
+ const resultWithoutCta = transformChannelPayload(mockDataWithoutCta);
1377
+ expect(resultWithoutCta.centralCommsPayload.webpushMessageContent.content.cta).toBeUndefined();
1378
+
1379
+ const mockDataWithCta = {
1380
+ channel: WEBPUSH,
1381
+ messageSubject: "test",
1382
+ messageContent: {
1383
+ content: {
1384
+ content: {
1385
+ title: "Title",
1386
+ message: "Message",
1387
+ cta: {
1388
+ actionLink: "https://example.com",
1389
+ actionText: "Click"
1390
+ }
1391
+ }
1392
+ }
1393
+ }
1394
+ };
1395
+
1396
+ const resultWithCta = transformChannelPayload(mockDataWithCta);
1397
+ expect(resultWithCta.centralCommsPayload.webpushMessageContent.content.cta).toEqual({
1398
+ actionLink: "https://example.com",
1399
+ actionText: "Click"
1400
+ });
1401
+ });
1402
+
1403
+ it("should conditionally include expandableDetails only when present", () => {
1404
+ const mockDataWithoutExpandable = {
1405
+ channel: WEBPUSH,
1406
+ messageSubject: "test",
1407
+ messageContent: {
1408
+ content: {
1409
+ content: {
1410
+ title: "Title",
1411
+ message: "Message"
1412
+ }
1413
+ }
1414
+ }
1415
+ };
1416
+
1417
+ const resultWithoutExpandable = transformChannelPayload(mockDataWithoutExpandable);
1418
+ expect(resultWithoutExpandable.centralCommsPayload.webpushMessageContent.content.expandableDetails).toBeUndefined();
1419
+
1420
+ const mockDataWithExpandable = {
1421
+ channel: WEBPUSH,
1422
+ messageSubject: "test",
1423
+ messageContent: {
1424
+ content: {
1425
+ content: {
1426
+ title: "Title",
1427
+ message: "Message",
1428
+ expandableDetails: {
1429
+ style: "BIG_TEXT",
1430
+ message: "Expanded"
1431
+ }
1432
+ }
1433
+ }
1434
+ }
1435
+ };
1436
+
1437
+ const resultWithExpandable = transformChannelPayload(mockDataWithExpandable);
1438
+ expect(resultWithExpandable.centralCommsPayload.webpushMessageContent.content.expandableDetails).toEqual({
1439
+ style: "BIG_TEXT",
1440
+ message: "Expanded"
1441
+ });
1442
+ });
1443
+
1444
+ it("should handle undefined webpushDeliverySettings", () => {
1445
+ const mockData = {
1446
+ channel: WEBPUSH,
1447
+ messageSubject: "test webpush notification"
1448
+ };
1449
+
1450
+ const options = {
1451
+ loyaltyMetaData: {
1452
+ transformedMessageDetails: {}
1453
+ }
1454
+ };
1455
+
1456
+ const result = transformChannelPayload(mockData, options);
1457
+
1458
+ expect(result.centralCommsPayload.webpushDeliverySettings).toEqual({});
1459
+ });
1460
+
1461
+ it("should handle empty offers array", () => {
1462
+ const mockData = {
1463
+ channel: WEBPUSH,
1464
+ messageSubject: "test",
1465
+ messageContent: {
1466
+ content: {
1467
+ content: {
1468
+ title: "Title",
1469
+ message: "Message"
1470
+ },
1471
+ offers: []
1472
+ }
1473
+ }
1474
+ };
1475
+
1476
+ const result = transformChannelPayload(mockData);
1477
+
1478
+ expect(result.centralCommsPayload.webpushMessageContent.offers).toEqual([]);
1479
+ });
1480
+ });
1481
+
1185
1482
  describe("getTemplateDiffState", () => {
1186
1483
  it("should return false for unsupported channel", () => {
1187
1484
  const result = getTemplateDiffState("UNSUPPORTED_CHANNEL", {}, {});
@@ -10,6 +10,7 @@ import {
10
10
  RCS,
11
11
  LINE,
12
12
  VIBER,
13
+ WEBPUSH,
13
14
  EMF,
14
15
  VENENO,
15
16
  TEXT,
@@ -63,6 +64,8 @@ export const transformChannelPayload = (data, options = {}) => {
63
64
  return transformLinePayload(data, options);
64
65
  case VIBER:
65
66
  return transformViberPayload(data, options);
67
+ case WEBPUSH:
68
+ return transformWebpushPayload(data, options);
66
69
  default:
67
70
  return data; // Return unchanged for unsupported channels
68
71
  }
@@ -317,6 +320,43 @@ const transformViberPayload = (viberData, options = {}) => {
317
320
  return payload;
318
321
  };
319
322
 
323
+ /**
324
+ * Transforms WebPush data to the required payload format
325
+ * @param {Object} webpushData - Current WebPush data
326
+ * @param {Object} options - Additional options (ouId, sourceEntityId, etc.)
327
+ * @returns {Object} - Transformed WebPush payload
328
+ */
329
+ const transformWebpushPayload = (webpushData, options = {}) => {
330
+ const { loyaltyMetaData = {} } = options;
331
+ const { transformedMessageDetails = {} } = loyaltyMetaData;
332
+ const { webpushDeliverySettings = {} } = transformedMessageDetails || {};
333
+
334
+ // Get base payload structure
335
+ const payload = createBasePayload(WEBPUSH, loyaltyMetaData);
336
+
337
+ // Extract webpush content from messageContent.content.content structure
338
+ const webpushContent = webpushData?.messageContent?.content?.content || {};
339
+ const accountId = webpushData?.messageContent?.content?.accountId || 0;
340
+
341
+ // Add WebPush-specific properties
342
+ payload.centralCommsPayload.webpushMessageContent = {
343
+ channel: WEBPUSH,
344
+ accountId,
345
+ content: {
346
+ title: webpushContent?.title || '',
347
+ message: webpushContent?.message || '',
348
+ ...(webpushContent?.iconImageUrl && { iconImageUrl: webpushContent.iconImageUrl }),
349
+ ...(webpushContent?.cta && { cta: webpushContent.cta }),
350
+ ...(webpushContent?.expandableDetails && { expandableDetails: webpushContent.expandableDetails }),
351
+ },
352
+ messageSubject: webpushData?.messageSubject || "",
353
+ offers: webpushData?.messageContent?.content?.offers || [],
354
+ };
355
+ payload.centralCommsPayload.webpushDeliverySettings = webpushDeliverySettings;
356
+
357
+ return payload;
358
+ };
359
+
320
360
  // Checks if the template has changed
321
361
  export const getTemplateDiffState = (channel, oldData, newData) => {
322
362
  switch (channel.toUpperCase()) {
@@ -4,4 +4,6 @@ export const MAX_SUPPORTED_IMAGE_SIZE = {
4
4
  INAPP: 5,
5
5
  WHATSAPP: 5,
6
6
  MOBILEPUSH: 2,
7
+ WEBPUSH: 5,
8
+ WEBPUSH_BRAND_ICON: 1
7
9
  };