@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,6 +1,4 @@
1
- import React, {
2
- useState, useEffect, useCallback, useMemo, useRef,
3
- } from "react";
1
+ import React, { useState, useEffect, useCallback } from "react";
4
2
  import isEmpty from 'lodash/isEmpty';
5
3
  import get from 'lodash/get';
6
4
  import { bindActionCreators } from "redux";
@@ -9,19 +7,21 @@ import { injectIntl, FormattedMessage } from "react-intl";
9
7
  import { DAEMON } from '@capillarytech/vulcan-react-sdk/utils/sagaInjectorTypes';
10
8
  import CapHeading from "@capillarytech/cap-ui-library/CapHeading";
11
9
  import CapSpin from "@capillarytech/cap-ui-library/CapSpin";
10
+ import CapInput from "@capillarytech/cap-ui-library/CapInput";
12
11
  import CapRadioGroup from "@capillarytech/cap-ui-library/CapRadioGroup";
13
12
  import CapRow from "@capillarytech/cap-ui-library/CapRow";
14
13
  import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
15
14
  import CapButton from "@capillarytech/cap-ui-library/CapButton";
15
+ import CapTab from "@capillarytech/cap-ui-library/CapTab";
16
16
  import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
17
- import { makeSelectInApp, makeSelectAccount, makeSelectGetTemplateDetailsInProgress } from "./selectors";
17
+ import { makeSelectInApp, makeSelectAccount } from "./selectors";
18
18
  import * as globalActions from '../Cap/actions';
19
19
  import {
20
20
  isLoadingMetaEntities,
21
21
  makeSelectMetaEntities,
22
22
  setInjectedTags,
23
23
  selectCurrentOrgDetails,
24
- selectLiquidStateDetails,
24
+ selectLiquidStateDetails
25
25
  } from "../Cap/selectors";
26
26
  import * as inAppActions from "./actions";
27
27
  import './index.scss';
@@ -40,7 +40,6 @@ import {
40
40
  ANDROID,
41
41
  BIG_PICTURE,
42
42
  BIG_TEXT,
43
- BIG_HTML,
44
43
  DEEP_LINK,
45
44
  DEVICE_SUPPORTED,
46
45
  INAPP_BUTTON_TYPES,
@@ -49,22 +48,19 @@ import {
49
48
  INITIAL_CTA_DATA,
50
49
  IOS,
51
50
  LAYOUT_RADIO_OPTIONS,
52
- IOS_CAPITAL,
51
+ AI_CONTENT_BOT_DISABLED,
53
52
  } from "./constants";
54
53
  import { INAPP, SMS } from "../CreativesContainer/constants";
55
54
  import {
56
55
  ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
57
56
  } from "../Whatsapp/constants";
57
+ import CapDeviceContent from "../../v2Components/CapDeviceContent";
58
58
  import { getCdnUrl } from "../../utils/cdnTransformation";
59
59
  import { getCtaObject, hasAnyErrors, getSingleTab } from "./utils";
60
60
  import { validateInAppContent } from "../../utils/commonUtils";
61
+ import ErrorInfoNote from "../../v2Components/ErrorInfoNote";
61
62
  import { hasLiquidSupportFeature } from "../../utils/common";
62
63
  import formBuilderMessages from "../../v2Components/FormBuilder/messages";
63
- import HTMLEditor from "../../v2Components/HtmlEditor";
64
- import { HTML_EDITOR_VARIANTS } from "../../v2Components/HtmlEditor/constants";
65
- import { INAPP_EDITOR_TYPES } from "../InAppWrapper/constants";
66
- import InappAdvanced from "../InappAdvance/index";
67
- import { ErrorInfoNote } from "../../v2Components/ErrorInfoNote";
68
64
 
69
65
  let editContent = {};
70
66
 
@@ -72,14 +68,13 @@ export const InApp = (props) => {
72
68
  const {
73
69
  intl,
74
70
  actions,
75
- globalActions,
76
71
  isFullMode,
77
72
  onCreateComplete,
78
73
  params,
79
74
  templateData = {},
80
- defaultData = {},
81
75
  editData = {},
82
76
  accountData = {},
77
+ globalActions,
83
78
  location,
84
79
  getDefaultTags,
85
80
  supportedTags,
@@ -87,19 +82,8 @@ export const InApp = (props) => {
87
82
  injectedTags,
88
83
  getFormData,
89
84
  selectedOfferDetails,
90
- fetchingLiquidValidation,
91
- templateName,
92
- getTemplateDetailsInProgress,
93
- isEditInApp,
94
- setIsLoadingContent,
95
- query,
96
- inAppCreateMode,
97
- isGetFormData,
98
- showTemplateName,
99
- onValidationFail,
100
- type,
101
- forwardedTags,
102
85
  currentOrgDetails,
86
+ fetchingLiquidValidation,
103
87
  // TestAndPreviewSlidebox props
104
88
  showTestAndPreviewSlidebox: propsShowTestAndPreviewSlidebox,
105
89
  handleTestAndPreview: propsHandleTestAndPreview,
@@ -112,7 +96,7 @@ export const InApp = (props) => {
112
96
  const [templateMediaType, setTemplateMediaType] = useState(
113
97
  INAPP_MEDIA_TYPES.TEXT
114
98
  );
115
- const [tempName, setTempName] = useState("");
99
+ const [templateName, setTemplateName] = useState("");
116
100
  const [templateLayoutType, setTemplateLayoutType] = useState(
117
101
  INAPP_MESSAGE_LAYOUT_TYPES.MODAL
118
102
  );
@@ -126,25 +110,6 @@ export const InApp = (props) => {
126
110
  const [templateMessageErrorIos, setTemplateMessageErrorIos] = useState(false);
127
111
  const [templateTitleErrorAndroid, setTemplateTitleErrorAndroid] = useState(false);
128
112
  const [templateTitleErrorIos, setTemplateTitleErrorIos] = useState(false);
129
- // HTML Editor content state (for INAPP HTML variant)
130
- // Initialize HTML content from edit data if available
131
- const getInitialHtmlContent = (device) => {
132
- const editContent = isFullMode
133
- ? get(editData?.templateDetails?.versions, `base.content`, {})
134
- : get(templateData?.versions, `base.content`, {});
135
- const deviceContent = editContent?.[device];
136
- return deviceContent?.message || "";
137
- };
138
-
139
- const [htmlContentAndroid, setHtmlContentAndroid] = useState(() => getInitialHtmlContent("ANDROID"));
140
- const [htmlContentIos, setHtmlContentIos] = useState(() => getInitialHtmlContent("IOS"));
141
- // Track if this is an HTML template (type === HTML or style === BIG_HTML)
142
- const [isHTMLTemplate, setIsHTMLTemplate] = useState(false);
143
- // Version to force HTMLEditor remount on layout changes
144
- const [htmlEditorContentVersion, setHtmlEditorContentVersion] = useState(0);
145
- // Refs to store latest content before layout changes
146
- const htmlContentAndroidRef = useRef(htmlContentAndroid);
147
- const htmlContentIosRef = useRef(htmlContentIos);
148
113
  const [accountId, setAccountId] = useState("");
149
114
  const [accessToken, setAccessToken] = useState("");
150
115
  const [accountName, setAccountName] = useState("");
@@ -253,6 +218,9 @@ export const InApp = (props) => {
253
218
  }
254
219
  }, [propsHandleCloseTestAndPreview]);
255
220
  const { accessibleFeatures = [] } = currentOrgDetails || {};
221
+ const isAiContentBotDisabled = accessibleFeatures?.includes(
222
+ AI_CONTENT_BOT_DISABLED
223
+ );
256
224
  const [errorMessage, setErrorMessage] = useState({
257
225
  STANDARD_ERROR_MSG: {
258
226
  ANDROID: [],
@@ -280,7 +248,7 @@ export const InApp = (props) => {
280
248
  // DEVICE_SUPPORTED is '1', which indicates if the particular account is supported, and '0' if the devive is not supported
281
249
  //get deep link keys in an array
282
250
  const deepLinkKeys = Object.values(JSON.parse(deepLinkObj || '{}'));
283
- const keys = deepLinkKeys?.map((link) => ({ label: link.name, value: link.link, title: link.link }));
251
+ const keys = deepLinkKeys?.map((link) => ({label: link.name, value: link.link, title: link.link }));
284
252
  setPanes(isAndroidSupported ? ANDROID : IOS);
285
253
  setDeepLink(keys);
286
254
  setAccountId(sourceAccountIdentifier);
@@ -305,32 +273,6 @@ export const InApp = (props) => {
305
273
  };
306
274
  }, [paramObj.id]);
307
275
 
308
- // Initialize template name from defaultData (from wrapper) or editData/templateData
309
- useEffect(() => {
310
- const defaultTemplateName = defaultData?.['template-name'] || '';
311
- if (defaultTemplateName && !isEditFlow) {
312
- setTempName(defaultTemplateName);
313
- // Call showTemplateName callback if provided (for header display)
314
- if (isFullMode && showTemplateName && defaultTemplateName) {
315
- showTemplateName({
316
- formData: { 'template-name': defaultTemplateName },
317
- onFormDataChange: (updatedFormData) => {
318
- const newName = updatedFormData?.['template-name'] || '';
319
- setTempName(newName);
320
- if (showTemplateName) {
321
- showTemplateName({
322
- formData: { 'template-name': newName },
323
- onFormDataChange: (formData) => {
324
- setTempName(formData?.['template-name'] || '');
325
- },
326
- });
327
- }
328
- },
329
- });
330
- }
331
- }
332
- }, [defaultData?.['template-name'], showTemplateName, isEditFlow]);
333
-
334
276
  useEffect(() => {
335
277
  const {
336
278
  name = "",
@@ -338,48 +280,16 @@ export const InApp = (props) => {
338
280
  createdAt = "",
339
281
  } = isFullMode ? editData?.templateDetails || {} : templateData || {};
340
282
  editContent = get(versions, `base.content`, {});
341
-
342
- // LIBRARY MODE FIX: Backend stores bodyType as 'POPUP' but UI expects 'MODAL'
343
- // Convert back to MODAL for display
344
- if (!isFullMode && editContent) {
345
- if (editContent.ANDROID?.bodyType === INAPP_MESSAGE_LAYOUT_TYPES.POPUP) {
346
- editContent.ANDROID.bodyType = INAPP_MESSAGE_LAYOUT_TYPES.MODAL;
347
- }
348
- if (editContent.IOS?.bodyType === INAPP_MESSAGE_LAYOUT_TYPES.POPUP) {
349
- editContent.IOS.bodyType = INAPP_MESSAGE_LAYOUT_TYPES.MODAL;
350
- }
351
- }
352
-
353
283
  if (editContent && !isEmpty(editContent)) {
354
284
  setEditFlow(true);
355
- setTempName(name);
285
+ setTemplateName(name);
356
286
  setTemplateDate(createdAt);
357
287
  setTemplateLayoutType(editContent?.ANDROID?.bodyType);
358
- // Call showTemplateName callback when in edit mode + full mode to show template name header
359
- if (showTemplateName && name) {
360
- showTemplateName({
361
- formData: { 'template-name': name },
362
- onFormDataChange: (updatedFormData) => {
363
- const newName = updatedFormData?.['template-name'] || '';
364
- setTempName(newName);
365
- if (showTemplateName) {
366
- showTemplateName({
367
- formData: { 'template-name': newName },
368
- onFormDataChange: (formData) => {
369
- setTempName(formData?.['template-name'] || '');
370
- },
371
- });
372
- }
373
- },
374
- });
375
- }
376
288
  const androidContent = editContent?.ANDROID;
377
- let androidIsHTML = false;
378
289
  if (!isEmpty(androidContent)) {
379
290
  const {
380
291
  title: androidTitle = '',
381
292
  message: androidMessage = '',
382
- type: androidType = '',
383
293
  ctas: androidCta = {},
384
294
  expandableDetails: androidExpandableDetails = {},
385
295
  } = androidContent || {};
@@ -389,30 +299,12 @@ export const InApp = (props) => {
389
299
  ctas: androidCtas,
390
300
  } = androidExpandableDetails || {};
391
301
  const androidCtaLength = androidCtas?.length;
392
- // Check if this is a Bee editor template
393
- const isBEEeditor = get(androidContent, 'isBEEeditor');
394
- const isBeeFreeTemplate = !isEmpty(androidTitle) && androidTitle.toLowerCase() === 'bee free template';
395
- // Check if this is an HTML editor template (identified by special title)
396
- const isHTMLEditorTemplate = !isEmpty(androidTitle) && androidTitle.toLowerCase() === 'html editor template';
397
- // Check if this is an HTML template
398
- // Prioritize HTML editor template title, then check type/style
399
- // But exclude if it's a Bee editor template
400
- androidIsHTML = isHTMLEditorTemplate || ((androidType === INAPP_MEDIA_TYPES.HTML || androidStyle === BIG_HTML)
401
- && !isBEEeditor
402
- && !isBeeFreeTemplate);
403
- setIsHTMLTemplate(androidIsHTML);
404
-
405
302
  setTitleAndroid(androidTitle);
406
303
  setTemplateMessageAndroid(androidMessage);
407
- // Initialize HTML content for HTMLEditor if feature is enabled and it's an HTML template
408
- if (androidIsHTML) {
409
- setHtmlContentAndroid(androidMessage);
410
- }
411
304
  setTemplateMediaType(
412
- androidIsHTML ? INAPP_MEDIA_TYPES.HTML
413
- : androidStyle === BIG_TEXT
414
- ? INAPP_MEDIA_TYPES.TEXT
415
- : INAPP_MEDIA_TYPES.IMAGE
305
+ androidStyle === BIG_TEXT
306
+ ? INAPP_MEDIA_TYPES.TEXT
307
+ : INAPP_MEDIA_TYPES.IMAGE
416
308
  );
417
309
  setInAppImageSrcAndroid(androidImage);
418
310
  setDeepLinkValueAndroid(androidCta[0]?.actionLink || '');
@@ -429,7 +321,6 @@ export const InApp = (props) => {
429
321
  const {
430
322
  title: iosTitle = '',
431
323
  message: iosMessage = '',
432
- type: iosType = '',
433
324
  ctas: iosCta = {},
434
325
  expandableDetails: iosExpandableDetails = {},
435
326
  } = iosContent || {};
@@ -439,53 +330,9 @@ export const InApp = (props) => {
439
330
  ctas: iosCtas,
440
331
  } = iosExpandableDetails || {};
441
332
  const iosCtaLength = iosCtas?.length;
442
-
443
- // Check if this is an HTML template (if Android wasn't HTML, check iOS)
444
- // Note: androidIsHTML is in the outer scope from the Android content check above
445
- if (!androidIsHTML) {
446
- // Check if this is a Bee editor template
447
- const isBEEeditor = get(iosContent, 'isBEEeditor');
448
- const isBeeFreeTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template';
449
- // Check if this is an HTML editor template (identified by special title)
450
- const isHTMLEditorTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'html editor template';
451
- // Check if this is an HTML template
452
- // Prioritize HTML editor template title, then check type/style
453
- // But exclude if it's a Bee editor template
454
- const iosIsHTML = isHTMLEditorTemplate || ((iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
455
- && !isBEEeditor
456
- && !isBeeFreeTemplate);
457
- setIsHTMLTemplate(iosIsHTML);
458
- // Initialize HTML content for HTMLEditor if feature is enabled and it's an HTML template
459
- if (iosIsHTML) {
460
- setHtmlContentIos(iosMessage);
461
- }
462
- } else {
463
- // If Android is HTML, also initialize iOS HTML content if available
464
- if (androidIsHTML) {
465
- setHtmlContentIos(iosMessage);
466
- }
467
- }
468
-
469
333
  setTitleIos(iosTitle);
470
334
  setTemplateMessageIos(iosMessage);
471
- // Update templateMediaType if iOS is HTML and Android wasn't
472
- if (!androidIsHTML) {
473
- // Check if this is a Bee editor template
474
- const isBEEeditor = get(iosContent, 'isBEEeditor');
475
- const isBeeFreeTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template';
476
- // Check if this is an HTML editor template (identified by special title)
477
- const isHTMLEditorTemplate = !isEmpty(iosTitle) && iosTitle.toLowerCase() === 'html editor template';
478
- // Check if this is an HTML template
479
- // Prioritize HTML editor template title, then check type/style
480
- const iosIsHTML = isHTMLEditorTemplate || ((iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
481
- && !isBEEeditor
482
- && !isBeeFreeTemplate);
483
- if (iosIsHTML) {
484
- setTemplateMediaType(INAPP_MEDIA_TYPES.HTML);
485
- } else {
486
- setTemplateMediaType(iosStyle === BIG_TEXT ? INAPP_MEDIA_TYPES.TEXT : INAPP_MEDIA_TYPES.IMAGE);
487
- }
488
- }
335
+ setTemplateMediaType(iosStyle === BIG_TEXT ? INAPP_MEDIA_TYPES.TEXT : INAPP_MEDIA_TYPES.IMAGE);
489
336
  setInAppImageSrcIos(iosImage);
490
337
  setButtonTypeIos(iosCtaLength ? INAPP_BUTTON_TYPES.CTA : INAPP_BUTTON_TYPES.NONE);
491
338
  setAddActionLinkIos(!isEmpty(iosCta) && true);
@@ -494,47 +341,12 @@ export const InApp = (props) => {
494
341
  setCtaDataIos(getCtaObject(iosCtas));
495
342
  }
496
343
  }
497
- } else {
498
- // Explicitly set edit flow to false if there's no edit content
499
- setEditFlow(false);
500
344
  }
501
345
  }, [editData.templateDetails || templateData]);
502
346
 
503
- // Extract editor type from defaultData for stable reference
504
- const editorType = useMemo(() => {
505
- const type = defaultData?.['editor-type'];
506
- return type;
507
- }, [defaultData]);
508
-
509
- // Separate effect for handling editor type from wrapper in create mode
510
- useEffect(() => {
511
- // Only process editor type if we're not in edit flow
512
- if (!isEditFlow) {
513
- if (editorType === INAPP_EDITOR_TYPES.HTML_EDITOR) {
514
- setIsHTMLTemplate(true);
515
- setTemplateMediaType(INAPP_MEDIA_TYPES.HTML);
516
- } else if (editorType === INAPP_EDITOR_TYPES.DRAG_DROP_EDITOR) {
517
- setIsHTMLTemplate(false);
518
- } else if (!editorType) {
519
- // If no editor type is set yet, ensure we start with false
520
- setIsHTMLTemplate(false);
521
- }
522
- }
523
- }, [editorType, isEditFlow]);
524
-
525
347
  // tag Code start from here
526
348
  useEffect(() => {
527
- // Reset tags fetch ref when switching between HTML and legacy editors
528
- tagsFetchedRef.current = false;
529
-
530
- // Only fetch tags if HTML Editor is not being used (legacy flow)
531
- // For HTML Editor, tags will be fetched via handleOnTagsContextChange
532
- if (isHTMLTemplate) {
533
- // HTML Editor will handle tag fetching via onContextChange
534
- return;
535
- }
536
-
537
- //fetching tags for legacy editor
349
+ //fetching tags
538
350
  const { type, module } = location.query || {};
539
351
  const isEmbedded = type === EMBEDDED;
540
352
  const context = isEmbedded ? module : DEFAULT;
@@ -549,7 +361,7 @@ export const InApp = (props) => {
549
361
  query.context = getDefaultTags;
550
362
  }
551
363
  globalActions.fetchSchemaForEntity(query);
552
- }, [isHTMLTemplate]);
364
+ }, []);
553
365
 
554
366
  useEffect(() => {
555
367
  let tag = get(metaEntities, `tags.standard`, []);
@@ -560,17 +372,7 @@ export const InApp = (props) => {
560
372
  updateTags(tag);
561
373
  }, [metaEntities]);
562
374
 
563
- // Track if we've already fetched tags to prevent duplicate calls
564
- const tagsFetchedRef = React.useRef(false);
565
-
566
- const handleOnTagsContextChange = useCallback((data) => {
567
- // This function is called when TagList needs to fetch tags
568
- // It triggers the API call to /meta/TAG endpoint via fetchSchemaForEntity
569
- // Only fetch if we haven't already fetched for this context
570
- if (tagsFetchedRef.current) {
571
- return;
572
- }
573
-
375
+ const handleOnTagsContextChange = (data) => {
574
376
  const { type } = location.query || {};
575
377
  const tempData = (data || '').toLowerCase();
576
378
  const isEmbedded = type === EMBEDDED;
@@ -582,192 +384,259 @@ export const InApp = (props) => {
582
384
  context,
583
385
  embedded,
584
386
  };
585
- // Mark as fetched to prevent duplicate calls
586
- tagsFetchedRef.current = true;
587
- // Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
588
- // The API endpoint will be: /meta/TAG?query={...}
589
387
  globalActions.fetchSchemaForEntity(query);
590
- }, [location.query, globalActions]);
388
+ };
591
389
 
390
+ const templateDescErrorHandler = (value) => {
391
+ let errorMessage = false;
392
+ const { unsupportedTags, isBraceError } = validateTags({
393
+ content: value,
394
+ tagsParam: tags,
395
+ injectedTagsParams: injectedTags,
396
+ location,
397
+ tagModule: getDefaultTags,
398
+ }) || {};
399
+ if (value === '' && INAPP_MEDIA_TYPES.NONE) {
400
+ errorMessage = formatMessage(messages.emptyTemplateDescErrorMessage);
401
+ } else if (unsupportedTags?.length > 0) {
402
+ errorMessage = formatMessage(
403
+ globalMessages.unsupportedTagsValidationError,
404
+ {
405
+ unsupportedTags,
406
+ },
407
+ );
408
+ }
409
+ if (isBraceError) {
410
+ errorMessage = formatMessage(globalMessages.unbalanacedCurlyBraces);
411
+ }
412
+ return errorMessage;
413
+ };
592
414
 
415
+ const onTagSelect = (data, id) => {
416
+ if (id === 0) {
417
+ const tempTitle = `${panes === ANDROID ? titleAndroid : titleIos}{{${data}}}`;
418
+ if (panes === ANDROID) {
419
+ setTitleAndroid(tempTitle);
420
+ } else {
421
+ setTitleIos(tempTitle);
422
+ }
423
+ } else {
424
+ const tempMsg = `${panes === ANDROID ? templateMessageAndroid : templateMessageIos}{{${data}}}`;
425
+ const error = templateDescErrorHandler(tempMsg);
426
+ if (panes === ANDROID) {
427
+ setTemplateMessageAndroid(tempMsg);
428
+ setTemplateMessageErrorAndroid(error);
429
+ } else {
430
+ setTemplateMessageIos(tempMsg);
431
+ setTemplateMessageErrorIos(error);
432
+ }
433
+ }
434
+ };
593
435
  // tag Code end
594
436
 
437
+ const onTemplateNameChange = ({ target: { value } }) => {
438
+ setTemplateName(value);
439
+ };
440
+
595
441
  const onTemplateLayoutTypeChange = ({ target: { value } }) => {
596
- // Update layout type and force HTMLEditor to remount with latest content
597
- // Increment version to force remount - this ensures editor displays current content
598
442
  setTemplateLayoutType(value);
599
- setHtmlEditorContentVersion((prev) => prev + 1);
600
443
  };
601
444
 
445
+ const onCopyTitleAndContent = () => {
446
+ if (panes === ANDROID) {
447
+ setTitleAndroid(titleIos);
448
+ setTemplateMessageAndroid(templateMessageIos);
449
+ } else {
450
+ setTitleIos(titleAndroid);
451
+ setTemplateMessageIos(templateMessageAndroid);
452
+ }
453
+ };
602
454
 
455
+ const PANES = [
456
+ {
457
+ content: (
458
+ <CapDeviceContent
459
+ intl={intl}
460
+ location={location}
461
+ injectedTags={injectIntl}
462
+ selectedOfferDetails={selectedOfferDetails}
463
+ panes={panes}
464
+ actions={actions}
465
+ editData={editData}
466
+ isFullMode={isFullMode}
467
+ inAppImageSrc={inAppImageSrcAndroid}
468
+ setInAppImageSrc={setInAppImageSrcAndroid}
469
+ isEditFlow={isEditFlow}
470
+ ctaData={ctaDataAndroid}
471
+ setCtaData={setCtaDataAndroid}
472
+ buttonType={buttonTypeAndroid}
473
+ setButtonType={setButtonTypeAndroid}
474
+ accountId={accountId}
475
+ accessToken={accessToken}
476
+ templateMediaType={templateMediaType}
477
+ setTemplateMediaType={setTemplateMediaType}
478
+ title={titleAndroid}
479
+ setTitle={setTitleAndroid}
480
+ templateMessageError={templateMessageErrorAndroid}
481
+ templateMessage={templateMessageAndroid}
482
+ setTemplateMessage={setTemplateMessageAndroid}
483
+ setTemplateMessageError={setTemplateMessageErrorAndroid}
484
+ templateTitleError={templateTitleErrorAndroid}
485
+ setTemplateTitleError={setTemplateTitleErrorAndroid}
486
+ addActionLink={addActionLinkAndroid}
487
+ setAddActionLink={setAddActionLinkAndroid}
488
+ deepLink={deepLink}
489
+ deepLinkValue={deepLinkValueAndroid}
490
+ setDeepLinkValue={setDeepLinkValueAndroid}
491
+ onCopyTitleAndContent={onCopyTitleAndContent}
492
+ tags={tags}
493
+ onTagSelect={onTagSelect}
494
+ handleOnTagsContextChange={handleOnTagsContextChange}
495
+ templateDescErrorHandler={templateDescErrorHandler}
496
+ isAiContentBotDisabled={isAiContentBotDisabled}
497
+ />
498
+ ),
499
+ tab: <FormattedMessage {...messages.Android} />,
500
+ key: ANDROID,
501
+ isSupported: get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED,
502
+ // DEVICE_SUPPORTED is '1', which indicates if the particular account is supported, and '0' if the devive is not supported
503
+ },
504
+ {
505
+ content: (
506
+ <CapDeviceContent
507
+ intl={intl}
508
+ location={location}
509
+ injectedTags={injectIntl}
510
+ selectedOfferDetails={selectedOfferDetails}
511
+ panes={panes}
512
+ actions={actions}
513
+ editData={editData}
514
+ isFullMode={isFullMode}
515
+ inAppImageSrc={inAppImageSrcIos}
516
+ setInAppImageSrc={setInAppImageSrcIos}
517
+ isEditFlow={isEditFlow}
518
+ ctaData={ctaDataIos}
519
+ setCtaData={setCtaDataIos}
520
+ buttonType={buttonTypeIos}
521
+ setButtonType={setButtonTypeIos}
522
+ accountId={accountId}
523
+ accessToken={accessToken}
524
+ templateMediaType={templateMediaType}
525
+ setTemplateMediaType={setTemplateMediaType}
526
+ title={titleIos}
527
+ setTitle={setTitleIos}
528
+ templateMessageError={templateMessageErrorIos}
529
+ templateMessage={templateMessageIos}
530
+ setTemplateMessage={setTemplateMessageIos}
531
+ setTemplateMessageError={setTemplateMessageErrorIos}
532
+ templateTitleError={templateTitleErrorIos}
533
+ setTemplateTitleError={setTemplateTitleErrorIos}
534
+ addActionLink={addActionLinkIos}
535
+ setAddActionLink={setAddActionLinkIos}
536
+ deepLink={deepLink}
537
+ deepLinkValue={deepLinkValueIos}
538
+ setDeepLinkValue={setDeepLinkValueIos}
539
+ onCopyTitleAndContent={onCopyTitleAndContent}
540
+ tags={tags}
541
+ onTagSelect={onTagSelect}
542
+ handleOnTagsContextChange={handleOnTagsContextChange}
543
+ templateDescErrorHandler={templateDescErrorHandler}
544
+ isAiContentBotDisabled={isAiContentBotDisabled}
545
+ />
546
+ ),
547
+ tab: <FormattedMessage {...messages.Ios} />,
548
+ key: IOS,
549
+ isSupported: get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED,
550
+ },
551
+ ];
552
+
553
+ const createModeContent = (
554
+ <CapRow>
555
+ {/* template name */}
556
+ <CapHeading type="h4">
557
+ <FormattedMessage {...messages.creativeName} />
558
+ </CapHeading>
559
+ <CapInput
560
+ id="inapp-template-name-input"
561
+ className="inapp-template-name-input"
562
+ onChange={onTemplateNameChange}
563
+ placeholder={formatMessage(globalMessages.templateNamePlaceholder)}
564
+ value={templateName}
565
+ size="default"
566
+ />
567
+ </CapRow>
568
+ );
603
569
  //create methods end
604
570
 
605
571
  //used by create and edit
606
-
607
- const isDisableDone = (device) => {
608
- const isIosDevice = device === IOS || device === IOS_CAPITAL;
609
- const isAndroidDevice = device === ANDROID;
610
- const isNoDevice = device === null || device === undefined;
611
-
612
- // Check for validation errors first - if there are errors, disable the button
613
- if (hasAnyErrors(errorMessage)) {
614
- return true;
615
- }
616
-
617
- // For HTMLEditor: only validate HTML content (when it's an HTML template)
618
- if (isHTMLTemplate) {
619
- // Get account-level device support restrictions
620
- const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
621
- const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
622
-
623
- // Check if devices have content - ensure content is a string before calling trim
624
- let hasAndroidContent = htmlContentAndroid && typeof htmlContentAndroid === 'string' && htmlContentAndroid?.trim() !== '';
625
- let hasIosContent = htmlContentIos && typeof htmlContentIos === 'string' && htmlContentIos?.trim() !== '';
626
-
627
- // LIBRARY MODE FIX: In library mode edit, htmlContent states might not be set yet
628
- // because HTMLEditor loads content via initialContent prop, not via onContentChange
629
- // Fallback to checking template data content
630
- if (!hasAndroidContent && isEditFlow && !isFullMode && templateData) {
631
- const androidTemplateContent = get(templateData, 'versions.base.content.ANDROID.message')
632
- || get(templateData, 'versions.base.content.ANDROID.beeHtml.value');
633
- hasAndroidContent = androidTemplateContent && androidTemplateContent.trim() !== '';
634
- }
635
- if (!hasIosContent && isEditFlow && !isFullMode && templateData) {
636
- const iosTemplateContent = get(templateData, 'versions.base.content.IOS.message')
637
- || get(templateData, 'versions.base.content.IOS.beeHtml.value');
638
- hasIosContent = iosTemplateContent && iosTemplateContent.trim() !== '';
572
+ const getPreviewSection = () => {
573
+ const templateTitle = panes === ANDROID ? titleAndroid : titleIos;
574
+ const templateMsg = panes === ANDROID ? templateMessageAndroid : templateMessageIos;
575
+ const mediaPreview = {};
576
+ let ctaData = {};
577
+ if (panes === ANDROID) {
578
+ ctaData = ctaDataAndroid;
579
+ switch (templateMediaType) {
580
+ case INAPP_MEDIA_TYPES.IMAGE:
581
+ mediaPreview.inAppImageSrcAndroid = inAppImageSrcAndroid;
582
+ break;
583
+ default:
584
+ break;
639
585
  }
640
-
641
- // If checking specific device, validate that device's content
642
- if (isAndroidDevice) {
643
- // Only validate Android if it's supported by account
644
- // But in library mode edit, check template data too
645
- if (androidSupported || (isEditFlow && !isFullMode && hasAndroidContent)) {
646
- if (!hasAndroidContent) {
647
- return true;
648
- }
649
- return false;
650
- }
651
- // Android not supported and no content in template - skip validation
652
- return false;
653
- }
654
- if (isIosDevice) {
655
- // Only validate iOS if it's supported by account
656
- // But in library mode edit, check template data too
657
- if (iosSupported || (isEditFlow && !isFullMode && hasIosContent)) {
658
- if (!hasIosContent) {
659
- return true;
660
- }
661
- return false;
662
- }
663
- // iOS not supported and no content in template - skip validation
664
- return false;
665
- }
666
-
667
- // If no specific device, check if at least one supported device has content
668
- // Users can create templates with content in Android-only, iOS-only, or both devices
669
- // Even when both devices are supported, user can create template with content in just one device
670
- if (androidSupported && iosSupported) {
671
- // Both devices supported - user can create template with content in Android, iOS, or both
672
- // Only disable if NEITHER device has content
673
- if (!hasAndroidContent && !hasIosContent) {
674
- return true;
675
- }
676
- // At least one device has content - enable Done button
677
- return false;
678
- }
679
- if (androidSupported) {
680
- // Only Android supported - require Android content
681
- if (!hasAndroidContent) {
682
- return true;
683
- }
684
- // Android has content - enable Done button
685
- return false;
686
- }
687
- if (iosSupported) {
688
- // Only iOS supported - require iOS content
689
- if (!hasIosContent) {
690
- return true;
691
- }
692
- // iOS has content - enable Done button
693
- return false;
586
+ } else {
587
+ ctaData = ctaDataIos;
588
+ switch (templateMediaType) {
589
+ case INAPP_MEDIA_TYPES.IMAGE:
590
+ mediaPreview.inAppImageSrcIos = inAppImageSrcIos;
591
+ break;
592
+ default:
593
+ break;
694
594
  }
695
- // Neither device supported - this shouldn't happen, but handle gracefully
696
- return true;
697
595
  }
596
+ return (
597
+ <UnifiedPreview
598
+ channel={INAPP}
599
+ content={{
600
+ inAppPreviewContent: {
601
+ mediaPreview,
602
+ templateTitle,
603
+ templateMsg,
604
+ ...((isBtnTypeCtaAndroid || isBtnTypeCTaIos) && {
605
+ ctaData,
606
+ }),
607
+ },
608
+ templateLayoutType,
609
+ }}
610
+ device={panes}
611
+ showDeviceToggle={false}
612
+ showHeader={false}
613
+ formatMessage={formatMessage}
614
+ />
615
+ );
616
+ };
698
617
 
699
- // Legacy flow validation (when HTMLEditor is not enabled)
700
- // Get account-level device support
701
- const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
702
- const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
703
-
704
- // If no tags are available (e.g., in tests), allow submission even with minimal content
705
- // This is to support test scenarios where full content validation might not be set up
706
- const hasTags = tags && tags.length > 0;
707
- const isTestScenario = !hasTags;
708
-
709
- // If no device specified, check if at least one supported device has valid content
710
- if (isNoDevice) {
711
- // In test scenarios, only require message content (title is optional)
712
- // In production, require both title and message
713
- const hasAndroidContent = androidSupported && templateMessageAndroid && templateMessageAndroid?.trim() !== '' && !templateMessageErrorAndroid && (isTestScenario || (titleAndroid && titleAndroid?.trim() !== '' && !templateTitleErrorAndroid));
714
- const hasIosContent = iosSupported && templateMessageIos && templateMessageIos?.trim() !== '' && !templateMessageErrorIos && (isTestScenario || (titleIos && titleIos?.trim() !== '' && !templateTitleErrorIos));
715
-
716
- // Check media requirements
717
- const androidMediaValid = !androidSupported || (templateMediaType === INAPP_MEDIA_TYPES.TEXT || inAppImageSrcAndroid !== '');
718
- const iosMediaValid = !iosSupported || (templateMediaType === INAPP_MEDIA_TYPES.TEXT || inAppImageSrcIos !== '');
719
-
720
- // Check CTA requirements
721
- const androidCtaValid = !isBtnTypeCtaAndroid || (ctaDataAndroid[0]?.isSaved);
722
- const iosCtaValid = !isBtnTypeCTaIos || (ctaDataIos[0]?.isSaved);
723
-
724
- // Check action link requirements
725
- const androidActionLinkValid = !addActionLinkAndroid || deepLinkValueAndroid;
726
- const iosActionLinkValid = !addActionLinkIos || deepLinkValueIos;
727
-
728
- // If both devices are supported, at least one should have valid content
729
- if (androidSupported && iosSupported) {
730
- const androidValid = hasAndroidContent && androidMediaValid && androidCtaValid && androidActionLinkValid;
731
- const iosValid = hasIosContent && iosMediaValid && iosCtaValid && iosActionLinkValid;
732
- return !(androidValid || iosValid);
733
- }
734
-
735
- // If only Android is supported, it must have valid content
736
- if (androidSupported) {
737
- return !(hasAndroidContent && androidMediaValid && androidCtaValid && androidActionLinkValid);
738
- }
739
-
740
- // If only iOS is supported, it must have valid content
741
- if (iosSupported) {
742
- return !(hasIosContent && iosMediaValid && iosCtaValid && iosActionLinkValid);
743
- }
744
-
745
- // Neither device supported - disable
618
+ const isDisableDone = (device) => {
619
+ const isIosDevice = device === IOS;
620
+ const isAndroidDevice = device === ANDROID;
621
+ //if template name is not entered
622
+ if (isFullMode && templateName.trim() === '') {
746
623
  return true;
747
624
  }
748
-
749
625
  //if template message is not entered
750
626
  //for android
751
- if (isAndroidDevice) {
752
- const androidMessage = templateMessageAndroid;
753
- if (androidMessage?.trim() === '' || templateMessageErrorAndroid) {
754
- return true;
755
- }
756
- }
757
- //for ios
758
- if (isIosDevice) {
759
- const iosMessage = templateMessageIos;
760
- if (iosMessage?.trim() === '' || templateMessageErrorIos) {
761
- return true;
762
- }
627
+ if (isAndroidDevice && (templateMessageAndroid.trim() === '' || templateMessageErrorAndroid)) {
628
+ return true;
763
629
  }
630
+ if (isIosDevice && (templateMessageIos.trim() === '' || templateMessageErrorIos)) {
631
+ return true;
632
+ }//for ios
764
633
 
765
634
  //if template title is not entered
766
635
  //for android
767
- if (isAndroidDevice && (titleAndroid?.trim() === '' || templateTitleErrorAndroid)) {
636
+ if (isAndroidDevice && (titleAndroid.trim() === '' || templateTitleErrorAndroid)) {
768
637
  return true;
769
638
  }
770
- if (isIosDevice && (titleIos?.trim() === '' || templateTitleErrorIos)) {
639
+ if (isIosDevice && (titleIos.trim() === '' || templateTitleErrorIos)) {
771
640
  return true;
772
641
  }//for ios
773
642
  //if media type is image and the mediaType file is not uploaded
@@ -826,11 +695,11 @@ export const InApp = (props) => {
826
695
  switch (templateMediaType) {
827
696
  case INAPP_MEDIA_TYPES.IMAGE:
828
697
  androidMediaParams = {
829
- image: getCdnUrl({ url: inAppImageSrcAndroid, channelName: INAPP }),
698
+ image: getCdnUrl({url: inAppImageSrcAndroid, channelName: INAPP }),
830
699
  style: BIG_PICTURE,
831
700
  };
832
701
  iosMediaParams = {
833
- image: getCdnUrl({ url: inAppImageSrcIos, channelName: INAPP }),
702
+ image: getCdnUrl({url: inAppImageSrcIos, channelName: INAPP }),
834
703
  style: BIG_PICTURE,
835
704
  };
836
705
  break;
@@ -842,46 +711,16 @@ export const InApp = (props) => {
842
711
  sourceAccountIdentifier = "",
843
712
  id,
844
713
  } = accountObj;
845
-
846
- // Use HTML content if HTMLEditor is enabled and it's an HTML template, otherwise use regular message
847
- const androidMessage = isHTMLTemplate && htmlContentAndroid
848
- ? htmlContentAndroid
849
- : templateMessageAndroid;
850
-
851
- // Determine type and style for Android
852
- const androidType = isHTMLTemplate ? INAPP_MEDIA_TYPES.HTML : templateMediaType;
853
- const androidExpandableStyle = isHTMLTemplate ? BIG_HTML : BIG_TEXT;
854
-
855
- // LIBRARY MODE FIX: Backend doesn't support 'MODAL' bodyType, convert to 'POPUP'
856
- const bodyTypeForBackend = templateLayoutType === INAPP_MESSAGE_LAYOUT_TYPES.MODAL ? INAPP_MESSAGE_LAYOUT_TYPES.POPUP : templateLayoutType;
857
-
858
- // Check account-level device support
859
- const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
860
- const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
861
-
862
- // Check if devices have content (for HTML Editor)
863
- const hasAndroidContent = htmlContentAndroid && htmlContentAndroid?.trim() !== '';
864
- const hasIosContent = htmlContentIos && htmlContentIos?.trim() !== '';
865
-
866
- // For HTML Editor, check if device has content and is supported
867
- // For legacy editor, use isDisableDone check
868
- // Only include devices that have content - allows Android-only, iOS-only, or both
869
- const shouldIncludeAndroid = isHTMLTemplate
870
- ? (androidSupported && hasAndroidContent)
871
- : !isDisableDone(ANDROID);
872
-
873
- // Construct Android content if device is supported and has content
874
- // Even when both devices are supported, only include devices that have content
875
- const androidContent = shouldIncludeAndroid ? {
714
+
715
+ // Construct Android content if not disabled
716
+ const androidContent = !isDisableDone(ANDROID) ? {
876
717
  ...commonDevicePayload,
877
- type: androidType,
878
- // Use 'html editor template' as title for HTML editor to differentiate from BEE editor
879
- title: isHTMLTemplate ? 'html editor template' : titleAndroid,
880
- message: androidMessage,
718
+ title: titleAndroid,
719
+ message: templateMessageAndroid,
881
720
  bodyType: templateLayoutType,
882
721
  expandableDetails: {
883
- style: androidExpandableStyle,
884
- message: androidMessage,
722
+ style: BIG_TEXT,
723
+ message: templateMessageAndroid,
885
724
  ...androidMediaParams,
886
725
  ...(isBtnTypeCtaAndroid && {
887
726
  ctas: getCtaPayload(ANDROID),
@@ -889,38 +728,19 @@ export const InApp = (props) => {
889
728
  },
890
729
  custom: [],
891
730
  ...(deepLinkValueAndroid && {
892
- ctas: [{ type: DEEP_LINK, actionLink: deepLinkValueAndroid }],
731
+ ctas: [{type: DEEP_LINK, actionLink: deepLinkValueAndroid}],
893
732
  }),
894
733
  } : {};
895
-
896
- // Use HTML content if HTMLEditor is enabled and it's an HTML template, otherwise use regular message
897
- const iosMessage = isHTMLTemplate && htmlContentIos
898
- ? htmlContentIos
899
- : templateMessageIos;
900
-
901
- // Determine type and style for iOS
902
- const iosType = isHTMLTemplate ? INAPP_MEDIA_TYPES.HTML : templateMediaType;
903
- const iosExpandableStyle = isHTMLTemplate ? BIG_HTML : BIG_TEXT;
904
-
905
- // For HTML Editor, check if device has content and is supported
906
- // For legacy editor, use isDisableDone check
907
- // Only include devices that have content - allows Android-only, iOS-only, or both
908
- const shouldIncludeIos = isHTMLTemplate
909
- ? (iosSupported && hasIosContent)
910
- : !isDisableDone(IOS);
911
-
912
- // Construct iOS content if device is supported and has content
913
- // Even when both devices are supported, only include devices that have content
914
- const iosContent = shouldIncludeIos ? {
734
+
735
+ // Construct iOS content if not disabled
736
+ const iosContent = !isDisableDone(IOS) ? {
915
737
  ...commonDevicePayload,
916
- type: iosType,
917
- // Use 'html editor template' as title for HTML editor to differentiate from BEE editor
918
- title: isHTMLTemplate ? 'html editor template' : titleIos,
919
- message: iosMessage,
920
- bodyType: bodyTypeForBackend,
738
+ title: titleIos,
739
+ message: templateMessageIos,
740
+ bodyType: templateLayoutType,
921
741
  expandableDetails: {
922
- style: iosExpandableStyle,
923
- message: iosMessage,
742
+ style: BIG_TEXT,
743
+ message: templateMessageIos,
924
744
  ...iosMediaParams,
925
745
  ...(isBtnTypeCTaIos && {
926
746
  ctas: getCtaPayload(IOS),
@@ -928,16 +748,12 @@ export const InApp = (props) => {
928
748
  },
929
749
  custom: [],
930
750
  ...(deepLinkValueIos && {
931
- ctas: [{ type: DEEP_LINK, actionLink: deepLinkValueIos }],
751
+ ctas: [{type: DEEP_LINK, actionLink: deepLinkValueIos}],
932
752
  }),
933
753
  } : {};
934
-
935
- // Ensure name is always set - use tempName as fallback if templateName is empty
936
- const templateNameValue = isEditFlow ? tempName : (templateName || tempName || '');
937
- const trimmedName = (templateNameValue && typeof templateNameValue === 'string') ? templateNameValue?.trim() : '';
938
-
754
+
939
755
  const data = {
940
- name: trimmedName,
756
+ name: templateName,
941
757
  versions: {
942
758
  base: {
943
759
  content: {
@@ -956,26 +772,28 @@ export const InApp = (props) => {
956
772
  return data;
957
773
  };
958
774
 
959
- const actionCallback = ({ errorMsg }) => {
960
- if (!errorMsg) {
775
+ const actionCallback = ({ errorMessage }) => {
776
+ if (!errorMessage) {
961
777
  CapNotification.success({
962
- message: isEditFlow
963
- ? formatMessage(messages.inAppEditNotification)
964
- : formatMessage(messages.inAppCreateNotification),
778
+ message: isEditFlow ? formatMessage(messages.inAppEditNotification, {
779
+ name: templateName,
780
+ }) : formatMessage(messages.inAppCreateNotification, {
781
+ name: templateName,
782
+ }),
965
783
  });
966
784
  actions.clearCreateResponse();
967
785
  } else {
968
786
  CapNotification.error({
969
- message: JSON.stringify(errorMsg),
787
+ message: JSON.stringify(errorMessage),
970
788
  });
971
789
  }
972
790
  };
973
791
 
974
792
  const onCreateInApp = () => {
975
793
  setSpin(true);
976
- actions.createInAppTemplate(createPayload(), (resp, errorMsg) => {
977
- actionCallback({ resp, errorMsg });
978
- if (!errorMsg) {
794
+ actions.createInAppTemplate(createPayload(), (resp, errorMessage) => {
795
+ actionCallback({ resp, errorMessage });
796
+ if (!errorMessage) {
979
797
  onCreateComplete();
980
798
  } else {
981
799
  setSpin(false);
@@ -990,9 +808,9 @@ export const InApp = (props) => {
990
808
  ...createPayload(),
991
809
  _id: params.id,
992
810
  },
993
- (resp, errorMsg) => {
994
- actionCallback({ resp, errorMsg });
995
- if (!errorMsg) {
811
+ (resp, errorMessage) => {
812
+ actionCallback({ resp, errorMessage });
813
+ if (!errorMessage) {
996
814
  onCreateComplete();
997
815
  } else {
998
816
  setSpin(false);
@@ -1001,67 +819,6 @@ export const InApp = (props) => {
1001
819
  );
1002
820
  };
1003
821
 
1004
- // Handle HTML content changes from HTMLEditor
1005
- const handleHtmlContentChange = useCallback((deviceContent, changedDevice) => {
1006
- // The onChange callback from useInAppContent passes the full deviceContent object
1007
- // But we only want to update the state for the device that actually changed
1008
- // Use the second parameter (changedDevice) if provided, otherwise update both
1009
-
1010
- // Clear validation errors when content changes (similar to Bee Editor)
1011
- // This ensures Done button re-enables after user fixes errors
1012
- if (changedDevice) {
1013
- setErrorMessage((prev) => ({
1014
- STANDARD_ERROR_MSG: {
1015
- ...prev.STANDARD_ERROR_MSG,
1016
- [changedDevice.toUpperCase()]: [],
1017
- GENERIC: [],
1018
- },
1019
- LIQUID_ERROR_MSG: {
1020
- ...prev.LIQUID_ERROR_MSG,
1021
- [changedDevice.toUpperCase()]: [],
1022
- GENERIC: [],
1023
- },
1024
- }));
1025
- }
1026
-
1027
- if (changedDevice) {
1028
- // Only update the device that actually changed
1029
- if (changedDevice.toUpperCase() === ANDROID && deviceContent?.android !== undefined) {
1030
- setHtmlContentAndroid(deviceContent.android || '');
1031
- } else if (changedDevice.toUpperCase() === IOS_CAPITAL && deviceContent?.ios !== undefined) {
1032
- setHtmlContentIos(deviceContent.ios || '');
1033
- }
1034
- } else {
1035
- // Fallback: update both if changedDevice not provided (for backward compatibility)
1036
- // Only update if value actually changed to avoid unnecessary re-renders
1037
- if (deviceContent?.android !== undefined) {
1038
- setHtmlContentAndroid((prev) => {
1039
- const newValue = deviceContent.android || '';
1040
- return prev !== newValue ? newValue : prev;
1041
- });
1042
- }
1043
- if (deviceContent?.ios !== undefined) {
1044
- setHtmlContentIos((prev) => {
1045
- const newValue = deviceContent.ios || '';
1046
- return prev !== newValue ? newValue : prev;
1047
- });
1048
- }
1049
- }
1050
- }, [ANDROID, IOS, setErrorMessage]);
1051
-
1052
- // Handle HTML save from HTMLEditor
1053
- const handleHtmlSave = useCallback((deviceContent) => {
1054
- // Update state for both devices if present in the callback
1055
- if (deviceContent?.android !== undefined) {
1056
- setHtmlContentAndroid(deviceContent.android || '');
1057
- }
1058
- if (deviceContent?.ios !== undefined) {
1059
- setHtmlContentIos(deviceContent.ios || '');
1060
- }
1061
- // Auto-save can trigger this, but we don't want to submit automatically
1062
- // The user will click the Create/Update button to submit
1063
- }, []);
1064
-
1065
822
  const onDoneCallback = () => {
1066
823
  if (isFullMode) {
1067
824
  if (isEditFlow) {
@@ -1069,17 +826,15 @@ export const InApp = (props) => {
1069
826
  }
1070
827
  return onCreateInApp();
1071
828
  }
1072
- const payload = createPayload();
1073
- getFormData({
1074
- value: payload,
829
+ return getFormData({
830
+ value: createPayload(),
1075
831
  _id: params && params.id,
1076
832
  validity: true,
1077
833
  type: INAPP,
1078
834
  });
1079
835
  };
1080
836
 
1081
- // Validation middleware for tag validation (both liquid and non-liquid flow)
1082
- const validationMiddleWare = async () => {
837
+ const liquidMiddleWare = () => {
1083
838
  // Set up callbacks for validation results
1084
839
  const onError = ({ standardErrors, liquidErrors }) => {
1085
840
  setErrorMessage((prev) => ({
@@ -1092,200 +847,45 @@ export const InApp = (props) => {
1092
847
  onDoneCallback();
1093
848
  };
1094
849
 
1095
- // Skip validation if no tags are available (e.g., in tests or when tags haven't loaded)
1096
- const hasTags = tags && tags.length > 0;
1097
-
1098
- // For liquid flow, use validateInAppContent
1099
- if (isLiquidFlow && hasTags) {
1100
- // Validate the INAPP content
1101
- const payload = createPayload();
1102
- validateInAppContent(payload, {
1103
- currentTab: panes === ANDROID ? 1 : 2, // Convert ANDROID/IOS to tab numbers
1104
- onError,
1105
- onSuccess,
1106
- getLiquidTags: (content, callback) => globalActions.getLiquidTags(content, callback),
1107
- formatMessage,
1108
- messages: formBuilderMessages,
1109
- tagLookupMap: metaEntities?.tagLookupMap || {},
1110
- eventContextTags: metaEntities?.eventContextTags || [],
1111
- isLiquidFlow,
1112
- forwardedTags: {},
1113
- skipTags: (tag) => {
1114
- // Skip certain tags if needed
1115
- const skipRegexes = [
1116
- /dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
1117
- /unsubscribe\(#[a-zA-Z\d]{6}\)/,
1118
- /Link_to_[a-zA-z]/,
1119
- /SURVEY.*\.TOKEN/,
1120
- /^[A-Za-z].*\([a-zA-Z\d]*\)/,
1121
- ];
1122
-
1123
- return skipRegexes.some((regex) => regex.test(tag));
1124
- },
1125
- singleTab: getSingleTab(accountData),
1126
- });
1127
- } else if (hasTags) {
1128
- // For non-liquid flow, validate tags using validateTags (only if tags are available)
1129
- const androidContent = htmlContentAndroid || '';
1130
- const iosContent = htmlContentIos || '';
1131
-
1132
- const androidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
1133
- const iosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
1134
-
1135
- let hasErrors = false;
1136
- const newErrors = {
1137
- STANDARD_ERROR_MSG: {
1138
- ANDROID: [],
1139
- IOS: [],
1140
- GENERIC: [],
1141
- },
1142
- LIQUID_ERROR_MSG: {
1143
- ANDROID: [],
1144
- IOS: [],
1145
- GENERIC: [],
1146
- },
1147
- };
1148
-
1149
- // Validate Android content
1150
- if (androidSupported && androidContent && androidContent?.trim() !== '') {
1151
- const validationResponse = validateTags({
1152
- content: androidContent,
1153
- tagsParam: tags,
1154
- injectedTagsParams: injectedTags || {},
1155
- location,
1156
- tagModule: getDefaultTags,
1157
- eventContextTags: metaEntities?.eventContextTags || [],
1158
- }) || {};
1159
-
1160
- if (validationResponse?.unsupportedTags?.length > 0) {
1161
- hasErrors = true;
1162
- newErrors.LIQUID_ERROR_MSG.ANDROID.push(
1163
- formatMessage(globalMessages.unsupportedTagsValidationError, {
1164
- unsupportedTags: validationResponse.unsupportedTags.join(", "),
1165
- })
1166
- );
1167
- }
1168
- if (validationResponse?.isBraceError) {
1169
- hasErrors = true;
1170
- newErrors.LIQUID_ERROR_MSG.ANDROID.push(
1171
- formatMessage(globalMessages.unbalanacedCurlyBraces)
1172
- );
1173
- }
1174
- }
1175
-
1176
- // Validate iOS content
1177
- if (iosSupported && iosContent && iosContent?.trim() !== '') {
1178
- const validationResponse = validateTags({
1179
- content: iosContent,
1180
- tagsParam: tags,
1181
- injectedTagsParams: injectedTags || {},
1182
- location,
1183
- tagModule: getDefaultTags,
1184
- eventContextTags: metaEntities?.eventContextTags || [],
1185
- }) || {};
1186
-
1187
- if (validationResponse?.unsupportedTags?.length > 0) {
1188
- hasErrors = true;
1189
- newErrors.LIQUID_ERROR_MSG.IOS.push(
1190
- formatMessage(globalMessages.unsupportedTagsValidationError, {
1191
- unsupportedTags: validationResponse.unsupportedTags.join(", "),
1192
- })
1193
- );
1194
- }
1195
- if (validationResponse?.isBraceError) {
1196
- hasErrors = true;
1197
- newErrors.LIQUID_ERROR_MSG.IOS.push(
1198
- formatMessage(globalMessages.unbalanacedCurlyBraces)
1199
- );
1200
- }
1201
- }
1202
-
1203
- if (hasErrors) {
1204
- setErrorMessage(newErrors);
1205
- } else {
1206
- // No errors, proceed with submission
1207
- onSuccess();
1208
- }
1209
- } else {
1210
- // No tags available, skip validation and proceed directly
1211
- onSuccess();
1212
- }
850
+ // Validate the INAPP content
851
+ const payload = createPayload();
852
+ validateInAppContent(payload, {
853
+ currentTab: panes === ANDROID ? 1 : 2, // Convert ANDROID/IOS to tab numbers
854
+ onError,
855
+ onSuccess,
856
+ getLiquidTags: (content, callback) => globalActions.getLiquidTags(content, callback),
857
+ formatMessage,
858
+ messages: formBuilderMessages,
859
+ tagLookupMap: metaEntities?.tagLookupMap || {},
860
+ eventContextTags: metaEntities?.eventContextTags || [],
861
+ isLiquidFlow,
862
+ forwardedTags: {},
863
+ skipTags: (tag) => {
864
+ // Skip certain tags if needed
865
+ const skipRegexes = [
866
+ /dynamic_expiry_date_after_\d+_days\.FORMAT_\d/,
867
+ /unsubscribe\(#[a-zA-Z\d]{6}\)/,
868
+ /Link_to_[a-zA-z]/,
869
+ /SURVEY.*\.TOKEN/,
870
+ /^[A-Za-z].*\([a-zA-Z\d]*\)/,
871
+ ];
872
+
873
+ return skipRegexes.some((regex) => regex.test(tag));
874
+ },
875
+ singleTab: getSingleTab(accountData),
876
+ });
1213
877
  };
1214
878
 
1215
879
  const isLiquidFlow = hasLiquidSupportFeature();
1216
-
1217
- // Check template data to determine editor type (for render decision)
1218
- const templateDetails = isFullMode ? editData?.templateDetails : templateData;
1219
- const versions = templateDetails?.versions || {};
1220
- const templateContent = get(versions, 'base.content', {});
1221
- const androidContent = templateContent?.ANDROID || {};
1222
- const iosContent = templateContent?.IOS || {};
1223
-
1224
- // Check if this is a Bee editor template
1225
- const isBEEeditor = get(androidContent, 'isBEEeditor') || get(iosContent, 'isBEEeditor');
1226
- const androidTitle = androidContent?.title || '';
1227
- const iosTitle = iosContent?.title || '';
1228
- const isBeeFreeTemplate = (!isEmpty(androidTitle) && androidTitle.toLowerCase() === 'bee free template')
1229
- || (!isEmpty(iosTitle) && iosTitle.toLowerCase() === 'bee free template');
1230
-
1231
- // Check if this is an HTML template from content data
1232
- const androidType = androidContent?.type || '';
1233
- const androidStyle = androidContent?.expandableDetails?.style || '';
1234
- const iosType = iosContent?.type || '';
1235
- const iosStyle = iosContent?.expandableDetails?.style || '';
1236
- const isHTMLTemplateFromData = (androidType === INAPP_MEDIA_TYPES.HTML || androidStyle === BIG_HTML
1237
- || iosType === INAPP_MEDIA_TYPES.HTML || iosStyle === BIG_HTML)
1238
- && !isBEEeditor
1239
- && !isBeeFreeTemplate;
1240
-
1241
- // Use state if available, otherwise fall back to direct data check
1242
- const shouldUseHTMLEditor = isHTMLTemplate || isHTMLTemplateFromData;
1243
-
1244
- // Only route to Bee editor if it's explicitly a Bee editor AND not an HTML template
1245
- const shouldUseBeeEditor = (isBEEeditor || isBeeFreeTemplate) && !shouldUseHTMLEditor;
1246
-
1247
- // Early returns to avoid nested ternary
1248
- if (isEditInApp && getTemplateDetailsInProgress) {
1249
- return <CapSpin spinning={getTemplateDetailsInProgress} />;
1250
- }
1251
-
1252
- // Allow BEE editor in both full mode edit (isEditInApp) AND library mode edit (isEditFlow && !isFullMode)
1253
- if ((isEditInApp || (isEditFlow && !isFullMode)) && shouldUseBeeEditor) {
1254
- return (
1255
- <InappAdvanced
1256
- getFormData={getFormData}
1257
- setIsLoadingContent={setIsLoadingContent}
1258
- defaultData={{ "template-name": tempName }}
1259
- location={{
1260
- pathname: `/inapp/edit`,
1261
- query,
1262
- search: '',
1263
- }}
1264
- params={{ mode: inAppCreateMode, id: params.id }}
1265
- isFullMode={isFullMode}
1266
- isGetFormData={isGetFormData}
1267
- showTemplateName={showTemplateName}
1268
- route={{ name: "inapp" }}
1269
- getDefaultTags={type}
1270
- onValidationFail={onValidationFail}
1271
- templateData={templateData}
1272
- templateName={tempName}
1273
- setTemplateName={setTempName}
1274
- forwardedTags={forwardedTags}
1275
- selectedOfferDetails={selectedOfferDetails}
1276
- onCreateComplete={onCreateComplete}
1277
- />
1278
- );
1279
- }
1280
-
1281
880
  return (
1282
881
  <CapSpin spinning={spin || fetchingLiquidValidation} tip={fetchingLiquidValidation ? <FormattedMessage {...formBuilderMessages.liquidSpinText} /> : ""}>
1283
882
  <CapRow className="cap-inapp-creatives">
1284
- <CapColumn span={shouldUseHTMLEditor ? 18 : 24}>
1285
- {/* Creative layout type */}
1286
- {shouldUseHTMLEditor && (
883
+ <CapColumn span={14}>
884
+ {isFullMode && createModeContent}
885
+ {/* Creative layout type*/}
886
+ {(isFullMode || !isEditFlow) && (
1287
887
  <>
1288
- <CapRow>
888
+ <CapRow className="inapp-creative-layout">
1289
889
  <CapHeading type="h4">
1290
890
  <FormattedMessage {...messages.creativeLayout} />
1291
891
  </CapHeading>
@@ -1302,64 +902,26 @@ export const InApp = (props) => {
1302
902
  />
1303
903
  </>
1304
904
  )}
1305
- {shouldUseHTMLEditor ? (
1306
- <HTMLEditor
1307
- key={`inapp-html-editor-v${htmlEditorContentVersion}`}
1308
- variant={HTML_EDITOR_VARIANTS.INAPP}
1309
- layoutType={templateLayoutType}
1310
- initialContent={{
1311
- android: htmlContentAndroidRef.current || htmlContentAndroid || templateMessageAndroid || '',
1312
- ios: htmlContentIosRef.current || htmlContentIos || templateMessageIos || '',
1313
- }}
1314
- onContentChange={handleHtmlContentChange}
1315
- onSave={handleHtmlSave}
1316
- tags={tags}
1317
- injectedTags={injectedTags}
1318
- location={location}
1319
- selectedOfferDetails={selectedOfferDetails}
1320
- onTagSelect={() => {
1321
- // Tag insertion is handled by HTMLEditor's CodeEditorPane at cursor position
1322
- // Content updates will be propagated via onContentChange callback
1323
- }}
1324
- // Don't pass globalActions to prevent duplicate API calls
1325
- // HTMLEditor will use onContextChange instead
1326
- onContextChange={handleOnTagsContextChange}
1327
- // Pass validation errors to HTMLEditor for display
1328
- errors={errorMessage}
1329
- isFullMode={isFullMode}
1330
- data-test="inapp-html-editor"
1331
- style={{ width: '138%' }}
1332
- />
1333
- ) : (
1334
- <InappAdvanced
1335
- getFormData={getFormData}
1336
- setIsLoadingContent={setIsLoadingContent}
1337
- defaultData={{ "template-name": tempName }}
1338
- location={{
1339
- pathname: `/inapp/create`,
1340
- query,
1341
- search: '',
1342
- }}
1343
- params={{ mode: inAppCreateMode }}
1344
- isFullMode={isFullMode}
1345
- isGetFormData={isGetFormData}
1346
- showTemplateName={showTemplateName}
1347
- route={{ name: "inapp" }}
1348
- getDefaultTags={type}
1349
- onValidationFail={onValidationFail}
1350
- templateData={templateData}
1351
- templateName={tempName}
1352
- setTemplateName={setTempName}
1353
- forwardedTags={forwardedTags}
1354
- selectedOfferDetails={selectedOfferDetails}
1355
- onCreateComplete={onCreateComplete}
1356
- />
1357
- )}
905
+ {/* device tab */}
906
+ <CapTab
907
+ panes={PANES.filter(
908
+ (devicePane) => devicePane?.isSupported === true
909
+ )}
910
+ onChange={(value) => {
911
+ setPanes(value);
912
+ }}
913
+ activeKey={panes}
914
+ defaultActiveKey={panes}
915
+ className="inapp-template-device-tab"
916
+ />
917
+ <div className="inapp-scroll-div" />
918
+ </CapColumn>
919
+ <CapColumn span={10} className="inapp-preview-container">
920
+ {getPreviewSection()}
1358
921
  </CapColumn>
1359
922
  </CapRow>
1360
- {/* Footer with Done/Update button - Only show for HTML editor, bee editor has its own footer */}
1361
- {
1362
- shouldUseHTMLEditor && (
923
+ <div className={`inapp-footer ${!isFullMode && `inapp-footer-lib`}`}>
924
+ {
1363
925
  <>
1364
926
  {hasAnyErrors(errorMessage) && (
1365
927
  <ErrorInfoNote
@@ -1367,28 +929,24 @@ export const InApp = (props) => {
1367
929
  currentTab={panes}
1368
930
  />
1369
931
  )}
1370
- <div className={`inapp-footer ${!isFullMode && `inapp-footer-lib`}`}>
1371
- <CapButton
1372
- onClick={validationMiddleWare}
1373
- disabled={shouldUseHTMLEditor ? isDisableDone(null) : isDisableDone(panes)}
1374
- className="inapp-create-btn"
1375
- >
1376
- {(() => {
1377
- if (isEditFlow) {
1378
- return isFullMode ? (
1379
- <FormattedMessage {...messages.update} />
1380
- ) : (
1381
- <FormattedMessage {...globalMessages.done} />
1382
- );
1383
- }
1384
- return isFullMode ? (
1385
- <FormattedMessage {...messages.create} />
1386
- ) : (
1387
- <FormattedMessage {...globalMessages.done} />
1388
- );
1389
- })()}
1390
- </CapButton>
1391
- {/* {!isFullMode && ( */}
932
+ <CapButton
933
+ onClick={isLiquidFlow ? liquidMiddleWare : onDoneCallback}
934
+ disabled={isDisableDone(panes)}
935
+ className="inapp-create-btn"
936
+ >
937
+ {isEditFlow ? (
938
+ isFullMode ? (
939
+ <FormattedMessage {...messages.update} />
940
+ ) : (
941
+ <FormattedMessage {...globalMessages.done} />
942
+ )
943
+ ) : isFullMode ? (
944
+ <FormattedMessage {...messages.create} />
945
+ ) : (
946
+ <FormattedMessage {...globalMessages.done} />
947
+ )}
948
+ </CapButton>
949
+ {/* {!isFullMode && ( */}
1392
950
  <CapButton
1393
951
  onClick={handleTestAndPreview}
1394
952
  className="inapp-test-preview-btn"
@@ -1397,10 +955,10 @@ export const InApp = (props) => {
1397
955
  >
1398
956
  <FormattedMessage {...creativesMessages.testAndPreview} />
1399
957
  </CapButton>
1400
- {/* )} */}
1401
- </div>
958
+ {/* )} */}
1402
959
  </>
1403
- )}
960
+ }
961
+ </div>
1404
962
  <TestAndPreviewSlidebox
1405
963
  show={propsShowTestAndPreviewSlidebox || showTestAndPreviewSlidebox}
1406
964
  onClose={handleCloseTestAndPreview}
@@ -1420,8 +978,7 @@ const mapStateToProps = createStructuredSelector({
1420
978
  loadingTags: isLoadingMetaEntities(),
1421
979
  injectedTags: setInjectedTags(),
1422
980
  currentOrgDetails: selectCurrentOrgDetails(),
1423
- fetchingLiquidValidation: selectLiquidStateDetails(),
1424
- getTemplateDetailsInProgress: makeSelectGetTemplateDetailsInProgress(),
981
+ fetchingLiquidValidation: selectLiquidStateDetails()
1425
982
  });
1426
983
 
1427
984
  const mapDispatchToProps = (dispatch) => ({