@capillarytech/creatives-library 8.0.242-alpha.10 → 8.0.242-alpha.11

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