@capillarytech/creatives-library 8.0.213 → 8.0.214-beta.0

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 (37) hide show
  1. package/HOW_BEE_EDITOR_WORKS.md +375 -0
  2. package/constants/unified.js +1 -0
  3. package/package.json +1 -1
  4. package/services/api.js +5 -0
  5. package/utils/common.js +6 -1
  6. package/v2Components/CapTagList/index.js +2 -1
  7. package/v2Components/CapTagListWithInput/index.js +5 -1
  8. package/v2Components/CapTagListWithInput/messages.js +1 -1
  9. package/v2Components/ErrorInfoNote/style.scss +1 -1
  10. package/v2Components/HtmlEditor/HTMLEditor.js +86 -14
  11. package/v2Components/HtmlEditor/_htmlEditor.scss +4 -4
  12. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  13. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +107 -96
  14. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +68 -92
  15. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  16. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  17. package/v2Containers/CreativesContainer/SlideBoxContent.js +85 -35
  18. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  19. package/v2Containers/CreativesContainer/index.js +107 -35
  20. package/v2Containers/CreativesContainer/messages.js +4 -0
  21. package/v2Containers/Email/actions.js +7 -0
  22. package/v2Containers/Email/constants.js +5 -1
  23. package/v2Containers/Email/index.js +13 -0
  24. package/v2Containers/Email/messages.js +32 -0
  25. package/v2Containers/Email/reducer.js +12 -1
  26. package/v2Containers/Email/sagas.js +17 -0
  27. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1005 -0
  28. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  29. package/v2Containers/EmailWrapper/constants.js +2 -0
  30. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +470 -71
  31. package/v2Containers/EmailWrapper/index.js +102 -23
  32. package/v2Containers/EmailWrapper/messages.js +61 -1
  33. package/v2Containers/EmailWrapper/tests/EmailHTMLEditor.test.js +177 -0
  34. package/v2Containers/EmailWrapper/tests/EmailHTMLEditorValidation.test.js +90 -0
  35. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +49 -49
  36. package/v2Containers/TagList/index.js +2 -0
  37. package/v2Containers/Templates/index.js +5 -0
@@ -13,7 +13,7 @@
13
13
  align-items: center;
14
14
  min-height: 25rem; // 400px = 25rem
15
15
  background: $CAP_G11; // Light background similar to #fafbfc
16
- border: 0.0625rem solid $CAP_G07; // 1px border similar to #dfe2e7
16
+ // border: 0.0625rem solid $CAP_G07; // 1px border similar to #dfe2e7
17
17
  border-radius: 0.5rem; // 8px = 0.5rem
18
18
  flex-direction: column;
19
19
  gap: 1rem; // 16px = 1rem
@@ -32,78 +32,78 @@
32
32
  align-items: center;
33
33
  gap: 0.5rem;
34
34
 
35
- .cap-button,
36
- .ant-btn,
37
- button {
38
- color: map-get($CAP_PRIMARY, base);
39
- border: none;
40
- background: $CAP_WHITE;
41
- border-radius: 0.25rem;
42
- padding: 0.375rem 0.5rem;
43
- font-size: 0.875rem;
44
- font-family: $FONT_FAMILY;
45
- font-weight: 500;
46
- height: auto;
47
- min-width: 5.9375rem;
48
- display: flex;
49
- align-items: center;
50
- gap: 0.25rem;
51
- box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
52
- line-height: 1.25rem;
53
- text-align: left;
54
-
55
- &:hover {
56
- background: $CAP_G09;
57
- box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.375rem 0.75rem -0.125rem rgba(9, 30, 66, 0.25);
58
- color: map-get($CAP_PRIMARY, base);
59
- border: none;
60
- }
61
-
62
- &:active,
63
- &:focus {
64
- background: $CAP_G08;
65
- box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.125rem 0.25rem -0.125rem rgba(9, 30, 66, 0.25);
66
- color: map-get($CAP_PRIMARY, base);
67
- border: none;
68
- }
69
-
70
- .anticon,
71
- .cap-icon {
72
- font-size: 1rem;
73
- color: map-get($CAP_PRIMARY, base);
74
- }
75
-
76
- span {
77
- font-size: 0.875rem;
78
- font-weight: 500;
79
- line-height: 1.25rem;
80
- color: map-get($CAP_PRIMARY, base);
81
- white-space: nowrap;
82
- }
83
-
84
- &:before,
85
- &:after {
86
- display: none;
87
- }
88
- }
89
-
90
- .tooltip-add-label-container {
91
-
92
- .cap-button,
93
- .ant-btn {
94
- color: map-get($CAP_PRIMARY, base);
95
- background: $CAP_WHITE;
96
- border: none;
97
- box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
98
- }
99
- }
100
-
101
- .cap-button-flat,
102
- .cap-button-add {
103
- background: $CAP_WHITE;
104
- color: map-get($CAP_PRIMARY, base);
105
- border: none;
106
- }
35
+ // .cap-button,
36
+ // .ant-btn,
37
+ // button {
38
+ // color: map-get($CAP_PRIMARY, base);
39
+ // border: none;
40
+ // background: $CAP_WHITE;
41
+ // border-radius: 0.25rem;
42
+ // padding: 0.375rem 0.5rem;
43
+ // font-size: 0.875rem;
44
+ // font-family: $FONT_FAMILY;
45
+ // font-weight: 500;
46
+ // height: auto;
47
+ // min-width: 5.9375rem;
48
+ // display: flex;
49
+ // align-items: center;
50
+ // gap: 0.25rem;
51
+ // box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
52
+ // line-height: 1.25rem;
53
+ // text-align: left;
54
+
55
+ // &:hover {
56
+ // background: $CAP_G09;
57
+ // box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.375rem 0.75rem -0.125rem rgba(9, 30, 66, 0.25);
58
+ // color: map-get($CAP_PRIMARY, base);
59
+ // border: none;
60
+ // }
61
+
62
+ // &:active,
63
+ // &:focus {
64
+ // background: $CAP_G08;
65
+ // box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.125rem 0.25rem -0.125rem rgba(9, 30, 66, 0.25);
66
+ // color: map-get($CAP_PRIMARY, base);
67
+ // border: none;
68
+ // }
69
+
70
+ // .anticon,
71
+ // .cap-icon {
72
+ // font-size: 1rem;
73
+ // color: map-get($CAP_PRIMARY, base);
74
+ // }
75
+
76
+ // span {
77
+ // font-size: 0.875rem;
78
+ // font-weight: 500;
79
+ // line-height: 1.25rem;
80
+ // color: map-get($CAP_PRIMARY, base);
81
+ // white-space: nowrap;
82
+ // }
83
+
84
+ // &:before,
85
+ // &:after {
86
+ // display: none;
87
+ // }
88
+ // }
89
+
90
+ // .tooltip-add-label-container {
91
+
92
+ // .cap-button,
93
+ // .ant-btn {
94
+ // color: map-get($CAP_PRIMARY, base);
95
+ // background: $CAP_WHITE;
96
+ // border: none;
97
+ // box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
98
+ // }
99
+ // }
100
+
101
+ // .cap-button-flat,
102
+ // .cap-button-add {
103
+ // background: $CAP_WHITE;
104
+ // color: map-get($CAP_PRIMARY, base);
105
+ // border: none;
106
+ // }
107
107
  }
108
108
 
109
109
  &__content {
@@ -130,6 +130,17 @@
130
130
  }
131
131
  }
132
132
 
133
+ .code-editor-pane__actions {
134
+ opacity: 0;
135
+ pointer-events: none;
136
+ transition: opacity 0.2s ease;
137
+ }
138
+
139
+ .code-editor-pane__content:hover .code-editor-pane__actions {
140
+ opacity: 1;
141
+ pointer-events: auto;
142
+ }
143
+
133
144
  .cm-foldGutter {
134
145
  width: 1rem;
135
146
 
@@ -235,30 +246,30 @@
235
246
  right: 0.5rem;
236
247
  z-index: 20;
237
248
 
238
- .cap-button,
239
- .ant-btn,
240
- button {
241
- background: $CAP_WHITE;
242
- color: map-get($CAP_PRIMARY, base);
243
- border: none;
244
- border-radius: 0.25rem;
245
- padding: 0.375rem 0.5rem;
246
- font-size: 0.875rem;
247
- font-family: $FONT_FAMILY;
248
- font-weight: 500;
249
- min-width: 5.9375rem;
250
- box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
251
-
252
- &:hover {
253
- background: $CAP_G09;
254
- color: map-get($CAP_PRIMARY, base);
255
- }
256
-
257
- &:active {
258
- background: $CAP_G08;
259
- color: map-get($CAP_PRIMARY, base);
260
- }
261
- }
249
+ // .cap-button,
250
+ // .ant-btn,
251
+ // button {
252
+ // background: $CAP_WHITE;
253
+ // color: map-get($CAP_PRIMARY, base);
254
+ // border: none;
255
+ // border-radius: 0.25rem;
256
+ // padding: 0.375rem 0.5rem;
257
+ // font-size: 0.875rem;
258
+ // font-family: $FONT_FAMILY;
259
+ // font-weight: 500;
260
+ // min-width: 5.9375rem;
261
+ // box-shadow: 0 0 0.0625rem 0 rgba(9, 30, 66, 0.31), 0 0.25rem 0.5rem -0.125rem rgba(9, 30, 66, 0.25);
262
+
263
+ // &:hover {
264
+ // background: $CAP_G09;
265
+ // color: map-get($CAP_PRIMARY, base);
266
+ // }
267
+
268
+ // &:active {
269
+ // background: $CAP_G08;
270
+ // color: map-get($CAP_PRIMARY, base);
271
+ // }
272
+ // }
262
273
  }
263
274
 
264
275
  .codemirror-editor {
@@ -8,7 +8,7 @@
8
8
  * - Theme support with proper styling
9
9
  */
10
10
 
11
- import React, { forwardRef, useImperativeHandle, useRef, useEffect, useState } from 'react';
11
+ import React, { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react';
12
12
  import PropTypes from 'prop-types';
13
13
 
14
14
  // CodeMirror 6 imports
@@ -30,6 +30,8 @@ import CapRow from '@capillarytech/cap-ui-library/CapRow';
30
30
  // Components
31
31
  import TagList from '../../../../v2Containers/TagList';
32
32
 
33
+ // Constants - removed unused imports since tag fetching is handled by parent
34
+
33
35
  // Context
34
36
  import { useEditorContext } from '../common/EditorContext';
35
37
 
@@ -44,7 +46,17 @@ const CodeEditorPaneComponent = ({
44
46
  className = '',
45
47
  isFullscreenMode = false,
46
48
  onLabelInsert,
47
- forwardedRef
49
+ forwardedRef,
50
+ // Tag-related props - tags are fetched and managed by parent component
51
+ tags = [],
52
+ injectedTags = {},
53
+ location,
54
+ eventContextTags = [],
55
+ selectedOfferDetails = [],
56
+ channel,
57
+ userLocale = 'en',
58
+ moduleFilterEnabled = true,
59
+ onTagContextChange
48
60
  }) => {
49
61
  const { content, validation } = useEditorContext();
50
62
  const { content: contentValue, updateContent } = content;
@@ -147,7 +159,7 @@ const CodeEditorPaneComponent = ({
147
159
  // For unified HTML editor, insert as template variable
148
160
  const formattedTag = `{{${tagText}}}`;
149
161
 
150
- // Insert the tag at cursor position
162
+ // Insert the tag at cursor position directly
151
163
  view.dispatch({
152
164
  changes: { from: pos, insert: formattedTag },
153
165
  selection: { anchor: pos + formattedTag.length }
@@ -156,31 +168,43 @@ const CodeEditorPaneComponent = ({
156
168
  // Focus back to editor
157
169
  view.focus();
158
170
 
159
- // Call the parent's handleLabelInsert if available
160
- if (onLabelInsert) {
161
- onLabelInsert(formattedTag, pos);
162
- }
171
+ // Note: We don't call onLabelInsert here because:
172
+ // 1. The tag is already inserted directly into the editor
173
+ // 2. onLabelInsert (handleLabelInsert from HTMLEditor) would try to insert again
174
+ // 3. This causes "Editor method not available" error
175
+ // The direct insertion via view.dispatch is sufficient
163
176
  }
164
177
  };
165
178
 
179
+ // Handle tag context change - delegate to parent component
180
+ // Tags are fetched in parent components (EmailHTMLEditor, INAPP, etc.)
181
+ // This component just passes the context change event up
182
+ const handleTagContextChange = useCallback((data) => {
183
+ if (onTagContextChange) {
184
+ // Parent component handles tag fetching and updates
185
+ onTagContextChange(data);
186
+ }
187
+ // No fallback - tags must be managed by parent component
188
+ }, [onTagContextChange]);
189
+
166
190
  // Initialize CodeMirror effect
167
191
  useEffect(() => {
168
192
  if (editorRef.current && !viewRef.current) {
169
- // Use the comprehensive extensions from properSyntaxHighlighting.js
170
- // This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
171
- const robustExtensions = createRobustExtensions();
172
-
173
- // Add additional extensions for line numbers, active line, and update listener
174
- const extensions = [
175
- lineNumbers(),
176
- highlightActiveLine(),
177
- ...robustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
178
- EditorView.updateListener.of((update) => {
179
- if (update.docChanged) {
180
- updateContentRef.current(update.state.doc.toString());
181
- }
182
- })
183
- ];
193
+ // Use the comprehensive extensions from properSyntaxHighlighting.js
194
+ // This includes: html(), syntaxHighlighting(comprehensiveVSCodeTheme), cleanEditorTheme
195
+ const robustExtensions = createRobustExtensions();
196
+
197
+ // Add additional extensions for line numbers, active line, and update listener
198
+ const extensions = [
199
+ lineNumbers(),
200
+ highlightActiveLine(),
201
+ ...robustExtensions, // Spread the robust extensions (html, syntax highlighting, theme)
202
+ EditorView.updateListener.of((update) => {
203
+ if (update.docChanged) {
204
+ updateContentRef.current(update.state.doc.toString());
205
+ }
206
+ })
207
+ ];
184
208
 
185
209
  const state = EditorState.create({
186
210
  doc: contentValue || '',
@@ -233,75 +257,18 @@ const CodeEditorPaneComponent = ({
233
257
  key="html-editor-taglist"
234
258
  label={intl.formatMessage(messages.addLabel)}
235
259
  onTagSelect={handleTagSelect}
236
- onContextChange={(context) => {
237
- }}
260
+ onContextChange={handleTagContextChange}
238
261
  className="tag-list-trigger"
239
- tags={[]} // Empty initially - TagList will fetch from API
240
- injectedTags={{
241
- // Add common HTML/Email specific tags as fallback
242
- 'Customer Info': {
243
- name: 'Customer Info',
244
- desc: 'Customer information tags',
245
- resolved: true,
246
- 'tag-header': true,
247
- subtags: {
248
- 'customer.firstName': {
249
- name: 'First Name',
250
- desc: 'Customer first name',
251
- resolved: true
252
- },
253
- 'customer.lastName': {
254
- name: 'Last Name',
255
- desc: 'Customer last name',
256
- resolved: true
257
- },
258
- 'customer.email': {
259
- name: 'Email',
260
- desc: 'Customer email address',
261
- resolved: true
262
- },
263
- 'customer.phone': {
264
- name: 'Phone',
265
- desc: 'Customer phone number',
266
- resolved: true
267
- }
268
- }
269
- },
270
- 'Common Tags': {
271
- name: 'Common Tags',
272
- desc: 'Commonly used template tags',
273
- resolved: true,
274
- 'tag-header': true,
275
- subtags: {
276
- 'organization.name': {
277
- name: 'Organization Name',
278
- desc: 'Organization name',
279
- resolved: true
280
- },
281
- 'currentDate': {
282
- name: 'Current Date',
283
- desc: 'Current date',
284
- resolved: true
285
- },
286
- 'unsubscribeLink': {
287
- name: 'Unsubscribe Link',
288
- desc: 'Unsubscribe link',
289
- resolved: true
290
- }
291
- }
292
- }
293
- }}
294
- moduleFilterEnabled={true}
295
- userLocale="en"
296
- channel="email"
262
+ tags={tags}
263
+ injectedTags={injectedTags}
264
+ moduleFilterEnabled={moduleFilterEnabled}
265
+ userLocale={userLocale}
266
+ channel={channel}
297
267
  disabled={readOnly}
298
- location={{
299
- query: {
300
- type: 'html-editor' // Identify the context
301
- }
302
- }}
303
- selectedOfferDetails={[]}
304
- eventContextTags={[]}
268
+ location={location}
269
+ selectedOfferDetails={selectedOfferDetails}
270
+ eventContextTags={eventContextTags}
271
+ popoverPlacement="rightTop"
305
272
  />
306
273
  </CapRow>
307
274
  </div>
@@ -323,9 +290,18 @@ CodeEditorPane.propTypes = {
323
290
  readOnly: PropTypes.bool,
324
291
  className: PropTypes.string,
325
292
  isFullscreenMode: PropTypes.bool,
326
- onLabelInsert: PropTypes.func
293
+ onLabelInsert: PropTypes.func,
294
+ // Tag-related props - tags are fetched and managed by parent component
295
+ tags: PropTypes.array,
296
+ injectedTags: PropTypes.object,
297
+ location: PropTypes.object,
298
+ eventContextTags: PropTypes.array,
299
+ selectedOfferDetails: PropTypes.array,
300
+ channel: PropTypes.string,
301
+ userLocale: PropTypes.string,
302
+ moduleFilterEnabled: PropTypes.bool,
303
+ onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
327
304
  };
328
305
 
329
306
  // Export with injectIntl - ref forwarding is handled by forwardRef wrapper
330
- export default injectIntl(CodeEditorPane);
331
-
307
+ export default injectIntl(CodeEditorPane);
@@ -161,7 +161,7 @@
161
161
 
162
162
  // Dragging state styling
163
163
  &--dragging &__splitter {
164
- background-color: map-get($CAP_PRIMARY, base);
164
+ background-color: map-get($CAP_SECONDARY, base);
165
165
  }
166
166
 
167
167
  &--dragging &__splitter-line {
@@ -69,7 +69,10 @@ export const useEditorContent = (
69
69
  } = options;
70
70
 
71
71
  // Unified content state
72
- const [content, setContent] = useState(initialContent || DEFAULT_CONTENT);
72
+ // Use DEFAULT_CONTENT only if initialContent is null or undefined (not empty string)
73
+ // This allows explicitly passing empty string for empty content
74
+ const initialContentValue = initialContent == null ? DEFAULT_CONTENT : initialContent;
75
+ const [content, setContent] = useState(initialContentValue);
73
76
  const [isDirty, setIsDirty] = useState(false);
74
77
  const [lastSaved, setLastSaved] = useState(null);
75
78
  const [isAutoSaveEnabled, setIsAutoSaveEnabled] = useState(autoSave);
@@ -79,7 +82,7 @@ export const useEditorContent = (
79
82
  const validationDebounceRef = useRef(null);
80
83
  const autoSaveRef = useRef(null);
81
84
  const validationCallbackRef = useRef(null);
82
- const lastContentRef = useRef(initialContent || DEFAULT_CONTENT);
85
+ const lastContentRef = useRef(initialContentValue);
83
86
 
84
87
  // Smart debouncing hooks
85
88
  const debouncedOnChange = useSmartDebounce(onChange || (() => {}), PERFORMANCE.PREVIEW_UPDATE_DEBOUNCE);
@@ -4,6 +4,7 @@ import styled from 'styled-components';
4
4
  import get from 'lodash/get';
5
5
  import isEmpty from 'lodash/isEmpty';
6
6
  import cloneDeep from 'lodash/cloneDeep';
7
+ import { CapSpin } from '@capillarytech/cap-ui-library';
7
8
  import TemplatesV2 from '../TemplatesV2';
8
9
  import TemplatePreview from '../../v2Components/TemplatePreview';
9
10
  import SmsWrapper from '../SmsWrapper';
@@ -164,6 +165,7 @@ export function SlideBoxContent(props) {
164
165
  handleTestAndPreview,
165
166
  handleCloseTestAndPreview,
166
167
  isTestAndPreviewMode,
168
+ isLoadingContent = false,
167
169
  } = props;
168
170
  const type = (messageDetails.type || '').toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
169
171
  const query = { type: !isFullMode && 'embedded', module: isFullMode ? 'default' : 'library', isEditFromCampaigns: (templateData || {}).isEditFromCampaigns};
@@ -629,44 +631,92 @@ export function SlideBoxContent(props) {
629
631
  handleTestAndPreview={handleTestAndPreview}
630
632
  handleCloseTestAndPreview={handleCloseTestAndPreview}
631
633
  isTestAndPreviewMode={isTestAndPreviewMode}
634
+ location={location}
632
635
  />
633
636
  )}
634
637
  {(isEditEmailWithId || isEmailEditWithContent) && (
635
- <Email
636
- key="cretives-container-email-edit"
637
- setIsLoadingContent={setIsLoadingContent}
638
- location={{
639
- pathname: `/email/edit`,
640
- query: { type: 'embedded', module: 'library'},
641
- }}
642
- route={{ name: 'email' }}
643
- isGetFormData={isGetFormData}
644
- getFormdata={getFormData}
645
- params={{ id: templateData._id }}
646
- templateData={templateData}
647
- getFormSubscriptionData={getFormData}
648
- getDefaultTags={type}
649
- isFullMode={isFullMode}
650
- editor={editor}
651
- cap={cap}
652
- showTemplateName={showTemplateName}
653
- onValidationFail={onValidationFail}
654
- forwardedTags={forwardedTags}
655
- selectedOfferDetails={selectedOfferDetails}
656
- onPreviewContentClicked={onPreviewContentClicked}
657
- onTestContentClicked={onTestContentClicked}
658
- moduleType={moduleType}
659
- showLiquidErrorInFooter={showLiquidErrorInFooter}
660
- eventContextTags={eventContextTags}
661
- isLoyaltyModule={isLoyaltyModule}
662
- showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
663
- handleTestAndPreview={handleTestAndPreview}
664
- handleCloseTestAndPreview={handleCloseTestAndPreview}
665
- isTestAndPreviewMode={(() => {
666
- return isTestAndPreviewMode;
667
- })()}
668
- />
669
- )}
638
+ (() => {
639
+ const supportCKEditor = commonUtil.hasSupportCKEditor();
640
+ // When supportCKEditor is true: Always use Email component (legacy flow)
641
+ if (supportCKEditor || templateData?.is_drag_drop) {
642
+ return (
643
+ <Email
644
+ key="cretives-container-email-edit"
645
+ setIsLoadingContent={setIsLoadingContent}
646
+ location={{
647
+ pathname: `/email/edit`,
648
+ query: { type: 'embedded', module: 'library'},
649
+ }}
650
+ route={{ name: 'email' }}
651
+ isGetFormData={isGetFormData}
652
+ getFormdata={getFormData}
653
+ params={{ id: templateData._id }}
654
+ templateData={templateData}
655
+ getFormSubscriptionData={getFormData}
656
+ getDefaultTags={type}
657
+ isFullMode={isFullMode}
658
+ editor={editor}
659
+ cap={cap}
660
+ showTemplateName={showTemplateName}
661
+ onValidationFail={onValidationFail}
662
+ forwardedTags={forwardedTags}
663
+ selectedOfferDetails={selectedOfferDetails}
664
+ onPreviewContentClicked={onPreviewContentClicked}
665
+ onTestContentClicked={onTestContentClicked}
666
+ moduleType={moduleType}
667
+ showLiquidErrorInFooter={showLiquidErrorInFooter}
668
+ eventContextTags={eventContextTags}
669
+ isLoyaltyModule={isLoyaltyModule}
670
+ showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
671
+ handleTestAndPreview={handleTestAndPreview}
672
+ handleCloseTestAndPreview={handleCloseTestAndPreview}
673
+ isTestAndPreviewMode={isTestAndPreviewMode}
674
+ />
675
+ );
676
+ }
677
+ // HTML template: Use EmailWrapper component (which uses EmailWithoutSaga)
678
+ return (
679
+ <EmailWrapper
680
+ key="cretives-container-email-edit-wrapper"
681
+ setIsLoadingContent={setIsLoadingContent}
682
+ onEmailModeChange={onEmailModeChange}
683
+ emailCreateMode="editor"
684
+ isGetFormData={isGetFormData}
685
+ getFormdata={getFormData}
686
+ templateData={templateData}
687
+ type={type}
688
+ step="createTemplateContent"
689
+ showNextStep={onCreateNextStep}
690
+ isFullMode={isFullMode}
691
+ editor={editor}
692
+ cap={cap}
693
+ onResetStep={onResetStep}
694
+ showTemplateName={showTemplateName}
695
+ onEnterTemplateName={onEnterTemplateName}
696
+ onRemoveTemplateName={onRemoveTemplateName}
697
+ onValidationFail={onValidationFail}
698
+ forwardedTags={forwardedTags}
699
+ selectedOfferDetails={selectedOfferDetails}
700
+ onPreviewContentClicked={onPreviewContentClicked}
701
+ onTestContentClicked={onTestContentClicked}
702
+ getCmsTemplatesInProgress={getCmsTemplatesInProgress}
703
+ moduleType={moduleType}
704
+ showLiquidErrorInFooter={showLiquidErrorInFooter}
705
+ eventContextTags={eventContextTags}
706
+ isLoyaltyModule={isLoyaltyModule}
707
+ showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
708
+ handleTestAndPreview={handleTestAndPreview}
709
+ handleCloseTestAndPreview={handleCloseTestAndPreview}
710
+ isTestAndPreviewMode={isTestAndPreviewMode}
711
+ location={{
712
+ pathname: `/email/edit/${templateData._id}`,
713
+ query: { type: 'embedded', module: 'library', id: templateData._id },
714
+ }}
715
+ params={{ id: templateData._id }}
716
+ />
717
+ );
718
+ })()
719
+ )}
670
720
  {isEditMPush && (
671
721
  (isFullMode && !commonUtil.hasNewMobilePushFeatureEnabled()) ||
672
722
  (!isFullMode && isLoyaltyModule) ||
@@ -31,8 +31,9 @@ function SlideBoxFooter(props) {
31
31
  showTestAndPreviewButton,
32
32
  shouldShowDoneFooter,
33
33
  shouldShowContinueFooter,
34
+ isContinueButtonDisabled,
35
+ continueButtonLabel,
34
36
  } = props;
35
-
36
37
  return (
37
38
  <div className='template-footer-width'>
38
39
  {isLiquidValidationError && (<ErrorInfoNote errorMessages={errorMessages} currentTab={currentTab?.toUpperCase()} />)}
@@ -70,8 +71,11 @@ function SlideBoxFooter(props) {
70
71
  </div>
71
72
  )}
72
73
  {shouldShowContinueFooter() && (
73
- <CapButton onClick={onCreateNextStep}>
74
- <FormattedMessage {...messages.continue} />
74
+ <CapButton
75
+ onClick={onCreateNextStep}
76
+ disabled={isContinueButtonDisabled || false}
77
+ >
78
+ <FormattedMessage {...(continueButtonLabel || messages.continue)} />
75
79
  </CapButton>
76
80
  )}
77
81
  {slidBoxContent === PREVIEW && (
@@ -99,5 +103,7 @@ SlideBoxFooter.propTypes = {
99
103
  onTestAndPreview: PropTypes.func,
100
104
  isEmptyContent: PropTypes.bool,
101
105
  showTestAndPreviewButton: PropTypes.bool,
106
+ isContinueButtonDisabled: PropTypes.bool,
107
+ continueButtonLabel: PropTypes.object,
102
108
  };
103
109
  export default SlideBoxFooter;