@capillarytech/creatives-library 8.0.242-alpha.0 → 8.0.242-alpha.2

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 (216) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/sagas/__tests__/assetPolling.test.js +74 -3
  7. package/sagas/assetPolling.js +8 -1
  8. package/services/api.js +10 -5
  9. package/services/tests/api.test.js +18 -0
  10. package/translations/en.json +0 -1
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +14 -1
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/utils/transformerUtils.js +0 -42
  16. package/v2Components/CapDeviceContent/index.js +61 -56
  17. package/v2Components/CapImageUpload/constants.js +0 -2
  18. package/v2Components/CapImageUpload/index.js +14 -54
  19. package/v2Components/CapImageUpload/index.scss +1 -4
  20. package/v2Components/CapImageUpload/messages.js +0 -4
  21. package/v2Components/CapTagList/index.js +6 -1
  22. package/v2Components/CapTagListWithInput/index.js +5 -1
  23. package/v2Components/CapTagListWithInput/messages.js +1 -1
  24. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  25. package/v2Components/ErrorInfoNote/index.js +412 -72
  26. package/v2Components/ErrorInfoNote/messages.js +22 -0
  27. package/v2Components/ErrorInfoNote/style.scss +279 -2
  28. package/v2Components/HtmlEditor/HTMLEditor.js +217 -90
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1132 -133
  30. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
  31. package/v2Components/HtmlEditor/_htmlEditor.scss +15 -23
  32. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  33. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  34. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  35. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  36. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  37. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -0
  38. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  39. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +1 -0
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  45. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  46. package/v2Components/HtmlEditor/components/PreviewPane/index.js +10 -11
  47. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  48. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +87 -62
  49. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  50. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  51. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +362 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  53. package/v2Components/HtmlEditor/constants.js +29 -20
  54. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  55. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  56. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  57. package/v2Components/HtmlEditor/index.js +1 -1
  58. package/v2Components/HtmlEditor/messages.js +95 -85
  59. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +99 -101
  60. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  61. package/v2Components/HtmlEditor/utils/validationAdapter.js +34 -41
  62. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  63. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  64. package/v2Components/TemplatePreview/index.js +47 -32
  65. package/v2Components/TemplatePreview/messages.js +4 -0
  66. package/v2Components/TestAndPreviewSlidebox/index.js +31 -25
  67. package/v2Containers/App/constants.js +0 -5
  68. package/v2Containers/BeeEditor/index.js +82 -80
  69. package/v2Containers/BeePopupEditor/constants.js +10 -0
  70. package/v2Containers/BeePopupEditor/index.js +193 -0
  71. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  72. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +0 -1
  73. package/v2Containers/CreativesContainer/SlideBoxContent.js +148 -120
  74. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  75. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  76. package/v2Containers/CreativesContainer/constants.js +1 -2
  77. package/v2Containers/CreativesContainer/index.js +173 -193
  78. package/v2Containers/CreativesContainer/messages.js +4 -4
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  80. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +36 -0
  81. package/v2Containers/Email/actions.js +7 -0
  82. package/v2Containers/Email/constants.js +5 -1
  83. package/v2Containers/Email/index.js +13 -0
  84. package/v2Containers/Email/messages.js +32 -0
  85. package/v2Containers/Email/reducer.js +12 -1
  86. package/v2Containers/Email/sagas.js +41 -6
  87. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  88. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1046 -0
  89. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  90. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  91. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  92. package/v2Containers/EmailWrapper/constants.js +2 -0
  93. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +436 -67
  94. package/v2Containers/EmailWrapper/index.js +99 -23
  95. package/v2Containers/EmailWrapper/messages.js +61 -1
  96. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +26 -1
  97. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +111 -77
  98. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  99. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  100. package/v2Containers/InApp/actions.js +7 -0
  101. package/v2Containers/InApp/constants.js +20 -4
  102. package/v2Containers/InApp/index.js +800 -357
  103. package/v2Containers/InApp/index.scss +4 -3
  104. package/v2Containers/InApp/messages.js +7 -3
  105. package/v2Containers/InApp/reducer.js +21 -3
  106. package/v2Containers/InApp/sagas.js +29 -9
  107. package/v2Containers/InApp/selectors.js +25 -5
  108. package/v2Containers/InApp/tests/index.test.js +154 -50
  109. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  110. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  111. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  112. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  113. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  114. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  115. package/v2Containers/InAppWrapper/constants.js +16 -0
  116. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  117. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  118. package/v2Containers/InAppWrapper/index.js +148 -0
  119. package/v2Containers/InAppWrapper/messages.js +49 -0
  120. package/v2Containers/InappAdvance/index.js +1099 -0
  121. package/v2Containers/InappAdvance/index.scss +10 -0
  122. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  123. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
  124. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
  125. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
  126. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
  127. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
  128. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
  129. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
  130. package/v2Containers/TagList/index.js +67 -1
  131. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  132. package/v2Containers/Templates/_templates.scss +56 -200
  133. package/v2Containers/Templates/actions.js +1 -2
  134. package/v2Containers/Templates/constants.js +0 -1
  135. package/v2Containers/Templates/index.js +124 -277
  136. package/v2Containers/Templates/messages.js +4 -24
  137. package/v2Containers/Templates/reducer.js +0 -2
  138. package/v2Containers/Templates/tests/index.test.js +0 -10
  139. package/v2Containers/TemplatesV2/index.js +2 -3
  140. package/v2Containers/TemplatesV2/messages.js +0 -4
  141. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
  142. package/v2Components/CapImageUrlUpload/constants.js +0 -19
  143. package/v2Components/CapImageUrlUpload/index.js +0 -455
  144. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  145. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  146. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -175
  147. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  148. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -144
  149. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  150. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  151. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  152. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  153. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  154. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  155. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -80
  156. package/v2Containers/WebPush/Create/index.js +0 -1755
  157. package/v2Containers/WebPush/Create/index.scss +0 -123
  158. package/v2Containers/WebPush/Create/messages.js +0 -199
  159. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -241
  160. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -290
  161. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -81
  162. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -240
  163. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
  164. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -144
  165. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  166. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  167. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  168. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  169. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  170. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  171. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  172. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  173. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -44
  174. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -110
  175. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  176. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -72
  177. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -55
  178. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -70
  179. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -512
  180. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -77
  181. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -527
  182. package/v2Containers/WebPush/Create/preview/constants.js +0 -162
  183. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -104
  184. package/v2Containers/WebPush/Create/preview/preview.scss +0 -409
  185. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -300
  186. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  187. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  188. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  189. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -303
  190. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  191. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  192. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  193. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -188
  194. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -106
  195. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  196. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -75
  197. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -174
  198. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  199. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1077
  200. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  201. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -943
  202. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -128
  203. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -121
  204. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  205. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -127
  206. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -116
  207. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  208. package/v2Containers/WebPush/actions.js +0 -60
  209. package/v2Containers/WebPush/constants.js +0 -108
  210. package/v2Containers/WebPush/index.js +0 -2
  211. package/v2Containers/WebPush/reducer.js +0 -104
  212. package/v2Containers/WebPush/sagas.js +0 -119
  213. package/v2Containers/WebPush/selectors.js +0 -65
  214. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  215. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  216. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -12,14 +12,14 @@
12
12
  * Note: Uses injectIntl with forwardRef to provide direct access to CodeEditorPane via ref
13
13
  */
14
14
 
15
- import React, { useRef, useCallback, useMemo, useState } from 'react';
15
+ import React, {
16
+ useRef, useCallback, useMemo, useState, useEffect,
17
+ } from 'react';
16
18
  import PropTypes from 'prop-types';
17
- import { Layout } from 'antd'; // Fallback - no Cap UI equivalent
18
19
  import { injectIntl, intlShape } from 'react-intl';
19
20
 
20
21
  // Cap UI Components (First Preference)
21
22
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
22
- import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
23
23
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
24
24
  import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
25
25
  import CapModal from '@capillarytech/cap-ui-library/CapModal';
@@ -40,7 +40,9 @@ import { useLayoutState } from './hooks/useLayoutState';
40
40
  import { useValidation } from './hooks/useValidation';
41
41
 
42
42
  // Constants
43
- import { HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT } from './constants';
43
+ import {
44
+ HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT, TAG, EMBEDDED, DEFAULT, FULL, ALL, SMS, EMAIL,
45
+ } from './constants';
44
46
 
45
47
  // Styles
46
48
  import './_htmlEditor.scss';
@@ -61,6 +63,21 @@ const HTMLEditor = ({
61
63
  showFullscreenButton = true,
62
64
  autoSave = true,
63
65
  autoSaveInterval = 30000, // 30 seconds
66
+ // Tag-related props - tags are fetched and managed by parent component (EmailHTMLEditor, INAPP, etc.)
67
+ tags = [],
68
+ injectedTags = {},
69
+ location,
70
+ eventContextTags = [],
71
+ selectedOfferDetails = [],
72
+ channel,
73
+ userLocale = 'en',
74
+ moduleFilterEnabled = true,
75
+ onTagContextChange, // Parent component handles tag fetching
76
+ onTagSelect = null,
77
+ onContextChange = null,
78
+ globalActions = null,
79
+ isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
80
+ isFullMode = true, // Full mode vs library mode - controls layout and visibility
64
81
  ...props
65
82
  }) => {
66
83
  // Separate refs for main and modal editors to avoid conflicts
@@ -69,9 +86,7 @@ const HTMLEditor = ({
69
86
  const [isFullscreenModalOpen, setIsFullscreenModalOpen] = useState(false);
70
87
 
71
88
  // Get the currently active editor ref based on fullscreen state
72
- const getActiveEditorRef = useCallback(() => {
73
- return isFullscreenModalOpen ? modalEditorRef : mainEditorRef;
74
- }, [isFullscreenModalOpen]);
89
+ const getActiveEditorRef = useCallback(() => isFullscreenModalOpen ? modalEditorRef : mainEditorRef, [isFullscreenModalOpen]);
75
90
 
76
91
  // Initialize custom hooks for state management - always call both hooks to follow Rules of Hooks
77
92
  const isEmailVariant = variant === HTML_EDITOR_VARIANTS.EMAIL;
@@ -80,7 +95,7 @@ const HTMLEditor = ({
80
95
  autoSave: isEmailVariant ? autoSave : false,
81
96
  autoSaveInterval,
82
97
  onSave: isEmailVariant ? onSave : null,
83
- onChange: isEmailVariant ? onContentChange : null
98
+ onChange: isEmailVariant ? onContentChange : null,
84
99
  };
85
100
 
86
101
  const emailContent = useEditorContent(
@@ -97,7 +112,7 @@ const HTMLEditor = ({
97
112
  // Convert string content to device-specific format
98
113
  inAppInitialContent = {
99
114
  [DEVICE_TYPES.ANDROID]: initialContent,
100
- [DEVICE_TYPES.IOS]: initialContent
115
+ [DEVICE_TYPES.IOS]: initialContent,
101
116
  };
102
117
  } else {
103
118
  // Use provided device-specific content
@@ -109,7 +124,7 @@ const HTMLEditor = ({
109
124
  autoSave: isInAppVariant ? autoSave : false,
110
125
  autoSaveInterval,
111
126
  onSave: isInAppVariant ? onSave : null,
112
- onChange: isInAppVariant ? onContentChange : null
127
+ onChange: isInAppVariant ? onContentChange : null,
113
128
  };
114
129
 
115
130
  const inAppContent = useInAppContent(inAppInitialContent, inAppOptions);
@@ -117,6 +132,64 @@ const HTMLEditor = ({
117
132
  // Use appropriate content hook based on variant
118
133
  const content = variant === HTML_EDITOR_VARIANTS.EMAIL ? emailContent : inAppContent;
119
134
 
135
+ // Update content when initialContent prop changes (for edit mode)
136
+ // This ensures the editor updates when template data loads
137
+ useEffect(() => {
138
+ if (isEmailVariant && emailContent && initialContent !== undefined && initialContent !== null) {
139
+ // Only update if content is different to avoid unnecessary updates
140
+ if (emailContent.content !== initialContent) {
141
+ emailContent.updateContent(initialContent, true); // immediate update
142
+ }
143
+ } else if (isInAppVariant && inAppContent && initialContent !== undefined && initialContent !== null) {
144
+ // Handle InApp variant updates
145
+ const contentToUpdate = typeof initialContent === 'string'
146
+ ? { [DEVICE_TYPES.ANDROID]: initialContent, [DEVICE_TYPES.IOS]: initialContent }
147
+ : initialContent;
148
+ if (inAppContent.updateContent) {
149
+ const currentContent = inAppContent.getDeviceContent?.(inAppContent.activeDevice);
150
+ const newContent = contentToUpdate[inAppContent.activeDevice] || contentToUpdate[DEVICE_TYPES.ANDROID] || '';
151
+ if (currentContent !== newContent) {
152
+ inAppContent.updateContent(newContent, true);
153
+ }
154
+ }
155
+ }
156
+ }, [initialContent, isEmailVariant, isInAppVariant]);
157
+ // Handle context change for tag API calls
158
+ // If variant is INAPP, use SMS layout; otherwise use the channel (EMAIL)
159
+ const handleContextChange = useCallback((contextData) => {
160
+ // If onContextChange is provided, use it instead of making our own API call
161
+ // This prevents duplicate API calls when parent component handles tag fetching
162
+ if (onContextChange) {
163
+ onContextChange(contextData);
164
+ return;
165
+ }
166
+
167
+ // Only make API call if onContextChange is not provided and globalActions is available
168
+ if (!globalActions || !location) {
169
+ return;
170
+ }
171
+
172
+ const { type } = location.query || {};
173
+ const tempData = (contextData || '').toLowerCase();
174
+ const isEmbedded = type === EMBEDDED;
175
+ const embedded = isEmbedded ? type : FULL;
176
+ const context = tempData === ALL ? DEFAULT : tempData;
177
+
178
+ // Determine layout: INAPP variant uses SMS, EMAIL variant uses EMAIL
179
+ const layout = variant === HTML_EDITOR_VARIANTS.INAPP ? SMS : EMAIL;
180
+
181
+ const query = {
182
+ layout,
183
+ type: TAG,
184
+ context,
185
+ embedded,
186
+ };
187
+
188
+ // Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
189
+ // The API endpoint will be: /meta/TAG?query={...}
190
+ globalActions.fetchSchemaForEntity(query);
191
+ }, [variant, globalActions, location, onContextChange]);
192
+
120
193
  // Destructure content properties for cleaner access throughout component
121
194
  const {
122
195
  activeDevice,
@@ -124,14 +197,14 @@ const HTMLEditor = ({
124
197
  switchDevice,
125
198
  toggleContentSync,
126
199
  getDeviceContent,
127
- markAsSaved
200
+ markAsSaved,
128
201
  } = content || {};
129
202
 
130
203
  const layout = useLayoutState({
131
204
  splitSizes: [50, 50],
132
205
  viewMode: 'desktop',
133
206
  mobileWidth: 375,
134
- isFullscreen: false
207
+ isFullscreen: false,
135
208
  });
136
209
 
137
210
  // Get current content for validation based on variant
@@ -158,7 +231,7 @@ const HTMLEditor = ({
158
231
  'sanitizer.productionValidHtml': messages.sanitizer.productionValidHtml,
159
232
  'sanitizer.productionSanitized': messages.sanitizer.productionSanitized,
160
233
  'sanitizer.productionInlineCss': messages.sanitizer.productionInlineCss,
161
- 'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent
234
+ 'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent,
162
235
  };
163
236
 
164
237
  const messageObj = messageMap[messageKey];
@@ -179,7 +252,7 @@ const HTMLEditor = ({
179
252
  'validator.largeImageDetected': messages.validator.largeImageDetected,
180
253
  'validator.unclosedCssRule': messages.validator.unclosedCssRule,
181
254
  'validator.emptyCssRule': messages.validator.emptyCssRule,
182
- 'validator.cssValidationFailed': messages.validator.cssValidationFailed
255
+ 'validator.cssValidationFailed': messages.validator.cssValidationFailed,
183
256
  };
184
257
 
185
258
  const messageObj = messageMap[messageKey];
@@ -190,57 +263,69 @@ const HTMLEditor = ({
190
263
  enableRealTime: true,
191
264
  debounceMs: 500,
192
265
  enableSanitization: true,
193
- securityLevel: 'standard'
266
+ securityLevel: 'standard',
194
267
  }, formatSanitizerMessage, formatValidatorMessage);
195
268
 
196
269
  // Handle label insertion at cursor position
270
+ // Note: This is called for notification purposes only when tag is inserted via CodeEditorPane
271
+ // The actual insertion happens in CodeEditorPane.handleTagSelect
197
272
  const handleLabelInsert = useCallback((label, position) => {
198
- // With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
199
- const activeEditorRef = getActiveEditorRef();
200
- const editor = activeEditorRef.current;
201
-
202
- if (!editor) {
203
- CapNotification.warning({
204
- message: intl.formatMessage(messages.labelInsertError),
205
- description: intl.formatMessage(messages.editorNotReady),
206
- duration: 3
207
- });
208
- return;
209
- }
210
-
211
- // Check if the required methods exist
212
- if (typeof editor?.insertText !== 'function') {
213
- CapNotification.error({
214
- message: intl.formatMessage(messages.labelInsertError),
215
- description: intl.formatMessage(messages.editorMethodNotAvailable),
216
- duration: 4
217
- });
218
- return;
219
- }
220
-
221
- try {
222
- // Get current cursor position or use provided position
223
- const cursor = position !== undefined
224
- ? position
225
- : (typeof editor?.getCursor === 'function' ? editor.getCursor() : 0);
226
-
227
- // Insert label at cursor position
228
- editor.insertText(label, cursor);
273
+ // If position is explicitly null, it means the editor wasn't ready when tag was selected
274
+ // In this case, CodeEditorPane couldn't insert the tag, so we should try here
275
+ if (position === null) {
276
+ // With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
277
+ const activeEditorRef = getActiveEditorRef();
278
+ const editor = activeEditorRef.current;
279
+
280
+ if (!editor) {
281
+ CapNotification.warning({
282
+ message: intl.formatMessage(messages.labelInsertError),
283
+ description: intl.formatMessage(messages.editorNotReady),
284
+ duration: 3,
285
+ });
286
+ return;
287
+ }
229
288
 
230
- // Focus the editor if focus method is available
231
- editor?.focus?.();
289
+ // Check if the required methods exist
290
+ if (typeof editor?.insertText !== 'function') {
291
+ CapNotification.error({
292
+ message: intl.formatMessage(messages.labelInsertError),
293
+ description: intl.formatMessage(messages.editorMethodNotAvailable),
294
+ duration: 4,
295
+ });
296
+ return;
297
+ }
232
298
 
233
- // Show success notification
299
+ try {
300
+ // Get current cursor position
301
+ const cursor = typeof editor?.getCursor === 'function' ? editor.getCursor() : 0;
302
+
303
+ // Insert label at cursor position
304
+ editor.insertText(label, cursor);
305
+
306
+ // Focus the editor if focus method is available
307
+ editor?.focus?.();
308
+
309
+ // Show success notification
310
+ CapNotification.success({
311
+ message: intl.formatMessage(messages.labelInserted),
312
+ description: intl.formatMessage(messages.labelInsertedDescription, { label }),
313
+ duration: 2,
314
+ });
315
+ } catch (error) {
316
+ CapNotification.error({
317
+ message: intl.formatMessage(messages.labelInsertError),
318
+ description: error.message,
319
+ duration: 4,
320
+ });
321
+ }
322
+ } else {
323
+ // Tag was already inserted by CodeEditorPane (position is a valid number)
324
+ // Just show success notification - no need to access editor
234
325
  CapNotification.success({
235
326
  message: intl.formatMessage(messages.labelInserted),
236
327
  description: intl.formatMessage(messages.labelInsertedDescription, { label }),
237
- duration: 2
238
- });
239
- } catch (error) {
240
- CapNotification.error({
241
- message: intl.formatMessage(messages.labelInsertError),
242
- description: error.message,
243
- duration: 4
328
+ duration: 2,
244
329
  });
245
330
  }
246
331
  }, [intl, getActiveEditorRef]);
@@ -259,13 +344,13 @@ const HTMLEditor = ({
259
344
 
260
345
  CapNotification.success({
261
346
  message: intl.formatMessage(messages.contentSaved),
262
- duration: 2
347
+ duration: 2,
263
348
  });
264
349
  } catch (error) {
265
350
  CapNotification.error({
266
351
  message: intl.formatMessage(messages.saveError),
267
352
  description: error.message,
268
- duration: 4
353
+ duration: 4,
269
354
  });
270
355
  }
271
356
  }, [content, onSave, intl, markAsSaved]);
@@ -307,6 +392,7 @@ const HTMLEditor = ({
307
392
  content,
308
393
  layout,
309
394
  validation,
395
+ isLiquidEnabled,
310
396
  editorRef: getActiveEditorRef(),
311
397
  handleLabelInsert,
312
398
  handleSave,
@@ -319,13 +405,14 @@ const HTMLEditor = ({
319
405
  switchDevice,
320
406
  toggleContentSync,
321
407
  getDeviceContent,
322
- layoutType
323
- })
408
+ layoutType,
409
+ }),
324
410
  }), [
325
411
  variant,
326
412
  content,
327
413
  layout,
328
414
  validation,
415
+ isLiquidEnabled,
329
416
  getActiveEditorRef,
330
417
  handleLabelInsert,
331
418
  handleSave,
@@ -336,7 +423,7 @@ const HTMLEditor = ({
336
423
  switchDevice,
337
424
  toggleContentSync,
338
425
  getDeviceContent,
339
- layoutType
426
+ layoutType,
340
427
  ]);
341
428
 
342
429
  // Loading state
@@ -348,9 +435,12 @@ const HTMLEditor = ({
348
435
  );
349
436
  }
350
437
 
438
+ // Add library-mode class when not in full mode
439
+ const editorClassName = `html-editor html-editor--${variant} ${!isFullMode ? 'html-editor--library-mode' : ''} ${className}`;
440
+
351
441
  return (
352
442
  <EditorProvider value={contextValue}>
353
- <div className={`html-editor html-editor--${variant} ${className}`} {...props}>
443
+ <div className={editorClassName} {...props}>
354
444
  {/* Editor Toolbar - Conditional based on variant */}
355
445
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
356
446
  <EditorToolbar
@@ -387,19 +477,23 @@ const HTMLEditor = ({
387
477
  ref={mainEditorRef}
388
478
  readOnly={readOnly}
389
479
  onLabelInsert={handleLabelInsert}
480
+ onErrorClick={handleValidationErrorClick}
481
+ tags={tags}
482
+ injectedTags={injectedTags}
483
+ location={location}
484
+ eventContextTags={eventContextTags}
485
+ selectedOfferDetails={selectedOfferDetails}
486
+ channel={channel}
487
+ userLocale={userLocale}
488
+ moduleFilterEnabled={moduleFilterEnabled}
489
+ onTagContextChange={onTagContextChange}
490
+ onTagSelect={onTagSelect}
491
+ onContextChange={handleContextChange}
390
492
  />
391
493
 
392
494
  {/* Preview Pane */}
393
495
  <PreviewPane />
394
496
  </SplitContainer>
395
-
396
- {/* Validation Display - Full Width Below Split Container */}
397
- <ValidationErrorDisplay
398
- validation={validation}
399
- onErrorClick={handleValidationErrorClick}
400
- variant={variant}
401
- className="html-editor-validation"
402
- />
403
497
  </CapRow>
404
498
 
405
499
  {/* Fullscreen Modal */}
@@ -411,17 +505,17 @@ const HTMLEditor = ({
411
505
  maskClosable={false}
412
506
  centered
413
507
  closable={false}
414
- width={"90vw"}
508
+ width="90vw"
415
509
  className="html-editor-fullscreen-modal"
416
510
  >
417
511
  <CapRow className="html-editor-fullscreen">
418
512
  {/* Editor Toolbar - Conditional based on variant */}
419
513
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
420
514
  <EditorToolbar
421
- showFullscreenButton={true} // Show fullscreen button in modal to allow closing
515
+ showFullscreenButton // Show fullscreen button in modal to allow closing
422
516
  onLabelInsert={handleLabelInsert}
423
517
  onSave={handleSave}
424
- isFullscreenMode={true}
518
+ isFullscreenMode
425
519
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
426
520
  />
427
521
  ) : (
@@ -434,10 +528,10 @@ const HTMLEditor = ({
434
528
  onKeepContentSameChange={toggleContentSync}
435
529
  />
436
530
  <EditorToolbar
437
- showFullscreenButton={true} // Show fullscreen button in modal to allow closing
531
+ showFullscreenButton // Show fullscreen button in modal to allow closing
438
532
  onLabelInsert={handleLabelInsert}
439
533
  onSave={handleSave}
440
- isFullscreenMode={true}
534
+ isFullscreenMode
441
535
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
442
536
  variant={variant}
443
537
  showTitle={false} // Hide title in InApp variant
@@ -452,21 +546,23 @@ const HTMLEditor = ({
452
546
  <CodeEditorPane
453
547
  ref={modalEditorRef}
454
548
  readOnly={readOnly}
455
- isFullscreenMode={true}
549
+ isFullscreenMode
456
550
  onLabelInsert={handleLabelInsert}
551
+ onErrorClick={handleValidationErrorClick}
552
+ tags={tags}
553
+ injectedTags={injectedTags}
554
+ location={location}
555
+ eventContextTags={eventContextTags}
556
+ selectedOfferDetails={selectedOfferDetails}
557
+ channel={channel}
558
+ userLocale={userLocale}
559
+ moduleFilterEnabled={moduleFilterEnabled}
560
+ onTagContextChange={onTagContextChange}
457
561
  />
458
562
 
459
- {/* Preview Pane */}
460
- <PreviewPane isFullscreenMode={true} isModalContext={true} />
461
- </SplitContainer>
462
-
463
- {/* Validation Display in Modal */}
464
- <ValidationErrorDisplay
465
- validation={validation}
466
- onErrorClick={handleValidationErrorClick}
467
- variant={variant}
468
- className="html-editor-validation"
469
- />
563
+ {/* Preview Pane */}
564
+ <PreviewPane isFullscreenMode isModalContext />
565
+ </SplitContainer>
470
566
  </CapRow>
471
567
  </CapRow>
472
568
  </CapModal>
@@ -481,7 +577,7 @@ HTMLEditor.propTypes = {
481
577
  layoutType: PropTypes.string, // Layout type for InApp variant
482
578
  initialContent: PropTypes.oneOfType([
483
579
  PropTypes.string,
484
- PropTypes.objectOf(PropTypes.string) // Per-device content for INAPP variant
580
+ PropTypes.objectOf(PropTypes.string), // Per-device content for INAPP variant
485
581
  ]),
486
582
  onSave: PropTypes.func,
487
583
  onContentChange: PropTypes.func,
@@ -489,11 +585,27 @@ HTMLEditor.propTypes = {
489
585
  readOnly: PropTypes.bool,
490
586
  showFullscreenButton: PropTypes.bool,
491
587
  autoSave: PropTypes.bool,
492
- autoSaveInterval: PropTypes.number
588
+ autoSaveInterval: PropTypes.number,
589
+ // Tag-related props - tags are fetched and managed by parent component
590
+ tags: PropTypes.array,
591
+ injectedTags: PropTypes.object,
592
+ location: PropTypes.object,
593
+ eventContextTags: PropTypes.array,
594
+ selectedOfferDetails: PropTypes.array,
595
+ channel: PropTypes.string,
596
+ userLocale: PropTypes.string,
597
+ moduleFilterEnabled: PropTypes.bool,
598
+ onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
599
+ onTagSelect: PropTypes.func,
600
+ onContextChange: PropTypes.func, // Deprecated: use globalActions instead
601
+ globalActions: PropTypes.object,
602
+ isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
603
+ isFullMode: PropTypes.bool, // Full mode vs library mode
493
604
  };
494
605
 
495
606
  HTMLEditor.defaultProps = {
496
607
  variant: HTML_EDITOR_VARIANTS.EMAIL, // Default to email variant
608
+ layoutType: null,
497
609
  initialContent: null, // Will use default from useEditorContent hook
498
610
  onSave: null,
499
611
  onContentChange: null,
@@ -501,7 +613,22 @@ HTMLEditor.defaultProps = {
501
613
  readOnly: false,
502
614
  showFullscreenButton: true,
503
615
  autoSave: true,
504
- autoSaveInterval: 30000
616
+ autoSaveInterval: 30000,
617
+ // Tag-related defaults - tags are fetched and managed by parent component
618
+ tags: [],
619
+ injectedTags: {},
620
+ location: null,
621
+ eventContextTags: [],
622
+ selectedOfferDetails: [],
623
+ channel: null,
624
+ userLocale: 'en',
625
+ moduleFilterEnabled: true,
626
+ onTagContextChange: null, // Parent component should provide this
627
+ onTagSelect: null,
628
+ onContextChange: null,
629
+ globalActions: null, // Redux actions for API calls
630
+ isLiquidEnabled: false,
631
+ isFullMode: true, // Default to full mode
505
632
  };
506
633
 
507
634
  // Export with forwardRef to allow direct access to CodeEditorPane via ref