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