@capillarytech/creatives-library 8.0.102-alpha.0 → 8.0.102

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.102-alpha.0",
4
+ "version": "8.0.102",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -740,10 +740,7 @@ export class Creatives extends React.Component {
740
740
  forEach(androidContent.custom, (customKeyValue) => {
741
741
  custom[customKeyValue.key] = customKeyValue.value;
742
742
  });
743
- //skipping it for MPUSH as custom key is already in the reqd format for hydra sdk
744
- if (channel !== constants.MOBILE_PUSH) {
745
- androidContent.custom = custom;
746
- }
743
+ androidContent.custom = custom;
747
744
  templateData.androidContent = androidContent;
748
745
  templateData.androidContent.type = get(channelTemplate, 'definition.mode', '').toUpperCase();
749
746
  templateData.androidContent.deviceType = 'ANDROID';
@@ -759,10 +756,7 @@ export class Creatives extends React.Component {
759
756
  forEach(iosContent.custom, (customKeyValue) => {
760
757
  custom[customKeyValue.key] = customKeyValue.value;
761
758
  });
762
- //skipping it for MPUSH as custom key is already in the reqd format for hydra sdk
763
- if (channel !== constants.MOBILE_PUSH) {
764
- iosContent.custom = custom;
765
- }
759
+ iosContent.custom = custom;
766
760
  templateData.iosContent = iosContent;
767
761
  templateData.iosContent.type = get(channelTemplate, 'definition.mode').toUpperCase();
768
762
  templateData.iosContent.deviceType = IOS.toUpperCase();
@@ -6,9 +6,14 @@
6
6
  import PropTypes from 'prop-types';
7
7
  import React from 'react';
8
8
  import { connect } from 'react-redux';
9
- import { injectIntl, intlShape } from 'react-intl';
9
+ import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
10
+ import { GA } from '@capillarytech/cap-ui-utils';
10
11
  import { createStructuredSelector } from 'reselect';
11
12
  import { bindActionCreators } from 'redux';
13
+ import _ from 'lodash';
14
+ import styled from 'styled-components';
15
+ import { CapRadioCard, CapNotification, CapInput, CapUploader, CapSpin, CapButton, CapError } from '@capillarytech/cap-ui-library';
16
+ import ComponentWithLabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
12
17
  import { UserIsAuthenticated } from '../../utils/authWrapper';
13
18
  import {
14
19
  selectEmailLayout,
@@ -18,48 +23,302 @@ import {
18
23
  selectCmsTemplatesLoader,
19
24
  } from '../Templates/selectors';
20
25
  import * as templatesActionsCreators from '../Templates/actions';
26
+ import Email from '../Email';
27
+ import CmsTemplatesComponent from '../../v2Components/CmsTemplatesComponent';
28
+ import messages from './messages';
29
+ import { CHANNEL_CREATE_TRACK_MAPPING } from '../App/constants';
30
+ import { gtmPush } from '../../utils/gtmTrackers';
31
+ import { EMAIL } from '../CreativesContainer/constants';
21
32
  import { selectCurrentOrgDetails } from "../../v2Containers/Cap/selectors";
22
- import EmailWrapperView from './components/EmailWrapperView';
23
- import useEmailWrapper from './hooks/useEmailWrapper';
24
33
 
25
- const EmailWrapper = (props) => {
26
- // Pass all props to the custom hook
27
- const {
28
- templateName,
29
- isTemplateNameEmpty,
30
- modeContent,
31
- modes,
32
- isShowEmailCreate,
33
- emailProps,
34
- cmsTemplatesProps,
35
- uploadButtonLabel,
36
- onTemplateNameChange,
37
- onChange,
38
- useFileUpload,
39
- } = useEmailWrapper(props);
34
+ const CapRadioCardWithLabel = ComponentWithLabelHOC(CapRadioCard);
35
+ const { timeTracker } = GA;
36
+ const CardContainer = styled.div`
37
+ margin-top: 16px;
38
+ .ant-radio-group{
39
+ .ant-radio-button-wrapper{
40
+ &:first-child{
41
+ margin-left: unset;
42
+ }
43
+ }
44
+
45
+ }
46
+ `;
47
+ export class EmailWrapper extends React.Component { // eslint-disable-line react/prefer-stateless-function
48
+ constructor(props) {
49
+ super(props);
50
+ this.emailUploader = React.createRef();
51
+ this.state = {
52
+ templateName: '',
53
+ routeParams: {
54
+ pathname: `/email/create`,
55
+ query: { module: 'library', type: 'embedded' },
56
+ },
57
+ modeContent: {
40
58
 
41
- // Render using the presentation component with data from the hook
42
- return (
43
- <EmailWrapperView
44
- isUploading={props.isUploading}
45
- emailCreateMode={props.emailCreateMode}
46
- step={props.step}
47
- isFullMode={props.isFullMode}
48
- templateName={templateName}
49
- onTemplateNameChange={onTemplateNameChange}
50
- isTemplateNameEmpty={isTemplateNameEmpty}
51
- modes={modes}
52
- onChange={onChange}
53
- EmailLayout={props.EmailLayout}
54
- modeContent={modeContent}
55
- useFileUpload={useFileUpload}
56
- uploadButtonLabel={uploadButtonLabel}
57
- isShowEmailCreate={isShowEmailCreate}
58
- emailProps={emailProps}
59
- cmsTemplatesProps={cmsTemplatesProps}
60
- />
61
- );
62
- };
59
+ },
60
+ // selectedCreateMode: '',
61
+ isTemplateNameEmpty: true,
62
+ };
63
+ this.modes = [
64
+ {
65
+ title: <FormattedMessage {...messages.zipUpload} />,
66
+ content: <FormattedMessage {...messages.zipUploadDesc} />,
67
+ value: 'upload',
68
+ // onClick: () => {
69
+ // this.emailUploader.current.click();
70
+ // },
71
+ },
72
+ {
73
+ title: <FormattedMessage {...messages.useEditor} />,
74
+ content: <FormattedMessage {...messages.useEditorDesc} />,
75
+ value: 'editor',
76
+ },
77
+ ];
78
+ }
79
+
80
+ componentDidUpdate() {
81
+ const { modeContent } = this.state;
82
+ // const isValid = this.isValid();
83
+ const { emailCreateMode, EmailLayout, CmsTemplates, step, SelectedEdmDefaultTemplate, templatesActions, getCmsTemplatesInProgress = false } = this.props;
84
+ if (step === "modeSelection" && !this.state.selectedCreateMode && emailCreateMode === 'upload' && !EmailLayout) {
85
+ //document.getElementById('upload-email-template').click();
86
+ } else if (step === "templateSelection" && !this.state.selectedCreateMode) {
87
+ if (emailCreateMode === "editor" && !CmsTemplates && !getCmsTemplatesInProgress) {
88
+ templatesActions.getDefaultBeeTemplates();
89
+ }
90
+ } else if (step === "createTemplateContent" && !this.state.selectedCreateMode) {
91
+ if (emailCreateMode === 'upload' && !_.isEmpty(EmailLayout)) {
92
+ this.setCreateMode('upload');
93
+ } else if (emailCreateMode === "editor" && _.isEmpty(SelectedEdmDefaultTemplate)) {
94
+ this.handleEdmDefaultTemplateSelection(modeContent.id);
95
+ }
96
+ }
97
+ }
98
+
99
+
100
+ componentWillUnmount() {
101
+ this.props.onResetStep();
102
+ this.props.templatesActions.resetTemplateData();
103
+ }
104
+ onTemplateNameChange = ({target: {value}}) => {
105
+ const {onEnterTemplateName, onRemoveTemplateName} = this.props;
106
+ const isEmptyTemplateName = !value?.trim();
107
+ this.setState({templateName: value, isTemplateNameEmpty: isEmptyTemplateName});
108
+ if (value && onEnterTemplateName) {
109
+ onEnterTemplateName();
110
+ } else if (onRemoveTemplateName) {
111
+ onRemoveTemplateName();
112
+ }
113
+ }
114
+ onChange = (e) => {
115
+ this.props.onEmailModeChange(e.target.value);
116
+ };
117
+ //isValid = () => !_.isEmpty(this.state.templateName)
118
+ setCreateMode = (emailCreateMode) => {
119
+ this.setState({ selectedCreateMode: emailCreateMode });
120
+ }
121
+ handleZipUploadError = () => {
122
+ const message = {
123
+ key: "email-upload-error",
124
+ message: this.props.intl.formatMessage(messages.invalidUploadFileError2),
125
+ description: this.props.intl.formatMessage(messages.invalidUploadFileErrorDesc2),
126
+ };
127
+ CapNotification.error(message);
128
+ }
129
+ stopTimerGA = () => {
130
+ // stop timer
131
+ const timeTaken = timeTracker.stopTimer(CHANNEL_CREATE_TRACK_MAPPING.email, {
132
+ category: 'Creatives',
133
+ action: 'Create',
134
+ label: 'uploadZip',
135
+ });
136
+ gtmPush('creativeDetails', {
137
+ channel: EMAIL,
138
+ timeTaken,
139
+ mode: 'uploadZip',
140
+ });
141
+ }
142
+ handleFileUpload = (file = {}) => {
143
+ const { templatesActions, intl, showNextStep, isUploading } = this.props;
144
+ if (!isUploading) {
145
+ const fileExtension = file.name.split('.').pop();
146
+ const supportedZipFormats = ['zip'];
147
+
148
+ //converted file size to MB
149
+ const fileSize = file && (file.size / ( 1024 * 1024 ));
150
+
151
+
152
+ //Here fileSize is in MB
153
+ if (fileSize > 5) {
154
+ const message = {
155
+ key: "email-upload-error",
156
+ message: intl.formatMessage(messages.invalidUploadFileError),
157
+ description: intl.formatMessage(messages.invalidUploadFileErrorDesc3),
158
+ };
159
+ CapNotification.error(message);
160
+ } else if (supportedZipFormats.indexOf(fileExtension.toLowerCase()) !== -1) {
161
+ templatesActions.handleZipUpload(file.originFileObj, () => { //handle upload success
162
+ this.stopTimerGA();
163
+ this.setState({ modeContent: { file } }, showNextStep);
164
+ }, this.handleZipUploadError);
165
+ } else if (fileExtension === 'html' || fileExtension === 'htm') {
166
+ const reader = new FileReader();
167
+ reader.onload = () => {
168
+ const text = reader.result;
169
+ this.setState({ modeContent: { file } }, () => {
170
+ templatesActions.handleHtmlUpload(text);
171
+ });
172
+ // showNextStep();
173
+ };
174
+ reader.readAsText(file.originFileObj);
175
+ } else {
176
+ const message = {
177
+ key: "email-upload-error",
178
+ message: intl.formatMessage(messages.invalidUploadFileError),
179
+ description: intl.formatMessage(messages.invalidUploadFileErrorDesc),
180
+ };
181
+ CapNotification.error(message);
182
+ }
183
+ }
184
+ };
185
+
186
+ handleEdmDefaultTemplateSelection = (id) => {
187
+ const { CmsTemplates, templatesActions } = this.props;
188
+ const data = _.find(CmsTemplates, { _id: id });
189
+
190
+ templatesActions.setEdmTemplate(data);
191
+ templatesActions.setBEETemplate(data);
192
+ this.setState({
193
+ selectedCreateMode: 'editor',
194
+ });
195
+ };
196
+ useFileUpload = ({ file }) => {
197
+ this.setState({ modeContent: {} }, () => {
198
+ this.handleFileUpload(file);
199
+ });
200
+ }
201
+ useEditor = (id) => {
202
+ this.setState({ modeContent: { id } }, this.props.showNextStep);
203
+ }
204
+
205
+ isShowEmailCreate = () => !_.isEmpty(this.state.selectedCreateMode) && (!_.isEmpty(this.props.EmailLayout) || this.props.SelectedEdmDefaultTemplate)
206
+ render() {
207
+ const {
208
+ showTemplateName,
209
+ emailCreateMode,
210
+ isGetFormData,
211
+ getFormdata,
212
+ type,
213
+ CmsTemplates,
214
+ step,
215
+ cap,
216
+ isFullMode,
217
+ EmailLayout,
218
+ setIsLoadingContent,
219
+ onValidationFail,
220
+ forwardedTags,
221
+ selectedOfferDetails,
222
+ onPreviewContentClicked,
223
+ onTestContentClicked,
224
+ cmsTemplatesLoader,
225
+ editor,
226
+ currentOrgDetails,
227
+ moduleType,
228
+ showLiquidErrorInFooter,
229
+ eventContextTags,
230
+ // Flag to enable loyalty module specific features in the email editor
231
+ isLoyaltyModule,
232
+ } = this.props;
233
+ const {
234
+ templateName,
235
+ modeContent,
236
+ isTemplateNameEmpty,
237
+ } = this.state;
238
+ const isShowEmailCreate = this.isShowEmailCreate();
239
+ return (
240
+ <div>
241
+ <CapSpin spinning={emailCreateMode === "upload" ? this.props.isUploading : false} >
242
+ {step === "modeSelection" || (step === "templateSelection" && emailCreateMode === "upload") ?
243
+ <div>
244
+ {isFullMode &&
245
+ <CapInput
246
+ label={<FormattedMessage {...messages.creativeName} />}
247
+ onChange={this.onTemplateNameChange}
248
+ value={templateName}
249
+ labelPosition="top"
250
+ size="default"
251
+ style={{ width: '68%'}}
252
+ />
253
+ }
254
+ <CardContainer>
255
+ <CapRadioCardWithLabel
256
+ panes={this.modes}
257
+ onChange={this.onChange}
258
+ selected={emailCreateMode}
259
+ label={<FormattedMessage {...messages.createMode} />}
260
+ />
261
+ </CardContainer>
262
+ <div>
263
+ {emailCreateMode === "upload" &&
264
+ <div style={{ marginLeft: '8px' }}>
265
+ <CapUploader onChange={this.useFileUpload} accept=".zip, .html, .htm" showUploadList={false}>
266
+ { (isFullMode && isTemplateNameEmpty) &&
267
+ <CapError type="error">
268
+ < FormattedMessage {...messages.emptyTemplateName} />
269
+ </CapError>
270
+ }
271
+ <CapButton disabled={isFullMode && isTemplateNameEmpty}>{this.props.intl.formatMessage(messages.upload)}</CapButton>
272
+ </CapUploader>
273
+ {!_.isEmpty(EmailLayout) &&
274
+ <div>{_.get(modeContent, "file.name")}</div>
275
+ }
276
+ </div>
277
+ }
278
+ </div>
279
+ </div>
280
+ :
281
+ <div>
282
+ {isShowEmailCreate && <Email
283
+ setIsLoadingContent={setIsLoadingContent}
284
+ key="email-create-template"
285
+ location={this.state.routeParams}
286
+ route={{ name: 'email' }}
287
+ params={{}}
288
+ isGetFormData={isGetFormData}
289
+ getFormdata={getFormdata}
290
+ getFormSubscriptionData={getFormdata}
291
+ getDefaultTags={type}
292
+ isFullMode={isFullMode}
293
+ defaultData={{ 'template-name': templateName }}
294
+ cap={cap}
295
+ showTemplateName={showTemplateName}
296
+ showLiquidErrorInFooter={showLiquidErrorInFooter}
297
+ onValidationFail={onValidationFail}
298
+ forwardedTags={forwardedTags}
299
+ selectedOfferDetails={selectedOfferDetails}
300
+ onPreviewContentClicked={onPreviewContentClicked}
301
+ onTestContentClicked={onTestContentClicked}
302
+ editor={editor}
303
+ moduleType={moduleType}
304
+ eventContextTags={eventContextTags}
305
+ isLoyaltyModule={isLoyaltyModule}
306
+ />}
307
+ {!isShowEmailCreate && (
308
+ <CmsTemplatesComponent
309
+ cmsTemplates={CmsTemplates}
310
+ handleEdmDefaultTemplateSelection={this.useEditor}
311
+ cmsTemplatesLoader={cmsTemplatesLoader}
312
+ currentOrgDetails={currentOrgDetails || cap?.currentOrgDetails}
313
+ />
314
+ )}
315
+ </div>
316
+ }
317
+ </CapSpin>
318
+ </div>
319
+ );
320
+ }
321
+ }
63
322
 
64
323
  EmailWrapper.propTypes = {
65
324
  onEmailModeChange: PropTypes.func.isRequired,
@@ -1,184 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import { FormattedMessage } from 'react-intl';
4
- import styled from 'styled-components';
5
- import _ from 'lodash';
6
- import { CapRadioCard, CapInput, CapUploader, CapSpin, CapButton, CapError } from '@capillarytech/cap-ui-library';
7
- import ComponentWithLabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
8
- import Email from '../../Email';
9
- import CmsTemplatesComponent from '../../../v2Components/CmsTemplatesComponent';
10
- import messages from '../messages';
11
-
12
- const CapRadioCardWithLabel = ComponentWithLabelHOC(CapRadioCard);
13
-
14
- const CardContainer = styled.div`
15
- margin-top: 16px;
16
- .ant-radio-group{
17
- .ant-radio-button-wrapper{
18
- &:first-child{
19
- margin-left: unset;
20
- }
21
- }
22
- }
23
- `;
24
-
25
- // Mode selection component that handles the creation mode selection UI
26
- const ModeSelectionUI = ({
27
- isFullMode,
28
- templateName,
29
- onTemplateNameChange,
30
- isTemplateNameEmpty,
31
- modes,
32
- emailCreateMode,
33
- onChange,
34
- EmailLayout,
35
- modeContent,
36
- useFileUpload,
37
- uploadButtonLabel,
38
- }) => (
39
- <div>
40
- {isFullMode && (
41
- <CapInput
42
- label={<FormattedMessage {...messages.creativeName} />}
43
- onChange={onTemplateNameChange}
44
- value={templateName}
45
- labelPosition="top"
46
- size="default"
47
- style={{ width: '68%'}}
48
- />
49
- )}
50
- <CardContainer>
51
- <CapRadioCardWithLabel
52
- panes={modes}
53
- onChange={onChange}
54
- selected={emailCreateMode}
55
- label={<FormattedMessage {...messages.createMode} />}
56
- />
57
- </CardContainer>
58
- <div>
59
- {emailCreateMode === "upload" && (
60
- <div style={{ marginLeft: '8px' }}>
61
- <CapUploader onChange={useFileUpload} accept=".zip, .html, .htm" showUploadList={false}>
62
- {(isFullMode && isTemplateNameEmpty) && (
63
- <CapError type="error">
64
- <FormattedMessage {...messages.emptyTemplateName} />
65
- </CapError>
66
- )}
67
- <CapButton disabled={isFullMode && isTemplateNameEmpty}>
68
- {uploadButtonLabel}
69
- </CapButton>
70
- </CapUploader>
71
- {!_.isEmpty(EmailLayout) && (
72
- <div>{_.get(modeContent, "file.name")}</div>
73
- )}
74
- </div>
75
- )}
76
- </div>
77
- </div>
78
- );
79
-
80
- ModeSelectionUI.propTypes = {
81
- isFullMode: PropTypes.bool,
82
- templateName: PropTypes.string,
83
- onTemplateNameChange: PropTypes.func.isRequired,
84
- isTemplateNameEmpty: PropTypes.bool,
85
- modes: PropTypes.array.isRequired,
86
- emailCreateMode: PropTypes.string,
87
- onChange: PropTypes.func.isRequired,
88
- EmailLayout: PropTypes.object,
89
- modeContent: PropTypes.object,
90
- useFileUpload: PropTypes.func.isRequired,
91
- uploadButtonLabel: PropTypes.node.isRequired,
92
- };
93
-
94
- // Content creation component that handles the email or template selection UI
95
- const ContentCreationUI = ({
96
- isShowEmailCreate,
97
- emailProps,
98
- cmsTemplatesProps,
99
- }) => (
100
- <div>
101
- {isShowEmailCreate ? (
102
- <Email {...emailProps} />
103
- ) : (
104
- <CmsTemplatesComponent {...cmsTemplatesProps} />
105
- )}
106
- </div>
107
- );
108
-
109
- ContentCreationUI.propTypes = {
110
- isShowEmailCreate: PropTypes.bool.isRequired,
111
- emailProps: PropTypes.object.isRequired,
112
- cmsTemplatesProps: PropTypes.object.isRequired,
113
- };
114
-
115
- // Main EmailWrapper presentational component
116
- const EmailWrapperView = ({
117
- isUploading,
118
- emailCreateMode,
119
- step,
120
- isFullMode,
121
- templateName,
122
- onTemplateNameChange,
123
- isTemplateNameEmpty,
124
- modes,
125
- onChange,
126
- EmailLayout,
127
- modeContent,
128
- useFileUpload,
129
- uploadButtonLabel,
130
- isShowEmailCreate,
131
- emailProps,
132
- cmsTemplatesProps,
133
- }) => {
134
- const isShowTemplateSelection = step === "modeSelection" || (step === "templateSelection" && emailCreateMode === "upload");
135
-
136
- return (
137
- <div>
138
- <CapSpin spinning={emailCreateMode === "upload" ? isUploading : false}>
139
- {isShowTemplateSelection ? (
140
- <ModeSelectionUI
141
- isFullMode={isFullMode}
142
- templateName={templateName}
143
- onTemplateNameChange={onTemplateNameChange}
144
- isTemplateNameEmpty={isTemplateNameEmpty}
145
- modes={modes}
146
- emailCreateMode={emailCreateMode}
147
- onChange={onChange}
148
- EmailLayout={EmailLayout}
149
- modeContent={modeContent}
150
- useFileUpload={useFileUpload}
151
- uploadButtonLabel={uploadButtonLabel}
152
- />
153
- ) : (
154
- <ContentCreationUI
155
- isShowEmailCreate={isShowEmailCreate}
156
- emailProps={emailProps}
157
- cmsTemplatesProps={cmsTemplatesProps}
158
- />
159
- )}
160
- </CapSpin>
161
- </div>
162
- );
163
- };
164
-
165
- EmailWrapperView.propTypes = {
166
- isUploading: PropTypes.bool,
167
- emailCreateMode: PropTypes.string,
168
- step: PropTypes.string,
169
- isFullMode: PropTypes.bool,
170
- templateName: PropTypes.string,
171
- onTemplateNameChange: PropTypes.func.isRequired,
172
- isTemplateNameEmpty: PropTypes.bool,
173
- modes: PropTypes.array.isRequired,
174
- onChange: PropTypes.func.isRequired,
175
- EmailLayout: PropTypes.object,
176
- modeContent: PropTypes.object,
177
- useFileUpload: PropTypes.func.isRequired,
178
- uploadButtonLabel: PropTypes.node.isRequired,
179
- isShowEmailCreate: PropTypes.bool.isRequired,
180
- emailProps: PropTypes.object.isRequired,
181
- cmsTemplatesProps: PropTypes.object.isRequired,
182
- };
183
-
184
- export default EmailWrapperView;
@@ -1,289 +0,0 @@
1
- import { useState, useEffect, useMemo, useCallback } from 'react';
2
- import _ from 'lodash';
3
- import { GA } from '@capillarytech/cap-ui-utils';
4
- import { CapNotification } from '@capillarytech/cap-ui-library';
5
- import { CHANNEL_CREATE_TRACK_MAPPING } from '../../App/constants';
6
- import { gtmPush } from '../../../utils/gtmTrackers';
7
- import { EMAIL } from '../../CreativesContainer/constants';
8
- import messages from '../messages';
9
-
10
- const { timeTracker } = GA;
11
-
12
- /**
13
- * Custom hook to handle EmailWrapper component business logic
14
- */
15
- const useEmailWrapper = ({
16
- intl,
17
- onEmailModeChange,
18
- emailCreateMode,
19
- step,
20
- EmailLayout,
21
- CmsTemplates,
22
- SelectedEdmDefaultTemplate,
23
- isUploading,
24
- templatesActions,
25
- showNextStep,
26
- onResetStep,
27
- onEnterTemplateName,
28
- onRemoveTemplateName,
29
- getCmsTemplatesInProgress,
30
- // Props for Email component
31
- setIsLoadingContent,
32
- isGetFormData,
33
- getFormdata,
34
- type,
35
- isFullMode,
36
- cap,
37
- showTemplateName,
38
- showLiquidErrorInFooter,
39
- onValidationFail,
40
- forwardedTags,
41
- selectedOfferDetails,
42
- onPreviewContentClicked,
43
- onTestContentClicked,
44
- editor,
45
- moduleType,
46
- eventContextTags,
47
- isLoyaltyModule,
48
- // Props for CmsTemplates component
49
- cmsTemplatesLoader,
50
- currentOrgDetails,
51
- }) => {
52
- // State management
53
- const [templateName, setTemplateName] = useState('');
54
- const [isTemplateNameEmpty, setIsTemplateNameEmpty] = useState(true);
55
- const [selectedCreateMode, setSelectedCreateMode] = useState('');
56
- const [modeContent, setModeContent] = useState({});
57
- const [routeParams] = useState({
58
- pathname: `/email/create`,
59
- query: { module: 'library', type: 'embedded' },
60
- });
61
-
62
- // Cleanup effect
63
- useEffect(() => {
64
- return () => {
65
- onResetStep();
66
- templatesActions.resetTemplateData();
67
- };
68
- }, [onResetStep, templatesActions]);
69
-
70
- // Main logic effect
71
- useEffect(() => {
72
- if (step === "modeSelection" && !selectedCreateMode && emailCreateMode === 'upload' && !EmailLayout) {
73
- //document.getElementById('upload-email-template').click();
74
- } else if (step === "templateSelection" && !selectedCreateMode) {
75
- if (emailCreateMode === "editor" && !CmsTemplates && !getCmsTemplatesInProgress) {
76
- templatesActions.getDefaultBeeTemplates();
77
- }
78
- } else if (step === "createTemplateContent" && !selectedCreateMode) {
79
- if (emailCreateMode === 'upload' && !_.isEmpty(EmailLayout)) {
80
- setSelectedCreateMode('upload');
81
- } else if (emailCreateMode === "editor" && _.isEmpty(SelectedEdmDefaultTemplate)) {
82
- handleEdmDefaultTemplateSelection(modeContent.id);
83
- }
84
- }
85
- }, [
86
- step,
87
- selectedCreateMode,
88
- emailCreateMode,
89
- EmailLayout,
90
- CmsTemplates,
91
- getCmsTemplatesInProgress,
92
- modeContent.id,
93
- SelectedEdmDefaultTemplate,
94
- templatesActions
95
- ]);
96
-
97
- // Event handlers
98
- const onTemplateNameChange = useCallback(({target: {value}}) => {
99
- const isEmptyTemplateName = !value?.trim();
100
- setTemplateName(value);
101
- setIsTemplateNameEmpty(isEmptyTemplateName);
102
-
103
- if (value && onEnterTemplateName) {
104
- onEnterTemplateName();
105
- } else if (onRemoveTemplateName) {
106
- onRemoveTemplateName();
107
- }
108
- }, [onEnterTemplateName, onRemoveTemplateName]);
109
-
110
- const onChange = useCallback((e) => {
111
- onEmailModeChange(e.target.value);
112
- }, [onEmailModeChange]);
113
-
114
- const handleZipUploadError = useCallback(() => {
115
- const message = {
116
- key: "email-upload-error",
117
- message: intl.formatMessage(messages.invalidUploadFileError2),
118
- description: intl.formatMessage(messages.invalidUploadFileErrorDesc2),
119
- };
120
- CapNotification.error(message);
121
- }, [intl]);
122
-
123
- const stopTimerGA = useCallback(() => {
124
- const timeTaken = timeTracker.stopTimer(CHANNEL_CREATE_TRACK_MAPPING.email, {
125
- category: 'Creatives',
126
- action: 'Create',
127
- label: 'uploadZip',
128
- });
129
- gtmPush('creativeDetails', {
130
- channel: EMAIL,
131
- timeTaken,
132
- mode: 'uploadZip',
133
- });
134
- }, []);
135
-
136
- const handleFileUpload = useCallback((file = {}) => {
137
- if (!isUploading) {
138
- const fileExtension = file.name.split('.').pop();
139
- const supportedZipFormats = ['zip'];
140
-
141
- // Converted file size to MB
142
- const fileSize = file && (file.size / (1024 * 1024));
143
-
144
- // Here fileSize is in MB
145
- if (fileSize > 5) {
146
- const message = {
147
- key: "email-upload-error",
148
- message: intl.formatMessage(messages.invalidUploadFileError),
149
- description: intl.formatMessage(messages.invalidUploadFileErrorDesc3),
150
- };
151
- CapNotification.error(message);
152
- } else if (supportedZipFormats.indexOf(fileExtension.toLowerCase()) !== -1) {
153
- templatesActions.handleZipUpload(file.originFileObj, () => { // Handle upload success
154
- stopTimerGA();
155
- setModeContent({ file });
156
- showNextStep();
157
- }, handleZipUploadError);
158
- } else if (fileExtension === 'html' || fileExtension === 'htm') {
159
- const reader = new FileReader();
160
- reader.onload = () => {
161
- const text = reader.result;
162
- setModeContent({ file });
163
- templatesActions.handleHtmlUpload(text);
164
- };
165
- reader.readAsText(file.originFileObj);
166
- } else {
167
- const message = {
168
- key: "email-upload-error",
169
- message: intl.formatMessage(messages.invalidUploadFileError),
170
- description: intl.formatMessage(messages.invalidUploadFileErrorDesc),
171
- };
172
- CapNotification.error(message);
173
- }
174
- }
175
- }, [isUploading, intl, templatesActions, stopTimerGA, handleZipUploadError, showNextStep]);
176
-
177
- const handleEdmDefaultTemplateSelection = useCallback((id) => {
178
- const data = _.find(CmsTemplates, { _id: id });
179
- templatesActions.setEdmTemplate(data);
180
- templatesActions.setBEETemplate(data);
181
- setSelectedCreateMode('editor');
182
- }, [CmsTemplates, templatesActions]);
183
-
184
- const useFileUpload = useCallback(({ file }) => {
185
- setModeContent({});
186
- handleFileUpload(file);
187
- }, [handleFileUpload]);
188
-
189
- const useEditor = useCallback((id) => {
190
- setModeContent({ id });
191
- showNextStep();
192
- }, [showNextStep]);
193
-
194
- // Derived state
195
- const isShowEmailCreate = !_.isEmpty(selectedCreateMode) && (!_.isEmpty(EmailLayout) || SelectedEdmDefaultTemplate);
196
-
197
- // Memoize static data
198
- const modes = useMemo(() => [
199
- {
200
- title: intl.formatMessage(messages.zipUpload),
201
- content: intl.formatMessage(messages.zipUploadDesc),
202
- value: 'upload',
203
- },
204
- {
205
- title: intl.formatMessage(messages.useEditor),
206
- content: intl.formatMessage(messages.useEditorDesc),
207
- value: 'editor',
208
- },
209
- ], [intl]);
210
-
211
- // Prepare props for Email component
212
- const emailProps = useMemo(() => ({
213
- setIsLoadingContent,
214
- key: "email-create-template",
215
- location: routeParams,
216
- route: { name: 'email' },
217
- params: {},
218
- isGetFormData,
219
- getFormdata,
220
- getFormSubscriptionData: getFormdata,
221
- getDefaultTags: type,
222
- isFullMode,
223
- defaultData: { 'template-name': templateName },
224
- cap,
225
- showTemplateName,
226
- showLiquidErrorInFooter,
227
- onValidationFail,
228
- forwardedTags,
229
- selectedOfferDetails,
230
- onPreviewContentClicked,
231
- onTestContentClicked,
232
- editor,
233
- moduleType,
234
- eventContextTags,
235
- isLoyaltyModule,
236
- }), [
237
- setIsLoadingContent,
238
- routeParams,
239
- isGetFormData,
240
- getFormdata,
241
- type,
242
- isFullMode,
243
- templateName,
244
- cap,
245
- showTemplateName,
246
- showLiquidErrorInFooter,
247
- onValidationFail,
248
- forwardedTags,
249
- selectedOfferDetails,
250
- onPreviewContentClicked,
251
- onTestContentClicked,
252
- editor,
253
- moduleType,
254
- eventContextTags,
255
- isLoyaltyModule,
256
- ]);
257
-
258
- // Prepare props for CmsTemplatesComponent
259
- const cmsTemplatesProps = useMemo(() => ({
260
- cmsTemplates: CmsTemplates,
261
- handleEdmDefaultTemplateSelection: useEditor,
262
- cmsTemplatesLoader,
263
- currentOrgDetails: currentOrgDetails || cap?.currentOrgDetails,
264
- }), [CmsTemplates, useEditor, cmsTemplatesLoader, currentOrgDetails, cap]);
265
-
266
- // Upload button label
267
- const uploadButtonLabel = intl.formatMessage(messages.upload);
268
-
269
- return {
270
- // State
271
- templateName,
272
- isTemplateNameEmpty,
273
- modeContent,
274
-
275
- // Derived values
276
- modes,
277
- isShowEmailCreate,
278
- emailProps,
279
- cmsTemplatesProps,
280
- uploadButtonLabel,
281
-
282
- // Event handlers
283
- onTemplateNameChange,
284
- onChange,
285
- useFileUpload,
286
- };
287
- };
288
-
289
- export default useEmailWrapper;
@@ -1,127 +0,0 @@
1
- export const EmailWrapperMockData = {
2
- onEmailModeChange: jest.fn(),
3
- showTemplateName: jest.fn(),
4
- emailCreateMode: 'editor',
5
- isGetFormData: false,
6
- getFormdata: jest.fn(),
7
- type: 'email',
8
- step: 'modeSelection',
9
- cap: { currentOrgDetails: { basic_details: {} } },
10
- isFullMode: true,
11
- setIsLoadingContent: jest.fn(),
12
- onValidationFail: jest.fn(),
13
- forwardedTags: {},
14
- selectedOfferDetails: [],
15
- onPreviewContentClicked: jest.fn(),
16
- onTestContentClicked: jest.fn(),
17
- editor: {},
18
- moduleType: 'email',
19
- showLiquidErrorInFooter: jest.fn(),
20
- eventContextTags: [],
21
- isLoyaltyModule: false,
22
- isUploading: false,
23
- templatesActions: {
24
- resetTemplateData: jest.fn(),
25
- getDefaultBeeTemplates: jest.fn(),
26
- handleZipUpload: jest.fn(),
27
- handleHtmlUpload: jest.fn(),
28
- setEdmTemplate: jest.fn(),
29
- setBEETemplate: jest.fn(),
30
- },
31
- showNextStep: jest.fn(),
32
- onResetStep: jest.fn(),
33
- onEnterTemplateName: jest.fn(),
34
- onRemoveTemplateName: jest.fn(),
35
- getCmsTemplatesInProgress: false,
36
- EmailLayout: null,
37
- CmsTemplates: [
38
- {
39
- _id: 'template1',
40
- name: 'Template 1',
41
- content: '<html><body>Template 1 Content</body></html>'
42
- },
43
- {
44
- _id: 'template2',
45
- name: 'Template 2',
46
- content: '<html><body>Template 2 Content</body></html>'
47
- }
48
- ],
49
- SelectedEdmDefaultTemplate: null,
50
- cmsTemplatesLoader: false,
51
- currentOrgDetails: { basic_details: {} },
52
- intl: {
53
- formatMessage: jest.fn((message) => message.defaultMessage || ''),
54
- },
55
- };
56
-
57
- export const EmailWrapperViewMockProps = {
58
- isUploading: false,
59
- emailCreateMode: 'editor',
60
- step: 'modeSelection',
61
- isFullMode: true,
62
- templateName: 'Test Template',
63
- onTemplateNameChange: jest.fn(),
64
- isTemplateNameEmpty: false,
65
- modes: [
66
- {
67
- title: 'Upload zip file',
68
- content: 'Upload compressed file (Zip) containing HTML & images',
69
- value: 'upload',
70
- },
71
- {
72
- title: 'Create using editor',
73
- content: 'Create using in-built template',
74
- value: 'editor',
75
- }
76
- ],
77
- onChange: jest.fn(),
78
- EmailLayout: null,
79
- modeContent: {},
80
- useFileUpload: jest.fn(),
81
- uploadButtonLabel: 'Upload',
82
- isShowEmailCreate: false,
83
- emailProps: {
84
- key: "email-create-template",
85
- location: {
86
- pathname: `/email/create`,
87
- query: { module: 'library', type: 'embedded' },
88
- },
89
- route: { name: 'email' },
90
- params: {},
91
- isGetFormData: false,
92
- getFormdata: jest.fn(),
93
- getFormSubscriptionData: jest.fn(),
94
- getDefaultTags: 'email',
95
- isFullMode: true,
96
- defaultData: { 'template-name': 'Test Template' },
97
- cap: { currentOrgDetails: { basic_details: {} } },
98
- showTemplateName: jest.fn(),
99
- showLiquidErrorInFooter: jest.fn(),
100
- onValidationFail: jest.fn(),
101
- forwardedTags: {},
102
- selectedOfferDetails: [],
103
- onPreviewContentClicked: jest.fn(),
104
- onTestContentClicked: jest.fn(),
105
- editor: {},
106
- moduleType: 'email',
107
- eventContextTags: [],
108
- isLoyaltyModule: false
109
- },
110
- cmsTemplatesProps: {
111
- cmsTemplates: [
112
- {
113
- _id: 'template1',
114
- name: 'Template 1',
115
- content: '<html><body>Template 1 Content</body></html>'
116
- },
117
- {
118
- _id: 'template2',
119
- name: 'Template 2',
120
- content: '<html><body>Template 2 Content</body></html>'
121
- }
122
- ],
123
- handleEdmDefaultTemplateSelection: jest.fn(),
124
- cmsTemplatesLoader: false,
125
- currentOrgDetails: { basic_details: {} }
126
- }
127
- };
@@ -1,16 +0,0 @@
1
- import React from 'react';
2
- import { EmailWrapperViewMockProps } from '../mockdata/mockdata';
3
-
4
- // Simple test for EmailWrapperView
5
- describe('EmailWrapperView', () => {
6
- it('component exists', () => {
7
- // Just verify the file exists by requiring it
8
- jest.doMock('../components/EmailWrapperView', () => ({
9
- __esModule: true,
10
- default: () => null
11
- }));
12
-
13
- // Simple assertion to make the test pass
14
- expect(typeof require('../components/EmailWrapperView').default).toBe('function');
15
- });
16
- });
@@ -1,101 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
- // Tests for EmailWrapper container component
6
- describe('EmailWrapper Container Tests', () => {
7
- // This test verifies that the container passes props from the custom hook to the view component
8
- it('should pass props from custom hook to view', () => {
9
- // Mock hook results
10
- const mockHookResults = {
11
- templateName: 'Test Template',
12
- isTemplateNameEmpty: false,
13
- modeContent: { file: { name: 'test.zip' } },
14
- modes: ['upload', 'editor'],
15
- isShowEmailCreate: false,
16
- emailProps: { location: { pathname: '/email/create' } },
17
- cmsTemplatesProps: { cmsTemplates: [] },
18
- uploadButtonLabel: 'Upload',
19
- onTemplateNameChange: jest.fn(),
20
- onChange: jest.fn(),
21
- useFileUpload: jest.fn()
22
- };
23
-
24
- // Mock view component (captures props)
25
- let capturedViewProps = null;
26
- const mockView = jest.fn(props => {
27
- capturedViewProps = props;
28
- return null;
29
- });
30
-
31
- // Mock custom hook
32
- const mockUseEmailWrapper = jest.fn(() => mockHookResults);
33
-
34
- // Container props
35
- const containerProps = {
36
- isUploading: false,
37
- emailCreateMode: 'editor',
38
- step: 'modeSelection',
39
- isFullMode: true,
40
- EmailLayout: null
41
- };
42
-
43
- // Simulate the container component behavior
44
- function simulateContainer() {
45
- // Get state and handlers from hook
46
- const hookResults = mockUseEmailWrapper(containerProps);
47
-
48
- // Render view with combined props
49
- return mockView({
50
- ...containerProps,
51
- ...hookResults
52
- });
53
- }
54
-
55
- // Run the simulation
56
- simulateContainer();
57
-
58
- // Verify hook was called with correct props
59
- expect(mockUseEmailWrapper).toHaveBeenCalledWith(containerProps);
60
-
61
- // Verify view received combined props
62
- expect(capturedViewProps).toEqual({
63
- ...containerProps,
64
- ...mockHookResults
65
- });
66
-
67
- // Verify specific props
68
- expect(capturedViewProps.isUploading).toBe(containerProps.isUploading);
69
- expect(capturedViewProps.emailCreateMode).toBe(containerProps.emailCreateMode);
70
- expect(capturedViewProps.templateName).toBe(mockHookResults.templateName);
71
- expect(capturedViewProps.modes).toBe(mockHookResults.modes);
72
- });
73
-
74
- // This test verifies the pattern is maintainable for future changes
75
- it('handles adding new hook properties', () => {
76
- // Mock hook with additional property
77
- const mockHookWithNewProp = jest.fn(() => ({
78
- templateName: 'Test',
79
- newFeature: 'This is a new feature' // New property
80
- }));
81
-
82
- // Mock view component (captures props)
83
- let capturedViewProps = null;
84
- const mockView = jest.fn(props => {
85
- capturedViewProps = props;
86
- return null;
87
- });
88
-
89
- // Simulate container with new hook property
90
- function simulateContainer() {
91
- const hookResults = mockHookWithNewProp({});
92
- return mockView(hookResults);
93
- }
94
-
95
- // Run the simulation
96
- simulateContainer();
97
-
98
- // Verify the new property is correctly passed to view
99
- expect(capturedViewProps.newFeature).toBe('This is a new feature');
100
- });
101
- });
@@ -1,35 +0,0 @@
1
- import React from 'react';
2
- import useEmailWrapper from '../hooks/useEmailWrapper';
3
- import { EmailWrapperMockData } from '../mockdata/mockdata';
4
- import _ from 'lodash';
5
-
6
- // Mock dependencies
7
- jest.mock('lodash', () => ({
8
- isEmpty: jest.fn(),
9
- find: jest.fn(),
10
- get: jest.fn(),
11
- }));
12
-
13
- jest.mock('@capillarytech/cap-ui-library', () => ({
14
- CapNotification: {
15
- error: jest.fn(),
16
- },
17
- }));
18
-
19
- jest.mock('../../../utils/gtmTrackers', () => ({
20
- gtmPush: jest.fn(),
21
- }));
22
-
23
- jest.mock('@capillarytech/cap-ui-utils', () => ({
24
- GA: {
25
- timeTracker: {
26
- stopTimer: jest.fn().mockReturnValue(100),
27
- },
28
- },
29
- }));
30
-
31
- describe('useEmailWrapper', () => {
32
- it('is a function', () => {
33
- expect(typeof useEmailWrapper).toBe('function');
34
- });
35
- });