@capillarytech/creatives-library 8.0.263 → 8.0.265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -3
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -15
- package/services/tests/api.test.js +0 -34
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
- package/tests/integration/TemplateCreation/api-response.js +1 -31
- package/tests/integration/TemplateCreation/msw-handler.js +0 -2
- package/utils/common.js +0 -11
- package/utils/commonUtils.js +5 -28
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/tests/transformerUtils.test.js +0 -297
- package/utils/transformTemplateConfig.js +10 -0
- package/utils/transformerUtils.js +0 -40
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +16 -65
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +1 -5
- package/v2Components/CapTagList/index.js +1 -6
- package/v2Components/CapTagListWithInput/index.js +1 -5
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/ErrorInfoNote/index.js +72 -402
- package/v2Components/ErrorInfoNote/messages.js +6 -32
- package/v2Components/ErrorInfoNote/style.scss +6 -278
- package/v2Components/FormBuilder/tests/index.test.js +4 -13
- package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
- package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +10 -7
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
- package/v2Components/HtmlEditor/constants.js +20 -45
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
- package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +94 -102
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
- package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
- package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
- package/v2Components/MobilePushPreviewV2/index.js +7 -33
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +90 -172
- package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
- package/v2Containers/CreativesContainer/constants.js +0 -4
- package/v2Containers/CreativesContainer/index.js +46 -408
- package/v2Containers/CreativesContainer/messages.js +0 -12
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
- package/v2Containers/Email/actions.js +0 -7
- package/v2Containers/Email/constants.js +1 -5
- package/v2Containers/Email/index.js +36 -237
- package/v2Containers/Email/messages.js +0 -32
- package/v2Containers/Email/reducer.js +1 -12
- package/v2Containers/Email/sagas.js +7 -61
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
- package/v2Containers/Email/tests/reducer.test.js +0 -46
- package/v2Containers/Email/tests/sagas.test.js +29 -320
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/EmailWrapper/constants.js +0 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
- package/v2Containers/EmailWrapper/index.js +23 -103
- package/v2Containers/EmailWrapper/messages.js +1 -65
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +359 -802
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +50 -154
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/TagList/index.js +19 -62
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +1 -265
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +38 -363
- package/v2Containers/Templates/messages.js +0 -28
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
- package/v2Containers/TemplatesV2/index.js +7 -15
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
- package/utils/imageUrlUpload.js +0 -141
- package/v2Components/CapImageUrlUpload/constants.js +0 -26
- package/v2Components/CapImageUrlUpload/index.js +0 -365
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Components/ErrorInfoNote/constants.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
- package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
- package/v2Components/MobilePushPreviewV2/constants.js +0 -6
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -194
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1099
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
- package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
- package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
- package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
- package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
- package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
- package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
- package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
- package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
- package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
- package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
- package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
- package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
- package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
- package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
- package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
- package/v2Containers/WebPush/Create/index.js +0 -1148
- package/v2Containers/WebPush/Create/index.scss +0 -134
- package/v2Containers/WebPush/Create/messages.js +0 -211
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
- package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
- package/v2Containers/WebPush/Create/preview/constants.js +0 -637
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
- package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
- package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
- package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/Create/utils/validation.js +0 -75
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -132
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- 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, {
|
|
16
|
-
useRef, useCallback, useMemo, useState, useEffect, useImperativeHandle, forwardRef,
|
|
17
|
-
} from 'react';
|
|
15
|
+
import React, { useRef, useCallback, useMemo, useState } from 'react';
|
|
18
16
|
import PropTypes from 'prop-types';
|
|
17
|
+
import { Layout } from 'antd'; // Fallback - no Cap UI equivalent
|
|
19
18
|
import { injectIntl, intlShape } from 'react-intl';
|
|
20
19
|
|
|
21
20
|
// Cap UI Components (First Preference)
|
|
22
21
|
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';
|
|
@@ -30,6 +30,7 @@ import DeviceToggle from './components/DeviceToggle';
|
|
|
30
30
|
import SplitContainer from './components/SplitContainer';
|
|
31
31
|
import CodeEditorPane from './components/CodeEditorPane';
|
|
32
32
|
import PreviewPane from './components/PreviewPane';
|
|
33
|
+
import ValidationErrorDisplay from './components/ValidationErrorDisplay';
|
|
33
34
|
import { EditorProvider } from './components/common/EditorContext';
|
|
34
35
|
|
|
35
36
|
// Hooks and utilities
|
|
@@ -39,11 +40,7 @@ import { useLayoutState } from './hooks/useLayoutState';
|
|
|
39
40
|
import { useValidation } from './hooks/useValidation';
|
|
40
41
|
|
|
41
42
|
// Constants
|
|
42
|
-
import {
|
|
43
|
-
HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT, TAG, EMBEDDED, DEFAULT, FULL, ALL, SMS, EMAIL,
|
|
44
|
-
BLOCKING_ERROR_RULE_IDS,
|
|
45
|
-
VALIDATION_SEVERITY,
|
|
46
|
-
} from './constants';
|
|
43
|
+
import { HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT } from './constants';
|
|
47
44
|
|
|
48
45
|
// Styles
|
|
49
46
|
import './_htmlEditor.scss';
|
|
@@ -51,33 +48,8 @@ import './components/FullscreenModal/_fullscreenModal.scss';
|
|
|
51
48
|
|
|
52
49
|
// Messages
|
|
53
50
|
import messages from './messages';
|
|
54
|
-
import { ISSUE_SOURCES } from './utils/validationConstants';
|
|
55
|
-
|
|
56
|
-
/** Check if an issue is a blocking error (Errors tab). Non-blocking = Warnings. */
|
|
57
|
-
const isBlockingError = (issue) => {
|
|
58
|
-
const { rule, source, severity } = issue || {};
|
|
59
|
-
if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') return true;
|
|
60
|
-
if (source === ISSUE_SOURCES.LIQUID && severity === VALIDATION_SEVERITY.ERROR) return true;
|
|
61
|
-
if (BLOCKING_ERROR_RULE_IDS.includes(rule)) return true;
|
|
62
|
-
return false;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/** Count issues as Errors (blocking) and Warnings (non-blocking). */
|
|
66
|
-
const countIssuesBySeverity = (allIssues) => {
|
|
67
|
-
let errors = 0;
|
|
68
|
-
let warnings = 0;
|
|
69
|
-
(allIssues || []).forEach((issue) => {
|
|
70
|
-
if (isBlockingError(issue)) errors += 1;
|
|
71
|
-
else warnings += 1;
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
errors,
|
|
75
|
-
warnings,
|
|
76
|
-
total: (allIssues || []).length,
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
51
|
|
|
80
|
-
const HTMLEditor =
|
|
52
|
+
const HTMLEditor = ({
|
|
81
53
|
intl,
|
|
82
54
|
variant = HTML_EDITOR_VARIANTS.EMAIL, // New prop: 'email' or 'inapp'
|
|
83
55
|
layoutType, // Layout type for InApp variant
|
|
@@ -89,33 +61,17 @@ const HTMLEditor = forwardRef(({
|
|
|
89
61
|
showFullscreenButton = true,
|
|
90
62
|
autoSave = true,
|
|
91
63
|
autoSaveInterval = 30000, // 30 seconds
|
|
92
|
-
// Tag-related props - tags are fetched and managed by parent component (EmailHTMLEditor, INAPP, etc.)
|
|
93
|
-
tags = [],
|
|
94
|
-
injectedTags = {},
|
|
95
|
-
location,
|
|
96
|
-
eventContextTags = [],
|
|
97
|
-
selectedOfferDetails = [],
|
|
98
|
-
channel,
|
|
99
|
-
userLocale = 'en',
|
|
100
|
-
moduleFilterEnabled = true,
|
|
101
|
-
onTagContextChange, // Parent component handles tag fetching
|
|
102
|
-
onTagSelect = null,
|
|
103
|
-
onContextChange = null,
|
|
104
|
-
globalActions = null,
|
|
105
|
-
isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
|
|
106
|
-
isFullMode = true, // Full mode vs library mode - controls layout and visibility
|
|
107
|
-
onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
|
|
108
|
-
onValidationChange = null, // Callback when validation state changes (for parent to track errors)
|
|
109
|
-
apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent { liquidErrors: [], standardErrors: [] }
|
|
110
64
|
...props
|
|
111
|
-
}
|
|
65
|
+
}) => {
|
|
112
66
|
// Separate refs for main and modal editors to avoid conflicts
|
|
113
67
|
const mainEditorRef = useRef(null);
|
|
114
68
|
const modalEditorRef = useRef(null);
|
|
115
69
|
const [isFullscreenModalOpen, setIsFullscreenModalOpen] = useState(false);
|
|
116
70
|
|
|
117
71
|
// Get the currently active editor ref based on fullscreen state
|
|
118
|
-
const getActiveEditorRef = useCallback(() =>
|
|
72
|
+
const getActiveEditorRef = useCallback(() => {
|
|
73
|
+
return isFullscreenModalOpen ? modalEditorRef : mainEditorRef;
|
|
74
|
+
}, [isFullscreenModalOpen]);
|
|
119
75
|
|
|
120
76
|
// Initialize custom hooks for state management - always call both hooks to follow Rules of Hooks
|
|
121
77
|
const isEmailVariant = variant === HTML_EDITOR_VARIANTS.EMAIL;
|
|
@@ -124,7 +80,7 @@ const HTMLEditor = forwardRef(({
|
|
|
124
80
|
autoSave: isEmailVariant ? autoSave : false,
|
|
125
81
|
autoSaveInterval,
|
|
126
82
|
onSave: isEmailVariant ? onSave : null,
|
|
127
|
-
onChange: isEmailVariant ? onContentChange : null
|
|
83
|
+
onChange: isEmailVariant ? onContentChange : null
|
|
128
84
|
};
|
|
129
85
|
|
|
130
86
|
const emailContent = useEditorContent(
|
|
@@ -141,7 +97,7 @@ const HTMLEditor = forwardRef(({
|
|
|
141
97
|
// Convert string content to device-specific format
|
|
142
98
|
inAppInitialContent = {
|
|
143
99
|
[DEVICE_TYPES.ANDROID]: initialContent,
|
|
144
|
-
[DEVICE_TYPES.IOS]: initialContent
|
|
100
|
+
[DEVICE_TYPES.IOS]: initialContent
|
|
145
101
|
};
|
|
146
102
|
} else {
|
|
147
103
|
// Use provided device-specific content
|
|
@@ -153,7 +109,7 @@ const HTMLEditor = forwardRef(({
|
|
|
153
109
|
autoSave: isInAppVariant ? autoSave : false,
|
|
154
110
|
autoSaveInterval,
|
|
155
111
|
onSave: isInAppVariant ? onSave : null,
|
|
156
|
-
onChange: isInAppVariant ? onContentChange : null
|
|
112
|
+
onChange: isInAppVariant ? onContentChange : null
|
|
157
113
|
};
|
|
158
114
|
|
|
159
115
|
const inAppContent = useInAppContent(inAppInitialContent, inAppOptions);
|
|
@@ -161,64 +117,6 @@ const HTMLEditor = forwardRef(({
|
|
|
161
117
|
// Use appropriate content hook based on variant
|
|
162
118
|
const content = variant === HTML_EDITOR_VARIANTS.EMAIL ? emailContent : inAppContent;
|
|
163
119
|
|
|
164
|
-
// Update content when initialContent prop changes (for edit mode)
|
|
165
|
-
// This ensures the editor updates when template data loads
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
if (isEmailVariant && emailContent && initialContent !== undefined && initialContent !== null) {
|
|
168
|
-
// Only update if content is different to avoid unnecessary updates
|
|
169
|
-
if (emailContent.content !== initialContent) {
|
|
170
|
-
emailContent.updateContent(initialContent, true); // immediate update
|
|
171
|
-
}
|
|
172
|
-
} else if (isInAppVariant && inAppContent && initialContent !== undefined && initialContent !== null) {
|
|
173
|
-
// Handle InApp variant updates
|
|
174
|
-
const contentToUpdate = typeof initialContent === 'string'
|
|
175
|
-
? { [DEVICE_TYPES.ANDROID]: initialContent, [DEVICE_TYPES.IOS]: initialContent }
|
|
176
|
-
: initialContent;
|
|
177
|
-
if (inAppContent.updateContent) {
|
|
178
|
-
const currentContent = inAppContent.getDeviceContent?.(inAppContent.activeDevice);
|
|
179
|
-
const newContent = contentToUpdate[inAppContent.activeDevice] || contentToUpdate[DEVICE_TYPES.ANDROID] || '';
|
|
180
|
-
if (currentContent !== newContent) {
|
|
181
|
-
inAppContent.updateContent(newContent, true);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}, [initialContent, isEmailVariant, isInAppVariant]);
|
|
186
|
-
// Handle context change for tag API calls
|
|
187
|
-
// If variant is INAPP, use SMS layout; otherwise use the channel (EMAIL)
|
|
188
|
-
const handleContextChange = useCallback((contextData) => {
|
|
189
|
-
// If onContextChange is provided, use it instead of making our own API call
|
|
190
|
-
// This prevents duplicate API calls when parent component handles tag fetching
|
|
191
|
-
if (onContextChange) {
|
|
192
|
-
onContextChange(contextData);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Only make API call if onContextChange is not provided and globalActions is available
|
|
197
|
-
if (!globalActions || !location) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const { type } = location.query || {};
|
|
202
|
-
const tempData = (contextData || '').toLowerCase();
|
|
203
|
-
const isEmbedded = type === EMBEDDED;
|
|
204
|
-
const embedded = isEmbedded ? type : FULL;
|
|
205
|
-
const context = tempData === ALL ? DEFAULT : tempData;
|
|
206
|
-
|
|
207
|
-
// Determine layout: INAPP variant uses SMS, EMAIL variant uses EMAIL
|
|
208
|
-
const layout = variant === HTML_EDITOR_VARIANTS.INAPP ? SMS : EMAIL;
|
|
209
|
-
|
|
210
|
-
const query = {
|
|
211
|
-
layout,
|
|
212
|
-
type: TAG,
|
|
213
|
-
context,
|
|
214
|
-
embedded,
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
// Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
|
|
218
|
-
// The API endpoint will be: /meta/TAG?query={...}
|
|
219
|
-
globalActions.fetchSchemaForEntity(query);
|
|
220
|
-
}, [variant, globalActions, location, onContextChange]);
|
|
221
|
-
|
|
222
120
|
// Destructure content properties for cleaner access throughout component
|
|
223
121
|
const {
|
|
224
122
|
activeDevice,
|
|
@@ -226,14 +124,14 @@ const HTMLEditor = forwardRef(({
|
|
|
226
124
|
switchDevice,
|
|
227
125
|
toggleContentSync,
|
|
228
126
|
getDeviceContent,
|
|
229
|
-
markAsSaved
|
|
127
|
+
markAsSaved
|
|
230
128
|
} = content || {};
|
|
231
129
|
|
|
232
130
|
const layout = useLayoutState({
|
|
233
131
|
splitSizes: [50, 50],
|
|
234
132
|
viewMode: 'desktop',
|
|
235
133
|
mobileWidth: 375,
|
|
236
|
-
isFullscreen: false
|
|
134
|
+
isFullscreen: false
|
|
237
135
|
});
|
|
238
136
|
|
|
239
137
|
// Get current content for validation based on variant
|
|
@@ -260,7 +158,7 @@ const HTMLEditor = forwardRef(({
|
|
|
260
158
|
'sanitizer.productionValidHtml': messages.sanitizer.productionValidHtml,
|
|
261
159
|
'sanitizer.productionSanitized': messages.sanitizer.productionSanitized,
|
|
262
160
|
'sanitizer.productionInlineCss': messages.sanitizer.productionInlineCss,
|
|
263
|
-
'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent
|
|
161
|
+
'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent
|
|
264
162
|
};
|
|
265
163
|
|
|
266
164
|
const messageObj = messageMap[messageKey];
|
|
@@ -281,7 +179,7 @@ const HTMLEditor = forwardRef(({
|
|
|
281
179
|
'validator.largeImageDetected': messages.validator.largeImageDetected,
|
|
282
180
|
'validator.unclosedCssRule': messages.validator.unclosedCssRule,
|
|
283
181
|
'validator.emptyCssRule': messages.validator.emptyCssRule,
|
|
284
|
-
'validator.cssValidationFailed': messages.validator.cssValidationFailed
|
|
182
|
+
'validator.cssValidationFailed': messages.validator.cssValidationFailed
|
|
285
183
|
};
|
|
286
184
|
|
|
287
185
|
const messageObj = messageMap[messageKey];
|
|
@@ -292,215 +190,57 @@ const HTMLEditor = forwardRef(({
|
|
|
292
190
|
enableRealTime: true,
|
|
293
191
|
debounceMs: 500,
|
|
294
192
|
enableSanitization: true,
|
|
295
|
-
securityLevel: 'standard'
|
|
296
|
-
apiValidationErrors, // Pass API validation errors to merge with client-side validation
|
|
193
|
+
securityLevel: 'standard'
|
|
297
194
|
}, formatSanitizerMessage, formatValidatorMessage);
|
|
298
195
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
getIssueCounts: () => {
|
|
305
|
-
if (!validation || typeof validation.getAllIssues !== 'function') {
|
|
306
|
-
return { errors: 0, warnings: 0, total: 0 };
|
|
307
|
-
}
|
|
308
|
-
return countIssuesBySeverity(validation.getAllIssues());
|
|
309
|
-
},
|
|
310
|
-
getValidationState: () => ({
|
|
311
|
-
isValidating: validation?.isValidating || false,
|
|
312
|
-
hasErrors: validation?.hasBlockingErrors || false,
|
|
313
|
-
issueCounts: !validation || typeof validation.getAllIssues !== 'function'
|
|
314
|
-
? { errors: 0, warnings: 0, total: 0 }
|
|
315
|
-
: countIssuesBySeverity(validation.getAllIssues()),
|
|
316
|
-
})
|
|
317
|
-
,
|
|
318
|
-
}), [validation, currentContent, apiValidationErrors]); // Include apiValidationErrors so ref methods return updated counts
|
|
319
|
-
|
|
320
|
-
// Use ref to store callback to avoid infinite loops (callback in deps would cause re-runs)
|
|
321
|
-
const onValidationChangeRef = useRef(onValidationChange);
|
|
322
|
-
useEffect(() => {
|
|
323
|
-
onValidationChangeRef.current = onValidationChange;
|
|
324
|
-
}, [onValidationChange]);
|
|
325
|
-
|
|
326
|
-
// Track last sent validation state to prevent duplicate updates
|
|
327
|
-
const lastSentValidationStateRef = useRef(null);
|
|
328
|
-
|
|
329
|
-
// Store validation ref to access current value without triggering re-renders
|
|
330
|
-
const validationRef = useRef(validation);
|
|
331
|
-
validationRef.current = validation;
|
|
332
|
-
|
|
333
|
-
// Extract STABLE primitive values from validation for dependency array
|
|
334
|
-
const isValidating = validation?.isValidating;
|
|
335
|
-
const validationTotalErrors = validation?.summary?.totalErrors || 0;
|
|
336
|
-
const validationTotalWarnings = validation?.summary?.totalWarnings || 0;
|
|
337
|
-
const validationLastValidated = validation?.lastValidated?.getTime?.() || null;
|
|
338
|
-
// Only Rule Group #1 (Input & Sanitization) blocks; use for UI gating (Done/Update/Preview/Test)
|
|
339
|
-
const validationHasBlockingErrors = validation?.hasBlockingErrors || false;
|
|
340
|
-
|
|
341
|
-
// Notify parent component when validation state changes
|
|
342
|
-
useEffect(() => {
|
|
343
|
-
if (!onValidationChangeRef.current) {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Skip if still validating (wait for validation to complete)
|
|
348
|
-
if (isValidating) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Calculate issue counts: Errors (blocking) and Warnings (non-blocking)
|
|
353
|
-
const calculateIssueCounts = () => {
|
|
354
|
-
const currentValidation = validationRef.current;
|
|
355
|
-
if (!currentValidation || typeof currentValidation.getAllIssues !== 'function') {
|
|
356
|
-
return { errors: 0, warnings: 0, total: 0 };
|
|
357
|
-
}
|
|
358
|
-
return countIssuesBySeverity(currentValidation.getAllIssues());
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const issueCounts = calculateIssueCounts();
|
|
362
|
-
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
363
|
-
|
|
364
|
-
// hasErrors = only Rule Group #1 (Input & Sanitization) – gates Done/Update/Preview/Test
|
|
365
|
-
const newState = {
|
|
366
|
-
isContentEmpty,
|
|
367
|
-
issueCounts,
|
|
368
|
-
validationComplete: true,
|
|
369
|
-
hasErrors: validationRef.current?.hasBlockingErrors ?? false,
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// Only call callback if state actually changed (prevent infinite loops)
|
|
373
|
-
const lastState = lastSentValidationStateRef.current;
|
|
374
|
-
const hasChanged = !lastState
|
|
375
|
-
|| lastState.isContentEmpty !== newState.isContentEmpty
|
|
376
|
-
|| lastState.validationComplete !== newState.validationComplete
|
|
377
|
-
|| lastState.hasErrors !== newState.hasErrors
|
|
378
|
-
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
379
|
-
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
380
|
-
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
381
|
-
|
|
382
|
-
if (hasChanged) {
|
|
383
|
-
lastSentValidationStateRef.current = newState;
|
|
384
|
-
onValidationChangeRef.current(newState);
|
|
385
|
-
}
|
|
386
|
-
}, [isValidating, validationTotalErrors, validationTotalWarnings, validationLastValidated, validationHasBlockingErrors, currentContent, apiValidationErrors]);
|
|
196
|
+
// Handle label insertion at cursor position
|
|
197
|
+
const handleLabelInsert = useCallback((label, position) => {
|
|
198
|
+
// With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
|
|
199
|
+
const activeEditorRef = getActiveEditorRef();
|
|
200
|
+
const editor = activeEditorRef.current;
|
|
387
201
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
hasInitializedRef.current = true;
|
|
395
|
-
|
|
396
|
-
// Send initial state with validationComplete=false to indicate validation pending
|
|
397
|
-
const isContentEmpty = !currentContent || currentContent.trim() === '';
|
|
398
|
-
onValidationChangeRef.current({
|
|
399
|
-
isContentEmpty,
|
|
400
|
-
issueCounts: { errors: 0, warnings: 0, total: 0 },
|
|
401
|
-
validationComplete: false, // Validation hasn't run yet
|
|
402
|
-
hasErrors: false,
|
|
403
|
-
});
|
|
404
|
-
}, [currentContent]); // Only depend on currentContent to run on initial content load
|
|
405
|
-
|
|
406
|
-
// Force validation state recalculation when API validation errors change
|
|
407
|
-
// This ensures that API errors are included in issue counts and displayed in ValidationErrorDisplay
|
|
408
|
-
useEffect(() => {
|
|
409
|
-
if (!onValidationChangeRef.current) {
|
|
202
|
+
if (!editor) {
|
|
203
|
+
CapNotification.warning({
|
|
204
|
+
message: intl.formatMessage(messages.labelInsertError),
|
|
205
|
+
description: intl.formatMessage(messages.editorNotReady),
|
|
206
|
+
duration: 3
|
|
207
|
+
});
|
|
410
208
|
return;
|
|
411
209
|
}
|
|
412
210
|
|
|
413
|
-
//
|
|
414
|
-
if (
|
|
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
|
+
});
|
|
415
218
|
return;
|
|
416
219
|
}
|
|
417
220
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const newState = {
|
|
425
|
-
isContentEmpty,
|
|
426
|
-
issueCounts,
|
|
427
|
-
validationComplete: true,
|
|
428
|
-
hasErrors: validation?.hasBlockingErrors ?? false,
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
const lastState = lastSentValidationStateRef.current;
|
|
432
|
-
const hasChanged = !lastState
|
|
433
|
-
|| lastState.hasErrors !== newState.hasErrors
|
|
434
|
-
|| lastState.issueCounts?.total !== newState.issueCounts?.total
|
|
435
|
-
|| lastState.issueCounts?.errors !== newState.issueCounts?.errors
|
|
436
|
-
|| lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
|
|
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);
|
|
437
226
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
onValidationChangeRef.current(newState);
|
|
441
|
-
}
|
|
442
|
-
}, [apiValidationErrors, validation, currentContent, onValidationChangeRef]);
|
|
227
|
+
// Insert label at cursor position
|
|
228
|
+
editor.insertText(label, cursor);
|
|
443
229
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
// The actual insertion happens in CodeEditorPane.handleTagSelect
|
|
447
|
-
const handleLabelInsert = useCallback((label, position) => {
|
|
448
|
-
// If position is explicitly null, it means the editor wasn't ready when tag was selected
|
|
449
|
-
// In this case, CodeEditorPane couldn't insert the tag, so we should try here
|
|
450
|
-
if (position === null) {
|
|
451
|
-
// With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
|
|
452
|
-
const activeEditorRef = getActiveEditorRef();
|
|
453
|
-
const editor = activeEditorRef.current;
|
|
454
|
-
|
|
455
|
-
if (!editor) {
|
|
456
|
-
CapNotification.warning({
|
|
457
|
-
message: intl.formatMessage(messages.labelInsertError),
|
|
458
|
-
description: intl.formatMessage(messages.editorNotReady),
|
|
459
|
-
duration: 3,
|
|
460
|
-
});
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
230
|
+
// Focus the editor if focus method is available
|
|
231
|
+
editor?.focus?.();
|
|
463
232
|
|
|
464
|
-
//
|
|
465
|
-
if (typeof editor?.insertText !== 'function') {
|
|
466
|
-
CapNotification.error({
|
|
467
|
-
message: intl.formatMessage(messages.labelInsertError),
|
|
468
|
-
description: intl.formatMessage(messages.editorMethodNotAvailable),
|
|
469
|
-
duration: 4,
|
|
470
|
-
});
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
try {
|
|
475
|
-
// Get current cursor position
|
|
476
|
-
const cursor = typeof editor?.getCursor === 'function' ? editor.getCursor() : 0;
|
|
477
|
-
|
|
478
|
-
// Insert label at cursor position
|
|
479
|
-
editor.insertText(label, cursor);
|
|
480
|
-
|
|
481
|
-
// Focus the editor if focus method is available
|
|
482
|
-
editor?.focus?.();
|
|
483
|
-
|
|
484
|
-
// Show success notification
|
|
485
|
-
CapNotification.success({
|
|
486
|
-
message: intl.formatMessage(messages.labelInserted),
|
|
487
|
-
description: intl.formatMessage(messages.labelInsertedDescription, { label }),
|
|
488
|
-
duration: 2,
|
|
489
|
-
});
|
|
490
|
-
} catch (error) {
|
|
491
|
-
CapNotification.error({
|
|
492
|
-
message: intl.formatMessage(messages.labelInsertError),
|
|
493
|
-
description: error.message,
|
|
494
|
-
duration: 4,
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
} else {
|
|
498
|
-
// Tag was already inserted by CodeEditorPane (position is a valid number)
|
|
499
|
-
// Just show success notification - no need to access editor
|
|
233
|
+
// Show success notification
|
|
500
234
|
CapNotification.success({
|
|
501
235
|
message: intl.formatMessage(messages.labelInserted),
|
|
502
236
|
description: intl.formatMessage(messages.labelInsertedDescription, { label }),
|
|
503
|
-
duration: 2
|
|
237
|
+
duration: 2
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
CapNotification.error({
|
|
241
|
+
message: intl.formatMessage(messages.labelInsertError),
|
|
242
|
+
description: error.message,
|
|
243
|
+
duration: 4
|
|
504
244
|
});
|
|
505
245
|
}
|
|
506
246
|
}, [intl, getActiveEditorRef]);
|
|
@@ -509,23 +249,23 @@ const HTMLEditor = forwardRef(({
|
|
|
509
249
|
const handleSave = useCallback(() => {
|
|
510
250
|
try {
|
|
511
251
|
const { html, css, javascript } = content?.content || {};
|
|
512
|
-
const
|
|
252
|
+
const currentContent = { html, css, javascript };
|
|
513
253
|
|
|
514
254
|
if (onSave) {
|
|
515
|
-
onSave(
|
|
255
|
+
onSave(currentContent);
|
|
516
256
|
}
|
|
517
257
|
|
|
518
258
|
markAsSaved?.();
|
|
519
259
|
|
|
520
260
|
CapNotification.success({
|
|
521
261
|
message: intl.formatMessage(messages.contentSaved),
|
|
522
|
-
duration: 2
|
|
262
|
+
duration: 2
|
|
523
263
|
});
|
|
524
264
|
} catch (error) {
|
|
525
265
|
CapNotification.error({
|
|
526
266
|
message: intl.formatMessage(messages.saveError),
|
|
527
267
|
description: error.message,
|
|
528
|
-
duration: 4
|
|
268
|
+
duration: 4
|
|
529
269
|
});
|
|
530
270
|
}
|
|
531
271
|
}, [content, onSave, intl, markAsSaved]);
|
|
@@ -536,27 +276,21 @@ const HTMLEditor = forwardRef(({
|
|
|
536
276
|
const editorInstance = activeEditorRef.current;
|
|
537
277
|
const { line, column = 1 } = error || {};
|
|
538
278
|
|
|
539
|
-
|
|
540
|
-
// This enables the buttons even if we can't navigate to a specific line
|
|
541
|
-
if (onErrorAcknowledged) {
|
|
542
|
-
onErrorAcknowledged();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (editorInstance) {
|
|
279
|
+
if (editorInstance && line) {
|
|
546
280
|
try {
|
|
547
|
-
//
|
|
548
|
-
if (
|
|
281
|
+
// Access the CodeMirror view through the exposed ref methods
|
|
282
|
+
if (editorInstance?.navigateToLine) {
|
|
549
283
|
editorInstance.navigateToLine(line, column);
|
|
550
284
|
} else {
|
|
551
|
-
// For API errors without line numbers, just focus the editor
|
|
552
285
|
editorInstance?.focus?.();
|
|
286
|
+
// Fallback: just focus the editor if navigation isn't available
|
|
553
287
|
}
|
|
554
288
|
} catch (err) {
|
|
555
289
|
// Fallback: just focus the editor
|
|
556
290
|
editorInstance?.focus?.();
|
|
557
291
|
}
|
|
558
292
|
}
|
|
559
|
-
}, [getActiveEditorRef
|
|
293
|
+
}, [getActiveEditorRef]);
|
|
560
294
|
|
|
561
295
|
// Handle fullscreen modal
|
|
562
296
|
const handleOpenFullscreen = useCallback(() => {
|
|
@@ -573,7 +307,6 @@ const HTMLEditor = forwardRef(({
|
|
|
573
307
|
content,
|
|
574
308
|
layout,
|
|
575
309
|
validation,
|
|
576
|
-
isLiquidEnabled,
|
|
577
310
|
editorRef: getActiveEditorRef(),
|
|
578
311
|
handleLabelInsert,
|
|
579
312
|
handleSave,
|
|
@@ -586,14 +319,13 @@ const HTMLEditor = forwardRef(({
|
|
|
586
319
|
switchDevice,
|
|
587
320
|
toggleContentSync,
|
|
588
321
|
getDeviceContent,
|
|
589
|
-
layoutType
|
|
590
|
-
})
|
|
322
|
+
layoutType
|
|
323
|
+
})
|
|
591
324
|
}), [
|
|
592
325
|
variant,
|
|
593
326
|
content,
|
|
594
327
|
layout,
|
|
595
328
|
validation,
|
|
596
|
-
isLiquidEnabled,
|
|
597
329
|
getActiveEditorRef,
|
|
598
330
|
handleLabelInsert,
|
|
599
331
|
handleSave,
|
|
@@ -604,7 +336,7 @@ const HTMLEditor = forwardRef(({
|
|
|
604
336
|
switchDevice,
|
|
605
337
|
toggleContentSync,
|
|
606
338
|
getDeviceContent,
|
|
607
|
-
layoutType
|
|
339
|
+
layoutType
|
|
608
340
|
]);
|
|
609
341
|
|
|
610
342
|
// Loading state
|
|
@@ -616,14 +348,9 @@ const HTMLEditor = forwardRef(({
|
|
|
616
348
|
);
|
|
617
349
|
}
|
|
618
350
|
|
|
619
|
-
// Add library-mode class when not in full mode
|
|
620
|
-
// Note: isFullMode defaults to true, so library mode is when isFullMode === false
|
|
621
|
-
const isLibraryMode = isFullMode === false;
|
|
622
|
-
const editorClassName = `html-editor html-editor--${variant} ${isLibraryMode ? 'html-editor--library-mode' : ''} ${className}`.trim();
|
|
623
|
-
|
|
624
351
|
return (
|
|
625
352
|
<EditorProvider value={contextValue}>
|
|
626
|
-
<div className={
|
|
353
|
+
<div className={`html-editor html-editor--${variant} ${className}`} {...props}>
|
|
627
354
|
{/* Editor Toolbar - Conditional based on variant */}
|
|
628
355
|
{variant === HTML_EDITOR_VARIANTS.EMAIL ? (
|
|
629
356
|
<EditorToolbar
|
|
@@ -660,23 +387,19 @@ const HTMLEditor = forwardRef(({
|
|
|
660
387
|
ref={mainEditorRef}
|
|
661
388
|
readOnly={readOnly}
|
|
662
389
|
onLabelInsert={handleLabelInsert}
|
|
663
|
-
onErrorClick={handleValidationErrorClick}
|
|
664
|
-
tags={tags}
|
|
665
|
-
injectedTags={injectedTags}
|
|
666
|
-
location={location}
|
|
667
|
-
eventContextTags={eventContextTags}
|
|
668
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
669
|
-
channel={channel}
|
|
670
|
-
userLocale={userLocale}
|
|
671
|
-
moduleFilterEnabled={moduleFilterEnabled}
|
|
672
|
-
onTagContextChange={onTagContextChange}
|
|
673
|
-
onTagSelect={onTagSelect}
|
|
674
|
-
onContextChange={handleContextChange}
|
|
675
390
|
/>
|
|
676
391
|
|
|
677
392
|
{/* Preview Pane */}
|
|
678
393
|
<PreviewPane />
|
|
679
394
|
</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
|
+
/>
|
|
680
403
|
</CapRow>
|
|
681
404
|
|
|
682
405
|
{/* Fullscreen Modal */}
|
|
@@ -685,20 +408,20 @@ const HTMLEditor = forwardRef(({
|
|
|
685
408
|
visible={isFullscreenModalOpen}
|
|
686
409
|
onCancel={handleCloseFullscreen}
|
|
687
410
|
footer={null}
|
|
688
|
-
maskClosable
|
|
411
|
+
maskClosable={false}
|
|
689
412
|
centered
|
|
690
413
|
closable={false}
|
|
691
|
-
width="
|
|
414
|
+
width={"90vw"}
|
|
692
415
|
className="html-editor-fullscreen-modal"
|
|
693
416
|
>
|
|
694
|
-
<CapRow className=
|
|
417
|
+
<CapRow className="html-editor-fullscreen">
|
|
695
418
|
{/* Editor Toolbar - Conditional based on variant */}
|
|
696
419
|
{variant === HTML_EDITOR_VARIANTS.EMAIL ? (
|
|
697
420
|
<EditorToolbar
|
|
698
|
-
showFullscreenButton // Show fullscreen button in modal to allow closing
|
|
421
|
+
showFullscreenButton={true} // Show fullscreen button in modal to allow closing
|
|
699
422
|
onLabelInsert={handleLabelInsert}
|
|
700
423
|
onSave={handleSave}
|
|
701
|
-
isFullscreenMode
|
|
424
|
+
isFullscreenMode={true}
|
|
702
425
|
onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
|
|
703
426
|
/>
|
|
704
427
|
) : (
|
|
@@ -711,10 +434,10 @@ const HTMLEditor = forwardRef(({
|
|
|
711
434
|
onKeepContentSameChange={toggleContentSync}
|
|
712
435
|
/>
|
|
713
436
|
<EditorToolbar
|
|
714
|
-
showFullscreenButton // Show fullscreen button in modal to allow closing
|
|
437
|
+
showFullscreenButton={true} // Show fullscreen button in modal to allow closing
|
|
715
438
|
onLabelInsert={handleLabelInsert}
|
|
716
439
|
onSave={handleSave}
|
|
717
|
-
isFullscreenMode
|
|
440
|
+
isFullscreenMode={true}
|
|
718
441
|
onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
|
|
719
442
|
variant={variant}
|
|
720
443
|
showTitle={false} // Hide title in InApp variant
|
|
@@ -729,30 +452,28 @@ const HTMLEditor = forwardRef(({
|
|
|
729
452
|
<CodeEditorPane
|
|
730
453
|
ref={modalEditorRef}
|
|
731
454
|
readOnly={readOnly}
|
|
732
|
-
isFullscreenMode
|
|
455
|
+
isFullscreenMode={true}
|
|
733
456
|
onLabelInsert={handleLabelInsert}
|
|
734
|
-
onErrorClick={handleValidationErrorClick}
|
|
735
|
-
tags={tags}
|
|
736
|
-
injectedTags={injectedTags}
|
|
737
|
-
location={location}
|
|
738
|
-
eventContextTags={eventContextTags}
|
|
739
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
740
|
-
channel={channel}
|
|
741
|
-
userLocale={userLocale}
|
|
742
|
-
moduleFilterEnabled={moduleFilterEnabled}
|
|
743
|
-
onTagContextChange={onTagContextChange}
|
|
744
457
|
/>
|
|
745
458
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
+
/>
|
|
749
470
|
</CapRow>
|
|
750
471
|
</CapRow>
|
|
751
472
|
</CapModal>
|
|
752
473
|
</div>
|
|
753
474
|
</EditorProvider>
|
|
754
475
|
);
|
|
755
|
-
}
|
|
476
|
+
};
|
|
756
477
|
|
|
757
478
|
HTMLEditor.propTypes = {
|
|
758
479
|
intl: intlShape.isRequired,
|
|
@@ -760,7 +481,7 @@ HTMLEditor.propTypes = {
|
|
|
760
481
|
layoutType: PropTypes.string, // Layout type for InApp variant
|
|
761
482
|
initialContent: PropTypes.oneOfType([
|
|
762
483
|
PropTypes.string,
|
|
763
|
-
PropTypes.objectOf(PropTypes.string)
|
|
484
|
+
PropTypes.objectOf(PropTypes.string) // Per-device content for INAPP variant
|
|
764
485
|
]),
|
|
765
486
|
onSave: PropTypes.func,
|
|
766
487
|
onContentChange: PropTypes.func,
|
|
@@ -768,33 +489,11 @@ HTMLEditor.propTypes = {
|
|
|
768
489
|
readOnly: PropTypes.bool,
|
|
769
490
|
showFullscreenButton: PropTypes.bool,
|
|
770
491
|
autoSave: PropTypes.bool,
|
|
771
|
-
autoSaveInterval: PropTypes.number
|
|
772
|
-
// Tag-related props - tags are fetched and managed by parent component
|
|
773
|
-
tags: PropTypes.array,
|
|
774
|
-
injectedTags: PropTypes.object,
|
|
775
|
-
location: PropTypes.object,
|
|
776
|
-
eventContextTags: PropTypes.array,
|
|
777
|
-
selectedOfferDetails: PropTypes.array,
|
|
778
|
-
channel: PropTypes.string,
|
|
779
|
-
userLocale: PropTypes.string,
|
|
780
|
-
moduleFilterEnabled: PropTypes.bool,
|
|
781
|
-
onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
|
|
782
|
-
onTagSelect: PropTypes.func,
|
|
783
|
-
onContextChange: PropTypes.func, // Deprecated: use globalActions instead
|
|
784
|
-
globalActions: PropTypes.object,
|
|
785
|
-
isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
|
|
786
|
-
isFullMode: PropTypes.bool, // Full mode vs library mode
|
|
787
|
-
onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
|
|
788
|
-
onValidationChange: PropTypes.func, // Callback when validation state changes
|
|
789
|
-
apiValidationErrors: PropTypes.shape({
|
|
790
|
-
liquidErrors: PropTypes.arrayOf(PropTypes.string),
|
|
791
|
-
standardErrors: PropTypes.arrayOf(PropTypes.string),
|
|
792
|
-
}), // API validation errors from validateLiquidTemplateContent
|
|
492
|
+
autoSaveInterval: PropTypes.number
|
|
793
493
|
};
|
|
794
494
|
|
|
795
495
|
HTMLEditor.defaultProps = {
|
|
796
496
|
variant: HTML_EDITOR_VARIANTS.EMAIL, // Default to email variant
|
|
797
|
-
layoutType: null,
|
|
798
497
|
initialContent: null, // Will use default from useEditorContent hook
|
|
799
498
|
onSave: null,
|
|
800
499
|
onContentChange: null,
|
|
@@ -802,26 +501,8 @@ HTMLEditor.defaultProps = {
|
|
|
802
501
|
readOnly: false,
|
|
803
502
|
showFullscreenButton: true,
|
|
804
503
|
autoSave: true,
|
|
805
|
-
autoSaveInterval: 30000
|
|
806
|
-
// Tag-related defaults - tags are fetched and managed by parent component
|
|
807
|
-
tags: [],
|
|
808
|
-
injectedTags: {},
|
|
809
|
-
location: null,
|
|
810
|
-
eventContextTags: [],
|
|
811
|
-
selectedOfferDetails: [],
|
|
812
|
-
channel: null,
|
|
813
|
-
userLocale: 'en',
|
|
814
|
-
moduleFilterEnabled: true,
|
|
815
|
-
onTagContextChange: null, // Parent component should provide this
|
|
816
|
-
onTagSelect: null,
|
|
817
|
-
onContextChange: null,
|
|
818
|
-
globalActions: null, // Redux actions for API calls
|
|
819
|
-
isLiquidEnabled: false,
|
|
820
|
-
isFullMode: true, // Default to full mode
|
|
821
|
-
onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
|
|
822
|
-
onValidationChange: null, // Callback when validation state changes
|
|
823
|
-
apiValidationErrors: null, // API validation errors from validateLiquidTemplateContent
|
|
504
|
+
autoSaveInterval: 30000
|
|
824
505
|
};
|
|
825
506
|
|
|
826
|
-
// Export with
|
|
827
|
-
export default injectIntl(HTMLEditor);
|
|
507
|
+
// Export with forwardRef to allow direct access to CodeEditorPane via ref
|
|
508
|
+
export default injectIntl(HTMLEditor, { forwardRef: true });
|