@capillarytech/creatives-library 8.0.345-alpha.12 → 8.0.345-alpha.14
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.
- package/constants/unified.js +0 -29
- package/package.json +1 -1
- package/services/tests/api.test.js +0 -13
- package/utils/commonUtils.js +1 -19
- package/v2Components/CapActionButton/constants.js +0 -7
- package/v2Components/CapActionButton/index.js +109 -167
- package/v2Components/CapActionButton/index.scss +6 -157
- package/v2Components/CapActionButton/messages.js +3 -19
- package/v2Components/CapActionButton/tests/index.test.js +17 -41
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -207
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -160
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -341
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
- package/v2Components/CommonTestAndPreview/constants.js +2 -38
- package/v2Components/CommonTestAndPreview/index.js +186 -676
- package/v2Components/CommonTestAndPreview/messages.js +3 -49
- package/v2Components/CommonTestAndPreview/sagas.js +6 -15
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +10 -8
- package/v2Components/TemplatePreview/_templatePreview.scss +23 -33
- package/v2Components/TemplatePreview/index.js +28 -143
- package/v2Components/TemplatePreview/tests/index.test.js +0 -142
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
- package/v2Containers/CreativesContainer/constants.js +0 -9
- package/v2Containers/CreativesContainer/index.js +103 -300
- package/v2Containers/CreativesContainer/index.scss +1 -51
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/Email/reducer.js +11 -3
- package/v2Containers/Email/sagas.js +9 -5
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -0
- package/v2Containers/Email/tests/sagas.test.js +21 -3
- package/v2Containers/Rcs/constants.js +8 -119
- package/v2Containers/Rcs/index.js +812 -2375
- package/v2Containers/Rcs/index.scss +6 -276
- package/v2Containers/Rcs/messages.js +3 -38
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70345 -98302
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
- package/v2Containers/Rcs/tests/index.test.js +121 -152
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +30 -646
- package/v2Containers/Rcs/utils.js +11 -478
- package/v2Containers/Sms/Create/index.js +40 -100
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +130 -636
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +2 -163
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +54 -119
- package/v2Containers/Templates/sagas.js +12 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1079 -1043
- package/v2Containers/Templates/tests/sagas.test.js +123 -193
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
- package/v2Containers/TemplatesV2/index.js +23 -86
- package/v2Containers/Whatsapp/index.js +20 -3
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
- package/utils/rcsPayloadUtils.js +0 -92
- package/utils/templateVarUtils.js +0 -201
- package/utils/tests/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +0 -18
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -87
- package/v2Components/SmsFallback/constants.js +0 -73
- package/v2Components/SmsFallback/index.js +0 -955
- package/v2Components/SmsFallback/index.scss +0 -265
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -118
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -197
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -277
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/TemplatePreview/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -67
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/index.js.rej +0 -1336
- package/v2Containers/Rcs/index.scss.rej +0 -74
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +0 -128
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -121
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
|
@@ -1,1336 +0,0 @@
|
|
|
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
|
-
);
|