@capillarytech/creatives-library 8.0.263 → 8.0.265

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 (280) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +1 -3
  4. package/initialReducer.js +0 -2
  5. package/package.json +1 -1
  6. package/services/api.js +0 -15
  7. package/services/tests/api.test.js +0 -34
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
  9. package/tests/integration/TemplateCreation/api-response.js +1 -31
  10. package/tests/integration/TemplateCreation/msw-handler.js +0 -2
  11. package/utils/common.js +0 -11
  12. package/utils/commonUtils.js +5 -28
  13. package/utils/tests/commonUtil.test.js +0 -224
  14. package/utils/tests/transformerUtils.test.js +0 -297
  15. package/utils/transformTemplateConfig.js +10 -0
  16. package/utils/transformerUtils.js +0 -40
  17. package/v2Components/CapDeviceContent/index.js +56 -61
  18. package/v2Components/CapImageUpload/constants.js +0 -2
  19. package/v2Components/CapImageUpload/index.js +16 -65
  20. package/v2Components/CapImageUpload/index.scss +1 -4
  21. package/v2Components/CapImageUpload/messages.js +1 -5
  22. package/v2Components/CapTagList/index.js +1 -6
  23. package/v2Components/CapTagListWithInput/index.js +1 -5
  24. package/v2Components/CapTagListWithInput/messages.js +1 -1
  25. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  26. package/v2Components/ErrorInfoNote/index.js +72 -402
  27. package/v2Components/ErrorInfoNote/messages.js +6 -32
  28. package/v2Components/ErrorInfoNote/style.scss +6 -278
  29. package/v2Components/FormBuilder/tests/index.test.js +4 -13
  30. package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
  31. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
  32. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
  33. package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
  34. package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
  35. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
  36. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
  37. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  38. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  39. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
  40. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
  41. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
  45. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  46. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
  47. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +10 -7
  48. package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
  49. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  50. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  51. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
  52. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
  53. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
  54. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
  55. package/v2Components/HtmlEditor/constants.js +20 -45
  56. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  57. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
  58. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  59. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  60. package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
  61. package/v2Components/HtmlEditor/index.js +1 -1
  62. package/v2Components/HtmlEditor/messages.js +94 -102
  63. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
  64. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
  65. package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
  66. package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
  67. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
  68. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  69. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
  70. package/v2Components/MobilePushPreviewV2/index.js +7 -33
  71. package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
  72. package/v2Components/TemplatePreview/index.js +32 -47
  73. package/v2Components/TemplatePreview/messages.js +0 -4
  74. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
  75. package/v2Containers/App/constants.js +0 -5
  76. package/v2Containers/BeeEditor/index.js +90 -172
  77. package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
  78. package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
  79. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
  80. package/v2Containers/CreativesContainer/constants.js +0 -4
  81. package/v2Containers/CreativesContainer/index.js +46 -408
  82. package/v2Containers/CreativesContainer/messages.js +0 -12
  83. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
  84. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
  85. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
  86. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
  87. package/v2Containers/Email/actions.js +0 -7
  88. package/v2Containers/Email/constants.js +1 -5
  89. package/v2Containers/Email/index.js +36 -237
  90. package/v2Containers/Email/messages.js +0 -32
  91. package/v2Containers/Email/reducer.js +1 -12
  92. package/v2Containers/Email/sagas.js +7 -61
  93. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  94. package/v2Containers/Email/tests/reducer.test.js +0 -46
  95. package/v2Containers/Email/tests/sagas.test.js +29 -320
  96. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
  97. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  98. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  99. package/v2Containers/EmailWrapper/constants.js +0 -2
  100. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
  101. package/v2Containers/EmailWrapper/index.js +23 -103
  102. package/v2Containers/EmailWrapper/messages.js +1 -65
  103. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
  104. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
  105. package/v2Containers/InApp/actions.js +0 -7
  106. package/v2Containers/InApp/constants.js +4 -20
  107. package/v2Containers/InApp/index.js +359 -802
  108. package/v2Containers/InApp/index.scss +3 -4
  109. package/v2Containers/InApp/messages.js +3 -7
  110. package/v2Containers/InApp/reducer.js +3 -21
  111. package/v2Containers/InApp/sagas.js +9 -29
  112. package/v2Containers/InApp/selectors.js +5 -25
  113. package/v2Containers/InApp/tests/index.test.js +50 -154
  114. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  115. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  116. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
  117. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
  118. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
  119. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
  120. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
  121. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
  122. package/v2Containers/TagList/index.js +19 -62
  123. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  124. package/v2Containers/Templates/_templates.scss +1 -265
  125. package/v2Containers/Templates/actions.js +1 -2
  126. package/v2Containers/Templates/constants.js +0 -1
  127. package/v2Containers/Templates/index.js +38 -363
  128. package/v2Containers/Templates/messages.js +0 -28
  129. package/v2Containers/Templates/reducer.js +0 -2
  130. package/v2Containers/Templates/tests/index.test.js +0 -10
  131. package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
  132. package/v2Containers/TemplatesV2/index.js +7 -15
  133. package/v2Containers/TemplatesV2/messages.js +0 -4
  134. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
  135. package/utils/imageUrlUpload.js +0 -141
  136. package/v2Components/CapImageUrlUpload/constants.js +0 -26
  137. package/v2Components/CapImageUrlUpload/index.js +0 -365
  138. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  139. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  140. package/v2Components/ErrorInfoNote/constants.js +0 -1
  141. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
  142. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
  143. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
  144. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
  145. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  146. package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
  147. package/v2Components/MobilePushPreviewV2/constants.js +0 -6
  148. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
  149. package/v2Containers/BeePopupEditor/constants.js +0 -10
  150. package/v2Containers/BeePopupEditor/index.js +0 -194
  151. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  152. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
  153. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
  154. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
  155. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
  156. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  157. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  158. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  159. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
  160. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  161. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
  162. package/v2Containers/InAppWrapper/constants.js +0 -16
  163. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  164. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  165. package/v2Containers/InAppWrapper/index.js +0 -148
  166. package/v2Containers/InAppWrapper/messages.js +0 -49
  167. package/v2Containers/InappAdvance/index.js +0 -1099
  168. package/v2Containers/InappAdvance/index.scss +0 -10
  169. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
  170. package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
  171. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
  172. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  173. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
  174. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
  175. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
  176. package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
  177. package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
  178. package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
  179. package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
  180. package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
  181. package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
  182. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
  183. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
  184. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
  185. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
  186. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
  187. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
  188. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
  189. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
  190. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
  191. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
  192. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  193. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  194. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  195. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  196. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  197. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  198. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
  199. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
  200. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
  201. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
  202. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
  203. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
  204. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
  205. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
  206. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
  207. package/v2Containers/WebPush/Create/index.js +0 -1148
  208. package/v2Containers/WebPush/Create/index.scss +0 -134
  209. package/v2Containers/WebPush/Create/messages.js +0 -211
  210. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
  211. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
  212. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
  213. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
  214. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
  215. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
  216. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  217. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  218. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
  219. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
  220. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  221. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  222. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  223. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  224. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
  225. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
  226. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  227. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  228. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
  229. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
  230. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
  231. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
  232. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  233. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
  234. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
  235. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
  236. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
  237. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
  238. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
  239. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
  240. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
  241. package/v2Containers/WebPush/Create/preview/constants.js +0 -637
  242. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
  243. package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
  244. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
  245. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  246. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  247. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  248. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
  249. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  250. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  251. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  252. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
  253. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
  254. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  255. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
  256. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
  257. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  258. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
  259. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  260. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
  261. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
  262. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
  263. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  264. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
  265. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
  266. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
  267. package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
  268. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
  269. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  270. package/v2Containers/WebPush/Create/utils/validation.js +0 -75
  271. package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
  272. package/v2Containers/WebPush/actions.js +0 -60
  273. package/v2Containers/WebPush/constants.js +0 -132
  274. package/v2Containers/WebPush/index.js +0 -2
  275. package/v2Containers/WebPush/reducer.js +0 -104
  276. package/v2Containers/WebPush/sagas.js +0 -119
  277. package/v2Containers/WebPush/selectors.js +0 -65
  278. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  279. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  280. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -1,1246 +0,0 @@
1
- import React, {
2
- useState, useEffect, useMemo, useCallback, useRef, useImperativeHandle, forwardRef,
3
- } from 'react';
4
- import PropTypes from 'prop-types';
5
- import { injectIntl, FormattedMessage } from 'react-intl';
6
- import isEmpty from 'lodash/isEmpty';
7
- import get from 'lodash/get';
8
- import _ from 'lodash';
9
- import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
10
- import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
11
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
12
- import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
13
- import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
14
- import HTMLEditor from '../../../v2Components/HtmlEditor';
15
- import CapTagListWithInput from '../../../v2Components/CapTagListWithInput';
16
- import formBuilderMessages from '../../../v2Components/FormBuilder/messages';
17
- import { validateLiquidTemplateContent } from '../../../utils/commonUtils';
18
- import { hasLiquidSupportFeature, isEmailUnsubscribeTagMandatory } from '../../../utils/common';
19
- import history from '../../../utils/history';
20
- import messages from '../messages';
21
- import emailMessages from '../../Email/messages';
22
- import { validateTags } from '../../../utils/tagValidations';
23
- import {
24
- TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
25
- } from '../../Whatsapp/constants';
26
- import { EMAIL } from '../../CreativesContainer/constants';
27
- import { OUTBOUND } from '../../../v2Components/FormBuilder/constants';
28
-
29
- /**
30
- * EmailHTMLEditor Component
31
- *
32
- * IMPORTANT: This component is ONLY used when supportCKEditor flag is FALSE (new flow).
33
- * When supportCKEditor is TRUE, the existing Email component with FormBuilder is used (legacy flow).
34
- *
35
- * A completely self-contained component for Email HTML Editor that handles:
36
- * - Tag loading and management
37
- * - Tag validation
38
- * - Content editing with HTMLEditor
39
- * - Template data extraction for edit mode
40
- * - Save logic (full mode & library mode) with liquid validation
41
- * - API calls via Email actions/sagas
42
- *
43
- * This component is independent and reusable, similar to Whatsapp and InApp channels.
44
- */
45
- const EmailHTMLEditor = (props) => {
46
- const {
47
- intl,
48
- location,
49
- params,
50
- getDefaultTags,
51
- supportedTags,
52
- metaEntities,
53
- injectedTags,
54
- globalActions,
55
- loadingTags,
56
- eventContextTags,
57
- forwardedTags,
58
- selectedOfferDetails,
59
- currentOrgDetails,
60
- isReadOnly = false,
61
- fetchingLiquidTags = false,
62
- createTemplateInProgress = false,
63
- fetchingCmsData = false,
64
- // Email Redux state
65
- Email,
66
- // Email actions for API calls
67
- emailActions,
68
- // Full mode props
69
- isFullMode,
70
- templateName,
71
- showTemplateName,
72
- isGetFormData,
73
- getFormdata,
74
- // Library mode props
75
- templateData: templateDataProp,
76
- // Uploaded content from zip file
77
- EmailLayout,
78
- // Liquid validation
79
- getLiquidTags,
80
- showLiquidErrorInFooter,
81
- onValidationFail,
82
- // Preview/Test
83
- // Parent loading control
84
- setIsLoadingContent,
85
- forwardedRef,
86
- // Module type for unsubscribe tag validation
87
- moduleType,
88
- // Validation state callback
89
- onHtmlEditorValidationStateChange,
90
- } = props;
91
-
92
- const { formatMessage } = intl;
93
-
94
- // State for content and subject
95
- const [htmlContent, setHtmlContent] = useState('');
96
- const [loadedHtmlContent, setLoadedHtmlContent] = useState(''); // Stable content for HTMLEditor initialization
97
- const [subject, setSubject] = useState('');
98
- const [subjectError, setSubjectError] = useState('');
99
- // State for template name (extracted from template data in Edit mode)
100
- const [extractedTemplateName, setExtractedTemplateName] = useState('');
101
- const [tagValidationError, setTagValidationError] = useState(null);
102
- const [isLoading, setIsLoading] = useState(true);
103
- const [errorsAcknowledged, setErrorsAcknowledged] = useState(false); // Track if user has acknowledged errors by clicking redirection icon
104
- // State for API validation errors (from validateLiquidTemplateContent)
105
- const [apiValidationErrors, setApiValidationErrors] = useState({
106
- liquidErrors: [],
107
- standardErrors: [],
108
- });
109
-
110
- // Refs for tracking initialization and previous values
111
- const contentInitializedRef = useRef(false);
112
- const subjectInitializedRef = useRef(false);
113
- const lastTemplateIdRef = useRef(null);
114
- const fetchingTemplateIdRef = useRef(null); // Track which template we're currently fetching
115
- const prevIsGetFormDataRef = useRef(false);
116
- const prevErrorCountRef = useRef(0); // Track previous error count to detect new errors
117
- const handleSaveRef = useRef(null);
118
-
119
- // Ref to HTMLEditor to access validation state
120
- const htmlEditorRef = useRef(null);
121
-
122
- // Compute tags directly from metaEntities (same approach as Email component)
123
- // Use deep equality check to prevent TagList from re-processing when tags haven't actually changed
124
- // This fixes the slow expansion and React Intl errors when clicking nested tags
125
- const tags = useMemo(() => {
126
- let tagList = get(metaEntities, 'tags.standard', []);
127
- const { type, module } = location?.query || {};
128
- if (type === EMBEDDED && module === LIBRARY && !getDefaultTags) {
129
- tagList = supportedTags || [];
130
- }
131
- return tagList;
132
- }, [metaEntities, supportedTags, location, getDefaultTags]);
133
-
134
- // Expose method to get formData for TestAndPreviewSlidebox
135
- useImperativeHandle(forwardedRef, () => ({
136
- getFormDataForPreview: () => {
137
- const baseLanguage = get(currentOrgDetails, 'basic_details.base_language', 'en');
138
- return {
139
- "0": {
140
- [baseLanguage]: {
141
- 'template-content': htmlContent || '',
142
- 'is_drag_drop': false,
143
- },
144
- activeTab: baseLanguage,
145
- selectedLanguages: [baseLanguage],
146
- base: true,
147
- },
148
- 'template-subject': subject || '',
149
- };
150
- },
151
- getContentForPreview: () => htmlContent || '',
152
- getValidationState: () => {
153
- if (htmlEditorRef.current && htmlEditorRef.current.getValidation) {
154
- return htmlEditorRef.current.getValidation();
155
- }
156
- return null;
157
- },
158
- isContentEmpty: () => {
159
- if (htmlEditorRef.current && htmlEditorRef.current.isContentEmpty) {
160
- return htmlEditorRef.current.isContentEmpty();
161
- }
162
- return !htmlContent || htmlContent.trim() === '';
163
- },
164
- getIssueCounts: () => {
165
- if (htmlEditorRef.current && htmlEditorRef.current.getIssueCounts) {
166
- return htmlEditorRef.current.getIssueCounts();
167
- }
168
- return {
169
- html: 0, label: 0, liquid: 0, total: 0,
170
- };
171
- },
172
- }), [htmlContent, subject, currentOrgDetails]);
173
-
174
- // Check if liquid support is enabled
175
- const isLiquidEnabled = hasLiquidSupportFeature();
176
-
177
- // Detect edit mode
178
- const hasParamsId = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
179
- const currentTemplateId = templateDataProp?._id || params?.id || location?.query?.id || location?.params?.id
180
- || location?.pathname?.match(/\/edit\/([^/]+)/)?.[1];
181
- const isEditMode = !!currentTemplateId || !!hasParamsId;
182
-
183
- // Load tags on component mount
184
- useEffect(() => {
185
- const { type, module } = location?.query || {};
186
- const isEmbedded = type === EMBEDDED;
187
- const query = {
188
- layout: EMAIL,
189
- type: TAG,
190
- context: isEmbedded ? module : DEFAULT,
191
- embedded: isEmbedded ? type : FULL,
192
- };
193
- if (getDefaultTags) {
194
- query.context = getDefaultTags;
195
- }
196
- if (globalActions && globalActions.fetchSchemaForEntity) {
197
- globalActions.fetchSchemaForEntity(query);
198
- }
199
- }, []);
200
-
201
- // Initialize content from EmailLayout (uploaded zip) in create mode
202
- useEffect(() => {
203
- // Only check EmailLayout in create mode (not edit mode)
204
- if (isEditMode) {
205
- return;
206
- }
207
-
208
- // Check if EmailLayout has content from zip upload
209
- if (EmailLayout) {
210
- // EmailLayout can be a string (HTML content) or an object with html property
211
- const uploadedContent = typeof EmailLayout === 'string'
212
- ? EmailLayout
213
- : (EmailLayout.html || EmailLayout.content || '');
214
-
215
- if (uploadedContent) {
216
- // IMPORTANT: Set both htmlContent and loadedHtmlContent for ZIP upload
217
- // loadedHtmlContent is used by HTMLEditor's initialContent prop
218
- setHtmlContent(uploadedContent);
219
- setLoadedHtmlContent(uploadedContent);
220
- setIsLoading(false);
221
- if (setIsLoadingContent) {
222
- setIsLoadingContent(false);
223
- }
224
- } else {
225
- // No uploaded content, stop loading
226
- setIsLoading(false);
227
- if (setIsLoadingContent) {
228
- setIsLoadingContent(false);
229
- }
230
- }
231
- } else {
232
- // No EmailLayout, stop loading
233
- setIsLoading(false);
234
- if (setIsLoadingContent) {
235
- setIsLoadingContent(false);
236
- }
237
- }
238
- }, [EmailLayout, isEditMode, setIsLoadingContent]);
239
-
240
- // Edit mode: Extract template data and load content/subject
241
- useEffect(() => {
242
- if (!isEditMode) {
243
- // Create mode: stop loading immediately
244
- setIsLoading(false);
245
- return;
246
- }
247
-
248
- const templateDataFromRedux = Email?.templateDetails || Email?.BEETemplate;
249
- const isTemplateLoading = Email?.getTemplateDetailsInProgress || Email?.fetchingCmsData;
250
-
251
- // Check if template ID changed (switching templates)
252
- const templateIdChanged = currentTemplateId
253
- && lastTemplateIdRef.current
254
- && currentTemplateId !== lastTemplateIdRef.current;
255
-
256
- // Reset refs when switching templates
257
- if (templateIdChanged) {
258
- contentInitializedRef.current = false;
259
- subjectInitializedRef.current = false;
260
- lastTemplateIdRef.current = currentTemplateId;
261
- setHtmlContent('');
262
- setSubject('');
263
- setExtractedTemplateName('');
264
- }
265
-
266
- // Set last template ID on first load
267
- if (currentTemplateId && !lastTemplateIdRef.current) {
268
- lastTemplateIdRef.current = currentTemplateId;
269
- }
270
-
271
- // Check if templateDataProp has complete data
272
- // Check if templateDataProp has complete data (must have actual content string)
273
- const hasCompleteTemplateData = templateDataProp && (
274
- (templateDataProp.emailBody || templateDataProp.html_content || templateDataProp['template-content'])
275
- || (templateDataProp.base && (templateDataProp.base.html_content || templateDataProp.base.emailBody || templateDataProp.base['template-content']))
276
- || (templateDataProp.versions?.base && (
277
- // Check active tab content in versions
278
- (() => {
279
- const { base } = templateDataProp.versions;
280
- const activeTab = base.activeTab || 'en';
281
- const langData = base[activeTab] || {};
282
- return langData['template-content'] || langData.html_content || base.html_content || base.emailBody;
283
- })()
284
- ))
285
- );
286
- if (hasCompleteTemplateData && !contentInitializedRef.current) {
287
- let extractedContent = '';
288
- let extractedSubject = '';
289
- let extractedName = '';
290
-
291
- if (templateDataProp.base) {
292
- extractedContent = templateDataProp.base['template-content'] || templateDataProp.base.html_content || templateDataProp.base.emailBody || '';
293
- extractedSubject = templateDataProp.base.subject || templateDataProp.base.emailSubject || '';
294
- } else if (templateDataProp.versions && templateDataProp.versions.base) {
295
- const { base } = templateDataProp.versions;
296
- const activeTab = base.activeTab || 'en';
297
- const languageData = base[activeTab] || {};
298
-
299
- extractedContent = languageData['template-content']
300
- || languageData.html_content
301
- || base.html_content
302
- || base.emailBody
303
- || '';
304
-
305
- extractedSubject = base.subject || base.emailSubject || '';
306
- } else {
307
- extractedContent = templateDataProp['template-content'] || templateDataProp.emailBody || templateDataProp.html_content || '';
308
- extractedSubject = templateDataProp.emailSubject || templateDataProp.subject || '';
309
- }
310
-
311
- // Extract template name from templateDataProp
312
- extractedName = templateDataProp.name || get(templateDataProp, 'versions.base.name') || '';
313
-
314
- // IMPORTANT: Set both htmlContent and loadedHtmlContent
315
- // loadedHtmlContent is used by HTMLEditor's initialContent prop
316
- setHtmlContent(extractedContent);
317
- setLoadedHtmlContent(extractedContent);
318
- setSubject(extractedSubject);
319
- setExtractedTemplateName(extractedName);
320
- contentInitializedRef.current = true;
321
- subjectInitializedRef.current = true;
322
- setIsLoading(false);
323
- if (setIsLoadingContent) {
324
- setIsLoadingContent(false);
325
- }
326
- return;
327
- }
328
-
329
- if (currentTemplateId && emailActions?.getTemplateDetails && !isTemplateLoading) {
330
- const isTemplateIdChanged = lastTemplateIdRef.current !== currentTemplateId;
331
- const needsContent = !contentInitializedRef.current;
332
- const alreadyFetching = fetchingTemplateIdRef.current === currentTemplateId;
333
- const shouldFetch = (isTemplateIdChanged || needsContent) && !alreadyFetching;
334
-
335
- // Fetch fresh data when template ID changes OR when we don't have content loaded yet
336
- // BUT only if we're not already fetching this template
337
- if (shouldFetch) {
338
- fetchingTemplateIdRef.current = currentTemplateId; // Mark as fetching
339
- emailActions.getTemplateDetails(currentTemplateId, 'email');
340
- lastTemplateIdRef.current = currentTemplateId;
341
- }
342
- }
343
-
344
- // **IMPORTANT: Clear stale Redux data in create mode**
345
- // When not in edit mode (create mode), clear any existing template data from Redux
346
- // This prevents previous template data from persisting when switching from Edit to Create
347
- if (!currentTemplateId && !isEditMode && (templateDataFromRedux?._id || templateDataFromRedux?.name)) {
348
- // Clear stale template data - component will handle via resetTemplateData or clearAllValues
349
- if (emailActions?.clearAllValues) {
350
- emailActions.clearAllValues();
351
- }
352
- // Reset component state to ensure clean slate for create mode
353
- setExtractedTemplateName('');
354
- contentInitializedRef.current = false;
355
- subjectInitializedRef.current = false;
356
- lastTemplateIdRef.current = null;
357
- }
358
-
359
- // Stop loading if template is loading
360
- if (isTemplateLoading) {
361
- return;
362
- }
363
-
364
- // Extract from Redux data
365
- const hasTemplateDataFromRedux = templateDataFromRedux
366
- && (templateDataFromRedux._id || templateDataFromRedux.name || templateDataFromRedux.versions);
367
-
368
- if (hasTemplateDataFromRedux && currentTemplateId) {
369
- const reduxTemplateId = templateDataFromRedux?._id;
370
-
371
- if (reduxTemplateId === currentTemplateId) {
372
- const baseData = get(templateDataFromRedux, 'versions.base') || get(templateDataFromRedux, 'base') || {};
373
- const activeTab = baseData.activeTab
374
- || get(currentOrgDetails, 'basic_details.base_language', 'en');
375
- const languageData = baseData[activeTab] || {};
376
-
377
- const extractedContent = languageData['template-content']
378
- || languageData.html_content
379
- || baseData.html_content
380
- || baseData['template-content']
381
- || get(templateDataFromRedux, 'html_content')
382
- || get(templateDataFromRedux, 'template-content')
383
- || '';
384
-
385
- const extractedSubject = baseData.subject
386
- || get(templateDataFromRedux, 'subject')
387
- || get(templateDataFromRedux, 'versions.base.subject')
388
- || '';
389
-
390
- // Extract template name from Redux data
391
- const extractedName = templateDataFromRedux.name || '';
392
-
393
- // Smart update logic:
394
- // 1. Always update loadedHtmlContent if Redux data is different (keep sync with backend)
395
- // 2. Update htmlContent ONLY if it matches the OLD loadedHtmlContent (user hasn't edited)
396
- // OR if it's the first initialization
397
- if (extractedContent !== loadedHtmlContent) {
398
- setLoadedHtmlContent(extractedContent);
399
-
400
- if (!contentInitializedRef.current || htmlContent === loadedHtmlContent) {
401
- setHtmlContent(extractedContent);
402
- setSubject(extractedSubject);
403
- setExtractedTemplateName(extractedName);
404
- }
405
- } else if (!contentInitializedRef.current) {
406
- // First load, even if content matches (e.g. empty), ensure we set state
407
- setHtmlContent(extractedContent);
408
- setLoadedHtmlContent(extractedContent);
409
- setSubject(extractedSubject);
410
- setExtractedTemplateName(extractedName);
411
- }
412
-
413
- contentInitializedRef.current = true;
414
- subjectInitializedRef.current = true;
415
-
416
- // Only clear fetching ref if we are not loading anymore
417
- // This prevents race conditions where we load stale data while real fetch is pending
418
- if (!isTemplateLoading) {
419
- fetchingTemplateIdRef.current = null;
420
- }
421
- }
422
-
423
- setIsLoading(false);
424
- if (setIsLoadingContent) {
425
- setIsLoadingContent(false);
426
- }
427
- return;
428
- }
429
-
430
- // Fallback: stop loading anyway
431
- if (!isTemplateLoading && !hasTemplateDataFromRedux && !hasCompleteTemplateData) {
432
- setHtmlContent('');
433
- setLoadedHtmlContent(''); // Set stable loaded content
434
- setSubject('');
435
- setExtractedTemplateName('');
436
- setIsLoading(false);
437
- if (setIsLoadingContent) {
438
- setIsLoadingContent(false);
439
- }
440
- }
441
- }, [
442
- Email?.templateDetails,
443
- Email?.BEETemplate,
444
- Email?.getTemplateDetailsInProgress,
445
- Email?.fetchingCmsData,
446
- templateDataProp,
447
- currentTemplateId,
448
- isEditMode,
449
- emailActions,
450
- currentOrgDetails,
451
- ]);
452
-
453
- // Handle loading state
454
- useEffect(() => {
455
- const isAnyApiInProgress = loadingTags || fetchingLiquidTags || createTemplateInProgress || fetchingCmsData;
456
-
457
- // Stop loading when no API is in progress and tags are loaded
458
- // Use optional chaining to safely access tags
459
- if (!isAnyApiInProgress && Array.isArray(tags) && tags?.length >= 0) {
460
- setIsLoading(false);
461
- } else if (isAnyApiInProgress) {
462
- setIsLoading(true);
463
- }
464
- }, [loadingTags, tags, fetchingLiquidTags, createTemplateInProgress, fetchingCmsData]);
465
-
466
- // Handle content change from HTMLEditor
467
- const handleContentChange = useCallback((content) => {
468
- setHtmlContent(content);
469
-
470
- // Validate tags
471
- if (tags.length > 0 || !isEmpty(injectedTags)) {
472
- const validationResult = validateTags({
473
- content,
474
- tagsParam: tags,
475
- injectedTagsParams: injectedTags,
476
- location,
477
- tagModule: getDefaultTags,
478
- eventContextTags,
479
- });
480
-
481
- if (!validationResult.valid) {
482
- setTagValidationError(validationResult);
483
- } else {
484
- setTagValidationError(null);
485
- }
486
- }
487
- }, [tags, injectedTags, location, getDefaultTags, eventContextTags]);
488
-
489
- // Store the last validation state received from HTMLEditor
490
- const lastValidationStateRef = useRef(null);
491
-
492
- // Use ref to store callback to avoid infinite loops (callback in deps would cause re-runs)
493
- const onValidationStateChangeRef = useRef(onHtmlEditorValidationStateChange);
494
- useEffect(() => {
495
- onValidationStateChangeRef.current = onHtmlEditorValidationStateChange;
496
- }, [onHtmlEditorValidationStateChange]);
497
-
498
- // Handle error acknowledgment - called when user clicks redirection icon
499
- const handleErrorAcknowledged = useCallback(() => {
500
- setErrorsAcknowledged(true);
501
- // Immediately update parent with acknowledged state
502
- if (onValidationStateChangeRef.current && lastValidationStateRef.current) {
503
- onValidationStateChangeRef.current({
504
- ...lastValidationStateRef.current,
505
- errorsAcknowledged: true,
506
- });
507
- }
508
- }, []); // No deps needed - using ref
509
-
510
- // Track last sent state to parent to prevent duplicate updates
511
- const lastSentToParentRef = useRef(null);
512
-
513
- // Callback to receive validation state from HTMLEditor
514
- const handleValidationChange = useCallback((validationState) => {
515
- const {
516
- isContentEmpty, issueCounts, validationComplete, hasErrors,
517
- } = validationState;
518
- const currentErrorCount = issueCounts?.total || 0;
519
-
520
- // Reset acknowledgment when new errors appear
521
- let shouldAcknowledgeErrors = errorsAcknowledged;
522
- if (hasErrors && validationComplete) {
523
- const isFirstTimeErrors = prevErrorCountRef.current === 0 || prevErrorCountRef.current === undefined;
524
- const errorCountChanged = currentErrorCount !== prevErrorCountRef.current;
525
-
526
- if (isFirstTimeErrors || (errorCountChanged && currentErrorCount > 0)) {
527
- setErrorsAcknowledged(false);
528
- shouldAcknowledgeErrors = false;
529
- }
530
- } else if (!hasErrors && validationComplete) {
531
- // No errors - allow buttons to be enabled
532
- prevErrorCountRef.current = 0;
533
- shouldAcknowledgeErrors = true;
534
- }
535
-
536
- // Update previous error count
537
- if (validationComplete) {
538
- prevErrorCountRef.current = currentErrorCount;
539
- }
540
-
541
- // Store last validation state (hasErrors = Rule Group #1 only, for save gating)
542
- lastValidationStateRef.current = {
543
- isContentEmpty,
544
- issueCounts,
545
- validationComplete,
546
- hasErrors: hasErrors === true,
547
- };
548
-
549
- // Notify parent about validation state (using ref to avoid infinite loops)
550
- // hasErrors = only Rule Group #1 (Input & Sanitization) – used by SlideBoxFooter for button gating
551
- if (onValidationStateChangeRef.current) {
552
- const newState = {
553
- isContentEmpty,
554
- issueCounts,
555
- validationComplete,
556
- hasErrors: hasErrors === true,
557
- errorsAcknowledged: hasErrors ? shouldAcknowledgeErrors : true,
558
- };
559
-
560
- const lastState = lastSentToParentRef.current;
561
- const hasChanged = !lastState
562
- || lastState.isContentEmpty !== newState.isContentEmpty
563
- || lastState.validationComplete !== newState.validationComplete
564
- || lastState.hasErrors !== newState.hasErrors
565
- || lastState.errorsAcknowledged !== newState.errorsAcknowledged
566
- || lastState.issueCounts?.total !== newState.issueCounts?.total;
567
-
568
- if (hasChanged) {
569
- lastSentToParentRef.current = newState;
570
- onValidationStateChangeRef.current(newState);
571
- }
572
- }
573
- }, [errorsAcknowledged]);
574
-
575
- // Note: Initial validation state is now sent by HTMLEditor's onValidationChange callback
576
- // Removed the separate useEffect that was causing infinite loops by depending on htmlContent
577
-
578
- // Handle tag insertion into Subject field
579
- const handleSubjectTagSelect = useCallback((data) => {
580
- if (data) {
581
- const tagToInsert = `{{${data}}}`;
582
- const input = document.getElementById('template-subject') || document.querySelector('#template-subject input');
583
- let subjectValue = subject || '';
584
- try {
585
- if (input && (typeof input.selectionStart === 'number')) {
586
- const startPos = input.selectionStart;
587
- const endPos = input.selectionEnd;
588
- subjectValue = `${subjectValue.substring(0, startPos)}${tagToInsert}${subjectValue.substring(endPos)}`;
589
- setSubject(subjectValue);
590
- try {
591
- input.focus();
592
- const newPos = startPos + tagToInsert.length;
593
- input.selectionStart = newPos;
594
- input.selectionEnd = newPos;
595
- } catch (e) {
596
- // Ignore focus errors
597
- }
598
- } else {
599
- subjectValue = `${subjectValue}${tagToInsert}`;
600
- setSubject(subjectValue);
601
- if (input) {
602
- try {
603
- input.value = subjectValue;
604
- } catch (e) {
605
- // Ignore value setting errors
606
- }
607
- }
608
- }
609
- } catch (e) {
610
- // Fallback: safe append
611
- subjectValue = `${subjectValue}${tagToInsert}`;
612
- setSubject(subjectValue);
613
- }
614
- }
615
- }, [subject]);
616
-
617
- // Handle subject change
618
- const handleSubjectChange = useCallback((e) => {
619
- const newSubject = e.target.value;
620
- setSubject(newSubject);
621
- if (newSubject && subjectError) {
622
- setSubjectError('');
623
- }
624
- }, [subjectError]);
625
-
626
- // Handle Save/Update with liquid validation
627
- const handleSave = useCallback(() => {
628
- // IMPORTANT: Clear API validation errors FIRST before checking for validation errors
629
- // This ensures that old API errors don't block the save when user fixes content and clicks Update again
630
- // We'll re-validate with fresh API call anyway
631
- if (isLiquidEnabled && getLiquidTags) {
632
- setApiValidationErrors({
633
- liquidErrors: [],
634
- standardErrors: [],
635
- });
636
- }
637
-
638
- // 1. Validate Subject - BLOCKING (matches CK/BEE behavior)
639
- if (!subject || !subject.trim()) {
640
- const errorMessage = formatMessage(emailMessages["Email Subject cannot be empty."]);
641
- setSubjectError(errorMessage);
642
- // Reset parent state so next click is detected as a change
643
- if (onValidationFail) {
644
- onValidationFail();
645
- }
646
- // IMPORTANT: Return here to block save - matches CK/BEE editor behavior
647
- return;
648
- }
649
- // Clear error if subject is valid
650
- if (subjectError) {
651
- setSubjectError('');
652
- }
653
-
654
- // 1.5. Check for validation errors (Errors = blocking, Warnings = non-blocking)
655
- // Get issue counts from ref or stored state
656
- let issueCounts = { errors: 0, warnings: 0, total: 0 };
657
- if (htmlEditorRef.current && typeof htmlEditorRef.current.getIssueCounts === 'function') {
658
- issueCounts = htmlEditorRef.current.getIssueCounts();
659
- } else if (lastValidationStateRef.current?.issueCounts) {
660
- issueCounts = lastValidationStateRef.current.issueCounts;
661
- }
662
-
663
- // Only Rule Group #1 (Input & Sanitization) blocks save; warnings do not block
664
- // Check both lastValidationStateRef (from onValidationChange callback) and getValidationState (current state)
665
- const validationState = htmlEditorRef.current && typeof htmlEditorRef.current.getValidationState === 'function'
666
- ? htmlEditorRef.current.getValidationState()
667
- : null;
668
- const hasBlockingErrors = lastValidationStateRef.current?.hasErrors === true
669
- || (validationState && validationState.hasErrors === true);
670
-
671
- if (hasBlockingErrors) {
672
- setErrorsAcknowledged(false);
673
- if (onValidationStateChangeRef.current) {
674
- onValidationStateChangeRef.current({
675
- isContentEmpty: !htmlContent || !htmlContent.trim(),
676
- issueCounts,
677
- validationComplete: true,
678
- hasErrors: true,
679
- errorsAcknowledged: false,
680
- });
681
- }
682
- if (onValidationFail) {
683
- onValidationFail();
684
- }
685
- return;
686
- }
687
-
688
- // 2. Validate Unsubscribe Tag (if mandatory)
689
- // Check if unsubscribe tag is mandatory and if it exists in content
690
- if (isEmailUnsubscribeTagMandatory() && moduleType === OUTBOUND) {
691
- // Check if content contains unsubscribe tag (either {{unsubscribe}} or {{unsubscribe(#...)})
692
- const unsubscribeRegex = /{{unsubscribe(\(#[a-zA-Z\d]{6}\))?}}/g; // eslint-disable-line no-useless-escape
693
- const hasUnsubscribeTag = unsubscribeRegex.test(htmlContent);
694
-
695
- if (!hasUnsubscribeTag) {
696
- // Show error notification
697
- const missingTagsMsg = intl.formatMessage(formBuilderMessages.missingTags);
698
- const errorMessage = `${missingTagsMsg} unsubscribe`;
699
- CapNotification.error({
700
- message: 'ERROR ! ! !',
701
- description: errorMessage,
702
- duration: 5,
703
- });
704
-
705
- // Reset parent state so next click is detected as a change
706
- if (onValidationFail) {
707
- onValidationFail();
708
- }
709
- // Block save - unsubscribe tag is mandatory
710
- return;
711
- }
712
- }
713
-
714
- // 3. Validate Content Tags
715
- // For NON-liquid orgs: BLOCKING validation (matches CK/BEE behavior)
716
- // For liquid orgs: Non-blocking (extractTags API will validate)
717
- if (tags.length > 0 || !isEmpty(injectedTags)) {
718
- const validationResult = validateTags({
719
- content: htmlContent,
720
- tagsParam: tags,
721
- injectedTagsParams: injectedTags,
722
- location,
723
- tagModule: getDefaultTags,
724
- eventContextTags,
725
- });
726
-
727
- const hasUnsupportedTags = validationResult?.unsupportedTags?.length > 0;
728
- if (!validationResult?.valid || hasUnsupportedTags) {
729
- setTagValidationError(validationResult);
730
-
731
- // IMPORTANT: For non-liquid orgs, block save (like CK/BEE editor)
732
- // For liquid orgs, continue (extractTags API will validate)
733
- if (!isLiquidEnabled) {
734
- // Show notification popup like CK/BEE editor
735
- const baseLanguage = get(currentOrgDetails, 'basic_details.base_language', 'en');
736
-
737
- const contentNotValidMsg = intl.formatMessage(formBuilderMessages.contentNotValidLanguage);
738
- let errorMessage = `${contentNotValidMsg} ${baseLanguage}`;
739
-
740
- if (hasUnsupportedTags) {
741
- const unsupportedTagsMsg = intl.formatMessage(formBuilderMessages.unsupportedTags);
742
- errorMessage += `\n${unsupportedTagsMsg} ${validationResult?.unsupportedTags?.join(', ')}`;
743
- }
744
- if (validationResult?.missingTags?.length > 0) {
745
- const missingTagsMsg = intl.formatMessage(formBuilderMessages.missingTags);
746
- errorMessage += `\n${missingTagsMsg} ${validationResult?.missingTags?.join(', ')}`;
747
- }
748
-
749
- const type = 'error';
750
- CapNotification[type]({
751
- message: `${type.toUpperCase()} ! ! ! `,
752
- description: errorMessage,
753
- duration: 5,
754
- });
755
-
756
- // Reset parent state so next click is detected as a change
757
- if (onValidationFail) {
758
- onValidationFail();
759
- }
760
- // Block save for non-liquid orgs
761
- return;
762
- }
763
- // For liquid orgs, just show warning and continue
764
- }
765
- // Clear tag errors if valid
766
- if (tagValidationError && validationResult?.valid && !hasUnsupportedTags) {
767
- setTagValidationError(null);
768
- }
769
- }
770
-
771
-
772
- const baseLanguage = get(currentOrgDetails, 'basic_details.base_language', 'en');
773
-
774
- // Actual save function - called after liquid validation (if enabled) or directly
775
- const performSave = () => {
776
- if (isFullMode) {
777
- // Full mode: Call email actions directly
778
- const templateDataFromRedux = Email?.templateDetails || Email?.BEETemplate;
779
- // IMPORTANT: Only use currentTemplateId from URL/params - don't fallback to Redux data
780
- // which might be stale from previous template in create mode
781
- const templateId = currentTemplateId;
782
- const isEditModeForSave = !!templateId;
783
-
784
- const langId = get(currentOrgDetails, `basic_details.languages.${baseLanguage}.lang_id`, '');
785
- const language = get(currentOrgDetails, `basic_details.languages.${baseLanguage}.language`, baseLanguage);
786
-
787
- // Generate or reuse tabKey
788
- let tabKey = _.uniqueId();
789
- if (isEditMode && templateDataFromRedux) {
790
- const existingTabKey = get(templateDataFromRedux, 'versions.base.tabKey')
791
- || get(templateDataFromRedux, 'base.tabKey');
792
- if (existingTabKey) {
793
- tabKey = existingTabKey;
794
- }
795
- }
796
-
797
- const languageData = {
798
- 'template-content': htmlContent || '',
799
- "is_drag_drop": false,
800
- "drag_drop_id": '',
801
- "lang_id": langId,
802
- "iso_code": baseLanguage,
803
- "language": language,
804
- "tabKey": tabKey,
805
- };
806
-
807
- const baseStructure = {
808
- [baseLanguage]: languageData,
809
- activeTab: baseLanguage,
810
- selectedLanguages: [baseLanguage],
811
- base: true,
812
- tabKey,
813
- subject: subject || '',
814
- };
815
-
816
- const historyEntry = {
817
- [baseLanguage]: { ...languageData },
818
- activeTab: baseLanguage,
819
- selectedLanguages: [baseLanguage],
820
- base: true,
821
- tabKey,
822
- subject: subject || '',
823
- };
824
-
825
- const finalTemplateName = isEditModeForSave ? (extractedTemplateName || '') : (templateName || '');
826
-
827
- const obj = {
828
- type: EMAIL,
829
- // In Edit mode, use extractedTemplateName; in Create mode, use templateName prop
830
- name: finalTemplateName,
831
- versions: {
832
- base: baseStructure,
833
- history: [historyEntry],
834
- },
835
- };
836
-
837
- if (isEditModeForSave && templateId) {
838
- obj._id = templateId;
839
- }
840
-
841
- if (emailActions?.transformEmailTemplate && emailActions?.createTemplate) {
842
- emailActions.transformEmailTemplate(obj, (newEmail) => {
843
- emailActions.createTemplate(newEmail, (createResponse) => {
844
- // Handle error response (from 409 or other failures)
845
- if (createResponse?.error) {
846
- // Error already shown by Email component via stopValidation
847
- // Reset acknowledgment so buttons are disabled again
848
- setErrorsAcknowledged(false);
849
- // Just reset parent state so next click is detected
850
- if (onValidationFail) {
851
- onValidationFail();
852
- }
853
- return;
854
- }
855
-
856
- // Handle success response
857
- if (createResponse && createResponse.templateId) {
858
- const successMessage = formatMessage(
859
- isEditModeForSave ? emailMessages.emailEditSuccess : emailMessages.emailCreateSuccess
860
- );
861
- CapNotification.success({
862
- message: successMessage,
863
- key: isEditModeForSave ? 'edit-template-success' : 'create-template-success',
864
- });
865
-
866
- const module = location?.query?.module || 'default';
867
- const type = location?.query?.type;
868
- const isLanguageSupport = location?.query?.isLanguageSupport || false;
869
- const isBEESupport = (location?.query?.isBEESupport !== "false") || false;
870
-
871
- if (getFormdata) {
872
- const { versions, ...rest } = createResponse.templateId;
873
- getFormdata({ value: versions, ...rest, validity: true });
874
- } else {
875
- const queryParams = type === 'embedded'
876
- ? {
877
- type: 'embedded', module, isLanguageSupport, isBEESupport,
878
- }
879
- : { module, isLanguageSupport, isBEESupport };
880
-
881
- const searchParams = new URLSearchParams();
882
- Object.keys(queryParams).forEach((key) => {
883
- if (queryParams[key] !== undefined && queryParams[key] !== null) {
884
- searchParams.append(key, queryParams[key]);
885
- }
886
- });
887
-
888
- history.push({
889
- pathname: '/email',
890
- search: searchParams.toString() ? `?${searchParams.toString()}` : '',
891
- });
892
- }
893
- }
894
- });
895
- });
896
- }
897
- } else {
898
- // Library mode: Use getFormdata flow
899
- const tmpData = {
900
- html_content: htmlContent || '',
901
- is_drag_drop: false,
902
- lang_id: get(currentOrgDetails, `basic_details.languages.${baseLanguage}.lang_id`, ''),
903
- iso_code: baseLanguage,
904
- language: get(currentOrgDetails, `basic_details.languages.${baseLanguage}.language`, baseLanguage),
905
- };
906
-
907
- const libraryPayload = {
908
- base: tmpData,
909
- secondary_templates: [{ template_data: tmpData }],
910
- };
911
- libraryPayload.base.subject = subject || '';
912
-
913
- if (location?.query?.module === 'library' && isGetFormData && getFormdata) {
914
- const response = {
915
- action: "getFormData",
916
- postAction: 'next',
917
- id: get(Email, 'templateDetails._id', ''),
918
- value: libraryPayload,
919
- validity: true,
920
- type: EMAIL,
921
- };
922
- getFormdata(response);
923
- } else if (getFormdata) {
924
- getFormdata({
925
- value: libraryPayload,
926
- validity: true,
927
- type: EMAIL,
928
- });
929
- }
930
- }
931
- };
932
-
933
- // If liquid enabled, validate first using extractTags API
934
- if (isLiquidEnabled && getLiquidTags) {
935
- // Note: API validation errors are already cleared at the start of handleSave
936
- // This ensures fresh validation on every save attempt
937
-
938
- const onError = ({ standardErrors, liquidErrors }) => {
939
- // Store API validation errors in state so they can be displayed in UI
940
- setApiValidationErrors({
941
- liquidErrors: liquidErrors || [],
942
- standardErrors: standardErrors || [],
943
- });
944
-
945
- if (showLiquidErrorInFooter) {
946
- showLiquidErrorInFooter({
947
- STANDARD_ERROR_MSG: standardErrors || [],
948
- LIQUID_ERROR_MSG: liquidErrors || [],
949
- });
950
- }
951
- // Don't reset ref here - liquid validation is async and resetting causes infinite loop
952
- // The parent's isGetFormData will be reset by onValidationFail, and the next click will be detected
953
- if (onValidationFail) {
954
- onValidationFail();
955
- }
956
- };
957
-
958
- const onSuccess = () => {
959
- // Clear API validation errors on success
960
- setApiValidationErrors({
961
- liquidErrors: [],
962
- standardErrors: [],
963
- });
964
- performSave();
965
- };
966
-
967
- validateLiquidTemplateContent(htmlContent || '', {
968
- getLiquidTags: getLiquidTags
969
- ? (inputContent, callback) => getLiquidTags(inputContent, callback)
970
- : (inputContent, callback) => globalActions?.getLiquidTags?.(inputContent, callback),
971
- formatMessage: intl.formatMessage,
972
- messages: formBuilderMessages,
973
- onError,
974
- onSuccess,
975
- tagLookupMap: metaEntities?.tagLookupMap,
976
- eventContextTags,
977
- isLiquidFlow: true,
978
- forwardedTags: forwardedTags || {},
979
- });
980
- } else {
981
- performSave();
982
- }
983
- }, [
984
- subject,
985
- htmlContent,
986
- tags,
987
- injectedTags,
988
- location,
989
- getDefaultTags,
990
- eventContextTags,
991
- formatMessage,
992
- subjectError,
993
- isFullMode,
994
- currentOrgDetails,
995
- Email,
996
- currentTemplateId,
997
- templateDataProp,
998
- templateName,
999
- emailActions,
1000
- getFormdata,
1001
- isGetFormData,
1002
- isLiquidEnabled,
1003
- getLiquidTags,
1004
- showLiquidErrorInFooter,
1005
- metaEntities,
1006
- forwardedTags,
1007
- globalActions,
1008
- intl,
1009
- extractedTemplateName,
1010
- ]);
1011
-
1012
- // Keep handleSave ref up to date - MUST be before isGetFormData effect
1013
- useEffect(() => {
1014
- handleSaveRef.current = handleSave;
1015
- }, [handleSave]);
1016
-
1017
- // Trigger save when isGetFormData becomes true (Create/Done button clicked)
1018
- useEffect(() => {
1019
- const isGetFormDataChanged = isGetFormData && !prevIsGetFormDataRef.current;
1020
- const wasReset = !isGetFormData && prevIsGetFormDataRef.current;
1021
-
1022
- if (isGetFormDataChanged) {
1023
- // Update ref immediately to prevent duplicate calls
1024
- prevIsGetFormDataRef.current = true;
1025
- // Call handleSave via ref to ensure we're using the latest version
1026
- // This avoids the effect re-running when handleSave changes
1027
- if (handleSaveRef.current) {
1028
- handleSaveRef.current();
1029
- } else {
1030
- console.warn('[EmailHTMLEditor] handleSaveRef.current is null!');
1031
- }
1032
- } else if (wasReset) {
1033
- // Reset ref when parent resets isGetFormData (e.g., after validation failure)
1034
- prevIsGetFormDataRef.current = false;
1035
- } else {
1036
- // Update ref to current value for next comparison
1037
- prevIsGetFormDataRef.current = isGetFormData;
1038
- }
1039
- }, [isGetFormData, isEditMode]); // Include isEditMode for logging
1040
-
1041
- // Handle tag context change
1042
- const handleOnTagsContextChange = useCallback((data) => {
1043
- const { type, module } = location?.query || {};
1044
- const isEmbedded = type === EMBEDDED;
1045
- const contextValue = data || (isEmbedded ? module : DEFAULT);
1046
- const query = {
1047
- layout: EMAIL,
1048
- type: TAG,
1049
- context: contextValue ? contextValue.toLowerCase() : contextValue,
1050
- embedded: isEmbedded ? type : FULL,
1051
- };
1052
- if (globalActions && globalActions.fetchSchemaForEntity) {
1053
- globalActions.fetchSchemaForEntity(query);
1054
- }
1055
- }, [location, globalActions]);
1056
-
1057
-
1058
- const spinTip = fetchingLiquidTags ? <FormattedMessage {...formBuilderMessages.liquidSpinText} /> : '';
1059
-
1060
- // Handle template name change
1061
- // Call showTemplateName when templateName is available (for CreativesContainer header)
1062
- // This matches the behavior of Email component which calls showTemplateName in onFormDataChange
1063
- // In Edit mode, use extractedTemplateName from template data; in Create mode, use templateName prop
1064
- const { onFormDataChange: onFormDataChangeProp } = props;
1065
-
1066
- // Create onFormDataChange callback that updates extractedTemplateName in Edit mode
1067
- // This matches the Email component's onFormDataChange which updates its state
1068
- const handleFormDataChange = useCallback((updatedFormData) => {
1069
- const newTemplateName = updatedFormData?.['template-name'] || '';
1070
-
1071
- // In Edit mode, update extractedTemplateName state (similar to Email component updating its formData state)
1072
- if (isEditMode && newTemplateName !== extractedTemplateName) {
1073
- setExtractedTemplateName(newTemplateName);
1074
- }
1075
-
1076
- // Call the parent's onFormDataChange if provided
1077
- if (onFormDataChangeProp) {
1078
- onFormDataChangeProp(updatedFormData);
1079
- }
1080
-
1081
- // Call showTemplateName again with updated formData (same pattern as Email component)
1082
- if (showTemplateName && isFullMode) {
1083
- showTemplateName({ formData: updatedFormData, onFormDataChange: handleFormDataChange });
1084
- }
1085
- }, [isEditMode, extractedTemplateName, onFormDataChangeProp, showTemplateName, isFullMode]);
1086
-
1087
- useEffect(() => {
1088
- if (showTemplateName && isFullMode) {
1089
- // In Edit mode, use extractedTemplateName; in Create mode, use templateName prop
1090
- const nameToUse = isEditMode ? extractedTemplateName : templateName;
1091
- const formData = {
1092
- 'template-name': nameToUse || '',
1093
- };
1094
- // Pass handleFormDataChange callback so CreativesContainer can update template name
1095
- // This is the same pattern used in Email component
1096
- showTemplateName({ formData, onFormDataChange: handleFormDataChange });
1097
- }
1098
- }, [showTemplateName, isFullMode, templateName, extractedTemplateName, isEditMode, handleFormDataChange]);
1099
-
1100
- return (
1101
- <CapSpin spinning={isLoading} tip={spinTip}>
1102
- <CapRow>
1103
- <CapColumn span={24}>
1104
- {/* Subject Field */}
1105
- <CapColumn span={24} style={{ marginBottom: CAP_SPACE_16 }}>
1106
- <CapTagListWithInput
1107
- inputId="template-subject"
1108
- inputValue={subject}
1109
- inputOnChange={handleSubjectChange}
1110
- inputPlaceholder={formatMessage(messages.enterEmailSubject)}
1111
- inputRequired
1112
- inputErrorMessage={subjectError}
1113
- headingText={formatMessage(messages.subject)}
1114
- headingType="h4"
1115
- headingStyle={{ marginRight: '85%' }}
1116
- onTagSelect={handleSubjectTagSelect}
1117
- onContextChange={handleOnTagsContextChange}
1118
- location={location}
1119
- tags={tags}
1120
- injectedTags={injectedTags || {}}
1121
- selectedOfferDetails={selectedOfferDetails}
1122
- eventContextTags={eventContextTags}
1123
- showHeading
1124
- showTagList
1125
- showInput
1126
- containerStyle={{
1127
- display: 'flex',
1128
- flexDirection: 'column',
1129
- }}
1130
- popoverPlacement="bottomRight"
1131
- />
1132
- </CapColumn>
1133
-
1134
- <HTMLEditor
1135
- ref={htmlEditorRef}
1136
- variant="email"
1137
- initialContent={loadedHtmlContent || ''}
1138
- onContentChange={handleContentChange}
1139
- onSave={handleSave}
1140
- readOnly={isReadOnly}
1141
- showFullscreenButton
1142
- autoSave={false}
1143
- tags={tags}
1144
- injectedTags={injectedTags}
1145
- location={location}
1146
- eventContextTags={eventContextTags}
1147
- selectedOfferDetails={selectedOfferDetails}
1148
- channel={EMAIL}
1149
- userLocale={intl.locale || 'en'}
1150
- moduleFilterEnabled={location?.query?.type !== EMBEDDED}
1151
- onTagContextChange={handleOnTagsContextChange}
1152
- isLiquidEnabled={isLiquidEnabled}
1153
- isFullMode={isFullMode}
1154
- onErrorAcknowledged={handleErrorAcknowledged}
1155
- onValidationChange={handleValidationChange}
1156
- apiValidationErrors={apiValidationErrors}
1157
- />
1158
- </CapColumn>
1159
- </CapRow>
1160
- </CapSpin>
1161
- );
1162
- };
1163
-
1164
- EmailHTMLEditor.propTypes = {
1165
- intl: PropTypes.object.isRequired,
1166
- location: PropTypes.object,
1167
- params: PropTypes.object,
1168
- getDefaultTags: PropTypes.string,
1169
- supportedTags: PropTypes.array,
1170
- metaEntities: PropTypes.object,
1171
- injectedTags: PropTypes.object,
1172
- globalActions: PropTypes.object,
1173
- loadingTags: PropTypes.bool,
1174
- eventContextTags: PropTypes.array,
1175
- forwardedTags: PropTypes.object,
1176
- selectedOfferDetails: PropTypes.array,
1177
- currentOrgDetails: PropTypes.object,
1178
- isReadOnly: PropTypes.bool,
1179
- fetchingLiquidTags: PropTypes.bool,
1180
- createTemplateInProgress: PropTypes.bool,
1181
- fetchingCmsData: PropTypes.bool,
1182
- // Email Redux state
1183
- Email: PropTypes.object,
1184
- // Email actions
1185
- emailActions: PropTypes.object,
1186
- // Full mode props
1187
- isFullMode: PropTypes.bool,
1188
- templateName: PropTypes.string,
1189
- showTemplateName: PropTypes.func,
1190
- onFormDataChange: PropTypes.func,
1191
- isGetFormData: PropTypes.bool,
1192
- getFormdata: PropTypes.func,
1193
- // Library mode props
1194
- templateData: PropTypes.object,
1195
- // Uploaded content from zip file
1196
- EmailLayout: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // eslint-disable-line react/require-default-props
1197
- // Liquid validation
1198
- getLiquidTags: PropTypes.func,
1199
- showLiquidErrorInFooter: PropTypes.func,
1200
- // Preview/Test
1201
- // Parent loading control
1202
- setIsLoadingContent: PropTypes.func,
1203
- forwardedRef: PropTypes.object,
1204
- onValidationFail: PropTypes.func,
1205
- moduleType: PropTypes.string,
1206
- onHtmlEditorValidationStateChange: PropTypes.func,
1207
- };
1208
-
1209
- EmailHTMLEditor.defaultProps = {
1210
- location: {},
1211
- params: {},
1212
- getDefaultTags: null,
1213
- supportedTags: [],
1214
- showTemplateName: null,
1215
- metaEntities: {},
1216
- injectedTags: {},
1217
- globalActions: {},
1218
- loadingTags: false,
1219
- eventContextTags: [],
1220
- forwardedTags: {},
1221
- selectedOfferDetails: [],
1222
- currentOrgDetails: {},
1223
- isReadOnly: false,
1224
- fetchingLiquidTags: false,
1225
- createTemplateInProgress: false,
1226
- fetchingCmsData: false,
1227
- Email: {},
1228
- emailActions: {},
1229
- isFullMode: false,
1230
- templateName: '',
1231
- onFormDataChange: null,
1232
- isGetFormData: false,
1233
- getFormdata: null,
1234
- templateData: null,
1235
- getLiquidTags: null,
1236
- showLiquidErrorInFooter: null,
1237
- setIsLoadingContent: null,
1238
- forwardedRef: null,
1239
- onValidationFail: null,
1240
- moduleType: null,
1241
- onHtmlEditorValidationStateChange: null,
1242
- };
1243
-
1244
- const EmailHTMLEditorWithIntl = injectIntl(EmailHTMLEditor);
1245
-
1246
- export default forwardRef((props, ref) => <EmailHTMLEditorWithIntl {...props} forwardedRef={ref} />);