@capillarytech/creatives-library 8.0.264 → 8.0.265

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 (158) hide show
  1. package/constants/unified.js +0 -1
  2. package/package.json +1 -1
  3. package/services/api.js +0 -5
  4. package/utils/common.js +0 -6
  5. package/utils/tagValidations.js +1 -2
  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/_unifiedPreview.scss +2 -2
  13. package/v2Components/FormBuilder/index.js +8 -8
  14. package/v2Containers/App/constants.js +0 -5
  15. package/v2Containers/CreativesContainer/SlideBoxContent.js +2 -57
  16. package/v2Containers/CreativesContainer/SlideBoxHeader.js +0 -1
  17. package/v2Containers/CreativesContainer/constants.js +0 -3
  18. package/v2Containers/CreativesContainer/index.js +0 -168
  19. package/v2Containers/CreativesContainer/messages.js +0 -4
  20. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
  21. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -304
  22. package/v2Containers/Email/index.js +7 -3
  23. package/v2Containers/FTP/index.js +1 -1
  24. package/v2Containers/InApp/index.js +0 -1
  25. package/v2Containers/Line/Container/Text/index.js +0 -1
  26. package/v2Containers/MobilePushNew/index.js +0 -1
  27. package/v2Containers/Rcs/index.js +0 -3
  28. package/v2Containers/SmsTrai/Edit/index.js +0 -1
  29. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  30. package/v2Containers/Templates/_templates.scss +0 -205
  31. package/v2Containers/Templates/actions.js +1 -2
  32. package/v2Containers/Templates/constants.js +0 -1
  33. package/v2Containers/Templates/index.js +34 -274
  34. package/v2Containers/Templates/messages.js +0 -24
  35. package/v2Containers/Templates/reducer.js +0 -2
  36. package/v2Containers/Templates/tests/index.test.js +0 -10
  37. package/v2Containers/TemplatesV2/index.js +7 -15
  38. package/v2Containers/TemplatesV2/messages.js +0 -4
  39. package/v2Containers/Viber/index.js +0 -1
  40. package/v2Containers/Whatsapp/index.js +0 -1
  41. package/v2Containers/Zalo/index.js +0 -1
  42. package/v2Containers/Zalo/tests/index.test.js +5 -1
  43. package/utils/imageUrlUpload.js +0 -141
  44. package/v2Components/CapImageUrlUpload/constants.js +0 -26
  45. package/v2Components/CapImageUrlUpload/index.js +0 -365
  46. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  47. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  48. package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
  49. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
  50. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  51. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
  52. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
  53. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
  54. package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
  55. package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
  56. package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
  57. package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
  58. package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
  59. package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
  60. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
  61. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
  62. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
  63. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
  64. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
  65. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
  66. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
  67. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
  68. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
  69. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
  70. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  71. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  72. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  73. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  74. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  75. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  76. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
  77. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
  78. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
  79. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
  80. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
  81. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
  82. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
  83. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
  84. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
  85. package/v2Containers/WebPush/Create/index.js +0 -1148
  86. package/v2Containers/WebPush/Create/index.scss +0 -134
  87. package/v2Containers/WebPush/Create/messages.js +0 -211
  88. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
  89. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
  90. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
  91. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
  92. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
  93. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
  94. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  95. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  96. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
  97. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
  98. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  99. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  100. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  101. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  102. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
  103. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
  104. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  105. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  106. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
  107. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
  108. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
  109. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
  110. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  111. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
  112. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
  113. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
  114. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
  115. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
  116. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
  117. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
  118. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
  119. package/v2Containers/WebPush/Create/preview/constants.js +0 -637
  120. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
  121. package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
  122. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
  123. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  124. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  125. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  126. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
  127. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  128. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  129. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  130. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
  131. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
  132. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  133. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
  134. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
  135. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  136. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
  137. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  138. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
  139. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
  140. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
  141. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  142. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
  143. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
  144. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
  145. package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
  146. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
  147. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  148. package/v2Containers/WebPush/Create/utils/validation.js +0 -76
  149. package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
  150. package/v2Containers/WebPush/actions.js +0 -60
  151. package/v2Containers/WebPush/constants.js +0 -132
  152. package/v2Containers/WebPush/index.js +0 -2
  153. package/v2Containers/WebPush/reducer.js +0 -104
  154. package/v2Containers/WebPush/sagas.js +0 -119
  155. package/v2Containers/WebPush/selectors.js +0 -65
  156. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  157. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  158. 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, isFullMode),
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
-