@capillarytech/creatives-library 8.0.358 → 8.0.359-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/index.html +0 -1
  2. package/package.json +1 -1
  3. package/utils/cdnTransformation.js +3 -75
  4. package/utils/tests/cdnTransformation.test.js +0 -127
  5. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +0 -16
  6. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +132 -14
  7. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +163 -54
  8. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +6 -52
  9. package/v2Components/CommonTestAndPreview/constants.js +0 -2
  10. package/v2Components/CommonTestAndPreview/index.js +231 -77
  11. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +0 -163
  12. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
  13. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +0 -255
  14. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
  15. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -194
  16. package/v2Components/FormBuilder/index.js +52 -162
  17. package/v2Components/TestAndPreviewSlidebox/index.js +2 -2
  18. package/v2Containers/App/constants.js +0 -3
  19. package/v2Containers/CreativesContainer/index.js +24 -60
  20. package/v2Containers/Templates/_templates.scss +77 -0
  21. package/v2Containers/Templates/index.js +92 -82
  22. package/v2Containers/Templates/sagas.js +1 -6
  23. package/v2Containers/Templates/tests/sagas.test.js +6 -23
  24. package/v2Containers/Viber/constants.js +19 -0
  25. package/v2Containers/Viber/index.js +714 -47
  26. package/v2Containers/Viber/index.scss +148 -0
  27. package/v2Containers/Viber/messages.js +116 -0
  28. package/v2Containers/Viber/tests/index.test.js +80 -0
  29. package/v2Containers/WebPush/Create/index.js +8 -91
  30. package/v2Containers/WebPush/Create/index.scss +0 -7
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
  32. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
  33. package/v2Containers/App/tests/constants.test.js +0 -61
  34. package/v2Containers/Templates/tests/webpush.test.js +0 -375
  35. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -348
  36. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
@@ -123,3 +123,151 @@
123
123
  .test-and-preview-button {
124
124
  margin-left: 6.25rem;
125
125
  }
126
+
127
+ .viber-remove-row-btn {
128
+ margin-top: $CAP_SPACE_24;
129
+ }
130
+
131
+ .viber-add-row-btn {
132
+ margin-top: $CAP_SPACE_08;
133
+ color: $FONT_COLOR_05;
134
+ background: transparent;
135
+ border: none;
136
+ box-shadow: none;
137
+
138
+ &:hover,
139
+ &:focus,
140
+ &:active {
141
+ color: $FONT_COLOR_05;
142
+ background: transparent;
143
+ border: none;
144
+ }
145
+ }
146
+
147
+ .viber-carousel-section {
148
+ margin-top: $CAP_SPACE_16;
149
+ }
150
+
151
+ .viber-carousel-card {
152
+ border: 1px solid $CAP_G06;
153
+ border-radius: $CAP_SPACE_04;
154
+ padding: $CAP_SPACE_16;
155
+ margin-bottom: $CAP_SPACE_16;
156
+ }
157
+
158
+ .viber-carousel-card-selector {
159
+ margin-bottom: $CAP_SPACE_16;
160
+ border-bottom: 1px solid $CAP_G07;
161
+ }
162
+
163
+ .viber-carousel-card-selector-btn {
164
+ min-width: 2.5rem;
165
+ padding: $CAP_SPACE_12 $CAP_SPACE_20;
166
+ border: none;
167
+ background: transparent;
168
+ color: $FONT_COLOR_04;
169
+ font-size: $CAP_SPACE_40;
170
+ cursor: pointer;
171
+ border-bottom: 3px solid transparent;
172
+ }
173
+
174
+ .viber-carousel-card-selector-btn.active {
175
+ color: $FONT_COLOR_01;
176
+ border-bottom-color: currentColor;
177
+ }
178
+
179
+ .viber-carousel-add-btn {
180
+ border-left: 1px solid $CAP_G07;
181
+ margin-left: $CAP_SPACE_16;
182
+ padding-left: $CAP_SPACE_24;
183
+ padding-right: $CAP_SPACE_24;
184
+ }
185
+
186
+ .viber-carousel-image-recommendation {
187
+ display: block;
188
+ margin: $CAP_SPACE_08 0 $CAP_SPACE_16 0;
189
+ color: $FONT_COLOR_03;
190
+ }
191
+
192
+ .viber-carousel-card-title-header {
193
+ align-items: center;
194
+ }
195
+
196
+ .viber-carousel-button {
197
+ border-top: 1px dashed $CAP_G06;
198
+ margin-top: $CAP_SPACE_12;
199
+ padding-top: $CAP_SPACE_12;
200
+ }
201
+
202
+ .viber-carousel-button-label {
203
+ margin-top: $CAP_SPACE_16;
204
+ }
205
+
206
+ .viber-carousel-button-title-header {
207
+ align-items: center;
208
+ }
209
+
210
+ .viber-carousel-url-type-select {
211
+ .ant-select-selection-selected-value {
212
+ max-width: none;
213
+ overflow: visible;
214
+ text-overflow: unset;
215
+ }
216
+
217
+ .ant-select-selection__rendered {
218
+ margin-right: $CAP_SPACE_24;
219
+ }
220
+ }
221
+
222
+ .viber-carousel-url-type-dropdown.ant-select-dropdown {
223
+ min-width: 12rem;
224
+ }
225
+
226
+ .viber-carousel-url-type-dropdown .ant-select-dropdown-menu-item {
227
+ white-space: normal;
228
+ word-break: break-word;
229
+ height: auto;
230
+ line-height: 1.25rem;
231
+ padding-top: $CAP_SPACE_06;
232
+ padding-bottom: $CAP_SPACE_06;
233
+ }
234
+
235
+ .viber-carousel-saved-button {
236
+ display: flex;
237
+ align-items: center;
238
+ border: 1px solid $CAP_G06;
239
+ border-radius: $CAP_SPACE_04;
240
+ padding: $CAP_SPACE_12 $CAP_SPACE_16;
241
+ margin-top: $CAP_SPACE_12;
242
+ }
243
+
244
+ .viber-carousel-saved-button-icon {
245
+ margin-right: $CAP_SPACE_12;
246
+ color: $FONT_COLOR_01;
247
+ }
248
+
249
+ .viber-carousel-saved-button-text {
250
+ flex: 1;
251
+ margin-right: $CAP_SPACE_12;
252
+ }
253
+
254
+ .viber-carousel-delete-icon-btn {
255
+ margin-left: $CAP_SPACE_08;
256
+ }
257
+
258
+ .viber-carousel-tab-label-disabled {
259
+ color: $FONT_COLOR_04;
260
+ cursor: not-allowed;
261
+ }
262
+
263
+ .viber-carousel-field-length-top {
264
+ display: block;
265
+ text-align: right;
266
+ margin-top: $CAP_SPACE_04;
267
+ }
268
+
269
+ .viber-form-error {
270
+ color: $CAP_RED;
271
+ display: block;
272
+ margin-top: $CAP_SPACE_08;
273
+ }
@@ -154,6 +154,10 @@ export default defineMessages({
154
154
  id: `${scope}.mediaVideo`,
155
155
  defaultMessage: 'Video',
156
156
  },
157
+ mediaCarousel: {
158
+ id: `${scope}.mediaCarousel`,
159
+ defaultMessage: 'Carousel',
160
+ },
157
161
  videoErrorMessage: {
158
162
  id: `${scope}.videoErrorMessage`,
159
163
  defaultMessage: 'Please upload the video with allowed file extension, size, dimension and aspect ratio',
@@ -210,6 +214,118 @@ export default defineMessages({
210
214
  id: `${scope}.cancel`,
211
215
  defaultMessage: 'Cancel',
212
216
  },
217
+ carouselCardsLabel: {
218
+ id: `${scope}.carouselCardsLabel`,
219
+ defaultMessage: 'Carousel cards',
220
+ },
221
+ carouselCardHeading: {
222
+ id: `${scope}.carouselCardHeading`,
223
+ defaultMessage: 'Card {index}',
224
+ },
225
+ carouselCardTextLabel: {
226
+ id: `${scope}.carouselCardTextLabel`,
227
+ defaultMessage: 'Title',
228
+ },
229
+ carouselCardTextPlaceholder: {
230
+ id: `${scope}.carouselCardTextPlaceholder`,
231
+ defaultMessage: 'Enter title',
232
+ },
233
+ carouselMediaUrlLabel: {
234
+ id: `${scope}.carouselMediaUrlLabel`,
235
+ defaultMessage: 'Card media URL',
236
+ },
237
+ carouselMediaUrlPlaceholder: {
238
+ id: `${scope}.carouselMediaUrlPlaceholder`,
239
+ defaultMessage: 'https://example.com/image.jpg',
240
+ },
241
+ carouselButtonTitleLabel: {
242
+ id: `${scope}.carouselButtonTitleLabel`,
243
+ defaultMessage: 'Button title',
244
+ },
245
+ carouselButtonTitlePlaceholder: {
246
+ id: `${scope}.carouselButtonTitlePlaceholder`,
247
+ defaultMessage: 'Enter button title',
248
+ },
249
+ carouselButtonActionLabel: {
250
+ id: `${scope}.carouselButtonActionLabel`,
251
+ defaultMessage: 'Button action URL',
252
+ },
253
+ carouselButtonUrlTypeLabel: {
254
+ id: `${scope}.carouselButtonUrlTypeLabel`,
255
+ defaultMessage: 'URL type',
256
+ },
257
+ carouselUrlTypeStatic: {
258
+ id: `${scope}.carouselUrlTypeStatic`,
259
+ defaultMessage: 'Static',
260
+ },
261
+ carouselUrlTypeDynamic: {
262
+ id: `${scope}.carouselUrlTypeDynamic`,
263
+ defaultMessage: 'Dynamic',
264
+ },
265
+ carouselButtonActionPlaceholder: {
266
+ id: `${scope}.carouselButtonActionPlaceholder`,
267
+ defaultMessage: 'https://example.com/action',
268
+ },
269
+ addCarouselCard: {
270
+ id: `${scope}.addCarouselCard`,
271
+ defaultMessage: 'Add card',
272
+ },
273
+ removeCarouselCard: {
274
+ id: `${scope}.removeCarouselCard`,
275
+ defaultMessage: 'Remove card',
276
+ },
277
+ addCarouselButton: {
278
+ id: `${scope}.addCarouselButton`,
279
+ defaultMessage: 'Add button',
280
+ },
281
+ removeCarouselButton: {
282
+ id: `${scope}.removeCarouselButton`,
283
+ defaultMessage: 'Remove button',
284
+ },
285
+ carouselCardsLimitError: {
286
+ id: `${scope}.carouselCardsLimitError`,
287
+ defaultMessage: 'Carousel message requires 2 to 5 cards',
288
+ },
289
+ carouselCardError: {
290
+ id: `${scope}.carouselCardError`,
291
+ defaultMessage: 'Each card needs text and a valid media URL',
292
+ },
293
+ carouselButtonError: {
294
+ id: `${scope}.carouselButtonError`,
295
+ defaultMessage: 'Each button needs title and a valid action URL',
296
+ },
297
+ characterLimitExceededError: {
298
+ id: `${scope}.characterLimitExceededError`,
299
+ defaultMessage: 'Character limit exceeded',
300
+ },
301
+ textCannotBeEmptyError: {
302
+ id: `${scope}.textCannotBeEmptyError`,
303
+ defaultMessage: "Text can't be empty",
304
+ },
305
+ urlCannotBeEmptyError: {
306
+ id: `${scope}.urlCannotBeEmptyError`,
307
+ defaultMessage: "URL can't be empty",
308
+ },
309
+ carouselButtonUrlMaxLengthError: {
310
+ id: `${scope}.carouselButtonUrlMaxLengthError`,
311
+ defaultMessage: 'URL can not exceed 1000 characters',
312
+ },
313
+ carouselFirstButtonTitleMaxLengthError: {
314
+ id: `${scope}.carouselFirstButtonTitleMaxLengthError`,
315
+ defaultMessage: 'Button text should not exceed 10',
316
+ },
317
+ carouselSecondButtonTitleMaxLengthError: {
318
+ id: `${scope}.carouselSecondButtonTitleMaxLengthError`,
319
+ defaultMessage: 'Button text should not exceed 12',
320
+ },
321
+ carouselCardTitleMinLengthError: {
322
+ id: `${scope}.carouselCardTitleMinLengthError`,
323
+ defaultMessage: 'Minimum 2 characters required',
324
+ },
325
+ carouselCardTitleMaxLengthError: {
326
+ id: `${scope}.carouselCardTitleMaxLengthError`,
327
+ defaultMessage: 'Title can not be more than 38 characters',
328
+ },
213
329
  assetIdMissingError: {
214
330
  id: `${scope}.assetIdMissingError`,
215
331
  defaultMessage: 'Asset upload initiated but no asset ID was returned from the server. Unable to track processing status.',
@@ -383,4 +383,84 @@ describe('Test Viber container', () => {
383
383
  const doneBtn = screen.getByRole('button', { name: /done/i });
384
384
  expect(doneBtn).toBeEnabled();
385
385
  });
386
+
387
+ it('does not show empty URL error on focus or while typing in carousel button action', async () => {
388
+ renderComponent({
389
+ actions: mockActions,
390
+ globalActions: mockGlobalActions,
391
+ templateData: { mode: 'create' },
392
+ viber: {
393
+ uploadedAssetData: {},
394
+ createTemplateInProgress: false,
395
+ },
396
+ location: {
397
+ pathname: '/sms/edit',
398
+ query: { type: false, module: 'default' },
399
+ search: '',
400
+ },
401
+ isFullMode: true,
402
+ handleClose: jest.fn(),
403
+ });
404
+
405
+ fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
406
+ const actionInput = await screen.findByPlaceholderText('https://example.com/action');
407
+
408
+ fireEvent.focus(actionInput);
409
+ expect(screen.queryByText("URL can't be empty")).not.toBeInTheDocument();
410
+
411
+ fireEvent.change(actionInput, { target: { value: 'https://' } });
412
+ expect(screen.queryByText("URL can't be empty")).not.toBeInTheDocument();
413
+ });
414
+
415
+ it('shows empty URL error on blur when carousel button action is left empty', async () => {
416
+ renderComponent({
417
+ actions: mockActions,
418
+ globalActions: mockGlobalActions,
419
+ templateData: { mode: 'create' },
420
+ viber: {
421
+ uploadedAssetData: {},
422
+ createTemplateInProgress: false,
423
+ },
424
+ location: {
425
+ pathname: '/sms/edit',
426
+ query: { type: false, module: 'default' },
427
+ search: '',
428
+ },
429
+ isFullMode: true,
430
+ handleClose: jest.fn(),
431
+ });
432
+
433
+ fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
434
+ const actionInput = await screen.findByPlaceholderText('https://example.com/action');
435
+
436
+ fireEvent.focus(actionInput);
437
+ fireEvent.blur(actionInput);
438
+
439
+ expect(await screen.findByText("URL can't be empty")).toBeInTheDocument();
440
+ });
441
+
442
+ it('shows empty URL error on save when carousel button action is empty', async () => {
443
+ renderComponent({
444
+ actions: mockActions,
445
+ globalActions: mockGlobalActions,
446
+ templateData: { mode: 'create' },
447
+ viber: {
448
+ uploadedAssetData: {},
449
+ createTemplateInProgress: false,
450
+ },
451
+ location: {
452
+ pathname: '/sms/edit',
453
+ query: { type: false, module: 'default' },
454
+ search: '',
455
+ },
456
+ isFullMode: true,
457
+ handleClose: jest.fn(),
458
+ });
459
+
460
+ fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
461
+ await screen.findByPlaceholderText('https://example.com/action');
462
+ fireEvent.click(screen.getByRole('button', { name: /save/i }));
463
+
464
+ expect(await screen.findByText("URL can't be empty")).toBeInTheDocument();
465
+ });
386
466
  });
@@ -45,8 +45,6 @@ import {
45
45
  ACTION_TYPES,
46
46
  NOTIFICATION_TITLE_MAX_LENGTH,
47
47
  MESSAGE_MAX_LENGTH,
48
- EXTERNAL_URL,
49
- SITE_URL,
50
48
  } from '../constants';
51
49
  import * as actions from '../actions';
52
50
  import {
@@ -75,9 +73,6 @@ import {
75
73
  import './index.scss';
76
74
  import { WEBPUSH } from '../../CreativesContainer/constants';
77
75
  import { isAiContentBotDisabled } from '../../../utils/common';
78
- import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
79
- import creativesMessages from '../../CreativesContainer/messages';
80
- import CapButton from '@capillarytech/cap-ui-library/CapButton';
81
76
 
82
77
  // Memoized TagList wrapper components for better performance
83
78
  const MemoizedTagList = memo(({
@@ -187,7 +182,6 @@ const WebPushCreate = ({
187
182
  const [redirectUrlError, setRedirectUrlError] = useState('');
188
183
  const [activeUploadField, setActiveUploadField] = useState(null);
189
184
  const [templateIdError, setTemplateIdError] = useState('');
190
- const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
191
185
 
192
186
  // Refs
193
187
  const titleCountRef = useRef(null);
@@ -575,67 +569,6 @@ const WebPushCreate = ({
575
569
  return !(templateNameInvalid || titleValidation || messageValidation);
576
570
  };
577
571
 
578
- const getTemplateContent = useCallback(() => {
579
- let cta = null;
580
- if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL && redirectUrl) {
581
- cta = { type: EXTERNAL_URL, actionLink: redirectUrl?.trim() };
582
- } else if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL && websiteLink) {
583
- cta = { type: SITE_URL, actionLink: websiteLink };
584
- }
585
-
586
- const hasImage = mediaType === WEBPUSH_MEDIA_TYPES.IMAGE && imageSrc;
587
- const hasCtas = buttons?.length > 0;
588
- let expandableDetails = null;
589
- if (hasImage || hasCtas) {
590
- expandableDetails = {
591
- media: hasImage ? [{ url: imageSrc, type: WEBPUSH_MEDIA_TYPES.IMAGE }] : [],
592
- ctas: hasCtas ? buttons.map((btn) => ({
593
- type: EXTERNAL_URL,
594
- action: '',
595
- title: btn.text || '',
596
- actionLink: btn.url || '',
597
- })) : [],
598
- };
599
- }
600
-
601
- const iconImageUrl = brandIconOption !== BRAND_ICON_OPTIONS.DONT_SHOW && brandIconSrc ? brandIconSrc : undefined;
602
-
603
- return {
604
- channel: WEBPUSH,
605
- accountId,
606
- content: {
607
- title: notificationTitle || '',
608
- message: message || '',
609
- ...(iconImageUrl ? { iconImageUrl } : {}),
610
- ...(cta ? { cta } : {}),
611
- ...(expandableDetails ? { expandableDetails } : {}),
612
- },
613
- messageSubject: templateName || notificationTitle || '',
614
- offers: [],
615
- };
616
- }, [
617
- notificationTitle,
618
- message,
619
- accountId,
620
- mediaType,
621
- imageSrc,
622
- brandIconOption,
623
- brandIconSrc,
624
- buttons,
625
- onClickBehaviour,
626
- redirectUrl,
627
- websiteLink,
628
- templateName,
629
- ]);
630
-
631
- const handleTestAndPreview = useCallback(() => {
632
- setShowTestAndPreviewSlidebox(true);
633
- }, []);
634
-
635
- const handleCloseTestAndPreview = useCallback(() => {
636
- setShowTestAndPreviewSlidebox(false);
637
- }, []);
638
-
639
572
  const isFormValid = () => {
640
573
  const templateNameInvalid = isFullMode && validateTemplateName(templateName);
641
574
  const titleValidation = validateTitle(notificationTitle);
@@ -1107,23 +1040,14 @@ const WebPushCreate = ({
1107
1040
  formatMessage={formatMessage}
1108
1041
  messages={messages}
1109
1042
  />
1110
- <CapRow className="webpush-test-preview-action">
1111
- <FormActions
1112
- onSave={handleSave}
1113
- isSaveDisabled={isSaveDisabled}
1114
- errorText={errorText}
1115
- accountErrorText={accountErrorText}
1116
- formatMessage={formatMessage}
1117
- messages={messages}
1118
- />
1119
- <CapButton
1120
- type="secondary"
1121
- onClick={handleTestAndPreview}
1122
- className="webpush-test-preview-btn"
1123
- >
1124
- {formatMessage(creativesMessages.testAndPreview)}
1125
- </CapButton>
1126
- </CapRow>
1043
+ <FormActions
1044
+ onSave={handleSave}
1045
+ isSaveDisabled={isSaveDisabled}
1046
+ errorText={errorText}
1047
+ accountErrorText={accountErrorText}
1048
+ formatMessage={formatMessage}
1049
+ messages={messages}
1050
+ />
1127
1051
  </CapColumn>
1128
1052
  <CapColumn className="preview-section" span={10}>
1129
1053
  <WebPushPreview
@@ -1135,13 +1059,6 @@ const WebPushCreate = ({
1135
1059
  buttons={buttons}
1136
1060
  />
1137
1061
  </CapColumn>
1138
- <TestAndPreviewSlidebox
1139
- show={showTestAndPreviewSlidebox}
1140
- onClose={handleCloseTestAndPreview}
1141
- content={getTemplateContent()}
1142
- currentChannel={WEBPUSH}
1143
- accountId={accountId || null}
1144
- />
1145
1062
  </CapRow>
1146
1063
  );
1147
1064
  };
@@ -135,12 +135,5 @@
135
135
  font-family: 'Roboto', open-sans, sans-serif;
136
136
  }
137
137
  }
138
-
139
- .webpush-test-preview-action {
140
- display: flex;
141
- .webpush-test-preview-btn {
142
- margin-left: $CAP_SPACE_12;
143
- }
144
- }
145
138
  }
146
139
 
@@ -1,169 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { FormattedMessage } from 'react-intl';
4
- import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
5
- import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
6
- import CapModal from '@capillarytech/cap-ui-library/CapModal';
7
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
8
- import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
9
- import PreviewControls from '../../../v2Containers/WebPush/Create/preview/PreviewControls';
10
- import PreviewContent from '../../../v2Containers/WebPush/Create/preview/PreviewContent';
11
- import DevicePreviewContent from '../../../v2Containers/WebPush/Create/preview/DevicePreviewContent';
12
- import {
13
- OS_OPTIONS,
14
- DEFAULT_OS,
15
- DEFAULT_BROWSER,
16
- } from '../../../v2Containers/WebPush/Create/preview/constants';
17
- import { getBrowserOptionsForOS } from '../../../v2Containers/WebPush/Create/preview/config/notificationMappings';
18
- import messages from '../messages';
19
-
20
- const WebPushPreviewContent = ({
21
- notificationTitle,
22
- notificationBody,
23
- imageSrc,
24
- brandIconSrc,
25
- buttons,
26
- url,
27
- isUpdating,
28
- error,
29
- isFullscreenOpen,
30
- setIsFullscreenOpen,
31
- selectedCustomer,
32
- }) => {
33
- const [selectedOS, setSelectedOS] = useState(DEFAULT_OS);
34
- const [selectedBrowser, setSelectedBrowser] = useState(DEFAULT_BROWSER);
35
-
36
- const browserOptionsForOS = getBrowserOptionsForOS(selectedOS);
37
-
38
- // Coerce browser when OS change removes current browser from available options
39
- useEffect(() => {
40
- const isValid = browserOptionsForOS.some((opt) => opt.value === selectedBrowser);
41
- if (!isValid && browserOptionsForOS.length) {
42
- setSelectedBrowser(browserOptionsForOS[0].value);
43
- }
44
- }, [browserOptionsForOS, selectedBrowser]);
45
-
46
- const handleOSChange = (value) => {
47
- setSelectedOS(value);
48
- };
49
-
50
- const handleBrowserChange = (value) => {
51
- setSelectedBrowser(value);
52
- };
53
-
54
- const handleCloseFullscreen = () => {
55
- setIsFullscreenOpen(false);
56
- };
57
-
58
- if (isUpdating) {
59
- return null;
60
- }
61
-
62
- if (error) {
63
- return null;
64
- }
65
-
66
- return (
67
- <CapRow className="webpush-test-preview-container webpush-preview-container">
68
- <CapRow className="webpush-preview-panel">
69
- <PreviewControls
70
- selectedOS={selectedOS}
71
- selectedBrowser={selectedBrowser}
72
- osOptions={OS_OPTIONS}
73
- browserOptions={browserOptionsForOS}
74
- onOSChange={handleOSChange}
75
- onBrowserChange={handleBrowserChange}
76
- layoutMode="newRow"
77
- showStateDropdown={false}
78
- />
79
-
80
- <PreviewContent
81
- notificationTitle={notificationTitle}
82
- notificationBody={notificationBody}
83
- url={url}
84
- selectedOS={selectedOS}
85
- selectedBrowser={selectedBrowser}
86
- notificationState="Collapsed"
87
- imageSrc={imageSrc}
88
- brandIconSrc={brandIconSrc}
89
- buttons={buttons}
90
- />
91
- </CapRow>
92
-
93
- <CapModal
94
- visible={isFullscreenOpen}
95
- onCancel={handleCloseFullscreen}
96
- width="90%"
97
- centered
98
- closable={false}
99
- footer={null}
100
- title={null}
101
- maskClosable
102
- className="webpush-fullscreen-modal webpush-preview-container"
103
- >
104
- <CapRow className="webpush-preview-header">
105
- <div className="webpush-heading-container">
106
- <CapRow type="flex" className="preview-for">
107
- <CapLabel type="label16">
108
- <FormattedMessage {...messages.previewFor} />
109
- </CapLabel>
110
- <CapLabel type="label2">
111
- {selectedCustomer ? selectedCustomer.name : <FormattedMessage {...messages.defaultPreview} />}
112
- </CapLabel>
113
- </CapRow>
114
- <CapIcon
115
- type="collapse2"
116
- onClick={() => handleCloseFullscreen()}
117
- className="webpush-fullscreen-close-icon"
118
- />
119
- </div>
120
- </CapRow>
121
- <CapDivider className="webpush-fullscreen-divider" />
122
- <DevicePreviewContent
123
- notificationTitle={notificationTitle}
124
- notificationBody={notificationBody}
125
- url={url}
126
- imageSrc={imageSrc}
127
- brandIconSrc={brandIconSrc}
128
- buttons={buttons}
129
- />
130
- </CapModal>
131
- </CapRow>
132
- );
133
- };
134
-
135
- WebPushPreviewContent.propTypes = {
136
- notificationTitle: PropTypes.string,
137
- notificationBody: PropTypes.string,
138
- imageSrc: PropTypes.string,
139
- brandIconSrc: PropTypes.string,
140
- buttons: PropTypes.arrayOf(
141
- PropTypes.shape({
142
- text: PropTypes.string,
143
- url: PropTypes.string,
144
- type: PropTypes.string,
145
- })
146
- ),
147
- url: PropTypes.string,
148
- isUpdating: PropTypes.bool,
149
- error: PropTypes.string,
150
- isFullscreenOpen: PropTypes.bool,
151
- setIsFullscreenOpen: PropTypes.func,
152
- selectedCustomer: PropTypes.object,
153
- };
154
-
155
- WebPushPreviewContent.defaultProps = {
156
- notificationTitle: '',
157
- notificationBody: '',
158
- imageSrc: '',
159
- brandIconSrc: '',
160
- buttons: [],
161
- url: '',
162
- isUpdating: false,
163
- error: null,
164
- isFullscreenOpen: false,
165
- setIsFullscreenOpen: () => {},
166
- selectedCustomer: null,
167
- };
168
-
169
- export default WebPushPreviewContent;