@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/AppRoot.js +80 -0
  2. package/app.js +5 -3
  3. package/constants/unified.js +0 -29
  4. package/entry.js +67 -1
  5. package/global-styles.js +1 -1
  6. package/mfe-exposed-components.js +2 -4
  7. package/package.json +2 -2
  8. package/services/api.js +1 -1
  9. package/services/tests/api.test.js +20 -35
  10. package/styles/containers/layout/_layoutPage.scss +8 -6
  11. package/utils/commonUtils.js +1 -19
  12. package/utils/getDataLayer.js +15 -0
  13. package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
  14. package/utils/mfeDetect.js +1 -0
  15. package/v2Components/CapActionButton/constants.js +1 -8
  16. package/v2Components/CapActionButton/index.js +144 -202
  17. package/v2Components/CapActionButton/index.scss +14 -232
  18. package/v2Components/CapActionButton/messages.js +3 -19
  19. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  20. package/v2Components/CapImageUpload/index.js +2 -2
  21. package/v2Components/CapTagList/index.js +0 -10
  22. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  26. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  27. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  28. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  29. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  34. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  35. package/v2Components/CommonTestAndPreview/index.js +222 -761
  36. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  37. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  38. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  47. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  48. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  49. package/v2Components/NavigationBar/index.js +9 -7
  50. package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
  51. package/v2Components/Pagination/_pagination.scss +2 -0
  52. package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
  53. package/v2Components/TemplatePreview/index.js +32 -147
  54. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  55. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  56. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  57. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  59. package/v2Containers/Cap/constants.js +1 -0
  60. package/v2Containers/Cap/index.js +47 -21
  61. package/v2Containers/Cap/sagas.js +19 -0
  62. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  63. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  64. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  65. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
  66. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  67. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  68. package/v2Containers/CreativesContainer/constants.js +0 -11
  69. package/v2Containers/CreativesContainer/index.js +104 -323
  70. package/v2Containers/CreativesContainer/index.scss +1 -83
  71. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  72. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  74. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  75. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  76. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  77. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  78. package/v2Containers/Rcs/constants.js +11 -131
  79. package/v2Containers/Rcs/index.js +829 -2682
  80. package/v2Containers/Rcs/index.scss +8 -443
  81. package/v2Containers/Rcs/messages.js +6 -45
  82. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
  83. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
  84. package/v2Containers/Rcs/tests/index.test.js +40 -151
  85. package/v2Containers/Rcs/tests/mockData.js +0 -38
  86. package/v2Containers/Rcs/tests/utils.test.js +30 -643
  87. package/v2Containers/Rcs/utils.js +12 -466
  88. package/v2Containers/Sms/Create/index.js +48 -115
  89. package/v2Containers/SmsTrai/Create/index.js +4 -9
  90. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  91. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  92. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  93. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  94. package/v2Containers/SmsWrapper/index.js +8 -37
  95. package/v2Containers/TagList/index.js +0 -6
  96. package/v2Containers/Templates/_templates.scss +11 -196
  97. package/v2Containers/Templates/actions.js +0 -11
  98. package/v2Containers/Templates/constants.js +0 -2
  99. package/v2Containers/Templates/index.js +60 -131
  100. package/v2Containers/Templates/sagas.js +13 -57
  101. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  102. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  103. package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
  104. package/v2Containers/TemplatesV2/index.js +43 -86
  105. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  106. package/v2Containers/Whatsapp/index.js +25 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  108. package/utils/rcsPayloadUtils.js +0 -92
  109. package/utils/templateVarUtils.js +0 -201
  110. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  111. package/utils/tests/templateVarUtils.test.js +0 -204
  112. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  113. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  114. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  115. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  116. package/v2Components/SmsFallback/constants.js +0 -94
  117. package/v2Components/SmsFallback/index.js +0 -958
  118. package/v2Components/SmsFallback/index.scss +0 -266
  119. package/v2Components/SmsFallback/messages.js +0 -78
  120. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  121. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  122. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  123. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  124. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  125. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  126. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  127. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  128. package/v2Components/TemplatePreview/constants.js +0 -2
  129. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  130. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  131. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  132. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  133. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  134. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  135. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  136. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  137. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  138. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  139. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  140. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  141. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  142. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  143. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  144. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  145. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,92 +0,0 @@
1
- import isEmpty from 'lodash/isEmpty';
2
- import { RCS_API_SMS_FALLBACK_KEYS } from '../constants/unified';
3
- import {
4
- COMBINED_SMS_TEMPLATE_VAR_REGEX,
5
- isAnyTemplateVarToken,
6
- splitTemplateVarString,
7
- } from './templateVarUtils';
8
-
9
- const {
10
- MESSAGE,
11
- TEMPLATE_CONFIGS,
12
- RCS_SMS_FALLBACK_VAR_MAPPED,
13
- } = RCS_API_SMS_FALLBACK_KEYS;
14
-
15
- function mergeSmsFallbackSlots(templateStr, varMapped) {
16
- const map = varMapped != null && typeof varMapped === 'object' ? varMapped : {};
17
- if (!Object.keys(map).length) return typeof templateStr === 'string' ? templateStr : '';
18
- const str = typeof templateStr === 'string' ? templateStr : '';
19
- return splitTemplateVarString(str, COMBINED_SMS_TEMPLATE_VAR_REGEX)
20
- .map((seg, i) => {
21
- if (!isAnyTemplateVarToken(seg)) return seg;
22
- const key = `${seg}_${i}`;
23
- if (Object.prototype.hasOwnProperty.call(map, key)) {
24
- const v = map[key];
25
- return v == null ? '' : String(v);
26
- }
27
- return seg;
28
- })
29
- .join('');
30
- }
31
-
32
- /**
33
- * Mutates one RCS messageContent entry: strips UI-only fields, promotes smsFallBackContent to a
34
- * sibling of rcsContent, and folds rcsSmsFallbackVarMapped into the message string for the API.
35
- */
36
- export const normalizeRcsMessageContentForApi = messageContentItem => {
37
- const {
38
- rcsContent = {},
39
- smsFallBackContent: rootSmsFallbackContent = {},
40
- } = messageContentItem;
41
- const {
42
- smsFallBackContent: legacySmsFallbackNestedUnderCard,
43
- ...rcsCardPayloadOnly
44
- } = { ...rcsContent };
45
- /* Legacy nested first, then root — so current root `smsFallBackContent` wins on key clashes. */
46
- const mergedSmsFallbackSources = {
47
- ...(legacySmsFallbackNestedUnderCard ?? {}),
48
- ...rootSmsFallbackContent,
49
- };
50
- messageContentItem.rcsContent = rcsCardPayloadOnly;
51
-
52
- delete messageContentItem.templateConfigs;
53
- delete rcsCardPayloadOnly.templateConfigs;
54
-
55
- if (Array.isArray(rcsCardPayloadOnly.cardContent)) {
56
- rcsCardPayloadOnly.cardContent.forEach(card => {
57
- if (Array.isArray(card.suggestions)) {
58
- card.suggestions.forEach(suggestion => {
59
- delete suggestion.isSaved;
60
- });
61
- }
62
- });
63
- }
64
-
65
- const varMappedMerged = mergedSmsFallbackSources[RCS_SMS_FALLBACK_VAR_MAPPED];
66
- const hasVarMapped =
67
- varMappedMerged != null
68
- && typeof varMappedMerged === 'object'
69
- && Object.keys(varMappedMerged).length > 0;
70
- const hasMessageKey = Object.prototype.hasOwnProperty.call(mergedSmsFallbackSources, MESSAGE);
71
- const rawMessage = hasMessageKey ? mergedSmsFallbackSources[MESSAGE] : undefined;
72
- const messageForApi =
73
- hasMessageKey && hasVarMapped
74
- ? mergeSmsFallbackSlots(rawMessage == null ? '' : String(rawMessage), varMappedMerged)
75
- : rawMessage;
76
-
77
- const apiSiblingSmsFallback = Object.fromEntries(
78
- [
79
- hasMessageKey && [MESSAGE, messageForApi],
80
- !isEmpty(mergedSmsFallbackSources[TEMPLATE_CONFIGS]) && [
81
- TEMPLATE_CONFIGS,
82
- mergedSmsFallbackSources[TEMPLATE_CONFIGS],
83
- ],
84
- ].filter(Boolean),
85
- );
86
-
87
- if (Object.keys(apiSiblingSmsFallback).length) {
88
- messageContentItem.smsFallBackContent = apiSiblingSmsFallback;
89
- } else {
90
- delete messageContentItem.smsFallBackContent;
91
- }
92
- };
@@ -1,201 +0,0 @@
1
- /**
2
- * Shared utilities for templates containing {{var}} and DLT `{#var#}` tokens.
3
- * Same split process used by WhatsApp/RCS: match vars with regex, then split content at each var.
4
- */
5
-
6
- import {
7
- COMBINED_SMS_TEMPLATE_VAR_REGEX,
8
- DEFAULT_MUSTACHE_VAR_REGEX,
9
- DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX,
10
- DLT_HASH_TOKEN_INNER_NAME_REGEX,
11
- DLT_VAR_BODY_CAPTURE_REGEX,
12
- MUSTACHE_TOKEN_INNER_NAME_REGEX,
13
- MUSTACHE_VAR_NAME_CAPTURE_REGEX,
14
- MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX,
15
- } from '../constants/unified';
16
-
17
- export { COMBINED_SMS_TEMPLATE_VAR_REGEX, DEFAULT_MUSTACHE_VAR_REGEX } from '../constants/unified';
18
-
19
- const isMustacheVarToken = (s) =>
20
- typeof s === 'string' && MUSTACHE_VAR_TOKEN_FULL_STRING_REGEX.test(s);
21
-
22
- export const isDltHashVarToken = (s) =>
23
- typeof s === 'string' && DLT_HASH_VAR_TOKEN_FULL_STRING_REGEX.test(s);
24
-
25
- export const isAnyTemplateVarToken = (s) => isMustacheVarToken(s) || isDltHashVarToken(s);
26
-
27
- /**
28
- * `RegExp.prototype.exec` only advances `lastIndex` when the `g` and/or `y` flag is set.
29
- * A non-global regex in a `while ((m = re.exec(s)) !== null)` loop never advances and can run forever.
30
- *
31
- * @param {RegExp} regex
32
- * @returns {RegExp} Same instance if already global; otherwise a new RegExp with `g` appended to flags.
33
- */
34
- function ensureGlobalRegexForExecLoop(regex) {
35
- if (!regex || !(regex instanceof RegExp) || regex.global) {
36
- return regex;
37
- }
38
- return new RegExp(regex.source, `${regex.flags}g`);
39
- }
40
-
41
- /**
42
- * Splits `content` into alternating plain-text segments and variable tokens, using the order of
43
- * matches in `matchedVariableTokens` (e.g. from `String.prototype.match` with a global regex).
44
- *
45
- * @param {string[]} matchedVariableTokens - Matched tokens in left-to-right order
46
- * @param {string} content - Full template string
47
- * @returns {string[]}
48
- */
49
- export const splitContentByOrderedVarTokens = (matchedVariableTokens, content) => {
50
- const segmentList = [];
51
- const tokenQueue = [...(matchedVariableTokens ?? [])];
52
- let remainder = content ?? '';
53
- while ((remainder?.length ?? 0) > 0) {
54
- const nextVarToken = tokenQueue?.[0];
55
- if (nextVarToken == null || nextVarToken === '') {
56
- segmentList.push(remainder);
57
- break;
58
- }
59
- const varStartIndex = remainder.indexOf(nextVarToken);
60
- if (varStartIndex !== -1) {
61
- segmentList.push(remainder.substring(0, varStartIndex));
62
- segmentList.push(nextVarToken);
63
- const afterVar = varStartIndex + (nextVarToken?.length ?? 0);
64
- remainder = remainder.substring(afterVar, remainder?.length ?? 0);
65
- tokenQueue.shift();
66
- } else {
67
- segmentList.push(remainder);
68
- break;
69
- }
70
- }
71
- return segmentList;
72
- };
73
-
74
- /**
75
- * Splits a template string into an array of text + {{var}} segments using the given regex.
76
- *
77
- * @param {string} str - Template string
78
- * @param {RegExp} [varRegex] - Regex that matches var tokens; defaults to DEFAULT_MUSTACHE_VAR_REGEX
79
- * @returns {string[]}
80
- */
81
- export const splitTemplateVarString = (str = '', varRegex = DEFAULT_MUSTACHE_VAR_REGEX) => {
82
- if (!str) return [];
83
- const matchedVariableTokens = str.match(varRegex) || [];
84
- return splitContentByOrderedVarTokens(matchedVariableTokens, str).filter(
85
- (segment) => segment !== ''
86
- );
87
- };
88
-
89
- /**
90
- * Extracts unique variable names from `{{var}}` (and, when using default capture, `{#var#}`) strings.
91
- *
92
- * @param {string} templateStr
93
- * @param {RegExp} [captureRegex] - regex with a single capture group for the var name; if omitted, also scans DLT `{#…#}`.
94
- * @returns {string[]}
95
- */
96
- export const extractTemplateVariables = (templateStr = '', captureRegex) => {
97
- if (!templateStr) return [];
98
- const variables = [];
99
- const addUniqueVar = (name) => {
100
- const trimmedName = (name || '').trim();
101
- if (trimmedName && !variables.includes(trimmedName)) variables.push(trimmedName);
102
- };
103
- const mustacheRe = ensureGlobalRegexForExecLoop(
104
- captureRegex || MUSTACHE_VAR_NAME_CAPTURE_REGEX,
105
- );
106
- let match;
107
- while ((match = mustacheRe.exec(templateStr)) !== null) {
108
- addUniqueVar(match?.[1]);
109
- }
110
- if (!captureRegex) {
111
- const dltRe = ensureGlobalRegexForExecLoop(DLT_VAR_BODY_CAPTURE_REGEX);
112
- let dltMatch;
113
- while ((dltMatch = dltRe.exec(templateStr)) !== null) {
114
- addUniqueVar(dltMatch?.[1] || 'var');
115
- }
116
- }
117
- return variables;
118
- };
119
-
120
- /**
121
- * Looks up the inner name of a `{{name}}` or `{#name#}` token in a flat key→value map.
122
- * Handles both exact matches and dot-path suffixes (e.g. `tag.FORMAT_1` → name `FORMAT_1`).
123
- * Returns the resolved string value, or `undefined` if not found / blank.
124
- */
125
- function resolveUserVarForToken(segment, userVarMap) {
126
- if (!userVarMap || typeof userVarMap !== 'object') return undefined;
127
- const mMustache = segment.match(MUSTACHE_TOKEN_INNER_NAME_REGEX);
128
- const mDlt = segment.match(DLT_HASH_TOKEN_INNER_NAME_REGEX);
129
- const innerName = ((mMustache ? mMustache[1] : (mDlt ? mDlt[1] : '')) ?? '').trim();
130
- if (!innerName) return undefined;
131
- const direct = userVarMap[innerName];
132
- if (direct != null && String(direct).trim() !== '') return String(direct);
133
- const hit = Object.keys(userVarMap).find((k) => k === innerName || k.endsWith(`.${innerName}`));
134
- if (hit != null && userVarMap[hit] != null && String(userVarMap[hit]).trim() !== '') return String(userVarMap[hit]);
135
- return undefined;
136
- }
137
-
138
- /**
139
- * SMS / DLT template preview: replace `{{…}}` / `{#…#}` tokens using `varMapData` keys `${token}_${index}`.
140
- * Used by SmsTraiEdit (RCS SMS fallback) and UnifiedPreview fallback SMS bubble.
141
- * DLT `{#…#}`: empty / unset slot values show the raw token (matches DLT preview UX); mustache `{{…}}` still
142
- * resolves to empty when the slot is cleared.
143
- *
144
- * @param {string} templateStr
145
- * @param {Object} varMapData - Slot map (`${token}_${index}` → value)
146
- * @param {Object|null} [userVarMap] - Optional Test & Preview custom values; used as fallback when a slot
147
- * is absent or blank. When omitted the function behaves identically to its original two-argument form.
148
- */
149
- export const getFallbackResolvedContent = (templateStr = '', varMapData = {}, userVarMap = null) => {
150
- const fallbackVarSlotMap = varMapData ?? {};
151
- const templateSegments = splitTemplateVarString(templateStr, COMBINED_SMS_TEMPLATE_VAR_REGEX);
152
- return templateSegments
153
- .map((segment, segmentIndex) => {
154
- const isVariableToken = typeof segment === 'string' && isAnyTemplateVarToken(segment);
155
- if (!isVariableToken) return segment;
156
- const slotKey = `${segment}_${segmentIndex}`;
157
- if (Object.prototype.hasOwnProperty.call(fallbackVarSlotMap, slotKey)) {
158
- const slotValue = fallbackVarSlotMap[slotKey];
159
- if (isDltHashVarToken(segment)) {
160
- if (slotValue == null || String(slotValue).trim() === '') {
161
- if (userVarMap) { const uv = resolveUserVarForToken(segment, userVarMap); if (uv !== undefined) return uv; }
162
- return segment;
163
- }
164
- return String(slotValue);
165
- }
166
- if (slotValue != null && String(slotValue).trim() !== '') return String(slotValue);
167
- if (userVarMap) { const uv = resolveUserVarForToken(segment, userVarMap); if (uv !== undefined) return uv; }
168
- return '';
169
- }
170
- if (userVarMap) { const uv = resolveUserVarForToken(segment, userVarMap); if (uv !== undefined) return uv; }
171
- if (isDltHashVarToken(segment)) return segment;
172
- return '';
173
- })
174
- .join('');
175
- };
176
-
177
- /**
178
- * SMS fallback **card** (library list): show filled slot text; if a slot is empty, unset, or
179
- * whitespace-only, show the raw `{{…}}` / `{#…#}` token so “save without labels” still shows
180
- * `{#var#}` in the card. {@link getFallbackResolvedContent} keeps DLT placeholders in preview when
181
- * slots are empty; mustache cleared slots can still render empty.
182
- */
183
- export const getFallbackResolvedContentForCardDisplay = (templateStr = '', varMapData = {}) => {
184
- const fallbackVarSlotMap = varMapData ?? {};
185
- const templateSegments = splitTemplateVarString(templateStr, COMBINED_SMS_TEMPLATE_VAR_REGEX);
186
- return templateSegments
187
- .map((segment, segmentIndex) => {
188
- const isVariableToken = typeof segment === 'string' && isAnyTemplateVarToken(segment);
189
- if (!isVariableToken) return segment;
190
- const slotKey = `${segment}_${segmentIndex}`;
191
- if (Object.prototype.hasOwnProperty.call(fallbackVarSlotMap, slotKey)) {
192
- const slotValue = fallbackVarSlotMap[slotKey];
193
- if (slotValue == null) return segment;
194
- const str = String(slotValue);
195
- if (str.trim() === '') return segment;
196
- return str;
197
- }
198
- return segment;
199
- })
200
- .join('');
201
- };
@@ -1,226 +0,0 @@
1
- import { normalizeRcsMessageContentForApi } from '../rcsPayloadUtils';
2
-
3
- describe('normalizeRcsMessageContentForApi', () => {
4
- it('defaults missing rcsContent to empty object and still normalizes root smsFallBackContent', () => {
5
- const item = {
6
- smsFallBackContent: { message: 'root-only' },
7
- };
8
- normalizeRcsMessageContentForApi(item);
9
- expect(item.rcsContent).toEqual({});
10
- expect(item.smsFallBackContent).toEqual({ message: 'root-only' });
11
- });
12
-
13
- it('moves nested smsFallBackContent out of rcsContent and merges onto root', () => {
14
- const item = {
15
- rcsContent: {
16
- contentType: 'RICHCARD',
17
- smsFallBackContent: {
18
- message: 'nested-msg',
19
- templateConfigs: { registeredSenderIds: ['H1'] },
20
- },
21
- },
22
- smsFallBackContent: { message: 'root-msg' },
23
- };
24
- normalizeRcsMessageContentForApi(item);
25
- expect(item.rcsContent).toEqual({ contentType: 'RICHCARD' });
26
- expect(item.smsFallBackContent.message).toBe('root-msg');
27
- expect(item.smsFallBackContent.templateConfigs).toEqual({
28
- registeredSenderIds: ['H1'],
29
- });
30
- });
31
-
32
- it('nested message merges when root has no message key', () => {
33
- const item = {
34
- rcsContent: {
35
- smsFallBackContent: { message: 'from-nested' },
36
- },
37
- };
38
- normalizeRcsMessageContentForApi(item);
39
- expect(item.rcsContent).toEqual({});
40
- expect(item.smsFallBackContent).toEqual({ message: 'from-nested' });
41
- });
42
-
43
- it('removes smsFallBackContent when nothing to serialize', () => {
44
- const item = {
45
- rcsContent: { smsFallBackContent: {} },
46
- smsFallBackContent: {},
47
- };
48
- normalizeRcsMessageContentForApi(item);
49
- expect(item.rcsContent).toEqual({});
50
- expect(item.smsFallBackContent).toBeUndefined();
51
- });
52
-
53
- it('does not add templateConfigs when empty', () => {
54
- const item = {
55
- rcsContent: {},
56
- smsFallBackContent: { message: 'x', templateConfigs: {} },
57
- };
58
- normalizeRcsMessageContentForApi(item);
59
- expect(item.smsFallBackContent).toEqual({ message: 'x' });
60
- });
61
-
62
- it('serializes templateConfigs only when message is absent but templateConfigs has data', () => {
63
- const item = {
64
- rcsContent: {
65
- smsFallBackContent: {
66
- templateConfigs: { registeredSenderIds: ['Z'] },
67
- },
68
- },
69
- };
70
- normalizeRcsMessageContentForApi(item);
71
- expect(item.smsFallBackContent).toEqual({
72
- templateConfigs: { registeredSenderIds: ['Z'] },
73
- });
74
- });
75
-
76
- it('includes message in api payload when key exists with empty string', () => {
77
- const item = {
78
- smsFallBackContent: { message: '', templateConfigs: { id: 't1' } },
79
- };
80
- normalizeRcsMessageContentForApi(item);
81
- expect(item.smsFallBackContent).toEqual({
82
- message: '',
83
- templateConfigs: { id: 't1' },
84
- });
85
- });
86
-
87
- it('merges when legacy nested is null-coalesced', () => {
88
- const item = {
89
- rcsContent: {
90
- contentType: 'TEXT',
91
- smsFallBackContent: null,
92
- },
93
- smsFallBackContent: { message: 'from-root' },
94
- };
95
- normalizeRcsMessageContentForApi(item);
96
- expect(item.rcsContent).toEqual({ contentType: 'TEXT' });
97
- expect(item.smsFallBackContent).toEqual({ message: 'from-root' });
98
- });
99
-
100
- it('folds rcsSmsFallbackVarMapped into message and omits var map from API shape', () => {
101
- const msg = '{{optout}} {{fullname}} test SMS';
102
- const varMapped = {
103
- '{{optout}}_0': '{{city}}',
104
- '{{fullname}}_2': '{{fullname}}',
105
- };
106
- const item = {
107
- rcsContent: { contentType: 'RICHCARD' },
108
- smsFallBackContent: {
109
- message: msg,
110
- rcsSmsFallbackVarMapped: varMapped,
111
- },
112
- };
113
- normalizeRcsMessageContentForApi(item);
114
- expect(item.smsFallBackContent).toEqual({
115
- message: '{{city}} {{fullname}} test SMS',
116
- });
117
- expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
118
- });
119
-
120
- it('merges nested rcsSmsFallbackVarMapped with root message; root message wins on key clash', () => {
121
- const nestedVar = { '{{a}}_0': '{{nested}}' };
122
- const item = {
123
- rcsContent: {
124
- contentType: 'RICHCARD',
125
- smsFallBackContent: {
126
- message: '{{a}} nested-tail',
127
- rcsSmsFallbackVarMapped: nestedVar,
128
- },
129
- },
130
- smsFallBackContent: {
131
- message: '{{a}} tail',
132
- rcsSmsFallbackVarMapped: { '{{a}}_0': '{{root}}' },
133
- },
134
- };
135
- normalizeRcsMessageContentForApi(item);
136
- expect(item.smsFallBackContent.message).toBe('{{root}} tail');
137
- expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
138
- });
139
-
140
- it('non-slot var map keys do not change message but var map is still stripped', () => {
141
- const varMapped = { 0: { label: 'City', tag: '{{city}}' } };
142
- const item = {
143
- rcsContent: { contentType: 'RICHCARD' },
144
- smsFallBackContent: {
145
- message: 'Hello',
146
- rcsSmsFallbackVarMapped: varMapped,
147
- },
148
- };
149
- normalizeRcsMessageContentForApi(item);
150
- expect(item.smsFallBackContent).toEqual({ message: 'Hello' });
151
- });
152
-
153
- it('omits rcsSmsFallbackVarMapped when merged value is empty object', () => {
154
- const item = {
155
- rcsContent: {
156
- smsFallBackContent: { rcsSmsFallbackVarMapped: {} },
157
- },
158
- smsFallBackContent: { message: 'only-msg' },
159
- };
160
- normalizeRcsMessageContentForApi(item);
161
- expect(item.smsFallBackContent).toEqual({ message: 'only-msg' });
162
- expect(item.smsFallBackContent.rcsSmsFallbackVarMapped).toBeUndefined();
163
- });
164
-
165
- it('removes root-level templateConfigs from RCS message content item', () => {
166
- const item = {
167
- rcsContent: { cardType: 'STANDALONE' },
168
- templateConfigs: {
169
- templateId: '69b3d33934a7bd0db5bada8e',
170
- template: '{{registered_store_name}} Its a test message',
171
- },
172
- };
173
- normalizeRcsMessageContentForApi(item);
174
- expect(item.templateConfigs).toBeUndefined();
175
- expect(item.rcsContent).toEqual({ cardType: 'STANDALONE' });
176
- });
177
-
178
- it('removes templateConfigs nested inside rcsContent', () => {
179
- const item = {
180
- rcsContent: {
181
- cardType: 'STANDALONE',
182
- templateConfigs: {
183
- templateId: '69b3d33934a7bd0db5bada8e',
184
- template: '{{registered_store_name}} Its a test message',
185
- },
186
- cardContent: [{ title: 'Card' }],
187
- },
188
- };
189
- normalizeRcsMessageContentForApi(item);
190
- expect(item.templateConfigs).toBeUndefined();
191
- expect(item.rcsContent.templateConfigs).toBeUndefined();
192
- expect(item.rcsContent.cardContent).toEqual([{ title: 'Card' }]);
193
- });
194
-
195
- it('strips isSaved from each suggestion in cardContent', () => {
196
- const item = {
197
- rcsContent: {
198
- cardContent: [
199
- {
200
- title: 'Card 1',
201
- suggestions: [
202
- { type: 'QUICK_REPLY', text: 'Yes', isSaved: true },
203
- { type: 'CTA', text: 'Go', url: 'https://example.com', isSaved: false },
204
- ],
205
- },
206
- ],
207
- },
208
- };
209
- normalizeRcsMessageContentForApi(item);
210
- expect(item.rcsContent.cardContent[0].suggestions).toEqual([
211
- { type: 'QUICK_REPLY', text: 'Yes' },
212
- { type: 'CTA', text: 'Go', url: 'https://example.com' },
213
- ]);
214
- });
215
-
216
- it('handles cardContent with no suggestions gracefully', () => {
217
- const item = {
218
- rcsContent: {
219
- cardContent: [{ title: 'Card 1' }, { title: 'Card 2', suggestions: [] }],
220
- },
221
- };
222
- normalizeRcsMessageContentForApi(item);
223
- expect(item.rcsContent.cardContent[0].suggestions).toBeUndefined();
224
- expect(item.rcsContent.cardContent[1].suggestions).toEqual([]);
225
- });
226
- });