@capillarytech/creatives-library 8.0.345-alpha.13 → 8.0.345-alpha.15

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 (138) hide show
  1. package/constants/unified.js +29 -0
  2. package/package.json +1 -1
  3. package/services/api.js +0 -20
  4. package/services/tests/api.test.js +13 -59
  5. package/utils/commonUtils.js +19 -1
  6. package/utils/rcsPayloadUtils.js +92 -0
  7. package/utils/templateVarUtils.js +201 -0
  8. package/utils/tests/templateVarUtils.test.js +204 -0
  9. package/v2Components/CapActionButton/constants.js +7 -0
  10. package/v2Components/CapActionButton/index.js +167 -109
  11. package/v2Components/CapActionButton/index.scss +157 -6
  12. package/v2Components/CapActionButton/messages.js +19 -3
  13. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  14. package/v2Components/CapCustomSkeleton/index.js +1 -1
  15. package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
  16. package/v2Components/CapTagList/index.js +10 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  29. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  30. package/v2Components/CommonTestAndPreview/constants.js +38 -2
  31. package/v2Components/CommonTestAndPreview/index.js +676 -186
  32. package/v2Components/CommonTestAndPreview/messages.js +49 -3
  33. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  34. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  46. package/v2Components/FormBuilder/index.js +8 -10
  47. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  48. package/v2Components/SmsFallback/constants.js +73 -0
  49. package/v2Components/SmsFallback/index.js +955 -0
  50. package/v2Components/SmsFallback/index.scss +265 -0
  51. package/v2Components/SmsFallback/messages.js +78 -0
  52. package/v2Components/SmsFallback/smsFallbackUtils.js +118 -0
  53. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  54. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  55. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
  58. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  59. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  60. package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
  61. package/v2Components/TemplatePreview/constants.js +2 -0
  62. package/v2Components/TemplatePreview/index.js +143 -28
  63. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  64. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  65. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  66. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  67. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  68. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  69. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  70. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  71. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  72. package/v2Containers/CreativesContainer/SlideBoxFooter.js +11 -4
  73. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  74. package/v2Containers/CreativesContainer/constants.js +9 -0
  75. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  76. package/v2Containers/CreativesContainer/index.js +300 -108
  77. package/v2Containers/CreativesContainer/index.scss +51 -1
  78. package/v2Containers/CreativesContainer/messages.js +0 -4
  79. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  80. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
  81. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  82. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  83. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  84. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -18
  85. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  86. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  87. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  88. package/v2Containers/Rcs/constants.js +119 -8
  89. package/v2Containers/Rcs/index.js +2379 -807
  90. package/v2Containers/Rcs/index.js.rej +1336 -0
  91. package/v2Containers/Rcs/index.scss +276 -6
  92. package/v2Containers/Rcs/index.scss.rej +74 -0
  93. package/v2Containers/Rcs/messages.js +38 -3
  94. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  95. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
  96. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  97. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
  98. package/v2Containers/Rcs/tests/index.test.js +152 -121
  99. package/v2Containers/Rcs/tests/mockData.js +38 -0
  100. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  101. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  102. package/v2Containers/Rcs/utils.js +478 -11
  103. package/v2Containers/Sms/Create/index.js +100 -40
  104. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  105. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  106. package/v2Containers/SmsTrai/Create/index.js +9 -4
  107. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  108. package/v2Containers/SmsTrai/Edit/index.js +636 -130
  109. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  110. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  111. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  112. package/v2Containers/SmsWrapper/index.js +37 -8
  113. package/v2Containers/TagList/index.js +6 -0
  114. package/v2Containers/Templates/ChannelTypeIllustration.js +6 -23
  115. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  116. package/v2Containers/Templates/_templates.scss +181 -126
  117. package/v2Containers/Templates/actions.js +11 -36
  118. package/v2Containers/Templates/constants.js +2 -23
  119. package/v2Containers/Templates/index.js +142 -333
  120. package/v2Containers/Templates/messages.js +0 -68
  121. package/v2Containers/Templates/reducer.js +0 -68
  122. package/v2Containers/Templates/sagas.js +55 -98
  123. package/v2Containers/Templates/selectors.js +0 -12
  124. package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -12
  125. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  126. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1042 -1256
  127. package/v2Containers/Templates/tests/index.test.js +0 -6
  128. package/v2Containers/Templates/tests/reducer.test.js +0 -178
  129. package/v2Containers/Templates/tests/sagas.test.js +200 -436
  130. package/v2Containers/Templates/tests/selector.test.js +0 -32
  131. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  132. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  133. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  134. package/v2Containers/TemplatesV2/index.js +86 -23
  135. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  136. package/v2Containers/Whatsapp/index.js +3 -20
  137. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
  138. package/v2Containers/Assets/images/archive_Empty_Illustration.svg +0 -9
@@ -0,0 +1,1336 @@
1
+ diff a/app/v2Containers/Rcs/index.js b/app/v2Containers/Rcs/index.js (rejected hunks)
2
+ @@ -1,12 +1,14 @@
3
+ /* eslint-disable no-unused-expressions */
4
+ import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
5
+ +import { flushSync } from 'react-dom';
6
+ import { bindActionCreators } from 'redux';
7
+ import { createStructuredSelector } from 'reselect';
8
+ +import { injectIntl, FormattedMessage } from 'react-intl';
9
+ import get from 'lodash/get';
10
+ import isEmpty from 'lodash/isEmpty';
11
+ import cloneDeep from 'lodash/cloneDeep';
12
+ import isNil from 'lodash/isNil';
13
+ +import styled from 'styled-components';
14
+ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
15
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
16
+ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
17
+ @@ -106,18 +131,37 @@ import UnifiedPreview from '../../v2Components/CommonTestAndPreview/UnifiedPrevi
18
+ import VarSegmentMessageEditor from '../../v2Components/VarSegmentMessageEditor';
19
+ import { ANDROID, RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
20
+ import TestAndPreviewSlidebox from '../../v2Components/TestAndPreviewSlidebox';
21
+ +import SmsFallback from '../../v2Components/SmsFallback';
22
+ +import { CHANNELS_TO_HIDE_FOR_SMS_ONLY } from '../../v2Components/SmsFallback/constants';
23
+ import CapImageUpload from '../../v2Components/CapImageUpload';
24
+ +import addCreativesIcon from '../Assets/images/addCreativesIllustration.svg';
25
+ import Templates from '../Templates';
26
+ import SmsTraiEdit from '../SmsTrai/Edit';
27
+ import TagList from '../TagList';
28
+ import { validateTags } from '../../utils/tagValidations';
29
+ +import { getCdnUrl } from '../../utils/cdnTransformation';
30
+ import { isTagIncluded } from '../../utils/commonUtils';
31
+ +import { splitTemplateVarString as splitTemplateVarStringUtil } from '../../utils/templateVarUtils';
32
+ +import { isTraiDLTEnable } from '../../utils/common';
33
+ +import { isUrl, isValidText } from '../Line/Container/Wrapper/utils';
34
+ +import {
35
+ + invalidVarRegex,
36
+ + RCS_CTA_URL_TYPE,
37
+ + URL_MAX_LENGTH,
38
+ +} from '../../v2Components/CapActionButton/constants';
39
+ import injectReducer from '../../utils/injectReducer';
40
+ import v2RcsReducer from './reducer';
41
+ +import {
42
+ + normalizeLibraryLoadedTitleDesc,
43
+ + mergeRcsSmsFallBackContentFromDetails,
44
+ + mergeRcsSmsFallbackVarMapLayers,
45
+ + extractRegisteredSenderIdsFromSmsFallbackRecord,
46
+ + pickFirstSmsFallbackTemplateString,
47
+ + syncCardVarMappedSemanticsFromSlots,
48
+ + hasMeaningfulSmsFallbackShape,
49
+ + getLibrarySmsFallbackApiBaselineFromTemplateData,
50
+ + pickRcsCardVarMappedEntries,
51
+ +} from './rcsLibraryHydrationUtils';
52
+ import {
53
+ areAllRcsSmsFallbackVarSlotsFilled,
54
+ buildRcsNumericMustachePlaceholderRegex,
55
+ @@ -142,15 +188,17 @@ export const Rcs = (props) => {
56
+ templatesActions,
57
+ globalActions,
58
+ location,
59
+ + handleClose,
60
+ getDefaultTags,
61
+ supportedTags,
62
+ metaEntities,
63
+ injectedTags,
64
+ loadingTags,
65
+ getFormData,
66
+ + isDltEnabled,
67
+ smsRegister,
68
+ selectedOfferDetails,
69
+ + currentOrgDetails,
70
+ eventContextTags,
71
+ accountData = {},
72
+ // TestAndPreviewSlidebox props
73
+ @@ -170,71 +236,189 @@ export const Rcs = (props) => {
74
+ const [templateMediaType, setTemplateMediaType] = useState(
75
+ RCS_MEDIA_TYPES.NONE,
76
+ );
77
+ + const [templateRejectionReason, setTemplateRejectionReason] = useState(null);
78
+ const [templateTitle, setTemplateTitle] = useState('');
79
+ const [templateDesc, setTemplateDesc] = useState('');
80
+ const [templateDescError, setTemplateDescError] = useState(false);
81
+ const [templateStatus, setTemplateStatus] = useState('');
82
+ + const [templateDate, setTemplateDate] = useState('');
83
+ + //fallback
84
+ + const [fallbackMessage, setFallbackMessage] = useState('');
85
+ + const [fallbackMessageError, setFallbackMessageError] = useState(false);
86
+ //fallback dlt
87
+ const [showDltContainer, setShowDltContainer] = useState(false);
88
+ const [dltMode, setDltMode] = useState('');
89
+ const [dltEditData, setDltEditData] = useState({});
90
+ + const [showDltCard, setShowDltCard] = useState(false);
91
+ + const [fallbackPreviewmode, setFallbackPreviewmode] = useState(false);
92
+ + const [dltPreviewData, setDltPreviewData] = useState('');
93
+ + const [buttonType, setButtonType] = useState(RCS_BUTTON_TYPES.NONE);
94
+ const [suggestionError, setSuggestionError] = useState(true);
95
+ const [templateType, setTemplateType] = useState('text_message');
96
+ + const [templateHeader, setTemplateHeader] = useState('');
97
+ + const [templateMessage, setTemplateMessage] = useState('');
98
+ + const [templateHeaderError, setTemplateHeaderError] = useState('');
99
+ + const [templateMessageError, setTemplateMessageError] = useState('');
100
+ + const validVarRegex = /\{\{(\d+)\}\}/g;
101
+ + const [updatedTitleData, setUpdatedTitleData] = useState([]);
102
+ + const [updatedDescData, setUpdatedDescData] = useState([]);
103
+ const [titleVarMappedData, setTitleVarMappedData] = useState({});
104
+ const [descVarMappedData, setDescVarMappedData] = useState({});
105
+ const [titleTextAreaId, setTitleTextAreaId] = useState();
106
+ const [descTextAreaId, setDescTextAreaId] = useState();
107
+ const [assetList, setAssetList] = useState({});
108
+ + const [assetListImage, setAssetListImage] = useState('');
109
+ const [rcsImageSrc, updateRcsImageSrc] = useState('');
110
+ const [rcsVideoSrc, setRcsVideoSrc] = useState({});
111
+ const [rcsThumbnailSrc, setRcsThumbnailSrc] = useState('');
112
+ const [selectedDimension, setSelectedDimension] = useState(RCS_IMAGE_DIMENSIONS.MEDIUM_HEIGHT.type);
113
+ + // Carousel (UI-only) state
114
+ + const [selectedCarousel, setSelectedCarousel] = useState('');
115
+ + const [selectedCarouselHeight, setSelectedCarouselHeight] = useState(MEDIUM);
116
+ + const [selectedCarouselWidth, setSelectedCarouselWidth] = useState(SMALL);
117
+ + const [carouselData, setCarouselData] = useState([]);
118
+ + const [carouselErrors, setCarouselErrors] = useState([]); // [{ title: string|false, description: string|false }]
119
+ + const [activeCarouselIndex, setActiveCarouselIndex] = useState('0');
120
+ + const [carouselResetNonce, setCarouselResetNonce] = useState(0);
121
+ + const [carouselFocusedVarId, setCarouselFocusedVarId] = useState('');
122
+ + const [imageError, setImageError] = useState(null);
123
+ const [templateTitleError, setTemplateTitleError] = useState(false);
124
+ const [cardVarMapped, setCardVarMapped] = useState({});
125
+ + /** `undefined` = not hydrated yet; `null` = no fallback / user removed template; object = selected fallback */
126
+ + const [smsFallbackData, setSmsFallbackData] = useState(undefined);
127
+ /** Bump when hydrated cardVarMapped payload changes so VarSegment TextAreas remount with fresh valueMap (controlled-input sync). */
128
+ const [rcsVarSegmentEditorRemountKey, setRcsVarSegmentEditorRemountKey] = useState(0);
129
+ const lastHydratedRcsCardVarSignatureRef = useRef(null);
130
+ + /**
131
+ + * Library: parent often passes a new `templateData` object reference every render. Re-applying the same
132
+ + * SMS fallback snapshot was resetting `smsFallbackData` and caused VarSegment inputs to flicker old/new.
133
+ + */
134
+ + const lastSmsFallbackHydrationKeyRef = useRef(null);
135
+ + /** Skip duplicate /meta/TAG fetches: same query is triggered from multiple mount points. */
136
+ + const lastTagSchemaQueryKeyRef = useRef(null);
137
+
138
+ /**
139
+ * Hydrate only from template payload — not from full `rcsData`. Uploads/asset updates change `rcsData`
140
+ + * without changing `templateDetails`; re-running hydration then cleared `smsFallbackData`.
141
+ */
142
+ const rcsHydrationDetails = useMemo(
143
+ () => (isFullMode ? rcsData?.templateDetails : templateData),
144
+ [isFullMode, rcsData?.templateDetails, templateData],
145
+ );
146
+
147
+ + // TestAndPreviewSlidebox state
148
+ + const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
149
+ + const [isTestAndPreviewMode, setIsTestAndPreviewMode] = useState(false);
150
+
151
+ + // Suggestions state (must be declared before any callbacks that reference it)
152
+ + const [suggestions, setSuggestions] = useState([]);
153
+ +
154
+ + const tempMsg = dltPreviewData === '' ? fallbackMessage : dltPreviewData;
155
+ +
156
+ + // Carousel: reuse existing resolution logic (resolveTemplateWithMap) per card.
157
+ + // This keeps carousel behavior consistent with rich-card/text-message in !isFullMode
158
+ + // and ensures shared variables (eg {{name}} across multiple cards) stay in sync.
159
+ + const buildCarouselCardsForPreview = (cards = []) => (cards || []).map((card = {}) => {
160
+ + const videoThumb = card.thumbnailSrc || card.videoAsset?.videoThumbnail || '';
161
+ + const videoSrc = card.videoAsset?.videoSrc || '';
162
+ + const resolvedTitle = !isFullMode ? resolveTemplateWithMap(card.title || '') : (card.title || '');
163
+ + const resolvedDesc = !isFullMode ? resolveTemplateWithMap(card.description || '') : (card.description || '');
164
+ + return {
165
+ + mediaType: (card.mediaType || '').toLowerCase(),
166
+ + imageSrc: card.imageSrc || '',
167
+ + videoSrc,
168
+ + videoPreviewImg: videoThumb,
169
+ + title: resolvedTitle,
170
+ + bodyText: resolvedDesc,
171
+ + suggestions: card.suggestions || [],
172
+ + };
173
+ + });
174
+ +
175
+ + // Get template content for TestAndPreviewSlidebox
176
+ + // Reference: Based on getRcsPreview() function (lines 2087-2111) which prepares content for TemplatePreview
177
+ + // getRcsPreview ALWAYS uses templateTitle and templateDesc for ALL template types (text_message, rich_card, carousel)
178
+ + // renderTextComponent (lines 1317-1485) also uses templateTitle and templateDesc
179
+ + // Note: templateHeader and templateMessage are defined but NOT used in the component
180
+ + const getTemplateContent = useCallback(() => {
181
+ + if (templateType === contentType.carousel) {
182
+ + const carouselDimKey = getCarouselDimensionKey();
183
+ + const carouselImgDims =
184
+ + RCS_CAROUSEL_IMAGE_DIMENSIONS[carouselDimKey] || RCS_CAROUSEL_IMAGE_DIMENSIONS.MEDIUM_MEDIUM;
185
+ + const carouselVidDims =
186
+ + RCS_CAROUSEL_VIDEO_THUMBNAIL_DIMENSIONS[carouselDimKey]
187
+ + || RCS_CAROUSEL_VIDEO_THUMBNAIL_DIMENSIONS.MEDIUM_MEDIUM;
188
+ + return {
189
+ + carouselData: buildCarouselCardsForPreview(carouselData),
190
+ + carouselPreviewDimensions: {
191
+ + imageWidth: carouselImgDims.width,
192
+ + imageHeight: carouselImgDims.height,
193
+ + videoThumbWidth: carouselVidDims.width,
194
+ + videoThumbHeight: carouselVidDims.height,
195
+ + },
196
+ + };
197
+ + }
198
+ +
199
+ + const isMediaTypeImage = templateMediaType === RCS_MEDIA_TYPES.IMAGE;
200
+ + const isMediaTypeVideo = templateMediaType === RCS_MEDIA_TYPES.VIDEO;
201
+ + const isMediaTypeText = templateMediaType === RCS_MEDIA_TYPES.NONE;
202
+ +
203
+ + // Build media preview object (same pattern as getRcsPreview)
204
+ + const mediaPreview = {};
205
+ + if (isMediaTypeImage && rcsImageSrc) {
206
+ + mediaPreview.rcsImageSrc = rcsImageSrc;
207
+ + }
208
+ + if (isMediaTypeVideo && !isMediaTypeText) {
209
+ + // For video, use thumbnailSrc as rcsVideoSrc (same as getRcsPreview line 2104)
210
+ + if (rcsThumbnailSrc) {
211
+ + mediaPreview.rcsVideoSrc = rcsThumbnailSrc;
212
+ + } else if (rcsVideoSrc?.videoSrc) {
213
+ + mediaPreview.rcsVideoSrc = rcsVideoSrc.videoSrc;
214
+ }
215
+ + // Also include thumbnailSrc separately if available
216
+ + if (rcsThumbnailSrc) {
217
+ + mediaPreview.rcsThumbnailSrc = rcsThumbnailSrc;
218
+ + }
219
+ + }
220
+
221
+ + // Build content object
222
+ + // Reference: getRcsPreview (line 2091-2092) uses templateTitle and templateDesc for ALL cases
223
+ + // templateTitle is used for rich_card/carousel title, empty for text_message
224
+ + // templateDesc is used for ALL types (text message body or rich card description)
225
+ + // For UnifiedPreview, we map templateTitle -> templateHeader and templateDesc -> templateMessage
226
+ + const contentObj = {
227
+ + // Map templateTitle to templateHeader and templateDesc to templateMessage
228
+ + templateHeader: templateTitle,
229
+ + templateMessage: templateDesc,
230
+ + ...mediaPreview,
231
+ + ...(suggestions.length > 0 && {
232
+ + suggestions: suggestions,
233
+ + }),
234
+ + };
235
+ +
236
+ + return contentObj;
237
+ + }, [
238
+ + templateType,
239
+ + templateMediaType,
240
+ + templateTitle,
241
+ + templateDesc,
242
+ + rcsImageSrc,
243
+ + rcsVideoSrc,
244
+ + rcsThumbnailSrc,
245
+ + suggestions,
246
+ + selectedDimension,
247
+ + carouselData,
248
+ + selectedCarouselHeight,
249
+ + selectedCarouselWidth,
250
+ + // IMPORTANT: carousel preview content resolves variables via cardVarMapped.
251
+ + // Without this dependency, Test&Preview slidebox content can get stale when label values change.
252
+ + cardVarMapped,
253
+ + isFullMode,
254
+ + ]);
255
+
256
+ // Handle Test and Preview button click
257
+ const handleTestAndPreview = useCallback(() => {
258
+ setShowTestAndPreviewSlidebox(true);
259
+ + setIsTestAndPreviewMode(true);
260
+ if (propsHandleTestAndPreview) {
261
+ propsHandleTestAndPreview();
262
+ }
263
+ @@ -446,156 +1393,169 @@ export const Rcs = (props) => {
264
+ }).join('');
265
+ };
266
+
267
+ -
268
+ -
269
+ + /** Build map of segment-id → resolved value for VarSegmentMessageEditor. */
270
+ + const getRcsValueMap = (fieldTemplateString, fieldType) => {
271
+ + if (!fieldTemplateString) return {};
272
+ + const titleVarTokenMatches = templateTitle?.match(rcsVarRegex) ?? [];
273
+ + const slotOffset = fieldType === TITLE_TEXT ? 0 : titleVarTokenMatches.length;
274
+ + const templateSegments = splitTemplateVarStringRcs(fieldTemplateString);
275
+ + const segmentIdToResolvedValue = {};
276
+ + let varOrdinal = 0;
277
+ + templateSegments.forEach((segmentToken, segmentIndexInField) => {
278
+ + if (rcsVarTestRegex.test(segmentToken)) {
279
+ + const varSegmentCompositeId = `${segmentToken}_${segmentIndexInField}`;
280
+ + const varName = getVarNameFromToken(segmentToken);
281
+ + const globalSlot = slotOffset + varOrdinal;
282
+ + varOrdinal += 1;
283
+ + segmentIdToResolvedValue[varSegmentCompositeId] = resolveCardVarMappedSlotValue(
284
+ + cardVarMapped,
285
+ + varName,
286
+ + globalSlot,
287
+ + isEditLike,
288
+ + rcsSpanningSemanticVarNames.has(varName),
289
+ + );
290
+ }
291
+ -
292
+ -
293
+ -
294
+ + });
295
+ + return segmentIdToResolvedValue;
296
+ + };
297
+
298
+ + const titleVarSegmentValueMapById = useMemo(
299
+ + () => getRcsValueMap(templateTitle, TITLE_TEXT),
300
+ + [templateTitle, cardVarMapped, isEditFlow, isFullMode, rcsSpanningSemanticVarNames],
301
+ + );
302
+ + const descriptionVarSegmentValueMapById = useMemo(
303
+ + () => getRcsValueMap(templateDesc, MESSAGE_TEXT),
304
+ + [templateDesc, templateTitle, cardVarMapped, isEditFlow, isFullMode, rcsSpanningSemanticVarNames],
305
+ + );
306
+
307
+ + const handleRcsVarChange = (varSegmentCompositeDomId, value, type) => {
308
+ + const underscoreIndexInCompositeId = varSegmentCompositeDomId.lastIndexOf('_');
309
+ + if (underscoreIndexInCompositeId === -1) return;
310
+ + const mustacheTokenFromCompositeId = varSegmentCompositeDomId.slice(0, underscoreIndexInCompositeId);
311
+ + const variableName = getVarNameFromToken(mustacheTokenFromCompositeId);
312
+ + if (variableName === undefined || variableName === null || variableName === '') return;
313
+ + const isInvalidValue = value?.trim() === '';
314
+ + const coercedSlotValue = isInvalidValue ? '' : value;
315
+ + const templateStringForField = type === TITLE_TEXT ? templateTitle : templateDesc;
316
+ + const globalVarSlotIndexZeroBased = getGlobalSlotIndexForRcsFieldId(
317
+ + varSegmentCompositeDomId,
318
+ + templateStringForField,
319
+ + type,
320
+ + );
321
+ + setCardVarMapped((previousCardVarMapped) => {
322
+ + const updatedCardVarMapped = { ...previousCardVarMapped };
323
+ + // Remove stale semantic key to avoid cross-field bleed for shared variable names.
324
+ + delete updatedCardVarMapped[variableName];
325
+ + if (globalVarSlotIndexZeroBased !== null && globalVarSlotIndexZeroBased !== undefined) {
326
+ + updatedCardVarMapped[String(globalVarSlotIndexZeroBased + 1)] = coercedSlotValue;
327
+ + }
328
+ + return updatedCardVarMapped;
329
+ + });
330
+ + };
331
+
332
+
333
+ + useEffect(() => {
334
+ + if (isFullMode || isEditFlow) return;
335
+ + const tokens = [
336
+ + ...(templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []),
337
+ + ...(templateDesc ? (templateDesc.match(rcsVarRegex) || []) : []),
338
+ + ];
339
+ + if (!tokens.length) return;
340
+ + setCardVarMapped((prev) => {
341
+ + const next = { ...(prev || {}) };
342
+ + let changed = false;
343
+ + tokens.forEach((t) => {
344
+ + const name = getVarNameFromToken(t);
345
+ + if (name && !Object.prototype.hasOwnProperty.call(next, name)) {
346
+ + next[name] = '';
347
+ + changed = true;
348
+ }
349
+ });
350
+ + return changed ? next : prev;
351
+ + });
352
+ + }, [isFullMode, templateTitle, templateDesc]);
353
+
354
+
355
+ + const RcsLabel = styled.div`
356
+ + display: flex;
357
+ + margin-top: 20px;
358
+ + `;
359
+ + const paramObj = params || {};
360
+ useEffect(() => {
361
+ + const { id } = paramObj;
362
+ + if (id && isFullMode) {
363
+ + setSpin(true);
364
+ + actions.getTemplateDetails(id, setSpin);
365
+ + }
366
+ + return () => {
367
+ + actions.clearEditResponse();
368
+ + };
369
+ + }, [paramObj.id]);
370
+ +
371
+ + useEffect(() => {
372
+ + if (!(isEditFlow || !isFullMode)) return;
373
+ +
374
+ + const initField = (targetString, currentVarMap, setVarMap, setUpdated) => {
375
+ + const arr = splitTemplateVarString(targetString);
376
+ + if (!arr?.length) {
377
+ + setVarMap({});
378
+ + setUpdated([]);
379
+ + return;
380
+ + }
381
+ + const nextVarMap = {};
382
+ + const nextUpdated = [...arr];
383
+ + arr.forEach((elem, idx) => {
384
+ + // RCS placeholders are alphanumeric/underscore (e.g. {{user_name}}), not numeric-only
385
+ + if (rcsVarTestRegex.test(elem)) {
386
+ + const id = `${elem}_${idx}`;
387
+ + const varName = getVarNameFromToken(elem);
388
+ + const mappedValue = (cardVarMapped?.[varName] ?? '').toString();
389
+ + nextVarMap[id] = mappedValue;
390
+ + if (mappedValue !== '') {
391
+ + nextUpdated[idx] = mappedValue;
392
+ + } else {
393
+ + nextUpdated[idx] = elem;
394
+ + }
395
+ + }
396
+ + });
397
+ + setVarMap(nextVarMap);
398
+ + setUpdated(nextUpdated);
399
+ + };
400
+ +
401
+ + initField(templateTitle, titleVarMappedData, setTitleVarMappedData, setUpdatedTitleData);
402
+ + initField(templateDesc, descVarMappedData, setDescVarMappedData, setUpdatedDescData);
403
+ + }, [templateTitle, templateDesc, cardVarMapped, isEditFlow, isFullMode]);
404
+ +
405
+ + useEffect(() => {
406
+ + if(!isEditFlow && isFullMode){
407
+ setRcsVideoSrc({});
408
+ updateRcsImageSrc('');
409
+ setUpdateRcsImageSrc('');
410
+ updateRcsThumbnailSrc('');
411
+ setAssetList({});
412
+ + }
413
+ + }, [templateMediaType]);
414
+
415
+ + const templateStatusHelper = (details) => {
416
+ + const status =
417
+ + get(details, 'versions.base.content.RCS.rcsContent.Status') ||
418
+ + get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
419
+ + switch (status) {
420
+ + case RCS_STATUSES.approved:
421
+ setTemplateStatus(RCS_STATUSES.approved);
422
+ break;
423
+ + case RCS_STATUSES.pending:
424
+ setTemplateStatus(RCS_STATUSES.pending);
425
+ break;
426
+ + case RCS_STATUSES.awaitingApproval:
427
+ setTemplateStatus(RCS_STATUSES.awaitingApproval);
428
+ break;
429
+ + case RCS_STATUSES.unavailable:
430
+ setTemplateStatus(RCS_STATUSES.unavailable);
431
+ break;
432
+ + case RCS_STATUSES.rejected:
433
+ setTemplateStatus(RCS_STATUSES.rejected);
434
+ break;
435
+ default:
436
+ setTemplateStatus(status);
437
+ break;
438
+ @@ -648,210 +1609,112 @@ export const Rcs = (props) => {
439
+ };
440
+
441
+ useEffect(() => {
442
+ + const details = isFullMode ? rcsData?.templateDetails : templateData;
443
+ if (details && Object.keys(details).length > 0) {
444
+ + const rcsContent = get(details, 'versions.base.content.RCS.rcsContent', {});
445
+ + const cardType = (rcsContent?.cardType || '').toString().toLowerCase();
446
+ + if (!isFullMode) {
447
+ + const tempCardVarMapped = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped', {});
448
+ + setCardVarMapped(tempCardVarMapped);
449
+ }
450
+ + setEditFlow(true);
451
+ + setTemplateName(details.name || '');
452
+ +
453
+ + // Carousel edit-mode hydration
454
+ + if (cardType === contentType.carousel) {
455
+ +
456
+ + setTemplateType(contentType.carousel);
457
+ + setTemplateMediaType(RCS_MEDIA_TYPES.NONE);
458
+ + const cardSettings = rcsContent?.cardSettings || {};
459
+ + const cardWidth = cardSettings?.cardWidth || SMALL;
460
+ + setSelectedCarouselWidth(cardWidth);
461
+ +
462
+ + const cards = Array.isArray(rcsContent?.cardContent) ? rcsContent.cardContent : [];
463
+ + const firstHeight = cards?.[0]?.media?.height || MEDIUM;
464
+ + setSelectedCarouselHeight(firstHeight);
465
+ + setSelectedCarousel(`${firstHeight}_${cardWidth}`);
466
+ + setActiveCarouselIndex('0');
467
+ +
468
+ + const hydratedCards = cards.map((c = {}, idx) => {
469
+ + const mediaType = c.mediaType;
470
+ + const media = c.media || {};
471
+ + const mediaUrl = media.mediaUrl || '';
472
+ + const thumbUrl = media.thumbnailUrl || '';
473
+ + const rawSuggestions = Array.isArray(c.suggestions) ? c.suggestions : [];
474
+ + const suggestions = idx === 0 && rawSuggestions.length === 0
475
+ + ? cloneDeep(RCS_CAROUSEL_FIRST_CARD_DEFAULT_SUGGESTIONS)
476
+ + : rawSuggestions;
477
+ + return {
478
+ + title: c.title || '',
479
+ + description: c.description || '',
480
+ + mediaType,
481
+ + imageSrc: mediaType === RCS_MEDIA_TYPES.IMAGE ? mediaUrl : '',
482
+ + videoAsset: mediaType === RCS_MEDIA_TYPES.VIDEO ? {
483
+ + videoSrc: mediaUrl,
484
+ + previewUrl: thumbUrl,
485
+ + videoThumbnail: thumbUrl,
486
+ + videoName: c?.media?.videoName || '',
487
+ + } : {},
488
+ + thumbnailSrc: mediaType === RCS_MEDIA_TYPES.VIDEO ? thumbUrl : '',
489
+ + suggestions,
490
+ + };
491
+ + });
492
+ + setCarouselData(
493
+ + hydratedCards.length > 0
494
+ + ? ensureFirstCardDefaultPhoneSuggestions(hydratedCards)
495
+ + : [cloneDeep(RCS_CAROUSEL_INITIAL_FIRST_CARD)],
496
+ + );
497
+ + setCarouselErrors(new Array(hydratedCards.length > 0 ? hydratedCards.length : 1).fill({}));
498
+ +
499
+ + // Status bar uses first card's Status, keep existing behavior.
500
+ + if (isHostInfoBip) {
501
+ + setTemplateStatus('');
502
+ + } else {
503
+ + templateStatusHelper(details);
504
+ }
505
+ + return;
506
+ + }
507
+ +
508
+ + // Standalone edit-mode hydration (existing behavior)
509
+ + const mediaType = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].mediaType', '');
510
+ + if (cardType !== contentType.carousel && mediaType === RCS_MEDIA_TYPES.NONE) {
511
+ setTemplateType(contentType.text_message);
512
+ + } else if (cardType !== contentType.carousel && mediaType !== RCS_MEDIA_TYPES.NONE) {
513
+ setTemplateType(contentType.rich_card);
514
+ }
515
+ + const loadedTitle = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].title', '');
516
+ + const loadedDesc = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].description', '');
517
+ + const loadedMap = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped', {});
518
+ + const normalizedTitle = (!isFullMode && !isEmpty(loadedMap)) ? getUnmappedDesc(loadedTitle, loadedMap) : loadedTitle;
519
+ + const normalizedDesc = (!isFullMode && !isEmpty(loadedMap)) ? getUnmappedDesc(loadedDesc, loadedMap) : loadedDesc;
520
+ setTemplateTitle(normalizedTitle);
521
+ setTemplateDesc(normalizedDesc);
522
+ + setSuggestions(get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].suggestions', []));
523
+ if (isHostInfoBip) {
524
+ + // Infobip doesn't use templateStatusHelper; clear to prevent stale status when switching hosts
525
+ setTemplateStatus('');
526
+ } else {
527
+ + templateStatusHelper(details);
528
+ }
529
+ + const mediaData = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].media', '');
530
+ + const cardSettings = get(details, 'versions.base.content.RCS.rcsContent.cardSettings', '');
531
+ setMediaData(mediaData, mediaType, cardSettings);
532
+ -
533
+ }
534
+ + // NOTE: isEditFlow is intentionally excluded from deps – it is SET inside this
535
+ + // effect (setEditFlow(true)), so including it would cause a second hydration
536
+ + // run that overrides the selectedDimension reset done by the templateType effect.
537
+ + // eslint-disable-next-line react-hooks/exhaustive-deps
538
+ + }, [rcsData, templateData, isFullMode, isHostInfoBip]);
539
+ +
540
+
541
+ useEffect(() => {
542
+ if (templateType === contentType.text_message) {
543
+ setTemplateMediaType(RCS_MEDIA_TYPES.NONE);
544
+ + setTemplateTitle('');
545
+ + setTemplateTitleError('');
546
+ if (!isEditFlow && isFullMode) {
547
+ setUpdateRcsImageSrc('');
548
+ + setUpdateRcsVideoSrc({});
549
+ setRcsVideoSrc({});
550
+ setSelectedDimension(RCS_IMAGE_DIMENSIONS.MEDIUM_HEIGHT.type);
551
+ }
552
+ @@ -1028,14 +1908,15 @@ export const Rcs = (props) => {
553
+ };
554
+ // tag Code end
555
+
556
+ + const renderLabel = (value, showLabel, desc) => {
557
+ + const isTemplateApproved = (templateStatus === RCS_STATUSES.approved);
558
+ return (
559
+ <>
560
+ + <RcsLabel>
561
+ <CapHeading type="h4">{formatMessage(messages[value])}</CapHeading>
562
+ + </RcsLabel>
563
+ {desc && (
564
+ + <CapLabel type="label3" style={{ marginBottom: '17px' }}>
565
+ {formatMessage(messages[desc])}
566
+ </CapLabel>
567
+ )}
568
+ @@ -1165,43 +2101,53 @@ export const Rcs = (props) => {
569
+ if(!isFullMode){
570
+ return false;
571
+ }
572
+ + if (!/^\w+$/.test(paramName)) {
573
+ return formatMessage(messages.unknownCharactersError);
574
+ }
575
+ }
576
+ return false;
577
+ };
578
+ +
579
+ + const templateHeaderErrorHandler = (value) => {
580
+ + let errorMessage = false;
581
+ + if (value?.length > TEMPLATE_HEADER_MAX_LENGTH) {
582
+ + errorMessage = formatMessage(messages.templateHeaderLengthError);
583
+ + } else {
584
+ + errorMessage = variableErrorHandling(value);
585
+ + }
586
+ + return errorMessage;
587
+ + };
588
+ +
589
+ +
590
+ + const templateMessageErrorHandler = (value) => {
591
+ + let errorMessage = false;
592
+ + if (value === '') {
593
+ + errorMessage = formatMessage(messages.emptyTemplateMessageErrorMessage);
594
+ + } else if (
595
+ + value?.length
596
+ + > TEMPLATE_MESSAGE_MAX_LENGTH
597
+ + ) {
598
+ + errorMessage = formatMessage(messages.templateMessageLengthError);
599
+ + } else {
600
+ + errorMessage = variableErrorHandling(value);
601
+ + }
602
+ + return errorMessage;
603
+ + };
604
+ +
605
+
606
+ const onMessageAddVar = () => {
607
+ + onAddVar(MESSAGE_TEXT, templateDesc, rcsVarRegex);
608
+ };
609
+
610
+ +const onAddVar = (type, messageContent, regex) => {
611
+ + // Always append the next variable at the end, like WhatsApp
612
+ + const existingVars = messageContent.match(/\{\{(\d+)\}\}/g) || [];
613
+ + const existingNumbers = existingVars.map(v => parseInt(v.match(/\d+/)[0], 10));
614
+ let nextNumber = 1;
615
+ while (existingNumbers.includes(nextNumber)) {
616
+ nextNumber++;
617
+ }
618
+ -
619
+ + if (nextNumber > 19) {
620
+ return;
621
+ }
622
+ const nextVar = `{{${nextNumber}}}`;
623
+ @@ -1386,14 +2429,14 @@ const onTitleAddVar = () => {
624
+ value={templateTitle || ""}
625
+ size="default"
626
+ data-testid="template_title"
627
+ + errorMessage={normalizeErrorMessage(templateTitleError)}
628
+ disabled={isEditFlow || !isFullMode}
629
+ />
630
+ )}
631
+ +</CapRow>
632
+ {(isEditFlow || !isFullMode) && templateTitleError && (
633
+ <CapError className="rcs-template-title-error">
634
+ + {normalizeErrorMessage(templateTitleError)}
635
+ </CapError>
636
+ )}
637
+ {!isEditFlow && isFullMode && renderTitleCharacterCount()}
638
+ @@ -1488,15 +2519,13 @@ const onTitleAddVar = () => {
639
+ </>
640
+ )
641
+ }
642
+ + </CapRow>
643
+ {(isEditFlow || !isFullMode) && templateDescError && (
644
+ <CapError className="rcs-template-message-error">
645
+ + {normalizeErrorMessage(templateDescError)}
646
+ </CapError>
647
+ )}
648
+ + {!isEditFlow && isFullMode && renderDescriptionCharacterCount()}
649
+ {!isFullMode && hasTag() && (
650
+ <CapAlert
651
+ message={
652
+ @@ -1738,15 +2975,19 @@ const onTitleAddVar = () => {
653
+ }, []);
654
+
655
+
656
+ + const uploadRcsVideo = (file, type, fileParams, index = 0) => {
657
+ + setImageError(null);
658
+ actions.uploadRcsAsset(file, type, {
659
+ ...fileParams,
660
+ type: 'video',
661
+ channel: RCS,
662
+ isGenerateVideoThumbnail: false,
663
+ + }, index);
664
+ };
665
+
666
+ + const updateRcsVideoSrc = (val) => {
667
+ + setRcsVideoSrc(val);
668
+ + };
669
+ const setUpdateRcsVideoSrc = useCallback((index, val) => {
670
+ setRcsVideoSrc(val);
671
+ setAssetList(val);
672
+ @@ -1817,20 +3060,21 @@ const onTitleAddVar = () => {
673
+ value: dim.type,
674
+ label: `${dim.label}`
675
+ }))}
676
+ + style={{ marginBottom: '20px' }}
677
+ />
678
+ </>
679
+ )}
680
+ {(isEditFlow || !isFullMode) ? (
681
+ + <CapRow className="rcs-image-preview">
682
+ <CapImage
683
+ src={rcsImageSrc}
684
+ alt="RCS Image"
685
+ className="rcs-preview-image"
686
+ />
687
+ + </CapRow>
688
+ ) : (
689
+ <CapImageUpload
690
+ + style={{ paddingTop: '20px' }}
691
+ allowedExtensionsRegex={ALLOWED_IMAGE_EXTENSIONS_REGEX}
692
+ imgWidth={RCS_IMAGE_DIMENSIONS[selectedDimension].width}
693
+ imgHeight={RCS_IMAGE_DIMENSIONS[selectedDimension].height}
694
+ @@ -1871,16 +3115,16 @@ const onTitleAddVar = () => {
695
+ value: dim.type,
696
+ label: `${dim.label}`
697
+ }))}
698
+ + style={{ marginBottom: '20px' }}
699
+ />
700
+ )}
701
+ {(isEditFlow || !isFullMode) ? (
702
+ + <CapRow className="rcs-video-preview">
703
+ <CapImage
704
+ src={rcsVideoSrc.videoThumbnail || ''}
705
+ className="rcs-preview-video"
706
+ />
707
+ + </CapRow>
708
+ ) : (
709
+ <>
710
+ <CapHeading type="h4" className="rcs-image-dimensions-label">Upload Video</CapHeading>
711
+ @@ -1925,22 +3170,44 @@ const onTitleAddVar = () => {
712
+ );
713
+ }
714
+ }
715
+ + return renderTextComponent();
716
+ + };
717
+ +
718
+ + const getRcsPreview = () => {
719
+ + // Carousel preview: WhatsApp-style static horizontal scroll in UnifiedPreview
720
+ + if (templateType === contentType.carousel) {
721
+ + const cardsForPreview = buildCarouselCardsForPreview(carouselData);
722
+ + const carouselDimKey = getCarouselDimensionKey();
723
+ + const carouselImgDims =
724
+ + RCS_CAROUSEL_IMAGE_DIMENSIONS[carouselDimKey] || RCS_CAROUSEL_IMAGE_DIMENSIONS.MEDIUM_MEDIUM;
725
+ + const carouselVidDims =
726
+ + RCS_CAROUSEL_VIDEO_THUMBNAIL_DIMENSIONS[carouselDimKey]
727
+ + || RCS_CAROUSEL_VIDEO_THUMBNAIL_DIMENSIONS.MEDIUM_MEDIUM;
728
+ + // Debug log for embedded/library mode preview payload (carousel)
729
+ + // eslint-disable-next-line no-console
730
+ + return (
731
+ + <UnifiedPreview
732
+ + channel={RCS}
733
+ + content={{
734
+ + carouselData: cardsForPreview,
735
+ + carouselPreviewDimensions: {
736
+ + imageWidth: carouselImgDims.width,
737
+ + imageHeight: carouselImgDims.height,
738
+ + videoThumbWidth: carouselVidDims.width,
739
+ + videoThumbHeight: carouselVidDims.height,
740
+ + },
741
+ + }}
742
+ + device={ANDROID}
743
+ + showDeviceToggle={false}
744
+ + showHeader={false}
745
+ + formatMessage={formatMessage}
746
+ + />
747
+ + );
748
+ + }
749
+
750
+ const dimensionObj = RCS_IMAGE_DIMENSIONS[selectedDimension];
751
+ + const resolvedTitle = !isFullMode ? resolveTemplateWithMap(templateTitle) : templateTitle;
752
+ + const resolvedDesc = !isFullMode ? resolveTemplateWithMap(templateDesc) : templateDesc;
753
+ return (
754
+ <UnifiedPreview
755
+ channel={RCS}
756
+ @@ -1963,308 +3230,167 @@ const onTitleAddVar = () => {
757
+ );
758
+ };
759
+
760
+ + const getUnmappedDesc = (str, mapping) => {
761
+ + if (!str) return '';
762
+ + if (!mapping || Object.keys(mapping).length === 0) return str;
763
+ + let result = str;
764
+ + const replacements = [];
765
+ + Object.entries(mapping).forEach(([key, value]) => {
766
+ + const raw = (value ?? '').toString();
767
+ + if (!raw || raw?.trim?.() === '') return;
768
+ + const braced = /^\{\{[\s\S]*\}\}$/.test(raw) ? raw : `{{${raw}}}`;
769
+ + replacements.push({ key, needle: raw });
770
+ + if (braced !== raw) replacements.push({ key, needle: braced });
771
+ + });
772
+ + const seen = new Set();
773
+ + const uniq = replacements
774
+ + .filter(({ key, needle }) => {
775
+ + const id = `${key}::${needle}`;
776
+ + if (seen.has(id)) return false;
777
+ + seen.add(id);
778
+ + return true;
779
+ + })
780
+ + .sort((a, b) => (b.needle.length - a.needle.length));
781
+ +
782
+ + uniq.forEach(({ key, needle }) => {
783
+ + if (!needle) return;
784
+ + const escaped = needle.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
785
+ + const regex = new RegExp(escaped, 'g');
786
+ + result = result.replace(regex, `{{${key}}}`);
787
+ + });
788
+ + return result;
789
+ + };
790
+
791
+ -
792
+ + const buildCardVarMappedForText = (titleText = '', descText = '') => {
793
+ + const existingMap = cardVarMapped || {};
794
+ + const uniqueAllowedKeys = Array.from(new Set([
795
+ + ...((titleText.match(rcsVarRegex) || []).map((token) => getVarNameFromToken(token))),
796
+ + ...((descText.match(rcsVarRegex) || []).map((token) => getVarNameFromToken(token))),
797
+ + ].filter(Boolean)));
798
+ +
799
+ + return uniqueAllowedKeys.reduce((acc, key) => {
800
+ + acc[key] = Object.prototype.hasOwnProperty.call(existingMap, key) ? existingMap[key] : '';
801
+ + return acc;
802
+ + }, {});
803
+ + };
804
+ +
805
+ + const buildStandaloneCardContent = () => {
806
+ + const resolvedTitle = !isFullMode ? resolveTemplateWithMap(templateTitle) : templateTitle;
807
+ + const resolvedDesc = !isFullMode ? resolveTemplateWithMap(templateDesc) : templateDesc;
808
+ + return [
809
+ + {
810
+ + title: resolvedTitle,
811
+ + description: resolvedDesc,
812
+ + mediaType: templateMediaType,
813
+ + ...(!isMediaTypeText && {media: {
814
+ + mediaUrl: rcsImageSrc || rcsVideoSrc.videoSrc || '',
815
+ + thumbnailUrl: rcsThumbnailSrc || '',
816
+ + height: isMediaTypeImage
817
+ + ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.heightType || MEDIUM
818
+ + : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.heightType || MEDIUM,
819
+ + }}),
820
+ + ...(!isFullMode && (() => ({ cardVarMapped: buildCardVarMappedForText(templateTitle, templateDesc) }))()),
821
+ + ...(suggestions.length > 0 && { suggestions }),
822
+ }
823
+ + ];
824
+ + };
825
+ +
826
+ + const buildCarouselCardContent = () => {
827
+ + return (carouselData || []).map((card = {}) => {
828
+ + const title = card.title || '';
829
+ + const description = card.description || '';
830
+ + const mediaType = card.mediaType || RCS_MEDIA_TYPES.IMAGE;
831
+ + const mediaUrl = mediaType === RCS_MEDIA_TYPES.VIDEO
832
+ + ? (card.videoAsset?.videoSrc || '')
833
+ + : (card.imageSrc || '');
834
+ + const thumbnailUrl = mediaType === RCS_MEDIA_TYPES.VIDEO ? (card.thumbnailSrc || '') : '';
835
+ + return {
836
+ + title,
837
+ + description,
838
+ + mediaType,
839
+ + ...(mediaType !== RCS_MEDIA_TYPES.NONE && {
840
+ + media: {
841
+ + mediaUrl,
842
+ + thumbnailUrl,
843
+ + height: selectedCarouselHeight || MEDIUM,
844
+ + },
845
+ }),
846
+ + ...(!isFullMode && (() => ({ cardVarMapped: buildCardVarMappedForText(title, description) }))()),
847
+ + ...(Array.isArray(card.suggestions) && card.suggestions.length > 0 && { suggestions: card.suggestions }),
848
+ };
849
+ + });
850
+ + };
851
+ +
852
+ + const buildRcsContent = () => {
853
+ + const isCarouselPayload = templateType === contentType.carousel;
854
+ + const alignment = isMediaTypeImage
855
+ + ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.alignment
856
+ + : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.alignment;
857
+ +
858
+ + return {
859
+ + cardType: isCarouselPayload ? "CAROUSEL" : STANDALONE,
860
+ + cardSettings: {
861
+ + ...(isCarouselPayload
862
+ + ? {
863
+ + cardOrientation: VERTICAL,
864
+ + cardWidth: selectedCarouselWidth || SMALL,
865
+ + }
866
+ + : {
867
+ + cardOrientation: isMediaTypeImage
868
+ + ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.orientation || VERTICAL
869
+ + : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.orientation || VERTICAL,
870
+ + ...(alignment && { mediaAlignment: alignment }),
871
+ + cardWidth: isFullMode
872
+ + ? SMALL
873
+ + : (isMediaTypeImage
874
+ + ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.heightType || SMALL
875
+ + : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.heightType || SMALL),
876
+ + }),
877
+ + },
878
+ + cardContent: isCarouselPayload ? buildCarouselCardContent() : buildStandaloneCardContent(),
879
+ + // Backend expects this to always be RICHCARD (even for carousel).
880
+ + contentType: RICHCARD,
881
+ + ...(isFullMode && {accountId:accountId, accessToken: accessToken, accountName: accountName, hostName: hostName}),
882
+ + };
883
+ + };
884
+ +
885
+ + const createPayload = () => {
886
+ + const base = get(dltEditData, `versions.base`, {});
887
+ + const {
888
+ + template_id: templateId = '',
889
+ + template_name = '',
890
+ + 'sms-editor': template = '',
891
+ + header: registeredSenderIds = [],
892
+ + } = base;
893
+
894
+ + return {
895
+ name: templateName,
896
+ versions: {
897
+ base: {
898
+ content: {
899
+ RCS: {
900
+ + rcsContent: buildRcsContent(),
901
+ + ...(fallbackMessage && {
902
+ + smsFallBackContent: {
903
+ + message: fallbackMessage,
904
+ + ...(isDltEnabled && {
905
+ + templateConfigs: {
906
+ + templateId,
907
+ + templateName: template_name,
908
+ + template,
909
+ + registeredSenderIds,
910
+ + },
911
+ + }),
912
+ },
913
+ + }),
914
+ },
915
+ },
916
+ },
917
+ },
918
+ type: RCS,
919
+ };
920
+ };
921
+
922
+ -
923
+ -
924
+ const actionCallback = ({ errorMessage, resp }, isEdit) => {
925
+ // eslint-disable-next-line no-undef
926
+ const error = errorMessage?.message || errorMessage;
927
+ @@ -2396,16 +3454,72 @@ const onTitleAddVar = () => {
928
+ }
929
+ }
930
+
931
+ +
932
+ + if (templateType === contentType.carousel) {
933
+ + if (!carouselData || carouselData.length < 2) return true;
934
+ + const allValid = (carouselData || []).every((c, i) => isCarouselCardValid(c, i));
935
+ + if (!allValid) return true;
936
+ +
937
+ + if (!isFullMode) {
938
+ + const allCarouselVars = new Set();
939
+ + (carouselData || []).forEach((card) => {
940
+ + splitTemplateVarString(card.title || '').filter(elem => rcsVarTestRegex.test(elem)).forEach(v => allCarouselVars.add(v.replace(/^\{\{|\}\}$/g, '')));
941
+ + splitTemplateVarString(card.description || '').filter(elem => rcsVarTestRegex.test(elem)).forEach(v => allCarouselVars.add(v.replace(/^\{\{|\}\}$/g, '')));
942
+ + });
943
+ + const carouselVarArray = Array.from(allCarouselVars);
944
+ + if (carouselVarArray.length > 0) {
945
+ + if (isEmpty(cardVarMapped)) return true;
946
+ + const anyMissing = carouselVarArray.some(name => {
947
+ + const v = cardVarMapped?.[name];
948
+ + if (typeof v !== 'string') return !v;
949
+ + return v.trim() === '';
950
+ + });
951
+ + if (anyMissing) return true;
952
+ + }
953
+ + }
954
+ +
955
+ + const hasCarouselErrors = (carouselErrors || []).some(
956
+ + (e) => e && (e.title || e.description)
957
+ + );
958
+ + if (hasCarouselErrors) return true;
959
+ +
960
+ + return false;
961
+ }
962
+
963
+ + if(!isFullMode){
964
+ + const titleVars = splitTemplateVarString(templateTitle).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
965
+ + const descVars = splitTemplateVarString(templateDesc).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
966
+ + const allVars = Array.from(new Set([ ...titleVars, ...descVars ]));
967
+ +
968
+ + if (allVars.length > 0 && isEmpty(cardVarMapped)) {
969
+ + return true;
970
+ + }
971
+ +
972
+ + const hasEmptyMapping =
973
+ + cardVarMapped &&
974
+ + Object.keys(cardVarMapped).length > 0 &&
975
+ + Object.entries(cardVarMapped).some(([_, v]) => {
976
+ + if (typeof v !== 'string') return !v; // null/undefined
977
+ + return v.trim() === ''; // empty string
978
+ + });
979
+ +
980
+ + if (hasEmptyMapping) {
981
+ + return true;
982
+ + }
983
+ +
984
+ + const anyMissing = allVars.some(name => {
985
+ + const v = cardVarMapped?.[name];
986
+ + if (typeof v !== 'string') return !v;
987
+ + return v.trim() === '';
988
+ + });
989
+ + if (anyMissing) {
990
+ + return true;
991
+ + }
992
+ }
993
+
994
+ + if (isMediaTypeText && templateDesc.trim() === '') {
995
+ return true;
996
+ +
997
+ }
998
+ if (isMediaTypeImage && (rcsImageSrc === '' || templateTitle === '' || templateDesc === '' )) {
999
+ return true;
1000
+ @@ -2423,36 +3537,85 @@ const onTitleAddVar = () => {
1001
+ return true;
1002
+ }
1003
+ }
1004
+ + if (templateDescError || templateTitleError || fallbackMessageError) {
1005
+ return true;
1006
+ }
1007
+ return false;
1008
+ };
1009
+
1010
+ const isEditDisableDone = () => {
1011
+ +
1012
+ if (!isHostInfoBip && templateStatus !== RCS_STATUSES.approved) {
1013
+ return true;
1014
+ }
1015
+
1016
+ + if (!isFullMode) {
1017
+ + if (templateName.trim() === '' || templateNameError) {
1018
+ + return true;
1019
+ + }
1020
+ }
1021
+
1022
+ + if (templateType === contentType.carousel) {
1023
+ + if (!carouselData || carouselData.length < 2) return true;
1024
+ + const allValid = (carouselData || []).every((c, i) => isCarouselCardValid(c, i));
1025
+ + if (!allValid) return true;
1026
+ +
1027
+ + if (!isFullMode) {
1028
+ + const allCarouselVars = new Set();
1029
+ + (carouselData || []).forEach((card) => {
1030
+ + splitTemplateVarString(card.title || '').filter(elem => rcsVarTestRegex.test(elem)).forEach(v => allCarouselVars.add(v.replace(/^\{\{|\}\}$/g, '')));
1031
+ + splitTemplateVarString(card.description || '').filter(elem => rcsVarTestRegex.test(elem)).forEach(v => allCarouselVars.add(v.replace(/^\{\{|\}\}$/g, '')));
1032
+ + });
1033
+ + const carouselVarArray = Array.from(allCarouselVars);
1034
+ + if (carouselVarArray.length > 0) {
1035
+ + if (isEmpty(cardVarMapped)) return true;
1036
+ + const anyMissing = carouselVarArray.some(name => {
1037
+ + const v = cardVarMapped?.[name];
1038
+ + if (typeof v !== 'string') return !v;
1039
+ + return v.trim() === '';
1040
+ + });
1041
+ + if (anyMissing) return true;
1042
+ + }
1043
+ + }
1044
+ +
1045
+ + const hasCarouselErrors = (carouselErrors || []).some(
1046
+ + (e) => e && (e.title || e.description)
1047
+ + );
1048
+ + if (hasCarouselErrors) return true;
1049
+ +
1050
+ + return false;
1051
+ }
1052
+
1053
+ + if(!isFullMode){
1054
+ + const titleVars = splitTemplateVarString(templateTitle).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
1055
+ + const descVars = splitTemplateVarString(templateDesc).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
1056
+ + const allVars = Array.from(new Set([ ...titleVars, ...descVars ]));
1057
+ +
1058
+ + if (allVars.length > 0 && isEmpty(cardVarMapped)) {
1059
+ + return true;
1060
+ + }
1061
+ +
1062
+ + const hasEmptyMapping =
1063
+ + cardVarMapped &&
1064
+ + Object.keys(cardVarMapped).length > 0 &&
1065
+ + Object.entries(cardVarMapped).some(([_, v]) => {
1066
+ + if (typeof v !== 'string') return !v; // null/undefined
1067
+ + return v.trim() === ''; // empty string
1068
+ + });
1069
+ +
1070
+ + if (hasEmptyMapping) {
1071
+ + return true;
1072
+ + }
1073
+ +
1074
+ + const anyMissing = allVars.some(name => {
1075
+ + const v = cardVarMapped?.[name];
1076
+ + if (typeof v !== 'string') return !v;
1077
+ + return v.trim() === '';
1078
+ + });
1079
+ + if (anyMissing) {
1080
+ + return true;
1081
+ + }
1082
+ + }
1083
+ if (isMediaTypeText && templateDesc.trim() === '') {
1084
+ return true;
1085
+ }
1086
+ @@ -2526,152 +3683,234 @@ const onTitleAddVar = () => {
1087
+ }
1088
+ };
1089
+
1090
+ + const renderTemplateStatusBar = () => (
1091
+ + templateStatus !== '' && (
1092
+ + <CapRow className="template-status-container">
1093
+ + {renderLabel('templateStatusLabel')}
1094
+
1095
+ -
1096
+ + {!isHostInfoBip && templateStatus && (
1097
+ + <CapAlert
1098
+ + message={getTemplateStatusMessage()}
1099
+ + type={getTemplateStatusType(templateStatus)}
1100
+ + />
1101
+ )}
1102
+ + </CapRow>
1103
+ + )
1104
+ + );
1105
+
1106
+ + const renderTemplateNameSection = () => (
1107
+ + isFullMode && (
1108
+ + <>
1109
+ + <CapHeading type="h4" style={{ marginBottom: '6px' }}>
1110
+ + {formatMessage(globalMessages.creativeNameLabel)}
1111
+ + </CapHeading>
1112
+ + <CapInput
1113
+ + id="rcs_template_name_input"
1114
+ + data-testid="template_name"
1115
+ + onChange={onTemplateNameChange}
1116
+ + errorMessage={templateNameError}
1117
+ + placeholder={formatMessage(
1118
+ + globalMessages.templateNamePlaceholder,
1119
+ + )}
1120
+ + value={templateName || ''}
1121
+ + size="default"
1122
+ + disabled={(isEditFlow || !isFullMode)}
1123
+ + />
1124
+ + </>
1125
+ + )
1126
+ + );
1127
+
1128
+ + const renderTemplateTypeSection = () => (
1129
+ + <>
1130
+ + {renderLabel('templateTypeLabel')}
1131
+ + <CapRadioGroup
1132
+ + id="select-rcs-template-type"
1133
+ + options={TEMPLATE_TYPE_OPTIONS}
1134
+ + onChange={onTemplateTypeChange}
1135
+ + value={templateType}
1136
+ + disabled={(isEditFlow || !isFullMode)}
1137
+ + />
1138
+ + </>
1139
+ + );
1140
+
1141
+ + const renderMediaSection = () => (
1142
+ + (templateType === contentType.rich_card || templateType === contentType.carousel) && (
1143
+ + <>
1144
+ + {templateType !== contentType.carousel && renderLabel('mediaLabel')}
1145
+ + <CapRow className="rcs-container-image">
1146
+ + {templateType === contentType.carousel ? renderCarouselSection() : (
1147
+ <>
1148
+ + <CapRadioGroup
1149
+ + options={mediaRadioOptions || []}
1150
+ + value={templateMediaType}
1151
+ + onChange={onTemplateMediaTypeChange}
1152
+ + disabled={(isEditFlow || !isFullMode)}
1153
+ + className="rcs-radio"
1154
+ + />
1155
+ + {getMediaBasedComponent()}
1156
+ + </>
1157
+ + )}
1158
+ + </CapRow>
1159
+ + </>
1160
+ + )
1161
+ + );
1162
+ +
1163
+ + const renderContentSection = () => (
1164
+ + <>
1165
+ + {templateType !== contentType.carousel && (
1166
+ + <>
1167
+ + {renderTextComponent()}
1168
+ + <CapDivider className="rcs-content-section-divider" />
1169
+ + </>
1170
+ + )}
1171
+ + {renderFallBackSmsComponent()}
1172
+ + <CapRow className="rcs-scroll-div" />
1173
+ + </>
1174
+ + );
1175
+ +
1176
+ + const renderEditorColumn = () => (
1177
+ + <CapColumn span={14}>
1178
+ + {renderTemplateStatusBar()}
1179
+ + {/* template name */}
1180
+ + {renderTemplateNameSection()}
1181
+ + {renderTemplateTypeSection()}
1182
+ + {/* Show media only for rich_card or carousel */}
1183
+ + {renderMediaSection()}
1184
+ + {renderContentSection()}
1185
+ + </CapColumn>
1186
+ + );
1187
+ +
1188
+ + const renderPreviewColumn = () => (
1189
+ + <CapColumn span={10} className="rcs-preview-container">
1190
+ + {getRcsPreview()}
1191
+ + </CapColumn>
1192
+ + );
1193
+ +
1194
+ + const renderMainLayout = () => (
1195
+ + <CapRow className="cap-rcs-creatives">
1196
+ + {renderEditorColumn()}
1197
+ + {renderPreviewColumn()}
1198
+ + </CapRow>
1199
+ + );
1200
+ +
1201
+ + const renderFooter = () => (
1202
+ + <CapRow className="rcs-footer">
1203
+ + {!isEditFlow && (
1204
+ + <>
1205
+ + <CapRow className="button-disabled-tooltip-wrapper">
1206
+ + <CapButton
1207
+ + onClick={onDoneCallback()}
1208
+ + disabled={isDisableDone()}
1209
+ + className="rcs-done-btn"
1210
+ + >
1211
+ + <FormattedMessage
1212
+ + {...(isHostInfoBip ? messages.doneButtonLabel : messages.sendForApprovalButtonLabel)}
1213
+ + />
1214
+ + </CapButton>
1215
+ + </CapRow>
1216
+ + <CapTooltip
1217
+ + title={formatMessage(messages.rcsTestPreviewDisabledTooltip)}
1218
+ + placement="top"
1219
+ + >
1220
+ + <CapRow className="button-disabled-tooltip-wrapper">
1221
+ <CapButton
1222
+ onClick={handleTestAndPreview}
1223
+ className="rcs-test-preview-btn"
1224
+ type="secondary"
1225
+ + disabled
1226
+ >
1227
+ <FormattedMessage {...creativesMessages.testAndPreview} />
1228
+ </CapButton>
1229
+ + </CapRow>
1230
+ + </CapTooltip>
1231
+ + </>
1232
+ + )}
1233
+ + {(!isFullMode || isEditFlow) && (
1234
+ + <>
1235
+ + <CapRow className="button-disabled-tooltip-wrapper">
1236
+ + <CapButton
1237
+ + onClick={onEditRcs}
1238
+ + disabled={isEditDisableDone()}
1239
+ + className="rcs-done-btn"
1240
+ + >
1241
+ + <FormattedMessage {...messages.doneButtonLabel} />
1242
+ + </CapButton>
1243
+ + </CapRow>
1244
+ + </>
1245
+ + )}
1246
+ + {isEditFlow && templateStatus === RCS_STATUSES.approved && (
1247
+ + <CapButton
1248
+ + onClick={handleTestAndPreview}
1249
+ + className="rcs-test-preview-btn"
1250
+ + type="secondary"
1251
+ + >
1252
+ + <FormattedMessage {...creativesMessages.testAndPreview} />
1253
+ + </CapButton>
1254
+ + )}
1255
+ + </CapRow>
1256
+ + );
1257
+ +
1258
+ + const renderFallbackPreviewSlidebox = () => (
1259
+ + fallbackPreviewmode && (
1260
+ + <CapSlideBox
1261
+ + className="rcs-fallback-preview"
1262
+ + show={fallbackPreviewmode}
1263
+ + header={(
1264
+ + <CapHeading type="h7" className="rcs-fallback-preview__header">
1265
+ + {formatMessage(messages.fallbackPreviewtitle)}
1266
+ + </CapHeading>
1267
+ + )}
1268
+ + content={(
1269
+ + <>
1270
+ + <TemplatePreview
1271
+ + channel={RCS}
1272
+ + content={{
1273
+ + rcsPreviewContent: {
1274
+ + rcsDesc: tempMsg,
1275
+ + },
1276
+ + }}
1277
+ + />
1278
+ + <CapHeading
1279
+ + type="h3"
1280
+ + className="margin-t-16 rcs-fallback-preview__count"
1281
+ + >
1282
+ + {formatMessage(messages.totalCharacters, {
1283
+ + smsCount: Math.ceil(
1284
+ + tempMsg?.length / FALLBACK_MESSAGE_MAX_LENGTH,
1285
+ + ),
1286
+ + number: tempMsg.length,
1287
+ + })}
1288
+ + </CapHeading>
1289
+ + </>
1290
+ + )}
1291
+ + handleClose={() => {
1292
+ + setFallbackPreviewmode(false);
1293
+ + setDltPreviewData('');
1294
+ + }}
1295
+ + />
1296
+ + )
1297
+ + );
1298
+ +
1299
+ + const getMainContent = () => {
1300
+ + if (showDltContainer && !fallbackPreviewmode) {
1301
+ + const dltSlideBoxContent = showDltContainer && getDltSlideBoxContent();
1302
+ + const { dltHeader = '', dltContent = '' } = dltSlideBoxContent;
1303
+ + return (
1304
+ + <CapSlideBox
1305
+ + show={showDltContainer}
1306
+ + header={dltHeader}
1307
+ + content={dltContent}
1308
+ + handleClose={closeDltContainerHandler}
1309
+ + size="size-xl"
1310
+ + />
1311
+ + );
1312
+ + }
1313
+ +
1314
+ + return (
1315
+ + <>
1316
+ + {renderMainLayout()}
1317
+ + {renderFooter()}
1318
+ + {renderFallbackPreviewSlidebox()}
1319
+ </>
1320
+ );
1321
+ };
1322
+ @@ -2680,44 +3919,12 @@ const onTitleAddVar = () => {
1323
+ <CapSpin spinning={loadingTags || spin}>
1324
+ {getMainContent()}
1325
+ </CapSpin>
1326
+ -
1327
+ -
1328
+ <TestAndPreviewSlidebox
1329
+ show={propsShowTestAndPreviewSlidebox || showTestAndPreviewSlidebox}
1330
+ onClose={handleCloseTestAndPreview}
1331
+ + formData={null} // RCS doesn't use formData structure like SMS
1332
+ + content={getTemplateContent()}
1333
+ currentChannel={RCS}
1334
+ />
1335
+ </>
1336
+ );