@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
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useRef, useCallback } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { FormattedMessage } from 'react-intl';
4
4
  import styled from 'styled-components';
@@ -11,10 +11,15 @@ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
11
11
  import CapButton from '@capillarytech/cap-ui-library/CapButton';
12
12
  import CapError from '@capillarytech/cap-ui-library/CapError';
13
13
  import ComponentWithLabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
14
- import Email from '../../Email';
14
+ import { CAP_COLOR_06, CAP_WHITE } from '@capillarytech/cap-ui-library/styled/variables';
15
+ import { EmailWithoutSaga } from '../../Email';
15
16
  import CmsTemplatesComponent from '../../../v2Components/CmsTemplatesComponent';
17
+ import EmailHTMLEditor from './EmailHTMLEditor';
18
+ import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
19
+ import { EMAIL } from '../../CreativesContainer/constants';
16
20
  import messages from '../messages';
17
21
  import { EMAIL_CREATE_MODES, STEPS } from '../constants';
22
+ import { hasSupportCKEditor } from '../../../utils/common';
18
23
 
19
24
  const CapRadioCardWithLabel = ComponentWithLabelHOC(CapRadioCard);
20
25
 
@@ -27,9 +32,23 @@ const CardContainer = styled.div`
27
32
  }
28
33
  }
29
34
  }
35
+
36
+ .ant-radio-group.cap-radioCard-v2 {
37
+ .ant-radio-button-wrapper {
38
+ &.ant-radio-button-wrapper-checked {
39
+ .icon-container {
40
+ background-color: ${CAP_COLOR_06};
41
+ .anticon,
42
+ .cap-icon {
43
+ color: ${CAP_WHITE};
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
30
49
  `;
31
50
 
32
- // Mode selection component that handles the creation mode selection UI
51
+ // Mode selection component
33
52
  const ModeSelectionUI = ({
34
53
  isFullMode,
35
54
  templateName,
@@ -51,7 +70,7 @@ const ModeSelectionUI = ({
51
70
  value={templateName}
52
71
  labelPosition="top"
53
72
  size="default"
54
- style={{ width: '68%'}}
73
+ style={{ width: '68%' }}
55
74
  />
56
75
  )}
57
76
  <CardContainer>
@@ -98,15 +117,21 @@ ModeSelectionUI.propTypes = {
98
117
  uploadButtonLabel: PropTypes.node.isRequired,
99
118
  };
100
119
 
101
- // Content creation component that handles the email or template selection UI
120
+ // Content creation component
102
121
  const ContentCreationUI = ({
103
122
  isShowEmailCreate,
104
123
  emailProps,
105
124
  cmsTemplatesProps,
125
+ isHTMLEditorMode,
126
+ htmlEditorProps,
106
127
  }) => (
107
128
  <>
108
129
  {isShowEmailCreate ? (
109
- <Email {...emailProps} />
130
+ isHTMLEditorMode ? (
131
+ <EmailHTMLEditor {...htmlEditorProps} />
132
+ ) : (
133
+ <EmailWithoutSaga {...emailProps} />
134
+ )
110
135
  ) : (
111
136
  <CmsTemplatesComponent {...cmsTemplatesProps} />
112
137
  )}
@@ -117,6 +142,8 @@ ContentCreationUI.propTypes = {
117
142
  isShowEmailCreate: PropTypes.bool.isRequired,
118
143
  emailProps: PropTypes.object.isRequired,
119
144
  cmsTemplatesProps: PropTypes.object.isRequired,
145
+ isHTMLEditorMode: PropTypes.bool,
146
+ htmlEditorProps: PropTypes.object,
120
147
  };
121
148
 
122
149
  // Main EmailWrapper presentational component
@@ -137,9 +164,130 @@ const EmailWrapperView = ({
137
164
  isShowEmailCreate,
138
165
  emailProps,
139
166
  cmsTemplatesProps,
167
+ metaEntities,
168
+ loadingTags,
169
+ injectedTags,
170
+ globalActions,
171
+ supportedTags,
172
+ getDefaultTags,
173
+ location,
174
+ currentOrgDetails,
175
+ forwardedTags,
176
+ selectedOfferDetails,
177
+ eventContextTags,
178
+ getFormdata,
179
+ isGetFormData,
180
+ getLiquidTags,
181
+ showLiquidErrorInFooter,
182
+ onValidationFail,
183
+ emailActions,
184
+ Email,
185
+ templateData: templateDataProp,
186
+ params,
187
+ fetchingLiquidTags,
188
+ createTemplateInProgress,
189
+ fetchingCmsData,
190
+ setIsLoadingContent,
191
+ showTestAndPreviewSlidebox,
192
+ handleCloseTestAndPreview,
193
+ showTemplateName,
140
194
  }) => {
195
+ const htmlEditorRef = useRef(null);
196
+ const supportCKEditor = hasSupportCKEditor();
197
+ const hasParamsIdForEditor = params?.id || location?.query?.id || location?.params?.id || location?.pathname?.includes('/edit/');
198
+ const isEditModeForEditor = hasParamsIdForEditor;
199
+ const isBEEFromProps = emailProps?.editor === 'BEE' && emailProps?.selectedEditorMode === null;
200
+ const isDragDropFromCreateMode = emailCreateMode === EMAIL_CREATE_MODES.DRAG_DROP;
201
+ const isExplicitlyBEEEditor = isBEEFromProps || isDragDropFromCreateMode;
202
+ let isHTMLEditorMode = false;
141
203
 
142
- const isShowTemplateSelection = step === STEPS.MODE_SELECTION || (step === STEPS.TEMPLATE_SELECTION && emailCreateMode === EMAIL_CREATE_MODES.UPLOAD);
204
+ if (supportCKEditor) {
205
+ isHTMLEditorMode = false; // Legacy flow: use Email component
206
+ } else if (isEditModeForEditor) {
207
+ isHTMLEditorMode = !isExplicitlyBEEEditor;
208
+ } else {
209
+ if (isExplicitlyBEEEditor || isBEEFromProps || isDragDropFromCreateMode) {
210
+ isHTMLEditorMode = false;
211
+ } else {
212
+ isHTMLEditorMode = emailProps?.selectedEditorMode === EMAIL_CREATE_MODES.HTML_EDITOR ||
213
+ emailCreateMode === EMAIL_CREATE_MODES.HTML_EDITOR;
214
+ }
215
+ }
216
+
217
+ const isShowTemplateSelection = step === STEPS.MODE_SELECTION;
218
+
219
+ // Create onFormDataChange callback for template name updates (similar to Email component)
220
+ // This allows CreativesContainer to update the template name when user clicks "Edit name"
221
+ // When user edits the name in CreativesContainer header, it calls this callback
222
+ // which updates the template name in EmailWrapper, and then CreativesContainer
223
+ // will call showTemplateName again with the updated formData
224
+ const handleFormDataChange = useCallback((updatedFormData) => {
225
+ const newTemplateName = updatedFormData?.['template-name'] || templateName;
226
+ if (newTemplateName !== templateName && onTemplateNameChange) {
227
+ // Update template name in parent (useEmailWrapper hook)
228
+ onTemplateNameChange({ target: { value: newTemplateName } });
229
+ }
230
+ // Note: CreativesContainer will call showTemplateName again after this callback
231
+ // (it stores the callback in templateContainerDetails and calls it after state updates)
232
+ }, [templateName, onTemplateNameChange]);
233
+
234
+ const htmlEditorProps = isHTMLEditorMode ? {
235
+ // Location and params
236
+ location,
237
+ params,
238
+ // Tag-related props
239
+ getDefaultTags,
240
+ supportedTags,
241
+ metaEntities,
242
+ injectedTags,
243
+ globalActions,
244
+ loadingTags,
245
+ eventContextTags,
246
+ forwardedTags,
247
+ selectedOfferDetails,
248
+ currentOrgDetails,
249
+ // Email Redux state and actions
250
+ Email,
251
+ emailActions,
252
+ // Full mode props
253
+ isFullMode,
254
+ templateName,
255
+ isGetFormData,
256
+ getFormdata,
257
+ // Library mode props
258
+ templateData: templateDataProp,
259
+ // Uploaded content from zip file
260
+ EmailLayout,
261
+ // Liquid validation
262
+ getLiquidTags,
263
+ showLiquidErrorInFooter,
264
+ onValidationFail,
265
+ // Template name
266
+ showTemplateName,
267
+ onFormDataChange: handleFormDataChange,
268
+ // Loading states
269
+ fetchingLiquidTags: fetchingLiquidTags || false,
270
+ createTemplateInProgress: createTemplateInProgress || false,
271
+ fetchingCmsData: fetchingCmsData || false,
272
+ // Parent loading control
273
+ setIsLoadingContent,
274
+ ref: htmlEditorRef,
275
+ } : {};
276
+
277
+ // Get formData for TestAndPreviewSlidebox when needed
278
+ const getFormDataForPreview = () => {
279
+ if (htmlEditorRef.current && htmlEditorRef.current.getFormDataForPreview) {
280
+ return htmlEditorRef.current.getFormDataForPreview();
281
+ }
282
+ return {};
283
+ };
284
+
285
+ const getContentForPreview = () => {
286
+ if (htmlEditorRef.current && htmlEditorRef.current.getContentForPreview) {
287
+ return htmlEditorRef.current.getContentForPreview();
288
+ }
289
+ return '';
290
+ };
143
291
 
144
292
  return (
145
293
  <>
@@ -163,9 +311,22 @@ const EmailWrapperView = ({
163
311
  isShowEmailCreate={isShowEmailCreate}
164
312
  emailProps={emailProps}
165
313
  cmsTemplatesProps={cmsTemplatesProps}
314
+ isHTMLEditorMode={isHTMLEditorMode}
315
+ htmlEditorProps={htmlEditorProps}
166
316
  />
167
317
  )}
168
318
  </CapSpin>
319
+ {/* Render TestAndPreviewSlidebox for HTML Editor (similar to legacy Email component) */}
320
+ {isHTMLEditorMode && showTestAndPreviewSlidebox && (
321
+ <TestAndPreviewSlidebox
322
+ show={showTestAndPreviewSlidebox}
323
+ onClose={handleCloseTestAndPreview}
324
+ formData={getFormDataForPreview()}
325
+ content={getContentForPreview()}
326
+ currentChannel={EMAIL}
327
+ currentTab={1}
328
+ />
329
+ )}
169
330
  </>
170
331
  );
171
332
  };
@@ -175,6 +336,10 @@ EmailWrapperView.propTypes = {
175
336
  emailCreateMode: PropTypes.string,
176
337
  step: PropTypes.string,
177
338
  isFullMode: PropTypes.bool,
339
+ getFormdata: PropTypes.func,
340
+ isGetFormData: PropTypes.bool,
341
+ getLiquidTags: PropTypes.func,
342
+ showLiquidErrorInFooter: PropTypes.func,
178
343
  templateName: PropTypes.string,
179
344
  onTemplateNameChange: PropTypes.func.isRequired,
180
345
  isTemplateNameEmpty: PropTypes.bool,
@@ -187,6 +352,27 @@ EmailWrapperView.propTypes = {
187
352
  isShowEmailCreate: PropTypes.bool.isRequired,
188
353
  emailProps: PropTypes.object.isRequired,
189
354
  cmsTemplatesProps: PropTypes.object.isRequired,
355
+ metaEntities: PropTypes.object,
356
+ loadingTags: PropTypes.bool,
357
+ injectedTags: PropTypes.object,
358
+ globalActions: PropTypes.object,
359
+ supportedTags: PropTypes.array,
360
+ getDefaultTags: PropTypes.string,
361
+ location: PropTypes.object,
362
+ currentOrgDetails: PropTypes.object,
363
+ forwardedTags: PropTypes.object,
364
+ selectedOfferDetails: PropTypes.array,
365
+ eventContextTags: PropTypes.array,
366
+ emailActions: PropTypes.object,
367
+ Email: PropTypes.object,
368
+ templateData: PropTypes.object,
369
+ params: PropTypes.object,
370
+ fetchingLiquidTags: PropTypes.bool,
371
+ createTemplateInProgress: PropTypes.bool,
372
+ fetchingCmsData: PropTypes.bool,
373
+ setIsLoadingContent: PropTypes.func,
374
+ showTestAndPreviewSlidebox: PropTypes.bool,
375
+ handleCloseTestAndPreview: PropTypes.func,
190
376
  };
191
377
 
192
378
  export default EmailWrapperView;
@@ -8,6 +8,8 @@ export const DEFAULT_ACTION = "app/EmailWrapper/DEFAULT_ACTION";
8
8
  export const EMAIL_CREATE_MODES = {
9
9
  UPLOAD: "upload",
10
10
  EDITOR: "editor",
11
+ HTML_EDITOR: "html_editor",
12
+ DRAG_DROP: "drag_drop",
11
13
  };
12
14
 
13
15
  export const STEPS = {