@capillarytech/creatives-library 8.0.129 → 8.0.131

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 (55) hide show
  1. package/containers/Login/index.js +1 -2
  2. package/containers/Templates/constants.js +10 -1
  3. package/containers/Templates/index.js +45 -45
  4. package/package.json +1 -1
  5. package/services/api.js +14 -7
  6. package/services/tests/haptic-api.test.js +387 -0
  7. package/utils/createMobilePushPayload.js +322 -0
  8. package/utils/tests/{createPayload.test.js → createMobilePushPayload.test.js} +333 -64
  9. package/utils/tests/vendorDataTransformers.test.js +512 -0
  10. package/utils/vendorDataTransformers.js +108 -0
  11. package/v2Components/CapDeviceContent/index.js +1 -1
  12. package/v2Components/CapDocumentUpload/index.js +2 -2
  13. package/v2Components/CapImageUpload/index.js +2 -2
  14. package/v2Components/CapMpushCTA/index.js +13 -12
  15. package/v2Components/CapTagList/index.js +5 -5
  16. package/v2Components/CapVideoUpload/index.js +17 -7
  17. package/v2Components/MobilePushPreviewV2/index.js +28 -15
  18. package/v2Components/TemplatePreview/_templatePreview.scss +131 -29
  19. package/v2Components/TemplatePreview/index.js +130 -131
  20. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +10 -10
  21. package/v2Containers/CreativesContainer/index.js +6 -4
  22. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +4748 -4658
  23. package/v2Containers/Login/index.js +1 -2
  24. package/v2Containers/MobilePush/tests/commonMethods.test.js +401 -0
  25. package/v2Containers/MobilePushNew/components/CtaButtons.js +18 -16
  26. package/v2Containers/MobilePushNew/components/MediaUploaders.js +46 -45
  27. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +12 -11
  28. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +134 -367
  29. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +1209 -143
  30. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +314 -3
  31. package/v2Containers/MobilePushNew/constants.js +1 -0
  32. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +163 -0
  33. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1131 -895
  34. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +172 -52
  35. package/v2Containers/MobilePushNew/hooks/useUpload.js +88 -74
  36. package/v2Containers/MobilePushNew/index.js +278 -1532
  37. package/v2Containers/MobilePushNew/messages.js +30 -0
  38. package/v2Containers/MobilePushNew/sagas.js +2 -7
  39. package/v2Containers/MobilePushNew/tests/sagas.test.js +41 -40
  40. package/v2Containers/MobilePushNew/tests/selectors.test.js +240 -0
  41. package/v2Containers/MobilePushNew/tests/utils.test.js +118 -19
  42. package/v2Containers/MobilePushNew/utils.js +53 -2
  43. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1171 -971
  44. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +684 -424
  45. package/v2Containers/Templates/_templates.scss +0 -1
  46. package/v2Containers/Templates/index.js +58 -29
  47. package/v2Containers/Templates/sagas.js +0 -1
  48. package/v2Containers/Whatsapp/constants.js +32 -0
  49. package/v2Containers/Whatsapp/index.js +104 -25
  50. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +3992 -3677
  51. package/v2Containers/Whatsapp/tests/haptic.test.js +405 -0
  52. package/assets/loading_img.gif +0 -0
  53. package/utils/createPayload.js +0 -405
  54. /package/v2Components/TemplatePreview/assets/images/{Android _ With date and time.svg → Android_With_date_and_time.svg} +0 -0
  55. /package/v2Components/TemplatePreview/assets/images/{iOS _ With date and time.svg → iOS_With_date_and_time.svg} +0 -0
@@ -20,6 +20,7 @@ import {
20
20
  isUrl,
21
21
  isValidText,
22
22
  } from "../../v2Containers/Line/Container/Wrapper/utils";
23
+ import { isDeepLink } from "../../v2Containers/MobilePushNew/utils";
23
24
  import {
24
25
  BTN_MAX_LENGTH,
25
26
  DEEP_LINK,
@@ -28,6 +29,7 @@ import {
28
29
  URL_MAX_LENGTH,
29
30
  } from "./constants";
30
31
  import { PRIMARY } from "../../v2Containers/MobilePushNew/constants";
32
+ import { URL } from "../../v2Containers/Whatsapp/constants";
31
33
 
32
34
  export const CapMpushCTA = (props) => {
33
35
  const {
@@ -87,7 +89,7 @@ export const CapMpushCTA = (props) => {
87
89
  [type]: value,
88
90
  };
89
91
 
90
- if (type === 'url') {
92
+ if (type === URL.toLowerCase()) {
91
93
  setLocalUrlValues(prev => ({
92
94
  ...prev,
93
95
  [index]: value
@@ -146,7 +148,7 @@ export const CapMpushCTA = (props) => {
146
148
  const index = parseInt(dataset.index, 10);
147
149
 
148
150
  // If user is typing a new URL, remove from cleared set
149
- if (value && value.trim() !== '') {
151
+ if (value && value?.trim() !== '') {
150
152
  clearedUrlsRef.current.delete(index);
151
153
  }
152
154
 
@@ -168,7 +170,7 @@ export const CapMpushCTA = (props) => {
168
170
  const onDeepLinkSelect = (value, index) => {
169
171
  setCtaDeepLinkValue(value);
170
172
  let errorMessage = false;
171
- if (!isUrl(value)) {
173
+ if (!isDeepLink(value)) {
172
174
  errorMessage = formatMessage(messages.ctaWebsiteUrlErrorMessage);
173
175
  }
174
176
  setUrlError(errorMessage);
@@ -179,7 +181,7 @@ export const CapMpushCTA = (props) => {
179
181
  const { value, dataset } = target;
180
182
  const index = parseInt(dataset.index, 10);
181
183
  // Parse comma-separated values into array
182
- const keysArray = value.split(',').map((key) => key.trim()).filter((key) => key.length > 0);
184
+ const keysArray = value?.split(',')?.map((key) => key?.trim())?.filter((key) => key?.length > 0);
183
185
  setCtaDeepLinkKeysValue(keysArray);
184
186
  setDeepLinkKeysError(keysArray.length === 0);
185
187
  updateHelper('deepLinkKeys', keysArray, index);
@@ -191,7 +193,7 @@ export const CapMpushCTA = (props) => {
191
193
  return true;
192
194
  } else if (urlType === DEEP_LINK && (ctaDeepLinkValue === "" || urlError)) {
193
195
  return true;
194
- } else if (urlType === DEEP_LINK && ctaDeepLinkValue && deepLinkKeysFromSelectionArray.length > 0 && (!Array.isArray(ctaDeepLinkKeysValue) || ctaDeepLinkKeysValue.length === 0 || deepLinkKeysError)) {
196
+ } else if (urlType === DEEP_LINK && ctaDeepLinkValue && deepLinkKeysFromSelectionArray?.length > 0 && (!Array.isArray(ctaDeepLinkKeysValue) || ctaDeepLinkKeysValue?.length === 0 || deepLinkKeysError)) {
195
197
  return true;
196
198
  } else if (urlType === EXTERNAL_URL && (url === "" || urlError)) {
197
199
  return true;
@@ -206,14 +208,14 @@ export const CapMpushCTA = (props) => {
206
208
  const renderArray = [];
207
209
  const filteredCtaData = ctaData?.filter((cta) => {
208
210
  if (buttonType === PRIMARY) {
209
- return cta.index === 0;
211
+ return cta?.index === 0;
210
212
  } else {
211
- return cta.index === 1;
213
+ return cta?.index === 1;
212
214
  }
213
215
  });
214
216
 
215
217
  filteredCtaData?.forEach((cta) => {
216
- const { index, text, url, urlType, isSaved } = cta || {};
218
+ const { index, text, urlType, isSaved } = cta || {};
217
219
  if (isSaved) {
218
220
  renderArray.push(
219
221
  <CapRow
@@ -240,10 +242,10 @@ export const CapMpushCTA = (props) => {
240
242
  {urlType === DEEP_LINK ? selectedDeepLink?.label : ''}
241
243
  </CapColumn>
242
244
  <CapColumn>
243
- {urlType === DEEP_LINK && deepLinkKeysFromSelectionArray.length > 0 && (ctaData[index]?.deepLinkKeys || selectedDeepLink?.keys) ? (
245
+ {urlType === DEEP_LINK && deepLinkKeysFromSelectionArray?.length > 0 && (ctaData[index]?.deepLinkKeys || selectedDeepLink?.keys) ? (
244
246
  <CapLabel type="label2" className="inapp-saved-cta-deep-link-keys">
245
247
  {(() => {
246
- const keys = selectedDeepLink?.keys || ctaData[index].deepLinkKeys;
248
+ const keys = selectedDeepLink?.keys || ctaData[index]?.deepLinkKeys;
247
249
  if (Array.isArray(keys)) {
248
250
  return keys.join(', ');
249
251
  }
@@ -347,14 +349,13 @@ export const CapMpushCTA = (props) => {
347
349
  if (deepLinkKeysArray.length > 0) {
348
350
  return deepLinkKeysArray.join(', ');
349
351
  }
350
- return "No value set";
351
352
  })()}
352
353
  </CapLabel>
353
354
  <CapInput
354
355
  id="inapp-deep-link-keys"
355
356
  className="inapp-deep-link-keys"
356
357
  onChange={onDeepLinkKeysChange}
357
- placeholder={formatMessage(messages.deepLinkKeysPlaceholder, { key: deepLinkKeysFromSelectionArray.join(', ') || 'deep link keys' })}
358
+ placeholder={formatMessage(messages.deepLinkKeysPlaceholder, { key: deepLinkKeysFromSelectionArray.join(', ')})}
358
359
  value={Array.isArray(ctaDeepLinkKeysValue) ? ctaDeepLinkKeysValue.join(', ') : (ctaDeepLinkKeysValue || '')}
359
360
  size="large"
360
361
  data-index={index}
@@ -39,7 +39,7 @@ import {
39
39
  TAG_TRANSLATION_DOC,
40
40
  STRING,
41
41
  } from "../../containers/TagList/constants";
42
- import { EMAIL, JP_LOCALE_HIDE_FEATURE } from '../../v2Containers/App/constants';
42
+ import { EMAIL, JP_LOCALE_HIDE_FEATURE, LOYALTY } from '../../v2Containers/App/constants';
43
43
  import { hidingDateTagsForJpLocale } from '../../v2Containers/TagList/utils';
44
44
 
45
45
 
@@ -168,7 +168,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
168
168
  isLoadingContextChange: true,
169
169
  selectedContext: data,
170
170
  // Keep loyalty-specific loading for backward compatibility
171
- isLoadingLoyaltyTags: data === 'Loyalty',
171
+ isLoadingLoyaltyTags: data === LOYALTY,
172
172
  });
173
173
 
174
174
  // Call the parent's context change handler
@@ -390,7 +390,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
390
390
  } = this.state;
391
391
 
392
392
  // Show loading spinner if general loading OR if specifically loading loyalty tags OR if context change is in progress
393
- const shouldShowLoading = this.props.loading || (selectedContext === 'Loyalty' && isLoadingLoyaltyTags) || isLoadingContextChange;
393
+ const shouldShowLoading = this.props.loading || (selectedContext === LOYALTY && isLoadingLoyaltyTags) || isLoadingContextChange;
394
394
 
395
395
  const options = [
396
396
  {
@@ -405,7 +405,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
405
405
  },
406
406
  ];
407
407
  const contentSection = (
408
- <div>
408
+ <CapRow>
409
409
  <CapSpin tip={formatMessage(messages.gettingTags)} spinning={shouldShowLoading}>
410
410
  <Search
411
411
  style={{ marginBottom: 8, width: '250px'}}
@@ -455,7 +455,7 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
455
455
  )}
456
456
 
457
457
  </CapSpin>
458
- </div>
458
+ </CapRow>
459
459
  );
460
460
  return (
461
461
  <>
@@ -46,6 +46,8 @@ function CapVideoUpload(props) {
46
46
  formClassName = 'video-form',
47
47
  showReUploadButton = true,
48
48
  mediaType = VIDEO.toLowerCase(),
49
+ assetUploading = false, // Add assetUploading prop
50
+ videoSrc: propVideoSrc = '', // Add videoSrc prop for fallback
49
51
  } = props;
50
52
  const [isVideoError, updateVideoErrorMessage] = useState(false);
51
53
  const [isVideo, updateVideoStatus] = useState(false);
@@ -56,7 +58,7 @@ function CapVideoUpload(props) {
56
58
 
57
59
  const videoEl = useRef(null);
58
60
  const {
59
- videoSrc = '',
61
+ videoSrc: uploadedVideoSrc = '',
60
62
  videoName,
61
63
  previewUrl,
62
64
  videoHeight,
@@ -65,6 +67,10 @@ function CapVideoUpload(props) {
65
67
  fileHandle = '',
66
68
  } = uploadedAssetList;
67
69
 
70
+ // Use uploadedAssetList.videoSrc first, then fall back to prop videoSrc
71
+ // Only use propVideoSrc if uploadedVideoSrc is empty to avoid conflicts with upload logic
72
+ const videoSrc = uploadedVideoSrc || (uploadedVideoSrc === '' ? propVideoSrc : '');
73
+
68
74
  const [isTemplateDrawerRequired, updateTemplateDrawerRequirement] = useState(false);
69
75
 
70
76
  const uploadVideo = useCallback((e, { files }) => {
@@ -123,11 +129,9 @@ function CapVideoUpload(props) {
123
129
  }, [videoData, index, videoSrc, onVideoUploadUpdateAssestList]);
124
130
 
125
131
  useEffect(() => {
126
- if (videoData[`assetUploadingVideo_${index}`] && videoData.assetUploading !== false) {
127
- const isSpinner = get(videoData, `assetUploadingVideo_${index}`, false);
128
- setSpinning(isSpinner);
129
- }
130
- }, [videoData[`assetUploadingVideo_${index}`]]);
132
+ // Use the assetUploading prop directly for loading state
133
+ setSpinning(assetUploading);
134
+ }, [assetUploading]);
131
135
 
132
136
 
133
137
  useEffect(() => {
@@ -205,9 +209,13 @@ function CapVideoUpload(props) {
205
209
  fileHandle: "",
206
210
  }
207
211
  );
212
+
213
+ // Reset file input value to allow re-upload of same file
208
214
  setTimeout(() => {
209
215
  const fileInput = document.getElementById("videoFileName");
210
216
  if (fileInput) {
217
+ // eslint-disable-next-line no-param-reassign
218
+ fileInput.value = '';
211
219
  fileInput.click();
212
220
  }
213
221
  }, 100);
@@ -340,7 +348,7 @@ function CapVideoUpload(props) {
340
348
  <div style={style} className="cap-custom-video-upload">
341
349
  <form encType="multipart/form-data" id="form" className={formClassName}>
342
350
  <input
343
- key="videoFile"
351
+ key={`videoFile-${index}-${isVideo ? 'uploaded' : 'empty'}`}
344
352
  style={{ display: 'none' }}
345
353
  id="videoFileName"
346
354
  type="file"
@@ -423,6 +431,8 @@ CapVideoUpload.propTypes = {
423
431
  formClassName: PropTypes.string,
424
432
  showReUploadButton: PropTypes.bool,
425
433
  mediaType: PropTypes.string,
434
+ assetUploading: PropTypes.bool,
435
+ videoSrc: PropTypes.string,
426
436
  };
427
437
 
428
438
  export default injectIntl(CapVideoUpload);
@@ -8,14 +8,14 @@ import PropTypes from 'prop-types';
8
8
 
9
9
  import React from 'react';
10
10
  import { CapTab, CapIcon } from '@capillarytech/cap-ui-library';
11
- import { get, map, isEmpty} from 'lodash';
11
+ import { get, isEmpty} from 'lodash';
12
12
  import TemplatePreview from '../TemplatePreview';
13
13
  import '../PreviewSideBar/_previewsidebar.scss';
14
14
  import { MOBILE_PUSH } from '../../v2Containers/CreativesContainer/constants';
15
15
  import { INAPP } from '../../v2Containers/App/constants';
16
16
  import { ANDROID, IOS } from '../../v2Containers/InApp/constants';
17
17
  import { getCtaObject } from '../../v2Containers/InApp/utils';
18
- import { VIDEO } from '../../v2Containers/MobilePushNew/constants';
18
+ import { CAROUSEL, VIDEO } from '../../v2Containers/MobilePushNew/constants';
19
19
 
20
20
  class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
21
21
  constructor(props) {
@@ -66,23 +66,28 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
66
66
  const iosContent = get(templateData, 'versions.base.IOS') || get(templateData, 'iosContent');
67
67
 
68
68
  const data = device?.toLowerCase() === "android" ? androidContent : iosContent;
69
- const { title = '', message = '', expandableDetails: { ctas = [], image = '', media = [], carouselData = [] } = {}} = data || {};
69
+ const {
70
+ title = '', message = '', type = '', expandableDetails: {
71
+ ctas = [], image = '', media = [], carouselData = [],
72
+ } = {},
73
+ } = data || {};
70
74
 
71
75
  // Handle video/GIF content from expandableDetails.media array (new format)
72
76
  let bodyVideo = {};
73
77
  let bodyGif = '';
74
78
  let bodyImage = image;
75
79
 
76
- if (media && Array.isArray(media) && media.length > 0) {
80
+ if (media?.length > 0) {
77
81
  const mediaItem = media[0];
78
- if (mediaItem.type === VIDEO) {
82
+ const { type, url } = mediaItem;
83
+ if (type === VIDEO) {
79
84
  // Distinguish between actual video and GIF based on URL extension
80
- if (mediaItem.url && mediaItem.url.toLowerCase().includes('.gif')) {
81
- bodyGif = mediaItem.url;
85
+ if (url && url?.toLowerCase()?.includes('.gif')) {
86
+ bodyGif = url;
82
87
  } else {
83
88
  bodyVideo = {
84
- videoSrc: mediaItem.url,
85
- videoPreview: mediaItem.url,
89
+ videoSrc: url,
90
+ videoPreview: url,
86
91
  };
87
92
  }
88
93
  // Clear bodyImage when we have video/GIF content
@@ -100,12 +105,20 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
100
105
  appName: templateData?.appName,
101
106
  carouselData,
102
107
  };
103
- if (ctas && ctas?.length) {
104
- if (device === "android" ) {
105
- content.actions = map(ctas, (cta) => ({label: cta?.actionText}));
106
- } else {
107
- content.actions = [{label: ctas[0]?.actionLabel}, {label: ctas[0]?.actionLabel2}];
108
- }
108
+
109
+ // Map CTAs consistently for both Android and iOS
110
+ if (ctas.length > 0 && type !== CAROUSEL) {
111
+ content.actions = ctas.map((cta) => {
112
+ const {
113
+ actionText, type: Type, actionLink, deepLinkKeys,
114
+ } = cta || {};
115
+ return {
116
+ label: actionText || '',
117
+ type: Type || '',
118
+ url: actionLink || '',
119
+ deepLinkKeys: deepLinkKeys || [],
120
+ };
121
+ });
109
122
  }
110
123
  }
111
124
  return content;
@@ -601,60 +601,60 @@
601
601
  img{
602
602
  max-width: 100%;
603
603
  max-height: 90px;
604
- margin: auto;
604
+ margin-left: 1.571rem;
605
605
  display: block;
606
606
  }
607
607
  }
608
608
  }
609
609
  .mobile-push-carousel-card {
610
610
  position: relative;
611
- min-width: 258px;
612
- margin-right: 12px;
613
- background: #fff;
614
- color: #091e42;
615
- border-radius: 8px;
611
+ min-width: 16.125rem; // 258px
612
+ margin-right: 0.75rem; // 12px
613
+ background: $CAP_WHITE;
614
+ color: $CAP_G01; // #091e42
615
+ border-radius: 0.5rem; // 8px
616
616
  display: flex;
617
617
  flex-direction: column;
618
618
  align-items: flex-start;
619
619
  padding: 0;
620
- box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.08);
620
+ box-shadow: 0 0 0.625rem 0 rgba(0,0,0,0.08); // 10px
621
621
 
622
622
  .carousel-image {
623
623
  width: 100%;
624
- height: 180px;
624
+ height: 11.25rem; // 180px
625
625
  object-fit: contain;
626
- border-radius: 8px 8px 0 0;
626
+ border-radius: 0.5rem 0.5rem 0 0; // 8px
627
627
  }
628
628
 
629
629
  .carousel-video-preview {
630
630
  width: 100%;
631
- height: 180px;
631
+ height: 11.25rem; // 180px
632
632
  object-fit: contain;
633
- border-radius: 8px 8px 0 0;
633
+ border-radius: 0.5rem 0.5rem 0 0; // 8px
634
634
  opacity: 0.7;
635
635
  }
636
636
 
637
637
  .carousel-content {
638
- padding: 10px 12px;
638
+ padding: 0.625rem 0.75rem; // 10px 12px
639
639
  width: 100%;
640
640
 
641
641
  .carousel-title {
642
- font-weight: 600;
643
- font-size: 14px;
644
- margin-bottom: 4px;
642
+ font-weight: $FONT_WEIGHT_MEDIUM;
643
+ font-size: $FONT_SIZE_M; // 14px
644
+ margin-bottom: 0.25rem; // 4px
645
645
  }
646
646
 
647
647
  .carousel-message {
648
- font-size: 13px;
649
- color: #555;
648
+ font-size: $FONT_SIZE_S; // 13px is not in variables, using 12px
649
+ color: $CAP_G04; // #5e6c84 is closest to #555
650
650
  }
651
651
 
652
652
  .carousel-link {
653
- margin-top: 8px;
653
+ margin-top: 0.5rem; // 8px
654
654
 
655
655
  a {
656
- color: #2466ea;
657
- font-size: 12px;
656
+ color: $CAP_SECONDARY_base; // #2466ea
657
+ font-size: $FONT_SIZE_S; // 12px
658
658
  text-decoration: underline;
659
659
  }
660
660
  }
@@ -671,8 +671,8 @@
671
671
  font-weight: 600;
672
672
  padding: $CAP_SPACE_04 $CAP_SPACE_08 $CAP_SPACE_04 $CAP_SPACE_08;
673
673
  display: flex;
674
- align-items: center;
675
- justify-content: space-between;
674
+ // align-items: center;
675
+ // justify-content: space-between;
676
676
  .app-icon{
677
677
  width: $CAP_SPACE_12;
678
678
  height: $CAP_SPACE_12;
@@ -718,18 +718,21 @@
718
718
  .actions{
719
719
  background-color: #ffffff;
720
720
  height: auto;
721
- padding: $CAP_SPACE_08;
722
- text-align: center;
721
+ padding: 0.571rem;
722
+ padding-left: 1.571rem;
723
723
  display: flex;
724
- flex-direction: column;
725
- align-items: center;
726
- justify-content: center;
724
+ flex-direction: row;
727
725
  gap: $CAP_SPACE_08;
728
726
  .action{
729
727
  font-size: $FONT_SIZE_S;
730
728
  font-weight: 600;
731
729
  color: #1970DA;
732
730
  height: 1.25rem;
731
+ text-transform: uppercase;
732
+ cursor: pointer;
733
+ &:hover {
734
+ text-decoration: underline;
735
+ }
733
736
  }
734
737
  }
735
738
  }
@@ -806,8 +809,8 @@
806
809
  }
807
810
  // Mobile Push Carousel Styles
808
811
  .mobile-push-carousel-container {
809
- background-color: #fff;
810
- padding-left: 22px;
812
+ background-color: $CAP_WHITE;
813
+ padding-left: 1.375rem; // 22px
811
814
  }
812
815
  }
813
816
 
@@ -1078,4 +1081,103 @@
1078
1081
  }
1079
1082
  .line-image-preview{
1080
1083
  max-width: 8.57rem;;
1084
+ }
1085
+
1086
+ .MOBILEPUSH {
1087
+ .ant-card-body {
1088
+ padding: 0;
1089
+ background-color: $CAP_WHITE;
1090
+ border-top: 1px solid $CAP_COLOR_16;
1091
+ .ant-card-meta {
1092
+ background-color: $CAP_G09;
1093
+ .ant-card-meta-description {
1094
+ .mobilepush-container {
1095
+ background-color: $CAP_WHITE;
1096
+ padding: $CAP_SPACE_12;
1097
+ .app-header {
1098
+ color: $CAP_G04; // #5D5D5D is closest to CAP_G04
1099
+ font-weight: $FONT_WEIGHT_SEMIBOLD; // 600 maps to SEMIBOLD
1100
+ padding: 0.285rem 0.571rem 0.285rem 0.571rem;
1101
+ display: flex;
1102
+ align-items: center;
1103
+ justify-content: space-between;
1104
+ .app-header-left {
1105
+ display: flex;
1106
+ align-items: center;
1107
+ div {
1108
+ word-break: break-word;
1109
+ }
1110
+ .app-icon {
1111
+ width: 0.857rem;
1112
+ height: 0.857rem;
1113
+ border-radius: 50%;
1114
+ background-color: $CAP_G04; // #737070 is closest to CAP_G04
1115
+ }
1116
+ }
1117
+ }
1118
+ .mobilepush-message {
1119
+ padding-left: 1.65rem;
1120
+ }
1121
+ .mobilepush-image {
1122
+ width: 100%;
1123
+ height: 7.5rem; // 120px to rem
1124
+ margin-top: 0.625rem; // 10px to rem
1125
+ }
1126
+ .mobilepush-image-padding {
1127
+ padding-left: 1.65rem;
1128
+ }
1129
+ .scroll-container {
1130
+ overflow-x: auto;
1131
+ display: flex;
1132
+ padding-top: $CAP_SPACE_06;
1133
+ padding-right: $CAP_SPACE_06;
1134
+ white-space: nowrap;
1135
+ scrollbar-width: none; // Hide scrollbar in Firefox
1136
+ &::-webkit-scrollbar {
1137
+ display: none; // Hide scrollbar in Chrome/Safari/Opera
1138
+ }
1139
+ overflow: hidden;
1140
+ height: 100%;
1141
+ width: 100%;
1142
+ margin-left: 1.65rem;
1143
+ .whatsapp-carousel-container {
1144
+ padding: $CAP_SPACE_04 0 $CAP_SPACE_08;
1145
+ border-radius: $CAP_SPACE_06;
1146
+ background-color: $CAP_WHITE;
1147
+ width: 80%;
1148
+ flex-shrink: 0;
1149
+ white-space: pre-wrap;
1150
+ word-break: break-word;
1151
+ overflow: auto;
1152
+ text-align: left;
1153
+ margin: 0;
1154
+ .whatsapp-carousel-card {
1155
+ margin: $CAP_SPACE_02 $CAP_SPACE_02 $CAP_SPACE_01 $CAP_SPACE_02;
1156
+ .whatsapp-carousel-body {
1157
+ margin-bottom: $CAP_SPACE_08;
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ .actions {
1163
+ background-color: $CAP_WHITE;
1164
+ height: auto;
1165
+ padding: $CAP_SPACE_08;
1166
+ text-align: center;
1167
+ display: flex;
1168
+ flex-direction: column;
1169
+ align-items: center;
1170
+ justify-content: center;
1171
+ gap: $CAP_SPACE_08;
1172
+ .action {
1173
+ font-size: $FONT_SIZE_S;
1174
+ font-weight: $FONT_WEIGHT_SEMIBOLD; // 600 maps to SEMIBOLD
1175
+ color: $CAP_COLOR_03; // #1970DA maps to CAP_COLOR_03 (primary brand color)
1176
+ height: 1.25rem;
1177
+ }
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1081
1183
  }