@capillarytech/creatives-library 8.0.241 → 8.0.242-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 (119) hide show
  1. package/package.json +1 -1
  2. package/sagas/__tests__/assetPolling.test.js +607 -0
  3. package/sagas/assetPolling.js +156 -0
  4. package/services/api.js +16 -0
  5. package/services/tests/api.test.js +124 -0
  6. package/translations/en.json +1 -0
  7. package/utils/assetStatusConstants.js +12 -0
  8. package/utils/asyncAssetUpload.js +161 -0
  9. package/utils/tests/asyncAssetUpload.test.js +292 -0
  10. package/utils/transformerUtils.js +42 -0
  11. package/v2Components/CapImageUpload/constants.js +2 -0
  12. package/v2Components/CapImageUpload/index.js +54 -14
  13. package/v2Components/CapImageUpload/index.scss +4 -1
  14. package/v2Components/CapImageUpload/messages.js +4 -0
  15. package/v2Components/CapImageUrlUpload/constants.js +19 -0
  16. package/v2Components/CapImageUrlUpload/index.js +455 -0
  17. package/v2Components/CapImageUrlUpload/index.scss +35 -0
  18. package/v2Components/CapImageUrlUpload/messages.js +47 -0
  19. package/v2Containers/App/constants.js +5 -0
  20. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +1 -0
  21. package/v2Containers/CreativesContainer/SlideBoxContent.js +57 -2
  22. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -0
  23. package/v2Containers/CreativesContainer/constants.js +2 -0
  24. package/v2Containers/CreativesContainer/index.js +152 -0
  25. package/v2Containers/CreativesContainer/messages.js +4 -0
  26. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  27. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  28. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +25 -0
  29. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +18 -0
  30. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +46 -0
  31. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +4 -0
  32. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +8 -0
  33. package/v2Containers/Templates/ChannelTypeIllustration.js +13 -1
  34. package/v2Containers/Templates/_templates.scss +203 -0
  35. package/v2Containers/Templates/actions.js +2 -1
  36. package/v2Containers/Templates/constants.js +1 -0
  37. package/v2Containers/Templates/index.js +273 -30
  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 +3 -2
  42. package/v2Containers/TemplatesV2/messages.js +4 -0
  43. package/v2Containers/WebPush/Create/components/ButtonForm.js +175 -0
  44. package/v2Containers/WebPush/Create/components/ButtonItem.js +101 -0
  45. package/v2Containers/WebPush/Create/components/ButtonList.js +144 -0
  46. package/v2Containers/WebPush/Create/components/_buttons.scss +246 -0
  47. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +554 -0
  48. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +607 -0
  49. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +633 -0
  50. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +666 -0
  51. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +74 -0
  52. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +80 -0
  53. package/v2Containers/WebPush/Create/index.js +1755 -0
  54. package/v2Containers/WebPush/Create/index.scss +123 -0
  55. package/v2Containers/WebPush/Create/messages.js +199 -0
  56. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +241 -0
  57. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +290 -0
  58. package/v2Containers/WebPush/Create/preview/PreviewContent.js +81 -0
  59. package/v2Containers/WebPush/Create/preview/PreviewControls.js +240 -0
  60. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +23 -0
  61. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +144 -0
  62. package/v2Containers/WebPush/Create/preview/assets/Light.svg +53 -0
  63. package/v2Containers/WebPush/Create/preview/assets/Top.svg +5 -0
  64. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  65. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  66. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +106 -0
  67. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +26 -0
  68. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +18 -0
  69. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +29 -0
  70. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +44 -0
  71. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +110 -0
  72. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +45 -0
  73. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +72 -0
  74. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +55 -0
  75. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +70 -0
  76. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +512 -0
  77. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +77 -0
  78. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +527 -0
  79. package/v2Containers/WebPush/Create/preview/constants.js +162 -0
  80. package/v2Containers/WebPush/Create/preview/notification-container.scss +104 -0
  81. package/v2Containers/WebPush/Create/preview/preview.scss +409 -0
  82. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +300 -0
  83. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +12 -0
  84. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +12 -0
  85. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +12 -0
  86. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +303 -0
  87. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +11 -0
  88. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +11 -0
  89. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +11 -0
  90. package/v2Containers/WebPush/Create/preview/styles/_base.scss +188 -0
  91. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +106 -0
  92. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +107 -0
  93. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +75 -0
  94. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +174 -0
  95. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +909 -0
  96. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +1077 -0
  97. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +723 -0
  98. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +943 -0
  99. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +128 -0
  100. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +121 -0
  101. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +144 -0
  102. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +127 -0
  103. package/v2Containers/WebPush/Create/utils/urlValidation.js +116 -0
  104. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +449 -0
  105. package/v2Containers/WebPush/actions.js +60 -0
  106. package/v2Containers/WebPush/constants.js +108 -0
  107. package/v2Containers/WebPush/index.js +2 -0
  108. package/v2Containers/WebPush/reducer.js +104 -0
  109. package/v2Containers/WebPush/sagas.js +119 -0
  110. package/v2Containers/WebPush/selectors.js +65 -0
  111. package/v2Containers/WebPush/tests/reducer.test.js +863 -0
  112. package/v2Containers/WebPush/tests/sagas.test.js +566 -0
  113. package/v2Containers/WebPush/tests/selectors.test.js +960 -0
  114. package/v2Containers/Whatsapp/constants.js +9 -0
  115. package/v2Containers/Whatsapp/reducer.js +34 -5
  116. package/v2Containers/Whatsapp/sagas.js +61 -10
  117. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +132 -0
  118. package/v2Containers/Whatsapp/tests/reducer.test.js +188 -0
  119. package/v2Containers/Whatsapp/tests/saga.test.js +420 -7
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Centralized Asset Status Polling Saga
3
+ * Reusable across all channels (WhatsApp, MobilePush, Email, SMS, etc.)
4
+ *
5
+ * Usage:
6
+ * import { pollAssetStatus } from '../../sagas/assetPolling';
7
+ *
8
+ * yield call(pollAssetStatus, {
9
+ * type: 'video',
10
+ * assetId: 'asset-123',
11
+ * onProcessing: (data) => ({ type: 'ASSET_PROCESSING', payload: data }),
12
+ * onCompleted: (asset) => ({ type: 'ASSET_COMPLETED', payload: asset }),
13
+ * onFailed: (error) => ({ type: 'ASSET_FAILED', payload: error }),
14
+ * onTimeout: () => ({ type: 'ASSET_TIMEOUT' })
15
+ * });
16
+ */
17
+
18
+ import { call, put } from 'redux-saga/effects';
19
+ import { getAssetStatus } from '../services/api';
20
+ import { ASSET_STATUS } from '../utils/assetStatusConstants';
21
+
22
+ /**
23
+ * Delay utility function compatible with redux-saga 0.16.2
24
+ * Returns a promise that resolves after the specified milliseconds
25
+ * @param {number} ms - Milliseconds to delay
26
+ * @returns {Promise} Promise that resolves after delay
27
+ */
28
+ export function delay(ms) {
29
+ return new Promise((resolve) => {
30
+ setTimeout(resolve, ms);
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Generic asset status polling saga
36
+ * @param {Object} config - Polling configuration
37
+ * @param {string} config.type - Asset type (image, video, document, gif)
38
+ * @param {string} config.assetId - Asset ID to poll
39
+ * @param {Function} config.onCompleted - Action creator for completed state
40
+ * @param {Function} config.onFailed - Action creator for failed state
41
+ * @param {Function} config.onTimeout - Action creator for timeout state
42
+ * @param {number} config.maxDuration - Max polling duration in ms (default: 90000)
43
+ * @param {number} config.initialDelay - Initial delay before first poll in ms (default: 1000)
44
+ * @param {number} config.pollInterval - Interval between polls in ms (default: 2000)
45
+ */
46
+ export function* pollAssetStatus(config) {
47
+ const {
48
+ type,
49
+ assetId,
50
+ onCompleted,
51
+ onFailed,
52
+ onTimeout,
53
+ maxDuration = 90000, // 90 seconds default
54
+ initialDelay: configInitialDelay = 1000, // 1 second default
55
+ pollInterval = 2000, // 2 seconds default
56
+ } = config;
57
+
58
+ const startTime = Date.now();
59
+ let isFirstPoll = true;
60
+
61
+ try {
62
+ while (true) {
63
+ // Initial delay, then regular interval
64
+ // Using call() for compatibility with redux-saga 0.16.2
65
+ yield call(delay, isFirstPoll ? configInitialDelay : pollInterval);
66
+ isFirstPoll = false;
67
+
68
+ // Check timeout
69
+ if (Date.now() - startTime > maxDuration) {
70
+ if (onTimeout) {
71
+ const timeoutAction = onTimeout({
72
+ assetId,
73
+ message: 'Asset processing is taking longer than expected. Please check back later.',
74
+ duration: Date.now() - startTime,
75
+ });
76
+ yield put(timeoutAction);
77
+ }
78
+ break;
79
+ }
80
+
81
+ // Poll status endpoint
82
+ const statusResponse = yield call(getAssetStatus, type, assetId);
83
+
84
+ // Validate API response structure
85
+ // Check for error indicators: success: false, error property, or missing response
86
+ if (!statusResponse || statusResponse.success === false || statusResponse.error) {
87
+ const errorMessage = statusResponse?.error ||
88
+ statusResponse?.message ||
89
+ 'Failed to fetch asset status: Invalid API response';
90
+
91
+ if (onFailed) {
92
+ const failedAction = onFailed({
93
+ assetId,
94
+ error: errorMessage,
95
+ duration: Date.now() - startTime,
96
+ });
97
+ yield put(failedAction);
98
+ }
99
+ break;
100
+ }
101
+
102
+ // Extract status data from response
103
+ const statusData = statusResponse?.response || {};
104
+
105
+ // Validate that statusData has a meaningful status property
106
+ if (!statusData || typeof statusData.status !== 'string') {
107
+ const errorMessage = 'Invalid asset status response: Missing or invalid status field';
108
+
109
+ if (onFailed) {
110
+ const failedAction = onFailed({
111
+ assetId,
112
+ error: errorMessage,
113
+ duration: Date.now() - startTime,
114
+ });
115
+ yield put(failedAction);
116
+ }
117
+ break;
118
+ }
119
+
120
+ if (statusData.status === ASSET_STATUS.COMPLETED) {
121
+ // Asset completed successfully
122
+ if (onCompleted) {
123
+ const completedAction = onCompleted({
124
+ assetId,
125
+ asset: statusData.asset,
126
+ duration: Date.now() - startTime,
127
+ });
128
+ yield put(completedAction);
129
+ }
130
+ break;
131
+ } else if (statusData.status === ASSET_STATUS.FAILED) {
132
+ // Asset processing failed
133
+ if (onFailed) {
134
+ const failedAction = onFailed({
135
+ assetId,
136
+ error: statusData.error || 'Processing failed',
137
+ duration: Date.now() - startTime,
138
+ });
139
+ yield put(failedAction);
140
+ }
141
+ break;
142
+ }
143
+
144
+ // Continue polling if still processing
145
+ }
146
+ } catch (error) {
147
+ // Handle polling errors (network issues, etc.)
148
+ if (onFailed) {
149
+ const errorAction = onFailed({
150
+ assetId,
151
+ error: `Failed to check asset status: ${error.message}`,
152
+ });
153
+ yield put(errorAction);
154
+ }
155
+ }
156
+ }
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'));
@@ -709,4 +714,15 @@ export const updateTestMessageMeta = (payload) => {
709
714
  return request(url, getAPICallObject('POST', payload?.data, false, true));
710
715
  };
711
716
 
717
+ /**
718
+ * P4.1: Get asset processing status for async uploads
719
+ * @param {string} type - Asset type (image, video, document)
720
+ * @param {string} assetId - Asset ID to check status
721
+ * @returns {Promise} Promise resolving to asset status
722
+ */
723
+ export const getAssetStatus = (type, assetId) => {
724
+ const url = `${API_ENDPOINT}/assets/${type}/${assetId}/status`;
725
+ return request(url, getAPICallObject('GET'));
726
+ };
727
+
712
728
  export {request, getAPICallObject};
@@ -25,6 +25,7 @@ import {
25
25
  updateTestMessageMeta,
26
26
  updateMetaConfig,
27
27
  getMediaDetails,
28
+ getAssetStatus,
28
29
  } from '../api';
29
30
  import { mockData } from './mockData';
30
31
  import getSchema from '../getSchema';
@@ -849,3 +850,126 @@ describe('getMediaDetails', () => {
849
850
  });
850
851
  });
851
852
  });
853
+
854
+ describe('getAssetStatus', () => {
855
+ beforeEach(() => {
856
+ global.fetch = jest.fn();
857
+ });
858
+
859
+ afterEach(() => {
860
+ jest.restoreAllMocks();
861
+ });
862
+
863
+ it('should return correct response on success', async () => {
864
+ const mockResponse = {
865
+ status: 200,
866
+ json: () => Promise.resolve({
867
+ status: 200,
868
+ response: {
869
+ assetId: 'asset-123',
870
+ status: 'completed',
871
+ asset: {
872
+ _id: 'asset-123',
873
+ type: 'IMAGE',
874
+ url: 'https://example.com/image.jpg',
875
+ },
876
+ },
877
+ }),
878
+ };
879
+ global.fetch.mockReturnValue(Promise.resolve(mockResponse));
880
+
881
+ const result = await getAssetStatus('image', 'asset-123');
882
+
883
+ expect(result).toEqual({
884
+ status: 200,
885
+ response: {
886
+ assetId: 'asset-123',
887
+ status: 'completed',
888
+ asset: {
889
+ _id: 'asset-123',
890
+ type: 'IMAGE',
891
+ url: 'https://example.com/image.jpg',
892
+ },
893
+ },
894
+ });
895
+ });
896
+
897
+ it('should handle processing status', async () => {
898
+ const mockResponse = {
899
+ status: 200,
900
+ json: () => Promise.resolve({
901
+ status: 200,
902
+ response: {
903
+ assetId: 'asset-123',
904
+ status: 'processing',
905
+ message: 'Asset is being processed',
906
+ },
907
+ }),
908
+ };
909
+ global.fetch.mockReturnValue(Promise.resolve(mockResponse));
910
+
911
+ const result = await getAssetStatus('video', 'asset-123');
912
+
913
+ expect(result).toEqual({
914
+ status: 200,
915
+ response: {
916
+ assetId: 'asset-123',
917
+ status: 'processing',
918
+ message: 'Asset is being processed',
919
+ },
920
+ });
921
+ });
922
+
923
+ it('should handle failed status', async () => {
924
+ const mockResponse = {
925
+ status: 200,
926
+ json: () => Promise.resolve({
927
+ status: 200,
928
+ response: {
929
+ assetId: 'asset-123',
930
+ status: 'failed',
931
+ error: 'Processing failed',
932
+ },
933
+ }),
934
+ };
935
+ global.fetch.mockReturnValue(Promise.resolve(mockResponse));
936
+
937
+ const result = await getAssetStatus('document', 'asset-123');
938
+
939
+ expect(result).toEqual({
940
+ status: 200,
941
+ response: {
942
+ assetId: 'asset-123',
943
+ status: 'failed',
944
+ error: 'Processing failed',
945
+ },
946
+ });
947
+ });
948
+
949
+ it('should handle fetch failure', async () => {
950
+ global.fetch.mockRejectedValue({ error: 'Network error' });
951
+
952
+ const result = await getAssetStatus('image', 'asset-123');
953
+
954
+ expect(result).toEqual({
955
+ error: 'Network error',
956
+ });
957
+ });
958
+
959
+ it('should construct correct URL with type and assetId', async () => {
960
+ const mockResponse = {
961
+ status: 200,
962
+ json: () => Promise.resolve({
963
+ status: 200,
964
+ response: { assetId: 'asset-123', status: 'processing' },
965
+ }),
966
+ };
967
+ global.fetch.mockReturnValue(Promise.resolve(mockResponse));
968
+
969
+ await getAssetStatus('video', 'asset-456');
970
+
971
+ expect(global.fetch).toHaveBeenCalled();
972
+ const callArgs = global.fetch.mock.calls[0];
973
+ expect(callArgs[0]).toContain('/assets/video/asset-456/status');
974
+ });
975
+ });
@@ -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",
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Asset Processing Status Constants
3
+ * Matches backend ASSET_STATUS constants from cap-creatives-api/src/constants/globals.js
4
+ * Used across sagas, reducers, and utilities for consistent status handling
5
+ */
6
+ export const ASSET_STATUS = {
7
+ PROCESSING: 'processing',
8
+ COMPLETED: 'completed',
9
+ FAILED: 'failed',
10
+ TIMEOUT: 'timeout', // UI-specific status for polling timeout
11
+ };
12
+
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Generic Async Asset Upload Utilities
3
+ *
4
+ * Provides reusable constants, reducer cases, and action creators for async asset uploads
5
+ * across all channels (WhatsApp, MobilePush, Email, SMS, etc.)
6
+ *
7
+ * Usage:
8
+ * import { createAsyncAssetUploadConstants, createAsyncAssetUploadReducerCases } from '../../utils/asyncAssetUpload';
9
+ *
10
+ * // In constants.js
11
+ * export const {
12
+ * UPLOAD_ASSET_PROCESSING,
13
+ * UPLOAD_ASSET_COMPLETED,
14
+ * UPLOAD_ASSET_FAILED,
15
+ * UPLOAD_ASSET_TIMEOUT,
16
+ * } = createAsyncAssetUploadConstants('WHATSAPP'); // or 'MOBILEPUSH', 'EMAIL', etc.
17
+ *
18
+ * // In reducer.js
19
+ * import { createAsyncAssetUploadReducerCases } from '../../utils/asyncAssetUpload';
20
+ * const asyncUploadCases = createAsyncAssetUploadReducerCases({
21
+ * PROCESSING: UPLOAD_ASSET_PROCESSING,
22
+ * COMPLETED: UPLOAD_ASSET_COMPLETED,
23
+ * FAILED: UPLOAD_ASSET_FAILED,
24
+ * TIMEOUT: UPLOAD_ASSET_TIMEOUT,
25
+ * });
26
+ *
27
+ * // In reducer switch statement
28
+ * case asyncUploadCases.PROCESSING:
29
+ * return asyncUploadCases.handleProcessing(state, action);
30
+ * case asyncUploadCases.COMPLETED:
31
+ * return asyncUploadCases.handleCompleted(state, action);
32
+ * // etc.
33
+ */
34
+
35
+ import { fromJS } from 'immutable';
36
+ import { ASSET_STATUS } from './assetStatusConstants';
37
+
38
+ /**
39
+ * Creates async asset upload constants for a given channel
40
+ * @param {string} channelPrefix - Channel prefix (e.g., 'WHATSAPP', 'MOBILEPUSH', 'EMAIL')
41
+ * @param {string} containerPath - Container path (e.g., 'app/v2Containers/Whatsapp')
42
+ * @returns {Object} Object containing all async upload constants
43
+ */
44
+ export function createAsyncAssetUploadConstants(channelPrefix, containerPath) {
45
+ const prefix = containerPath || `app/v2Containers/${channelPrefix}`;
46
+ const channelName = channelPrefix.toUpperCase();
47
+
48
+ return {
49
+ [`UPLOAD_${channelName}_ASSET_PROCESSING`]: `${prefix}/UPLOAD_${channelName}_ASSET_PROCESSING`,
50
+ [`UPLOAD_${channelName}_ASSET_COMPLETED`]: `${prefix}/UPLOAD_${channelName}_ASSET_COMPLETED`,
51
+ [`UPLOAD_${channelName}_ASSET_FAILED`]: `${prefix}/UPLOAD_${channelName}_ASSET_FAILED`,
52
+ [`UPLOAD_${channelName}_ASSET_TIMEOUT`]: `${prefix}/UPLOAD_${channelName}_ASSET_TIMEOUT`,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Creates reducer case handlers for async asset uploads
58
+ * @param {Object} actionTypes - Action type constants
59
+ * @param {Object} options - Configuration options
60
+ * @param {string} options.uploadedAssetDataKey - Key for uploaded asset data (default: 'uploadedAssetData')
61
+ * @param {string} options.assetUploadingKey - Key for asset uploading flag (default: 'assetUploading')
62
+ * @param {string} options.uploadAssetSuccessKey - Key for upload success flag (default: 'uploadAssetSuccess')
63
+ * @param {string} options.assetProcessingKey - Key for asset processing state (default: 'assetProcessing')
64
+ * @returns {Object} Object containing reducer case handlers
65
+ */
66
+ export function createAsyncAssetUploadReducerCases(actionTypes, options = {}) {
67
+ const {
68
+ uploadedAssetDataKey = 'uploadedAssetData',
69
+ assetUploadingKey = 'assetUploading',
70
+ uploadAssetSuccessKey = 'uploadAssetSuccess',
71
+ assetProcessingKey = 'assetProcessing',
72
+ } = options;
73
+
74
+ return {
75
+ // Action type constants for use in switch statements
76
+ PROCESSING: actionTypes.PROCESSING,
77
+ COMPLETED: actionTypes.COMPLETED,
78
+ FAILED: actionTypes.FAILED,
79
+ TIMEOUT: actionTypes.TIMEOUT,
80
+
81
+ // Reducer handlers
82
+ handleProcessing: (state, action) => {
83
+ const startTime = new Date().getTime();
84
+ return state
85
+ .set(uploadAssetSuccessKey, false)
86
+ .set(assetUploadingKey, true)
87
+ .setIn([assetProcessingKey, action.payload.assetId], fromJS({
88
+ status: ASSET_STATUS.PROCESSING,
89
+ startTime,
90
+ asset: action.payload.asset,
91
+ error: null
92
+ }));
93
+ },
94
+
95
+ handleCompleted: (state, action) => {
96
+ const templateType = action.templateType;
97
+ const assetDataKey = templateType !== undefined
98
+ ? `${uploadedAssetDataKey}${templateType}`
99
+ : uploadedAssetDataKey;
100
+
101
+ return state
102
+ .set(uploadAssetSuccessKey, true)
103
+ .set(assetUploadingKey, false)
104
+ .setIn([assetProcessingKey, action.payload.assetId], fromJS({
105
+ status: ASSET_STATUS.COMPLETED,
106
+ asset: action.payload.asset,
107
+ error: null
108
+ }))
109
+ .set(assetDataKey, fromJS(action.payload.asset));
110
+ },
111
+
112
+ handleFailed: (state, action) => {
113
+ return state
114
+ .set(uploadAssetSuccessKey, false)
115
+ .set(assetUploadingKey, false)
116
+ .setIn([assetProcessingKey, action.payload.assetId], fromJS({
117
+ status: ASSET_STATUS.FAILED,
118
+ error: action.payload.error
119
+ }));
120
+ },
121
+
122
+ handleTimeout: (state, action) => {
123
+ return state
124
+ .set(uploadAssetSuccessKey, false)
125
+ .set(assetUploadingKey, false)
126
+ .setIn([assetProcessingKey, action.payload.assetId], fromJS({
127
+ status: ASSET_STATUS.TIMEOUT,
128
+ error: action.payload.message
129
+ }));
130
+ },
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Creates polling configuration for use with centralized pollAssetStatus saga
136
+ * @param {string} assetType - Asset type ('image', 'video', 'document', etc.)
137
+ * @param {string} assetId - Asset ID
138
+ * @param {Object} actionTypes - Action type constants
139
+ * @returns {Object} Polling configuration object
140
+ */
141
+ export function createPollingConfig(assetType, assetId, actionTypes, templateType) {
142
+ return {
143
+ type: assetType,
144
+ assetId,
145
+ onCompleted: (data) => ({
146
+ type: actionTypes.COMPLETED,
147
+ payload: data,
148
+ templateType,
149
+ }),
150
+ onFailed: (data) => ({
151
+ type: actionTypes.FAILED,
152
+ payload: data,
153
+ templateType,
154
+ }),
155
+ onTimeout: (data) => ({
156
+ type: actionTypes.TIMEOUT,
157
+ payload: data,
158
+ templateType,
159
+ }),
160
+ };
161
+ }