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

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 +220 -91
  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 +107 -45
  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 +9 -0
  38. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  39. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -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 +70 -72
  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/useEmailWrapper.test.js +111 -77
  97. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  98. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  99. package/v2Containers/InApp/actions.js +7 -0
  100. package/v2Containers/InApp/constants.js +20 -4
  101. package/v2Containers/InApp/index.js +801 -357
  102. package/v2Containers/InApp/index.scss +4 -3
  103. package/v2Containers/InApp/messages.js +7 -3
  104. package/v2Containers/InApp/reducer.js +21 -3
  105. package/v2Containers/InApp/sagas.js +29 -9
  106. package/v2Containers/InApp/selectors.js +25 -5
  107. package/v2Containers/InApp/tests/index.test.js +154 -50
  108. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  109. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  110. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  111. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  112. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  113. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  114. package/v2Containers/InAppWrapper/constants.js +16 -0
  115. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  116. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  117. package/v2Containers/InAppWrapper/index.js +148 -0
  118. package/v2Containers/InAppWrapper/messages.js +49 -0
  119. package/v2Containers/InappAdvance/index.js +1099 -0
  120. package/v2Containers/InappAdvance/index.scss +10 -0
  121. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  122. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
  123. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
  124. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
  125. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
  126. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
  127. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
  129. package/v2Containers/TagList/index.js +67 -1
  130. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  131. package/v2Containers/Templates/_templates.scss +56 -200
  132. package/v2Containers/Templates/actions.js +1 -2
  133. package/v2Containers/Templates/constants.js +0 -1
  134. package/v2Containers/Templates/index.js +124 -277
  135. package/v2Containers/Templates/messages.js +4 -24
  136. package/v2Containers/Templates/reducer.js +0 -2
  137. package/v2Containers/Templates/tests/index.test.js +0 -10
  138. package/v2Containers/TemplatesV2/index.js +2 -3
  139. package/v2Containers/TemplatesV2/messages.js +0 -4
  140. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
  141. package/v2Components/CapImageUrlUpload/constants.js +0 -19
  142. package/v2Components/CapImageUrlUpload/index.js +0 -455
  143. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  144. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  145. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
  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,14 @@ const HTMLEditor = ({
348
435
  );
349
436
  }
350
437
 
438
+ // Add library-mode class when not in full mode
439
+ // Note: isFullMode defaults to true, so library mode is when isFullMode === false
440
+ const isLibraryMode = isFullMode === false;
441
+ const editorClassName = `html-editor html-editor--${variant} ${isLibraryMode ? 'html-editor--library-mode' : ''} ${className}`.trim();
442
+
351
443
  return (
352
444
  <EditorProvider value={contextValue}>
353
- <div className={`html-editor html-editor--${variant} ${className}`} {...props}>
445
+ <div className={editorClassName} {...props}>
354
446
  {/* Editor Toolbar - Conditional based on variant */}
355
447
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
356
448
  <EditorToolbar
@@ -387,19 +479,23 @@ const HTMLEditor = ({
387
479
  ref={mainEditorRef}
388
480
  readOnly={readOnly}
389
481
  onLabelInsert={handleLabelInsert}
482
+ onErrorClick={handleValidationErrorClick}
483
+ tags={tags}
484
+ injectedTags={injectedTags}
485
+ location={location}
486
+ eventContextTags={eventContextTags}
487
+ selectedOfferDetails={selectedOfferDetails}
488
+ channel={channel}
489
+ userLocale={userLocale}
490
+ moduleFilterEnabled={moduleFilterEnabled}
491
+ onTagContextChange={onTagContextChange}
492
+ onTagSelect={onTagSelect}
493
+ onContextChange={handleContextChange}
390
494
  />
391
495
 
392
496
  {/* Preview Pane */}
393
497
  <PreviewPane />
394
498
  </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
499
  </CapRow>
404
500
 
405
501
  {/* Fullscreen Modal */}
@@ -411,17 +507,17 @@ const HTMLEditor = ({
411
507
  maskClosable={false}
412
508
  centered
413
509
  closable={false}
414
- width={"90vw"}
510
+ width="90vw"
415
511
  className="html-editor-fullscreen-modal"
416
512
  >
417
- <CapRow className="html-editor-fullscreen">
513
+ <CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
418
514
  {/* Editor Toolbar - Conditional based on variant */}
419
515
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
420
516
  <EditorToolbar
421
- showFullscreenButton={true} // Show fullscreen button in modal to allow closing
517
+ showFullscreenButton // Show fullscreen button in modal to allow closing
422
518
  onLabelInsert={handleLabelInsert}
423
519
  onSave={handleSave}
424
- isFullscreenMode={true}
520
+ isFullscreenMode
425
521
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
426
522
  />
427
523
  ) : (
@@ -434,10 +530,10 @@ const HTMLEditor = ({
434
530
  onKeepContentSameChange={toggleContentSync}
435
531
  />
436
532
  <EditorToolbar
437
- showFullscreenButton={true} // Show fullscreen button in modal to allow closing
533
+ showFullscreenButton // Show fullscreen button in modal to allow closing
438
534
  onLabelInsert={handleLabelInsert}
439
535
  onSave={handleSave}
440
- isFullscreenMode={true}
536
+ isFullscreenMode
441
537
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
442
538
  variant={variant}
443
539
  showTitle={false} // Hide title in InApp variant
@@ -452,21 +548,23 @@ const HTMLEditor = ({
452
548
  <CodeEditorPane
453
549
  ref={modalEditorRef}
454
550
  readOnly={readOnly}
455
- isFullscreenMode={true}
551
+ isFullscreenMode
456
552
  onLabelInsert={handleLabelInsert}
553
+ onErrorClick={handleValidationErrorClick}
554
+ tags={tags}
555
+ injectedTags={injectedTags}
556
+ location={location}
557
+ eventContextTags={eventContextTags}
558
+ selectedOfferDetails={selectedOfferDetails}
559
+ channel={channel}
560
+ userLocale={userLocale}
561
+ moduleFilterEnabled={moduleFilterEnabled}
562
+ onTagContextChange={onTagContextChange}
457
563
  />
458
564
 
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
- />
565
+ {/* Preview Pane */}
566
+ <PreviewPane isFullscreenMode isModalContext />
567
+ </SplitContainer>
470
568
  </CapRow>
471
569
  </CapRow>
472
570
  </CapModal>
@@ -481,7 +579,7 @@ HTMLEditor.propTypes = {
481
579
  layoutType: PropTypes.string, // Layout type for InApp variant
482
580
  initialContent: PropTypes.oneOfType([
483
581
  PropTypes.string,
484
- PropTypes.objectOf(PropTypes.string) // Per-device content for INAPP variant
582
+ PropTypes.objectOf(PropTypes.string), // Per-device content for INAPP variant
485
583
  ]),
486
584
  onSave: PropTypes.func,
487
585
  onContentChange: PropTypes.func,
@@ -489,11 +587,27 @@ HTMLEditor.propTypes = {
489
587
  readOnly: PropTypes.bool,
490
588
  showFullscreenButton: PropTypes.bool,
491
589
  autoSave: PropTypes.bool,
492
- autoSaveInterval: PropTypes.number
590
+ autoSaveInterval: PropTypes.number,
591
+ // Tag-related props - tags are fetched and managed by parent component
592
+ tags: PropTypes.array,
593
+ injectedTags: PropTypes.object,
594
+ location: PropTypes.object,
595
+ eventContextTags: PropTypes.array,
596
+ selectedOfferDetails: PropTypes.array,
597
+ channel: PropTypes.string,
598
+ userLocale: PropTypes.string,
599
+ moduleFilterEnabled: PropTypes.bool,
600
+ onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
601
+ onTagSelect: PropTypes.func,
602
+ onContextChange: PropTypes.func, // Deprecated: use globalActions instead
603
+ globalActions: PropTypes.object,
604
+ isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
605
+ isFullMode: PropTypes.bool, // Full mode vs library mode
493
606
  };
494
607
 
495
608
  HTMLEditor.defaultProps = {
496
609
  variant: HTML_EDITOR_VARIANTS.EMAIL, // Default to email variant
610
+ layoutType: null,
497
611
  initialContent: null, // Will use default from useEditorContent hook
498
612
  onSave: null,
499
613
  onContentChange: null,
@@ -501,7 +615,22 @@ HTMLEditor.defaultProps = {
501
615
  readOnly: false,
502
616
  showFullscreenButton: true,
503
617
  autoSave: true,
504
- autoSaveInterval: 30000
618
+ autoSaveInterval: 30000,
619
+ // Tag-related defaults - tags are fetched and managed by parent component
620
+ tags: [],
621
+ injectedTags: {},
622
+ location: null,
623
+ eventContextTags: [],
624
+ selectedOfferDetails: [],
625
+ channel: null,
626
+ userLocale: 'en',
627
+ moduleFilterEnabled: true,
628
+ onTagContextChange: null, // Parent component should provide this
629
+ onTagSelect: null,
630
+ onContextChange: null,
631
+ globalActions: null, // Redux actions for API calls
632
+ isLiquidEnabled: false,
633
+ isFullMode: true, // Default to full mode
505
634
  };
506
635
 
507
636
  // Export with forwardRef to allow direct access to CodeEditorPane via ref