@capillarytech/creatives-library 9.0.29 → 9.0.30
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 +29 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +22 -1
- package/utils/rcsPayloadUtils.js +102 -0
- package/utils/templateVarUtils.js +198 -0
- package/utils/tests/rcsPayloadUtils.test.js +295 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +21 -1
- package/v2Components/CapActionButton/index.js +261 -144
- package/v2Components/CapActionButton/index.scss +245 -14
- package/v2Components/CapActionButton/messages.js +32 -3
- package/v2Components/CapActionButton/tests/index.test.js +74 -19
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +9 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +35 -2
- package/v2Components/CommonTestAndPreview/index.js +770 -231
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +147 -32
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
- package/v2Containers/CreativesContainer/index.js +322 -106
- package/v2Containers/CreativesContainer/index.scss +102 -1
- package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/carouselUtils.js +224 -0
- package/v2Containers/Rcs/components/CarouselCard.js +317 -0
- package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
- package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
- package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
- package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
- package/v2Containers/Rcs/constants.js +132 -16
- package/v2Containers/Rcs/index.js +1822 -946
- package/v2Containers/Rcs/index.scss +443 -8
- package/v2Containers/Rcs/messages.js +45 -22
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +494 -0
- package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
- package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
- package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
- package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
- package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74440 -39387
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
- package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
- package/v2Containers/Rcs/tests/index.test.js +275 -40
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +912 -0
- package/v2Containers/Rcs/tests/utils.test.js +682 -30
- package/v2Containers/Rcs/utils.js +514 -12
- package/v2Containers/Sms/Create/index.js +115 -48
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +5 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +196 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +131 -59
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +88 -25
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
2
|
+
import { isUrl, isValidText } from '../Line/Container/Wrapper/utils';
|
|
3
|
+
import {
|
|
4
|
+
invalidVarRegex,
|
|
5
|
+
RCS_CTA_URL_TYPE,
|
|
6
|
+
URL_MAX_LENGTH,
|
|
7
|
+
} from '../../v2Components/CapActionButton/constants';
|
|
8
|
+
import {
|
|
9
|
+
RCS_MEDIA_TYPES,
|
|
10
|
+
RCS_BUTTON_TYPES,
|
|
11
|
+
TEMPLATE_TITLE_MAX_LENGTH,
|
|
12
|
+
RCS_RICH_CARD_MAX_LENGTH,
|
|
13
|
+
RCS_NUMERIC_VAR_TOKEN_REGEX,
|
|
14
|
+
RCS_CAROUSEL_ASSET_INDEX_BASE,
|
|
15
|
+
RCS_CAROUSEL_FIRST_CARD_DEFAULT_SUGGESTIONS,
|
|
16
|
+
MEDIUM,
|
|
17
|
+
} from './constants';
|
|
18
|
+
import { sanitizeCardVarMappedValue } from './utils';
|
|
19
|
+
|
|
20
|
+
const getVarNameFromToken = (token = '') => token.replace(/^\{\{|\}\}$/g, '');
|
|
21
|
+
|
|
22
|
+
export const getCarouselImageAssetIndex = (cardIndex) =>
|
|
23
|
+
RCS_CAROUSEL_ASSET_INDEX_BASE + (cardIndex * 3);
|
|
24
|
+
export const getCarouselVideoAssetIndex = (cardIndex) =>
|
|
25
|
+
RCS_CAROUSEL_ASSET_INDEX_BASE + (cardIndex * 3) + 1;
|
|
26
|
+
export const getCarouselThumbnailAssetIndex = (cardIndex) =>
|
|
27
|
+
RCS_CAROUSEL_ASSET_INDEX_BASE + (cardIndex * 3) + 2;
|
|
28
|
+
|
|
29
|
+
export const isThumbnailAssetIndex = (index) => {
|
|
30
|
+
if (index === 1) return true; // standalone thumbnail
|
|
31
|
+
if (index >= RCS_CAROUSEL_ASSET_INDEX_BASE) {
|
|
32
|
+
return ((index - RCS_CAROUSEL_ASSET_INDEX_BASE) % 3) === 2; // carousel thumbnail slot
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Carousel dimension key: `${HEIGHT}_${WIDTH}` (e.g., SHORT_SMALL) */
|
|
38
|
+
export const getCarouselDimensionKey = (selectedCarouselHeight, selectedCarouselWidth) =>
|
|
39
|
+
`${selectedCarouselHeight}_${selectedCarouselWidth}`;
|
|
40
|
+
|
|
41
|
+
export const ensureFirstCardDefaultPhoneSuggestions = (cards) => {
|
|
42
|
+
const next = cloneDeep(cards || []);
|
|
43
|
+
if (next.length === 0) return next;
|
|
44
|
+
const suggestions = next[0].suggestions;
|
|
45
|
+
if (!Array.isArray(suggestions) || suggestions.length === 0) {
|
|
46
|
+
next[0] = {
|
|
47
|
+
...next[0],
|
|
48
|
+
suggestions: cloneDeep(RCS_CAROUSEL_FIRST_CARD_DEFAULT_SUGGESTIONS),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const carouselButtonTextHasForbiddenChars = (value) => {
|
|
55
|
+
if (!value) return false;
|
|
56
|
+
if (value.includes('[') || value.includes(']')) return true;
|
|
57
|
+
const withoutValidVariables = value.replace(/\{\{[^}]*\}\}/g, '');
|
|
58
|
+
if (withoutValidVariables.includes('{') || withoutValidVariables.includes('}')) return true;
|
|
59
|
+
return false;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const isCompleteSavedCarouselSuggestion = (suggestion) => {
|
|
63
|
+
if (!suggestion || !suggestion.isSaved) return false;
|
|
64
|
+
const text = (suggestion.text || '').trim();
|
|
65
|
+
if (!text || !isValidText(text) || carouselButtonTextHasForbiddenChars(text)) return false;
|
|
66
|
+
if (suggestion.type === RCS_BUTTON_TYPES.PHONE_NUMBER) {
|
|
67
|
+
return String(suggestion.phoneNumber || '').length >= 5;
|
|
68
|
+
}
|
|
69
|
+
if (suggestion.type === RCS_BUTTON_TYPES.CTA) {
|
|
70
|
+
const url = String(suggestion.url || '').trim();
|
|
71
|
+
if (!url || url.length > URL_MAX_LENGTH) return false;
|
|
72
|
+
const subtype = suggestion.urlType || RCS_CTA_URL_TYPE.STATIC;
|
|
73
|
+
if (subtype === RCS_CTA_URL_TYPE.DYNAMIC) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (!isUrl(url)) return false;
|
|
77
|
+
const varMatches = url.match(invalidVarRegex);
|
|
78
|
+
return !(varMatches && varMatches.length > 0);
|
|
79
|
+
}
|
|
80
|
+
if (suggestion.type === RCS_BUTTON_TYPES.QUICK_REPLY) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const isCarouselCardValid = (card, cardIndex) => {
|
|
87
|
+
if (!card) return false;
|
|
88
|
+
if (!card.title || !card.title.trim()) return false;
|
|
89
|
+
if ((card.title || '').length > TEMPLATE_TITLE_MAX_LENGTH) return false;
|
|
90
|
+
if (!card.description || !card.description.trim()) return false;
|
|
91
|
+
if ((card.description || '').length > RCS_RICH_CARD_MAX_LENGTH) return false;
|
|
92
|
+
let mediaOk = false;
|
|
93
|
+
if (card.mediaType === RCS_MEDIA_TYPES.IMAGE) {
|
|
94
|
+
mediaOk = !!(card.imageSrc && String(card.imageSrc).trim());
|
|
95
|
+
} else if (card.mediaType === RCS_MEDIA_TYPES.VIDEO) {
|
|
96
|
+
const hasVideo = !!(card.videoAsset && card.videoAsset.videoSrc && String(card.videoAsset.videoSrc).trim());
|
|
97
|
+
const hasThumb = !!(card.thumbnailSrc && String(card.thumbnailSrc).trim());
|
|
98
|
+
mediaOk = hasVideo && hasThumb;
|
|
99
|
+
} else {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (!mediaOk) return false;
|
|
103
|
+
if (cardIndex === 0) {
|
|
104
|
+
const sugg = Array.isArray(card.suggestions) ? card.suggestions : [];
|
|
105
|
+
if (!sugg.some(isCompleteSavedCarouselSuggestion)) return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const checkDisableAddCarouselButton = (carouselData, activeCarouselIndex) => {
|
|
111
|
+
const idx = parseInt(activeCarouselIndex, 10);
|
|
112
|
+
const activeCard = carouselData?.[idx];
|
|
113
|
+
return !isCarouselCardValid(activeCard, idx);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** Carousel: global variables across the whole carousel (all cards, title+body) */
|
|
117
|
+
export const getNextCarouselVarToken = (carouselData) => {
|
|
118
|
+
const nums = [];
|
|
119
|
+
(carouselData || []).forEach((card = {}) => {
|
|
120
|
+
const titleMatches = (card.title || '').match(RCS_NUMERIC_VAR_TOKEN_REGEX) || [];
|
|
121
|
+
const descMatches = (card.description || '').match(RCS_NUMERIC_VAR_TOKEN_REGEX) || [];
|
|
122
|
+
[...titleMatches, ...descMatches].forEach((tok) => {
|
|
123
|
+
const num = parseInt((tok.match(/\d+/) || [])[0], 10);
|
|
124
|
+
if (!Number.isNaN(num)) nums.push(num);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const existing = new Set(nums);
|
|
128
|
+
let nextNumber = 1;
|
|
129
|
+
while (existing.has(nextNumber)) nextNumber++;
|
|
130
|
+
if (nextNumber > 19) return '';
|
|
131
|
+
return `{{${nextNumber}}}`;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** Carousel: per-card character counts (same limits as rich card) */
|
|
135
|
+
export const getCarouselTitleCharacterCount = (carouselData, cardIndex) => {
|
|
136
|
+
const titleText = carouselData?.[cardIndex]?.title || '';
|
|
137
|
+
return titleText ? titleText.length : 0;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const getCarouselDescriptionCharacterCount = (carouselData, cardIndex) => {
|
|
141
|
+
const descriptionText = carouselData?.[cardIndex]?.description || '';
|
|
142
|
+
return descriptionText ? descriptionText.length : 0;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** Preview (TestAndPreviewSlidebox / listing preview): resolve each card's title/desc through `resolveTemplateWithMap`. */
|
|
146
|
+
export const buildCarouselCardsForPreview = (cards = [], { isFullMode, rcsVarRegex, resolveTemplateWithMap }) => {
|
|
147
|
+
// Track cumulative variable slot index across all cards so resolveTemplateWithMap
|
|
148
|
+
// uses the correct globalSlot → slotKey for each token (e.g. card 1's {{3}} needs
|
|
149
|
+
// slotOffset=2 so slotKey becomes "3", not "1").
|
|
150
|
+
let slotOffset = 0;
|
|
151
|
+
return (cards || []).map((card = {}) => {
|
|
152
|
+
const rawTitle = card.title || '';
|
|
153
|
+
const rawDesc = card.description || '';
|
|
154
|
+
const titleVarCount = (rawTitle.match(rcsVarRegex) || []).length;
|
|
155
|
+
const descVarCount = (rawDesc.match(rcsVarRegex) || []).length;
|
|
156
|
+
const resolvedTitle = !isFullMode
|
|
157
|
+
? resolveTemplateWithMap(rawTitle, slotOffset)
|
|
158
|
+
: rawTitle;
|
|
159
|
+
const resolvedDesc = !isFullMode
|
|
160
|
+
? resolveTemplateWithMap(rawDesc, slotOffset + titleVarCount)
|
|
161
|
+
: rawDesc;
|
|
162
|
+
if (!isFullMode) {
|
|
163
|
+
slotOffset += titleVarCount + descVarCount;
|
|
164
|
+
}
|
|
165
|
+
const videoThumb = card.thumbnailSrc || card.videoAsset?.videoThumbnail || '';
|
|
166
|
+
const videoSrc = card.videoAsset?.videoSrc || '';
|
|
167
|
+
return {
|
|
168
|
+
mediaType: (card.mediaType || '').toLowerCase(),
|
|
169
|
+
imageSrc: card.imageSrc || '',
|
|
170
|
+
videoSrc,
|
|
171
|
+
videoPreviewImg: videoThumb,
|
|
172
|
+
title: resolvedTitle,
|
|
173
|
+
bodyText: resolvedDesc,
|
|
174
|
+
suggestions: card.suggestions || [],
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/** Payload (create/edit RCS template): build `cardContent` for the carousel case. */
|
|
180
|
+
export const buildCarouselCardContentForPayload = (carouselData, {
|
|
181
|
+
isSlotMappingMode,
|
|
182
|
+
cardVarMapped,
|
|
183
|
+
selectedCarouselHeight,
|
|
184
|
+
rcsVarRegex,
|
|
185
|
+
}) => (carouselData || []).map((card = {}) => {
|
|
186
|
+
const cardMediaType = card.mediaType;
|
|
187
|
+
const isCardVideo = cardMediaType === RCS_MEDIA_TYPES.VIDEO;
|
|
188
|
+
const isCardImage = cardMediaType === RCS_MEDIA_TYPES.IMAGE;
|
|
189
|
+
const mediaUrl = isCardVideo
|
|
190
|
+
? (card.videoAsset?.videoSrc || '')
|
|
191
|
+
: (card.imageSrc || '');
|
|
192
|
+
const thumbnailUrl = isCardVideo
|
|
193
|
+
? (card.thumbnailSrc || card.videoAsset?.videoThumbnail || '')
|
|
194
|
+
: '';
|
|
195
|
+
const cardVarTokens = [
|
|
196
|
+
...((card.title || '').match(rcsVarRegex) ?? []),
|
|
197
|
+
...((card.description || '').match(rcsVarRegex) ?? []),
|
|
198
|
+
];
|
|
199
|
+
const cardVarMappedForCard = {};
|
|
200
|
+
if (isSlotMappingMode && cardVarTokens.length > 0) {
|
|
201
|
+
cardVarTokens.forEach((token) => {
|
|
202
|
+
const varName = getVarNameFromToken(token);
|
|
203
|
+
if (!varName) return;
|
|
204
|
+
cardVarMappedForCard[varName] = sanitizeCardVarMappedValue(cardVarMapped?.[varName] ?? '');
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
title: card.title || '',
|
|
209
|
+
description: card.description || '',
|
|
210
|
+
mediaType: cardMediaType,
|
|
211
|
+
...((isCardImage || isCardVideo) && {
|
|
212
|
+
media: {
|
|
213
|
+
mediaUrl,
|
|
214
|
+
thumbnailUrl,
|
|
215
|
+
height: selectedCarouselHeight || MEDIUM,
|
|
216
|
+
...(isCardVideo && card.videoAsset?.videoName && { videoName: card.videoAsset.videoName }),
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
...(Array.isArray(card.suggestions) && card.suggestions.length > 0 && {
|
|
220
|
+
suggestions: card.suggestions,
|
|
221
|
+
}),
|
|
222
|
+
...(Object.keys(cardVarMappedForCard).length > 0 && { cardVarMapped: cardVarMappedForCard }),
|
|
223
|
+
};
|
|
224
|
+
});
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import CapCard from '@capillarytech/cap-ui-library/CapCard';
|
|
4
|
+
import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
|
|
5
|
+
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
6
|
+
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
7
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
8
|
+
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
|
|
9
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
10
|
+
import CapRadioGroup from '@capillarytech/cap-ui-library/CapRadioGroup';
|
|
11
|
+
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
12
|
+
import CapInput from '@capillarytech/cap-ui-library/CapInput';
|
|
13
|
+
import CapError from '@capillarytech/cap-ui-library/CapError';
|
|
14
|
+
import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
|
|
15
|
+
import TagList from '../../TagList';
|
|
16
|
+
import globalMessages from '../../Cap/messages';
|
|
17
|
+
import messages from '../messages';
|
|
18
|
+
import CarouselCardMedia from './CarouselCardMedia';
|
|
19
|
+
import CarouselCardButtons from './CarouselCardButtons';
|
|
20
|
+
import CarouselCharacterCount from './CarouselCharacterCount';
|
|
21
|
+
|
|
22
|
+
const { TextArea } = CapInput;
|
|
23
|
+
|
|
24
|
+
/** One carousel card's editor: media, title, description, buttons — used as a tab pane in the carousel editor. */
|
|
25
|
+
const CarouselCard = ({
|
|
26
|
+
cardIndex,
|
|
27
|
+
card,
|
|
28
|
+
carouselLength,
|
|
29
|
+
isEditFlow,
|
|
30
|
+
isFullMode,
|
|
31
|
+
mediaRadioOptions,
|
|
32
|
+
cardError,
|
|
33
|
+
location,
|
|
34
|
+
tagsForPicker,
|
|
35
|
+
injectedTags,
|
|
36
|
+
selectedOfferDetails,
|
|
37
|
+
formatMessage,
|
|
38
|
+
titleMaxLength,
|
|
39
|
+
descriptionMaxLength,
|
|
40
|
+
titleCharCount,
|
|
41
|
+
descriptionCharCount,
|
|
42
|
+
dimKey,
|
|
43
|
+
rcsData,
|
|
44
|
+
uploadRcsImage,
|
|
45
|
+
uploadRcsVideo,
|
|
46
|
+
hostName,
|
|
47
|
+
onDelete,
|
|
48
|
+
onMediaTypeChange,
|
|
49
|
+
onTitleChange,
|
|
50
|
+
onDescriptionChange,
|
|
51
|
+
onTitleAddVar,
|
|
52
|
+
onDescriptionAddVar,
|
|
53
|
+
onTagSelect,
|
|
54
|
+
onContextChange,
|
|
55
|
+
renderEditMessage,
|
|
56
|
+
onFieldChange,
|
|
57
|
+
onSuggestionsChange,
|
|
58
|
+
}) => (
|
|
59
|
+
<CapCard
|
|
60
|
+
title={`Card ${cardIndex + 1}`}
|
|
61
|
+
extra={
|
|
62
|
+
!isEditFlow &&
|
|
63
|
+
(carouselLength === 1 ? (
|
|
64
|
+
<CapTooltip title={formatMessage(messages.rcsCarouselMinCardDeleteTooltip)}>
|
|
65
|
+
<span className="button-disabled-tooltip-wrapper rcs-carousel-delete-tooltip-wrap">
|
|
66
|
+
<CapButton
|
|
67
|
+
className="rcs-carousel-card-delete"
|
|
68
|
+
type="flat"
|
|
69
|
+
onClick={onDelete}
|
|
70
|
+
disabled
|
|
71
|
+
aria-label={formatMessage(messages.rcsCarouselMinCardDeleteTooltip)}
|
|
72
|
+
>
|
|
73
|
+
<CapIcon type="delete" />
|
|
74
|
+
</CapButton>
|
|
75
|
+
</span>
|
|
76
|
+
</CapTooltip>
|
|
77
|
+
) : (
|
|
78
|
+
<CapButton
|
|
79
|
+
className="rcs-carousel-card-delete"
|
|
80
|
+
type="flat"
|
|
81
|
+
onClick={onDelete}
|
|
82
|
+
aria-label={formatMessage(globalMessages.delete)}
|
|
83
|
+
>
|
|
84
|
+
<CapIcon type="delete" />
|
|
85
|
+
</CapButton>
|
|
86
|
+
))
|
|
87
|
+
}
|
|
88
|
+
className="rcs-carousel-card"
|
|
89
|
+
>
|
|
90
|
+
{/* Media selection should be at top of card */}
|
|
91
|
+
{isEditFlow ? (
|
|
92
|
+
<div className="rcs-creative-name-readonly">
|
|
93
|
+
<CapHeading type="h4">{formatMessage(messages.mediaTypeLabel)}</CapHeading>
|
|
94
|
+
<CapHeading type="h5" className="rcs-creative-name-value">
|
|
95
|
+
{mediaRadioOptions.find((option) => option.value === card.mediaType)?.label || '-'}
|
|
96
|
+
</CapHeading>
|
|
97
|
+
</div>
|
|
98
|
+
) : (
|
|
99
|
+
<CapRow className="rcs-carousel-media-selection">
|
|
100
|
+
<CapColumn className="rcs-carousel-media-selection-heading">
|
|
101
|
+
<CapHeading type="h4">{formatMessage(messages.mediaTypeLabel)}</CapHeading>
|
|
102
|
+
</CapColumn>
|
|
103
|
+
<CapColumn>
|
|
104
|
+
<CapRadioGroup
|
|
105
|
+
id={`rcs-carousel-media-radio-${cardIndex}`}
|
|
106
|
+
options={mediaRadioOptions}
|
|
107
|
+
value={card.mediaType}
|
|
108
|
+
onChange={({ target: { value } }) => onMediaTypeChange(value)}
|
|
109
|
+
disabled={!isFullMode}
|
|
110
|
+
className="rcs-radio"
|
|
111
|
+
/>
|
|
112
|
+
</CapColumn>
|
|
113
|
+
</CapRow>
|
|
114
|
+
)}
|
|
115
|
+
|
|
116
|
+
<CapRow className={`rcs-carousel-media-upload ${isEditFlow ? 'rcs-carousel-media-upload--readonly-above' : ''}`}>
|
|
117
|
+
<CarouselCardMedia
|
|
118
|
+
cardIndex={cardIndex}
|
|
119
|
+
card={card}
|
|
120
|
+
dimKey={dimKey}
|
|
121
|
+
isFullMode={isFullMode}
|
|
122
|
+
isEditFlow={isEditFlow}
|
|
123
|
+
rcsData={rcsData}
|
|
124
|
+
uploadRcsImage={uploadRcsImage}
|
|
125
|
+
uploadRcsVideo={uploadRcsVideo}
|
|
126
|
+
formatMessage={formatMessage}
|
|
127
|
+
onFieldChange={onFieldChange}
|
|
128
|
+
/>
|
|
129
|
+
</CapRow>
|
|
130
|
+
|
|
131
|
+
{/* Title after media */}
|
|
132
|
+
<CapRow className="rcs-carousel-card-row">
|
|
133
|
+
<CapHeader
|
|
134
|
+
className="rcs-template-title-label"
|
|
135
|
+
title={<CapHeading type="h4">Card title</CapHeading>}
|
|
136
|
+
suffix={
|
|
137
|
+
(isEditFlow || !isFullMode) ? (
|
|
138
|
+
<TagList
|
|
139
|
+
label={formatMessage(globalMessages.addLabels)}
|
|
140
|
+
onTagSelect={onTagSelect}
|
|
141
|
+
location={location}
|
|
142
|
+
tags={tagsForPicker}
|
|
143
|
+
onContextChange={onContextChange}
|
|
144
|
+
injectedTags={injectedTags || {}}
|
|
145
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
146
|
+
/>
|
|
147
|
+
) : (
|
|
148
|
+
<CapButton
|
|
149
|
+
data-testid={`rcs-carousel-title-add-var-${cardIndex}`}
|
|
150
|
+
type="flat"
|
|
151
|
+
isAddBtn
|
|
152
|
+
onClick={onTitleAddVar}
|
|
153
|
+
disabled={!!cardError?.title}
|
|
154
|
+
>
|
|
155
|
+
{formatMessage(messages.addVar)}
|
|
156
|
+
</CapButton>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
<CapRow className="rcs_text_area_wrapper">
|
|
161
|
+
{(isEditFlow || !isFullMode) ? (
|
|
162
|
+
renderEditMessage(card.title || '')
|
|
163
|
+
) : (
|
|
164
|
+
<CapInput
|
|
165
|
+
value={card.title || ''}
|
|
166
|
+
placeholder={formatMessage(messages.templateTitlePlaceholder)}
|
|
167
|
+
onChange={onTitleChange}
|
|
168
|
+
disabled={isEditFlow || !isFullMode}
|
|
169
|
+
errorMessage={cardError?.title}
|
|
170
|
+
/>
|
|
171
|
+
)}
|
|
172
|
+
</CapRow>
|
|
173
|
+
</CapRow>
|
|
174
|
+
{!isEditFlow && (
|
|
175
|
+
<CapRow className="rcs-carousel-character-count-row">
|
|
176
|
+
<CarouselCharacterCount
|
|
177
|
+
formatMessage={formatMessage}
|
|
178
|
+
currentLength={titleCharCount}
|
|
179
|
+
maxLength={titleMaxLength}
|
|
180
|
+
/>
|
|
181
|
+
</CapRow>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
{/* Description after title */}
|
|
185
|
+
<CapRow className="rcs-carousel-card-row">
|
|
186
|
+
<CapHeader
|
|
187
|
+
title={<CapHeading type="h4">Card body text</CapHeading>}
|
|
188
|
+
suffix={
|
|
189
|
+
(isEditFlow || !isFullMode) ? (
|
|
190
|
+
<TagList
|
|
191
|
+
label={formatMessage(globalMessages.addLabels)}
|
|
192
|
+
onTagSelect={onTagSelect}
|
|
193
|
+
location={location}
|
|
194
|
+
tags={tagsForPicker}
|
|
195
|
+
onContextChange={onContextChange}
|
|
196
|
+
injectedTags={injectedTags || {}}
|
|
197
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
198
|
+
/>
|
|
199
|
+
) : (
|
|
200
|
+
<CapButton
|
|
201
|
+
data-testid={`rcs-carousel-desc-add-var-${cardIndex}`}
|
|
202
|
+
type="flat"
|
|
203
|
+
isAddBtn
|
|
204
|
+
onClick={onDescriptionAddVar}
|
|
205
|
+
disabled={!!cardError?.description}
|
|
206
|
+
>
|
|
207
|
+
{formatMessage(messages.addVar)}
|
|
208
|
+
</CapButton>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
/>
|
|
212
|
+
<CapRow className="rcs_text_area_wrapper">
|
|
213
|
+
{(isEditFlow || !isFullMode) ? (
|
|
214
|
+
renderEditMessage(card.description || '')
|
|
215
|
+
) : (
|
|
216
|
+
<TextArea
|
|
217
|
+
autosize={{ minRows: 3, maxRows: 5 }}
|
|
218
|
+
value={card.description || ''}
|
|
219
|
+
placeholder={formatMessage(messages.templateDescPlaceholder)}
|
|
220
|
+
onChange={onDescriptionChange}
|
|
221
|
+
disabled={isEditFlow || !isFullMode}
|
|
222
|
+
errorMessage={
|
|
223
|
+
cardError?.description && (
|
|
224
|
+
<CapError className="rcs-template-message-error">
|
|
225
|
+
{cardError.description}
|
|
226
|
+
</CapError>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
</CapRow>
|
|
232
|
+
</CapRow>
|
|
233
|
+
{!isEditFlow && (
|
|
234
|
+
<CapRow className="rcs-carousel-character-count-row">
|
|
235
|
+
<CarouselCharacterCount
|
|
236
|
+
formatMessage={formatMessage}
|
|
237
|
+
currentLength={descriptionCharCount}
|
|
238
|
+
maxLength={descriptionMaxLength}
|
|
239
|
+
/>
|
|
240
|
+
</CapRow>
|
|
241
|
+
)}
|
|
242
|
+
|
|
243
|
+
<CapDivider className="rcs-carousel-card-divider" />
|
|
244
|
+
<CarouselCardButtons
|
|
245
|
+
cardIndex={cardIndex}
|
|
246
|
+
suggestions={card.suggestions || []}
|
|
247
|
+
isEditFlow={isEditFlow}
|
|
248
|
+
isFullMode={isFullMode}
|
|
249
|
+
hostName={hostName}
|
|
250
|
+
formatMessage={formatMessage}
|
|
251
|
+
onSuggestionsChange={onSuggestionsChange}
|
|
252
|
+
location={location}
|
|
253
|
+
tags={tagsForPicker}
|
|
254
|
+
injectedTags={injectedTags}
|
|
255
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
256
|
+
onContextChange={onContextChange}
|
|
257
|
+
/>
|
|
258
|
+
</CapCard>
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
CarouselCard.propTypes = {
|
|
262
|
+
cardIndex: PropTypes.number.isRequired,
|
|
263
|
+
card: PropTypes.object,
|
|
264
|
+
carouselLength: PropTypes.number,
|
|
265
|
+
isEditFlow: PropTypes.bool,
|
|
266
|
+
isFullMode: PropTypes.bool,
|
|
267
|
+
mediaRadioOptions: PropTypes.array,
|
|
268
|
+
cardError: PropTypes.object,
|
|
269
|
+
location: PropTypes.object,
|
|
270
|
+
tagsForPicker: PropTypes.array,
|
|
271
|
+
injectedTags: PropTypes.object,
|
|
272
|
+
selectedOfferDetails: PropTypes.array,
|
|
273
|
+
formatMessage: PropTypes.func.isRequired,
|
|
274
|
+
titleMaxLength: PropTypes.number,
|
|
275
|
+
descriptionMaxLength: PropTypes.number,
|
|
276
|
+
titleCharCount: PropTypes.number,
|
|
277
|
+
descriptionCharCount: PropTypes.number,
|
|
278
|
+
dimKey: PropTypes.string,
|
|
279
|
+
rcsData: PropTypes.object,
|
|
280
|
+
uploadRcsImage: PropTypes.func.isRequired,
|
|
281
|
+
uploadRcsVideo: PropTypes.func.isRequired,
|
|
282
|
+
hostName: PropTypes.string,
|
|
283
|
+
onDelete: PropTypes.func.isRequired,
|
|
284
|
+
onMediaTypeChange: PropTypes.func.isRequired,
|
|
285
|
+
onTitleChange: PropTypes.func.isRequired,
|
|
286
|
+
onDescriptionChange: PropTypes.func.isRequired,
|
|
287
|
+
onTitleAddVar: PropTypes.func.isRequired,
|
|
288
|
+
onDescriptionAddVar: PropTypes.func.isRequired,
|
|
289
|
+
onTagSelect: PropTypes.func.isRequired,
|
|
290
|
+
onContextChange: PropTypes.func,
|
|
291
|
+
renderEditMessage: PropTypes.func.isRequired,
|
|
292
|
+
onFieldChange: PropTypes.func.isRequired,
|
|
293
|
+
onSuggestionsChange: PropTypes.func.isRequired,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
CarouselCard.defaultProps = {
|
|
297
|
+
card: {},
|
|
298
|
+
carouselLength: 1,
|
|
299
|
+
isEditFlow: false,
|
|
300
|
+
isFullMode: false,
|
|
301
|
+
mediaRadioOptions: [],
|
|
302
|
+
cardError: {},
|
|
303
|
+
location: {},
|
|
304
|
+
tagsForPicker: [],
|
|
305
|
+
injectedTags: {},
|
|
306
|
+
selectedOfferDetails: [],
|
|
307
|
+
titleMaxLength: 0,
|
|
308
|
+
descriptionMaxLength: 0,
|
|
309
|
+
titleCharCount: 0,
|
|
310
|
+
descriptionCharCount: 0,
|
|
311
|
+
dimKey: '',
|
|
312
|
+
rcsData: {},
|
|
313
|
+
hostName: '',
|
|
314
|
+
onContextChange: () => {},
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export default React.memo(CarouselCard);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
4
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
5
|
+
import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
|
|
6
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
7
|
+
import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
|
|
8
|
+
import CapActionButton from '../../../v2Components/CapActionButton';
|
|
9
|
+
import { MAX_BUTTONS, RCS_BUTTON_TYPES } from '../constants';
|
|
10
|
+
import messages from '../messages';
|
|
11
|
+
|
|
12
|
+
/** Reuse rich-card buttons UI per carousel card. */
|
|
13
|
+
const CarouselCardButtons = ({
|
|
14
|
+
cardIndex,
|
|
15
|
+
suggestions,
|
|
16
|
+
isEditFlow,
|
|
17
|
+
isFullMode,
|
|
18
|
+
hostName,
|
|
19
|
+
formatMessage,
|
|
20
|
+
onSuggestionsChange,
|
|
21
|
+
location,
|
|
22
|
+
tags,
|
|
23
|
+
injectedTags,
|
|
24
|
+
selectedOfferDetails,
|
|
25
|
+
onContextChange,
|
|
26
|
+
}) => {
|
|
27
|
+
const suggestionsForCard = suggestions || [];
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
<CapHeader
|
|
31
|
+
className="rcs-button-cta"
|
|
32
|
+
title={(
|
|
33
|
+
<CapRow type="flex">
|
|
34
|
+
<CapHeading type="h4">
|
|
35
|
+
{formatMessage(messages.btnLabel)}
|
|
36
|
+
</CapHeading>
|
|
37
|
+
</CapRow>
|
|
38
|
+
)}
|
|
39
|
+
description={!isEditFlow && (
|
|
40
|
+
<CapLabel type="label3">{formatMessage(messages.btnDesc)}</CapLabel>
|
|
41
|
+
)}
|
|
42
|
+
/>
|
|
43
|
+
<CapActionButton
|
|
44
|
+
buttonType={RCS_BUTTON_TYPES.NONE}
|
|
45
|
+
updateButtonChange={(data, btnIndex) => {
|
|
46
|
+
// Match existing behavior: allow CapActionButton to manage save gating.
|
|
47
|
+
const updated = cloneDeep(suggestionsForCard);
|
|
48
|
+
if (btnIndex === MAX_BUTTONS) {
|
|
49
|
+
onSuggestionsChange(data);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
updated[btnIndex] = data;
|
|
53
|
+
onSuggestionsChange(updated);
|
|
54
|
+
}}
|
|
55
|
+
deleteButtonHandler={(btnIndex) => {
|
|
56
|
+
if (cardIndex === 0 && btnIndex === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const savedCount = (suggestionsForCard || []).filter((suggestion) => suggestion && suggestion.isSaved).length;
|
|
60
|
+
const target = (suggestionsForCard || []).find((suggestion) => suggestion && suggestion.index === btnIndex);
|
|
61
|
+
if (cardIndex === 0 && target?.isSaved && savedCount <= 1) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const updated = cloneDeep(suggestionsForCard)
|
|
65
|
+
.filter((i) => i.index !== btnIndex)
|
|
66
|
+
.map((i, idx) => ({ ...i, index: idx }));
|
|
67
|
+
onSuggestionsChange(updated);
|
|
68
|
+
}}
|
|
69
|
+
suggestions={suggestionsForCard}
|
|
70
|
+
isEditFlow={isEditFlow}
|
|
71
|
+
isFullMode={isFullMode}
|
|
72
|
+
maxButtons={MAX_BUTTONS}
|
|
73
|
+
host={hostName}
|
|
74
|
+
minSavedSuggestions={cardIndex === 0 ? 1 : 0}
|
|
75
|
+
hideDeleteSuggestionIndexes={cardIndex === 0 ? [0] : []}
|
|
76
|
+
location={location}
|
|
77
|
+
tags={tags}
|
|
78
|
+
injectedTags={injectedTags}
|
|
79
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
80
|
+
onContextChange={onContextChange}
|
|
81
|
+
/>
|
|
82
|
+
</>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
CarouselCardButtons.propTypes = {
|
|
87
|
+
cardIndex: PropTypes.number.isRequired,
|
|
88
|
+
suggestions: PropTypes.array,
|
|
89
|
+
isEditFlow: PropTypes.bool,
|
|
90
|
+
isFullMode: PropTypes.bool,
|
|
91
|
+
hostName: PropTypes.string,
|
|
92
|
+
formatMessage: PropTypes.func.isRequired,
|
|
93
|
+
onSuggestionsChange: PropTypes.func.isRequired,
|
|
94
|
+
location: PropTypes.object,
|
|
95
|
+
tags: PropTypes.array,
|
|
96
|
+
injectedTags: PropTypes.object,
|
|
97
|
+
selectedOfferDetails: PropTypes.array,
|
|
98
|
+
onContextChange: PropTypes.func,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
CarouselCardButtons.defaultProps = {
|
|
102
|
+
suggestions: [],
|
|
103
|
+
isEditFlow: false,
|
|
104
|
+
isFullMode: false,
|
|
105
|
+
hostName: '',
|
|
106
|
+
location: {},
|
|
107
|
+
tags: [],
|
|
108
|
+
injectedTags: {},
|
|
109
|
+
selectedOfferDetails: [],
|
|
110
|
+
onContextChange: () => {},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default React.memo(CarouselCardButtons);
|