cap-creatives-ui 8.0.280 → 8.0.321

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 (247) hide show
  1. package/.github/workflows/pr-title-check.yml +88 -0
  2. package/app/constants/unified.js +21 -1
  3. package/app/containers/App/constants.js +0 -1
  4. package/app/containers/Login/test/index.test.js +123 -0
  5. package/app/containers/Login/test/selectors.test.js +165 -0
  6. package/app/initialState.js +0 -2
  7. package/app/services/api.js +6 -0
  8. package/app/services/tests/api.test.js +7 -0
  9. package/app/services/tests/getSchema.test.js +95 -0
  10. package/app/utils/common.js +23 -9
  11. package/app/utils/commonUtils.js +64 -93
  12. package/app/utils/tagValidations.js +83 -219
  13. package/app/utils/templateVarUtils.js +172 -0
  14. package/app/utils/tests/common.test.js +265 -323
  15. package/app/utils/tests/commonUtil.test.js +461 -118
  16. package/app/utils/tests/commonUtils.test.js +581 -0
  17. package/app/utils/tests/messageUtils.test.js +95 -0
  18. package/app/utils/tests/smsCharCount.test.js +304 -0
  19. package/app/utils/tests/smsCharCountV2.test.js +213 -10
  20. package/app/utils/tests/tagValidations.test.js +474 -357
  21. package/app/utils/tests/templateVarUtils.test.js +160 -0
  22. package/app/v2Components/CapDeviceContent/index.js +10 -7
  23. package/app/v2Components/CapTagList/index.js +32 -24
  24. package/app/v2Components/CapTagList/style.scss +48 -0
  25. package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  26. package/app/v2Components/CapTagListWithInput/index.js +8 -0
  27. package/app/v2Components/CapWhatsappCTA/index.js +2 -0
  28. package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
  29. package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
  30. package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  31. package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
  32. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
  33. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
  34. package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
  35. package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
  36. package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
  37. package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
  38. package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
  39. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  40. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  41. package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
  42. package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
  43. package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
  44. package/app/v2Components/CommonTestAndPreview/index.js +878 -156
  45. package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
  46. package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  47. package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
  48. package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
  49. package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
  50. package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  51. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
  52. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
  53. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
  54. package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
  55. package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  56. package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
  57. package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
  58. package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
  59. package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  60. package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
  61. package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
  62. package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
  63. package/app/v2Components/ErrorInfoNote/index.js +24 -26
  64. package/app/v2Components/FormBuilder/index.js +182 -204
  65. package/app/v2Components/FormBuilder/messages.js +4 -8
  66. package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
  67. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
  68. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
  69. package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
  70. package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
  71. package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
  72. package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
  73. package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
  74. package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  75. package/app/v2Components/SmsFallback/constants.js +73 -0
  76. package/app/v2Components/SmsFallback/index.js +956 -0
  77. package/app/v2Components/SmsFallback/index.scss +265 -0
  78. package/app/v2Components/SmsFallback/messages.js +78 -0
  79. package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  80. package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  81. package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  82. package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  83. package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  84. package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  85. package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  86. package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  87. package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
  88. package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  89. package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  90. package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  91. package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
  92. package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  93. package/app/v2Containers/BeeEditor/index.js +3 -0
  94. package/app/v2Containers/BeePopupEditor/index.js +9 -2
  95. package/app/v2Containers/Cap/mockData.js +0 -14
  96. package/app/v2Containers/Cap/reducer.js +3 -55
  97. package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
  98. package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  99. package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  100. package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  101. package/app/v2Containers/CommunicationFlow/constants.js +200 -0
  102. package/app/v2Containers/CommunicationFlow/index.js +102 -0
  103. package/app/v2Containers/CommunicationFlow/messages.js +346 -0
  104. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  105. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  106. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  107. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  108. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  109. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  110. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  111. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  112. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  113. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  114. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  115. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  116. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  117. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  118. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  119. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  120. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  121. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  122. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  123. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  124. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  125. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  126. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  127. package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  128. package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  129. package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
  130. package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
  131. package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  132. package/app/v2Containers/CreativesContainer/constants.js +24 -0
  133. package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  134. package/app/v2Containers/CreativesContainer/index.js +346 -71
  135. package/app/v2Containers/CreativesContainer/index.scss +51 -1
  136. package/app/v2Containers/CreativesContainer/messages.js +12 -0
  137. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  138. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
  139. package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
  140. package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  141. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
  142. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  143. package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
  144. package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  145. package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  146. package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  147. package/app/v2Containers/Email/index.js +2 -5
  148. package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
  149. package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  150. package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
  151. package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  152. package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
  153. package/app/v2Containers/EmailWrapper/index.js +4 -0
  154. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  155. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
  156. package/app/v2Containers/FTP/index.js +2 -51
  157. package/app/v2Containers/FTP/messages.js +0 -4
  158. package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
  159. package/app/v2Containers/InApp/index.js +297 -118
  160. package/app/v2Containers/InApp/tests/index.test.js +17 -6
  161. package/app/v2Containers/InApp/tests/mockData.js +1 -1
  162. package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  163. package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  164. package/app/v2Containers/InAppWrapper/index.js +3 -0
  165. package/app/v2Containers/InappAdvance/index.js +5 -104
  166. package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
  167. package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
  168. package/app/v2Containers/Line/Container/Text/index.js +0 -1
  169. package/app/v2Containers/MobilePush/Create/index.js +105 -28
  170. package/app/v2Containers/MobilePush/Create/messages.js +4 -0
  171. package/app/v2Containers/MobilePush/Edit/index.js +250 -68
  172. package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
  173. package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
  174. package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
  175. package/app/v2Containers/MobilePushNew/index.js +78 -35
  176. package/app/v2Containers/MobilePushNew/messages.js +8 -0
  177. package/app/v2Containers/MobilepushWrapper/index.js +11 -1
  178. package/app/v2Containers/Rcs/constants.js +32 -1
  179. package/app/v2Containers/Rcs/index.js +963 -916
  180. package/app/v2Containers/Rcs/index.scss +85 -6
  181. package/app/v2Containers/Rcs/messages.js +10 -1
  182. package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  183. package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
  184. package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  185. package/app/v2Containers/Rcs/tests/index.test.js +41 -38
  186. package/app/v2Containers/Rcs/tests/mockData.js +38 -0
  187. package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  188. package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
  189. package/app/v2Containers/Rcs/utils.js +358 -10
  190. package/app/v2Containers/Sms/Create/index.js +122 -39
  191. package/app/v2Containers/Sms/Create/messages.js +4 -0
  192. package/app/v2Containers/Sms/Edit/index.js +37 -3
  193. package/app/v2Containers/Sms/commonMethods.js +3 -6
  194. package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  195. package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
  196. package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  197. package/app/v2Containers/SmsTrai/Create/index.js +9 -4
  198. package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
  199. package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
  200. package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
  201. package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
  202. package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
  203. package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
  204. package/app/v2Containers/SmsWrapper/index.js +41 -8
  205. package/app/v2Containers/TagList/index.js +63 -2
  206. package/app/v2Containers/TagList/messages.js +8 -0
  207. package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
  208. package/app/v2Containers/TagList/tests/mockdata.js +17 -0
  209. package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
  210. package/app/v2Containers/Templates/_templates.scss +61 -2
  211. package/app/v2Containers/Templates/actions.js +11 -0
  212. package/app/v2Containers/Templates/constants.js +2 -0
  213. package/app/v2Containers/Templates/index.js +90 -40
  214. package/app/v2Containers/Templates/reducer.js +3 -1
  215. package/app/v2Containers/Templates/sagas.js +57 -12
  216. package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  217. package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  218. package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
  219. package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
  220. package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  221. package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  222. package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  223. package/app/v2Containers/TemplatesV2/index.js +147 -49
  224. package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  225. package/app/v2Containers/Viber/index.js +9 -10
  226. package/app/v2Containers/Viber/index.scss +1 -1
  227. package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
  228. package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
  229. package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
  230. package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
  231. package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
  232. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
  233. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
  234. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
  235. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  236. package/app/v2Containers/WebPush/Create/index.js +36 -6
  237. package/app/v2Containers/WebPush/Create/index.scss +5 -0
  238. package/app/v2Containers/WebPush/Create/messages.js +8 -1
  239. package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
  240. package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
  241. package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
  242. package/app/v2Containers/Whatsapp/index.js +28 -53
  243. package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
  244. package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
  245. package/app/v2Containers/Zalo/index.js +5 -11
  246. package/package.json +2 -2
  247. package/version +9 -0
@@ -1,10 +1,11 @@
1
1
  /* eslint-disable no-restricted-syntax */
2
2
  /* eslint-disable no-undef */
3
- import React, { useState, useEffect } from 'react';
3
+ import React, { useState, useEffect, useMemo, useRef } from 'react';
4
+ import PropTypes from 'prop-types';
4
5
  import { createStructuredSelector } from 'reselect';
5
6
  import { bindActionCreators } from 'redux';
6
7
  import { FormattedMessage } from 'react-intl';
7
- import { get, cloneDeep, isEmpty, isObject } from 'lodash';
8
+ import { get, cloneDeep, isEmpty } from 'lodash';
8
9
  import styled from 'styled-components';
9
10
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
10
11
  import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
@@ -27,22 +28,22 @@ import {
27
28
  CAP_SPACE_32,
28
29
  CAP_SPACE_04,
29
30
  CAP_WHITE,
30
- CAP_G10,
31
31
  } from '@capillarytech/cap-ui-library/styled/variables';
32
32
  import { makeSelectTemplateDetailsResponse } from '../../Sms/Edit/selectors';
33
33
  import { makeSelectMetaEntities, selectLiquidStateDetails, setInjectedTags } from '../../Cap/selectors';
34
34
  import * as smsEditActions from '../../Sms/Edit/actions';
35
35
  import * as globalActions from '../../Cap/actions';
36
36
  import messages from './messages';
37
+ import globalMessages from '../../Cap/messages';
37
38
  import TagList from '../../TagList';
38
39
  import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
39
40
  import UnifiedPreview from '../../../v2Components/CommonTestAndPreview/UnifiedPreview';
40
41
  import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
41
42
  import withCreatives from '../../../hoc/withCreatives';
42
- import { validateTags } from '../../../utils/tagValidations';
43
43
  import {
44
44
  CHARLIMIT,
45
45
  SMS,
46
+ SMS_TRAI_CONTENT_MAX_LENGTH,
46
47
  SMS_TRAI_VAR,
47
48
  TAG,
48
49
  EMBEDDED,
@@ -54,20 +55,36 @@ import {
54
55
  import v2EditSmsReducer from '../../Sms/Edit/reducer';
55
56
  import { v2SmsEditSagas } from '../../Sms/Edit/sagas';
56
57
  import ErrorInfoNote from '../../../v2Components/ErrorInfoNote';
58
+ import { isTraiDLTEnable, hasTraiDltFeature } from '../../../utils/common';
57
59
  import { validateLiquidTemplateContent } from '../../../utils/commonUtils';
58
- import { hasLiquidSupportFeature } from '../../../utils/common';
60
+ import { validateTags } from '../../../utils/tagValidations';
59
61
  import { ANDROID } from '../../../v2Components/CommonTestAndPreview/constants';
62
+ import {
63
+ getFallbackResolvedContent,
64
+ splitTemplateVarString,
65
+ COMBINED_SMS_TEMPLATE_VAR_REGEX,
66
+ isAnyTemplateVarToken,
67
+ isDltHashVarToken,
68
+ } from '../../../utils/templateVarUtils';
69
+ import VarSegmentMessageEditor from '../../../v2Components/VarSegmentMessageEditor';
70
+ import rcsMessages from '../../Rcs/messages';
71
+
72
+ import './index.scss';
60
73
 
61
- let varMap = {};
62
- let traiData = {};
63
- let tagValidationResponse = {};
64
74
  const { TextArea } = CapInput;
65
75
  const { CapLabelInline } = CapLabel;
66
76
 
77
+ /** Redux `metaEntities` may be an Immutable.Map; TagList needs plain `tags.standard`. */
78
+ const getStandardTagsFromMeta = (metaEntities) => {
79
+ if (!metaEntities) return [];
80
+ const plain = typeof metaEntities.toJS === 'function' ? metaEntities.toJS() : metaEntities;
81
+ const standard = get(plain, 'tags.standard');
82
+ return Array.isArray(standard) ? standard : [];
83
+ };
84
+
67
85
  export const SmsTraiEdit = (props) => {
68
86
  const {
69
87
  intl,
70
- handleClose,
71
88
  params,
72
89
  actions,
73
90
  templateDetails,
@@ -83,8 +100,21 @@ export const SmsTraiEdit = (props) => {
83
100
  templateData = {},
84
101
  selectedOfferDetails,
85
102
  eventContextTags,
103
+ waitEventContextTags,
86
104
  fetchingLiquidTags,
87
105
  getLiquidTags,
106
+ showLiquidErrorInFooter,
107
+ smsRegister,
108
+ // RCS -> SMS fallback edit mode
109
+ isRcsSmsFallback = false,
110
+ /** When editing an existing RCS template, lock Unicode (matches product: no mid-edit toggle). */
111
+ isRcsEditFlow = false,
112
+ showPreviewInRcsFallback = false,
113
+ hidePreview = false,
114
+ isOverview = false,
115
+ forceFullTagContext = false,
116
+ /** RCS parent: merge `rcsSmsFallbackVarMapped` into `smsFallbackData` (same idea as `cardVarMapped`). */
117
+ onRcsFallbackEditorStateChange,
88
118
  } = props || {};
89
119
 
90
120
  const { formatMessage } = intl;
@@ -97,20 +127,161 @@ export const SmsTraiEdit = (props) => {
97
127
  const [isTagValidationError, updateIsTagValidationError] = useState(false);
98
128
  const [totalMessageLength, setTotalMessageLength] = useState(0);
99
129
  const [isUnicodeAllowed, updateIsUnicodeAllowed] = useState(true);
130
+ const [fallbackText, setFallbackText] = useState('');
131
+ const [fallbackVarMappedData, setFallbackVarMappedData] = useState({});
132
+ const [fallbackFocusedId, setFallbackFocusedId] = useState('');
100
133
  const [showMsgLengthNote, updateshowMsgLengthNote] = useState(false);
101
134
  const [liquidErrorMessages, setLiquidErrorMessages] = useState({});
102
135
  const [isLiquidValidationError, setIsLiquidValidationError] = useState(false);
136
+ /** After user closes the validation panel, keep it hidden until Save is clicked again (even if ErrorInfoNote remounts). */
137
+ const [liquidErrorPanelDismissed, setLiquidErrorPanelDismissed] = useState(false);
103
138
  const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
139
+
140
+ /** Per-instance only — was module-level (leaked across mounts / multiple editors). */
141
+ const varMapRef = useRef({});
142
+ const traiDataRef = useRef({});
143
+ const tagValidationResponseRef = useRef({});
144
+ /** De-dupe repeated /meta/TAG fetches from popover/context callbacks. */
145
+ const lastTagSchemaQueryKeyRef = useRef(null);
146
+ /** Latest template props for RCS fallback init — avoids effect deps on unstable `templateData` references. */
147
+ const rcsFallbackTemplateSourceRef = useRef({ isFullMode, templateDetails, templateData });
148
+ rcsFallbackTemplateSourceRef.current = { isFullMode, templateDetails, templateData };
149
+
150
+ const fetchTagSchemaIfNewQuery = (query) => {
151
+ const key = JSON.stringify(query);
152
+ if (lastTagSchemaQueryKeyRef.current === key) return;
153
+ lastTagSchemaQueryKeyRef.current = key;
154
+ globalActions.fetchSchemaForEntity(query);
155
+ };
156
+
157
+ const traiDltEnabled = useMemo(
158
+ () => isTraiDLTEnable(isFullMode, smsRegister),
159
+ [isFullMode, smsRegister],
160
+ );
161
+ /**
162
+ * VarSegment for RCS SMS fallback when:
163
+ * - `isTraiDLTEnable` passes (library + `smsRegister === 'DLT'`, or full-mode library), or
164
+ * - org has TRAI DLT (`hasTraiDltFeature`) — needed when slidebox used to force `isFullMode={false}`
165
+ * or `smsRegister` is missing so `isTraiDLTEnable` alone is false, or
166
+ * - RCS template inline edit (`isRcsEditFlow`).
167
+ */
168
+ const useRcsFallbackVarSegment = useMemo(() => {
169
+ if (!isRcsSmsFallback) return false;
170
+ return traiDltEnabled || hasTraiDltFeature() || isRcsEditFlow;
171
+ }, [isRcsSmsFallback, traiDltEnabled, isRcsEditFlow]);
172
+
173
+ /**
174
+ * RCS SMS fallback: always show character count vs TRAI max (`SMS_TRAI_CONTENT_MAX_LENGTH`).
175
+ * Do not use `totalCharacters` ({smsCount} SMS via length/160) here: resolved template + variable
176
+ * values can exceed one GSM segment while DLT still shows a single registered template — length/160
177
+ * is only a rough segment hint and reads as “wrong SMS count” in campaigns.
178
+ */
179
+ const renderDescriptionCharacterCount = (className = "rcs-character-count") => (
180
+ <CapLabel type="label1" className={className}>
181
+ {formatMessage(messages.charactersCountLabel, {
182
+ current: totalMessageLength,
183
+ max: SMS_TRAI_CONTENT_MAX_LENGTH,
184
+ })}
185
+ </CapLabel>
186
+ );
187
+
188
+ const dltConsecutiveRunLength = (segments, index) => {
189
+ if (!Array.isArray(segments) || typeof segments[index] !== 'string' || !isDltHashVarToken(segments[index])) {
190
+ return 0;
191
+ }
192
+ let lo = index;
193
+ while (lo > 0 && isDltHashVarToken(segments[lo - 1])) lo -= 1;
194
+ let hi = index;
195
+ while (hi < segments.length - 1 && isDltHashVarToken(segments[hi + 1])) hi += 1;
196
+ return hi - lo + 1;
197
+ };
198
+
199
+ const renderRcsFallbackMessage = (str = '') => {
200
+ if (!useRcsFallbackVarSegment) {
201
+ return (
202
+ <CapRow className="rcs-create-template-message-input">
203
+ <div className="rcs_text_area_wrapper">
204
+ <TextArea
205
+ id="rcs_fallback_message_textarea"
206
+ autosize={{ minRows: 4, maxRows: 12 }}
207
+ value={fallbackText}
208
+ onChange={(e) => setFallbackText(e.target.value)}
209
+ placeholder={formatMessage(rcsMessages.fallbackMsgPlaceholder)}
210
+ data-testid="rcs_fallback_plain_text_area"
211
+ />
212
+ {renderDescriptionCharacterCount()}
213
+ </div>
214
+ </CapRow>
215
+ );
216
+ }
217
+ return (
218
+ <CapRow className="rcs-create-template-message-input">
219
+ <div className="rcs_text_area_wrapper">
220
+ <VarSegmentMessageEditor
221
+ templateString={str}
222
+ valueMap={fallbackVarMappedData || {}}
223
+ onChange={(varSegmentFieldId, nextSlotValue) => {
224
+ setFallbackVarMappedData((previousSlotMap) => ({
225
+ ...(previousSlotMap || {}),
226
+ [varSegmentFieldId]: nextSlotValue,
227
+ }));
228
+ }}
229
+ onFocus={setFallbackFocusedId}
230
+ varRegex={COMBINED_SMS_TEMPLATE_VAR_REGEX}
231
+ placeholderPrefix=""
232
+ getPlaceholder={() => formatMessage(rcsMessages.fallbackMsgPlaceholder)}
233
+ renderVarFooter={(dltSegmentToken, dltSegmentIndex) => {
234
+ if (!isDltHashVarToken(dltSegmentToken)) return null;
235
+ const segments = splitTemplateVarString(str, COMBINED_SMS_TEMPLATE_VAR_REGEX);
236
+ const varCounts = dltConsecutiveRunLength(segments, dltSegmentIndex);
237
+ if (varCounts < 1) return null;
238
+ /* Inline layout/color so hint is visible even if CapSpin / load order blocks SCSS */
239
+ const dltVarSlotHintStyle = {
240
+ display: 'block',
241
+ marginTop: CAP_SPACE_04,
242
+ marginLeft: 'auto',
243
+ marginRight: 0,
244
+ width: 'fit-content',
245
+ maxWidth: '100%',
246
+ textAlign: 'right',
247
+ color: 'rgba(0, 0, 0, 0.45)',
248
+ fontSize: 12,
249
+ fontWeight: 400,
250
+ lineHeight: '16px',
251
+ };
252
+ return (
253
+ <span className="sms-trai-rcs-fallback-var-hint" style={dltVarSlotHintStyle}>
254
+ {formatMessage(messages.textAreaCounts, {
255
+ varCounts,
256
+ var: SMS_TRAI_VAR,
257
+ charCounts: varCounts * CHARLIMIT,
258
+ })}
259
+ </span>
260
+ );
261
+ }}
262
+ />
263
+ {(!isFullMode)
264
+ ? renderDescriptionCharacterCount('rcs-character-count rcs-character-count--compact')
265
+ : (isFullMode && renderDescriptionCharacterCount())}
266
+ </div>
267
+ </CapRow>
268
+ );
269
+ };
104
270
  const SMSTraiFooter = styled.div`
105
271
  background-color: ${CAP_WHITE};
106
272
  padding: ${CAP_SPACE_32} ${CAP_SPACE_24};
107
273
  position: fixed;
108
274
  bottom: 2rem;
109
275
  margin-left: -2rem;
276
+ /* Prevent global creatives tabpane min-height from stretching footer validation tabs */
277
+ .error-info-note__tabs {
278
+ .ant-tabs-tabpane {
279
+ min-height: auto;
280
+ }
281
+ }
110
282
  .ant-btn {
111
283
  margin-right: ${CAP_SPACE_16};
112
284
  }
113
- }
114
285
  `;
115
286
  const TraiEditTemplateDetails = styled.div`
116
287
  margin-bottom: ${CAP_SPACE_16};
@@ -119,41 +290,127 @@ export const SmsTraiEdit = (props) => {
119
290
  }
120
291
  `;
121
292
 
293
+ /**
294
+ * RCS embedded SMS fallback receives a new `templateData` object from `mapFallbackValueToEditTemplateData`
295
+ * on every parent render. Depending on `[templateDetails || templateData]` re-ran init and wiped
296
+ * VarSegmentMessageEditor state whenever Test & Preview (or any) re-rendered Rcs. Key only changes
297
+ * when template id / sms-editor / header actually change.
298
+ *
299
+ * Do NOT include `unicode-validity` in the key: parent merges `onRcsFallbackEditorStateChange`
300
+ * unicode patches into `templateData`, which would change the key every toggle and re-run init —
301
+ * resetting local checkbox state (visible as the unicode control flipping).
302
+ *
303
+ * Do NOT include `rcs-sms-fallback-var-mapped` in the key: the parent merges slot edits into
304
+ * `templateData` on every change, so the key would change each keystroke and this effect would
305
+ * re-run and reset slots — visible as toggling between old and new values.
306
+ */
307
+ const rcsFallbackTemplateInitKey = useMemo(() => {
308
+ if (!isRcsSmsFallback) return null;
309
+ // RCS fallback slidebox always passes `templateData` from `mapFallbackValueToEditTemplateData`.
310
+ // `templateDetails` stays empty there (no route id fetch) — do not branch on `isFullMode` or init
311
+ // key is null, the effect never runs, and `loading` stays true forever.
312
+ const activeSmsTemplateSource = templateData;
313
+ if (!activeSmsTemplateSource || isEmpty(activeSmsTemplateSource)) return null;
314
+ const templateBase = get(activeSmsTemplateSource, 'versions.base', {}) || {};
315
+ const smsEditorTemplateString = templateBase?.['sms-editor'] ?? '';
316
+ const registeredSenderHeaderList = templateBase?.header;
317
+ const headerListFingerprint = Array.isArray(registeredSenderHeaderList)
318
+ ? registeredSenderHeaderList.join('\u001f')
319
+ : '';
320
+ const templateRecordId = activeSmsTemplateSource?._id ?? '';
321
+ return [
322
+ templateRecordId,
323
+ smsEditorTemplateString,
324
+ headerListFingerprint,
325
+ ].join('\u0000');
326
+ }, [isRcsSmsFallback, templateData]);
327
+
122
328
  useEffect(() => {
123
- //fetching tags
124
329
  const { type, module } = location.query || {};
125
330
  const isEmbedded = type === EMBEDDED;
126
331
  const query = {
127
332
  layout: SMS,
128
333
  type: TAG,
129
334
  context: isEmbedded ? module : DEFAULT,
130
- embedded: isEmbedded ? type : FULL,
335
+ embedded: forceFullTagContext ? FULL : (isEmbedded ? type : FULL),
131
336
  };
132
337
  if (getDefaultTags) {
133
338
  query.context = getDefaultTags;
134
339
  }
135
- globalActions.fetchSchemaForEntity(query);
136
- //fetching template data in fullmode
340
+ fetchTagSchemaIfNewQuery(query);
137
341
  const { id } = params || {};
138
342
  if (id) {
139
343
  actions.getTemplateDetails(id);
140
344
  }
141
- //cleanup code
142
345
  return () => {
143
346
  actions.resetEditTemplate();
144
- varMap = {};
347
+ varMapRef.current = {};
145
348
  };
146
349
  }, []);
147
350
 
148
- //computing placeholder array for mapping values and rendering dynamic form
149
351
  useEffect(() => {
150
- traiData = isFullMode ? templateDetails : templateData;
151
- if (traiData && !isEmpty(traiData)) {
152
- let msg = get(traiData, `versions.base.sms-editor`, '');
352
+ if (!isRcsSmsFallback || rcsFallbackTemplateInitKey == null) return;
353
+ const {
354
+ isFullMode: isFullModeFromRef,
355
+ templateDetails: templateDetailsFromRef,
356
+ templateData: templateDataFromRef,
357
+ } = rcsFallbackTemplateSourceRef.current;
358
+ const activeTemplateSourceForInit = isRcsSmsFallback
359
+ ? templateDataFromRef
360
+ : (isFullModeFromRef ? templateDetailsFromRef : templateDataFromRef);
361
+ if (!activeTemplateSourceForInit || isEmpty(activeTemplateSourceForInit)) return;
362
+ traiDataRef.current = activeTemplateSourceForInit;
363
+ const templateBase = get(activeTemplateSourceForInit, 'versions.base', {});
364
+ const unicodeValidity = get(templateBase, 'unicode-validity', true);
365
+ const templateMsg = get(activeTemplateSourceForInit, 'versions.base.sms-editor', '') || '';
366
+ if (!useRcsFallbackVarSegment) {
367
+ setFallbackText(templateMsg);
368
+ setFallbackVarMappedData({});
369
+ setUpdatedSmsEditor(String(templateMsg).split(''));
370
+ setTotalMessageLength(String(templateMsg).length);
371
+ updateIsUnicodeAllowed(unicodeValidity);
372
+ updateLoading(false);
373
+ return;
374
+ }
375
+ const savedVarMap = get(templateBase, 'rcs-sms-fallback-var-mapped', {}) || {};
376
+ const initialVarMap = {};
377
+ const templateSegments = splitTemplateVarString(templateMsg, COMBINED_SMS_TEMPLATE_VAR_REGEX);
378
+ let varOrdinal = 0;
379
+ templateSegments.forEach((segmentToken, segmentIndexInTemplate) => {
380
+ const isVar =
381
+ typeof segmentToken === 'string' && isAnyTemplateVarToken(segmentToken);
382
+ if (!isVar) return;
383
+ varOrdinal += 1;
384
+ const varSegmentSlotId = `${segmentToken}_${segmentIndexInTemplate}`;
385
+ const persistedSlotValue =
386
+ savedVarMap?.[varSegmentSlotId]
387
+ ?? savedVarMap?.[`${segmentToken}_${varOrdinal}`];
388
+ // Persisted '' means the user cleared the slot — must not fall back to `segmentToken` for mustache
389
+ // (that would resurrect {{…}} in the input and look like the tag "came back").
390
+ if (typeof persistedSlotValue === 'string') {
391
+ initialVarMap[varSegmentSlotId] = persistedSlotValue;
392
+ } else if (isDltHashVarToken(segmentToken)) {
393
+ initialVarMap[varSegmentSlotId] = '';
394
+ } else {
395
+ initialVarMap[varSegmentSlotId] = segmentToken;
396
+ }
397
+ });
398
+ setFallbackText(templateMsg);
399
+ setFallbackVarMappedData(initialVarMap);
400
+ const initialResolvedFallbackDisplay = getFallbackResolvedContent(templateMsg, initialVarMap);
401
+ setUpdatedSmsEditor(initialResolvedFallbackDisplay.split(''));
402
+ setTotalMessageLength(initialResolvedFallbackDisplay.length);
403
+ updateIsUnicodeAllowed(unicodeValidity);
404
+ updateLoading(false);
405
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- init only when semantic key changes; template props are read from ref
406
+ }, [isRcsSmsFallback, rcsFallbackTemplateInitKey, useRcsFallbackVarSegment]);
407
+
408
+ useEffect(() => {
409
+ if (isRcsSmsFallback) return;
410
+ traiDataRef.current = isFullMode ? templateDetails : templateData;
411
+ if (traiDataRef.current && !isEmpty(traiDataRef.current)) {
412
+ let msg = get(traiDataRef.current, `versions.base.sms-editor`, '');
153
413
  const templateMessageArray = [];
154
- //converting sms-editor string to an array split at '{#var#}'
155
- //split and push string before '{#var#}[0 to index]', push '{#var#}',
156
- //split and repeat for remaining string[index+7 to length]
157
414
  while (msg.length !== 0) {
158
415
  const index = msg.search(SMS_TRAI_VAR);
159
416
  if (index !== -1) {
@@ -167,30 +424,59 @@ export const SmsTraiEdit = (props) => {
167
424
  }
168
425
  const filteredTemplateMessageArray = templateMessageArray.filter((i) => i === 0 || i);
169
426
  updateTempMsgArray(filteredTemplateMessageArray);
170
- //stop spinner
171
427
  updateLoading(false);
172
428
  }
173
- }, [templateDetails || templateData]);
429
+ }, [templateDetails || templateData, isRcsSmsFallback, isFullMode]);
430
+
431
+ useEffect(() => {
432
+ if (!isRcsSmsFallback) return;
433
+ if (!useRcsFallbackVarSegment) {
434
+ const plainFallbackSmsText = fallbackText || '';
435
+ setUpdatedSmsEditor(plainFallbackSmsText.split(''));
436
+ setTotalMessageLength(plainFallbackSmsText.length);
437
+ return;
438
+ }
439
+ const resolvedFallbackDisplay = getFallbackResolvedContent(
440
+ fallbackText || '',
441
+ fallbackVarMappedData || {},
442
+ );
443
+ setUpdatedSmsEditor(resolvedFallbackDisplay.split(''));
444
+ setTotalMessageLength(resolvedFallbackDisplay.length);
445
+ }, [isRcsSmsFallback, useRcsFallbackVarSegment, fallbackText, fallbackVarMappedData]);
446
+
447
+ useEffect(() => {
448
+ if (!isRcsSmsFallback) return;
449
+ if (typeof onRcsFallbackEditorStateChange !== 'function') return;
450
+ onRcsFallbackEditorStateChange({
451
+ rcsSmsFallbackVarMapped: fallbackVarMappedData || {},
452
+ });
453
+ }, [isRcsSmsFallback, fallbackVarMappedData, onRcsFallbackEditorStateChange]);
454
+
455
+ useEffect(() => {
456
+ if (!isRcsSmsFallback) return;
457
+ if (typeof onRcsFallbackEditorStateChange !== 'function') return;
458
+ onRcsFallbackEditorStateChange({
459
+ unicodeValidity: isUnicodeAllowed,
460
+ });
461
+ }, [isRcsSmsFallback, isUnicodeAllowed, onRcsFallbackEditorStateChange]);
174
462
 
175
- //compute/get varMapped and updated-sms-editor
176
463
  useEffect(() => {
464
+ if (isRcsSmsFallback) return;
177
465
  if (tempMsgArray.length !== 0) {
178
- const traiBase = traiData?.versions?.base || {};
466
+ const traiBase = traiDataRef.current?.versions?.base || {};
179
467
  const {
180
468
  'var-mapped': varMapped = {},
181
469
  'updated-sms-editor': traiSmsEditor = '',
182
470
  'unicode-validity': unicodeValidity = true,
183
471
  } = traiBase;
184
- //if varMap and updated-sms-editor is already present on non first edits,use those values
185
472
  if (!isEmpty(varMapped)) {
186
- varMap = cloneDeep(varMapped);
473
+ varMapRef.current = cloneDeep(varMapped);
187
474
  if (isFullMode) {
188
475
  setUpdatedSmsEditor(traiSmsEditor);
189
476
  } else {
190
477
  computeUpdatedSmsEditor();
191
478
  }
192
479
  } else {
193
- //computing and setting varMap for first edit
194
480
  let varCount = 1;
195
481
  let horizontalSpaceCount = 0;
196
482
  for (let i = 0; i < tempMsgArray.length; i += 1) {
@@ -201,7 +487,7 @@ export const SmsTraiEdit = (props) => {
201
487
  horizontalSpaceCount += 1;
202
488
  }
203
489
  if (tempMsgArray[i] !== nextElem && nextElem?.replace(/[^\S\r\n]/gm, '') !== '') {
204
- varMap[`${tempMsgArray[i]}_${(i - varCount - horizontalSpaceCount) + 1}`] = {
490
+ varMapRef.current[`${tempMsgArray[i]}_${(i - varCount - horizontalSpaceCount) + 1}`] = {
205
491
  data: '',
206
492
  count: varCount,
207
493
  };
@@ -215,7 +501,6 @@ export const SmsTraiEdit = (props) => {
215
501
  setUpdatedSmsEditor(tempMsgArray);
216
502
  }
217
503
  updateIsUnicodeAllowed(unicodeValidity);
218
- // calcaulate message length here
219
504
  calculateTotalMessageLength();
220
505
  }
221
506
  }, [tempMsgArray]);
@@ -229,40 +514,84 @@ export const SmsTraiEdit = (props) => {
229
514
  }
230
515
  }, []);
231
516
 
232
- //performs tag validation
233
517
  useEffect(() => {
518
+ const runValidateTags = (content) =>
519
+ validateTags({
520
+ content,
521
+ tagsParam: tags,
522
+ location,
523
+ tagModule: getDefaultTags,
524
+ isFullMode,
525
+ }) || {};
526
+
527
+ if (isRcsSmsFallback) {
528
+ if (isFullMode) {
529
+ tagValidationResponseRef.current = {};
530
+ updateIsTagValidationError(false);
531
+ return;
532
+ }
533
+ // TRAI/DLT VarSegment: `validateIfTagClosed` only understands paired `{{…}}` and breaks on
534
+ // legitimate `{#…#}` / mixed TRAI shapes (extra `{`/`}` counts). Do not tie Done to it;
535
+ // slot completeness is enforced by `areAllRcsSmsFallbackVarSlotsFilled` on the RCS screen.
536
+ if (useRcsFallbackVarSegment) {
537
+ tagValidationResponseRef.current = {};
538
+ updateIsTagValidationError(false);
539
+ return;
540
+ }
541
+ const validationContent = fallbackText || '';
542
+ if (!validationContent.trim()) {
543
+ tagValidationResponseRef.current = {};
544
+ updateIsTagValidationError(false);
545
+ return;
546
+ }
547
+ tagValidationResponseRef.current = validateTags({
548
+ content: validationContent,
549
+ tagsParam: tags,
550
+ location,
551
+ tagModule: getDefaultTags,
552
+ isFullMode: true,
553
+ }) || {};
554
+ const braceErr = !!tagValidationResponseRef.current.isBraceError;
555
+ // Plain (non-VarSegment) fallback textarea: only unbalanced `{{…}}` disables Done.
556
+ updateIsTagValidationError(braceErr);
557
+ return;
558
+ }
234
559
  if (
235
560
  !isFullMode &&
236
561
  updatedSmsEditor?.length > 0 &&
237
562
  !updatedSmsEditor.includes(SMS_TRAI_VAR)
238
563
  ) {
239
- tagValidationResponse =
240
- validateTags({
241
- content: updatedSmsEditor.join(''),
242
- tagsParam: tags,
243
- injectedTagsParams: injectedTags,
244
- location,
245
- tagModule: getDefaultTags,
246
- eventContextTags,
247
- isFullMode,
248
- }) || {};
249
- updateIsTagValidationError(
250
- tagValidationResponse.unsupportedTags.length > 0,
251
- );
564
+ tagValidationResponseRef.current = runValidateTags(updatedSmsEditor.join(''));
565
+ const missing = (tagValidationResponseRef.current.missingTags || []).length > 0;
566
+ const braceErr = !!tagValidationResponseRef.current.isBraceError;
567
+ updateIsTagValidationError(missing || braceErr);
568
+ } else if (!isRcsSmsFallback) {
569
+ tagValidationResponseRef.current = {};
570
+ updateIsTagValidationError(false);
252
571
  }
253
- }, [updatedSmsEditor, tags]);
572
+ }, [
573
+ updatedSmsEditor,
574
+ tags,
575
+ isRcsSmsFallback,
576
+ isFullMode,
577
+ useRcsFallbackVarSegment,
578
+ fallbackText,
579
+ fallbackVarMappedData,
580
+ getDefaultTags,
581
+ location,
582
+ ]);
254
583
 
255
584
  const computeUpdatedSmsEditor = () => {
256
585
  const arr = [...tempMsgArray];
257
- const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
258
- for (const key in varMap) {
259
- if (varMap[key].data !== '') {
586
+ const varMapKeys = Object.keys(varMapRef.current)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
587
+ for (const key in varMapRef.current) {
588
+ if (varMapRef.current[key].data !== '') {
260
589
  const _id = Number(key.slice(8)); //Eg: -> extracting index 1 from keys like {#var# } _1
261
590
  const loopIndex =
262
591
  varMapKeys[varMapKeys?.indexOf(_id) + 1] || arr.length;
263
592
  for (let i = _id; i < loopIndex; i += 1) {
264
593
  if (i === _id) {
265
- arr[i] = varMap[key].data; //data for first #var# of the textbox
594
+ arr[i] = varMapRef.current[key].data; //data for first #var# of the textbox
266
595
  } else if (arr[i] === SMS_TRAI_VAR) {
267
596
  arr[i] = ''; //'' for remaining #var# of a textbox
268
597
  }
@@ -272,8 +601,8 @@ export const SmsTraiEdit = (props) => {
272
601
  setUpdatedSmsEditor(arr);
273
602
  };
274
603
 
275
- //Saving on done start
276
604
  const onUpdateTemplateComplete = (editResponse, errorMsg) => {
605
+ updateLoading(false);
277
606
  if (editResponse?.templateId) {
278
607
  CapNotification.success({
279
608
  message: formatMessage(messages.smsEditNotification),
@@ -288,7 +617,40 @@ export const SmsTraiEdit = (props) => {
288
617
  };
289
618
 
290
619
  const onSubmitWrapper = () => {
620
+ setIsLiquidValidationError(false);
621
+ setLiquidErrorMessages({});
622
+ setLiquidErrorPanelDismissed(false);
623
+ // Liquid validation (extractTags) only in library mode
624
+ if (isFullMode) {
625
+ onDoneCallback();
626
+ return;
627
+ }
291
628
  const content = updatedSmsEditor.join('');
629
+ const syncValidation = validateTags({
630
+ content,
631
+ tagsParam: tags,
632
+ location,
633
+ isFullMode,
634
+ });
635
+ if (!syncValidation?.valid) {
636
+ const standardErrors = [];
637
+ if (syncValidation?.isBraceError) {
638
+ standardErrors.push(formatMessage(globalMessages.unbalanacedCurlyBraces));
639
+ }
640
+ if (syncValidation?.missingTags?.length) {
641
+ standardErrors.push(
642
+ formatMessage(formBuilderMessages.missingTagsValidationError, {
643
+ missingTags: syncValidation.missingTags.join(', '),
644
+ })
645
+ );
646
+ }
647
+ setLiquidErrorMessages({
648
+ STANDARD_ERROR_MSG: standardErrors,
649
+ LIQUID_ERROR_MSG: [],
650
+ });
651
+ setIsLiquidValidationError(true);
652
+ return;
653
+ }
292
654
  const onError = ({ standardErrors, liquidErrors }) => {
293
655
  setLiquidErrorMessages({
294
656
  STANDARD_ERROR_MSG: standardErrors,
@@ -298,82 +660,142 @@ export const SmsTraiEdit = (props) => {
298
660
  };
299
661
 
300
662
  const onSuccess = () => {
663
+ setIsLiquidValidationError(false);
664
+ setLiquidErrorMessages({});
301
665
  onDoneCallback();
302
666
  };
667
+
668
+ // When liquid is enabled and isFullMode is true, skip liquid validation and proceed directly
669
+ if (isFullMode) {
670
+ onSuccess();
671
+ return;
672
+ }
673
+
303
674
  validateLiquidTemplateContent(content, {
304
675
  getLiquidTags,
305
676
  formatMessage: intl.formatMessage,
306
677
  messages: formBuilderMessages,
307
678
  onError,
308
679
  onSuccess,
309
- tagLookupMap: metaEntities?.tagLookupMap,
310
- eventContextTags,
311
- isLiquidFlow: true,
312
- forwardedTags: {},
313
680
  });
314
681
  };
315
682
 
316
683
  const onDoneCallback = () => {
317
- if (updatedSmsEditor.includes(SMS_TRAI_VAR) && !isFullMode) {
318
- //during save textbox should not be empty
684
+ // RCS fallback: allow Save when only "missing tags" ({{name}} may not be in TagList); still block unbalanced braces.
685
+ if (isTagValidationError) {
686
+ if (!isRcsSmsFallback) return;
687
+ const tagValidationSnapshot = tagValidationResponseRef.current ?? {};
688
+ if (tagValidationSnapshot.isBraceError) return;
689
+ }
690
+ const editorJoined = Array.isArray(updatedSmsEditor)
691
+ ? updatedSmsEditor.join('')
692
+ : String(updatedSmsEditor || '');
693
+ if (!isRcsSmsFallback && editorJoined.includes(SMS_TRAI_VAR) && !isFullMode) {
319
694
  updateIsValidationError(true);
320
695
  } else {
321
- //start spinner
322
- updateLoading(true);
323
- const traiVersions = traiData.versions || {};
324
- traiVersions.base = {
325
- ...traiVersions.base,
326
- 'var-mapped': varMap,
327
- 'updated-sms-editor': updatedSmsEditor,
328
- 'unicode-validity': isUnicodeAllowed,
329
- };
696
+ // Only show full-screen spinner when waiting on full-mode SMS edit API — not for RCS SMS fallback
697
+ // handoff (getFormSubscriptionData → SmsFallback closeSlidebox) and not for embedded sync handoff.
698
+ if (isFullMode && !isRcsSmsFallback) {
699
+ updateLoading(true);
700
+ }
701
+ if (!traiDataRef.current.versions) {
702
+ traiDataRef.current.versions = { base: {}, history: [] };
703
+ }
704
+ const traiVersions = traiDataRef.current.versions;
705
+ if (isRcsSmsFallback) {
706
+ const {
707
+ 'var-mapped': _ignoredVarMapped,
708
+ 'updated-sms-editor': _ignoredUpdatedSmsEditor,
709
+ 'rcs-sms-fallback-var-mapped': _ignoredFallbackVarMapped,
710
+ ...baseWithoutDerivedFields
711
+ } = traiVersions.base || {};
712
+ traiVersions.base = {
713
+ ...baseWithoutDerivedFields,
714
+ 'sms-editor': fallbackText || '',
715
+ 'unicode-validity': isUnicodeAllowed,
716
+ ...(useRcsFallbackVarSegment && {
717
+ 'rcs-sms-fallback-var-mapped': fallbackVarMappedData || {},
718
+ }),
719
+ };
720
+ } else {
721
+ traiVersions.base = {
722
+ ...traiVersions.base,
723
+ 'var-mapped': varMapRef.current,
724
+ 'updated-sms-editor': updatedSmsEditor,
725
+ 'unicode-validity': isUnicodeAllowed,
726
+ };
727
+ }
330
728
  traiVersions.history = [traiVersions.base];
331
- if (isFullMode) {
332
- actions.editTemplate(traiData, onUpdateTemplateComplete);
729
+ // RCS → SMS fallback (slidebox / inline): always hand off via getFormSubscriptionData so parent
730
+ // can persist fallback state and close the slide — never the standalone TRAI editTemplate API here.
731
+ if (isFullMode && !isRcsSmsFallback) {
732
+ actions.editTemplate(traiDataRef.current, onUpdateTemplateComplete);
333
733
  } else {
334
734
  getFormSubscriptionData({
335
- value: traiData.versions,
735
+ value: traiVersions,
736
+ // Consumers/tests read `versions.base` first (see getBaseFromSmsTraiFormData, rcsDltEditCompletionHandler).
737
+ versions: traiVersions,
336
738
  _id: params && params.id,
337
739
  validity: true,
338
740
  type: SMS,
339
741
  });
742
+ updateLoading(false);
340
743
  }
341
744
  }
342
745
  };
343
- //Saving on done end
344
746
 
345
- // tag code start
747
+ const locationQueryType = location?.query?.type;
748
+ const locationQueryModule = location?.query?.module;
749
+
346
750
  useEffect(() => {
347
- let tag =
348
- metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
349
- const { type, module } = location.query || {};
350
- if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
751
+ let tag = getStandardTagsFromMeta(metaEntities);
752
+ if ((!Array.isArray(tag) || tag.length === 0) && Array.isArray(supportedTags) && supportedTags.length > 0) {
351
753
  tag = supportedTags;
352
754
  }
353
- updateTags(tag);
354
- }, [metaEntities]);
755
+ if (
756
+ locationQueryType === EMBEDDED &&
757
+ locationQueryModule === LIBRARY &&
758
+ !getDefaultTags
759
+ ) {
760
+ tag = supportedTags || [];
761
+ }
762
+ updateTags(Array.isArray(tag) ? tag : []);
763
+ }, [metaEntities, getDefaultTags, supportedTags, locationQueryType, locationQueryModule]);
355
764
 
356
765
  const handleOnTagsContextChange = (data) => {
766
+ // CapTagList passes "Outbound" | "Loyalty" from the module filter; TagList may pass "Outbound" on init.
767
+ if (data == null || data === '') return;
768
+ const normalizedContext = String(data).toLowerCase();
357
769
  const { type } = location.query || {};
358
770
  const isEmbedded = type === EMBEDDED;
359
771
  const query = {
360
772
  layout: SMS,
361
773
  type: TAG,
362
- context:
363
- (data || '').toLowerCase() === ALL
364
- ? DEFAULT
365
- : (data || '').toLowerCase(),
366
- embedded: isEmbedded ? type : FULL,
774
+ context: normalizedContext === ALL ? DEFAULT : normalizedContext,
775
+ embedded: forceFullTagContext ? FULL : (isEmbedded ? type : FULL),
367
776
  };
368
- globalActions.fetchSchemaForEntity(query);
777
+ fetchTagSchemaIfNewQuery(query);
369
778
  };
370
779
 
371
780
  const onTagSelect = (data) => {
372
- if (textAreaId >= 0 && varMap && updatedSmsEditor) {
781
+ if (isRcsSmsFallback) {
782
+ if (!useRcsFallbackVarSegment) {
783
+ setFallbackText((prev) => `${prev || ''}{{${data}}}`);
784
+ return;
785
+ }
786
+ if (!fallbackFocusedId) return;
787
+ const prevVal = fallbackVarMappedData?.[fallbackFocusedId] ?? '';
788
+ const nextVal = `${prevVal}{{${data}}}`;
789
+ setFallbackVarMappedData((prev) => ({
790
+ ...(prev || {}),
791
+ [fallbackFocusedId]: nextVal,
792
+ }));
793
+ return;
794
+ }
795
+ if (textAreaId >= 0 && varMapRef.current && updatedSmsEditor) {
373
796
  const arr = [...updatedSmsEditor];
374
- const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
797
+ const varMapKeys = Object.keys(varMapRef.current)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
375
798
  const loopIndex = varMapKeys[varMapKeys?.indexOf(textAreaId) + 1] || arr.length;
376
- //when trying to insert tag in empty textarea,{#var#} is replaced with "" and then tag is added
377
799
  for (let i = textAreaId; i < loopIndex; i += 1) {
378
800
  if (arr[i] === SMS_TRAI_VAR) {
379
801
  arr[i] = '';
@@ -381,26 +803,23 @@ export const SmsTraiEdit = (props) => {
381
803
  }
382
804
  const messageData = `${arr[textAreaId]}{{${data}}}`;
383
805
  arr[textAreaId] = messageData;
384
- varMap[`${SMS_TRAI_VAR}_${textAreaId}`].data = messageData;
806
+ varMapRef.current[`${SMS_TRAI_VAR}_${textAreaId}`].data = messageData;
385
807
  setUpdatedSmsEditor(arr);
386
808
  }
387
809
  };
388
- //setting the id of currently selected text area, is used onTagSelect
810
+
389
811
  const setTextAreaId = (event) => {
390
812
  updateTextAreaId(Number(event.target.id));
391
813
  };
392
- // tag code end
393
814
 
394
- // on change event of Text Area
395
815
  const textAreaValueChange = ({ target: { value, id } }) => {
816
+ if (isRcsSmsFallback) return;
396
817
  const _id = Number(id);
397
818
  const arr = [...updatedSmsEditor];
398
- const varMapKeys = Object.keys(varMap)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
819
+ const varMapKeys = Object.keys(varMapRef.current)?.map((key) => Number(key.slice(8)))?.sort((a, b) => a - b) || [];
399
820
  const loopIndex = varMapKeys[varMapKeys?.indexOf(_id) + 1] || arr.length;
400
821
 
401
- //assign entered value to varMap
402
- varMap[`${SMS_TRAI_VAR}_${_id}`].data = value;
403
- //based on entered value update updatedSmsEditor
822
+ varMapRef.current[`${SMS_TRAI_VAR}_${_id}`].data = value;
404
823
  if (value === '') {
405
824
  for (let i = _id; i < loopIndex; i += 1) {
406
825
  if (i === _id || arr[i] === '') {
@@ -410,7 +829,7 @@ export const SmsTraiEdit = (props) => {
410
829
  } else {
411
830
  for (let i = _id; i < loopIndex; i += 1) {
412
831
  if (i === _id) {
413
- arr[i] = varMap[`${SMS_TRAI_VAR}_${_id}`].data;
832
+ arr[i] = varMapRef.current[`${SMS_TRAI_VAR}_${_id}`].data;
414
833
  } else if (arr[i] === SMS_TRAI_VAR) {
415
834
  arr[i] = '';
416
835
  }
@@ -513,18 +932,33 @@ export const SmsTraiEdit = (props) => {
513
932
  };
514
933
 
515
934
  const smsLengthForVar = () => (
516
- <CapHeading type="h5" style={{ marginTop: CAP_SPACE_04, float: 'right' }}>
517
- {formatMessage(messages.totalCharacters, {
518
- smsCount: Math.ceil(totalMessageLength / 160),
519
- number: totalMessageLength,
520
- })}
935
+ <CapHeading
936
+ type="h5"
937
+ className={isRcsSmsFallback ? 'rcs-character-count rcs-character-count--compact' : ''}
938
+ style={isRcsSmsFallback ? {} : { marginTop: CAP_SPACE_04, marginBottom: 0 }}
939
+ >
940
+ {isRcsSmsFallback
941
+ ? formatMessage(messages.charactersCountLabel, {
942
+ current: totalMessageLength,
943
+ max: SMS_TRAI_CONTENT_MAX_LENGTH,
944
+ })
945
+ : formatMessage(messages.totalCharacters, {
946
+ smsCount: Math.ceil(totalMessageLength / 160),
947
+ number: totalMessageLength,
948
+ })}
521
949
  </CapHeading>
522
950
  );
523
951
 
524
- // to compute the length of the message
525
- //40 characters is blocked per'{#var#}' if textbox is empty otherwise it will be textbox length
526
- // and the remaining string length is added to it
527
952
  const calculateTotalMessageLength = () => {
953
+ if (isRcsSmsFallback) {
954
+ if (useRcsFallbackVarSegment) {
955
+ const resolved = getFallbackResolvedContent(fallbackText || '', fallbackVarMappedData || {});
956
+ setTotalMessageLength(resolved.length);
957
+ } else {
958
+ setTotalMessageLength((fallbackText || '').length);
959
+ }
960
+ return;
961
+ }
528
962
  const msgLenWithoutVar = tempMsgArray
529
963
  ?.filter((i) => i !== SMS_TRAI_VAR)
530
964
  .join('');
@@ -535,33 +969,43 @@ export const SmsTraiEdit = (props) => {
535
969
 
536
970
  const calculateLenForTextBox = () => {
537
971
  let countVarChar = 0;
538
- Object.keys(varMap).forEach((i) => {
539
- if (varMap[i].data) {
540
- countVarChar += varMap[i].data?.length;
541
- if (!showMsgLengthNote && varMap[i].data?.length > varMap[i].count * CHARLIMIT) {
972
+ Object.keys(varMapRef.current).forEach((i) => {
973
+ if (varMapRef.current[i].data) {
974
+ countVarChar += varMapRef.current[i].data?.length;
975
+ if (!showMsgLengthNote && varMapRef.current[i].data?.length > varMapRef.current[i].count * CHARLIMIT) {
542
976
  updateshowMsgLengthNote(true);
543
977
  }
544
978
  } else {
545
- countVarChar += varMap[i].count * CHARLIMIT;
979
+ countVarChar += varMapRef.current[i].count * CHARLIMIT;
546
980
  }
547
981
  });
548
982
  return countVarChar;
549
983
  };
550
984
 
551
985
  const tagValidationErrorMessage = () => {
552
- const { unsupportedTags = [] } = tagValidationResponse;
553
- let tagError = '';
554
- if (unsupportedTags.length > 0) {
555
- tagError = formatMessage(messages.unsupportedTagsValidationError, {
556
- unsupportedTags,
557
- });
986
+ const { missingTags = [], unsupportedTags = [], isBraceError } = tagValidationResponseRef.current || {};
987
+ const listForMessage = (unsupportedTags && unsupportedTags.length > 0)
988
+ ? unsupportedTags
989
+ : missingTags;
990
+ if (isBraceError) {
991
+ return <CapError>{formatMessage(globalMessages.unbalanacedCurlyBraces)}</CapError>;
992
+ }
993
+ if (listForMessage.length > 0) {
994
+ return (
995
+ <CapError>
996
+ {formatMessage(messages.unsupportedTagsValidationError, {
997
+ unsupportedTags: listForMessage,
998
+ })}
999
+ </CapError>
1000
+ );
558
1001
  }
559
- return <CapError>{tagError}</CapError>;
1002
+ return null;
560
1003
  };
561
1004
 
562
1005
  const disablehandler = () => {
563
- if (traiData && !isEmpty(traiData)) {
564
- const msg = get(traiData, `versions.base.sms-editor`, '');
1006
+ if (isRcsSmsFallback) return false;
1007
+ if (traiDataRef.current && !isEmpty(traiDataRef.current)) {
1008
+ const msg = get(traiDataRef.current, `versions.base.sms-editor`, '');
565
1009
  const index = msg.search(SMS_TRAI_VAR);
566
1010
  if (index === -1) {
567
1011
  return true;
@@ -575,7 +1019,6 @@ export const SmsTraiEdit = (props) => {
575
1019
  updateIsUnicodeAllowed(checked);
576
1020
  };
577
1021
 
578
- // Get template content for test and preview
579
1022
  const getTemplateContent = () => {
580
1023
  if (!updatedSmsEditor || updatedSmsEditor.length === 0) {
581
1024
  return '';
@@ -583,39 +1026,69 @@ export const SmsTraiEdit = (props) => {
583
1026
  return updatedSmsEditor.join('');
584
1027
  };
585
1028
 
586
- // Build formData for TestAndPreviewSlidebox - templateConfigs with templateId and template for DLT test
587
1029
  const getFormDataForTestAndPreview = () => {
588
- const smsBase = get(traiData, 'versions.base') || get(templateDetails, 'versions.base') || get(templateData, 'versions.base');
1030
+ const smsBase = get(traiDataRef.current, 'versions.base') || get(templateDetails, 'versions.base') || get(templateData, 'versions.base');
589
1031
  if (!smsBase || !smsBase.template_id) {
590
1032
  return {};
591
1033
  }
592
1034
  const templateRaw = smsBase['updated-sms-editor'] || smsBase['sms-editor'] || '';
593
1035
  const template = Array.isArray(templateRaw) ? templateRaw.join('') : templateRaw;
594
- return { templateConfigs: { templateId: smsBase.template_id, template } };
1036
+ const traiDlt = isTraiDLTEnable(isFullMode, smsRegister);
1037
+ const headerIds = get(traiDataRef.current, 'versions.base.header', []) || [];
1038
+ return {
1039
+ templateConfigs: {
1040
+ templateId: smsBase.template_id,
1041
+ template,
1042
+ traiDltEnabled: traiDlt,
1043
+ registeredSenderIds: traiDlt ? headerIds : [],
1044
+ },
1045
+ };
595
1046
  };
596
1047
 
597
- // Handle test and preview button click
598
1048
  const handleTestAndPreview = () => {
599
1049
  setShowTestAndPreviewSlidebox(true);
600
1050
  };
601
1051
 
602
- // Handle close test and preview slidebox
603
1052
  const handleCloseTestAndPreview = () => {
604
1053
  setShowTestAndPreviewSlidebox(false);
605
1054
  };
606
1055
 
607
- const isLiquidSupportFeatureEnabled = hasLiquidSupportFeature();
1056
+ const shouldShowPreview =
1057
+ !isRcsSmsFallback || (showPreviewInRcsFallback && !hidePreview);
1058
+ const smsSidePreviewColumn = (
1059
+ <CapColumn span={8} offset={1}>
1060
+ <UnifiedPreview
1061
+ channel={SMS}
1062
+ content={updatedSmsEditor.join('')}
1063
+ device={ANDROID}
1064
+ showDeviceToggle={false}
1065
+ showHeader={false}
1066
+ formatMessage={formatMessage}
1067
+ senderId={isUnicodeAllowed ? 'Unicode' : 'ASCII'}
1068
+ />
1069
+ </CapColumn>
1070
+ );
1071
+
608
1072
  return (
609
1073
  <>
610
- <CapSpin spinning={loading || fetchingLiquidTags} tip={fetchingLiquidTags && formatMessage(formBuilderMessages.liquidSpinText)}>
1074
+ <CapSpin
1075
+ spinning={loading || fetchingLiquidTags}
1076
+ tip={fetchingLiquidTags && formatMessage(formBuilderMessages.liquidSpinText)}
1077
+ className={[
1078
+ isRcsSmsFallback && 'sms-trai-edit-rcs-fallback',
1079
+ isOverview && 'sms-trai-edit--overview',
1080
+ ]
1081
+ .filter(Boolean)
1082
+ .join(' ') || undefined}
1083
+ >
611
1084
  <CapRow>
612
- {traiData && !isEmpty(traiData) && (
1085
+ {traiDataRef.current && !isEmpty(traiDataRef.current) && !isRcsSmsFallback && (
613
1086
  <TraiEditTemplateDetails>
614
1087
  <CapLabelInline type="label1">
615
1088
  {formatMessage(messages.templateLabel)}
616
1089
  </CapLabelInline>
617
1090
  <CapLabelInline type="label2">
618
- {get(traiData, `versions.base.template_name`, '')}
1091
+ {get(traiDataRef.current, `versions.base.template_name`, '')}
619
1092
  </CapLabelInline>
620
1093
 
621
1094
  <CapLabelInline type="label1">
@@ -626,15 +1099,15 @@ export const SmsTraiEdit = (props) => {
626
1099
  {formatMessage(messages.senderIdlabel)}
627
1100
  </CapLabelInline>
628
1101
  <CapLabelInline type="label2">
629
- {[...get(traiData, `versions.base.header`, [])].join(', ')}
1102
+ {[...get(traiDataRef.current, `versions.base.header`, [])].join(', ')}
630
1103
  </CapLabelInline>
631
1104
  </TraiEditTemplateDetails>
632
1105
  )}
633
- <CapColumn span={14}>
1106
+ <CapColumn span={shouldShowPreview ? 14 : 24}>
634
1107
  <CapRow>
635
1108
  <CapHeader
636
1109
  title={formatMessage(messages.traiEditTitle)}
637
- size="regular"
1110
+ size={isRcsSmsFallback ? 'label1' : 'regular'}
638
1111
  suffix={(
639
1112
  <TagList
640
1113
  label={formatMessage(messages.addLabels)}
@@ -643,47 +1116,72 @@ export const SmsTraiEdit = (props) => {
643
1116
  tags={tags || []}
644
1117
  onContextChange={handleOnTagsContextChange}
645
1118
  injectedTags={injectedTags || {}}
646
- hidePopover={disablehandler()}
1119
+ channel={SMS}
1120
+ hidePopover={false}
1121
+ disabled={!isRcsSmsFallback && disablehandler()}
647
1122
  selectedOfferDetails={selectedOfferDetails}
648
1123
  eventContextTags={eventContextTags}
1124
+ waitEventContextTags={waitEventContextTags}
1125
+ popoverOverlayStyle={isRcsSmsFallback ? { zIndex: 10020 } : undefined}
1126
+ popoverOverlayClassName={isRcsSmsFallback ? 'sms-fallback-taglist-popover rcs-sms-fallback-taglist-popover' : undefined}
649
1127
  />
650
1128
  )}
651
1129
  />
652
1130
  </CapRow>
653
1131
 
654
- <CapRow
655
- style={{
656
- backgroundColor: CAP_G10,
657
- padding: CAP_SPACE_16,
658
- }}
1132
+ {isRcsSmsFallback ? (
1133
+ <>
1134
+ {renderRcsFallbackMessage(fallbackText)}
1135
+ </>
1136
+ ) : (
1137
+ <>
1138
+ <CapRow className="sms-trai-editor-segment-row">
1139
+ <div className="sms-trai-segmented-editor">
1140
+ {renderedContent()}
1141
+ </div>
1142
+ </CapRow>
1143
+ <CapRow className="sms-trai-length-row">
1144
+ {smsLengthForVar()}
1145
+ </CapRow>
1146
+ </>
1147
+ )}
1148
+ {isRcsSmsFallback && isTagValidationError && (
1149
+ <CapRow>
1150
+ {tagValidationErrorMessage()}
1151
+ </CapRow>
1152
+ )}
1153
+ {!isRcsSmsFallback && isTagValidationError && tagValidationErrorMessage()}
1154
+ <CapCheckbox
1155
+ onChange={unicodeHandler}
1156
+ checked={isUnicodeAllowed}
1157
+ disabled={
1158
+ isRcsSmsFallback
1159
+ ? isRcsEditFlow
1160
+ : disablehandler()
1161
+ }
659
1162
  >
660
- {renderedContent()}
661
- </CapRow>
662
- <CapRow>
663
- {smsLengthForVar()}
664
- </CapRow>
665
- {isTagValidationError && tagValidationErrorMessage()}
666
- <CapCheckbox onChange={unicodeHandler} checked={isUnicodeAllowed} disabled={disablehandler()}>
667
1163
  {formatMessage(messages.unicodeLabel)}
668
1164
  </CapCheckbox>
669
1165
  {showMsgLengthNote && <CapInfoNote message={<FormattedMessage {...messages.msgLengthNote} values={{ var: '{#var#}' }} />} />}
670
- <div style={{ marginBottom: '100px' }} />
671
- </CapColumn>
672
- <CapColumn span={8} offset={1}>
673
- <UnifiedPreview
674
- channel={SMS}
675
- content={updatedSmsEditor.join('')}
676
- device={ANDROID}
677
- showDeviceToggle={false}
678
- showHeader={false}
679
- formatMessage={formatMessage}
680
- senderId={isUnicodeAllowed ? 'Unicode' : 'ASCII'}
681
- />
1166
+ <div className="sms-trai-edit-bottom-spacer" />
682
1167
  </CapColumn>
1168
+ {shouldShowPreview && smsSidePreviewColumn}
683
1169
  </CapRow>
684
1170
  <SMSTraiFooter>
685
- {isLiquidValidationError && (
686
- <ErrorInfoNote errorMessages={liquidErrorMessages} />
1171
+ {isLiquidValidationError && !liquidErrorPanelDismissed && (
1172
+ <ErrorInfoNote
1173
+ errorMessages={liquidErrorMessages}
1174
+ intl={intl}
1175
+ onClose={() => {
1176
+ setLiquidErrorPanelDismissed(true);
1177
+ if (typeof showLiquidErrorInFooter === 'function') {
1178
+ showLiquidErrorInFooter({
1179
+ STANDARD_ERROR_MSG: [],
1180
+ LIQUID_ERROR_MSG: [],
1181
+ });
1182
+ }
1183
+ }}
1184
+ />
687
1185
  )}
688
1186
  <CapButton
689
1187
  onClick={handleTestAndPreview}
@@ -693,9 +1191,9 @@ export const SmsTraiEdit = (props) => {
693
1191
  <FormattedMessage {...messages.testAndPreviewButtonLabel} />
694
1192
  </CapButton>
695
1193
  <CapButton
696
- onClick={isLiquidSupportFeatureEnabled ? onSubmitWrapper : onDoneCallback}
1194
+ onClick={onDoneCallback}
697
1195
  className="create-msg"
698
- disabled={isTagValidationError || (isLiquidSupportFeatureEnabled && !isObject(metaEntities?.tagLookupMap))}
1196
+ disabled={isTagValidationError}
699
1197
  >
700
1198
  <FormattedMessage {...messages.saveButtonLabel} />
701
1199
  </CapButton>
@@ -712,6 +1210,15 @@ export const SmsTraiEdit = (props) => {
712
1210
  );
713
1211
  };
714
1212
 
1213
+ SmsTraiEdit.propTypes = {
1214
+ isRcsSmsFallback: PropTypes.bool,
1215
+ isRcsEditFlow: PropTypes.bool,
1216
+ showPreviewInRcsFallback: PropTypes.bool,
1217
+ hidePreview: PropTypes.bool,
1218
+ isOverview: PropTypes.bool,
1219
+ onRcsFallbackEditorStateChange: PropTypes.func,
1220
+ };
1221
+
715
1222
  const mapStateToProps = createStructuredSelector({
716
1223
  templateDetails: makeSelectTemplateDetailsResponse(),
717
1224
  metaEntities: makeSelectMetaEntities(),