@capillarytech/creatives-library 9.0.29 → 9.0.31

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 (147) hide show
  1. package/constants/unified.js +29 -0
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/commonUtils.js +22 -1
  6. package/utils/rcsPayloadUtils.js +102 -0
  7. package/utils/templateVarUtils.js +198 -0
  8. package/utils/tests/rcsPayloadUtils.test.js +295 -0
  9. package/utils/tests/templateVarUtils.test.js +204 -0
  10. package/v2Components/CapActionButton/constants.js +21 -1
  11. package/v2Components/CapActionButton/index.js +261 -144
  12. package/v2Components/CapActionButton/index.scss +245 -14
  13. package/v2Components/CapActionButton/messages.js +32 -3
  14. package/v2Components/CapActionButton/tests/index.test.js +74 -19
  15. package/v2Components/CapImageUpload/index.js +2 -2
  16. package/v2Components/CapTagList/index.js +9 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
  28. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  29. package/v2Components/CommonTestAndPreview/constants.js +35 -2
  30. package/v2Components/CommonTestAndPreview/index.js +774 -231
  31. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  32. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  33. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  34. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  46. package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
  47. package/v2Components/CommonTestAndPreview/utils.js +84 -0
  48. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  49. package/v2Components/SmsFallback/constants.js +94 -0
  50. package/v2Components/SmsFallback/index.js +958 -0
  51. package/v2Components/SmsFallback/index.scss +266 -0
  52. package/v2Components/SmsFallback/messages.js +78 -0
  53. package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
  54. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  55. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
  58. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  59. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
  60. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  61. package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
  62. package/v2Components/TemplatePreview/constants.js +2 -0
  63. package/v2Components/TemplatePreview/index.js +147 -32
  64. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  65. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  66. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  67. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  68. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  69. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  70. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  71. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  72. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
  73. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
  74. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  75. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  79. package/v2Containers/CreativesContainer/constants.js +11 -0
  80. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
  81. package/v2Containers/CreativesContainer/index.js +322 -106
  82. package/v2Containers/CreativesContainer/index.scss +102 -1
  83. package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
  84. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  85. package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
  86. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
  88. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
  90. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  92. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  93. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  94. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  95. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  96. package/v2Containers/Rcs/carouselUtils.js +224 -0
  97. package/v2Containers/Rcs/components/CarouselCard.js +317 -0
  98. package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
  99. package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
  100. package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
  101. package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
  102. package/v2Containers/Rcs/constants.js +132 -16
  103. package/v2Containers/Rcs/index.js +1822 -946
  104. package/v2Containers/Rcs/index.scss +443 -8
  105. package/v2Containers/Rcs/messages.js +45 -22
  106. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +494 -0
  107. package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
  108. package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
  109. package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
  110. package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
  111. package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
  112. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74440 -39387
  113. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
  114. package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
  115. package/v2Containers/Rcs/tests/index.test.js +275 -40
  116. package/v2Containers/Rcs/tests/mockData.js +38 -0
  117. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +912 -0
  118. package/v2Containers/Rcs/tests/utils.test.js +682 -30
  119. package/v2Containers/Rcs/utils.js +514 -12
  120. package/v2Containers/Sms/Create/index.js +115 -48
  121. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  122. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  123. package/v2Containers/SmsTrai/Create/index.js +9 -4
  124. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  125. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  126. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  127. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  129. package/v2Containers/SmsWrapper/index.js +37 -8
  130. package/v2Containers/TagList/index.js +5 -0
  131. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  132. package/v2Containers/Templates/_templates.scss +196 -12
  133. package/v2Containers/Templates/actions.js +11 -0
  134. package/v2Containers/Templates/constants.js +2 -0
  135. package/v2Containers/Templates/index.js +131 -59
  136. package/v2Containers/Templates/sagas.js +57 -13
  137. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
  138. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  139. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  140. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  141. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  142. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  143. package/v2Containers/TemplatesV2/index.js +88 -25
  144. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  145. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -0,0 +1,151 @@
1
+ import {
2
+ getPersonalizationTagColumnLabel,
3
+ smsTemplateHasMustacheTags,
4
+ buildSyntheticSmsMustacheTags,
5
+ normalizeRcsTestCardMedia,
6
+ mapRcsSuggestionForTestMeta,
7
+ typeMatches,
8
+ unwrapEntity,
9
+ } from '../utils';
10
+
11
+ describe('CommonTestAndPreview/utils', () => {
12
+ describe('getPersonalizationTagColumnLabel', () => {
13
+ it('prefers fullPath over name', () => {
14
+ expect(getPersonalizationTagColumnLabel({ fullPath: 'a.b', name: 'b' })).toBe('a.b');
15
+ });
16
+
17
+ it('falls back to name when fullPath is absent', () => {
18
+ expect(getPersonalizationTagColumnLabel({ name: 'b' })).toBe('b');
19
+ });
20
+
21
+ it('returns empty string when both are absent', () => {
22
+ expect(getPersonalizationTagColumnLabel({})).toBe('');
23
+ expect(getPersonalizationTagColumnLabel(undefined)).toBe('');
24
+ });
25
+ });
26
+
27
+ describe('smsTemplateHasMustacheTags', () => {
28
+ it('returns true when body contains a mustache tag', () => {
29
+ expect(smsTemplateHasMustacheTags('Hello {{name}}')).toBe(true);
30
+ });
31
+
32
+ it('returns false when body has no mustache tag', () => {
33
+ expect(smsTemplateHasMustacheTags('Hello there')).toBe(false);
34
+ });
35
+
36
+ it('returns false for non-string body', () => {
37
+ expect(smsTemplateHasMustacheTags(null)).toBe(false);
38
+ expect(smsTemplateHasMustacheTags(undefined)).toBe(false);
39
+ expect(smsTemplateHasMustacheTags(123)).toBe(false);
40
+ });
41
+ });
42
+
43
+ describe('buildSyntheticSmsMustacheTags', () => {
44
+ it('returns empty array for empty/non-string body', () => {
45
+ expect(buildSyntheticSmsMustacheTags('')).toEqual([]);
46
+ expect(buildSyntheticSmsMustacheTags(undefined)).toEqual([]);
47
+ expect(buildSyntheticSmsMustacheTags(42)).toEqual([]);
48
+ });
49
+
50
+ it('builds one synthetic tag row per unique mustache token', () => {
51
+ const result = buildSyntheticSmsMustacheTags('Hi {{name}}, your code is {{otp}}');
52
+ expect(result).toEqual([
53
+ { name: 'name', metaData: { userDriven: false }, children: [] },
54
+ { name: 'otp', metaData: { userDriven: false }, children: [] },
55
+ ]);
56
+ });
57
+ });
58
+
59
+ describe('normalizeRcsTestCardMedia', () => {
60
+ it('returns undefined for non-object media', () => {
61
+ expect(normalizeRcsTestCardMedia(null)).toBeUndefined();
62
+ expect(normalizeRcsTestCardMedia('not-an-object')).toBeUndefined();
63
+ });
64
+
65
+ it('prefers mediaUrl over url', () => {
66
+ const result = normalizeRcsTestCardMedia({ mediaUrl: 'a.png', url: 'b.png', thumbnailUrl: 't.png', height: 100 });
67
+ expect(result).toEqual({ mediaUrl: 'a.png', thumbnailUrl: 't.png', height: '100' });
68
+ });
69
+
70
+ it('falls back to url when mediaUrl is blank', () => {
71
+ const result = normalizeRcsTestCardMedia({ mediaUrl: ' ', url: 'b.png' });
72
+ expect(result).toEqual({ mediaUrl: 'b.png', thumbnailUrl: '' });
73
+ });
74
+
75
+ it('omits height when not provided', () => {
76
+ const result = normalizeRcsTestCardMedia({ mediaUrl: 'a.png' });
77
+ expect(result).toEqual({ mediaUrl: 'a.png', thumbnailUrl: '' });
78
+ expect(result.height).toBeUndefined();
79
+ });
80
+
81
+ it('returns empty mediaUrl when neither mediaUrl nor url are usable', () => {
82
+ const result = normalizeRcsTestCardMedia({ thumbnailUrl: 't.png' });
83
+ expect(result).toEqual({ mediaUrl: '', thumbnailUrl: 't.png' });
84
+ });
85
+ });
86
+
87
+ describe('mapRcsSuggestionForTestMeta', () => {
88
+ it('maps a fully populated suggestion row', () => {
89
+ expect(mapRcsSuggestionForTestMeta({
90
+ type: 'reply', text: 'Yes', phoneNumber: '123', url: 'https://x.com', postback: 'pb',
91
+ }, 0)).toEqual({
92
+ index: 0, type: 'reply', text: 'Yes', phoneNumber: '123', url: 'https://x.com', postback: 'pb',
93
+ });
94
+ });
95
+
96
+ it('falls back to phone_number and text-as-postback when fields are missing', () => {
97
+ expect(mapRcsSuggestionForTestMeta({ text: 'Call', phone_number: '456' }, 1)).toEqual({
98
+ index: 1, type: '', text: 'Call', phoneNumber: '456', url: null, postback: 'Call',
99
+ });
100
+ });
101
+
102
+ it('handles a completely empty suggestion row', () => {
103
+ expect(mapRcsSuggestionForTestMeta(undefined, 2)).toEqual({
104
+ index: 2, type: '', text: '', phoneNumber: '', url: null, postback: '',
105
+ });
106
+ });
107
+ });
108
+
109
+ describe('typeMatches', () => {
110
+ it('returns false when either argument is missing', () => {
111
+ expect(typeMatches('', 'gsm_sender_id')).toBe(false);
112
+ expect(typeMatches('gsm-sender-id', '')).toBe(false);
113
+ });
114
+
115
+ it('matches regardless of case and hyphen/underscore style', () => {
116
+ expect(typeMatches('GSM-SENDER-ID', 'gsm_sender_id')).toBe(true);
117
+ });
118
+
119
+ it('returns false for genuinely different types', () => {
120
+ expect(typeMatches('email', 'gsm_sender_id')).toBe(false);
121
+ });
122
+ });
123
+
124
+ describe('unwrapEntity', () => {
125
+ it('returns null for non-object response', () => {
126
+ expect(unwrapEntity(null)).toBeNull();
127
+ expect(unwrapEntity('str')).toBeNull();
128
+ });
129
+
130
+ it('returns top-level entity when present', () => {
131
+ expect(unwrapEntity({ entity: { id: 1 } })).toEqual({ id: 1 });
132
+ });
133
+
134
+ it('returns nested result.entity when top-level entity is absent', () => {
135
+ expect(unwrapEntity({ result: { entity: { id: 2 } } })).toEqual({ id: 2 });
136
+ });
137
+
138
+ it('returns nested data.entity when result.entity is absent', () => {
139
+ expect(unwrapEntity({ data: { entity: { id: 3 } } })).toEqual({ id: 3 });
140
+ });
141
+
142
+ it('returns null when entity is explicitly null', () => {
143
+ expect(unwrapEntity({ entity: null })).toBeNull();
144
+ });
145
+
146
+ it('returns the response itself when no entity shape matches', () => {
147
+ const response = { foo: 'bar' };
148
+ expect(unwrapEntity(response)).toBe(response);
149
+ });
150
+ });
151
+ });
@@ -0,0 +1,84 @@
1
+ import get from 'lodash/get';
2
+ import { extractTemplateVariables } from '../../utils/templateVarUtils';
3
+ import { SMS_MUSTACHE_TAG_PATTERN } from './constants';
4
+
5
+ /** Show token path from extract-tags (fullPath preferred, falls back to name). */
6
+ export const getPersonalizationTagColumnLabel = (tagNode) =>
7
+ tagNode?.fullPath ?? tagNode?.name ?? '';
8
+
9
+ /** Non-global so ensureGlobalRegexForExecLoop creates a fresh instance on each call. */
10
+ const SMS_MUSTACHE_VAR_REGEX = /\{\{([^}]+)\}\}/;
11
+
12
+ /** True if the SMS body contains at least one `{{…}}` mustache token (DLT `{#…#}` slots excluded). */
13
+ export const smsTemplateHasMustacheTags = (body) =>
14
+ typeof body === 'string' && SMS_MUSTACHE_TAG_PATTERN.test(body);
15
+
16
+ /**
17
+ * Build tag rows from `{{…}}` mustache tokens only — DLT `{#…#}` slots are excluded because
18
+ * they are pre-bound template variables, not user-fillable personalization tags.
19
+ * Passing a mustache-only captureRegex to extractTemplateVariables skips the DLT branch.
20
+ */
21
+ export const buildSyntheticSmsMustacheTags = (body = '') => {
22
+ if (!body || typeof body !== 'string') return [];
23
+ return extractTemplateVariables(body, SMS_MUSTACHE_VAR_REGEX).map((name) => ({
24
+ name,
25
+ metaData: { userDriven: false },
26
+ children: [],
27
+ }));
28
+ };
29
+
30
+ /** RCS createMessageMeta: normalize media shape (mediaUrl, thumbnailUrl, height string). */
31
+ export const normalizeRcsTestCardMedia = (media) => {
32
+ if (!media || typeof media !== 'object') return undefined;
33
+ const mediaUrl =
34
+ media.mediaUrl != null && String(media.mediaUrl).trim() !== ''
35
+ ? String(media.mediaUrl)
36
+ : media.url != null && String(media.url).trim() !== ''
37
+ ? String(media.url)
38
+ : '';
39
+ const thumbnailUrl = media.thumbnailUrl != null ? String(media.thumbnailUrl) : '';
40
+ const height = media.height != null ? String(media.height) : undefined;
41
+ const out = { mediaUrl, thumbnailUrl };
42
+ if (height) out.height = height;
43
+ return out;
44
+ };
45
+
46
+ /** RCS createMessageMeta: normalize suggestion shape (index, type, text, phoneNumber, url, postback). */
47
+ export const mapRcsSuggestionForTestMeta = (suggestionRow, index) => ({
48
+ index,
49
+ type: suggestionRow?.type ?? '',
50
+ text: suggestionRow?.text != null ? String(suggestionRow.text) : '',
51
+ phoneNumber:
52
+ suggestionRow?.phoneNumber != null
53
+ ? String(suggestionRow.phoneNumber)
54
+ : suggestionRow?.phone_number != null
55
+ ? String(suggestionRow.phone_number)
56
+ : '',
57
+ url: suggestionRow?.url !== undefined ? suggestionRow.url : null,
58
+ postback:
59
+ suggestionRow?.postback != null
60
+ ? String(suggestionRow.postback)
61
+ : suggestionRow?.text != null
62
+ ? String(suggestionRow.text)
63
+ : '',
64
+ });
65
+
66
+ /** Match API contactInfo.type (some envs use different casing). */
67
+ export const typeMatches = (itemType, expectedKey) => {
68
+ if (!itemType || !expectedKey) return false;
69
+ const normalizedItemType = String(itemType).toLowerCase().replace(/-/g, '_');
70
+ const normalizedExpectedType = String(expectedKey).toLowerCase().replace(/-/g, '_');
71
+ return normalizedItemType === normalizedExpectedType;
72
+ };
73
+
74
+ /**
75
+ * Campaigns domainProperties may return { entity } or { result: { entity } } / { data: { entity } }.
76
+ */
77
+ export const unwrapEntity = (response) => {
78
+ if (!response || typeof response !== 'object') return null;
79
+ if (response.entity != null) return response.entity;
80
+ const nested = get(response, 'result.entity') ?? get(response, 'data.entity');
81
+ if (nested != null) return nested;
82
+ if (response.entity === null) return null;
83
+ return response;
84
+ };
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import CreativesContainer from '../../v2Containers/CreativesContainer';
4
+ import CapPageSpinner from '../CapPageSpinner';
5
+ import {
6
+ EMBEDDED_SMS_CREATIVES_LOCATION,
7
+ SMS_FALLBACK_CHANNEL_KEY,
8
+ SMS_FALLBACK_CREATIVE_EDITOR,
9
+ SMS_FALLBACK_ENABLE_NEW_CHANNELS,
10
+ } from './constants';
11
+
12
+ /**
13
+ * Reuse the exact embedded CreativesContainer SMS flow (same as normal SMS create).
14
+ * Avoid overriding Templates with local config so "Create new" follows built-in createTemplate path.
15
+ */
16
+ export function SmsFallbackLocalSelector({
17
+ hidden,
18
+ fetchDetailsLoading,
19
+ templateList,
20
+ channelsToHide,
21
+ smsRegister,
22
+ onCloseCreatives,
23
+ onSelectTemplate,
24
+ filterContent,
25
+ location,
26
+ /** Required when user completes embedded SMS create/edit — `CreativesContainer` calls this on save (see `processCentralCommsMetaId`). */
27
+ getCreativesData,
28
+ }) {
29
+ const rootClassName = [
30
+ 'sms-fallback-selector',
31
+ hidden ? 'sms-fallback-selector--visually-hidden' : '',
32
+ ]
33
+ .filter(Boolean)
34
+ .join(' ');
35
+
36
+ return (
37
+ <div
38
+ className={rootClassName}
39
+ aria-hidden={hidden}
40
+ inert={hidden ? '' : undefined}
41
+ >
42
+ {fetchDetailsLoading && (
43
+ <div className="sms-fallback-selector__loading" aria-busy="true" aria-live="polite">
44
+ <CapPageSpinner spinning />
45
+ </div>
46
+ )}
47
+ <CreativesContainer
48
+ creativesMode="create"
49
+ location={location || EMBEDDED_SMS_CREATIVES_LOCATION}
50
+ templateData={null}
51
+ handleCloseCreatives={onCloseCreatives}
52
+ isFullMode={false}
53
+ smsRegister={smsRegister}
54
+ editor={SMS_FALLBACK_CREATIVE_EDITOR}
55
+ enableNewChannels={SMS_FALLBACK_ENABLE_NEW_CHANNELS}
56
+ channel={SMS_FALLBACK_CHANNEL_KEY}
57
+ channelsToHide={channelsToHide}
58
+ selectedBadges={[]}
59
+ localTemplatesConfig={{
60
+ useLocalTemplates: true,
61
+ localTemplates: templateList.templates,
62
+ localTemplatesLoading: templateList.loading,
63
+ localTemplatesFilterContent: filterContent,
64
+ localTemplatesOnPageChange: templateList.loadMore,
65
+ localTemplatesUseSkeleton: true,
66
+ }}
67
+ onSelectTemplate={onSelectTemplate}
68
+ getCreativesData={getCreativesData}
69
+ />
70
+ </div>
71
+ );
72
+ }
73
+
74
+ SmsFallbackLocalSelector.propTypes = {
75
+ hidden: PropTypes.bool,
76
+ fetchDetailsLoading: PropTypes.bool,
77
+ templateList: PropTypes.shape({
78
+ templates: PropTypes.array,
79
+ loading: PropTypes.bool,
80
+ loadMore: PropTypes.func,
81
+ }).isRequired,
82
+ channelsToHide: PropTypes.arrayOf(PropTypes.string),
83
+ smsRegister: PropTypes.any,
84
+ onCloseCreatives: PropTypes.func.isRequired,
85
+ onSelectTemplate: PropTypes.func.isRequired,
86
+ filterContent: PropTypes.node,
87
+ location: PropTypes.object,
88
+ getCreativesData: PropTypes.func.isRequired,
89
+ };
90
+
91
+ export default SmsFallbackLocalSelector;
@@ -0,0 +1,94 @@
1
+ import {
2
+ WHATSAPP,
3
+ RCS,
4
+ ZALO,
5
+ WEBPUSH,
6
+ } from '../../v2Containers/CreativesContainer/constants';
7
+
8
+ /**
9
+ * View modes for the SMS fallback slidebox (template list, create, edit).
10
+ * Used by SmsFallback and any channel that reuses it (RCS, etc.).
11
+ */
12
+ export const SMS_FALLBACK_VIEW = {
13
+ SELECTING: 'selecting',
14
+ CREATING: 'creating',
15
+ EDITING: 'editing',
16
+ };
17
+
18
+ /**
19
+ * Channel keys to hide in TemplatesV2 so only SMS tab is shown.
20
+ * Uses normalized keys matching TemplatesV2 pane.key normalization (lowercase).
21
+ * Parent can override via channelsToHide prop.
22
+ */
23
+ export const CHANNELS_TO_HIDE_FOR_SMS_ONLY = [
24
+ 'email',
25
+ 'mobilepush',
26
+ 'webpush',
27
+ 'viber',
28
+ 'whatsapp',
29
+ 'zalo',
30
+ 'facebook',
31
+ 'rcs',
32
+ 'inapp',
33
+ 'line',
34
+ 'wechat',
35
+ 'call_task',
36
+ 'ftp',
37
+ 'assets',
38
+ ];
39
+
40
+ /** DLT category filter values — aligned with Templates `SMS_FILTERS` / `filterSMSTemplates`. */
41
+ export const SMS_CATEGORY_FILTERS = {
42
+ ALL: 'all',
43
+ PROMOTIONAL: 'promo',
44
+ SERVICE_EXPLICIT: 'explicit',
45
+ SERVICE_IMPLICIT: 'implicit',
46
+ };
47
+
48
+ /** `location` for embedded SMS creatives picker (CreativesContainer / TemplatesV2). */
49
+ export const EMBEDDED_SMS_CREATIVES_LOCATION = {
50
+ pathname: '/sms/create',
51
+ query: { type: 'embedded', module: 'library' },
52
+ search: '',
53
+ };
54
+
55
+ /** `location` for embedded SMS TRAI edit inside slidebox. */
56
+ export const EMBEDDED_SMS_CREATIVES_EDIT_LOCATION = {
57
+ pathname: '/sms/edit',
58
+ query: { type: 'embedded', module: 'library' },
59
+ search: '',
60
+ };
61
+
62
+ /** API `channel` param for `getTemplateDetails` when loading an SMS template by id. */
63
+ export const SMS_TEMPLATE_DETAILS_API_CHANNEL = 'Sms';
64
+
65
+ export const SMS_FALLBACK_CREATIVE_EDITOR = 'BEE';
66
+
67
+ export const SMS_FALLBACK_CHANNEL_KEY = 'sms';
68
+
69
+ /** Route shape passed to SmsWrapper / SmsTraiEdit for embedded SMS flows. */
70
+ export const SMS_FALLBACK_ROUTE = { name: SMS_FALLBACK_CHANNEL_KEY };
71
+
72
+ /** Channels enabled in the SMS-only embedded picker (hide others via `channelsToHide`). */
73
+ export const SMS_FALLBACK_ENABLE_NEW_CHANNELS = [WHATSAPP, RCS, ZALO, WEBPUSH];
74
+
75
+ /** Action types for the SmsFallback slidebox reducer. */
76
+ export const SMS_FALLBACK_ACTIONS = {
77
+ OPEN_SELECTOR: 'OPEN_SELECTOR',
78
+ OPEN_EDITING: 'OPEN_EDITING',
79
+ OPEN_CREATING: 'OPEN_CREATING',
80
+ SELECT_TEMPLATE_START: 'SELECT_TEMPLATE_START',
81
+ SELECT_TEMPLATE_DONE: 'SELECT_TEMPLATE_DONE',
82
+ SET_FETCH_LOADING: 'SET_FETCH_LOADING',
83
+ GO_BACK_TO_LIST: 'GO_BACK_TO_LIST',
84
+ CLOSE: 'CLOSE',
85
+ SET_IS_GET_FORM_DATA: 'SET_IS_GET_FORM_DATA',
86
+ SET_LIQUID_ERROR: 'SET_LIQUID_ERROR',
87
+ SET_LOADING_CONTENT: 'SET_LOADING_CONTENT',
88
+ OPEN_TEST_PREVIEW: 'OPEN_TEST_PREVIEW',
89
+ CLOSE_TEST_PREVIEW: 'CLOSE_TEST_PREVIEW',
90
+ SET_CREATE_FIELDS_INVALID: 'SET_CREATE_FIELDS_INVALID',
91
+ SET_CATEGORY_FILTER: 'SET_CATEGORY_FILTER',
92
+ };
93
+
94
+ export const JS_TYPE_BOOLEAN = 'boolean';