@capillarytech/creatives-library 8.0.255-alpha.4 → 8.0.256

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 (164) hide show
  1. package/constants/unified.js +0 -1
  2. package/package.json +1 -1
  3. package/services/api.js +0 -5
  4. package/translations/en.json +3 -4
  5. package/utils/common.js +0 -6
  6. package/utils/tests/transformerUtils.test.js +0 -297
  7. package/utils/transformerUtils.js +0 -40
  8. package/v2Components/CapImageUpload/constants.js +0 -2
  9. package/v2Components/CapImageUpload/index.js +16 -65
  10. package/v2Components/CapImageUpload/index.scss +1 -4
  11. package/v2Components/CapImageUpload/messages.js +1 -5
  12. package/v2Components/CommonTestAndPreview/UnifiedPreview/InAppPreviewContent.js +3 -3
  13. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +3 -3
  14. package/v2Components/CommonTestAndPreview/UnifiedPreview/SmsPreviewContent.js +3 -3
  15. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +3 -3
  16. package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +1 -1
  17. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +37 -6
  18. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -2
  19. package/v2Components/TemplatePreview/_templatePreview.scss +1 -2
  20. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
  21. package/v2Containers/App/constants.js +0 -5
  22. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +3 -4
  23. package/v2Containers/CreativesContainer/SlideBoxContent.js +2 -57
  24. package/v2Containers/CreativesContainer/SlideBoxHeader.js +0 -1
  25. package/v2Containers/CreativesContainer/constants.js +0 -3
  26. package/v2Containers/CreativesContainer/index.js +0 -168
  27. package/v2Containers/CreativesContainer/messages.js +0 -4
  28. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
  29. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -304
  30. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +12 -36
  31. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +6 -8
  32. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +75 -100
  33. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +54 -72
  34. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +214 -286
  35. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +12 -16
  36. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +48 -60
  37. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  38. package/v2Containers/Templates/_templates.scss +0 -205
  39. package/v2Containers/Templates/actions.js +1 -2
  40. package/v2Containers/Templates/constants.js +0 -1
  41. package/v2Containers/Templates/index.js +34 -274
  42. package/v2Containers/Templates/messages.js +0 -24
  43. package/v2Containers/Templates/reducer.js +0 -2
  44. package/v2Containers/Templates/tests/index.test.js +0 -10
  45. package/v2Containers/TemplatesV2/index.js +7 -15
  46. package/v2Containers/TemplatesV2/messages.js +0 -4
  47. package/v2Containers/Whatsapp/index.js +1 -1
  48. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +870 -1442
  49. package/utils/imageUrlUpload.js +0 -141
  50. package/v2Components/CapImageUrlUpload/constants.js +0 -26
  51. package/v2Components/CapImageUrlUpload/index.js +0 -365
  52. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  53. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  54. package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
  55. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
  56. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  57. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
  58. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
  59. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
  60. package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
  61. package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
  62. package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
  63. package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
  64. package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
  65. package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
  66. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
  67. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
  68. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
  69. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
  70. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
  71. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
  72. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
  73. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
  74. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
  75. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
  76. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  77. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  78. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  79. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  80. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  81. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  82. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
  83. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
  84. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
  85. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
  86. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
  87. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
  88. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
  89. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
  90. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
  91. package/v2Containers/WebPush/Create/index.js +0 -1148
  92. package/v2Containers/WebPush/Create/index.scss +0 -134
  93. package/v2Containers/WebPush/Create/messages.js +0 -203
  94. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
  95. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
  96. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
  97. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
  98. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
  99. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
  100. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  101. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  102. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
  103. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
  104. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  105. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  106. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  107. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  108. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
  109. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
  110. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  111. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  112. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
  113. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
  114. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -47
  115. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -141
  116. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  117. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
  118. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
  119. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
  120. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
  121. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
  122. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -81
  123. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
  124. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
  125. package/v2Containers/WebPush/Create/preview/constants.js +0 -637
  126. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
  127. package/v2Containers/WebPush/Create/preview/preview.scss +0 -351
  128. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
  129. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  130. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  131. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  132. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
  133. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  134. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  135. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  136. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
  137. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
  138. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  139. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
  140. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
  141. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  142. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
  143. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  144. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
  145. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
  146. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
  147. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  148. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
  149. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
  150. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
  151. package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
  152. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
  153. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  154. package/v2Containers/WebPush/Create/utils/validation.js +0 -75
  155. package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
  156. package/v2Containers/WebPush/actions.js +0 -60
  157. package/v2Containers/WebPush/constants.js +0 -132
  158. package/v2Containers/WebPush/index.js +0 -2
  159. package/v2Containers/WebPush/reducer.js +0 -104
  160. package/v2Containers/WebPush/sagas.js +0 -119
  161. package/v2Containers/WebPush/selectors.js +0 -65
  162. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  163. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  164. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -1,1148 +0,0 @@
1
- import React, {
2
- useState,
3
- useEffect,
4
- useRef,
5
- useCallback,
6
- useMemo,
7
- memo,
8
- } from 'react';
9
- import PropTypes from 'prop-types';
10
- import { injectIntl, intlShape } from 'react-intl';
11
- import { createStructuredSelector, createSelector } from 'reselect';
12
- import { bindActionCreators } from 'redux';
13
- import {
14
- injectReducer,
15
- injectSaga,
16
- } from '@capillarytech/vulcan-react-sdk/utils';
17
- import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
18
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
19
- import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
20
- import TagList from '../../TagList';
21
- import WebPushPreview from './preview/WebPushPreview';
22
- import TemplateNameSection from './components/TemplateNameSection';
23
- import NotificationTitleSection from './components/NotificationTitleSection';
24
- import MessageSection from './components/MessageSection';
25
- import MediaSection from './components/MediaSection';
26
- import BrandIconSection from './components/BrandIconSection';
27
- import ButtonsLinksSection from './components/ButtonsLinksSection';
28
- import FormActions from './components/FormActions';
29
- import { useCharacterCount } from './hooks/useCharacterCount';
30
- import { useButtonManagement } from './hooks/useButtonManagement';
31
- import { useImageUpload } from './hooks/useImageUpload';
32
- import { useTagManagement } from './hooks/useTagManagement';
33
- import isEmpty from 'lodash/isEmpty';
34
- import get from 'lodash/get';
35
- import * as templateActions from '../../Templates/actions';
36
- import { makeSelectTemplates } from '../../Templates/selectors';
37
-
38
- import {
39
- WEBPUSH_MEDIA_TYPES,
40
- BRAND_ICON_OPTIONS,
41
- IMAGE_UPLOAD_METHODS,
42
- UPLOAD_FIELD_TYPES,
43
- WEBPUSH_BUTTON_TYPES,
44
- ON_CLICK_BEHAVIOUR_OPTIONS,
45
- ACTION_TYPES,
46
- NOTIFICATION_TITLE_MAX_LENGTH,
47
- MESSAGE_MAX_LENGTH,
48
- } from '../constants';
49
- import * as actions from '../actions';
50
- import {
51
- makeSelectWebPush,
52
- makeSelectCreateError,
53
- makeSelectCreateTemplateInProgress,
54
- makeSelectEditTemplateInProgress,
55
- makeSelectEditError,
56
- } from '../selectors';
57
- import webPushReducer from '../reducer';
58
- import webPushSagas from '../sagas';
59
- import { v2TemplateSaga } from '../../Templates/sagas';
60
- import withCreatives from '../../../hoc/withCreatives';
61
- import messages from './messages';
62
- import { createWebPushPayload } from './utils/payloadBuilder';
63
- import {
64
- validateTemplateName as validateTemplateNameUtil,
65
- validateTitle as validateTitleUtil,
66
- validateUrl as validateUrlUtil,
67
- validateMessageContent as validateMessageContentUtil,
68
- } from './utils/validation';
69
- import {
70
- makeSelectMetaEntities,
71
- setInjectedTags,
72
- } from '../../Cap/selectors';
73
- import './index.scss';
74
- import { WEBPUSH } from '../../CreativesContainer/constants';
75
-
76
- // Memoized TagList wrapper components for better performance
77
- const MemoizedTagList = memo(({
78
- moduleFilterEnabled,
79
- label,
80
- onContextChange,
81
- location,
82
- tags,
83
- injectedTags,
84
- selectedOfferDetails,
85
- eventContextTags,
86
- forwardedTags,
87
- onTagSelect,
88
- }) => (
89
- <TagList
90
- moduleFilterEnabled={moduleFilterEnabled}
91
- label={label}
92
- onContextChange={onContextChange}
93
- location={location}
94
- tags={tags}
95
- injectedTags={injectedTags}
96
- selectedOfferDetails={selectedOfferDetails}
97
- eventContextTags={eventContextTags}
98
- forwardedTags={forwardedTags}
99
- onTagSelect={onTagSelect}
100
- />
101
- ), (prevProps, nextProps) => {
102
- // Custom comparison function for better memoization
103
- return (
104
- prevProps.moduleFilterEnabled === nextProps.moduleFilterEnabled
105
- && prevProps.label === nextProps.label
106
- && prevProps.onContextChange === nextProps.onContextChange
107
- && prevProps.location === nextProps.location
108
- && prevProps.tags === nextProps.tags
109
- && prevProps.injectedTags === nextProps.injectedTags
110
- && prevProps.selectedOfferDetails === nextProps.selectedOfferDetails
111
- && prevProps.eventContextTags === nextProps.eventContextTags
112
- && prevProps.forwardedTags === nextProps.forwardedTags
113
- && prevProps.onTagSelect === nextProps.onTagSelect
114
- );
115
- });
116
-
117
- MemoizedTagList.displayName = 'MemoizedTagList';
118
-
119
- const WebPushCreate = ({
120
- isFullMode,
121
- handleClose,
122
- intl,
123
- webPushActions,
124
- createTemplateInProgress,
125
- createTemplateError,
126
- editTemplateInProgress,
127
- editTemplateError,
128
- accountData,
129
- webPush,
130
- onCreateComplete,
131
- getFormData,
132
- isGetFormData,
133
- templateData,
134
- creativesMode,
135
- params,
136
- globalActions,
137
- location,
138
- metaEntities,
139
- injectedTags,
140
- getDefaultTags,
141
- supportedTags = [],
142
- forwardedTags,
143
- selectedOfferDetails = [],
144
- eventContextTags = [],
145
- templateActions: templateActionsProps,
146
- Templates,
147
- }) => {
148
- const { formatMessage } = intl;
149
-
150
- // Form state - kept in main component for now
151
- const [templateName, setTemplateName] = useState('');
152
- const [notificationTitle, setNotificationTitle] = useState('');
153
- const [message, setMessage] = useState('');
154
- const [mediaType, setMediaType] = useState(WEBPUSH_MEDIA_TYPES.NONE);
155
- const [templateNameError, setTemplateNameError] = useState(false);
156
- const [titleError, setTitleError] = useState('');
157
- const [messageError, setMessageError] = useState('');
158
- const [fieldCompletion, setFieldCompletion] = useState({
159
- templateName: !isFullMode,
160
- notificationTitle: false,
161
- message: false,
162
- });
163
- const [brandIconOption, setBrandIconOption] = useState(BRAND_ICON_OPTIONS.DONT_SHOW);
164
- const [onClickBehaviour, setOnClickBehaviour] = useState(ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL);
165
- const [redirectUrl, setRedirectUrl] = useState('');
166
- const [redirectUrlError, setRedirectUrlError] = useState('');
167
- const [activeUploadField, setActiveUploadField] = useState(null);
168
- const [templateIdError, setTemplateIdError] = useState('');
169
-
170
- // Refs
171
- const titleCountRef = useRef(null);
172
- const messageCountRef = useRef(null);
173
- const saveInitiatedRef = useRef(false);
174
- const messageTextAreaRef = useRef(null);
175
-
176
- // Custom hooks
177
- const { updateCharacterCount } = useCharacterCount(formatMessage, messages);
178
-
179
- const buttonState = useButtonManagement([]);
180
- const { buttons, setButtons } = buttonState;
181
-
182
- const imageUpload = useImageUpload({
183
- webPush,
184
- webPushActions,
185
- index: 0,
186
- initialUploadMethod: IMAGE_UPLOAD_METHODS.UPLOAD_IMAGE,
187
- });
188
- const {
189
- imageSrc,
190
- imageUrl,
191
- isValidating: isImageValidating,
192
- isUploading: isImageUploading,
193
- setImageSrc,
194
- setImageUrl,
195
- } = imageUpload;
196
-
197
- const brandIconUpload = useImageUpload({
198
- webPush,
199
- webPushActions,
200
- index: 1,
201
- initialUploadMethod: IMAGE_UPLOAD_METHODS.UPLOAD_IMAGE,
202
- });
203
- const {
204
- imageSrc: brandIconSrc,
205
- imageUrl: brandIconUrl,
206
- isValidating: isBrandIconValidating,
207
- isUploading: isBrandIconUploading,
208
- setImageSrc: setBrandIconSrc,
209
- setImageUrl: setBrandIconUrl,
210
- } = brandIconUpload;
211
-
212
- // Memoize supportedTags to prevent unnecessary re-renders in useTagManagement
213
- const memoizedSupportedTags = useMemo(() => supportedTags, [supportedTags]);
214
-
215
- const tagState = useTagManagement({
216
- location,
217
- globalActions,
218
- metaEntities,
219
- getDefaultTags,
220
- supportedTags: memoizedSupportedTags,
221
- injectedTags,
222
- eventContextTags,
223
- });
224
- const { tags, handleOnTagsContextChange, validationConfig } = tagState;
225
- const { weCrmAccounts } = Templates;
226
-
227
- // Edit mode detection: check creativesMode or presence of template ID
228
- const isEditMode = useMemo(
229
- () =>
230
- creativesMode === 'editTemplate'
231
- || creativesMode === 'edit'
232
- || !!(templateData?._id)
233
- || !!(params?.id),
234
- [creativesMode, templateData?._id, params?.id],
235
- );
236
-
237
- const accountId = useMemo(() => {
238
- const fallbackAccountId = get(templateData, 'definition.accountId');
239
- return (
240
- accountData?.accountId
241
- || accountData?.id
242
- || fallbackAccountId
243
- || get(accountData, 'sourceAccountIdentifier')
244
- );
245
- }, [
246
- accountData?.accountId,
247
- accountData?.id,
248
- accountData?.sourceAccountIdentifier,
249
- templateData,
250
- ]);
251
-
252
- const selectedWebPushAccount = useMemo(() =>
253
- weCrmAccounts?.find(account => account?.id === accountId),
254
- [weCrmAccounts, accountId]);
255
-
256
- const websiteLink = useMemo(
257
- () => accountData?.configs?.websiteLink || selectedWebPushAccount?.configs?.websiteLink || '',
258
- [accountData?.configs?.websiteLink, selectedWebPushAccount?.configs?.websiteLink],
259
- );
260
-
261
- // Fetch account details when websiteLink is missing but accountId exists
262
- useEffect(() => {
263
- if (!websiteLink && accountId && templateActionsProps?.getWeCrmAccounts) {
264
- templateActionsProps.getWeCrmAccounts('WebPush');
265
- }
266
- }, [websiteLink, accountId, templateActionsProps?.getWeCrmAccounts]);
267
-
268
- const previewUrl = useMemo(
269
- () => (
270
- onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL
271
- ? redirectUrl
272
- : websiteLink || redirectUrl
273
- ),
274
- [onClickBehaviour, redirectUrl, websiteLink],
275
- );
276
-
277
- const onClickBehaviourOptions = useMemo(
278
- () => ([
279
- { value: ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL, label: formatMessage(messages.openSite) },
280
- { value: ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL, label: formatMessage(messages.redirectToSpecificUrl) },
281
- ]),
282
- [formatMessage],
283
- );
284
-
285
- const validateTemplateName = useCallback((value) => validateTemplateNameUtil(value), []);
286
-
287
- const validateTitle = useCallback(
288
- (value) => validateTitleUtil(value, formatMessage, messages),
289
- [formatMessage],
290
- );
291
-
292
- const validateUrl = useCallback(
293
- (value) => validateUrlUtil(value, formatMessage, messages),
294
- [formatMessage],
295
- );
296
-
297
- const updateFieldCompletion = useCallback((field, hasValue) => {
298
- setFieldCompletion((prev) => {
299
- if (prev[field] === hasValue) {
300
- return prev;
301
- }
302
- return {
303
- ...prev,
304
- [field]: hasValue,
305
- };
306
- });
307
- }, []);
308
-
309
-
310
-
311
- const validateMessageContent = useCallback(
312
- (value) => validateMessageContentUtil(value, formatMessage, messages, validationConfig),
313
- [formatMessage, validationConfig],
314
- );
315
-
316
- useEffect(() => {
317
- if (!isEditMode) {
318
- return;
319
- }
320
-
321
- if (isEmpty(templateData)) {
322
- return;
323
- }
324
-
325
- const webpushContent = get(templateData, 'versions.base.content.webpush', {});
326
-
327
- const extractedTemplateName = templateData?.name || '';
328
- const extractedNotificationTitle = webpushContent?.title || '';
329
- const extractedMessage = webpushContent?.message || '';
330
-
331
- // Update state to populate form fields
332
- setTemplateName(extractedTemplateName);
333
- setNotificationTitle(extractedNotificationTitle);
334
- setMessage(extractedMessage);
335
-
336
- const nextMediaType = webpushContent?.mediaType || WEBPUSH_MEDIA_TYPES.NONE;
337
- setMediaType(nextMediaType);
338
-
339
- const existingImage = webpushContent?.image || '';
340
- if (existingImage) {
341
- setImageSrc(existingImage);
342
- setImageUrl('');
343
- } else {
344
- setImageSrc('');
345
- setImageUrl('');
346
- }
347
-
348
- const existingBrandIcon = webpushContent?.brandIcon || '';
349
- if (existingBrandIcon) {
350
- setBrandIconOption(BRAND_ICON_OPTIONS.UPLOAD_IMAGE);
351
- setBrandIconSrc(existingBrandIcon);
352
- setBrandIconUrl('');
353
- } else {
354
- setBrandIconOption(BRAND_ICON_OPTIONS.DONT_SHOW);
355
- setBrandIconSrc('');
356
- setBrandIconUrl('');
357
- }
358
-
359
- const webpushCtas = Array.isArray(webpushContent?.ctas) ? webpushContent.ctas : [];
360
- setButtons(
361
- webpushCtas.map((cta, index) => ({
362
- text: cta?.actionText || '',
363
- url: cta?.actionLink || '',
364
- type: index === 0 ? WEBPUSH_BUTTON_TYPES.PRIMARY : WEBPUSH_BUTTON_TYPES.SECONDARY,
365
- })),
366
- );
367
-
368
- const onClickAction = webpushContent?.onClickAction || {};
369
- if (onClickAction?.type === ACTION_TYPES.URL) {
370
- setOnClickBehaviour(ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL);
371
- setRedirectUrl(onClickAction?.url || '');
372
- setRedirectUrlError('');
373
- } else if (onClickAction?.type === ACTION_TYPES.SITE_URL) {
374
- setOnClickBehaviour(ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL);
375
- setRedirectUrl(websiteLink);
376
- setRedirectUrlError('');
377
- } else {
378
- setOnClickBehaviour(ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL);
379
- setRedirectUrl(websiteLink);
380
- setRedirectUrlError('');
381
- }
382
-
383
- updateFieldCompletion('notificationTitle', extractedNotificationTitle.trim().length > 0);
384
- updateFieldCompletion('message', extractedMessage.trim().length > 0);
385
- if (isFullMode) {
386
- updateFieldCompletion('templateName', extractedTemplateName.trim().length > 0);
387
- setTemplateNameError('');
388
- }
389
- setTitleError('');
390
- setMessageError('');
391
-
392
- updateCharacterCount(
393
- titleCountRef,
394
- extractedNotificationTitle.length,
395
- NOTIFICATION_TITLE_MAX_LENGTH,
396
- );
397
- updateCharacterCount(
398
- messageCountRef,
399
- extractedMessage.length,
400
- MESSAGE_MAX_LENGTH,
401
- );
402
- }, [
403
- isEditMode,
404
- templateData,
405
- isFullMode,
406
- updateFieldCompletion,
407
- updateCharacterCount,
408
- ]);
409
-
410
- const handleTemplateNameChange = useCallback((e) => {
411
- const { value } = e.target;
412
- setTemplateName(value);
413
- if (isFullMode) {
414
- const nextError = validateTemplateName(value);
415
- setTemplateNameError((prev) => (prev === nextError ? prev : nextError));
416
- }
417
- updateFieldCompletion('templateName', value.trim().length > 0);
418
- }, [isFullMode, updateFieldCompletion, validateTemplateName]);
419
-
420
- const handleNotificationTitleChange = useCallback((e) => {
421
- const { value } = e.target;
422
- setNotificationTitle(value);
423
- const nextError = validateTitle(value);
424
- setTitleError((prev) => (prev === nextError ? prev : nextError));
425
- updateFieldCompletion('notificationTitle', value.trim().length > 0);
426
- updateCharacterCount(titleCountRef, value.length, NOTIFICATION_TITLE_MAX_LENGTH);
427
- }, [updateCharacterCount, updateFieldCompletion, validateTitle]);
428
-
429
- // Handler to capture the textarea ref for CapEmojiPicker
430
- const handleMessageTextAreaRef = useCallback((node) => {
431
- if (!node) {
432
- messageTextAreaRef.current = null;
433
- return;
434
- }
435
- // Handles different TextArea implementations (antd's resizable, etc.)
436
- messageTextAreaRef.current = node?.resizableTextArea?.textArea || node?.textAreaRef || node;
437
- }, []);
438
-
439
- // Optimize message change handler - use functional updates where possible
440
- // Handles both event (from TextArea) and direct value (from CapEmojiPicker.Wrapper)
441
- const handleMessageChange = useCallback((eOrValue) => {
442
- const value = typeof eOrValue === 'string' ? eOrValue : eOrValue.target.value;
443
- // Cap length to maximum (maxLength not passed to TextArea to avoid embedded character count)
444
- const cappedValue = value.length > MESSAGE_MAX_LENGTH
445
- ? value.slice(0, MESSAGE_MAX_LENGTH)
446
- : value;
447
- if (typeof eOrValue !== 'string' && eOrValue.target && cappedValue !== value) {
448
- eOrValue.target.value = cappedValue;
449
- }
450
- setMessage(cappedValue);
451
- const nextError = validateMessageContent(cappedValue);
452
- setMessageError((prev) => (prev === nextError ? prev : nextError));
453
- updateFieldCompletion('message', cappedValue.trim().length > 0);
454
- updateCharacterCount(messageCountRef, cappedValue.length, MESSAGE_MAX_LENGTH);
455
- }, [updateCharacterCount, updateFieldCompletion, validateMessageContent]);
456
-
457
- const handleMediaTypeChange = useCallback((value) => {
458
- setMediaType(value);
459
- if (value === WEBPUSH_MEDIA_TYPES.NONE) {
460
- setImageSrc('');
461
- setImageUrl('');
462
- }
463
- }, [setImageSrc, setImageUrl]);
464
-
465
- // Brand icon option change handler (kept in main component)
466
- const handleBrandIconChange = useCallback((e) => {
467
- const option = e.target.value;
468
- setBrandIconOption(option);
469
- setBrandIconSrc('');
470
- setBrandIconUrl('');
471
- }, [setBrandIconSrc, setBrandIconUrl]);
472
-
473
-
474
-
475
- const handleOnClickBehaviourChange = useCallback((e) => {
476
- const value = e.target.value;
477
- setOnClickBehaviour(value);
478
- if (value !== ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL) {
479
- setRedirectUrl('');
480
- setRedirectUrlError('');
481
- }
482
- }, []);
483
-
484
- const handleRedirectUrlChange = useCallback((e) => {
485
- const { value } = e.target;
486
- setRedirectUrl(value);
487
- const nextError = validateUrl(value);
488
- setRedirectUrlError((prev) => (prev === nextError ? prev : nextError));
489
- }, [validateUrl]);
490
-
491
- // Optimized tag insertion handlers - split into separate callbacks
492
- const handleTagSelectTitle = useCallback((tagValue) => {
493
- const tagText = `{{${tagValue}}}`;
494
- setNotificationTitle((prevTitle) => {
495
- const nextTitle = `${prevTitle}${tagText}`.slice(0, NOTIFICATION_TITLE_MAX_LENGTH);
496
- // Use functional updates to avoid dependency on notificationTitle
497
- const nextError = validateTitle(nextTitle);
498
- setTitleError((prev) => (prev === nextError ? prev : nextError));
499
- updateFieldCompletion('notificationTitle', nextTitle.trim().length > 0);
500
- updateCharacterCount(titleCountRef, nextTitle.length, NOTIFICATION_TITLE_MAX_LENGTH);
501
- return nextTitle;
502
- });
503
- }, [updateCharacterCount, updateFieldCompletion, validateTitle]);
504
-
505
- const handleTagSelectMessage = useCallback((tagValue) => {
506
- const tagText = `{{${tagValue}}}`;
507
- setMessage((prevMessage) => {
508
- const nextMessage = `${prevMessage}${tagText}`.slice(0, MESSAGE_MAX_LENGTH);
509
- // Use functional updates to avoid dependency on message
510
- const nextError = validateMessageContent(nextMessage);
511
- setMessageError((prev) => (prev === nextError ? prev : nextError));
512
- updateFieldCompletion('message', nextMessage.trim().length > 0);
513
- updateCharacterCount(messageCountRef, nextMessage.length, MESSAGE_MAX_LENGTH);
514
- return nextMessage;
515
- });
516
- }, [updateCharacterCount, updateFieldCompletion, validateMessageContent]);
517
-
518
-
519
- useEffect(() => {
520
- updateCharacterCount(
521
- titleCountRef,
522
- notificationTitle.length,
523
- NOTIFICATION_TITLE_MAX_LENGTH,
524
- );
525
- updateCharacterCount(
526
- messageCountRef,
527
- message.length,
528
- MESSAGE_MAX_LENGTH,
529
- );
530
- }, [updateCharacterCount, notificationTitle, message]);
531
-
532
- useEffect(() => {
533
- setFieldCompletion((prev) => {
534
- const nextTemplateNameComplete = !isFullMode
535
- || templateName.trim().length > 0;
536
- if (prev.templateName === nextTemplateNameComplete) {
537
- return prev;
538
- }
539
- return {
540
- ...prev,
541
- templateName: nextTemplateNameComplete,
542
- };
543
- });
544
- }, [isFullMode, templateName]);
545
-
546
- // Pure validator that returns boolean without setting error state
547
- const validateFormSilent = () => {
548
- const templateNameInvalid = isFullMode && validateTemplateName(templateName);
549
- const titleValidation = validateTitle(notificationTitle);
550
- const messageValidation = validateMessageContent(message);
551
-
552
- return !(templateNameInvalid || titleValidation || messageValidation);
553
- };
554
-
555
- const isFormValid = () => {
556
- const templateNameInvalid = isFullMode && validateTemplateName(templateName);
557
- const titleValidation = validateTitle(notificationTitle);
558
- const messageValidation = validateMessageContent(message);
559
-
560
- setTemplateNameError((prev) => (prev === templateNameInvalid ? prev : templateNameInvalid));
561
- setTitleError((prev) => (prev === titleValidation ? prev : titleValidation));
562
- setMessageError((prev) => (prev === messageValidation ? prev : messageValidation));
563
-
564
- return !(templateNameInvalid || titleValidation || messageValidation);
565
- };
566
-
567
- const handleSave = () => {
568
- if (!isFormValid()) {
569
- return;
570
- }
571
-
572
- // Set flag to indicate save/edit operation has been initiated
573
- saveInitiatedRef.current = true;
574
-
575
- const payload = createWebPushPayload({
576
- templateName,
577
- notificationTitle,
578
- message,
579
- mediaType,
580
- accountId,
581
- isFullMode,
582
- imageSrc,
583
- imageUrl,
584
- imageUploadMethod: imageUpload.uploadMethod,
585
- brandIconOption,
586
- brandIconSrc,
587
- brandIconUrl,
588
- buttons,
589
- onClickBehaviour,
590
- redirectUrl,
591
- websiteLink,
592
- });
593
-
594
- // In library mode (not full mode), use getFormData to communicate with parent
595
- if (!isFullMode && getFormData) {
596
- const formDataForLibrary = {
597
- validity: true,
598
- value: payload,
599
- type: WEBPUSH,
600
- };
601
- getFormData(formDataForLibrary);
602
- if (handleClose) {
603
- handleClose();
604
- }
605
- return;
606
- }
607
-
608
- // Full mode: proceed with API calls
609
- if (isEditMode) {
610
- // Get template ID from params or templateData
611
- const templateId = params?.id || templateData?._id;
612
- if (templateId) {
613
- // Clear any previous template ID error
614
- setTemplateIdError('');
615
- webPushActions.editTemplate(
616
- {
617
- ...payload,
618
- _id: templateId,
619
- },
620
- (resp, errorMessage) => {
621
- if (!errorMessage) {
622
- // Clear flag before calling callbacks to prevent double-invocation
623
- // from the editResponse useEffect
624
- saveInitiatedRef.current = false;
625
- if (onCreateComplete) {
626
- onCreateComplete(true);
627
- }
628
- if (handleClose) {
629
- handleClose();
630
- }
631
- }
632
- },
633
- );
634
- } else {
635
- // Template ID is missing in edit mode - surface error
636
- const errorMsg = formatMessage(messages.templateIdMissingError);
637
- setTemplateIdError(errorMsg);
638
- // Reset save flag since we're not proceeding with the save
639
- saveInitiatedRef.current = false;
640
- // Notify parent component of failure
641
- if (onCreateComplete) {
642
- onCreateComplete(false);
643
- }
644
- return;
645
- }
646
- } else {
647
- webPushActions.createTemplate(payload);
648
- }
649
- };
650
-
651
- // Clear response state on mount to prevent stale responses from triggering callbacks
652
- useEffect(() => {
653
- webPushActions.clearCreateResponse();
654
- webPushActions.clearEditResponse();
655
- saveInitiatedRef.current = false;
656
- }, []);
657
-
658
- // Handle create response
659
- useEffect(() => {
660
- const response = webPush?.response || {};
661
- if (
662
- response &&
663
- Object.keys(response).length > 0 &&
664
- !isEditMode &&
665
- saveInitiatedRef.current
666
- ) {
667
- // Reset flag after handling response
668
- saveInitiatedRef.current = false;
669
- if (onCreateComplete) {
670
- onCreateComplete(true);
671
- }
672
- if (handleClose) {
673
- handleClose();
674
- }
675
- }
676
- }, [webPush?.response, onCreateComplete, handleClose, isEditMode]);
677
-
678
- // Handle edit response
679
- useEffect(() => {
680
- const editResponse = webPush?.editResponse || {};
681
- if (
682
- editResponse &&
683
- Object.keys(editResponse).length > 0 &&
684
- isEditMode &&
685
- saveInitiatedRef.current
686
- ) {
687
- // Reset flag after handling response
688
- saveInitiatedRef.current = false;
689
- if (onCreateComplete) {
690
- onCreateComplete(true);
691
- }
692
- if (handleClose) {
693
- handleClose();
694
- }
695
- }
696
- }, [webPush?.editResponse, onCreateComplete, handleClose, isEditMode]);
697
-
698
- // Handle getFormData request from parent (library mode)
699
- useEffect(() => {
700
- if (isGetFormData && getFormData && validateFormSilent()) {
701
- const payload = createWebPushPayload({
702
- templateName,
703
- notificationTitle,
704
- message,
705
- mediaType,
706
- accountId,
707
- isFullMode,
708
- imageSrc,
709
- imageUrl,
710
- imageUploadMethod: imageUpload?.uploadMethod,
711
- brandIconOption,
712
- brandIconSrc,
713
- brandIconUrl,
714
- buttons,
715
- onClickBehaviour,
716
- redirectUrl,
717
- websiteLink,
718
- });
719
- const formDataForLibrary = {
720
- validity: true,
721
- value: payload,
722
- type: WEBPUSH,
723
- };
724
- getFormData(formDataForLibrary);
725
- }
726
- // eslint-disable-next-line react-hooks/exhaustive-deps
727
- }, [isGetFormData, getFormData]);
728
-
729
- const isUploadingAsset = useMemo(
730
- () => webPush?.assetUploading || false,
731
- [webPush?.assetUploading],
732
- );
733
-
734
- // Track which field is currently controlling uploads/validation
735
- useEffect(() => {
736
- if (isImageValidating || isImageUploading) {
737
- setActiveUploadField(UPLOAD_FIELD_TYPES.IMAGE);
738
- return;
739
- }
740
- if (isBrandIconValidating || isBrandIconUploading) {
741
- setActiveUploadField(UPLOAD_FIELD_TYPES.BRAND_ICON);
742
- return;
743
- }
744
- if (!isUploadingAsset) {
745
- setActiveUploadField(null);
746
- }
747
- }, [
748
- isImageValidating,
749
- isImageUploading,
750
- isBrandIconValidating,
751
- isBrandIconUploading,
752
- isUploadingAsset,
753
- ]);
754
-
755
- const isImageFieldActive = useMemo(
756
- () => (
757
- isImageValidating
758
- || isImageUploading
759
- || (isUploadingAsset && activeUploadField === UPLOAD_FIELD_TYPES.IMAGE)
760
- ),
761
- [
762
- isImageValidating,
763
- isImageUploading,
764
- isUploadingAsset,
765
- activeUploadField,
766
- ],
767
- );
768
-
769
- const isBrandIconFieldActive = useMemo(
770
- () => (
771
- isBrandIconValidating
772
- || isBrandIconUploading
773
- || (isUploadingAsset && activeUploadField === UPLOAD_FIELD_TYPES.BRAND_ICON)
774
- ),
775
- [
776
- isBrandIconValidating,
777
- isBrandIconUploading,
778
- isUploadingAsset,
779
- activeUploadField,
780
- ],
781
- );
782
-
783
- const isAnyUploadActive = useMemo(
784
- () => isImageFieldActive || isBrandIconFieldActive,
785
- [isImageFieldActive, isBrandIconFieldActive],
786
- );
787
-
788
- const isMediaSectionLocked = useMemo(
789
- () => isBrandIconFieldActive,
790
- [isBrandIconFieldActive],
791
- );
792
-
793
- const isBrandIconSectionLocked = useMemo(
794
- () => isImageFieldActive,
795
- [isImageFieldActive],
796
- );
797
-
798
- // Optimize tagListCommonProps - split into stable and dynamic parts
799
- const moduleFilterEnabled = useMemo(
800
- () => location?.query?.type !== 'embedded',
801
- [location?.query?.type],
802
- );
803
-
804
- const tagListLabel = useMemo(
805
- () => formatMessage(messages.addLabels),
806
- [formatMessage],
807
- );
808
-
809
- // Stable props that don't change often
810
- const tagListStableProps = useMemo(
811
- () => ({
812
- moduleFilterEnabled,
813
- label: tagListLabel,
814
- onContextChange: handleOnTagsContextChange,
815
- location,
816
- }),
817
- [moduleFilterEnabled, tagListLabel, handleOnTagsContextChange, location],
818
- );
819
-
820
- // Dynamic props that change with tags
821
- const tagListDynamicProps = useMemo(
822
- () => ({
823
- tags,
824
- injectedTags,
825
- selectedOfferDetails,
826
- eventContextTags,
827
- forwardedTags,
828
- }),
829
- [tags, injectedTags, selectedOfferDetails, eventContextTags, forwardedTags],
830
- );
831
-
832
- // Memoized TagList components with optimized props
833
- const titleTagList = useMemo(
834
- () => (
835
- <MemoizedTagList
836
- {...tagListStableProps}
837
- {...tagListDynamicProps}
838
- onTagSelect={handleTagSelectTitle}
839
- />
840
- ),
841
- [tagListStableProps, tagListDynamicProps, handleTagSelectTitle],
842
- );
843
-
844
- const messageTagList = useMemo(
845
- () => (
846
- <MemoizedTagList
847
- {...tagListStableProps}
848
- {...tagListDynamicProps}
849
- onTagSelect={handleTagSelectMessage}
850
- />
851
- ),
852
- [tagListStableProps, tagListDynamicProps, handleTagSelectMessage],
853
- );
854
-
855
- const isSaveDisabled = useMemo(
856
- () => (
857
- createTemplateInProgress
858
- || editTemplateInProgress
859
- || isUploadingAsset
860
- || isImageValidating
861
- || isImageUploading
862
- || isBrandIconValidating
863
- || isBrandIconUploading
864
- || buttonState.isAddingButton
865
- || (isFullMode && !fieldCompletion.templateName)
866
- || !fieldCompletion.notificationTitle
867
- || !fieldCompletion.message
868
- || !accountId
869
- || (
870
- mediaType === WEBPUSH_MEDIA_TYPES.IMAGE
871
- && (
872
- (imageUpload.uploadMethod === IMAGE_UPLOAD_METHODS.UPLOAD_IMAGE && !imageSrc)
873
- || (imageUpload.uploadMethod === IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL && !imageUrl)
874
- )
875
- )
876
- || (
877
- brandIconOption !== BRAND_ICON_OPTIONS.DONT_SHOW
878
- && (
879
- (brandIconOption === BRAND_ICON_OPTIONS.UPLOAD_IMAGE && !brandIconSrc)
880
- || (brandIconOption === BRAND_ICON_OPTIONS.ADD_IMAGE_URL && !brandIconUrl)
881
- )
882
- )
883
- || (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL && (!redirectUrl.trim() || redirectUrlError))
884
- ),
885
- [
886
- createTemplateInProgress,
887
- editTemplateInProgress,
888
- isUploadingAsset,
889
- isImageValidating,
890
- isImageUploading,
891
- isBrandIconValidating,
892
- isBrandIconUploading,
893
- buttonState.isAddingButton,
894
- isFullMode,
895
- fieldCompletion.templateName,
896
- fieldCompletion.notificationTitle,
897
- fieldCompletion.message,
898
- accountId,
899
- mediaType,
900
- imageUpload.uploadMethod,
901
- imageSrc,
902
- imageUrl,
903
- brandIconOption,
904
- brandIconSrc,
905
- brandIconUrl,
906
- onClickBehaviour,
907
- redirectUrl,
908
- redirectUrlError,
909
- ],
910
- );
911
-
912
- const errorText = useMemo(() => {
913
- // Check for templateIdError first (validation error before API call)
914
- if (templateIdError) {
915
- return templateIdError;
916
- }
917
- const error = isEditMode ? editTemplateError : createTemplateError;
918
- if (!error) {
919
- return '';
920
- }
921
- if (typeof error === 'string') {
922
- return error;
923
- }
924
- if (error?.message) {
925
- return error.message;
926
- }
927
- if (typeof error?.toJS === 'function') {
928
- const errorObject = error.toJS();
929
- if (typeof errorObject === 'string') {
930
- return errorObject;
931
- }
932
- if (errorObject?.message) {
933
- return errorObject.message;
934
- }
935
- try {
936
- return JSON.stringify(errorObject);
937
- } catch (err) {
938
- return '';
939
- }
940
- }
941
- try {
942
- return JSON.stringify(error);
943
- } catch (err) {
944
- return '';
945
- }
946
- }, [templateIdError, createTemplateError, editTemplateError, isEditMode]);
947
-
948
- const accountErrorText = useMemo(
949
- () => (!accountId ? formatMessage(messages.accountRequired) : ''),
950
- [accountId, formatMessage],
951
- );
952
-
953
- return (
954
- <CapRow className="webpush-container">
955
- <CapColumn className="content-section" span={14}>
956
- <TemplateNameSection
957
- isFullMode={isFullMode}
958
- value={templateName}
959
- error={templateNameError}
960
- onChange={handleTemplateNameChange}
961
- formatMessage={formatMessage}
962
- messages={messages}
963
- />
964
- <NotificationTitleSection
965
- value={notificationTitle}
966
- error={titleError}
967
- onChange={handleNotificationTitleChange}
968
- formatMessage={formatMessage}
969
- messages={messages}
970
- tagList={titleTagList}
971
- titleCountRef={titleCountRef}
972
- />
973
- <MessageSection
974
- value={message}
975
- error={messageError}
976
- onChange={handleMessageChange}
977
- formatMessage={formatMessage}
978
- messages={messages}
979
- tagList={messageTagList}
980
- messageCountRef={messageCountRef}
981
- messageTextAreaRef={messageTextAreaRef}
982
- handleMessageTextAreaRef={handleMessageTextAreaRef}
983
- />
984
- <MediaSection
985
- mediaType={mediaType}
986
- onMediaTypeChange={handleMediaTypeChange}
987
- imageUpload={imageUpload}
988
- isLocked={isMediaSectionLocked}
989
- isAnyUploadActive={isAnyUploadActive}
990
- formatMessage={formatMessage}
991
- messages={messages}
992
- webPush={webPush}
993
- isFullMode={isFullMode}
994
- />
995
- <BrandIconSection
996
- brandIconOption={brandIconOption}
997
- onBrandIconChange={handleBrandIconChange}
998
- brandIconUpload={brandIconUpload}
999
- isLocked={isBrandIconSectionLocked}
1000
- isAnyUploadActive={isAnyUploadActive}
1001
- formatMessage={formatMessage}
1002
- messages={messages}
1003
- webPush={webPush}
1004
- isFullMode={isFullMode}
1005
- />
1006
- <ButtonsLinksSection
1007
- onClickBehaviour={onClickBehaviour}
1008
- onClickBehaviourOptions={onClickBehaviourOptions}
1009
- onClickBehaviourChange={handleOnClickBehaviourChange}
1010
- redirectUrl={redirectUrl}
1011
- redirectUrlError={redirectUrlError}
1012
- onRedirectUrlChange={handleRedirectUrlChange}
1013
- buttonState={buttonState}
1014
- formatMessage={formatMessage}
1015
- messages={messages}
1016
- />
1017
- <FormActions
1018
- onSave={handleSave}
1019
- isSaveDisabled={isSaveDisabled}
1020
- errorText={errorText}
1021
- accountErrorText={accountErrorText}
1022
- formatMessage={formatMessage}
1023
- messages={messages}
1024
- />
1025
- </CapColumn>
1026
- <CapColumn className="preview-section" span={10}>
1027
- <WebPushPreview
1028
- notificationTitle={notificationTitle}
1029
- notificationBody={message}
1030
- url={previewUrl}
1031
- imageSrc={imageSrc}
1032
- brandIconSrc={brandIconSrc}
1033
- buttons={buttons}
1034
- />
1035
- </CapColumn>
1036
- </CapRow>
1037
- );
1038
- };
1039
-
1040
- WebPushCreate.propTypes = {
1041
- isFullMode: PropTypes.bool,
1042
- handleClose: PropTypes.func,
1043
- intl: intlShape.isRequired,
1044
- webPushActions: PropTypes.object,
1045
- createTemplateInProgress: PropTypes.bool,
1046
- createTemplateError: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
1047
- editTemplateInProgress: PropTypes.bool,
1048
- editTemplateError: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
1049
- accountData: PropTypes.object,
1050
- webPush: PropTypes.object,
1051
- onCreateComplete: PropTypes.func,
1052
- getFormData: PropTypes.func,
1053
- isGetFormData: PropTypes.bool,
1054
- templateData: PropTypes.object,
1055
- creativesMode: PropTypes.string,
1056
- params: PropTypes.object,
1057
- globalActions: PropTypes.object,
1058
- location: PropTypes.object,
1059
- metaEntities: PropTypes.object,
1060
- injectedTags: PropTypes.object,
1061
- getDefaultTags: PropTypes.string,
1062
- supportedTags: PropTypes.array,
1063
- forwardedTags: PropTypes.object,
1064
- selectedOfferDetails: PropTypes.array,
1065
- eventContextTags: PropTypes.array,
1066
- templateActions: PropTypes.object,
1067
- };
1068
-
1069
- WebPushCreate.defaultProps = {
1070
- isFullMode: true,
1071
- handleClose: () => { },
1072
- webPushActions: {},
1073
- createTemplateInProgress: false,
1074
- editTemplateInProgress: false,
1075
- createTemplateError: '',
1076
- accountData: {},
1077
- webPush: {},
1078
- onCreateComplete: () => { },
1079
- getFormData: null,
1080
- isGetFormData: false,
1081
- templateData: null,
1082
- creativesMode: 'createTemplate',
1083
- params: null,
1084
- globalActions: {},
1085
- location: null,
1086
- metaEntities: null,
1087
- injectedTags: {},
1088
- getDefaultTags: '',
1089
- supportedTags: [],
1090
- forwardedTags: {},
1091
- selectedOfferDetails: [],
1092
- eventContextTags: [],
1093
- templateActions: {},
1094
- Templates: {},
1095
- };
1096
-
1097
- const mapStateToProps = createStructuredSelector({
1098
- webPush: makeSelectWebPush(),
1099
- createTemplateInProgress: makeSelectCreateTemplateInProgress(),
1100
- createTemplateError: makeSelectCreateError(),
1101
- editTemplateInProgress: makeSelectEditTemplateInProgress(),
1102
- editTemplateError: makeSelectEditError(),
1103
- metaEntities: makeSelectMetaEntities(),
1104
- injectedTags: setInjectedTags(),
1105
- Templates: makeSelectTemplates(),
1106
- accountData: createSelector(
1107
- (state) => state.get('templates'),
1108
- (templatesState) => {
1109
- if (!templatesState) {
1110
- return {};
1111
- }
1112
- const templates = templatesState.toJS();
1113
- return templates?.selectedWebPushAccount || {};
1114
- },
1115
- ),
1116
- });
1117
-
1118
- const mapDispatchToProps = (dispatch) => ({
1119
- webPushActions: bindActionCreators(actions, dispatch),
1120
- templateActions: bindActionCreators(templateActions, dispatch),
1121
- });
1122
-
1123
- const withWebPushSaga = injectSaga({
1124
- key: 'webPush',
1125
- saga: webPushSagas,
1126
- mode: DAEMON,
1127
- });
1128
-
1129
- const withTemplateSaga = injectSaga({
1130
- key: 'webPushTemplates',
1131
- saga: v2TemplateSaga,
1132
- mode: DAEMON,
1133
- });
1134
-
1135
- const withReducer = injectReducer({
1136
- key: 'webPush',
1137
- reducer: webPushReducer,
1138
- });
1139
-
1140
- export default withCreatives({
1141
- WrappedComponent: injectIntl(WebPushCreate),
1142
- mapStateToProps,
1143
- mapDispatchToProps,
1144
- userAuth: true,
1145
- sagas: [withWebPushSaga, withTemplateSaga],
1146
- reducers: [withReducer],
1147
- });
1148
-