@capillarytech/creatives-library 8.0.316 → 8.0.317-alpha.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 (172) hide show
  1. package/constants/unified.js +15 -0
  2. package/package.json +1 -1
  3. package/services/api.js +6 -0
  4. package/services/tests/api.test.js +7 -0
  5. package/utils/common.js +6 -1
  6. package/utils/templateVarUtils.js +172 -0
  7. package/utils/tests/tagValidations.test.js +34 -0
  8. package/utils/tests/templateVarUtils.test.js +160 -0
  9. package/v2Components/CapTagList/index.js +25 -22
  10. package/v2Components/CapTagList/style.scss +48 -0
  11. package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  12. package/v2Components/CapTagListWithInput/index.js +4 -0
  13. package/v2Components/CapWhatsappCTA/index.js +2 -0
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +11 -5
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  24. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +12 -0
  25. package/v2Components/CommonTestAndPreview/constants.js +38 -0
  26. package/v2Components/CommonTestAndPreview/index.js +693 -155
  27. package/v2Components/CommonTestAndPreview/messages.js +41 -3
  28. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  29. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +269 -1
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  34. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +25 -4
  35. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  36. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  37. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  38. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  39. package/v2Components/FormBuilder/index.js +14 -1
  40. package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
  41. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  42. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
  43. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
  44. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  45. package/v2Components/SmsFallback/constants.js +73 -0
  46. package/v2Components/SmsFallback/index.js +956 -0
  47. package/v2Components/SmsFallback/index.scss +265 -0
  48. package/v2Components/SmsFallback/messages.js +78 -0
  49. package/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  50. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  51. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  52. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  53. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  54. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  55. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  56. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  57. package/v2Components/TestAndPreviewSlidebox/index.js +8 -1
  58. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  59. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  60. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  61. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  62. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  63. package/v2Containers/BeeEditor/index.js +3 -0
  64. package/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  65. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  66. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  67. package/v2Containers/CommunicationFlow/constants.js +200 -0
  68. package/v2Containers/CommunicationFlow/index.js +102 -0
  69. package/v2Containers/CommunicationFlow/messages.js +346 -0
  70. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  71. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  72. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  73. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  74. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  75. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  76. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  77. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  78. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  79. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  80. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  81. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  82. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  83. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  84. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  85. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  86. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  87. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  88. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  89. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  90. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  91. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  92. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  93. package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  94. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  95. package/v2Containers/CreativesContainer/SlideBoxContent.js +64 -5
  96. package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
  97. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  98. package/v2Containers/CreativesContainer/constants.js +12 -0
  99. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  100. package/v2Containers/CreativesContainer/index.js +289 -93
  101. package/v2Containers/CreativesContainer/index.scss +51 -1
  102. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  103. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
  104. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  105. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  106. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  107. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -10
  108. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  109. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  110. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  111. package/v2Containers/Email/index.js +1 -0
  112. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +7 -1
  113. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  114. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
  115. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  116. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -0
  117. package/v2Containers/EmailWrapper/index.js +4 -0
  118. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  119. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
  120. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  121. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  122. package/v2Containers/InAppWrapper/index.js +3 -0
  123. package/v2Containers/MobilePush/Create/index.js +2 -0
  124. package/v2Containers/MobilePush/Edit/index.js +2 -0
  125. package/v2Containers/MobilepushWrapper/index.js +3 -1
  126. package/v2Containers/Rcs/constants.js +32 -1
  127. package/v2Containers/Rcs/index.js +951 -873
  128. package/v2Containers/Rcs/index.scss +85 -6
  129. package/v2Containers/Rcs/messages.js +10 -1
  130. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  131. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +40834 -1963
  132. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  133. package/v2Containers/Rcs/tests/index.test.js +41 -38
  134. package/v2Containers/Rcs/tests/mockData.js +38 -0
  135. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  136. package/v2Containers/Rcs/tests/utils.test.js +379 -1
  137. package/v2Containers/Rcs/utils.js +358 -10
  138. package/v2Containers/Sms/Create/index.js +83 -36
  139. package/v2Containers/Sms/Edit/index.js +2 -0
  140. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  141. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  142. package/v2Containers/SmsTrai/Create/index.js +9 -4
  143. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  144. package/v2Containers/SmsTrai/Edit/index.js +611 -128
  145. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  146. package/v2Containers/SmsTrai/Edit/messages.js +9 -4
  147. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4327 -2374
  148. package/v2Containers/SmsWrapper/index.js +39 -8
  149. package/v2Containers/TagList/index.js +47 -2
  150. package/v2Containers/TagList/messages.js +4 -0
  151. package/v2Containers/TagList/tests/TagList.test.js +122 -20
  152. package/v2Containers/TagList/tests/mockdata.js +17 -0
  153. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  154. package/v2Containers/Templates/_templates.scss +61 -2
  155. package/v2Containers/Templates/actions.js +11 -0
  156. package/v2Containers/Templates/constants.js +2 -0
  157. package/v2Containers/Templates/index.js +90 -40
  158. package/v2Containers/Templates/sagas.js +57 -12
  159. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  160. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  161. package/v2Containers/Templates/tests/sagas.test.js +193 -12
  162. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  163. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  164. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  165. package/v2Containers/TemplatesV2/index.js +86 -23
  166. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  167. package/v2Containers/Viber/index.js +5 -0
  168. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
  169. package/v2Containers/WebPush/Create/index.js +9 -1
  170. package/v2Containers/Whatsapp/index.js +8 -20
  171. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +598 -34
  172. package/v2Containers/Zalo/index.js +2 -0
@@ -10,8 +10,8 @@ import { connect } from 'react-redux';
10
10
  import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
11
11
  import { createStructuredSelector } from 'reselect';
12
12
  import { bindActionCreators, compose } from 'redux';
13
- import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon, CapTooltip } from '@capillarytech/cap-ui-library';
14
- import { find, get } from 'lodash';
13
+ import { CapTab, CapCustomCard, CapButton, CapHeader, CapIcon, CapSpin, CapTooltip } from '@capillarytech/cap-ui-library';
14
+ import { find, get, pick } from 'lodash';
15
15
  import Helmet from 'react-helmet';
16
16
 
17
17
  import { UserIsAuthenticated } from '../../utils/authWrapper';
@@ -36,13 +36,14 @@ import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../../v2Contai
36
36
  import {
37
37
  CALL_TASK,
38
38
  COMMON_CHANNELS,
39
+ LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK,
39
40
  LOYALTY_SUPPORTED_ACTION,
40
41
  MOBILE_PUSH,
41
42
  NORMALIZED_CHANNEL_ALIASES,
42
43
  SMS,
43
44
  } from "../CreativesContainer/constants";
44
45
 
45
- const {CapCustomCardList} = CapCustomCard;
46
+ const { CapCustomCardList } = CapCustomCard;
46
47
 
47
48
  const StyledCapTab = withStyles(CapTab, CapTabStyle);
48
49
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
@@ -119,9 +120,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
119
120
  return !normalizedChannelsToHideSet.has(paneKey);
120
121
  });
121
122
 
122
- if (isFullMode) {
123
- filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: 'assets' });
124
- } else {
123
+ if (isFullMode && !normalizedChannelsToHideSet.has(normalizeChannel(ASSETS))) {
124
+ filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: ASSETS });
125
+ } else if (!isFullMode) {
125
126
  // Add special-mode panes only when not hidden (use normalized checks)
126
127
  if (!normalizedChannelsToHideSet.has(CALL_TASK.toLowerCase())) {
127
128
  filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.callTask), key: CALL_TASK.toLowerCase() });
@@ -222,7 +223,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
222
223
  this.setState({selectedChannel: nextProps.channel, panes });
223
224
  }
224
225
  }
225
- getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
226
+
227
+ getTemplateDataForGrid = ({ templates, handlers, filterContent, channel, isLoading, loadingTip }) => {
226
228
  const currentChannel = channel.toUpperCase();
227
229
  const cardDataList = templates.map((template) => {
228
230
  const templateData =
@@ -248,7 +250,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
248
250
  </CapSpin>
249
251
 
250
252
  </div>);
251
- }
253
+ };
254
+
252
255
  getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
253
256
  getCallTaskComponent = () => (
254
257
  <CallTask
@@ -312,6 +315,29 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
312
315
  if (messageStrategy !== "X_ENGAGE" && channel === 'facebook' && !isFullMode) {
313
316
  return this.getFacebookComponent();
314
317
  }
318
+ const localConfig = this.props.localTemplatesConfig || pick(this.props, LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK);
319
+ const useLocalTemplates = localConfig.useLocalTemplates;
320
+ if (useLocalTemplates && channel === (this.props.channel || 'sms')) {
321
+ // Reuse full Templates component (same UI as Redux flow) with local data only
322
+ const location = { pathname: `/${channel}`, search: '', query: !this.props.isFullMode ? { type: 'embedded', module: 'library' } : {} };
323
+ return (
324
+ <Templates
325
+ key={`${channel}-local`}
326
+ location={location}
327
+ route={{ name: channel }}
328
+ router={this.props.router}
329
+ isFullMode={this.props.isFullMode}
330
+ createNew={this.props.createNew}
331
+ onSelectTemplate={this.props.onSelectTemplate}
332
+ handlePeviewTemplate={this.props.handlePeviewTemplate}
333
+ messageStrategy={this.props.messageStrategy}
334
+ smsRegister={this.props.smsRegister}
335
+ hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
336
+ localTemplatesConfig={localConfig}
337
+ />
338
+ );
339
+ }
340
+
315
341
  const location = {pathname: `/${channel}`, search: '', query};
316
342
  switch (channel) {
317
343
  case 'call_task':
@@ -361,29 +387,55 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
361
387
  }
362
388
  render() {
363
389
  const { isFullMode, className, cap = {}, Global = {}} = this.props;
390
+ const useLocalTemplates = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
364
391
  const { accessiblePermissions = []} = cap.user || Global.user || {};
365
392
  let isCreativeAccessible = true;
366
393
  if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
367
394
  isCreativeAccessible = false;
368
395
  }
396
+ // Recompute active pane content every render so local-list mode updates
397
+ // (templates/loading/search UI) are not stuck with the initial cached pane.
398
+ const panes = this.setChannelContent(this.state.selectedChannel, this.state.panes);
399
+ const hideChannelTabsForLocalSms = useLocalTemplates && panes.length === 1;
400
+ const activeLocalPane = hideChannelTabsForLocalSms
401
+ ? (panes.find(
402
+ (p) => String(p.key).toLowerCase() === String(this.state.selectedChannel).toLowerCase(),
403
+ ) || panes[0])
404
+ : null;
369
405
  return (
370
406
  !isCreativeAccessible ? <AccessForbidden /> : (
371
- <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`} data-testid="cap-wrapper">
372
- {isFullMode && <Helmet
373
- title={this.props.intl.formatMessage(messages.creatives)}
374
- meta={[
375
- { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
376
- ]}
377
- />}
378
- <div className="component-wrapper">
379
- {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
380
- <StyledCapTab
381
- panes={this.state.panes}
382
- onChange={this.channelChange}
383
- activeKey={this.state.selectedChannel}
384
- defaultActiveKey={this.state.selectedChannel}
385
- isFullMode={isFullMode}
407
+ <div
408
+ className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}${useLocalTemplates ? ' creatives-templates-container--local-sms' : ''}`}
409
+ data-testid="cap-wrapper"
410
+ >
411
+ {isFullMode && !useLocalTemplates && (
412
+ <Helmet
413
+ title={this.props.intl.formatMessage(messages.creatives)}
414
+ meta={[
415
+ { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
416
+ ]}
386
417
  />
418
+ )}
419
+ <div className="component-wrapper">
420
+ {isFullMode && (
421
+ <CapHeader
422
+ title={<FormattedMessage {...messages.creatives} />}
423
+ {...(!useLocalTemplates && {
424
+ description: <FormattedMessage {...messages.creativesDesc} />,
425
+ })}
426
+ />
427
+ )}
428
+ {hideChannelTabsForLocalSms ? (
429
+ <div className="templates-v2-local-sms-pane">{activeLocalPane?.content}</div>
430
+ ) : (
431
+ <StyledCapTab
432
+ panes={panes}
433
+ onChange={this.channelChange}
434
+ activeKey={this.state.selectedChannel}
435
+ defaultActiveKey={this.state.selectedChannel}
436
+ isFullMode={isFullMode}
437
+ />
438
+ )}
387
439
  </div>
388
440
  </div>
389
441
  )
@@ -415,6 +467,17 @@ TemplatesV2.propTypes = {
415
467
  currentOrgDetails: PropTypes.object,
416
468
  restrictPersonalization: PropTypes.bool,
417
469
  isAnonymousType: PropTypes.bool,
470
+ // Optional: reuse grid UI with local template list (e.g. SMS fallback). Pass object or same keys as individual props.
471
+ localTemplatesConfig: PropTypes.shape({
472
+ useLocalTemplates: PropTypes.bool,
473
+ localTemplates: PropTypes.arrayOf(PropTypes.object),
474
+ localTemplatesLoading: PropTypes.bool,
475
+ localTemplatesLoadingTip: PropTypes.string,
476
+ localTemplatesFilterContent: PropTypes.node,
477
+ localTemplatesFooterContent: PropTypes.node,
478
+ localTemplatesOnPageChange: PropTypes.func,
479
+ localTemplatesUseSkeleton: PropTypes.bool,
480
+ }),
418
481
  };
419
482
 
420
483
  TemplatesV2.defaultProps = {
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Embedded SMS template list: localTemplatesConfig + SMS-only channel visibility (RCS SMS fallback).
3
+ */
4
+ import React from 'react';
5
+ import { injectIntl } from 'react-intl';
6
+ import '@testing-library/jest-dom';
7
+ import cloneDeep from 'lodash/cloneDeep';
8
+ import { Provider } from 'react-redux';
9
+ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
10
+ import history from '../../../utils/history';
11
+ import { initialReducer } from '../../../initialReducer';
12
+ import { render, screen } from '../../../utils/test-utils';
13
+ import { TemplatesV2 } from '../index';
14
+ import { Templates, authData, currentOrgDetails as currentOrgDetailsMock } from './mockData';
15
+ import { CHANNELS_TO_HIDE_FOR_SMS_ONLY } from '../../../v2Components/SmsFallback/constants';
16
+
17
+ const mockTemplates = jest.fn(() => <div data-testid="templates-mock">Templates</div>);
18
+ jest.mock('v2Containers/Templates', () => ({
19
+ __esModule: true,
20
+ default: (props) => mockTemplates(props),
21
+ }));
22
+
23
+ jest.mock('../../../utils/authWrapper', () => ({
24
+ UserIsAuthenticated: jest.fn((config) => config),
25
+ }));
26
+
27
+ const ComponentToRender = injectIntl(TemplatesV2);
28
+ const renderComponent = (p) => {
29
+ const store = configureStore({}, initialReducer, history);
30
+ return render(
31
+ <Provider store={store}>
32
+ <ComponentToRender {...p} />
33
+ </Provider>,
34
+ );
35
+ };
36
+
37
+ describe('TemplatesV2 local SMS templates (embedded)', () => {
38
+ const templateActions = {
39
+ templateActions: jest.fn(),
40
+ deleteTemplate: jest.fn(),
41
+ getAccountsSettings: jest.fn(),
42
+ getAllTemplates: jest.fn(),
43
+ getCdnTransformationConfig: jest.fn(),
44
+ getDefaultBeeTemplates: jest.fn(),
45
+ getSenderDetails: jest.fn(),
46
+ getTemplateDetails: jest.fn(),
47
+ getUserList: jest.fn(),
48
+ getWeCrmAccounts: jest.fn(),
49
+ handleHtmlUpload: jest.fn(),
50
+ handleZipUpload: jest.fn(),
51
+ resetAccount: jest.fn(),
52
+ resetTemplate: jest.fn(),
53
+ resetTemplateData: jest.fn(),
54
+ resetTemplateStoreData: jest.fn(),
55
+ resetUploadData: jest.fn(),
56
+ setBEETemplate: jest.fn(),
57
+ setChannelAccount: jest.fn(),
58
+ setEdmTemplate: jest.fn(),
59
+ setFacebookAccount: jest.fn(),
60
+ setViberAccount: jest.fn(),
61
+ setWeChatAccount: jest.fn(),
62
+ };
63
+
64
+ const baseProps = {
65
+ cap: {
66
+ user: { accessiblePermissions: ['CREATIVES_UI_VIEW'] },
67
+ },
68
+ actions: { defaultAction: jest.fn(), getTemplates: jest.fn() },
69
+ Templates,
70
+ TemplatesList: Templates?.templates,
71
+ authData,
72
+ templateActions,
73
+ isFullMode: false,
74
+ className: 'embed-test',
75
+ channel: 'sms',
76
+ channelsToHide: CHANNELS_TO_HIDE_FOR_SMS_ONLY,
77
+ channelsToDisable: [],
78
+ onChannelChange: jest.fn(),
79
+ enableNewChannels: [],
80
+ /** Without JP_LOCALE_HIDE_FEATURE so SMS panes are not stripped to Email/Line/Gallery only */
81
+ currentOrgDetails: {
82
+ ...currentOrgDetailsMock,
83
+ accessibleFeatures: (currentOrgDetailsMock.accessibleFeatures || []).filter(
84
+ (f) => f !== 'JP_LOCALE_HIDE_FEATURE',
85
+ ),
86
+ },
87
+ location: {
88
+ pathname: 'v2',
89
+ basename: '/creatives/ui/',
90
+ query: {},
91
+ },
92
+ router: { push: jest.fn() },
93
+ };
94
+
95
+ beforeEach(() => {
96
+ mockTemplates.mockClear();
97
+ });
98
+
99
+ it('adds local-sms container class and single-pane layout when only SMS is visible', () => {
100
+ const p = cloneDeep(baseProps);
101
+ p.localTemplatesConfig = {
102
+ useLocalTemplates: true,
103
+ localTemplates: [],
104
+ localTemplatesLoading: false,
105
+ };
106
+ renderComponent(p);
107
+
108
+ const wrapper = screen.getByTestId('cap-wrapper');
109
+ expect(wrapper).toHaveClass('creatives-templates-container--local-sms');
110
+ expect(document.querySelector('.templates-v2-local-sms-pane')).toBeTruthy();
111
+ expect(mockTemplates).toHaveBeenCalled();
112
+ });
113
+
114
+ it('passes localTemplatesConfig into Templates for the SMS pane', () => {
115
+ const localConfig = {
116
+ useLocalTemplates: true,
117
+ localTemplates: [{ _id: '1', name: 'A' }],
118
+ localTemplatesLoading: false,
119
+ };
120
+ const p = cloneDeep(baseProps);
121
+ p.localTemplatesConfig = localConfig;
122
+ renderComponent(p);
123
+
124
+ expect(mockTemplates).toHaveBeenCalled();
125
+ const passed = mockTemplates.mock.calls.find(
126
+ (call) => call[0] && call[0].localTemplatesConfig && call[0].localTemplatesConfig.useLocalTemplates,
127
+ );
128
+ expect(passed).toBeTruthy();
129
+ expect(passed[0].localTemplatesConfig).toMatchObject(localConfig);
130
+ });
131
+ });
@@ -82,6 +82,7 @@ export const Viber = (props) => {
82
82
  viberData = {},
83
83
  selectedOfferDetails = [],
84
84
  eventContextTags,
85
+ waitEventContextTags,
85
86
  // TestAndPreviewSlidebox props
86
87
  showTestAndPreviewSlidebox: propsShowTestAndPreviewSlidebox,
87
88
  handleTestAndPreview: propsHandleTestAndPreview,
@@ -232,6 +233,8 @@ export const Viber = (props) => {
232
233
  tagsParam: tags,
233
234
  location,
234
235
  tagModule: 'outbound',
236
+ eventContextTags,
237
+ waitEventContextTags,
235
238
  isFullMode,
236
239
  }) || {};
237
240
  if (value.trim() === '') {
@@ -287,6 +290,7 @@ export const Viber = (props) => {
287
290
  userLocale={localStorage.getItem("jlocale") || "en"}
288
291
  selectedOfferDetails={selectedOfferDetails}
289
292
  eventContextTags={eventContextTags}
293
+ waitEventContextTags={waitEventContextTags}
290
294
  />
291
295
  </CapColumn>
292
296
  <div className="viber-textarea-wrapper">
@@ -592,6 +596,7 @@ export const Viber = (props) => {
592
596
  userLocale={localStorage.getItem("jlocale") || "en"}
593
597
  selectedOfferDetails={selectedOfferDetails}
594
598
  eventContextTags={eventContextTags}
599
+ waitEventContextTags={waitEventContextTags}
595
600
  />
596
601
  <CapColumn className="cta-actions">
597
602
  <CapButton
@@ -22,8 +22,6 @@ export const useTagManagement = ({
22
22
  metaEntities,
23
23
  getDefaultTags,
24
24
  supportedTags = EMPTY_ARRAY,
25
- injectedTags,
26
- eventContextTags = EMPTY_ARRAY,
27
25
  }) => {
28
26
  const [tags, setTags] = useState([]);
29
27
  const tagFetchKeyRef = useRef(null);
@@ -84,6 +84,7 @@ const MemoizedTagList = memo(({
84
84
  injectedTags,
85
85
  selectedOfferDetails,
86
86
  eventContextTags,
87
+ waitEventContextTags,
87
88
  forwardedTags,
88
89
  onTagSelect,
89
90
  restrictPersonalization = false,
@@ -99,6 +100,7 @@ const MemoizedTagList = memo(({
99
100
  injectedTags={injectedTags}
100
101
  selectedOfferDetails={selectedOfferDetails}
101
102
  eventContextTags={eventContextTags}
103
+ waitEventContextTags={waitEventContextTags}
102
104
  forwardedTags={forwardedTags}
103
105
  onTagSelect={onTagSelect}
104
106
  restrictPersonalization={restrictPersonalization}
@@ -116,6 +118,7 @@ const MemoizedTagList = memo(({
116
118
  && prevProps.injectedTags === nextProps.injectedTags
117
119
  && prevProps.selectedOfferDetails === nextProps.selectedOfferDetails
118
120
  && prevProps.eventContextTags === nextProps.eventContextTags
121
+ && prevProps.waitEventContextTags === nextProps.waitEventContextTags
119
122
  && prevProps.forwardedTags === nextProps.forwardedTags
120
123
  && prevProps.onTagSelect === nextProps.onTagSelect
121
124
  && prevProps.restrictPersonalization === nextProps.restrictPersonalization
@@ -152,6 +155,7 @@ const WebPushCreate = ({
152
155
  forwardedTags,
153
156
  selectedOfferDetails = [],
154
157
  eventContextTags = [],
158
+ waitEventContextTags = {},
155
159
  templateActions: templateActionsProps,
156
160
  Templates,
157
161
  restrictPersonalization = false,
@@ -232,6 +236,7 @@ const WebPushCreate = ({
232
236
  supportedTags: memoizedSupportedTags,
233
237
  injectedTags,
234
238
  eventContextTags,
239
+ waitEventContextTags,
235
240
  });
236
241
  const { tags, handleOnTagsContextChange, validationConfig } = tagState;
237
242
  const { weCrmAccounts } = Templates;
@@ -837,12 +842,13 @@ const WebPushCreate = ({
837
842
  injectedTags,
838
843
  selectedOfferDetails,
839
844
  eventContextTags,
845
+ waitEventContextTags,
840
846
  forwardedTags,
841
847
  restrictPersonalization,
842
848
  disabled: restrictPersonalization,
843
849
  disableTooltipMsg: restrictPersonalization ? formatMessage(messages.personalizationNotSupportedAnonymous) : undefined,
844
850
  }),
845
- [tags, injectedTags, selectedOfferDetails, eventContextTags, forwardedTags, restrictPersonalization, formatMessage],
851
+ [tags, injectedTags, selectedOfferDetails, eventContextTags, waitEventContextTags, forwardedTags, restrictPersonalization, formatMessage],
846
852
  );
847
853
 
848
854
  // Memoized TagList components with optimized props
@@ -1083,6 +1089,7 @@ WebPushCreate.propTypes = {
1083
1089
  forwardedTags: PropTypes.object,
1084
1090
  selectedOfferDetails: PropTypes.array,
1085
1091
  eventContextTags: PropTypes.array,
1092
+ waitEventContextTags: PropTypes.object,
1086
1093
  templateActions: PropTypes.object,
1087
1094
  restrictPersonalization: PropTypes.bool,
1088
1095
  };
@@ -1111,6 +1118,7 @@ WebPushCreate.defaultProps = {
1111
1118
  forwardedTags: {},
1112
1119
  selectedOfferDetails: [],
1113
1120
  eventContextTags: [],
1121
+ waitEventContextTags: {},
1114
1122
  templateActions: {},
1115
1123
  Templates: {},
1116
1124
  restrictPersonalization: false,
@@ -118,6 +118,7 @@ import { ANDROID } from '../../v2Components/CommonTestAndPreview/constants';
118
118
  import CapImageUpload from '../../v2Components/CapImageUpload';
119
119
  import TagList from '../TagList';
120
120
  import { validateTags } from '../../utils/tagValidations';
121
+ import { splitContentByOrderedVarTokens } from '../../utils/templateVarUtils';
121
122
  import { capitalizeString } from '../../utils/Formatter';
122
123
  import CapWhatsappCTA from '../../v2Components/CapWhatsappCTA';
123
124
  import {
@@ -180,6 +181,7 @@ export const Whatsapp = (props) => {
180
181
  getFormData,
181
182
  selectedOfferDetails,
182
183
  eventContextTags,
184
+ waitEventContextTags = {},
183
185
  metaDataStatus = "",
184
186
  showTestAndPreviewSlidebox: propsShowTestAndPreviewSlidebox,
185
187
  handleTestAndPreview: propsHandleTestAndPreview,
@@ -482,28 +484,10 @@ export const Whatsapp = (props) => {
482
484
  );
483
485
  };
484
486
 
485
- const converStringToVarArr = (validVarArr, content) => {
486
- const templateVarArray = [];
487
- while (content?.length !== 0) {
488
- //converting content string to an array split at var
489
- const index = content.indexOf(validVarArr?.[0]);
490
- if (index !== -1) {
491
- templateVarArray.push(content.substring(0, index)); //push string before var
492
- templateVarArray.push(validVarArr?.[0]); //push var
493
- content = content.substring(index + validVarArr?.[0]?.length, content?.length); //remaining str
494
- validVarArr?.shift(); //remove considered var
495
- } else {
496
- templateVarArray.push(content); //remaining str
497
- break;
498
- }
499
- }
500
- return templateVarArray;
501
- }
502
-
503
487
  const computeTextMessage = (msg, varMap, regex) => {
504
488
  const validVarArr = msg?.match(regex) || [];
505
489
  //conerting msg string to variable arr
506
- const templateHeaderArray = converStringToVarArr(validVarArr, msg);
490
+ const templateHeaderArray = splitContentByOrderedVarTokens(validVarArr, msg);
507
491
  if (templateHeaderArray?.length !== 0) {
508
492
  let clonedVarMap = {};
509
493
  if (!isEmpty(varMap)) {
@@ -557,7 +541,7 @@ export const Whatsapp = (props) => {
557
541
  setUnsubscribeRequired(true);
558
542
  }
559
543
  //converting msg string to variable arr
560
- const templateMessageArray = converStringToVarArr(validVarArr, msg);
544
+ const templateMessageArray = splitContentByOrderedVarTokens(validVarArr, msg);
561
545
  updateTempMsgArray(templateMessageArray.filter((i) => i === 0 || i));
562
546
  };
563
547
 
@@ -950,6 +934,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
950
934
  injectedTags={injectedTags || {}}
951
935
  selectedOfferDetails={selectedOfferDetails}
952
936
  eventContextTags={eventContextTags}
937
+ waitEventContextTags={waitEventContextTags}
953
938
  />
954
939
  )}
955
940
  </>
@@ -1951,6 +1936,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1951
1936
  injectedTags={injectedTags || {}}
1952
1937
  selectedOfferDetails={selectedOfferDetails}
1953
1938
  eventContextTags={eventContextTags}
1939
+ waitEventContextTags={waitEventContextTags}
1954
1940
  />
1955
1941
  )
1956
1942
  : !isAuthenticationTemplate && (
@@ -2757,6 +2743,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
2757
2743
  injectedTags={injectedTags || {}}
2758
2744
  selectedOfferDetails={selectedOfferDetails}
2759
2745
  eventContextTags={eventContextTags}
2746
+ waitEventContextTags={waitEventContextTags}
2760
2747
  />
2761
2748
  )
2762
2749
  }
@@ -2875,6 +2862,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
2875
2862
  injectedTags={injectedTags || {}}
2876
2863
  selectedOfferDetails={selectedOfferDetails}
2877
2864
  eventContextTags={eventContextTags}
2865
+ waitEventContextTags={waitEventContextTags}
2878
2866
  />
2879
2867
  )}
2880
2868
  {isBtnTypeQuickReply && (