@capillarytech/creatives-library 8.0.329 → 8.0.330
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 -14
- package/package.json +1 -1
- package/services/api.js +0 -17
- package/services/tests/api.test.js +0 -85
- package/utils/commonUtils.js +0 -10
- package/utils/tests/commonUtil.test.js +0 -169
- 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 +53 -87
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -20
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +34 -145
- package/v2Components/CommonTestAndPreview/actions.js +0 -10
- package/v2Components/CommonTestAndPreview/constants.js +1 -53
- package/v2Components/CommonTestAndPreview/index.js +168 -1006
- package/v2Components/CommonTestAndPreview/messages.js +3 -147
- package/v2Components/CommonTestAndPreview/reducer.js +0 -10
- package/v2Components/CommonTestAndPreview/sagas.js +6 -15
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +286 -328
- 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/SendTestMessage.test.js +24 -65
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -31
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -168
- package/v2Components/CommonTestAndPreview/tests/reducer.test.js +0 -71
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/CommonTestAndPreview/tests/selectors.test.js +0 -17
- package/v2Components/FormBuilder/index.js +1 -7
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -8
- 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 +93 -286
- 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 +10 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/Rcs/constants.js +1 -34
- package/v2Containers/Rcs/index.js +884 -999
- package/v2Containers/Rcs/index.scss +6 -85
- package/v2Containers/Rcs/messages.js +1 -10
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +2453 -41456
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
- package/v2Containers/Rcs/tests/index.test.js +38 -41
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +1 -379
- package/v2Containers/Rcs/utils.js +10 -358
- package/v2Containers/Sms/Create/index.js +38 -100
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +128 -609
- package/v2Containers/SmsTrai/Edit/messages.js +4 -9
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2600 -4586
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +2 -63
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +40 -90
- 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 +4872 -5790
- package/utils/templateVarUtils.js +0 -172
- package/utils/tests/templateVarUtils.test.js +0 -160
- package/v2Components/CommonTestAndPreview/AddTestCustomer.js +0 -42
- package/v2Components/CommonTestAndPreview/CustomerCreationModal.js +0 -155
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +0 -93
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/AddTestCustomer.test.js +0 -66
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -648
- package/v2Components/CommonTestAndPreview/tests/CustomerCreationModal.test.js +0 -174
- package/v2Components/CommonTestAndPreview/tests/ExistingCustomerModal.test.js +0 -114
- 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 -107
- 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 -261
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- 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/rcsLibraryHydrationUtils.js +0 -205
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -251
- 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,12 +1,13 @@
|
|
|
1
1
|
/* eslint-disable no-unused-expressions */
|
|
2
|
-
import React, { useState, useEffect, useCallback
|
|
2
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
3
3
|
import { bindActionCreators } from 'redux';
|
|
4
4
|
import { createStructuredSelector } from 'reselect';
|
|
5
|
-
import { FormattedMessage } from 'react-intl';
|
|
5
|
+
import { injectIntl, FormattedMessage } from 'react-intl';
|
|
6
6
|
import get from 'lodash/get';
|
|
7
7
|
import isEmpty from 'lodash/isEmpty';
|
|
8
8
|
import cloneDeep from 'lodash/cloneDeep';
|
|
9
9
|
import isNil from 'lodash/isNil';
|
|
10
|
+
import styled from 'styled-components';
|
|
10
11
|
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
11
12
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
12
13
|
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
|
|
@@ -21,16 +22,35 @@ import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
|
21
22
|
import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
|
|
22
23
|
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
23
24
|
import CapImage from '@capillarytech/cap-ui-library/CapImage';
|
|
25
|
+
import CapCard from '@capillarytech/cap-ui-library/CapCard';
|
|
24
26
|
import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
|
|
25
27
|
import CapSelect from '@capillarytech/cap-ui-library/CapSelect';
|
|
28
|
+
import CapCustomCard from '@capillarytech/cap-ui-library/CapCustomCard';
|
|
29
|
+
import CapDropdown from '@capillarytech/cap-ui-library/CapDropdown';
|
|
30
|
+
import CapMenu from '@capillarytech/cap-ui-library/CapMenu';
|
|
26
31
|
import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
|
|
32
|
+
import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
|
|
27
33
|
import CapError from '@capillarytech/cap-ui-library/CapError';
|
|
34
|
+
import CapCheckbox from '@capillarytech/cap-ui-library/CapCheckbox';
|
|
28
35
|
import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
|
|
36
|
+
import CapLink from '@capillarytech/cap-ui-library/CapLink';
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
CAP_G01,
|
|
40
|
+
CAP_SPACE_04,
|
|
41
|
+
CAP_SPACE_16,
|
|
42
|
+
CAP_SPACE_24,
|
|
43
|
+
CAP_SPACE_28,
|
|
44
|
+
CAP_SPACE_32,
|
|
45
|
+
CAP_WHITE,
|
|
46
|
+
CAP_SECONDARY,
|
|
47
|
+
} from '@capillarytech/cap-ui-library/styled/variables';
|
|
29
48
|
|
|
30
49
|
import CapVideoUpload from '../../v2Components/CapVideoUpload';
|
|
31
50
|
import * as globalActions from '../Cap/actions';
|
|
32
51
|
import CapActionButton from '../../v2Components/CapActionButton';
|
|
33
52
|
import { makeSelectRcs, makeSelectAccount } from './selectors';
|
|
53
|
+
import { DATE_DISPLAY_FORMAT, TIME_DISPLAY_FORMAT } from '../App/constants';
|
|
34
54
|
import {
|
|
35
55
|
isLoadingMetaEntities,
|
|
36
56
|
makeSelectMetaEntities,
|
|
@@ -40,15 +60,6 @@ import * as RcsActions from './actions';
|
|
|
40
60
|
import { isAiContentBotDisabled } from '../../utils/common';
|
|
41
61
|
import * as TemplatesActions from '../Templates/actions';
|
|
42
62
|
import './index.scss';
|
|
43
|
-
import {
|
|
44
|
-
normalizeLibraryLoadedTitleDesc,
|
|
45
|
-
mergeRcsSmsFallBackContentFromDetails,
|
|
46
|
-
mergeRcsSmsFallbackVarMapLayers,
|
|
47
|
-
pickFirstSmsFallbackTemplateString,
|
|
48
|
-
syncCardVarMappedSemanticsFromSlots,
|
|
49
|
-
hasMeaningfulSmsFallbackShape,
|
|
50
|
-
getLibrarySmsFallbackApiBaselineFromTemplateData,
|
|
51
|
-
} from './rcsLibraryHydrationUtils';
|
|
52
63
|
import {
|
|
53
64
|
RCS,
|
|
54
65
|
SMS,
|
|
@@ -70,6 +81,8 @@ import {
|
|
|
70
81
|
MESSAGE_TEXT,
|
|
71
82
|
ALLOWED_EXTENSIONS_VIDEO_REGEX,
|
|
72
83
|
RCS_VIDEO_SIZE,
|
|
84
|
+
TEMPLATE_HEADER_MAX_LENGTH,
|
|
85
|
+
TEMPLATE_MESSAGE_MAX_LENGTH,
|
|
73
86
|
RCS_THUMBNAIL_MIN_SIZE,
|
|
74
87
|
RCS_THUMBNAIL_MAX_SIZE,
|
|
75
88
|
contentType,
|
|
@@ -82,46 +95,35 @@ import {
|
|
|
82
95
|
MAX_BUTTONS,
|
|
83
96
|
INITIAL_SUGGESTIONS_DATA_STOP,
|
|
84
97
|
RCS_BUTTON_TYPES,
|
|
98
|
+
titletype,
|
|
99
|
+
descType,
|
|
85
100
|
STANDALONE,
|
|
86
101
|
VERTICAL,
|
|
87
102
|
SMALL,
|
|
88
103
|
MEDIUM,
|
|
89
104
|
RICHCARD,
|
|
90
|
-
RCS_NUMERIC_VAR_NAME_REGEX,
|
|
91
|
-
RCS_NUMERIC_VAR_TOKEN_REGEX,
|
|
92
|
-
RCS_TAG_AREA_FIELD_TITLE,
|
|
93
|
-
RCS_TAG_AREA_FIELD_DESC,
|
|
94
105
|
} from './constants';
|
|
95
106
|
import globalMessages from '../Cap/messages';
|
|
96
107
|
import messages from './messages';
|
|
97
108
|
import creativesMessages from '../CreativesContainer/messages';
|
|
98
109
|
import withCreatives from '../../hoc/withCreatives';
|
|
99
110
|
import UnifiedPreview from '../../v2Components/CommonTestAndPreview/UnifiedPreview';
|
|
100
|
-
import
|
|
101
|
-
import { ANDROID, RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../../v2Components/CommonTestAndPreview/constants';
|
|
111
|
+
import { ANDROID } from '../../v2Components/CommonTestAndPreview/constants';
|
|
102
112
|
import TestAndPreviewSlidebox from '../../v2Components/TestAndPreviewSlidebox';
|
|
103
|
-
import { splitTemplateVarString } from '../../utils/templateVarUtils';
|
|
104
113
|
import CapImageUpload from '../../v2Components/CapImageUpload';
|
|
114
|
+
import addCreativesIcon from '../Assets/images/addCreativesIllustration.svg';
|
|
105
115
|
import Templates from '../Templates';
|
|
106
116
|
import SmsTraiEdit from '../SmsTrai/Edit';
|
|
107
|
-
import SmsFallback from '../../v2Components/SmsFallback';
|
|
108
|
-
import { CHANNELS_TO_HIDE_FOR_SMS_ONLY } from '../../v2Components/SmsFallback/constants';
|
|
109
117
|
import TagList from '../TagList';
|
|
110
118
|
import { validateTags } from '../../utils/tagValidations';
|
|
111
|
-
import {
|
|
119
|
+
import { getCdnUrl } from '../../utils/cdnTransformation';
|
|
112
120
|
import { isTagIncluded } from '../../utils/commonUtils';
|
|
113
121
|
import injectReducer from '../../utils/injectReducer';
|
|
114
122
|
import v2RcsReducer from './reducer';
|
|
115
|
-
import {
|
|
116
|
-
areAllRcsSmsFallbackVarSlotsFilled,
|
|
117
|
-
buildRcsNumericMustachePlaceholderRegex,
|
|
118
|
-
getTemplateStatusType,
|
|
119
|
-
normalizeCardVarMapped,
|
|
120
|
-
coalesceCardVarMappedToTemplate,
|
|
121
|
-
resolveCardVarMappedSlotValue,
|
|
122
|
-
sanitizeCardVarMappedValue,
|
|
123
|
-
} from './utils';
|
|
123
|
+
import { getTemplateStatusType } from './utils';
|
|
124
124
|
|
|
125
|
+
|
|
126
|
+
const { Group: CapCheckboxGroup } = CapCheckbox;
|
|
125
127
|
export const Rcs = (props) => {
|
|
126
128
|
const {
|
|
127
129
|
intl,
|
|
@@ -135,14 +137,15 @@ export const Rcs = (props) => {
|
|
|
135
137
|
templatesActions,
|
|
136
138
|
globalActions,
|
|
137
139
|
location,
|
|
140
|
+
handleClose,
|
|
138
141
|
getDefaultTags,
|
|
139
142
|
supportedTags,
|
|
140
143
|
metaEntities,
|
|
141
144
|
injectedTags,
|
|
142
145
|
loadingTags,
|
|
143
146
|
getFormData,
|
|
147
|
+
isDltEnabled,
|
|
144
148
|
smsRegister,
|
|
145
|
-
orgUnitId,
|
|
146
149
|
selectedOfferDetails,
|
|
147
150
|
eventContextTags,
|
|
148
151
|
accountData = {},
|
|
@@ -153,8 +156,8 @@ export const Rcs = (props) => {
|
|
|
153
156
|
} = props || {};
|
|
154
157
|
const { formatMessage } = intl;
|
|
155
158
|
const { TextArea } = CapInput;
|
|
159
|
+
const { CapCustomCardList } = CapCustomCard;
|
|
156
160
|
const [isEditFlow, setEditFlow] = useState(false);
|
|
157
|
-
const isEditLike = isEditFlow || !isFullMode;
|
|
158
161
|
const [tags, updateTags] = useState([]);
|
|
159
162
|
const [spin, setSpin] = useState(false);
|
|
160
163
|
//template
|
|
@@ -163,71 +166,112 @@ export const Rcs = (props) => {
|
|
|
163
166
|
const [templateMediaType, setTemplateMediaType] = useState(
|
|
164
167
|
RCS_MEDIA_TYPES.NONE,
|
|
165
168
|
);
|
|
169
|
+
const [templateRejectionReason, setTemplateRejectionReason] = useState(null);
|
|
166
170
|
const [templateTitle, setTemplateTitle] = useState('');
|
|
167
171
|
const [templateDesc, setTemplateDesc] = useState('');
|
|
168
172
|
const [templateDescError, setTemplateDescError] = useState(false);
|
|
169
173
|
const [templateStatus, setTemplateStatus] = useState('');
|
|
174
|
+
const [templateDate, setTemplateDate] = useState('');
|
|
175
|
+
//fallback
|
|
176
|
+
const [fallbackMessage, setFallbackMessage] = useState('');
|
|
177
|
+
const [fallbackMessageError, setFallbackMessageError] = useState(false);
|
|
170
178
|
//fallback dlt
|
|
171
179
|
const [showDltContainer, setShowDltContainer] = useState(false);
|
|
172
180
|
const [dltMode, setDltMode] = useState('');
|
|
173
181
|
const [dltEditData, setDltEditData] = useState({});
|
|
174
|
-
|
|
175
|
-
const [
|
|
182
|
+
const [showDltCard, setShowDltCard] = useState(false);
|
|
183
|
+
const [fallbackPreviewmode, setFallbackPreviewmode] = useState(false);
|
|
184
|
+
const [dltPreviewData, setDltPreviewData] = useState('');
|
|
176
185
|
const [suggestions, setSuggestions] = useState(INITIAL_SUGGESTIONS_DATA_STOP);
|
|
177
|
-
const buttonType = RCS_BUTTON_TYPES.NONE;
|
|
186
|
+
const [buttonType, setButtonType] = useState(RCS_BUTTON_TYPES.NONE);
|
|
178
187
|
const [suggestionError, setSuggestionError] = useState(true);
|
|
179
188
|
const [templateType, setTemplateType] = useState('text_message');
|
|
189
|
+
const [templateHeader, setTemplateHeader] = useState('');
|
|
190
|
+
const [templateMessage, setTemplateMessage] = useState('');
|
|
191
|
+
const [templateHeaderError, setTemplateHeaderError] = useState('');
|
|
192
|
+
const [templateMessageError, setTemplateMessageError] = useState('');
|
|
193
|
+
const validVarRegex = /\{\{(\d+)\}\}/g;
|
|
194
|
+
const [updatedTitleData, setUpdatedTitleData] = useState([]);
|
|
195
|
+
const [updatedDescData, setUpdatedDescData] = useState([]);
|
|
180
196
|
const [titleVarMappedData, setTitleVarMappedData] = useState({});
|
|
181
197
|
const [descVarMappedData, setDescVarMappedData] = useState({});
|
|
182
198
|
const [titleTextAreaId, setTitleTextAreaId] = useState();
|
|
183
199
|
const [descTextAreaId, setDescTextAreaId] = useState();
|
|
184
200
|
const [assetList, setAssetList] = useState({});
|
|
201
|
+
const [assetListImage, setAssetListImage] = useState('');
|
|
185
202
|
const [rcsImageSrc, updateRcsImageSrc] = useState('');
|
|
186
203
|
const [rcsVideoSrc, setRcsVideoSrc] = useState({});
|
|
187
204
|
const [rcsThumbnailSrc, setRcsThumbnailSrc] = useState('');
|
|
188
205
|
const [selectedDimension, setSelectedDimension] = useState(RCS_IMAGE_DIMENSIONS.MEDIUM_HEIGHT.type);
|
|
206
|
+
const [imageError, setImageError] = useState(null);
|
|
189
207
|
const [templateTitleError, setTemplateTitleError] = useState(false);
|
|
190
208
|
const [cardVarMapped, setCardVarMapped] = useState({});
|
|
191
|
-
/** Bump when hydrated cardVarMapped payload changes so VarSegment TextAreas remount with fresh valueMap (controlled-input sync). */
|
|
192
|
-
const [rcsVarSegmentEditorRemountKey, setRcsVarSegmentEditorRemountKey] = useState(0);
|
|
193
|
-
const lastHydratedRcsCardVarSignatureRef = useRef(null);
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Hydrate only from template payload — not from full `rcsData`. Uploads/asset updates change `rcsData`
|
|
197
|
-
* without changing `templateDetails`; re-running hydration then cleared `smsFallbackData`, so the SMS
|
|
198
|
-
* fallback card / content stopped appearing until re-selected.
|
|
199
|
-
*/
|
|
200
|
-
const rcsHydrationDetails = useMemo(
|
|
201
|
-
() => (isFullMode ? rcsData?.templateDetails : templateData),
|
|
202
|
-
[isFullMode, rcsData?.templateDetails, templateData],
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
/** Skip duplicate /meta/TAG fetches: same query is triggered from (1) useEffect below, (2) title TagList mount, (3) description TagList mount — each calls getTagsforContext('Outbound'). */
|
|
206
|
-
const lastTagSchemaQueryKeyRef = useRef(null);
|
|
207
|
-
/**
|
|
208
|
-
* Library: parent often passes a new `templateData` object reference every render. Re-applying the same
|
|
209
|
-
* SMS fallback snapshot was resetting `smsFallbackData` and caused VarSegment inputs to flicker old/new.
|
|
210
|
-
*/
|
|
211
|
-
const lastSmsFallbackHydrationKeyRef = useRef(null);
|
|
212
|
-
|
|
213
|
-
const fetchTagSchemaIfNewQuery = useCallback(
|
|
214
|
-
(query) => {
|
|
215
|
-
const key = JSON.stringify(query);
|
|
216
|
-
if (lastTagSchemaQueryKeyRef.current === key) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
lastTagSchemaQueryKeyRef.current = key;
|
|
220
|
-
globalActions.fetchSchemaForEntity(query);
|
|
221
|
-
},
|
|
222
|
-
[globalActions],
|
|
223
|
-
);
|
|
224
209
|
|
|
225
210
|
// TestAndPreviewSlidebox state
|
|
226
211
|
const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
|
|
212
|
+
const [isTestAndPreviewMode, setIsTestAndPreviewMode] = useState(false);
|
|
213
|
+
|
|
214
|
+
const tempMsg = dltPreviewData === '' ? fallbackMessage : dltPreviewData;
|
|
215
|
+
|
|
216
|
+
// Get template content for TestAndPreviewSlidebox
|
|
217
|
+
// Reference: Based on getRcsPreview() function (lines 2087-2111) which prepares content for TemplatePreview
|
|
218
|
+
// getRcsPreview ALWAYS uses templateTitle and templateDesc for ALL template types (text_message, rich_card, carousel)
|
|
219
|
+
// renderTextComponent (lines 1317-1485) also uses templateTitle and templateDesc
|
|
220
|
+
// Note: templateHeader and templateMessage are defined but NOT used in the component
|
|
221
|
+
const getTemplateContent = useCallback(() => {
|
|
222
|
+
const isMediaTypeImage = templateMediaType === RCS_MEDIA_TYPES.IMAGE;
|
|
223
|
+
const isMediaTypeVideo = templateMediaType === RCS_MEDIA_TYPES.VIDEO;
|
|
224
|
+
const isMediaTypeText = templateMediaType === RCS_MEDIA_TYPES.NONE;
|
|
225
|
+
|
|
226
|
+
// Build media preview object (same pattern as getRcsPreview)
|
|
227
|
+
const mediaPreview = {};
|
|
228
|
+
if (isMediaTypeImage && rcsImageSrc) {
|
|
229
|
+
mediaPreview.rcsImageSrc = rcsImageSrc;
|
|
230
|
+
}
|
|
231
|
+
if (isMediaTypeVideo && !isMediaTypeText) {
|
|
232
|
+
// For video, use thumbnailSrc as rcsVideoSrc (same as getRcsPreview line 2104)
|
|
233
|
+
if (rcsThumbnailSrc) {
|
|
234
|
+
mediaPreview.rcsVideoSrc = rcsThumbnailSrc;
|
|
235
|
+
} else if (rcsVideoSrc?.videoSrc) {
|
|
236
|
+
mediaPreview.rcsVideoSrc = rcsVideoSrc.videoSrc;
|
|
237
|
+
}
|
|
238
|
+
// Also include thumbnailSrc separately if available
|
|
239
|
+
if (rcsThumbnailSrc) {
|
|
240
|
+
mediaPreview.rcsThumbnailSrc = rcsThumbnailSrc;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Build content object
|
|
245
|
+
// Reference: getRcsPreview (line 2091-2092) uses templateTitle and templateDesc for ALL cases
|
|
246
|
+
// templateTitle is used for rich_card/carousel title, empty for text_message
|
|
247
|
+
// templateDesc is used for ALL types (text message body or rich card description)
|
|
248
|
+
// For UnifiedPreview, we map templateTitle -> templateHeader and templateDesc -> templateMessage
|
|
249
|
+
const contentObj = {
|
|
250
|
+
// Map templateTitle to templateHeader and templateDesc to templateMessage
|
|
251
|
+
templateHeader: templateTitle,
|
|
252
|
+
templateMessage: templateDesc,
|
|
253
|
+
...mediaPreview,
|
|
254
|
+
...(suggestions.length > 0 && {
|
|
255
|
+
suggestions: suggestions,
|
|
256
|
+
}),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return contentObj;
|
|
260
|
+
}, [
|
|
261
|
+
templateMediaType,
|
|
262
|
+
templateTitle,
|
|
263
|
+
templateDesc,
|
|
264
|
+
rcsImageSrc,
|
|
265
|
+
rcsVideoSrc,
|
|
266
|
+
rcsThumbnailSrc,
|
|
267
|
+
suggestions,
|
|
268
|
+
selectedDimension,
|
|
269
|
+
]);
|
|
227
270
|
|
|
228
271
|
// Handle Test and Preview button click
|
|
229
272
|
const handleTestAndPreview = useCallback(() => {
|
|
230
273
|
setShowTestAndPreviewSlidebox(true);
|
|
274
|
+
setIsTestAndPreviewMode(true);
|
|
231
275
|
if (propsHandleTestAndPreview) {
|
|
232
276
|
propsHandleTestAndPreview();
|
|
233
277
|
}
|
|
@@ -236,28 +280,23 @@ export const Rcs = (props) => {
|
|
|
236
280
|
// Handle close Test and Preview slidebox
|
|
237
281
|
const handleCloseTestAndPreview = useCallback(() => {
|
|
238
282
|
setShowTestAndPreviewSlidebox(false);
|
|
283
|
+
setIsTestAndPreviewMode(false);
|
|
239
284
|
if (propsHandleCloseTestAndPreview) {
|
|
240
285
|
propsHandleCloseTestAndPreview();
|
|
241
286
|
}
|
|
242
287
|
}, [propsHandleCloseTestAndPreview]);
|
|
243
288
|
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return { ...prev, ...patch };
|
|
254
|
-
});
|
|
255
|
-
}, []);
|
|
289
|
+
// Helper to get RCS orientation from selectedDimension
|
|
290
|
+
const getRcsOrientation = () => {
|
|
291
|
+
const isMediaTypeImage = templateMediaType === RCS_MEDIA_TYPES.IMAGE;
|
|
292
|
+
if (isMediaTypeImage) {
|
|
293
|
+
return RCS_IMAGE_DIMENSIONS[selectedDimension]?.orientation || VERTICAL;
|
|
294
|
+
}
|
|
295
|
+
// For video
|
|
296
|
+
return RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.orientation || VERTICAL;
|
|
297
|
+
};
|
|
256
298
|
|
|
257
|
-
/** RCS template save / edit API: `rcsContent.accountId` must stay `sourceAccountIdentifier` (pairs with accessToken). */
|
|
258
299
|
const [accountId, setAccountId] = useState('');
|
|
259
|
-
/** WeCRM list row `id` — only for CommonTestAndPreview → createMessageMeta payload, not for template save. */
|
|
260
|
-
const [wecrmAccountId, setWecrmAccountId] = useState('');
|
|
261
300
|
const [accessToken, setAccessToken] = useState('');
|
|
262
301
|
const [hostName, setHostName] = useState('');
|
|
263
302
|
const [accountName, setAccountName] = useState('');
|
|
@@ -265,23 +304,14 @@ export const Rcs = (props) => {
|
|
|
265
304
|
const accountObj = accountData.selectedRcsAccount || {};
|
|
266
305
|
if (!isEmpty(accountObj)) {
|
|
267
306
|
const {
|
|
268
|
-
id: wecrmId,
|
|
269
307
|
sourceAccountIdentifier = '',
|
|
270
308
|
configs = {},
|
|
271
309
|
} = accountObj;
|
|
310
|
+
|
|
272
311
|
setAccountId(sourceAccountIdentifier);
|
|
273
|
-
setWecrmAccountId(
|
|
274
|
-
wecrmId != null && String(wecrmId).trim() !== '' ? String(wecrmId) : '',
|
|
275
|
-
);
|
|
276
312
|
setAccessToken(configs.accessToken || '');
|
|
277
313
|
setHostName(accountObj.hostName || '');
|
|
278
314
|
setAccountName(accountObj.name || '');
|
|
279
|
-
} else {
|
|
280
|
-
setAccountId('');
|
|
281
|
-
setWecrmAccountId('');
|
|
282
|
-
setAccessToken('');
|
|
283
|
-
setHostName('');
|
|
284
|
-
setAccountName('');
|
|
285
315
|
}
|
|
286
316
|
}, [accountData.selectedRcsAccount]);
|
|
287
317
|
|
|
@@ -337,9 +367,7 @@ export const Rcs = (props) => {
|
|
|
337
367
|
if (isFullMode) return;
|
|
338
368
|
if (loadingTags || !tags || tags.length === 0) return;
|
|
339
369
|
const templateStr = type === TITLE_TEXT ? templateTitle : templateDesc;
|
|
340
|
-
const
|
|
341
|
-
type === TITLE_TEXT ? 0 : (templateTitle ? templateTitle.match(rcsVarRegex) || [] : []).length;
|
|
342
|
-
const resolved = resolveTemplateWithMap(templateStr, slotOffset); // placeholders -> mapped value (or '')
|
|
370
|
+
const resolved = resolveTemplateWithMap(templateStr); // placeholders -> mapped value (or '')
|
|
343
371
|
if (!resolved) {
|
|
344
372
|
if (type === TITLE_TEXT) setTemplateTitleError(false);
|
|
345
373
|
if (type === MESSAGE_TEXT) setTemplateDescError(false);
|
|
@@ -384,41 +412,13 @@ export const Rcs = (props) => {
|
|
|
384
412
|
|
|
385
413
|
const getVarNameFromToken = (token = '') => token.replace(/^\{\{|\}\}$/g, '');
|
|
386
414
|
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
/** Global slot index (0-based, title vars then desc) for a VarSegmentMessageEditor `id` (`{{tok}}_segIdx`), or null if not found. */
|
|
390
|
-
const getGlobalSlotIndexForRcsFieldId = (varSegmentCompositeId, fieldTemplateStr, fieldType) => {
|
|
391
|
-
const titleVarTokenMatches = templateTitle?.match(rcsVarRegex) ?? [];
|
|
392
|
-
const offset = fieldType === TITLE_TEXT ? 0 : titleVarTokenMatches.length;
|
|
393
|
-
const templateSegments = splitTemplateVarStringRcs(fieldTemplateStr ?? '');
|
|
394
|
-
let varOrdinal = 0;
|
|
395
|
-
for (let segmentIndexInField = 0; segmentIndexInField < templateSegments.length; segmentIndexInField += 1) {
|
|
396
|
-
const segmentToken = templateSegments[segmentIndexInField];
|
|
397
|
-
if (rcsVarTestRegex.test(segmentToken)) {
|
|
398
|
-
if (`${segmentToken}_${segmentIndexInField}` === varSegmentCompositeId) {
|
|
399
|
-
return offset + varOrdinal;
|
|
400
|
-
}
|
|
401
|
-
varOrdinal += 1;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return null;
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const resolveTemplateWithMap = (str = '', slotOffset = 0) => {
|
|
415
|
+
const resolveTemplateWithMap = (str = '') => {
|
|
408
416
|
if (!str) return '';
|
|
409
|
-
const arr =
|
|
410
|
-
let varOrdinal = 0;
|
|
417
|
+
const arr = splitTemplateVarString(str);
|
|
411
418
|
return arr.map((elem) => {
|
|
412
419
|
if (rcsVarTestRegex.test(elem)) {
|
|
413
420
|
const key = getVarNameFromToken(elem);
|
|
414
|
-
const
|
|
415
|
-
varOrdinal += 1;
|
|
416
|
-
const v = resolveCardVarMappedSlotValue(
|
|
417
|
-
cardVarMapped,
|
|
418
|
-
key,
|
|
419
|
-
globalSlot,
|
|
420
|
-
isEditLike,
|
|
421
|
-
);
|
|
421
|
+
const v = cardVarMapped?.[key];
|
|
422
422
|
if (isNil(v) || String(v)?.trim?.() === '') return elem;
|
|
423
423
|
return String(v);
|
|
424
424
|
}
|
|
@@ -426,154 +426,107 @@ export const Rcs = (props) => {
|
|
|
426
426
|
}).join('');
|
|
427
427
|
};
|
|
428
428
|
|
|
429
|
-
/**
|
|
430
|
-
* Content for TestAndPreviewSlidebox — apply cardVarMapped whenever the slot editor is shown
|
|
431
|
-
* (VarSegmentMessageEditor: isEditFlow || !isFullMode). Full-mode create without edit uses plain
|
|
432
|
-
* TextArea only — no mapping. Full-mode + edit uses slots; !isFullMode alone is not enough.
|
|
433
|
-
*/
|
|
434
|
-
const getTemplateContent = useCallback(() => {
|
|
435
|
-
const isMediaTypeImage = templateMediaType === RCS_MEDIA_TYPES.IMAGE;
|
|
436
|
-
const isMediaTypeVideo = templateMediaType === RCS_MEDIA_TYPES.VIDEO;
|
|
437
|
-
const isMediaTypeText = templateMediaType === RCS_MEDIA_TYPES.NONE;
|
|
438
|
-
|
|
439
|
-
const isSlotMappingMode = isEditFlow || !isFullMode;
|
|
440
|
-
const titleVarCountForResolve = isMediaTypeText
|
|
441
|
-
? 0
|
|
442
|
-
: ((templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []).length);
|
|
443
|
-
const resolvedTitle = isMediaTypeText
|
|
444
|
-
? ''
|
|
445
|
-
: (isSlotMappingMode ? resolveTemplateWithMap(templateTitle, 0) : templateTitle);
|
|
446
|
-
const resolvedDesc = isSlotMappingMode
|
|
447
|
-
? resolveTemplateWithMap(templateDesc, titleVarCountForResolve)
|
|
448
|
-
: templateDesc;
|
|
449
|
-
|
|
450
|
-
const mediaPreview = {};
|
|
451
|
-
if (isMediaTypeImage && rcsImageSrc) {
|
|
452
|
-
mediaPreview.rcsImageSrc = rcsImageSrc;
|
|
453
|
-
}
|
|
454
|
-
if (isMediaTypeVideo && !isMediaTypeText) {
|
|
455
|
-
if (rcsThumbnailSrc) {
|
|
456
|
-
mediaPreview.rcsVideoSrc = rcsThumbnailSrc;
|
|
457
|
-
} else if (rcsVideoSrc?.videoSrc) {
|
|
458
|
-
mediaPreview.rcsVideoSrc = rcsVideoSrc.videoSrc;
|
|
459
|
-
}
|
|
460
|
-
if (rcsThumbnailSrc) {
|
|
461
|
-
mediaPreview.rcsThumbnailSrc = rcsThumbnailSrc;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const contentObj = {
|
|
466
|
-
templateHeader: resolvedTitle,
|
|
467
|
-
templateMessage: resolvedDesc,
|
|
468
|
-
...mediaPreview,
|
|
469
|
-
...(suggestions.length > 0 && {
|
|
470
|
-
suggestions: suggestions,
|
|
471
|
-
}),
|
|
472
|
-
};
|
|
473
429
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
if (isFullMode || isEditFlow) return;
|
|
432
|
+
const tokens = [
|
|
433
|
+
...(templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []),
|
|
434
|
+
...(templateDesc ? (templateDesc.match(rcsVarRegex) || []) : []),
|
|
435
|
+
];
|
|
436
|
+
if (!tokens.length) return;
|
|
437
|
+
setCardVarMapped((prev) => {
|
|
438
|
+
const next = { ...(prev || {}) };
|
|
439
|
+
let changed = false;
|
|
440
|
+
tokens.forEach((t) => {
|
|
441
|
+
const name = getVarNameFromToken(t);
|
|
442
|
+
if (name && !Object.prototype.hasOwnProperty.call(next, name)) {
|
|
443
|
+
next[name] = '';
|
|
444
|
+
changed = true;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
return changed ? next : prev;
|
|
448
|
+
});
|
|
449
|
+
}, [isFullMode, templateTitle, templateDesc]);
|
|
488
450
|
|
|
489
|
-
const testAndPreviewContent = useMemo(() => getTemplateContent(), [getTemplateContent]);
|
|
490
451
|
|
|
452
|
+
const RcsLabel = styled.div`
|
|
453
|
+
display: flex;
|
|
454
|
+
margin-top: 20px;
|
|
455
|
+
`;
|
|
491
456
|
const paramObj = params || {};
|
|
492
457
|
useEffect(() => {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}, [paramObj.id
|
|
458
|
+
const { id } = paramObj;
|
|
459
|
+
if (id && isFullMode) {
|
|
460
|
+
setSpin(true);
|
|
461
|
+
actions.getTemplateDetails(id, setSpin);
|
|
462
|
+
}
|
|
463
|
+
return () => {
|
|
464
|
+
actions.clearEditResponse();
|
|
465
|
+
};
|
|
466
|
+
}, [paramObj.id]);
|
|
502
467
|
|
|
503
468
|
useEffect(() => {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
const nextVarMap = {};
|
|
515
|
-
let varOrdinal = 0;
|
|
516
|
-
arr.forEach((elem, idx) => {
|
|
517
|
-
// RCS placeholders are alphanumeric/underscore (e.g. {{user_name}}), not numeric-only
|
|
518
|
-
if (rcsVarTestRegex.test(elem)) {
|
|
519
|
-
const id = `${elem}_${idx}`;
|
|
520
|
-
const varName = getVarNameFromToken(elem);
|
|
521
|
-
const globalSlot = slotOffset + varOrdinal;
|
|
522
|
-
varOrdinal += 1;
|
|
523
|
-
const mappedValue = resolveCardVarMappedSlotValue(
|
|
524
|
-
cardVarMapped,
|
|
525
|
-
varName,
|
|
526
|
-
globalSlot,
|
|
527
|
-
isEditLike,
|
|
528
|
-
);
|
|
529
|
-
nextVarMap[id] = mappedValue;
|
|
469
|
+
if (!(isEditFlow || !isFullMode)) return;
|
|
470
|
+
|
|
471
|
+
const initField = (targetString, currentVarMap, setVarMap, setUpdated) => {
|
|
472
|
+
const arr = splitTemplateVarString(targetString);
|
|
473
|
+
if (!arr?.length) {
|
|
474
|
+
setVarMap({});
|
|
475
|
+
setUpdated([]);
|
|
476
|
+
return;
|
|
530
477
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
478
|
+
const nextVarMap = {};
|
|
479
|
+
const nextUpdated = [...arr];
|
|
480
|
+
arr.forEach((elem, idx) => {
|
|
481
|
+
// RCS placeholders are alphanumeric/underscore (e.g. {{user_name}}), not numeric-only
|
|
482
|
+
if (rcsVarTestRegex.test(elem)) {
|
|
483
|
+
const id = `${elem}_${idx}`;
|
|
484
|
+
const varName = getVarNameFromToken(elem);
|
|
485
|
+
const mappedValue = (cardVarMapped?.[varName] ?? '').toString();
|
|
486
|
+
nextVarMap[id] = mappedValue;
|
|
487
|
+
if (mappedValue !== '') {
|
|
488
|
+
nextUpdated[idx] = mappedValue;
|
|
489
|
+
} else {
|
|
490
|
+
nextUpdated[idx] = elem;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
setVarMap(nextVarMap);
|
|
495
|
+
setUpdated(nextUpdated);
|
|
496
|
+
};
|
|
538
497
|
|
|
539
|
-
|
|
540
|
-
|
|
498
|
+
initField(templateTitle, titleVarMappedData, setTitleVarMappedData, setUpdatedTitleData);
|
|
499
|
+
initField(templateDesc, descVarMappedData, setDescVarMappedData, setUpdatedDescData);
|
|
500
|
+
}, [templateTitle, templateDesc, cardVarMapped, isEditFlow, isFullMode]);
|
|
501
|
+
|
|
502
|
+
useEffect(() => {
|
|
503
|
+
if(!isEditFlow && isFullMode){
|
|
541
504
|
setRcsVideoSrc({});
|
|
542
505
|
updateRcsImageSrc('');
|
|
543
506
|
setUpdateRcsImageSrc('');
|
|
544
507
|
updateRcsThumbnailSrc('');
|
|
545
508
|
setAssetList({});
|
|
546
|
-
|
|
547
|
-
|
|
509
|
+
}
|
|
510
|
+
}, [templateMediaType]);
|
|
548
511
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
?? cardContentFirst?.status
|
|
554
|
-
?? cardContentFirst?.approvalStatus
|
|
555
|
-
?? '';
|
|
556
|
-
const status = typeof raw === 'string' ? raw.trim() : String(raw);
|
|
557
|
-
const n = status.toLowerCase();
|
|
558
|
-
switch (n) {
|
|
559
|
-
case 'approved':
|
|
512
|
+
const templateStatusHelper = (details) => {
|
|
513
|
+
const status = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].Status', '');
|
|
514
|
+
switch (status) {
|
|
515
|
+
case RCS_STATUSES.approved:
|
|
560
516
|
setTemplateStatus(RCS_STATUSES.approved);
|
|
561
517
|
break;
|
|
562
|
-
case
|
|
518
|
+
case RCS_STATUSES.pending:
|
|
563
519
|
setTemplateStatus(RCS_STATUSES.pending);
|
|
564
520
|
break;
|
|
565
|
-
case
|
|
521
|
+
case RCS_STATUSES.awaitingApproval:
|
|
566
522
|
setTemplateStatus(RCS_STATUSES.awaitingApproval);
|
|
567
523
|
break;
|
|
568
|
-
case
|
|
524
|
+
case RCS_STATUSES.unavailable:
|
|
569
525
|
setTemplateStatus(RCS_STATUSES.unavailable);
|
|
570
526
|
break;
|
|
571
|
-
case
|
|
527
|
+
case RCS_STATUSES.rejected:
|
|
572
528
|
setTemplateStatus(RCS_STATUSES.rejected);
|
|
573
529
|
break;
|
|
574
|
-
case 'created':
|
|
575
|
-
setTemplateStatus(RCS_STATUSES.created);
|
|
576
|
-
break;
|
|
577
530
|
default:
|
|
578
531
|
setTemplateStatus(status);
|
|
579
532
|
break;
|
|
@@ -584,6 +537,7 @@ export const Rcs = (props) => {
|
|
|
584
537
|
if (mediaType) {
|
|
585
538
|
setTemplateMediaType(mediaType);
|
|
586
539
|
}
|
|
540
|
+
const tempOrientation = cardSettings.cardOrientation;
|
|
587
541
|
const tempAlignment = cardSettings.mediaAlignment;
|
|
588
542
|
const tempHeight = mediaData.height;
|
|
589
543
|
|
|
@@ -626,197 +580,44 @@ export const Rcs = (props) => {
|
|
|
626
580
|
};
|
|
627
581
|
|
|
628
582
|
useEffect(() => {
|
|
629
|
-
const details =
|
|
583
|
+
const details = isFullMode ? rcsData?.templateDetails : templateData;
|
|
630
584
|
if (details && Object.keys(details).length > 0) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
'versions.base.content.RCS.rcsContent.cardContent[0]',
|
|
635
|
-
);
|
|
636
|
-
const cardFromTop = get(details, 'rcsContent.cardContent[0]');
|
|
637
|
-
const card0 = { ...(cardFromTop || {}), ...(cardFromVersions || {}) };
|
|
638
|
-
const cardVarMappedFromCardContent =
|
|
639
|
-
card0?.cardVarMapped != null && typeof card0.cardVarMapped === 'object'
|
|
640
|
-
? card0.cardVarMapped
|
|
641
|
-
: {};
|
|
642
|
-
const cardVarMappedFromRootMirror =
|
|
643
|
-
details?.rcsCardVarMapped != null && typeof details.rcsCardVarMapped === 'object'
|
|
644
|
-
? details.rcsCardVarMapped
|
|
645
|
-
: {};
|
|
646
|
-
// Root mirror from getCreativesData / getFormData — campaigns often preserve flat fields when
|
|
647
|
-
// nested versions.cardContent[0].cardVarMapped is dropped on reload.
|
|
648
|
-
const mergedCardVarMappedFromPayload = {
|
|
649
|
-
...cardVarMappedFromRootMirror,
|
|
650
|
-
...cardVarMappedFromCardContent,
|
|
651
|
-
};
|
|
652
|
-
const loadedTitleForMap = card0?.title != null ? String(card0.title) : '';
|
|
653
|
-
const loadedDescForMap = card0?.description != null ? String(card0.description) : '';
|
|
654
|
-
const hydratedCardVarPayloadSignature = `${loadedTitleForMap}\u0000${loadedDescForMap}\u0000${JSON.stringify(
|
|
655
|
-
Object.keys(mergedCardVarMappedFromPayload)
|
|
656
|
-
.sort()
|
|
657
|
-
.reduce((accumulator, mapKey) => {
|
|
658
|
-
accumulator[mapKey] = mergedCardVarMappedFromPayload[mapKey];
|
|
659
|
-
return accumulator;
|
|
660
|
-
}, {}),
|
|
661
|
-
)}`;
|
|
662
|
-
if (lastHydratedRcsCardVarSignatureRef.current !== hydratedCardVarPayloadSignature) {
|
|
663
|
-
lastHydratedRcsCardVarSignatureRef.current = hydratedCardVarPayloadSignature;
|
|
664
|
-
setRcsVarSegmentEditorRemountKey((previousKey) => previousKey + 1);
|
|
585
|
+
if (!isFullMode) {
|
|
586
|
+
const tempCardVarMapped = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped', {});
|
|
587
|
+
setCardVarMapped(tempCardVarMapped);
|
|
665
588
|
}
|
|
666
|
-
const
|
|
667
|
-
...(loadedTitleForMap ? loadedTitleForMap.match(rcsVarRegex) ?? [] : []),
|
|
668
|
-
...(loadedDescForMap ? loadedDescForMap.match(rcsVarRegex) ?? [] : []),
|
|
669
|
-
];
|
|
670
|
-
const orderedTagNamesForMap = tokenListForMap.map((token) => getVarNameFromToken(token)).filter(Boolean);
|
|
671
|
-
// Full-mode library/API payloads need normalize for legacy slot shapes. Campaign round-trip from
|
|
672
|
-
// getFormData already stores TagList values as {{TagName}}; normalize can strip or remap them.
|
|
673
|
-
const cardVarMappedBeforeCoalesce = isFullMode
|
|
674
|
-
? normalizeCardVarMapped(mergedCardVarMappedFromPayload, orderedTagNamesForMap)
|
|
675
|
-
: { ...mergedCardVarMappedFromPayload };
|
|
676
|
-
const cardVarMappedAfterCoalesce = coalesceCardVarMappedToTemplate(
|
|
677
|
-
cardVarMappedBeforeCoalesce,
|
|
678
|
-
loadedTitleForMap,
|
|
679
|
-
loadedDescForMap,
|
|
680
|
-
rcsVarRegex,
|
|
681
|
-
);
|
|
682
|
-
const cardVarMappedAfterNumericSlotSync = !isFullMode
|
|
683
|
-
? syncCardVarMappedSemanticsFromSlots(
|
|
684
|
-
cardVarMappedAfterCoalesce,
|
|
685
|
-
loadedTitleForMap,
|
|
686
|
-
loadedDescForMap,
|
|
687
|
-
rcsVarRegex,
|
|
688
|
-
)
|
|
689
|
-
: cardVarMappedAfterCoalesce;
|
|
690
|
-
const hydratedCardVarMappedResult = { ...cardVarMappedAfterNumericSlotSync };
|
|
691
|
-
// Pre-populate variable/tag mappings while opening an existing template in edit flows
|
|
692
|
-
setCardVarMapped((previousVarMapState) => {
|
|
693
|
-
const previousVarMap = previousVarMapState ?? {};
|
|
694
|
-
if (previousVarMap === hydratedCardVarMappedResult) return previousVarMapState;
|
|
695
|
-
const previousVarMapKeys = Object.keys(previousVarMap);
|
|
696
|
-
const nextVarMapKeys = Object.keys(hydratedCardVarMappedResult);
|
|
697
|
-
if (previousVarMapKeys.length === nextVarMapKeys.length) {
|
|
698
|
-
const allSlotValuesMatchPrevious = previousVarMapKeys.every(
|
|
699
|
-
(key) => previousVarMap[key] === hydratedCardVarMappedResult[key],
|
|
700
|
-
);
|
|
701
|
-
if (allSlotValuesMatchPrevious) return previousVarMapState;
|
|
702
|
-
}
|
|
703
|
-
return hydratedCardVarMappedResult;
|
|
704
|
-
});
|
|
705
|
-
const mediaType =
|
|
706
|
-
card0.mediaType
|
|
707
|
-
|| get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].mediaType', '');
|
|
589
|
+
const mediaType = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].mediaType', '');
|
|
708
590
|
if (mediaType === RCS_MEDIA_TYPES.NONE) {
|
|
709
591
|
setTemplateType(contentType.text_message);
|
|
710
592
|
} else {
|
|
711
593
|
setTemplateType(contentType.rich_card);
|
|
712
594
|
}
|
|
713
595
|
setEditFlow(true);
|
|
714
|
-
setTemplateName(details
|
|
715
|
-
const loadedTitle =
|
|
716
|
-
const loadedDesc =
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
isFullMode,
|
|
721
|
-
cardVarMappedAfterHydration: hydratedCardVarMappedResult,
|
|
722
|
-
rcsVarRegex,
|
|
723
|
-
});
|
|
596
|
+
setTemplateName(details.name || '');
|
|
597
|
+
const loadedTitle = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].title', '');
|
|
598
|
+
const loadedDesc = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].description', '');
|
|
599
|
+
const loadedMap = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].cardVarMapped', {});
|
|
600
|
+
const normalizedTitle = (!isFullMode && !isEmpty(loadedMap)) ? getUnmappedDesc(loadedTitle, loadedMap) : loadedTitle;
|
|
601
|
+
const normalizedDesc = (!isFullMode && !isEmpty(loadedMap)) ? getUnmappedDesc(loadedDesc, loadedMap) : loadedDesc;
|
|
724
602
|
setTemplateTitle(normalizedTitle);
|
|
725
603
|
setTemplateDesc(normalizedDesc);
|
|
726
|
-
setSuggestions(
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
);
|
|
731
|
-
const cardForStatus = {
|
|
732
|
-
...card0,
|
|
733
|
-
Status:
|
|
734
|
-
card0.Status
|
|
735
|
-
?? card0.status
|
|
736
|
-
?? card0.approvalStatus
|
|
737
|
-
?? get(details, 'templateStatus')
|
|
738
|
-
?? get(details, 'approvalStatus')
|
|
739
|
-
?? get(details, 'creativeStatus')
|
|
740
|
-
?? get(details, 'versions.base.content.RCS.templateApprovalStatus')
|
|
741
|
-
?? '',
|
|
742
|
-
};
|
|
743
|
-
templateStatusHelper(cardForStatus);
|
|
744
|
-
const mediaData =
|
|
745
|
-
card0.media != null && card0.media !== ''
|
|
746
|
-
? card0.media
|
|
747
|
-
: get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].media', '');
|
|
748
|
-
const cardSettings =
|
|
749
|
-
get(details, 'versions.base.content.RCS.rcsContent.cardSettings', '')
|
|
750
|
-
|| get(details, 'rcsContent.cardSettings', '');
|
|
604
|
+
setSuggestions(get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].suggestions', []));
|
|
605
|
+
templateStatusHelper(details);
|
|
606
|
+
const mediaData = get(details, 'versions.base.content.RCS.rcsContent.cardContent[0].media', '');
|
|
607
|
+
const cardSettings = get(details, 'versions.base.content.RCS.rcsContent.cardSettings', '');
|
|
751
608
|
setMediaData(mediaData, mediaType, cardSettings);
|
|
752
|
-
|
|
753
|
-
const smsFallbackContent = mergeRcsSmsFallBackContentFromDetails(details);
|
|
754
|
-
const base = get(smsFallbackContent, 'versions.base', {});
|
|
755
|
-
const updatedEditor = base['updated-sms-editor'] ?? base['sms-editor'];
|
|
756
|
-
const smsEditor = base['sms-editor'];
|
|
757
|
-
const fromNested = Array.isArray(updatedEditor)
|
|
758
|
-
? updatedEditor.join('')
|
|
759
|
-
: (typeof updatedEditor === 'string' ? updatedEditor : (smsEditor || ''));
|
|
760
|
-
const fallbackMessage = smsFallbackContent.smsContent
|
|
761
|
-
|| smsFallbackContent.smsTemplateContent
|
|
762
|
-
|| smsFallbackContent.message
|
|
763
|
-
|| fromNested
|
|
764
|
-
|| '';
|
|
765
|
-
const varMappedFromPayload = smsFallbackContent[RCS_SMS_FALLBACK_VAR_MAPPED_PROP] || {};
|
|
766
|
-
const hasVarMapped = Object.keys(varMappedFromPayload).length > 0;
|
|
767
|
-
const hasFallbackPayload =
|
|
768
|
-
smsFallbackContent
|
|
769
|
-
&& Object.keys(smsFallbackContent).length > 0
|
|
770
|
-
&& (
|
|
771
|
-
!!smsFallbackContent.smsTemplateName
|
|
772
|
-
|| !!fallbackMessage
|
|
773
|
-
|| hasVarMapped
|
|
774
|
-
);
|
|
775
|
-
if (hasFallbackPayload) {
|
|
776
|
-
if (!fallbackMessage && !hasVarMapped && process.env.NODE_ENV !== 'production') {
|
|
777
|
-
console.warn('[RCS SMS Fallback] No message text found in API response. Inspect shape:', smsFallbackContent);
|
|
778
|
-
}
|
|
779
|
-
const unicodeFromApi =
|
|
780
|
-
typeof smsFallbackContent.unicodeValidity === 'boolean'
|
|
781
|
-
? smsFallbackContent.unicodeValidity
|
|
782
|
-
: (typeof base['unicode-validity'] === 'boolean' ? base['unicode-validity'] : true);
|
|
783
|
-
const nextSmsState = {
|
|
784
|
-
templateName: smsFallbackContent.smsTemplateName || '',
|
|
785
|
-
content: fallbackMessage,
|
|
786
|
-
templateContent: fallbackMessage,
|
|
787
|
-
unicodeValidity: unicodeFromApi,
|
|
788
|
-
...(hasVarMapped && { rcsSmsFallbackVarMapped: varMappedFromPayload }),
|
|
789
|
-
};
|
|
790
|
-
const hydrationKey = JSON.stringify({
|
|
791
|
-
creativeKey: details._id || details.name || details.creativeName || '',
|
|
792
|
-
templateName: nextSmsState.templateName,
|
|
793
|
-
content: nextSmsState.content,
|
|
794
|
-
unicodeValidity: nextSmsState.unicodeValidity,
|
|
795
|
-
varMapped: nextSmsState.rcsSmsFallbackVarMapped || {},
|
|
796
|
-
});
|
|
797
|
-
if (
|
|
798
|
-
isFullMode
|
|
799
|
-
|| lastSmsFallbackHydrationKeyRef.current !== hydrationKey
|
|
800
|
-
) {
|
|
801
|
-
lastSmsFallbackHydrationKeyRef.current = hydrationKey;
|
|
802
|
-
setSmsFallbackData(nextSmsState);
|
|
803
|
-
}
|
|
804
|
-
} else if (isFullMode || lastSmsFallbackHydrationKeyRef.current !== '__EMPTY__') {
|
|
805
|
-
lastSmsFallbackHydrationKeyRef.current = '__EMPTY__';
|
|
806
|
-
setSmsFallbackData(null);
|
|
807
|
-
}
|
|
808
609
|
}
|
|
809
|
-
}, [
|
|
610
|
+
}, [rcsData, templateData, isFullMode, isEditFlow]);
|
|
611
|
+
|
|
810
612
|
|
|
811
613
|
useEffect(() => {
|
|
812
614
|
if (templateType === contentType.text_message) {
|
|
813
615
|
setTemplateMediaType(RCS_MEDIA_TYPES.NONE);
|
|
814
|
-
|
|
815
|
-
|
|
616
|
+
setTemplateTitle('');
|
|
617
|
+
setTemplateTitleError('');
|
|
816
618
|
if (!isEditFlow && isFullMode) {
|
|
817
|
-
setTemplateTitle('');
|
|
818
|
-
setTemplateTitleError('');
|
|
819
619
|
setUpdateRcsImageSrc('');
|
|
620
|
+
setUpdateRcsVideoSrc({});
|
|
820
621
|
setRcsVideoSrc({});
|
|
821
622
|
setSelectedDimension(RCS_IMAGE_DIMENSIONS.MEDIUM_HEIGHT.type);
|
|
822
623
|
}
|
|
@@ -843,8 +644,7 @@ export const Rcs = (props) => {
|
|
|
843
644
|
if (!showDltContainer) {
|
|
844
645
|
const { type, module } = location.query || {};
|
|
845
646
|
const isEmbedded = type === EMBEDDED;
|
|
846
|
-
|
|
847
|
-
const context = isEmbedded ? module : 'outbound';
|
|
647
|
+
const context = isEmbedded ? module : DEFAULT;
|
|
848
648
|
const embedded = isEmbedded ? type : FULL;
|
|
849
649
|
const query = {
|
|
850
650
|
layout: SMS,
|
|
@@ -855,9 +655,9 @@ export const Rcs = (props) => {
|
|
|
855
655
|
if (getDefaultTags) {
|
|
856
656
|
query.context = getDefaultTags;
|
|
857
657
|
}
|
|
858
|
-
|
|
658
|
+
globalActions.fetchSchemaForEntity(query);
|
|
859
659
|
}
|
|
860
|
-
}, [showDltContainer
|
|
660
|
+
}, [showDltContainer]);
|
|
861
661
|
|
|
862
662
|
useEffect(() => {
|
|
863
663
|
let tag = get(metaEntities, `tags.standard`, []);
|
|
@@ -880,78 +680,39 @@ export const Rcs = (props) => {
|
|
|
880
680
|
context,
|
|
881
681
|
embedded,
|
|
882
682
|
};
|
|
883
|
-
|
|
884
|
-
query.context = getDefaultTags;
|
|
885
|
-
}
|
|
886
|
-
fetchTagSchemaIfNewQuery(query);
|
|
683
|
+
globalActions.fetchSchemaForEntity(query);
|
|
887
684
|
};
|
|
888
685
|
|
|
889
|
-
const
|
|
890
|
-
if (!templateStr || !numericVarName || !tagName) return templateStr;
|
|
891
|
-
const re = buildRcsNumericMustachePlaceholderRegex(numericVarName);
|
|
892
|
-
return templateStr.replace(re, `{{${tagName}}}`);
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
const onTagSelect = (data, areaId, field) => {
|
|
686
|
+
const onTagSelect = (data, areaId) => {
|
|
896
687
|
if (!areaId) return;
|
|
897
688
|
const sep = areaId.lastIndexOf('_');
|
|
898
689
|
if (sep === -1) return;
|
|
899
|
-
const
|
|
900
|
-
if (
|
|
690
|
+
const numId = Number(areaId.slice(sep + 1));
|
|
691
|
+
if (isNaN(numId)) return;
|
|
901
692
|
const token = areaId.slice(0, sep);
|
|
902
693
|
const variableName = getVarNameFromToken(token);
|
|
903
694
|
if (!variableName) return;
|
|
904
|
-
const isNumericSlot = RCS_NUMERIC_VAR_NAME_REGEX.test(String(variableName));
|
|
905
|
-
const fieldStr = field === RCS_TAG_AREA_FIELD_TITLE ? templateTitle : templateDesc;
|
|
906
|
-
const fieldType = field === RCS_TAG_AREA_FIELD_TITLE ? TITLE_TEXT : MESSAGE_TEXT;
|
|
907
|
-
const globalSlotForArea = getGlobalSlotIndexForRcsFieldId(areaId, fieldStr, fieldType);
|
|
908
|
-
|
|
909
695
|
setCardVarMapped((prev) => {
|
|
910
696
|
const base = (prev?.[variableName] ?? '').toString();
|
|
911
697
|
const nextVal = `${base}{{${data}}}`;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
} else {
|
|
917
|
-
// Use the global slot index key only — writing by semantic name (variableName)
|
|
918
|
-
// would contaminate any other field that shares the same var name (e.g. {{gt}}
|
|
919
|
-
// in both title and description), causing the label value to bleed across fields.
|
|
920
|
-
if (globalSlotForArea !== null && globalSlotForArea !== undefined) {
|
|
921
|
-
next[String(globalSlotForArea + 1)] = nextVal;
|
|
922
|
-
// Same reasoning as handleRcsVarChange: delete the semantic key so
|
|
923
|
-
// resolveCardVarMappedSlotValue uses only the numeric slot and the
|
|
924
|
-
// value cannot bleed across fields that share the same var name.
|
|
925
|
-
delete next[variableName];
|
|
926
|
-
} else {
|
|
927
|
-
next[variableName] = nextVal;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
return next;
|
|
698
|
+
return {
|
|
699
|
+
...(prev || {}),
|
|
700
|
+
[variableName]: nextVal,
|
|
701
|
+
};
|
|
931
702
|
});
|
|
932
|
-
|
|
933
|
-
if (isNumericSlot && (field === RCS_TAG_AREA_FIELD_TITLE || field === RCS_TAG_AREA_FIELD_DESC)) {
|
|
934
|
-
if (field === RCS_TAG_AREA_FIELD_TITLE) {
|
|
935
|
-
setTemplateTitle((prev) => {
|
|
936
|
-
const nextStr = replaceNumericPlaceholderWithTagInTemplate(prev || '', variableName, data);
|
|
937
|
-
if (nextStr === prev) return prev;
|
|
938
|
-
setTemplateTitleError(variableErrorHandling(nextStr));
|
|
939
|
-
return nextStr;
|
|
940
|
-
});
|
|
941
|
-
} else {
|
|
942
|
-
setTemplateDesc((prev) => {
|
|
943
|
-
const nextStr = replaceNumericPlaceholderWithTagInTemplate(prev || '', variableName, data);
|
|
944
|
-
if (nextStr === prev) return prev;
|
|
945
|
-
setTemplateDescError(variableErrorHandling(nextStr));
|
|
946
|
-
return nextStr;
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
703
|
};
|
|
951
704
|
|
|
952
|
-
const onTitleTagSelect = (
|
|
705
|
+
const onTitleTagSelect = (data) => onTagSelect(data, titleTextAreaId);
|
|
706
|
+
|
|
707
|
+
const onDescTagSelect = (data) => onTagSelect(data, descTextAreaId);
|
|
708
|
+
|
|
709
|
+
const onTagSelectFallback = (data) => {
|
|
710
|
+
const tempMsg = `${fallbackMessage}{{${data}}}`;
|
|
711
|
+
const error = fallbackMessageErrorHandler(tempMsg);
|
|
712
|
+
setFallbackMessage(tempMsg);
|
|
713
|
+
setFallbackMessageError(error);
|
|
714
|
+
};
|
|
953
715
|
|
|
954
|
-
const onDescTagSelect = (tagName) => onTagSelect(tagName, descTextAreaId, RCS_TAG_AREA_FIELD_DESC);
|
|
955
716
|
|
|
956
717
|
//removing optout tag for rcs
|
|
957
718
|
const getRcsTags = () => {
|
|
@@ -964,14 +725,15 @@ export const Rcs = (props) => {
|
|
|
964
725
|
};
|
|
965
726
|
// tag Code end
|
|
966
727
|
|
|
967
|
-
const renderLabel = (value, desc) => {
|
|
728
|
+
const renderLabel = (value, showLabel, desc) => {
|
|
729
|
+
const isTemplateApproved = (templateStatus === RCS_STATUSES.approved);
|
|
968
730
|
return (
|
|
969
731
|
<>
|
|
970
|
-
<
|
|
732
|
+
<RcsLabel>
|
|
971
733
|
<CapHeading type="h4">{formatMessage(messages[value])}</CapHeading>
|
|
972
|
-
</
|
|
734
|
+
</RcsLabel>
|
|
973
735
|
{desc && (
|
|
974
|
-
<CapLabel type="label3"
|
|
736
|
+
<CapLabel type="label3" style={{ marginBottom: '17px' }}>
|
|
975
737
|
{formatMessage(messages[desc])}
|
|
976
738
|
</CapLabel>
|
|
977
739
|
)}
|
|
@@ -1057,6 +819,47 @@ export const Rcs = (props) => {
|
|
|
1057
819
|
setTemplateDescError(error);
|
|
1058
820
|
};
|
|
1059
821
|
|
|
822
|
+
|
|
823
|
+
const templateDescErrorHandler = (value) => {
|
|
824
|
+
let errorMessage = false;
|
|
825
|
+
const { isBraceError } = validateTags({
|
|
826
|
+
content: value,
|
|
827
|
+
tagsParam: tags,
|
|
828
|
+
location,
|
|
829
|
+
tagModule: getDefaultTags,
|
|
830
|
+
isFullMode,
|
|
831
|
+
}) || {};
|
|
832
|
+
|
|
833
|
+
const maxLength = templateType === contentType.text_message
|
|
834
|
+
? RCS_TEXT_MESSAGE_MAX_LENGTH
|
|
835
|
+
: RCS_RICH_CARD_MAX_LENGTH;
|
|
836
|
+
|
|
837
|
+
if (value === '' && isMediaTypeText) {
|
|
838
|
+
errorMessage = formatMessage(messages.emptyTemplateDescErrorMessage);
|
|
839
|
+
} else if (value?.length > maxLength) {
|
|
840
|
+
errorMessage = formatMessage(messages.templateMessageLengthError);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (isBraceError) {
|
|
844
|
+
errorMessage = formatMessage(globalMessages.unbalanacedCurlyBraces);
|
|
845
|
+
}
|
|
846
|
+
return errorMessage;
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
const onFallbackMessageChange = ({ target: { value } }) => {
|
|
851
|
+
const error = fallbackMessageErrorHandler(value);
|
|
852
|
+
setFallbackMessage(value);
|
|
853
|
+
setFallbackMessageError(error);
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
const fallbackMessageErrorHandler = (value) => {
|
|
857
|
+
if (value?.length > FALLBACK_MESSAGE_MAX_LENGTH) {
|
|
858
|
+
return formatMessage(messages.fallbackMsgLenError);
|
|
859
|
+
}
|
|
860
|
+
return false;
|
|
861
|
+
};
|
|
862
|
+
|
|
1060
863
|
// Check for forbidden characters: square brackets [] and single curly braces {}
|
|
1061
864
|
const forbiddenCharactersValidation = (value) => {
|
|
1062
865
|
if (!value) return false;
|
|
@@ -1107,36 +910,47 @@ export const Rcs = (props) => {
|
|
|
1107
910
|
}
|
|
1108
911
|
return false;
|
|
1109
912
|
};
|
|
913
|
+
|
|
914
|
+
const templateHeaderErrorHandler = (value) => {
|
|
915
|
+
let errorMessage = false;
|
|
916
|
+
if (value?.length > TEMPLATE_HEADER_MAX_LENGTH) {
|
|
917
|
+
errorMessage = formatMessage(messages.templateHeaderLengthError);
|
|
918
|
+
} else {
|
|
919
|
+
errorMessage = variableErrorHandling(value);
|
|
920
|
+
}
|
|
921
|
+
return errorMessage;
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
const templateMessageErrorHandler = (value) => {
|
|
926
|
+
let errorMessage = false;
|
|
927
|
+
if (value === '') {
|
|
928
|
+
errorMessage = formatMessage(messages.emptyTemplateMessageErrorMessage);
|
|
929
|
+
} else if (
|
|
930
|
+
value?.length
|
|
931
|
+
> TEMPLATE_MESSAGE_MAX_LENGTH
|
|
932
|
+
) {
|
|
933
|
+
errorMessage = formatMessage(messages.templateMessageLengthError);
|
|
934
|
+
} else {
|
|
935
|
+
errorMessage = variableErrorHandling(value);
|
|
936
|
+
}
|
|
937
|
+
return errorMessage;
|
|
938
|
+
};
|
|
939
|
+
|
|
1110
940
|
|
|
1111
941
|
const onMessageAddVar = () => {
|
|
1112
|
-
onAddVar(templateDesc);
|
|
942
|
+
onAddVar(MESSAGE_TEXT, templateDesc, rcsVarRegex);
|
|
1113
943
|
};
|
|
1114
944
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
* (duplicate numbers would share a cardVarMapped key and bleed values across fields).
|
|
1120
|
-
*/
|
|
1121
|
-
const getNextRcsNumericVarNumber = (titleStr, descStr) => {
|
|
1122
|
-
const allExistingVars = [
|
|
1123
|
-
...(titleStr.match(RCS_NUMERIC_VAR_TOKEN_REGEX) || []),
|
|
1124
|
-
...(descStr.match(RCS_NUMERIC_VAR_TOKEN_REGEX) || []),
|
|
1125
|
-
];
|
|
1126
|
-
const existingNumbers = allExistingVars.flatMap(v => {
|
|
1127
|
-
const m = v.match(/\d+/);
|
|
1128
|
-
return m ? [parseInt(m[0], 10)] : [];
|
|
1129
|
-
});
|
|
945
|
+
const onAddVar = (type, messageContent, regex) => {
|
|
946
|
+
// Always append the next variable at the end, like WhatsApp
|
|
947
|
+
const existingVars = messageContent.match(/\{\{(\d+)\}\}/g) || [];
|
|
948
|
+
const existingNumbers = existingVars.map(v => parseInt(v.match(/\d+/)[0], 10));
|
|
1130
949
|
let nextNumber = 1;
|
|
1131
950
|
while (existingNumbers.includes(nextNumber)) {
|
|
1132
951
|
nextNumber++;
|
|
1133
952
|
}
|
|
1134
|
-
|
|
1135
|
-
};
|
|
1136
|
-
|
|
1137
|
-
const onAddVar = (messageContent) => {
|
|
1138
|
-
const nextNumber = getNextRcsNumericVarNumber(templateTitle, messageContent);
|
|
1139
|
-
if (nextNumber === null) {
|
|
953
|
+
if (nextNumber > 19) {
|
|
1140
954
|
return;
|
|
1141
955
|
}
|
|
1142
956
|
const nextVar = `{{${nextNumber}}}`;
|
|
@@ -1148,13 +962,15 @@ const onAddVar = (messageContent) => {
|
|
|
1148
962
|
};
|
|
1149
963
|
|
|
1150
964
|
const onTitleAddVar = () => {
|
|
1151
|
-
//
|
|
1152
|
-
// duplicate a number already used in the description. Duplicate numeric
|
|
1153
|
-
// names would share the same cardVarMapped semantic key, causing the
|
|
1154
|
-
// description slot to reflect the title slot value and vice-versa.
|
|
965
|
+
// Always append the next variable at the end, like WhatsApp
|
|
1155
966
|
const messageContent = templateTitle;
|
|
1156
|
-
const
|
|
1157
|
-
|
|
967
|
+
const existingVars = messageContent.match(/\{\{(\d+)\}\}/g) || [];
|
|
968
|
+
const existingNumbers = existingVars.map(v => parseInt(v.match(/\d+/)[0], 10));
|
|
969
|
+
let nextNumber = 1;
|
|
970
|
+
while (existingNumbers.includes(nextNumber)) {
|
|
971
|
+
nextNumber++;
|
|
972
|
+
}
|
|
973
|
+
if (nextNumber > 19) {
|
|
1158
974
|
return;
|
|
1159
975
|
}
|
|
1160
976
|
const nextVar = `{{${nextNumber}}}`;
|
|
@@ -1166,6 +982,66 @@ const onTitleAddVar = () => {
|
|
|
1166
982
|
setTemplateTitleError(error);
|
|
1167
983
|
};
|
|
1168
984
|
|
|
985
|
+
|
|
986
|
+
const splitTemplateVarString = (str) => {
|
|
987
|
+
if (!str) return [];
|
|
988
|
+
const validVarArr = str.match(rcsVarRegex) || [];
|
|
989
|
+
const templateVarArray = [];
|
|
990
|
+
let content = str;
|
|
991
|
+
while (content?.length !== 0) {
|
|
992
|
+
const index = content.indexOf(validVarArr?.[0]);
|
|
993
|
+
if (index !== -1) {
|
|
994
|
+
templateVarArray.push(content.substring(0, index));
|
|
995
|
+
templateVarArray.push(validVarArr?.[0]);
|
|
996
|
+
content = content.substring(index + validVarArr?.[0]?.length, content?.length);
|
|
997
|
+
validVarArr?.shift();
|
|
998
|
+
} else {
|
|
999
|
+
templateVarArray.push(content);
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return templateVarArray.filter(Boolean);
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
const textAreaValue = (idValue, type) => {
|
|
1007
|
+
if (idValue >= 0) {
|
|
1008
|
+
const templateStr = type === TITLE_TEXT ? templateTitle : templateDesc;
|
|
1009
|
+
const templateArr = splitTemplateVarString(templateStr);
|
|
1010
|
+
const token = templateArr?.[idValue] || "";
|
|
1011
|
+
if (token && rcsVarTestRegex.test(token)) {
|
|
1012
|
+
const varName = getVarNameFromToken(token);
|
|
1013
|
+
return (cardVarMapped?.[varName] ?? '').toString();
|
|
1014
|
+
}
|
|
1015
|
+
return "";
|
|
1016
|
+
}
|
|
1017
|
+
return "";
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
const textAreaValueChange = (e, type) => {
|
|
1021
|
+
const value = e?.target?.value ?? '';
|
|
1022
|
+
const id = e?.target?.id || e?.currentTarget?.id || '';
|
|
1023
|
+
if (!id) return;
|
|
1024
|
+
const sep = id.lastIndexOf('_');
|
|
1025
|
+
if (sep === -1) return;
|
|
1026
|
+
const isInvalidValue = value?.trim() === "";
|
|
1027
|
+
const token = id.slice(0, sep);
|
|
1028
|
+
const variableName = getVarNameFromToken(token);
|
|
1029
|
+
|
|
1030
|
+
if (variableName) {
|
|
1031
|
+
setCardVarMapped((prev) => ({
|
|
1032
|
+
...prev,
|
|
1033
|
+
[variableName]: isInvalidValue ? "" : value,
|
|
1034
|
+
}));
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
const setTextAreaId = (e, type) => {
|
|
1039
|
+
const id = e?.target?.id || e?.currentTarget?.id || '';
|
|
1040
|
+
if (!id) return;
|
|
1041
|
+
if (type === TITLE_TEXT) setTitleTextAreaId(id);
|
|
1042
|
+
else setDescTextAreaId(id);
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1169
1045
|
const renderButtonComponent = () => {
|
|
1170
1046
|
return (
|
|
1171
1047
|
<>
|
|
@@ -1196,69 +1072,39 @@ const onTitleAddVar = () => {
|
|
|
1196
1072
|
);
|
|
1197
1073
|
};
|
|
1198
1074
|
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
() => getRcsValueMap(templateDesc, MESSAGE_TEXT),
|
|
1229
|
-
[templateDesc, templateTitle, cardVarMapped, isEditFlow, isFullMode],
|
|
1230
|
-
);
|
|
1231
|
-
|
|
1232
|
-
const handleRcsVarChange = (id, value, type) => {
|
|
1233
|
-
const sep = id.lastIndexOf('_');
|
|
1234
|
-
if (sep === -1) return;
|
|
1235
|
-
const token = id.slice(0, sep);
|
|
1236
|
-
const variableName = getVarNameFromToken(token);
|
|
1237
|
-
if (variableName === undefined || variableName === null || variableName === '') return;
|
|
1238
|
-
const isInvalidValue = value?.trim() === '';
|
|
1239
|
-
const coercedSlotValue = isInvalidValue ? '' : value;
|
|
1240
|
-
const fieldStr = type === TITLE_TEXT ? templateTitle : templateDesc;
|
|
1241
|
-
const globalSlot = getGlobalSlotIndexForRcsFieldId(id, fieldStr, type);
|
|
1242
|
-
setCardVarMapped((previousVarMap) => {
|
|
1243
|
-
const nextVarMap = { ...previousVarMap };
|
|
1244
|
-
if (globalSlot !== null && globalSlot !== undefined) {
|
|
1245
|
-
// Write by global slot index only — title and description can share the
|
|
1246
|
-
// same var name (e.g. {{gt}}), and writing by semantic name would cause
|
|
1247
|
-
// the description slot to resolve the title's value and vice-versa.
|
|
1248
|
-
const numericKey = String(globalSlot + 1);
|
|
1249
|
-
nextVarMap[numericKey] = coercedSlotValue;
|
|
1250
|
-
// Remove any stale semantic key so resolveCardVarMappedSlotValue never
|
|
1251
|
-
// falls back to it. Guard: when variableName is already the numeric slot
|
|
1252
|
-
// key (e.g. {{1}} at slot 0 → both equal "1"), skip the delete or it
|
|
1253
|
-
// would erase the value we just wrote.
|
|
1254
|
-
if (variableName !== numericKey) {
|
|
1255
|
-
delete nextVarMap[variableName];
|
|
1075
|
+
const renderedRCSEditMessage = (descArray, type) => {
|
|
1076
|
+
const renderArray = [];
|
|
1077
|
+
if (descArray?.length) {
|
|
1078
|
+
descArray.forEach((elem, index) => {
|
|
1079
|
+
if (rcsVarTestRegex.test(elem)) {
|
|
1080
|
+
// Variable input
|
|
1081
|
+
renderArray.push(
|
|
1082
|
+
<TextArea
|
|
1083
|
+
id={`${elem}_${index}`}
|
|
1084
|
+
key={`${elem}_${index}`}
|
|
1085
|
+
placeholder={`enter the value for ${elem}`}
|
|
1086
|
+
autosize={{ minRows: 1, maxRows: 3 }}
|
|
1087
|
+
onChange={e => textAreaValueChange(e, type)}
|
|
1088
|
+
value={textAreaValue(index, type)}
|
|
1089
|
+
onFocus={(e) => setTextAreaId(e, type)}
|
|
1090
|
+
/>
|
|
1091
|
+
);
|
|
1092
|
+
} else if (elem) {
|
|
1093
|
+
// Static text
|
|
1094
|
+
renderArray.push(
|
|
1095
|
+
<TextArea
|
|
1096
|
+
key={`static_${index}`}
|
|
1097
|
+
value={elem}
|
|
1098
|
+
autosize={{ minRows: 1, maxRows: 3 }}
|
|
1099
|
+
disabled
|
|
1100
|
+
className="rcs-edit-template-message-static-textarea"
|
|
1101
|
+
style={{ background: '#fafafa', color: '#888' }}
|
|
1102
|
+
/>
|
|
1103
|
+
);
|
|
1256
1104
|
}
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
return nextVarMap;
|
|
1261
|
-
});
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return renderArray;
|
|
1262
1108
|
};
|
|
1263
1109
|
|
|
1264
1110
|
const renderTextComponent = () => {
|
|
@@ -1302,19 +1148,10 @@ const onTitleAddVar = () => {
|
|
|
1302
1148
|
</>
|
|
1303
1149
|
}
|
|
1304
1150
|
/>
|
|
1151
|
+
<div className="rcs_text_area_wrapper">
|
|
1305
1152
|
{(isEditFlow || !isFullMode) ? (
|
|
1306
|
-
|
|
1307
|
-
key={`rcs-title-vars-${rcsVarSegmentEditorRemountKey}`}
|
|
1308
|
-
templateString={templateTitle}
|
|
1309
|
-
valueMap={titleVarSegmentValueMapById}
|
|
1310
|
-
onChange={(id, value) => handleRcsVarChange(id, value, TITLE_TEXT)}
|
|
1311
|
-
onFocus={(id) => setTitleTextAreaId(id)}
|
|
1312
|
-
varRegex={rcsVarRegex}
|
|
1313
|
-
placeholderPrefix=""
|
|
1314
|
-
getPlaceholder={() => formatMessage(messages.rcsVarSlotPlaceholder)}
|
|
1315
|
-
/>
|
|
1153
|
+
renderedRCSEditMessage(splitTemplateVarString(templateTitle), TITLE_TEXT)
|
|
1316
1154
|
) : (
|
|
1317
|
-
<div className="rcs_text_area_wrapper">
|
|
1318
1155
|
<CapInput
|
|
1319
1156
|
className={`rcs-template-title-input ${
|
|
1320
1157
|
!isTemplateApproved ? "rcs-edit-disabled" : ""
|
|
@@ -1327,8 +1164,8 @@ const onTitleAddVar = () => {
|
|
|
1327
1164
|
errorMessage={templateTitleError}
|
|
1328
1165
|
disabled={isEditFlow || !isFullMode}
|
|
1329
1166
|
/>
|
|
1330
|
-
</div>
|
|
1331
1167
|
)}
|
|
1168
|
+
</div>
|
|
1332
1169
|
{(isEditFlow || !isFullMode) && templateTitleError && (
|
|
1333
1170
|
<CapError className="rcs-template-title-error">
|
|
1334
1171
|
{templateTitleError}
|
|
@@ -1376,18 +1213,7 @@ const onTitleAddVar = () => {
|
|
|
1376
1213
|
<CapRow className="rcs-create-template-message-input">
|
|
1377
1214
|
<div className="rcs_text_area_wrapper">
|
|
1378
1215
|
{(isEditFlow || !isFullMode)
|
|
1379
|
-
? (
|
|
1380
|
-
<VarSegmentMessageEditor
|
|
1381
|
-
key={`rcs-desc-vars-${rcsVarSegmentEditorRemountKey}`}
|
|
1382
|
-
templateString={templateDesc}
|
|
1383
|
-
valueMap={descriptionVarSegmentValueMapById}
|
|
1384
|
-
onChange={(id, value) => handleRcsVarChange(id, value, MESSAGE_TEXT)}
|
|
1385
|
-
onFocus={(id) => setDescTextAreaId(id)}
|
|
1386
|
-
varRegex={rcsVarRegex}
|
|
1387
|
-
placeholderPrefix=""
|
|
1388
|
-
getPlaceholder={() => formatMessage(messages.rcsVarSlotPlaceholder)}
|
|
1389
|
-
/>
|
|
1390
|
-
)
|
|
1216
|
+
? renderedRCSEditMessage(splitTemplateVarString(templateDesc), MESSAGE_TEXT)
|
|
1391
1217
|
: (
|
|
1392
1218
|
<>
|
|
1393
1219
|
<TextArea
|
|
@@ -1431,9 +1257,7 @@ const onTitleAddVar = () => {
|
|
|
1431
1257
|
{templateDescError}
|
|
1432
1258
|
</CapError>
|
|
1433
1259
|
)}
|
|
1434
|
-
{
|
|
1435
|
-
? renderDescriptionCharacterCount('rcs-character-count rcs-character-count--compact')
|
|
1436
|
-
: (!isEditFlow && isFullMode && renderDescriptionCharacterCount())}
|
|
1260
|
+
{!isEditFlow && isFullMode && renderDescriptionCharacterCount()}
|
|
1437
1261
|
{!isFullMode && hasTag() && (
|
|
1438
1262
|
<CapAlert
|
|
1439
1263
|
message={
|
|
@@ -1453,6 +1277,18 @@ const onTitleAddVar = () => {
|
|
|
1453
1277
|
);
|
|
1454
1278
|
};
|
|
1455
1279
|
|
|
1280
|
+
|
|
1281
|
+
const fallbackSmsLength = () => (
|
|
1282
|
+
<CapLabel type="label1" className="fallback-sms-length">
|
|
1283
|
+
{formatMessage(messages.totalCharacters, {
|
|
1284
|
+
smsCount: Math.ceil(
|
|
1285
|
+
fallbackMessage?.length / FALLBACK_MESSAGE_MAX_LENGTH,
|
|
1286
|
+
),
|
|
1287
|
+
number: fallbackMessage?.length,
|
|
1288
|
+
})}
|
|
1289
|
+
</CapLabel>
|
|
1290
|
+
);
|
|
1291
|
+
|
|
1456
1292
|
// Get character count for title (rich card only)
|
|
1457
1293
|
const getTitleCharacterCount = () => {
|
|
1458
1294
|
if (templateType === contentType.text_message) return 0;
|
|
@@ -1512,7 +1348,7 @@ const onTitleAddVar = () => {
|
|
|
1512
1348
|
const hasTag = () => {
|
|
1513
1349
|
// Check cardVarMapped values for tags
|
|
1514
1350
|
if (cardVarMapped && Object.keys(cardVarMapped).length > 0) {
|
|
1515
|
-
const hasTagInMapped = Object.values(cardVarMapped).some(
|
|
1351
|
+
const hasTagInMapped = Object.values(cardVarMapped).some(value =>
|
|
1516
1352
|
isTagIncluded(value)
|
|
1517
1353
|
);
|
|
1518
1354
|
if (hasTagInMapped) return true;
|
|
@@ -1530,6 +1366,14 @@ const onTitleAddVar = () => {
|
|
|
1530
1366
|
return false;
|
|
1531
1367
|
};
|
|
1532
1368
|
|
|
1369
|
+
//adding creative dlt fallback sms handlers
|
|
1370
|
+
const addDltMsgHandler = () => {
|
|
1371
|
+
setShowDltContainer(true);
|
|
1372
|
+
setDltMode(RCS_DLT_MODE.TEMPLATES);
|
|
1373
|
+
setDltEditData({});
|
|
1374
|
+
setFallbackMessage('');
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1533
1377
|
const closeDltContainerHandler = () => {
|
|
1534
1378
|
setShowDltContainer(false);
|
|
1535
1379
|
setDltMode('');
|
|
@@ -1552,17 +1396,68 @@ const onTitleAddVar = () => {
|
|
|
1552
1396
|
const fallMsg = get(tempData, `versions.base.updated-sms-editor`, []).join(
|
|
1553
1397
|
'',
|
|
1554
1398
|
);
|
|
1555
|
-
const templateNameFromDlt = get(dltEditData, 'name', '')
|
|
1556
|
-
|| get(tempData, 'versions.base.name', '')
|
|
1557
|
-
|| '';
|
|
1558
1399
|
closeDltContainerHandler();
|
|
1559
1400
|
setDltEditData(tempData);
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1401
|
+
setFallbackMessage(fallMsg);
|
|
1402
|
+
setFallbackMessageError(fallMsg?.length > FALLBACK_MESSAGE_MAX_LENGTH);
|
|
1403
|
+
setShowDltCard(true);
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1406
|
+
const rcsDltCardDeleteHandler = () => {
|
|
1407
|
+
closeDltContainerHandler();
|
|
1408
|
+
setDltEditData({});
|
|
1409
|
+
setFallbackMessage('');
|
|
1410
|
+
setFallbackMessageError(false);
|
|
1411
|
+
setShowDltCard(false);
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
const dltFallbackListingPreviewhandler = (data) => {
|
|
1415
|
+
const {
|
|
1416
|
+
'updated-sms-editor': updatedSmsEditor = [],
|
|
1417
|
+
'sms-editor': smsEditor = '',
|
|
1418
|
+
} = data.versions.base || {};
|
|
1419
|
+
setFallbackPreviewmode(true);
|
|
1420
|
+
setDltPreviewData(
|
|
1421
|
+
updatedSmsEditor === '' ? smsEditor : updatedSmsEditor.join(''),
|
|
1422
|
+
);
|
|
1423
|
+
};
|
|
1424
|
+
|
|
1425
|
+
const getDltContentCardList = (content, channel) => {
|
|
1426
|
+
const extra = [
|
|
1427
|
+
<CapIcon
|
|
1428
|
+
type="edit"
|
|
1429
|
+
style={{ marginRight: '8px' }}
|
|
1430
|
+
onClick={() => rcsDltEditSelectHandler(dltEditData)}
|
|
1431
|
+
/>,
|
|
1432
|
+
<CapDropdown
|
|
1433
|
+
overlay={(
|
|
1434
|
+
<CapMenu>
|
|
1435
|
+
<>
|
|
1436
|
+
<CapMenu.Item
|
|
1437
|
+
className="ant-dropdown-menu-item"
|
|
1438
|
+
onClick={() => setFallbackPreviewmode(true)}
|
|
1439
|
+
>
|
|
1440
|
+
{formatMessage(globalMessages.preview)}
|
|
1441
|
+
</CapMenu.Item>
|
|
1442
|
+
<CapMenu.Item
|
|
1443
|
+
className="ant-dropdown-menu-item"
|
|
1444
|
+
onClick={rcsDltCardDeleteHandler}
|
|
1445
|
+
>
|
|
1446
|
+
{formatMessage(globalMessages.delete)}
|
|
1447
|
+
</CapMenu.Item>
|
|
1448
|
+
</>
|
|
1449
|
+
</CapMenu>
|
|
1450
|
+
)}
|
|
1451
|
+
>
|
|
1452
|
+
<CapIcon type="more" />
|
|
1453
|
+
</CapDropdown>,
|
|
1454
|
+
];
|
|
1455
|
+
return {
|
|
1456
|
+
title: channel,
|
|
1457
|
+
content,
|
|
1458
|
+
cardType: channel,
|
|
1459
|
+
extra,
|
|
1460
|
+
};
|
|
1566
1461
|
};
|
|
1567
1462
|
|
|
1568
1463
|
const getDltSlideBoxContent = () => {
|
|
@@ -1589,6 +1484,7 @@ const onTitleAddVar = () => {
|
|
|
1589
1484
|
isFullMode={isFullMode}
|
|
1590
1485
|
isDltFromRcs
|
|
1591
1486
|
onSelectTemplate={rcsDltEditSelectHandler}
|
|
1487
|
+
handlePeviewTemplate={dltFallbackListingPreviewhandler}
|
|
1592
1488
|
/>
|
|
1593
1489
|
);
|
|
1594
1490
|
} else if (dltMode === RCS_DLT_MODE.EDIT) {
|
|
@@ -1609,32 +1505,147 @@ const onTitleAddVar = () => {
|
|
|
1609
1505
|
return { dltHeader, dltContent };
|
|
1610
1506
|
};
|
|
1611
1507
|
|
|
1612
|
-
const renderFallBackSmsComponent = () =>
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1508
|
+
const renderFallBackSmsComponent = () => {
|
|
1509
|
+
// Completely disable fallback functionality when DLT is disabled
|
|
1510
|
+
return null;
|
|
1511
|
+
|
|
1512
|
+
// const contentArr = [];
|
|
1513
|
+
// const showAddCreativeBtnForDlt = isDltEnabled && !showDltCard;
|
|
1514
|
+
// const showCardForDlt = isDltEnabled && showDltCard;
|
|
1515
|
+
// const showNonDltFallbackComp = !showAddCreativeBtnForDlt && !showCardForDlt;
|
|
1516
|
+
// //pushing common fallback sms headings
|
|
1517
|
+
// contentArr.push(
|
|
1518
|
+
// <CapRow
|
|
1519
|
+
// style={{
|
|
1520
|
+
// marginBottom: isDltEnabled ? '20px' : '10px',
|
|
1521
|
+
// }}
|
|
1522
|
+
// >
|
|
1523
|
+
// <CapHeader
|
|
1524
|
+
// title={(
|
|
1525
|
+
// <CapRow type="flex">
|
|
1526
|
+
// <CapHeading type="h4">
|
|
1527
|
+
// {formatMessage(messages.fallbackLabel)}
|
|
1528
|
+
// </CapHeading>
|
|
1529
|
+
// <CapTooltipWithInfo
|
|
1530
|
+
// placement="right"
|
|
1531
|
+
// infoIconProps={{
|
|
1532
|
+
// style: { marginLeft: '4px', marginTop: '3px' },
|
|
1533
|
+
// }}
|
|
1534
|
+
// title={formatMessage(messages.fallbackToolTip)}
|
|
1535
|
+
// />
|
|
1536
|
+
// </CapRow>
|
|
1537
|
+
// )}
|
|
1538
|
+
// description={formatMessage(messages.fallbackDesc)}
|
|
1539
|
+
// suffix={
|
|
1540
|
+
// isDltEnabled ? null : (
|
|
1541
|
+
// <CapButton
|
|
1542
|
+
// type="flat"
|
|
1543
|
+
// className="fallback-preview-btn"
|
|
1544
|
+
// prefix={<CapIcon type="eye" />}
|
|
1545
|
+
// style={{ color: CAP_SECONDARY.base }}
|
|
1546
|
+
// onClick={() => setFallbackPreviewmode(true)}
|
|
1547
|
+
// disabled={fallbackMessage === '' || fallbackMessageError}
|
|
1548
|
+
// >
|
|
1549
|
+
// {formatMessage(globalMessages.preview)}
|
|
1550
|
+
// </CapButton>
|
|
1551
|
+
// )
|
|
1552
|
+
// }
|
|
1553
|
+
// />
|
|
1554
|
+
// </CapRow>,
|
|
1555
|
+
// );
|
|
1556
|
+
|
|
1557
|
+
//dlt is enabled, and dlt content is not yet added, show button to add dlt creative
|
|
1558
|
+
// showAddCreativeBtnForDlt
|
|
1559
|
+
// && contentArr.push(
|
|
1560
|
+
// <CapCard className="rcs-dlt-fallback-card">
|
|
1561
|
+
// <CapRow type="flex" justify="center" align="middle">
|
|
1562
|
+
// <CapColumn span={10}>
|
|
1563
|
+
// <CapImage src={addCreativesIcon} />
|
|
1564
|
+
// </CapColumn>
|
|
1565
|
+
// <CapColumn span={14}>
|
|
1566
|
+
// <CapButton
|
|
1567
|
+
// className="add-dlt-btn"
|
|
1568
|
+
// type="secondary"
|
|
1569
|
+
// onClick={addDltMsgHandler}
|
|
1570
|
+
// >
|
|
1571
|
+
// {formatMessage(messages.addSmsCreative)}
|
|
1572
|
+
// </CapButton>
|
|
1573
|
+
// </CapColumn>
|
|
1574
|
+
// </CapRow>
|
|
1575
|
+
// </CapCard>,
|
|
1576
|
+
// );
|
|
1577
|
+
|
|
1578
|
+
// //dlt is enabled and dlt content is added, show it in a card
|
|
1579
|
+
// showCardForDlt
|
|
1580
|
+
// && contentArr.push(
|
|
1581
|
+
// <CapCustomCardList
|
|
1582
|
+
// cardList={[getDltContentCardList(fallbackMessage, SMS)]}
|
|
1583
|
+
// className="rcs-dlt-card"
|
|
1584
|
+
// />,
|
|
1585
|
+
// fallbackMessageError && (
|
|
1586
|
+
// <CapError className="rcs-fallback-len-error">
|
|
1587
|
+
// {formatMessage(messages.fallbackMsgLenError)}
|
|
1588
|
+
// </CapError>
|
|
1589
|
+
// ),
|
|
1590
|
+
// );
|
|
1591
|
+
|
|
1592
|
+
// //dlt is not enabled, show non dlt text area
|
|
1593
|
+
// showNonDltFallbackComp
|
|
1594
|
+
// && contentArr.push(
|
|
1595
|
+
// <>
|
|
1596
|
+
// <CapRow>
|
|
1597
|
+
// <CapHeader
|
|
1598
|
+
// title={(
|
|
1599
|
+
// <CapHeading type="h4">
|
|
1600
|
+
// {formatMessage(messages.fallbackTextAreaLabel)}
|
|
1601
|
+
// </CapHeading>
|
|
1602
|
+
// )}
|
|
1603
|
+
// suffix={(
|
|
1604
|
+
// <TagList
|
|
1605
|
+
// label={formatMessage(globalMessages.addLabels)}
|
|
1606
|
+
// onTagSelect={onTagSelectFallback}
|
|
1607
|
+
// location={location}
|
|
1608
|
+
// tags={tags || []}
|
|
1609
|
+
// onContextChange={handleOnTagsContextChange}
|
|
1610
|
+
// injectedTags={injectedTags || {}}
|
|
1611
|
+
// selectedOfferDetails={selectedOfferDetails}
|
|
1612
|
+
// />
|
|
1613
|
+
// )}
|
|
1614
|
+
// />
|
|
1615
|
+
// </CapRow>
|
|
1616
|
+
// <div className="rcs_fallback_msg_textarea_wrapper">
|
|
1617
|
+
// <TextArea
|
|
1618
|
+
// id="rcs_fallback_message_textarea"
|
|
1619
|
+
// autosize={{ minRows: 3, maxRows: 5 }}
|
|
1620
|
+
// placeholder={formatMessage(messages.fallbackMsgPlaceholder)}
|
|
1621
|
+
// onChange={onFallbackMessageChange}
|
|
1622
|
+
// errorMessage={fallbackMessageError}
|
|
1623
|
+
// value={fallbackMessage || ""}
|
|
1624
|
+
// />
|
|
1625
|
+
// {!aiContentBotDisabled && (
|
|
1626
|
+
// <CapAskAira.ContentGenerationBot
|
|
1627
|
+
// text={fallbackMessage || ""}
|
|
1628
|
+
// setText={(text) => {
|
|
1629
|
+
// onFallbackMessageChange({ target: { value: text } });
|
|
1630
|
+
// }}
|
|
1631
|
+
// iconPlacement="float-br"
|
|
1632
|
+
// rootStyle={{
|
|
1633
|
+
// bottom: "0.5rem",
|
|
1634
|
+
// right: "0.5rem",
|
|
1635
|
+
// position: "absolute",
|
|
1636
|
+
// }}
|
|
1637
|
+
// />
|
|
1638
|
+
// )}
|
|
1639
|
+
// </div>
|
|
1640
|
+
// <CapRow>{fallbackSmsLength()}</CapRow>
|
|
1641
|
+
// </>
|
|
1642
|
+
// );
|
|
1643
|
+
|
|
1644
|
+
// return <>{contentArr}</>;
|
|
1645
|
+
};
|
|
1636
1646
|
|
|
1637
1647
|
const uploadRcsImage = useCallback((file, type, fileParams, index) => {
|
|
1648
|
+
setImageError(null);
|
|
1638
1649
|
const isRcsThumbnail = index === 1;
|
|
1639
1650
|
actions.uploadRcsAsset(file, type, {
|
|
1640
1651
|
isRcsThumbnail,
|
|
@@ -1646,6 +1657,7 @@ const onTitleAddVar = () => {
|
|
|
1646
1657
|
|
|
1647
1658
|
const setUpdateRcsImageSrc = useCallback(
|
|
1648
1659
|
(val) => {
|
|
1660
|
+
setAssetListImage(val);
|
|
1649
1661
|
updateRcsImageSrc(val);
|
|
1650
1662
|
actions.clearRcsMediaAsset(0);
|
|
1651
1663
|
},
|
|
@@ -1675,6 +1687,7 @@ const onTitleAddVar = () => {
|
|
|
1675
1687
|
|
|
1676
1688
|
|
|
1677
1689
|
const uploadRcsVideo = (file, type, fileParams) => {
|
|
1690
|
+
setImageError(null);
|
|
1678
1691
|
actions.uploadRcsAsset(file, type, {
|
|
1679
1692
|
...fileParams,
|
|
1680
1693
|
type: 'video',
|
|
@@ -1683,6 +1696,9 @@ const onTitleAddVar = () => {
|
|
|
1683
1696
|
});
|
|
1684
1697
|
};
|
|
1685
1698
|
|
|
1699
|
+
const updateRcsVideoSrc = (val) => {
|
|
1700
|
+
setRcsVideoSrc(val);
|
|
1701
|
+
};
|
|
1686
1702
|
const setUpdateRcsVideoSrc = useCallback((index, val) => {
|
|
1687
1703
|
setRcsVideoSrc(val);
|
|
1688
1704
|
setAssetList(val);
|
|
@@ -1711,7 +1727,7 @@ const onTitleAddVar = () => {
|
|
|
1711
1727
|
<>
|
|
1712
1728
|
<CapHeading type="h4" className="rcs-image-dimensions-label">Upload Thumbnail</CapHeading>
|
|
1713
1729
|
<CapImageUpload
|
|
1714
|
-
|
|
1730
|
+
style={{ paddingTop: '20px' }}
|
|
1715
1731
|
allowedExtensionsRegex={ALLOWED_IMAGE_EXTENSIONS_REGEX}
|
|
1716
1732
|
imgWidth={RCS_VIDEO_THUMBNAIL_DIMENSIONS[currentDimension].width}
|
|
1717
1733
|
imgHeight={RCS_VIDEO_THUMBNAIL_DIMENSIONS[currentDimension].height}
|
|
@@ -1723,6 +1739,7 @@ const onTitleAddVar = () => {
|
|
|
1723
1739
|
updateOnReUpload={updateOnRcsThumbnailReUpload}
|
|
1724
1740
|
minImgSize={RCS_THUMBNAIL_MIN_SIZE}
|
|
1725
1741
|
index={1}
|
|
1742
|
+
className="cap-custom-image-upload"
|
|
1726
1743
|
key={`rcs-uploaded-image-${currentDimension}`}
|
|
1727
1744
|
imageData={thumbnailData}
|
|
1728
1745
|
channel={RCS}
|
|
@@ -1753,7 +1770,7 @@ const onTitleAddVar = () => {
|
|
|
1753
1770
|
value: dim.type,
|
|
1754
1771
|
label: `${dim.label}`
|
|
1755
1772
|
}))}
|
|
1756
|
-
|
|
1773
|
+
style={{ marginBottom: '20px' }}
|
|
1757
1774
|
/>
|
|
1758
1775
|
</>
|
|
1759
1776
|
)}
|
|
@@ -1767,6 +1784,7 @@ const onTitleAddVar = () => {
|
|
|
1767
1784
|
</div>
|
|
1768
1785
|
) : (
|
|
1769
1786
|
<CapImageUpload
|
|
1787
|
+
style={{ paddingTop: '20px' }}
|
|
1770
1788
|
allowedExtensionsRegex={ALLOWED_IMAGE_EXTENSIONS_REGEX}
|
|
1771
1789
|
imgWidth={RCS_IMAGE_DIMENSIONS[selectedDimension].width}
|
|
1772
1790
|
imgHeight={RCS_IMAGE_DIMENSIONS[selectedDimension].height}
|
|
@@ -1777,7 +1795,7 @@ const onTitleAddVar = () => {
|
|
|
1777
1795
|
updateImageSrc={setUpdateRcsImageSrc}
|
|
1778
1796
|
updateOnReUpload={updateOnRcsImageReUpload}
|
|
1779
1797
|
index={0}
|
|
1780
|
-
className="cap-custom-image-upload
|
|
1798
|
+
className="cap-custom-image-upload"
|
|
1781
1799
|
key={`rcs-uploaded-image-${selectedDimension}`}
|
|
1782
1800
|
imageData={rcsData}
|
|
1783
1801
|
channel={RCS}
|
|
@@ -1807,7 +1825,7 @@ const onTitleAddVar = () => {
|
|
|
1807
1825
|
value: dim.type,
|
|
1808
1826
|
label: `${dim.label}`
|
|
1809
1827
|
}))}
|
|
1810
|
-
|
|
1828
|
+
style={{ marginBottom: '20px' }}
|
|
1811
1829
|
/>
|
|
1812
1830
|
)}
|
|
1813
1831
|
{(isEditFlow || !isFullMode) ? (
|
|
@@ -1867,16 +1885,8 @@ const onTitleAddVar = () => {
|
|
|
1867
1885
|
const getRcsPreview = () => {
|
|
1868
1886
|
|
|
1869
1887
|
const dimensionObj = RCS_IMAGE_DIMENSIONS[selectedDimension];
|
|
1870
|
-
const
|
|
1871
|
-
const
|
|
1872
|
-
? 0
|
|
1873
|
-
: ((templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []).length);
|
|
1874
|
-
const resolvedTitle = isMediaTypeText
|
|
1875
|
-
? ''
|
|
1876
|
-
: (isSlotMappingMode ? resolveTemplateWithMap(templateTitle, 0) : templateTitle);
|
|
1877
|
-
const resolvedDesc = isSlotMappingMode
|
|
1878
|
-
? resolveTemplateWithMap(templateDesc, titleVarCountForResolve)
|
|
1879
|
-
: templateDesc;
|
|
1888
|
+
const resolvedTitle = !isFullMode ? resolveTemplateWithMap(templateTitle) : templateTitle;
|
|
1889
|
+
const resolvedDesc = !isFullMode ? resolveTemplateWithMap(templateDesc) : templateDesc;
|
|
1880
1890
|
return (
|
|
1881
1891
|
<UnifiedPreview
|
|
1882
1892
|
channel={RCS}
|
|
@@ -1899,65 +1909,51 @@ const onTitleAddVar = () => {
|
|
|
1899
1909
|
);
|
|
1900
1910
|
};
|
|
1901
1911
|
|
|
1912
|
+
const getUnmappedDesc = (str, mapping) => {
|
|
1913
|
+
if (!str) return '';
|
|
1914
|
+
if (!mapping || Object.keys(mapping).length === 0) return str;
|
|
1915
|
+
let result = str;
|
|
1916
|
+
const replacements = [];
|
|
1917
|
+
Object.entries(mapping).forEach(([key, value]) => {
|
|
1918
|
+
const raw = (value ?? '').toString();
|
|
1919
|
+
if (!raw || raw?.trim?.() === '') return;
|
|
1920
|
+
const braced = /^\{\{[\s\S]*\}\}$/.test(raw) ? raw : `{{${raw}}}`;
|
|
1921
|
+
replacements.push({ key, needle: raw });
|
|
1922
|
+
if (braced !== raw) replacements.push({ key, needle: braced });
|
|
1923
|
+
});
|
|
1924
|
+
const seen = new Set();
|
|
1925
|
+
const uniq = replacements
|
|
1926
|
+
.filter(({ key, needle }) => {
|
|
1927
|
+
const id = `${key}::${needle}`;
|
|
1928
|
+
if (seen.has(id)) return false;
|
|
1929
|
+
seen.add(id);
|
|
1930
|
+
return true;
|
|
1931
|
+
})
|
|
1932
|
+
.sort((a, b) => (b.needle.length - a.needle.length));
|
|
1933
|
+
|
|
1934
|
+
uniq.forEach(({ key, needle }) => {
|
|
1935
|
+
if (!needle) return;
|
|
1936
|
+
const escaped = needle.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
1937
|
+
const regex = new RegExp(escaped, 'g');
|
|
1938
|
+
result = result.replace(regex, `{{${key}}}`);
|
|
1939
|
+
});
|
|
1940
|
+
return result;
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1902
1943
|
const createPayload = () => {
|
|
1903
|
-
const
|
|
1944
|
+
const base = get(dltEditData, `versions.base`, {});
|
|
1945
|
+
const {
|
|
1946
|
+
template_id: templateId = '',
|
|
1947
|
+
template_name = '',
|
|
1948
|
+
'sms-editor': template = '',
|
|
1949
|
+
header: registeredSenderIds = [],
|
|
1950
|
+
} = base;
|
|
1951
|
+
const resolvedTitle = !isFullMode ? resolveTemplateWithMap(templateTitle) : templateTitle;
|
|
1952
|
+
const resolvedDesc = !isFullMode ? resolveTemplateWithMap(templateDesc) : templateDesc;
|
|
1904
1953
|
const alignment = isMediaTypeImage
|
|
1905
1954
|
? RCS_IMAGE_DIMENSIONS[selectedDimension]?.alignment
|
|
1906
1955
|
: RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.alignment;
|
|
1907
1956
|
|
|
1908
|
-
const heightTypeForCardWidth = isMediaTypeText
|
|
1909
|
-
? undefined
|
|
1910
|
-
: isMediaTypeImage
|
|
1911
|
-
? RCS_IMAGE_DIMENSIONS[selectedDimension]?.heightType
|
|
1912
|
-
: isMediaTypeVideo
|
|
1913
|
-
? RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.heightType
|
|
1914
|
-
: undefined;
|
|
1915
|
-
const cardWidthFromSelection =
|
|
1916
|
-
heightTypeForCardWidth === MEDIUM ? MEDIUM : SMALL;
|
|
1917
|
-
|
|
1918
|
-
/** Library: merge props + state so SMS fallback is not dropped when local state is empty but templateData has consumer data. */
|
|
1919
|
-
const smsFromApiShape = getLibrarySmsFallbackApiBaselineFromTemplateData(templateData);
|
|
1920
|
-
const smsFallbackMerged = !isFullMode
|
|
1921
|
-
? (() => {
|
|
1922
|
-
const local =
|
|
1923
|
-
smsFallbackData && typeof smsFallbackData === 'object' ? smsFallbackData : {};
|
|
1924
|
-
return {
|
|
1925
|
-
...smsFromApiShape,
|
|
1926
|
-
...local,
|
|
1927
|
-
rcsSmsFallbackVarMapped: mergeRcsSmsFallbackVarMapLayers(smsFromApiShape, local),
|
|
1928
|
-
};
|
|
1929
|
-
})()
|
|
1930
|
-
: (smsFallbackData || {});
|
|
1931
|
-
const smsFallbackForPayload = (() => {
|
|
1932
|
-
if (isFullMode) {
|
|
1933
|
-
return hasMeaningfulSmsFallbackShape(smsFallbackData) ? smsFallbackData : null;
|
|
1934
|
-
}
|
|
1935
|
-
const mapped = {
|
|
1936
|
-
templateName:
|
|
1937
|
-
smsFallbackMerged.templateName
|
|
1938
|
-
|| smsFallbackMerged.smsTemplateName
|
|
1939
|
-
|| '',
|
|
1940
|
-
// Use `||` so empty `content` does not block campaign/API `message` (common in embedded flows).
|
|
1941
|
-
content:
|
|
1942
|
-
smsFallbackMerged.content
|
|
1943
|
-
|| smsFallbackMerged.smsContent
|
|
1944
|
-
|| smsFallbackMerged.smsTemplateContent
|
|
1945
|
-
|| smsFallbackMerged.message
|
|
1946
|
-
|| '',
|
|
1947
|
-
templateContent:
|
|
1948
|
-
pickFirstSmsFallbackTemplateString(smsFallbackMerged)
|
|
1949
|
-
|| '',
|
|
1950
|
-
...(typeof smsFallbackMerged.unicodeValidity === 'boolean'
|
|
1951
|
-
&& { unicodeValidity: smsFallbackMerged.unicodeValidity }),
|
|
1952
|
-
...(smsFallbackMerged[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]
|
|
1953
|
-
&& Object.keys(smsFallbackMerged[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]).length > 0 && {
|
|
1954
|
-
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]:
|
|
1955
|
-
smsFallbackMerged[RCS_SMS_FALLBACK_VAR_MAPPED_PROP],
|
|
1956
|
-
}),
|
|
1957
|
-
};
|
|
1958
|
-
return hasMeaningfulSmsFallbackShape(mapped) ? mapped : null;
|
|
1959
|
-
})();
|
|
1960
|
-
|
|
1961
1957
|
const payload = {
|
|
1962
1958
|
name: templateName,
|
|
1963
1959
|
versions: {
|
|
@@ -1969,14 +1965,12 @@ const onTitleAddVar = () => {
|
|
|
1969
1965
|
cardSettings: {
|
|
1970
1966
|
cardOrientation: isMediaTypeImage ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.orientation || VERTICAL : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.orientation || VERTICAL,
|
|
1971
1967
|
...(alignment && { mediaAlignment: alignment }),
|
|
1972
|
-
cardWidth:
|
|
1968
|
+
cardWidth: SMALL,
|
|
1973
1969
|
},
|
|
1974
1970
|
cardContent: [
|
|
1975
1971
|
{
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
title: templateTitle,
|
|
1979
|
-
description: templateDesc,
|
|
1972
|
+
title: resolvedTitle,
|
|
1973
|
+
description: resolvedDesc,
|
|
1980
1974
|
mediaType: templateMediaType,
|
|
1981
1975
|
...(!isMediaTypeText && {media: {
|
|
1982
1976
|
mediaUrl: rcsImageSrc || rcsVideoSrc.videoSrc || '',
|
|
@@ -1985,30 +1979,23 @@ const onTitleAddVar = () => {
|
|
|
1985
1979
|
? RCS_IMAGE_DIMENSIONS[selectedDimension]?.heightType || MEDIUM
|
|
1986
1980
|
: RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.heightType || MEDIUM,
|
|
1987
1981
|
}}),
|
|
1988
|
-
...(
|
|
1989
|
-
const
|
|
1990
|
-
...(templateTitle
|
|
1991
|
-
...(templateDesc
|
|
1982
|
+
...(!isFullMode && (() => {
|
|
1983
|
+
const tokens = [
|
|
1984
|
+
...(templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []),
|
|
1985
|
+
...(templateDesc ? (templateDesc.match(rcsVarRegex) || []) : []),
|
|
1992
1986
|
];
|
|
1993
|
-
const
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
cardVarMapped
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
isSlotMappingMode,
|
|
2003
|
-
);
|
|
2004
|
-
const sanitizedSlotValue = sanitizeCardVarMappedValue(resolvedRawValue);
|
|
2005
|
-
persistedSlotVarMap[String(slotIndexZeroBased + 1)] = sanitizedSlotValue;
|
|
2006
|
-
if (!seenSemanticVarNames.has(varName)) {
|
|
2007
|
-
seenSemanticVarNames.add(varName);
|
|
2008
|
-
persistedSlotVarMap[varName] = sanitizedSlotValue;
|
|
1987
|
+
const allowedKeys = tokens
|
|
1988
|
+
.map((t) => getVarNameFromToken(t))
|
|
1989
|
+
.filter(Boolean);
|
|
1990
|
+
const nextMap = {};
|
|
1991
|
+
allowedKeys.forEach((k) => {
|
|
1992
|
+
if (Object.prototype.hasOwnProperty.call(cardVarMapped || {}, k)) {
|
|
1993
|
+
nextMap[k] = cardVarMapped[k];
|
|
1994
|
+
} else {
|
|
1995
|
+
nextMap[k] = '';
|
|
2009
1996
|
}
|
|
2010
1997
|
});
|
|
2011
|
-
return { cardVarMapped:
|
|
1998
|
+
return { cardVarMapped: nextMap };
|
|
2012
1999
|
})()),
|
|
2013
2000
|
...(suggestions.length > 0 && { suggestions }),
|
|
2014
2001
|
}
|
|
@@ -2016,29 +2003,17 @@ const onTitleAddVar = () => {
|
|
|
2016
2003
|
contentType: isFullMode ? templateType : RICHCARD,
|
|
2017
2004
|
...(isFullMode && {accountId:accountId, accessToken: accessToken, accountName: accountName, hostName: hostName}),
|
|
2018
2005
|
},
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
smsFallBackContent: {
|
|
2028
|
-
smsTemplateName: smsFallbackForPayload.templateName || '',
|
|
2029
|
-
smsContent: smsBodyText,
|
|
2030
|
-
// cap-campaigns-v2 `normalizeRcsMessageContentForApi` only serializes `message` (+ templateConfigs); without this key SMS fallback is dropped on send.
|
|
2031
|
-
message: smsBodyText,
|
|
2032
|
-
...(typeof smsFallbackForPayload.unicodeValidity === 'boolean' && {
|
|
2033
|
-
unicodeValidity: smsFallbackForPayload.unicodeValidity,
|
|
2034
|
-
}),
|
|
2035
|
-
...(smsFallbackForPayload.rcsSmsFallbackVarMapped
|
|
2036
|
-
&& Object.keys(smsFallbackForPayload.rcsSmsFallbackVarMapped).length > 0 && {
|
|
2037
|
-
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: smsFallbackForPayload.rcsSmsFallbackVarMapped,
|
|
2038
|
-
}),
|
|
2006
|
+
smsFallBackContent: {
|
|
2007
|
+
message: fallbackMessage,
|
|
2008
|
+
...(isDltEnabled && {
|
|
2009
|
+
templateConfigs: {
|
|
2010
|
+
templateId,
|
|
2011
|
+
templateName: template_name,
|
|
2012
|
+
template,
|
|
2013
|
+
registeredSenderIds,
|
|
2039
2014
|
},
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2015
|
+
}),
|
|
2016
|
+
},
|
|
2042
2017
|
},
|
|
2043
2018
|
},
|
|
2044
2019
|
},
|
|
@@ -2048,84 +2023,6 @@ const onTitleAddVar = () => {
|
|
|
2048
2023
|
return payload;
|
|
2049
2024
|
};
|
|
2050
2025
|
|
|
2051
|
-
/** Shape expected by CommonTestAndPreview buildRcsTestMessagePayload (versions.base.content.RCS). */
|
|
2052
|
-
const testPreviewFormData = useMemo(() => {
|
|
2053
|
-
const payload = createPayload();
|
|
2054
|
-
const rcs = payload?.versions?.base?.content?.RCS;
|
|
2055
|
-
if (!rcs) return null;
|
|
2056
|
-
// createMessageMeta uses WeCRM `id` when present; else template API account id (sourceAccountIdentifier).
|
|
2057
|
-
const accountIdForCreateMessageMeta =
|
|
2058
|
-
(wecrmAccountId != null && String(wecrmAccountId).trim() !== '')
|
|
2059
|
-
? String(wecrmAccountId)
|
|
2060
|
-
: accountId;
|
|
2061
|
-
const rcsForTest = {
|
|
2062
|
-
...rcs,
|
|
2063
|
-
rcsContent: {
|
|
2064
|
-
...rcs.rcsContent,
|
|
2065
|
-
...(accountIdForCreateMessageMeta ? { accountId: accountIdForCreateMessageMeta } : {}),
|
|
2066
|
-
},
|
|
2067
|
-
};
|
|
2068
|
-
const out = {
|
|
2069
|
-
versions: {
|
|
2070
|
-
base: {
|
|
2071
|
-
content: {
|
|
2072
|
-
RCS: rcsForTest,
|
|
2073
|
-
},
|
|
2074
|
-
},
|
|
2075
|
-
},
|
|
2076
|
-
};
|
|
2077
|
-
const fb = smsFallbackData;
|
|
2078
|
-
if (fb && (fb.smsTemplateId || fb.templateContent || fb.content)) {
|
|
2079
|
-
out.templateConfigs = {
|
|
2080
|
-
templateId: fb.smsTemplateId || '',
|
|
2081
|
-
template: fb.templateContent || fb.content || '',
|
|
2082
|
-
traiDltEnabled: isTraiDLTEnable(isFullMode, smsRegister),
|
|
2083
|
-
registeredSenderIds: Array.isArray(fb.registeredSenderIds) ? fb.registeredSenderIds : [],
|
|
2084
|
-
};
|
|
2085
|
-
}
|
|
2086
|
-
return out;
|
|
2087
|
-
}, [
|
|
2088
|
-
templateName,
|
|
2089
|
-
templateTitle,
|
|
2090
|
-
templateDesc,
|
|
2091
|
-
templateMediaType,
|
|
2092
|
-
cardVarMapped,
|
|
2093
|
-
suggestions,
|
|
2094
|
-
rcsImageSrc,
|
|
2095
|
-
rcsVideoSrc,
|
|
2096
|
-
rcsThumbnailSrc,
|
|
2097
|
-
selectedDimension,
|
|
2098
|
-
smsFallbackData,
|
|
2099
|
-
isFullMode,
|
|
2100
|
-
isEditFlow,
|
|
2101
|
-
templateType,
|
|
2102
|
-
accountId,
|
|
2103
|
-
wecrmAccountId,
|
|
2104
|
-
accessToken,
|
|
2105
|
-
accountName,
|
|
2106
|
-
hostName,
|
|
2107
|
-
smsRegister,
|
|
2108
|
-
]);
|
|
2109
|
-
|
|
2110
|
-
/**
|
|
2111
|
-
* Library/campaign: `createPayload` merges root + nested `smsFallBackContent` from `templateData`
|
|
2112
|
-
* with `smsFallbackData`. Done/slot validation must use the same merge — otherwise local state can
|
|
2113
|
-
* miss `templateContent` / var map while the parent payload still has them (DLT campaigns).
|
|
2114
|
-
*/
|
|
2115
|
-
const librarySmsFallbackMergedForValidation = useMemo(() => {
|
|
2116
|
-
if (isFullMode) {
|
|
2117
|
-
return smsFallbackData;
|
|
2118
|
-
}
|
|
2119
|
-
const smsFromApiShape = getLibrarySmsFallbackApiBaselineFromTemplateData(templateData);
|
|
2120
|
-
const local =
|
|
2121
|
-
smsFallbackData && typeof smsFallbackData === 'object' ? smsFallbackData : {};
|
|
2122
|
-
return {
|
|
2123
|
-
...smsFromApiShape,
|
|
2124
|
-
...local,
|
|
2125
|
-
rcsSmsFallbackVarMapped: mergeRcsSmsFallbackVarMapLayers(smsFromApiShape, local),
|
|
2126
|
-
};
|
|
2127
|
-
}, [isFullMode, templateData, smsFallbackData]);
|
|
2128
|
-
|
|
2129
2026
|
const actionCallback = ({ errorMessage, resp }, isEdit) => {
|
|
2130
2027
|
// eslint-disable-next-line no-undef
|
|
2131
2028
|
const error = errorMessage?.message || errorMessage;
|
|
@@ -2155,9 +2052,6 @@ const onTitleAddVar = () => {
|
|
|
2155
2052
|
_id: params?.id,
|
|
2156
2053
|
validity: true,
|
|
2157
2054
|
type: RCS,
|
|
2158
|
-
// CreativesContainer closes the slide box *after* getCreativesData runs so the parent receives
|
|
2159
|
-
// the RCS payload first (closing immediately used to skip getCreativesData → empty "Add creative").
|
|
2160
|
-
closeSlideBoxAfterSubmit: !isFullMode,
|
|
2161
2055
|
};
|
|
2162
2056
|
getFormData(formDataParams);
|
|
2163
2057
|
};
|
|
@@ -2171,7 +2065,6 @@ const onTitleAddVar = () => {
|
|
|
2171
2065
|
actionCallback({ resp, errorMessage });
|
|
2172
2066
|
setSpin(false); // Always turn off spinner
|
|
2173
2067
|
if (!errorMessage) {
|
|
2174
|
-
setTemplateStatus(RCS_STATUSES.pending);
|
|
2175
2068
|
onCreateComplete();
|
|
2176
2069
|
}
|
|
2177
2070
|
});
|
|
@@ -2183,64 +2076,6 @@ const onTitleAddVar = () => {
|
|
|
2183
2076
|
}
|
|
2184
2077
|
};
|
|
2185
2078
|
|
|
2186
|
-
/** When a fallback SMS row exists, require non-empty body (trimmed) and filled var slots (DLT). */
|
|
2187
|
-
const smsFallbackBlocksDone = () => {
|
|
2188
|
-
// Non-DLT library: user removed SMS fallback (local null) but template still carries fallback — block Done.
|
|
2189
|
-
if (
|
|
2190
|
-
!isFullMode
|
|
2191
|
-
&& !isTraiDLTEnable(isFullMode, smsRegister)
|
|
2192
|
-
&& smsFallbackData == null
|
|
2193
|
-
&& hasMeaningfulSmsFallbackShape(
|
|
2194
|
-
getLibrarySmsFallbackApiBaselineFromTemplateData(templateData),
|
|
2195
|
-
)
|
|
2196
|
-
) {
|
|
2197
|
-
return true;
|
|
2198
|
-
}
|
|
2199
|
-
if (!smsFallbackData) return false;
|
|
2200
|
-
// Full-mode (Send for approval): SMS fallback is optional. Tag-slot mapping is a display/preview
|
|
2201
|
-
// concern, not a structural requirement for approval — the registered SMS template body stands on
|
|
2202
|
-
// its own. Never block the Send for approval button due to missing or unfilled fallback var slots.
|
|
2203
|
-
if (isFullMode) return false;
|
|
2204
|
-
const merged = librarySmsFallbackMergedForValidation;
|
|
2205
|
-
const templateText = pickFirstSmsFallbackTemplateString(merged);
|
|
2206
|
-
if (!templateText) {
|
|
2207
|
-
return true;
|
|
2208
|
-
}
|
|
2209
|
-
const rawVarMap =
|
|
2210
|
-
merged.rcsSmsFallbackVarMapped
|
|
2211
|
-
|| merged['rcs-sms-fallback-var-mapped'];
|
|
2212
|
-
const varMap =
|
|
2213
|
-
rawVarMap != null && typeof rawVarMap === 'object' ? rawVarMap : {};
|
|
2214
|
-
return !areAllRcsSmsFallbackVarSlotsFilled(templateText, varMap);
|
|
2215
|
-
};
|
|
2216
|
-
|
|
2217
|
-
/**
|
|
2218
|
-
* Library / campaigns (`!isFullMode`): card slots are often stored on numeric keys (`1`,`2`,…) while
|
|
2219
|
-
* semantic keys stay `""` from API round-trip. `resolveCardVarMappedSlotValue` matches createPayload
|
|
2220
|
-
* / preview — naive `cardVarMapped[name]` wrongly kept Done disabled for DLT.
|
|
2221
|
-
*/
|
|
2222
|
-
const isLibraryCampaignCardVarMappingIncomplete = () => {
|
|
2223
|
-
if (isFullMode) return false;
|
|
2224
|
-
const titleTokens = splitTemplateVarStringRcs(templateTitle).filter((elem) =>
|
|
2225
|
-
rcsVarTestRegex.test(elem),
|
|
2226
|
-
);
|
|
2227
|
-
const descTokens = splitTemplateVarStringRcs(templateDesc).filter((elem) =>
|
|
2228
|
-
rcsVarTestRegex.test(elem),
|
|
2229
|
-
);
|
|
2230
|
-
const orderedVarNames = [
|
|
2231
|
-
...titleTokens.map((t) => t.replace(/^\{\{|\}\}$/g, '')),
|
|
2232
|
-
...descTokens.map((t) => t.replace(/^\{\{|\}\}$/g, '')),
|
|
2233
|
-
];
|
|
2234
|
-
if (orderedVarNames.length > 0 && isEmpty(cardVarMapped)) {
|
|
2235
|
-
return true;
|
|
2236
|
-
}
|
|
2237
|
-
return orderedVarNames.some((name, globalIdx) => {
|
|
2238
|
-
const v = resolveCardVarMappedSlotValue(cardVarMapped, name, globalIdx, true);
|
|
2239
|
-
const s = v == null ? '' : String(v);
|
|
2240
|
-
return s.trim() === '';
|
|
2241
|
-
});
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
2079
|
const isDisableDone = () => {
|
|
2245
2080
|
if(isEditFlow){
|
|
2246
2081
|
return false;
|
|
@@ -2251,16 +2086,40 @@ const onTitleAddVar = () => {
|
|
|
2251
2086
|
}
|
|
2252
2087
|
}
|
|
2253
2088
|
|
|
2254
|
-
if
|
|
2255
|
-
|
|
2256
|
-
|
|
2089
|
+
if(!isFullMode){
|
|
2090
|
+
const titleVars = splitTemplateVarString(templateTitle).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
|
|
2091
|
+
const descVars = splitTemplateVarString(templateDesc).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
|
|
2092
|
+
const allVars = Array.from(new Set([ ...titleVars, ...descVars ]));
|
|
2257
2093
|
|
|
2258
|
-
|
|
2259
|
-
|
|
2094
|
+
if (allVars.length > 0 && isEmpty(cardVarMapped)) {
|
|
2095
|
+
return true;
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
const hasEmptyMapping =
|
|
2099
|
+
cardVarMapped &&
|
|
2100
|
+
Object.keys(cardVarMapped).length > 0 &&
|
|
2101
|
+
Object.entries(cardVarMapped).some(([_, v]) => {
|
|
2102
|
+
if (typeof v !== 'string') return !v; // null/undefined
|
|
2103
|
+
return v.trim() === ''; // empty string
|
|
2104
|
+
});
|
|
2105
|
+
|
|
2106
|
+
if (hasEmptyMapping) {
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
const anyMissing = allVars.some(name => {
|
|
2111
|
+
const v = cardVarMapped?.[name];
|
|
2112
|
+
if (typeof v !== 'string') return !v;
|
|
2113
|
+
return v.trim() === '';
|
|
2114
|
+
});
|
|
2115
|
+
if (anyMissing) {
|
|
2116
|
+
return true;
|
|
2117
|
+
}
|
|
2260
2118
|
}
|
|
2261
2119
|
|
|
2262
|
-
|
|
2120
|
+
if (isMediaTypeText && templateDesc.trim() === '') {
|
|
2263
2121
|
return true;
|
|
2122
|
+
|
|
2264
2123
|
}
|
|
2265
2124
|
if (isMediaTypeImage && (rcsImageSrc === '' || templateTitle === '' || templateDesc === '' )) {
|
|
2266
2125
|
return true;
|
|
@@ -2278,36 +2137,53 @@ const onTitleAddVar = () => {
|
|
|
2278
2137
|
return true;
|
|
2279
2138
|
}
|
|
2280
2139
|
}
|
|
2281
|
-
if (templateDescError || templateTitleError) {
|
|
2282
|
-
return true;
|
|
2283
|
-
}
|
|
2284
|
-
if (
|
|
2285
|
-
smsFallbackData?.content
|
|
2286
|
-
&& smsFallbackData.content.length > FALLBACK_MESSAGE_MAX_LENGTH
|
|
2287
|
-
) {
|
|
2140
|
+
if (templateDescError || templateTitleError || fallbackMessageError) {
|
|
2288
2141
|
return true;
|
|
2289
2142
|
}
|
|
2290
2143
|
return false;
|
|
2291
2144
|
};
|
|
2292
2145
|
|
|
2293
2146
|
const isEditDisableDone = () => {
|
|
2147
|
+
|
|
2294
2148
|
if (templateStatus !== RCS_STATUSES.approved) {
|
|
2295
2149
|
return true;
|
|
2296
2150
|
}
|
|
2297
2151
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
// }
|
|
2303
|
-
if (isLibraryCampaignCardVarMappingIncomplete()) {
|
|
2304
|
-
return true;
|
|
2152
|
+
if (!isFullMode) {
|
|
2153
|
+
if (templateName.trim() === '' || templateNameError) {
|
|
2154
|
+
return true;
|
|
2155
|
+
}
|
|
2305
2156
|
}
|
|
2157
|
+
if(!isFullMode){
|
|
2158
|
+
const titleVars = splitTemplateVarString(templateTitle).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
|
|
2159
|
+
const descVars = splitTemplateVarString(templateDesc).filter(elem => rcsVarTestRegex.test(elem)).map(v => v.replace(/^\{\{|\}\}$/g, ''));
|
|
2160
|
+
const allVars = Array.from(new Set([ ...titleVars, ...descVars ]));
|
|
2306
2161
|
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2162
|
+
if (allVars.length > 0 && isEmpty(cardVarMapped)) {
|
|
2163
|
+
return true;
|
|
2164
|
+
}
|
|
2310
2165
|
|
|
2166
|
+
const hasEmptyMapping =
|
|
2167
|
+
cardVarMapped &&
|
|
2168
|
+
Object.keys(cardVarMapped).length > 0 &&
|
|
2169
|
+
Object.entries(cardVarMapped).some(([_, v]) => {
|
|
2170
|
+
if (typeof v !== 'string') return !v; // null/undefined
|
|
2171
|
+
return v.trim() === ''; // empty string
|
|
2172
|
+
});
|
|
2173
|
+
|
|
2174
|
+
if (hasEmptyMapping) {
|
|
2175
|
+
return true;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
const anyMissing = allVars.some(name => {
|
|
2179
|
+
const v = cardVarMapped?.[name];
|
|
2180
|
+
if (typeof v !== 'string') return !v;
|
|
2181
|
+
return v.trim() === '';
|
|
2182
|
+
});
|
|
2183
|
+
if (anyMissing) {
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2311
2187
|
if (isMediaTypeText && templateDesc.trim() === '') {
|
|
2312
2188
|
return true;
|
|
2313
2189
|
}
|
|
@@ -2326,13 +2202,7 @@ const onTitleAddVar = () => {
|
|
|
2326
2202
|
return true;
|
|
2327
2203
|
}
|
|
2328
2204
|
}
|
|
2329
|
-
if (templateTitleError || templateDescError) {
|
|
2330
|
-
return true;
|
|
2331
|
-
}
|
|
2332
|
-
if (
|
|
2333
|
-
smsFallbackData?.content
|
|
2334
|
-
&& smsFallbackData.content.length > FALLBACK_MESSAGE_MAX_LENGTH
|
|
2335
|
-
) {
|
|
2205
|
+
if (templateTitleError || templateDescError || fallbackMessageError) {
|
|
2336
2206
|
return true;
|
|
2337
2207
|
}
|
|
2338
2208
|
return false;
|
|
@@ -2382,56 +2252,52 @@ const onTitleAddVar = () => {
|
|
|
2382
2252
|
};
|
|
2383
2253
|
|
|
2384
2254
|
const getMainContent = () => {
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2255
|
+
if (showDltContainer && !fallbackPreviewmode) {
|
|
2256
|
+
const dltSlideBoxContent = showDltContainer && getDltSlideBoxContent();
|
|
2257
|
+
const { dltHeader = '', dltContent = '' } = dltSlideBoxContent;
|
|
2258
|
+
return (
|
|
2259
|
+
<CapSlideBox
|
|
2260
|
+
show={showDltContainer}
|
|
2261
|
+
header={dltHeader}
|
|
2262
|
+
content={dltContent}
|
|
2263
|
+
handleClose={closeDltContainerHandler}
|
|
2264
|
+
size="size-xl"
|
|
2265
|
+
/>
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2388
2268
|
|
|
2389
2269
|
return (
|
|
2390
2270
|
<>
|
|
2391
|
-
{templateStatus !== '' && (
|
|
2392
|
-
<
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
{
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
)}
|
|
2404
|
-
</CapColumn>
|
|
2405
|
-
</CapRow>
|
|
2271
|
+
{templateStatus !== '' && (<CapRow className="template-status-container">
|
|
2272
|
+
<CapLabel type="label2">
|
|
2273
|
+
{formatMessage(messages.templateStatusLabel)}
|
|
2274
|
+
</CapLabel>
|
|
2275
|
+
|
|
2276
|
+
{templateStatus && (
|
|
2277
|
+
<CapAlert
|
|
2278
|
+
message={getTemplateStatusMessage()}
|
|
2279
|
+
type={getTemplateStatusType(templateStatus)}
|
|
2280
|
+
/>
|
|
2281
|
+
)}
|
|
2282
|
+
</CapRow>
|
|
2406
2283
|
)}
|
|
2407
|
-
<CapRow className=
|
|
2284
|
+
<CapRow className="cap-rcs-creatives">
|
|
2408
2285
|
<CapColumn span={14}>
|
|
2409
2286
|
{/* template name */}
|
|
2410
2287
|
{isFullMode && (
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
onChange={onTemplateNameChange}
|
|
2425
|
-
errorMessage={templateNameError}
|
|
2426
|
-
placeholder={formatMessage(
|
|
2427
|
-
globalMessages.templateNamePlaceholder,
|
|
2428
|
-
)}
|
|
2429
|
-
value={templateName || ''}
|
|
2430
|
-
size="default"
|
|
2431
|
-
label={formatMessage(globalMessages.creativeNameLabel)}
|
|
2432
|
-
disabled={(isEditFlow || !isFullMode)}
|
|
2433
|
-
/>
|
|
2434
|
-
)
|
|
2288
|
+
<CapInput
|
|
2289
|
+
id="rcs_template_name_input"
|
|
2290
|
+
data-testid="template_name"
|
|
2291
|
+
onChange={onTemplateNameChange}
|
|
2292
|
+
errorMessage={templateNameError}
|
|
2293
|
+
placeholder={formatMessage(
|
|
2294
|
+
globalMessages.templateNamePlaceholder,
|
|
2295
|
+
)}
|
|
2296
|
+
value={templateName || ''}
|
|
2297
|
+
size="default"
|
|
2298
|
+
label={formatMessage(globalMessages.creativeNameLabel)}
|
|
2299
|
+
disabled={(isEditFlow || !isFullMode)}
|
|
2300
|
+
/>
|
|
2435
2301
|
)}
|
|
2436
2302
|
{renderLabel('templateTypeLabel')}
|
|
2437
2303
|
<CapRadioGroup
|
|
@@ -2459,7 +2325,7 @@ const onTitleAddVar = () => {
|
|
|
2459
2325
|
</>
|
|
2460
2326
|
)}
|
|
2461
2327
|
{renderTextComponent()}
|
|
2462
|
-
<CapDivider
|
|
2328
|
+
<CapDivider style={{ margin: `${CAP_SPACE_28} 0` }} />
|
|
2463
2329
|
{renderFallBackSmsComponent()}
|
|
2464
2330
|
<div className="rcs-scroll-div" />
|
|
2465
2331
|
</CapColumn>
|
|
@@ -2471,8 +2337,7 @@ const onTitleAddVar = () => {
|
|
|
2471
2337
|
|
|
2472
2338
|
|
|
2473
2339
|
<div className="rcs-footer">
|
|
2474
|
-
{
|
|
2475
|
-
{!isEditFlow && isFullMode && (
|
|
2340
|
+
{!isEditFlow && (
|
|
2476
2341
|
<>
|
|
2477
2342
|
<div className="button-disabled-tooltip-wrapper">
|
|
2478
2343
|
<CapButton
|
|
@@ -2493,6 +2358,7 @@ const onTitleAddVar = () => {
|
|
|
2493
2358
|
className="rcs-test-preview-btn"
|
|
2494
2359
|
type="secondary"
|
|
2495
2360
|
disabled={true}
|
|
2361
|
+
style={{ marginLeft: "8px" }}
|
|
2496
2362
|
>
|
|
2497
2363
|
<FormattedMessage {...creativesMessages.testAndPreview} />
|
|
2498
2364
|
</CapButton>
|
|
@@ -2525,6 +2391,51 @@ const onTitleAddVar = () => {
|
|
|
2525
2391
|
</>
|
|
2526
2392
|
)}
|
|
2527
2393
|
</div>
|
|
2394
|
+
|
|
2395
|
+
|
|
2396
|
+
{fallbackPreviewmode && (
|
|
2397
|
+
<CapSlideBox
|
|
2398
|
+
className="rcs-fallback-preview"
|
|
2399
|
+
show={fallbackPreviewmode}
|
|
2400
|
+
header={(
|
|
2401
|
+
<CapHeading type="h7" style={{ color: CAP_G01 }}>
|
|
2402
|
+
{formatMessage(messages.fallbackPreviewtitle)}
|
|
2403
|
+
</CapHeading>
|
|
2404
|
+
)}
|
|
2405
|
+
content={(
|
|
2406
|
+
<>
|
|
2407
|
+
<UnifiedPreview
|
|
2408
|
+
channel={RCS}
|
|
2409
|
+
content={{
|
|
2410
|
+
rcsPreviewContent: {
|
|
2411
|
+
rcsDesc: tempMsg,
|
|
2412
|
+
},
|
|
2413
|
+
}}
|
|
2414
|
+
device={ANDROID}
|
|
2415
|
+
showDeviceToggle={false}
|
|
2416
|
+
showHeader={false}
|
|
2417
|
+
formatMessage={formatMessage}
|
|
2418
|
+
/>
|
|
2419
|
+
<CapHeading
|
|
2420
|
+
type="h3"
|
|
2421
|
+
style={{ textAlign: 'center' }}
|
|
2422
|
+
className="margin-t-16"
|
|
2423
|
+
>
|
|
2424
|
+
{formatMessage(messages.totalCharacters, {
|
|
2425
|
+
smsCount: Math.ceil(
|
|
2426
|
+
tempMsg?.length / FALLBACK_MESSAGE_MAX_LENGTH,
|
|
2427
|
+
),
|
|
2428
|
+
number: tempMsg.length,
|
|
2429
|
+
})}
|
|
2430
|
+
</CapHeading>
|
|
2431
|
+
</>
|
|
2432
|
+
)}
|
|
2433
|
+
handleClose={() => {
|
|
2434
|
+
setFallbackPreviewmode(false);
|
|
2435
|
+
setDltPreviewData('');
|
|
2436
|
+
}}
|
|
2437
|
+
/>
|
|
2438
|
+
)}
|
|
2528
2439
|
</>
|
|
2529
2440
|
);
|
|
2530
2441
|
};
|
|
@@ -2533,38 +2444,12 @@ const onTitleAddVar = () => {
|
|
|
2533
2444
|
<CapSpin spinning={loadingTags || spin}>
|
|
2534
2445
|
{getMainContent()}
|
|
2535
2446
|
</CapSpin>
|
|
2536
|
-
|
|
2537
|
-
{showDltContainer && (() => {
|
|
2538
|
-
const { dltHeader = '', dltContent = '' } = getDltSlideBoxContent() || {};
|
|
2539
|
-
return (
|
|
2540
|
-
<CapSlideBox
|
|
2541
|
-
show={showDltContainer}
|
|
2542
|
-
header={dltHeader}
|
|
2543
|
-
content={dltContent}
|
|
2544
|
-
handleClose={closeDltContainerHandler}
|
|
2545
|
-
size="size-xl"
|
|
2546
|
-
/>
|
|
2547
|
-
);
|
|
2548
|
-
})()}
|
|
2549
|
-
|
|
2550
2447
|
<TestAndPreviewSlidebox
|
|
2551
2448
|
show={propsShowTestAndPreviewSlidebox || showTestAndPreviewSlidebox}
|
|
2552
2449
|
onClose={handleCloseTestAndPreview}
|
|
2553
|
-
formData={
|
|
2554
|
-
content={
|
|
2450
|
+
formData={null} // RCS doesn't use formData structure like SMS
|
|
2451
|
+
content={getTemplateContent()}
|
|
2555
2452
|
currentChannel={RCS}
|
|
2556
|
-
orgUnitId={orgUnitId}
|
|
2557
|
-
smsFallbackContent={
|
|
2558
|
-
smsFallbackData && (smsFallbackData.templateContent || smsFallbackData.content)
|
|
2559
|
-
? {
|
|
2560
|
-
templateContent:
|
|
2561
|
-
smsFallbackData.templateContent || smsFallbackData.content || '',
|
|
2562
|
-
templateName: smsFallbackData.templateName || '',
|
|
2563
|
-
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: smsFallbackData?.rcsSmsFallbackVarMapped ?? {},
|
|
2564
|
-
}
|
|
2565
|
-
: null
|
|
2566
|
-
}
|
|
2567
|
-
smsRegister={smsRegister}
|
|
2568
2453
|
/>
|
|
2569
2454
|
</>
|
|
2570
2455
|
);
|