@capillarytech/creatives-library 8.0.359-alpha.0 → 8.0.359-alpha.1

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/index.html +1 -0
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/cdnTransformation.js +75 -3
  6. package/utils/commonUtils.js +19 -1
  7. package/utils/rcsPayloadUtils.js +92 -0
  8. package/utils/templateVarUtils.js +201 -0
  9. package/utils/tests/cdnTransformation.test.js +127 -0
  10. package/utils/tests/rcsPayloadUtils.test.js +226 -0
  11. package/utils/tests/templateVarUtils.test.js +204 -0
  12. package/v2Components/CapActionButton/constants.js +7 -0
  13. package/v2Components/CapActionButton/index.js +166 -108
  14. package/v2Components/CapActionButton/index.scss +157 -6
  15. package/v2Components/CapActionButton/messages.js +19 -3
  16. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  17. package/v2Components/CapImageUpload/index.js +2 -2
  18. package/v2Components/CapTagList/index.js +10 -0
  19. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +72 -49
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  26. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +16 -0
  28. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +157 -15
  29. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +14 -132
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +400 -239
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +202 -10
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  34. package/v2Components/CommonTestAndPreview/constants.js +40 -2
  35. package/v2Components/CommonTestAndPreview/index.js +887 -453
  36. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  37. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  38. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +163 -0
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  47. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
  48. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
  49. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +454 -1
  50. package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
  51. package/v2Components/CommonTestAndPreview/tests/index.test.js +327 -4
  52. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  53. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  54. package/v2Components/FormBuilder/index.js +167 -56
  55. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  56. package/v2Components/SmsFallback/constants.js +73 -0
  57. package/v2Components/SmsFallback/index.js +956 -0
  58. package/v2Components/SmsFallback/index.scss +265 -0
  59. package/v2Components/SmsFallback/messages.js +78 -0
  60. package/v2Components/SmsFallback/smsFallbackUtils.js +119 -0
  61. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  62. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  63. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  64. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +223 -0
  65. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  66. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  67. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  68. package/v2Components/TemplatePreview/_templatePreview.scss +37 -22
  69. package/v2Components/TemplatePreview/constants.js +2 -0
  70. package/v2Components/TemplatePreview/index.js +143 -31
  71. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  72. package/v2Components/TestAndPreviewSlidebox/index.js +15 -3
  73. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  74. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  75. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  76. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  77. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  78. package/v2Containers/App/constants.js +3 -0
  79. package/v2Containers/App/tests/constants.test.js +61 -0
  80. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +17 -0
  81. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  82. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  83. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  84. package/v2Containers/CreativesContainer/constants.js +9 -0
  85. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +79 -0
  86. package/v2Containers/CreativesContainer/index.js +382 -127
  87. package/v2Containers/CreativesContainer/index.scss +83 -1
  88. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  89. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +79 -34
  90. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  92. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  93. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  94. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  95. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  96. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  97. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  98. package/v2Containers/Rcs/constants.js +120 -11
  99. package/v2Containers/Rcs/index.js +2577 -812
  100. package/v2Containers/Rcs/index.scss +281 -8
  101. package/v2Containers/Rcs/messages.js +34 -3
  102. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  103. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98036 -70145
  104. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  105. package/v2Containers/Rcs/tests/index.test.js +152 -121
  106. package/v2Containers/Rcs/tests/mockData.js +38 -0
  107. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  108. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  109. package/v2Containers/Rcs/utils.js +478 -11
  110. package/v2Containers/Sms/Create/index.js +106 -40
  111. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  112. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  113. package/v2Containers/SmsTrai/Create/index.js +9 -4
  114. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  115. package/v2Containers/SmsTrai/Edit/index.js +640 -130
  116. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  117. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  118. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  119. package/v2Containers/SmsWrapper/index.js +37 -8
  120. package/v2Containers/TagList/index.js +6 -0
  121. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  122. package/v2Containers/Templates/_templates.scss +166 -86
  123. package/v2Containers/Templates/actions.js +11 -0
  124. package/v2Containers/Templates/constants.js +2 -0
  125. package/v2Containers/Templates/index.js +203 -145
  126. package/v2Containers/Templates/sagas.js +62 -13
  127. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  128. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1062 -1017
  129. package/v2Containers/Templates/tests/sagas.test.js +222 -22
  130. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  131. package/v2Containers/Templates/tests/webpush.test.js +375 -0
  132. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  133. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  134. package/v2Containers/TemplatesV2/index.js +86 -23
  135. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  136. package/v2Containers/Viber/constants.js +0 -19
  137. package/v2Containers/Viber/index.js +47 -714
  138. package/v2Containers/Viber/index.scss +0 -148
  139. package/v2Containers/Viber/messages.js +0 -116
  140. package/v2Containers/Viber/tests/index.test.js +0 -80
  141. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  142. package/v2Containers/WebPush/Create/index.js +91 -8
  143. package/v2Containers/WebPush/Create/index.scss +7 -0
  144. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +348 -0
  145. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -6,7 +6,7 @@
6
6
  * Routes to channel-specific preview content components
7
7
  */
8
8
 
9
- import React from 'react';
9
+ import React, { useState } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
12
12
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
@@ -21,8 +21,22 @@ import InAppPreviewContent from './InAppPreviewContent';
21
21
  import MobilePushPreviewContent from './MobilePushPreviewContent';
22
22
  import ViberPreviewContent from './ViberPreviewContent';
23
23
  import ZaloPreviewContent from './ZaloPreviewContent';
24
- import { CHANNELS, DESKTOP, TABLET, MOBILE, ANDROID, IOS } from '../constants';
24
+ import WebPushPreviewContent from './WebPushPreviewContent';
25
+ import CapTab from '@capillarytech/cap-ui-library/CapTab';
26
+ import {
27
+ CHANNELS,
28
+ DESKTOP,
29
+ TABLET,
30
+ MOBILE,
31
+ ANDROID,
32
+ IOS,
33
+ PREVIEW_TAB_RCS,
34
+ PREVIEW_TAB_SMS_FALLBACK,
35
+ PREVIEW_TAB_KEYS,
36
+ RCS_SMS_FALLBACK_VAR_MAPPED_PROP,
37
+ } from '../constants';
25
38
  import messages from '../messages';
39
+ import { getFallbackResolvedContent } from '../../../utils/templateVarUtils';
26
40
  import './_unifiedPreview.scss';
27
41
 
28
42
  const UnifiedPreview = ({
@@ -38,12 +52,30 @@ const UnifiedPreview = ({
38
52
  lastModified,
39
53
  updatedByName,
40
54
  showHeader, // Default to true for Test and Preview flow, false for channel component previews
55
+ smsFallbackContent,
56
+ smsFallbackResolvedText,
57
+ activePreviewTab,
58
+ onPreviewTabChange,
41
59
  }) => {
60
+ const hasFallbackSmsTemplate = !!(smsFallbackContent?.templateContent || smsFallbackContent?.content);
61
+ const showRcsSmsFallbackTabs = channel === CHANNELS.RCS && hasFallbackSmsTemplate;
62
+ const rawFallbackTemplate = smsFallbackContent?.templateContent ?? smsFallbackContent?.content ?? '';
63
+ const rcsFallbackVarMapped = smsFallbackContent?.[RCS_SMS_FALLBACK_VAR_MAPPED_PROP] ?? {};
64
+ const hasRcsFallbackVarMapped = Object.keys(rcsFallbackVarMapped).length > 0;
65
+ // When no varmap is present, show raw template so {{tags}} remain visible in the preview.
66
+ // Only apply slot substitution when varmap entries exist or a resolved text is available.
67
+ const smsFallbackDisplayContent =
68
+ smsFallbackResolvedText != null && smsFallbackResolvedText !== ''
69
+ ? smsFallbackResolvedText
70
+ : hasRcsFallbackVarMapped
71
+ ? getFallbackResolvedContent(rawFallbackTemplate, rcsFallbackVarMapped)
72
+ : rawFallbackTemplate;
42
73
  /**
43
74
  * Render channel-specific preview content
44
75
  * For Phase 5, we'll render placeholders
45
76
  * Phase 6+ will implement actual content components
46
77
  */
78
+ const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
47
79
  const renderChannelContent = () => {
48
80
  // Phase 5: Placeholder content for all channels
49
81
  // Phase 6+: Import and render actual channel-specific components
@@ -124,6 +156,21 @@ const UnifiedPreview = ({
124
156
  case CHANNELS.RCS: {
125
157
  // RCS content is an object with rcsTitle, rcsDesc, rcsImageSrc, suggestions, etc.
126
158
  const rcsContent = typeof content === 'object' ? content : {};
159
+
160
+ if (showRcsSmsFallbackTabs && activePreviewTab === PREVIEW_TAB_SMS_FALLBACK) {
161
+ return (
162
+ <SmsPreviewContent
163
+ content={smsFallbackDisplayContent}
164
+ device={ANDROID}
165
+ isUpdating={isUpdating}
166
+ error={error}
167
+ formatMessage={formatMessage}
168
+ senderId={smsFallbackContent?.senderId}
169
+ showHeader={showHeader}
170
+ />
171
+ );
172
+ }
173
+
127
174
  return (
128
175
  <RcsPreviewContent
129
176
  content={rcsContent}
@@ -197,6 +244,45 @@ const UnifiedPreview = ({
197
244
  );
198
245
  }
199
246
 
247
+ case CHANNELS.WEBPUSH: {
248
+ // WebPush content arrives as a JSON string (from getCurrentContent or preview API response)
249
+ let webPushOuter = {};
250
+ try {
251
+ webPushOuter = typeof content === 'string' ? JSON.parse(content) : (typeof content === 'object' && content !== null ? content : {});
252
+ } catch (e) {
253
+ webPushOuter = {};
254
+ }
255
+ const { content: webPushInner = {} } = webPushOuter;
256
+ const {
257
+ title,
258
+ message,
259
+ iconImageUrl,
260
+ cta,
261
+ expandableDetails = {},
262
+ } = webPushInner;
263
+ const { media: webPushMedia = [], ctas: webPushCtas = [] } = expandableDetails;
264
+ const webPushButtons = webPushCtas.map(({ title: text = '', actionLink: url = '', type = '' }) => ({
265
+ text,
266
+ url,
267
+ type,
268
+ }));
269
+ return (
270
+ <WebPushPreviewContent
271
+ notificationTitle={title || ''}
272
+ notificationBody={message || ''}
273
+ imageSrc={webPushMedia[0]?.url || ''}
274
+ brandIconSrc={iconImageUrl || ''}
275
+ buttons={webPushButtons}
276
+ url={cta?.actionLink || ''}
277
+ isUpdating={isUpdating}
278
+ error={error}
279
+ isFullscreenOpen={isFullscreenOpen}
280
+ setIsFullscreenOpen={setIsFullscreenOpen}
281
+ selectedCustomer={selectedCustomer}
282
+ />
283
+ );
284
+ }
285
+
200
286
  default:
201
287
  return (
202
288
  <div className="channel-preview-placeholder">
@@ -212,6 +298,8 @@ const UnifiedPreview = ({
212
298
  }
213
299
  };
214
300
 
301
+ const showToggle = channel !== CHANNELS.WEBPUSH && showDeviceToggle;
302
+
215
303
  /**
216
304
  * Render loading state for all channels
217
305
  */
@@ -222,9 +310,10 @@ const UnifiedPreview = ({
222
310
  <PreviewHeader
223
311
  selectedCustomer={selectedCustomer}
224
312
  device={device}
225
- showDeviceToggle={showDeviceToggle}
313
+ showDeviceToggle={showToggle}
226
314
  onDeviceChange={onDeviceChange}
227
315
  channel={channel}
316
+ setIsFullscreenOpen={setIsFullscreenOpen}
228
317
  />
229
318
  )}
230
319
  <CapRow className="preview-loading-container">
@@ -247,9 +336,10 @@ const UnifiedPreview = ({
247
336
  <PreviewHeader
248
337
  selectedCustomer={selectedCustomer}
249
338
  device={device}
250
- showDeviceToggle={showDeviceToggle}
339
+ showDeviceToggle={showToggle}
251
340
  onDeviceChange={onDeviceChange}
252
341
  channel={channel}
342
+ setIsFullscreenOpen={setIsFullscreenOpen}
253
343
  />
254
344
  )}
255
345
  <CapRow className="preview-error-container">
@@ -268,6 +358,96 @@ const UnifiedPreview = ({
268
358
  * PreviewHeader is shown only when showHeader is true (for Test and Preview flow)
269
359
  * Channel-specific content will be in separate files in future phases
270
360
  */
361
+ if (showRcsSmsFallbackTabs) {
362
+ const renderPane = ({ paneChannel, paneDevice, paneShowDeviceToggle, paneContent }) => (
363
+ <CapRow className="unified-preview-tab-pane">
364
+ {showHeader && (
365
+ <PreviewHeader
366
+ selectedCustomer={selectedCustomer}
367
+ device={paneDevice}
368
+ showDeviceToggle={paneShowDeviceToggle}
369
+ onDeviceChange={onDeviceChange}
370
+ channel={paneChannel}
371
+ />
372
+ )}
373
+
374
+ <CapRow className={`preview-content-container ${!showHeader ? 'preview-content-container-no-header' : ''}`}>
375
+ {paneContent}
376
+ </CapRow>
377
+
378
+
379
+ </CapRow>
380
+ );
381
+
382
+ return (
383
+ <CapRow className="unified-preview unified-preview-rcs-tabs">
384
+ <CapTab
385
+ activeKey={activePreviewTab}
386
+ onChange={onPreviewTabChange}
387
+ panes={[
388
+ {
389
+ tab: formatMessage(messages.rcsTab),
390
+ key: PREVIEW_TAB_RCS,
391
+ content: renderPane({
392
+ paneChannel: CHANNELS.RCS,
393
+ paneDevice: device,
394
+ paneShowDeviceToggle: showDeviceToggle,
395
+ paneContent: (
396
+ <RcsPreviewContent
397
+ content={typeof content === 'object' ? content : {}}
398
+ device={device}
399
+ isUpdating={isUpdating}
400
+ error={error}
401
+ formatMessage={formatMessage}
402
+ senderId={content?.senderId}
403
+ showHeader={showHeader}
404
+ />
405
+ ),
406
+ }),
407
+ },
408
+ {
409
+ tab: formatMessage(messages.smsFallbackTab),
410
+ key: PREVIEW_TAB_SMS_FALLBACK,
411
+ content: renderPane({
412
+ paneChannel: CHANNELS.SMS,
413
+ paneDevice: device,
414
+ paneShowDeviceToggle: showDeviceToggle,
415
+ paneContent: (
416
+ <SmsPreviewContent
417
+ content={smsFallbackDisplayContent}
418
+ device={device}
419
+ isUpdating={isUpdating}
420
+ error={error}
421
+ formatMessage={formatMessage}
422
+ senderId={smsFallbackContent?.senderId}
423
+ showHeader={showHeader}
424
+ />
425
+ ),
426
+ }),
427
+ },
428
+ ]}
429
+ />
430
+ {(lastModified || updatedByName) && (
431
+ <CapRow className="preview-metadata">
432
+ {lastModified && (
433
+ <span className="metadata-item">
434
+ {formatMessage(messages.lastModified)}
435
+ :
436
+ {' '}
437
+ {lastModified}
438
+ </span>
439
+ )}
440
+ {updatedByName && (
441
+ <span className="metadata-item">
442
+ {formatMessage(messages.byAuthor, { name: updatedByName })}
443
+ </span>
444
+ )}
445
+ </CapRow>
446
+ )}
447
+ </CapRow>
448
+ );
449
+ }
450
+
271
451
  return (
272
452
  <CapRow className="unified-preview">
273
453
  {/* PreviewHeader - shown only in Test and Preview flow, hidden in channel component previews */}
@@ -275,15 +455,16 @@ const UnifiedPreview = ({
275
455
  <PreviewHeader
276
456
  selectedCustomer={selectedCustomer}
277
457
  device={device}
278
- showDeviceToggle={showDeviceToggle}
458
+ showDeviceToggle={showToggle}
279
459
  onDeviceChange={onDeviceChange}
280
460
  channel={channel}
461
+ setIsFullscreenOpen={setIsFullscreenOpen}
281
462
  />
282
463
  )}
283
464
 
284
465
  {/* Channel-specific preview content */}
285
466
  <CapRow className={`preview-content-container ${!showHeader ? 'preview-content-container-no-header' : ''}`}>
286
- {[CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS, CHANNELS.INAPP, CHANNELS.MOBILEPUSH, CHANNELS.VIBER].includes(channel) ? (
467
+ {[CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS, CHANNELS.INAPP, CHANNELS.MOBILEPUSH, CHANNELS.VIBER, CHANNELS.WEBPUSH].includes(channel) ? (
287
468
  renderChannelContent()
288
469
  ) : (
289
470
  <DeviceFrame device={device || DESKTOP}>
@@ -305,9 +486,7 @@ const UnifiedPreview = ({
305
486
  )}
306
487
  {updatedByName && (
307
488
  <span className="metadata-item">
308
- {formatMessage(messages.by)}
309
- {' '}
310
- {updatedByName}
489
+ {formatMessage(messages.byAuthor, { name: updatedByName })}
311
490
  </span>
312
491
  )}
313
492
  </CapRow>
@@ -319,7 +498,7 @@ const UnifiedPreview = ({
319
498
  UnifiedPreview.propTypes = {
320
499
  // Core
321
500
  channel: PropTypes.oneOf(Object.values(CHANNELS)).isRequired,
322
- content: PropTypes.object.isRequired,
501
+ content: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
323
502
 
324
503
  // Display options
325
504
  device: PropTypes.oneOf([DESKTOP, TABLET, MOBILE, ANDROID, IOS]),
@@ -348,6 +527,15 @@ UnifiedPreview.propTypes = {
348
527
 
349
528
  // Header display
350
529
  showHeader: PropTypes.bool, // Show PreviewHeader (true for Test and Preview flow, false for channel component previews)
530
+
531
+ // RCS: SMS fallback preview support (same shape as SmsFallback / RCS smsFallbackData)
532
+ smsFallbackContent: PropTypes.shape({
533
+ templateContent: PropTypes.string,
534
+ senderId: PropTypes.string,
535
+ templateName: PropTypes.string,
536
+ }),
537
+ activePreviewTab: PropTypes.oneOf(PREVIEW_TAB_KEYS),
538
+ onPreviewTabChange: PropTypes.func,
351
539
  };
352
540
 
353
541
  UnifiedPreview.defaultProps = {
@@ -360,6 +548,10 @@ UnifiedPreview.defaultProps = {
360
548
  lastModified: null,
361
549
  updatedByName: null,
362
550
  showHeader: true, // Default to true for Test and Preview flow
551
+ smsFallbackContent: null,
552
+ smsFallbackResolvedText: undefined,
553
+ activePreviewTab: PREVIEW_TAB_RCS,
554
+ onPreviewTabChange: () => {},
363
555
  };
364
556
 
365
557
  export default UnifiedPreview;
@@ -213,6 +213,10 @@
213
213
  margin-right: 0.5rem;
214
214
  }
215
215
 
216
+ .cap-slide-box-v2.common-test-and-preview-slidebox .cap-slide-box-v2-container {
217
+ z-index: 10030;
218
+ }
219
+
216
220
  .common-test-and-preview-slidebox {
217
221
 
218
222
  .ant-modal-mask,
@@ -486,6 +490,13 @@
486
490
  }
487
491
  }
488
492
 
493
+ .tags-section-title {
494
+ margin: $CAP_SPACE_12 0;
495
+ display: block;
496
+ color: $CAP_G01;
497
+ font-weight: $FONT_WEIGHT_MEDIUM;
498
+ }
499
+
489
500
  .values-table {
490
501
  margin-bottom: $CAP_SPACE_16;
491
502
  border: $CAP_SPACE_01 solid $CAP_G12;
@@ -66,7 +66,6 @@ export const GET_WECRM_ACCOUNTS_REQUESTED = 'app/CommonTestAndPreview/GET_WECRM_
66
66
  export const GET_WECRM_ACCOUNTS_SUCCESS = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_SUCCESS';
67
67
  export const GET_WECRM_ACCOUNTS_FAILURE = 'app/CommonTestAndPreview/GET_WECRM_ACCOUNTS_FAILURE';
68
68
 
69
-
70
69
  // ============================================
71
70
  // CUSTOMER MODAL TYPE (test customer addition)
72
71
  // ============================================
@@ -85,8 +84,46 @@ export const CHANNELS = {
85
84
  MOBILEPUSH: 'MOBILEPUSH',
86
85
  VIBER: 'VIBER',
87
86
  ZALO: 'ZALO',
87
+ WEBPUSH: 'WEBPUSH',
88
88
  };
89
- export const CHANNELS_USING_ANDROID_PREVIEW_DEVICE = [CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.VIBER, CHANNELS.ZALO, CHANNELS.INAPP, CHANNELS.MOBILEPUSH];
89
+
90
+ /** Unified preview tab keys when RCS shows RCS + SMS fallback panes. */
91
+ export const PREVIEW_TAB_RCS = 'rcs';
92
+ export const PREVIEW_TAB_SMS_FALLBACK = 'smsFallback';
93
+ export const PREVIEW_TAB_KEYS = [PREVIEW_TAB_RCS, PREVIEW_TAB_SMS_FALLBACK];
94
+
95
+ /** Channels whose Test & Preview defaults to Android/iOS device frame (not desktop). */
96
+ export const CHANNELS_USING_ANDROID_PREVIEW_DEVICE = [
97
+ CHANNELS.SMS,
98
+ CHANNELS.WHATSAPP,
99
+ CHANNELS.RCS,
100
+ CHANNELS.INAPP,
101
+ CHANNELS.MOBILEPUSH,
102
+ CHANNELS.VIBER,
103
+ ];
104
+
105
+ /** RCS createMessageMeta test payload — rich card envelope literals. */
106
+ export const RCS_TEST_META_CONTENT_TYPE_RICHCARD = 'RICHCARD';
107
+ export const RCS_TEST_META_CARD_TYPE_STANDALONE = 'STANDALONE';
108
+ export const RCS_TEST_META_CARD_ORIENTATION_VERTICAL = 'VERTICAL';
109
+ export const RCS_TEST_META_CARD_WIDTH_SMALL = 'SMALL';
110
+
111
+ /** React key fallback when a custom-values section has no `key` or title id. */
112
+ export const CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY = 'section';
113
+
114
+ /**
115
+ * Matches at least one `{{variableName}}` token in SMS / RCS fallback template text.
116
+ */
117
+ export const SMS_MUSTACHE_TAG_PATTERN = /\{\{[^}]+\}\}/;
118
+
119
+ /** TRAI DLT `{#…#}` placeholders (same idea as `COMBINED_SMS_TEMPLATE_VAR_REGEX` in unified constants). */
120
+ export const SMS_DLT_HASH_TAG_PATTERN = /\{\#[^#]*\#\}/;
121
+
122
+ /**
123
+ * Key on `smsFallbackContent` passed into Test & Preview — RCS slot map from SmsTraiEdit
124
+ * (`onRcsFallbackEditorStateChange`), merged before tag extraction like the embedded SMS editor.
125
+ */
126
+ export const RCS_SMS_FALLBACK_VAR_MAPPED_PROP = 'rcsSmsFallbackVarMapped';
90
127
 
91
128
  // ============================================
92
129
  // DEVICE CONSTANTS
@@ -203,6 +240,7 @@ export const DYNAMIC_URL = 'DYNAMIC_URL';
203
240
  export const IMAGE = 'IMAGE';
204
241
  export const VIDEO = 'VIDEO';
205
242
  export const URL = 'URL';
243
+ export const DAYS = 'DAYS';
206
244
 
207
245
  // Initial Payload Template (for reference)
208
246
  export const INITIAL_PAYLOAD = {