@capillarytech/creatives-library 3.3.0 → 3.3.3

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 (43) hide show
  1. package/config/app.js +2 -0
  2. package/containers/Cap/messsages.js +63 -0
  3. package/index.js +6 -0
  4. package/initialState.js +3 -0
  5. package/package.json +2 -1
  6. package/routes.js +6 -1
  7. package/services/api.js +25 -9
  8. package/translations/en.json +56 -0
  9. package/translations/zh.json +56 -0
  10. package/v2Components/Header/index.js +46 -0
  11. package/v2Components/Header/tests/index.test.js +10 -0
  12. package/v2Components/MarketingObjective/MarketingObjective.style.js +73 -0
  13. package/v2Components/MarketingObjective/index.js +107 -0
  14. package/v2Components/MarketingObjective/messages.js +23 -0
  15. package/v2Components/NavigationBar/index.js +5 -10
  16. package/v2Components/NavigationBar/messages.js +28 -0
  17. package/v2Containers/App/constants.js +29 -0
  18. package/v2Containers/Assets/Gallery/index.js +11 -5
  19. package/v2Containers/Cap/index.js +3 -0
  20. package/v2Containers/CreativesContainer/SlideBoxContent.js +18 -0
  21. package/v2Containers/CreativesContainer/constants.js +1 -0
  22. package/v2Containers/CreativesContainer/index.js +10 -1
  23. package/v2Containers/Email/index.js +7 -0
  24. package/v2Containers/Email/messages.js +8 -0
  25. package/v2Containers/EmailWrapper/index.js +15 -1
  26. package/v2Containers/Facebook/Facebook.style.js +75 -0
  27. package/v2Containers/Facebook/actions.js +13 -0
  28. package/v2Containers/Facebook/constants.js +24 -0
  29. package/v2Containers/Facebook/index.js +358 -0
  30. package/v2Containers/Facebook/messages.js +167 -0
  31. package/v2Containers/Facebook/reducer.js +31 -0
  32. package/v2Containers/Facebook/sagas.js +41 -0
  33. package/v2Containers/Facebook/selectors.js +20 -0
  34. package/v2Containers/MobilePush/Create/index.js +8 -0
  35. package/v2Containers/MobilePush/Edit/index.js +8 -0
  36. package/v2Containers/Sms/Create/index.js +6 -1
  37. package/v2Containers/Sms/Edit/index.js +6 -1
  38. package/v2Containers/Templates/index.js +10 -4
  39. package/v2Containers/TemplatesV2/_templatesV2.scss +5 -0
  40. package/v2Containers/TemplatesV2/index.js +53 -22
  41. package/v2Containers/TemplatesV2/messages.js +4 -0
  42. package/v2Containers/WeChat/MapTemplates/index.js +9 -1
  43. package/v2Containers/WeChat/RichmediaTemplates/Create/index.js +9 -2
@@ -0,0 +1,107 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import PropTypes from 'prop-types';
3
+ import { injectIntl, FormattedMessage } from "react-intl";
4
+ import {
5
+ CapHeading,
6
+ CapRadioCard,
7
+ CapLabel,
8
+ CapButton,
9
+ CapSpin,
10
+ } from '@capillarytech/cap-ui-library';
11
+ import messages from './messages';
12
+ import styles from './MarketingObjective.style';
13
+ import withStyles from '../../hoc/withStyles';
14
+
15
+ const renderMarketingObjectiveList = ({
16
+ onMarketingObjectiveChange,
17
+ marketingObjectiveSelectedOption,
18
+ defaultmarketingObjective,
19
+ marketingObjectiveList = [],
20
+ }) => {
21
+ const marketingList = (marketingObjective) => {
22
+ const { label, panes = [] } = marketingObjective;
23
+ const selectedPaneExists = panes.some((item) => item.value === marketingObjectiveSelectedOption);
24
+ const modifiedPanes = !selectedPaneExists
25
+ ? [...panes, {
26
+ value: marketingObjectiveSelectedOption,
27
+ }]
28
+ : panes;
29
+ return (
30
+ <div key={label} className={selectedPaneExists ? 'selected-radio-group' : 'non-selected-radio-group'}>
31
+ <CapHeading type="h4" className="radio-group-heading">{label}</CapHeading>
32
+ <CapRadioCard
33
+ onChange={({ target: { value } }) => onMarketingObjectiveChange(value)}
34
+ panes={modifiedPanes}
35
+ cardWidth="324px"
36
+ cardHeight="auto"
37
+ selected={marketingObjectiveSelectedOption}
38
+ defaultValue={defaultmarketingObjective}
39
+ />
40
+ </div>
41
+ );
42
+ };
43
+
44
+ return marketingObjectiveList.map((marketinObjective) => marketingList(marketinObjective));
45
+ };
46
+
47
+ const MarketingObjective = ({
48
+ onMarketingObjectiveSelect,
49
+ selectedMarketingObjective,
50
+ defaultmarketingObjective,
51
+ onSubmit,
52
+ className,
53
+ marketingObjectiveList,
54
+ }) => {
55
+ const [marketingObjectiveSelectedOption, onMarketingObjectiveChange] = useState(selectedMarketingObjective || defaultmarketingObjective);
56
+
57
+ useEffect(() => {
58
+ [...document.querySelectorAll('.non-selected-radio-group .ant-radio-group')].forEach((node) => {
59
+ const paneToSelect = node.lastChild;
60
+ paneToSelect.click();
61
+ });
62
+ }, [marketingObjectiveSelectedOption]);
63
+
64
+ const onSubmitAction = () => {
65
+ onMarketingObjectiveSelect(marketingObjectiveSelectedOption);
66
+ onSubmit(false);
67
+ };
68
+
69
+ return (
70
+ <div className={className}>
71
+ <>
72
+ <CapHeading type="h3">
73
+ <FormattedMessage {...messages.fbMarketingObjectiveTitle} />
74
+ </CapHeading>
75
+ <CapLabel type="label1">
76
+ <FormattedMessage {...messages.fbMarketingObjectiveDescription} />
77
+ </CapLabel>
78
+ </>
79
+ <CapSpin spinning={!marketingObjectiveList.length}>
80
+ {renderMarketingObjectiveList({
81
+ onMarketingObjectiveChange,
82
+ marketingObjectiveSelectedOption,
83
+ defaultmarketingObjective,
84
+ marketingObjectiveList,
85
+ })}
86
+ </CapSpin>
87
+ <CapButton
88
+ type="primary"
89
+ className="submit-button-section"
90
+ onClick={() => onSubmitAction(false)}
91
+ >
92
+ <FormattedMessage {...messages.fbMarketingObjectiveDone} />
93
+ </CapButton>
94
+ </div>
95
+ );
96
+ };
97
+
98
+ MarketingObjective.propTypes = {
99
+ onMarketingObjectiveSelect: PropTypes.func,
100
+ selectedMarketingObjective: PropTypes.string,
101
+ defaultmarketingObjective: PropTypes.string,
102
+ onSubmit: PropTypes.func,
103
+ className: PropTypes.string,
104
+ marketingObjectiveList: PropTypes.arrayOf(PropTypes.object),
105
+ };
106
+
107
+ export default injectIntl(withStyles(MarketingObjective, styles));
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Marketing Objective Messages
3
+ *
4
+ * This contains all the text for the Facebook component.
5
+ */
6
+ import { defineMessages } from 'react-intl';
7
+
8
+ const messagePrefix = 'creatives.containersV2.FacebookMarketingObjective';
9
+
10
+ export default defineMessages({
11
+ fbMarketingObjectiveTitle: {
12
+ id: `${messagePrefix}.fbMarketingObjectiveTitle`,
13
+ defaultMessage: 'Facebook Marketing Objective',
14
+ },
15
+ fbMarketingObjectiveDescription: {
16
+ id: `${messagePrefix}.fbMarketingObjectiveDescription`,
17
+ defaultMessage: 'Objectives help break reporting into a more granular level',
18
+ },
19
+ fbMarketingObjectiveDone: {
20
+ id: `${messagePrefix}.fbMarketingObjectiveDone`,
21
+ defaultMessage: 'Done',
22
+ },
23
+ });
@@ -7,7 +7,7 @@
7
7
  import React from 'react';
8
8
  import PropTypes from 'prop-types';
9
9
  import styled from 'styled-components';
10
- import { isEmpty, forEach, find } from 'lodash';
10
+ import { isEmpty, forEach } from 'lodash';
11
11
  import { loadItem } from 'services/localStorageApi';
12
12
  import { intlShape, injectIntl } from 'react-intl';
13
13
  import TopBar from '../TopBar';
@@ -86,14 +86,16 @@ class NavigationBar extends React.Component {
86
86
 
87
87
  getProductsList = () => {
88
88
  const { formatMessage } = this.props.intl;
89
- const { campaignOrgV2Status } = this.props;
90
89
  const { currentOrgDetails } = this.props.userData;
91
90
  const productsList = [];
92
91
  if (!isEmpty(currentOrgDetails)) {
93
92
  forEach(currentOrgDetails.module_details, (module) => {
94
93
  if (module.name.toLowerCase() !== PRODUCT_MASTERS) {
94
+ const productName = module.code;
95
+ const intlProductName = messages[productName];
96
+ const moduleName = intlProductName ? formatMessage(intlProductName) : module.name.toLowerCase();
95
97
  productsList.push({
96
- value: module.name.toLowerCase(),
98
+ value: moduleName,
97
99
  url: module.url,
98
100
  key: module.code,
99
101
  });
@@ -101,12 +103,6 @@ class NavigationBar extends React.Component {
101
103
  });
102
104
  //below changes are temporary and should be fixed once we get correct modules list
103
105
  //changing campaigns module name to engage+
104
- if ( !campaignOrgV2Status ) {
105
- const campaignsModule = find(productsList, ['value', 'campaigns']);
106
- if (campaignsModule) {
107
- campaignsModule.value = 'Engage+';
108
- }
109
- }
110
106
  //adding insights+ to module list
111
107
  productsList.push({
112
108
  value: formatMessage(messages.insights),
@@ -224,7 +220,6 @@ NavigationBar.propTypes = {
224
220
  intl: intlShape.isRequired,
225
221
  location: PropTypes.object,
226
222
  type: PropTypes.string,
227
- campaignOrgV2Status: PropTypes.bool,
228
223
  };
229
224
 
230
225
  export default injectIntl(NavigationBar);
@@ -29,4 +29,32 @@ export default defineMessages({
29
29
  id: `${scope}.logout`,
30
30
  defaultMessage: 'Logout',
31
31
  },
32
+ businessProcesses: {
33
+ id: `${scope}.businessProcesses`,
34
+ defaultMessage: 'Workbench',
35
+ },
36
+ campaign: {
37
+ id: `${scope}.campaign`,
38
+ defaultMessage: 'Engage+',
39
+ },
40
+ wecrm: {
41
+ id: `${scope}.wecrm`,
42
+ defaultMessage: 'Wecrm',
43
+ },
44
+ loyaltyProgram: {
45
+ id: `${scope}.loyaltyProgram`,
46
+ defaultMessage: 'Loyalty+',
47
+ },
48
+ storePerformance: {
49
+ id: `${scope}.storePerformance`,
50
+ defaultMessage: 'Store Performance',
51
+ },
52
+ memberCare: {
53
+ id: `${scope}.memberCare`,
54
+ defaultMessage: 'Member Care',
55
+ },
56
+ storeCare: {
57
+ id: `${scope}.storeCare`,
58
+ defaultMessage: 'Store Care',
59
+ },
32
60
  });
@@ -12,3 +12,32 @@ export const getTopbarMenuDataValue = () => ([
12
12
  { label: <FormattedMessage {...globalMessages.incentive} />, link: '/coupons/ui/', key: 'incentive' },
13
13
  { label: <FormattedMessage {...globalMessages.creatives} />, link: '/creatives/ui/v2', key: 'creatives' },
14
14
  ]);
15
+
16
+ export const TRACK_CREATE_SMS = 'createSms';
17
+ export const TRACK_CREATE_EMAIL = 'createEmail';
18
+ export const TRACK_CREATE_MPUSH = 'createMpush';
19
+ export const TRACK_CREATE_WECHAT = 'createWeChat';
20
+ export const TRACK_CREATE_IMAGE = 'createImage';
21
+
22
+
23
+ export const TRACK_EDIT_SMS = 'editSms';
24
+ export const TRACK_EDIT_EMAIL = 'editEmail';
25
+ export const TRACK_EDIT_MPUSH = 'editMpush';
26
+ export const TRACK_EDIT_WECHAT = 'editWeChat';
27
+ export const TRACK_EDIT_IMAGE = 'editImage';
28
+
29
+ export const CHANNEL_CREATE_TRACK_MAPPING = {
30
+ sms: TRACK_CREATE_SMS,
31
+ email: TRACK_CREATE_EMAIL,
32
+ mobilepush: TRACK_CREATE_MPUSH,
33
+ wechat: TRACK_CREATE_WECHAT,
34
+ gallery: TRACK_CREATE_IMAGE,
35
+ };
36
+
37
+ export const CHANNEL_EDIT_TRACK_MAPPING = {
38
+ sms: TRACK_EDIT_SMS,
39
+ email: TRACK_EDIT_EMAIL,
40
+ mobilepush: TRACK_EDIT_MPUSH,
41
+ wechat: TRACK_EDIT_WECHAT,
42
+ gallery: TRACK_EDIT_IMAGE,
43
+ };
@@ -120,15 +120,18 @@ export class Gallery extends React.Component { // eslint-disable-line react/pref
120
120
  getAllAssets = ({params, getNextPage, resetPage}, isReRender = false) => {
121
121
  let queryParams = params;
122
122
  let page = this.state.page;
123
+ let shouldFetchGalleryAssets = false;
123
124
  if (!this.props.Gallery.fetchingAllAssets && ((resetPage || (page === 1 && this.state.totalCount === 0) || page <= (this.state.totalCount / this.state.perPageLimit)) || isReRender )) {
124
125
  if (getNextPage) {
125
126
  page += 1;
127
+ shouldFetchGalleryAssets = true;
126
128
  }
127
129
 
128
130
  let totalCount = this.state.totalCount;
129
131
  if (resetPage) {
130
132
  page = 1;
131
133
  totalCount = 0;
134
+ shouldFetchGalleryAssets = true;
132
135
  }
133
136
  if ((!params || _.isEmpty(params))) {
134
137
  queryParams = {
@@ -137,12 +140,15 @@ export class Gallery extends React.Component { // eslint-disable-line react/pref
137
140
  }
138
141
  if (this.state.searchText !== "") {
139
142
  queryParams = {...queryParams, name: this.state.searchText};
143
+ shouldFetchGalleryAssets = true;
144
+ }
145
+ if (!this.props.Gallery.fetchingAllAssets && (_.isEmpty(this.props.Gallery.assetList) || shouldFetchGalleryAssets)) {
146
+ this.setState({page, totalCount}, () => {
147
+ queryParams.page = page;
148
+ queryParams.perPage = this.state.perPageLimit;
149
+ this.props.actions.getAllAssets('image', queryParams);
150
+ });
140
151
  }
141
- this.setState({page, totalCount}, () => {
142
- queryParams.page = page;
143
- queryParams.perPage = this.state.perPageLimit;
144
- this.props.actions.getAllAssets('image', queryParams);
145
- });
146
152
  }
147
153
  };
148
154
  handleGallery = (e) => {
@@ -9,6 +9,7 @@ import { createStructuredSelector } from 'reselect';
9
9
  import _ from 'lodash';
10
10
  import moment from 'moment';
11
11
  // import 'moment/locale/zh-cn';
12
+ import { GA } from '@capillarytech/cap-ui-utils';
12
13
  import { injectIntl, FormattedMessage } from 'react-intl';
13
14
  import messages from './messages';
14
15
  import { makeSelectAuthenticated, makeSelectUser } from './selectors';
@@ -33,6 +34,8 @@ const CapWrapper = styled.div`
33
34
  flex-direction: column;
34
35
  `;
35
36
 
37
+ GA.initialize({ accessKey: 'UA-152081629-1' });
38
+
36
39
  export class Cap extends React.Component { // eslint-disable-line react/prefer-stateless-function
37
40
 
38
41
  constructor(props) {
@@ -12,6 +12,7 @@ import MobilepushWrapper from '../MobilepushWrapper';
12
12
  import EmailPreviewV2 from '../../v2Components/EmailPreviewV2';
13
13
  import MobilePushPreview from '../../v2Components/MobilePushPreviewV2';
14
14
  import WechatWrapper from '../WeChat/Wrapper';
15
+ import Facebook from '../Facebook';
15
16
  import { RICH_MEDIA, MAP_TEMPLATE, CREATE } from '../WeChat/Wrapper/constants';
16
17
  import CallTask from '../CallTask';
17
18
  import MobliPushEdit from '../MobilePush/Edit';
@@ -30,6 +31,7 @@ function getWechatTemplateType(mode, templateData, weChatTemplateType = '') {
30
31
  }
31
32
  return weChatTemplateType;
32
33
  }
34
+
33
35
  function SlideBoxContent(props) {
34
36
  const {
35
37
  slidBoxContent,
@@ -67,6 +69,7 @@ function SlideBoxContent(props) {
67
69
  selectedWeChatAccount,
68
70
  weChatMaptemplateStep,
69
71
  onWeChatMaptemplateStepChange,
72
+ onFacebookSubmit,
70
73
  } = props;
71
74
  const type = messageDetails.type.toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
72
75
  const query = { type: !isFullMode && 'embedded', module: isFullMode ? 'default' : 'library', isEditFromCampaigns: (templateData || {}).isEditFromCampaigns};
@@ -87,6 +90,7 @@ function SlideBoxContent(props) {
87
90
  let isMpushPreview = false;
88
91
  let isEditCallTask = false;
89
92
  let isEditMPush = false;
93
+ let isEditFacebook = false;
90
94
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL;
91
95
  if (templateData && channel) {
92
96
  channel = templateData.type;// for edit mode with template data
@@ -95,6 +99,7 @@ function SlideBoxContent(props) {
95
99
  isEditMPush = slidBoxContent === 'editTemplate' && channel === constants.MOBILE_PUSH;
96
100
  isEditEmailWithId = slidBoxContent === 'editTemplate' && channel === constants.EMAIL && templateData._id;
97
101
  isEmailEditWithContent = slidBoxContent === 'editTemplate' && channel === constants.EMAIL && !templateData._id;
102
+ isEditFacebook = slidBoxContent === 'editTemplate' && channel === constants.FACEBOOK;
98
103
  isPreview = slidBoxContent === 'preview' && channel === constants.SMS;
99
104
  isEmailPreview = slidBoxContent === 'preview' && channel === constants.EMAIL;
100
105
  isMpushPreview = slidBoxContent === 'preview' && channel === constants.MOBILE_PUSH;
@@ -116,6 +121,8 @@ function SlideBoxContent(props) {
116
121
  channelsToHide={channelsToHide}
117
122
  forwardedTags={forwardedTags}
118
123
  channelsToDisable={channelsToDisable}
124
+ messageDetails={messageDetails}
125
+ onFacebookSubmit={onFacebookSubmit}
119
126
  />
120
127
  )}
121
128
  {isPreview && (
@@ -302,6 +309,16 @@ function SlideBoxContent(props) {
302
309
  onValidationFail={onValidationFail}
303
310
  selectedOfferDetails={selectedOfferDetails}/>
304
311
  }
312
+ {
313
+ isEditFacebook && (
314
+ <Facebook
315
+ templateData={templateData}
316
+ messageDetails={messageDetails}
317
+ cap={cap}
318
+ onFacebookSubmit={onFacebookSubmit}
319
+ />
320
+ )
321
+ }
305
322
  </CreativesWrapper>
306
323
  );
307
324
  }
@@ -341,5 +358,6 @@ SlideBoxContent.propTypes = {
341
358
  selectedWeChatAccount: PropTypes.object,
342
359
  onWeChatMaptemplateStepChange: PropTypes.func,
343
360
  weChatMaptemplateStep: PropTypes.string,
361
+ onFacebookSubmit: PropTypes.func,
344
362
  };
345
363
  export default SlideBoxContent;
@@ -12,5 +12,6 @@ export const LINE = "LINE";
12
12
  export const CALL_TASK = "CALL_TASK";
13
13
  export const MOBILE_PUSH = "MOBILEPUSH";
14
14
  export const WECHAT = "WECHAT";
15
+ export const FACEBOOK = "FACEBOOK";
15
16
  export const SHOW_CONTANER_LOADER = "app/CreativesContainer/SHOW_CONTANER_LOADER";
16
17
  export const HIDE_CONTAINER_LOADER = "app/CreativesContainer/HIDE_CONTAINER_LOADER";
@@ -207,6 +207,14 @@ class Creatives extends React.Component {
207
207
  };
208
208
  break;
209
209
  }
210
+ case constants.FACEBOOK: {
211
+ creativesTemplateData = {
212
+ selectedMarketingObjective: templateData.selectedMarketingObjective,
213
+ edit: true,
214
+ type: channel,
215
+ };
216
+ break;
217
+ }
210
218
  default:
211
219
  break;
212
220
  }
@@ -465,7 +473,7 @@ class Creatives extends React.Component {
465
473
  }
466
474
  render() {
467
475
  const {slidBoxContent, isGetFormData, showSlideBox, templateData, currentChannel, emailCreateMode, templateStep, isLoadingContent, mobilePushCreateMode, isDiscardMessage, weChatTemplateType, weChatMaptemplateStep} = this.state;
468
- const {isFullMode, creativesMode, cap, isUploading, channelsToHide, selectedWeChatAccount, forwardedTags, channelsToDisable, selectedOfferDetails} = this.props;
476
+ const {isFullMode, creativesMode, cap, isUploading, channelsToHide, selectedWeChatAccount, forwardedTags, channelsToDisable, selectedOfferDetails, getCreativesData} = this.props;
469
477
  const mapTemplateCreate = slidBoxContent === 'createTemplate' && weChatTemplateType === MAP_TEMPLATE && templateStep !== 'modeSelection';
470
478
  /* TODO: Instead of passing down same props separately to each component down, write common function to these props and pass it accordingly */
471
479
  return (
@@ -530,6 +538,7 @@ class Creatives extends React.Component {
530
538
  weChatMaptemplateStep={weChatMaptemplateStep}
531
539
  selectedWeChatAccount={selectedWeChatAccount}
532
540
  onWeChatMaptemplateStepChange={this.onWeChatMaptemplateStepChange}
541
+ onFacebookSubmit={getCreativesData}
533
542
  />
534
543
  }
535
544
  footer={this.shouldShowFooter() &&
@@ -29,6 +29,8 @@ import EmailPreview from '../../v2Components/EmailPreview';
29
29
  import Pagination from '../../v2Components/Pagination';
30
30
  import * as creativesContainerActions from '../CreativesContainer/actions';
31
31
  import withCreatives from '../../hoc/withCreatives';
32
+ import { GA } from '@capillarytech/cap-ui-utils';
33
+ import { TRACK_CREATE_EMAIL, TRACK_EDIT_EMAIL } from '../App/constants'
32
34
 
33
35
  const {CapCustomCardList} = CapCustomCard;
34
36
  export class Email extends React.Component { // eslint-disable-line react/prefer-stateless-function
@@ -620,6 +622,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
620
622
  });
621
623
  }
622
624
  onUpdateTemplateComplete = (createResponse) => {
625
+ GA.timeTracker.stopTimer(this.state.isEdit ? TRACK_EDIT_EMAIL : TRACK_CREATE_EMAIL, {
626
+ category: 'Creatives',
627
+ action: this.state.isEdit ? 'Create' : 'Edit',
628
+ label: 'using editor',
629
+ });
623
630
  if (createResponse && createResponse.templateId) {
624
631
  // this.resetSchema();
625
632
  let message;
@@ -286,4 +286,12 @@ export default defineMessages({
286
286
  id: 'creatives.containersV2.Email.h3imageSelection',
287
287
  defaultMessage: 'Image selection',
288
288
  },
289
+ "English": {
290
+ id: 'creatives.containersV2.Email.english',
291
+ defaultMessage: 'English',
292
+ },
293
+ "Add label": {
294
+ id: 'creatives.containersV2.Email.addLabel',
295
+ defaultMessage: 'Add label',
296
+ },
289
297
  });
@@ -7,6 +7,7 @@ import PropTypes from 'prop-types';
7
7
  import React from 'react';
8
8
  import { connect } from 'react-redux';
9
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';
12
13
  import _ from 'lodash';
@@ -19,7 +20,10 @@ import * as templatesActionsCreators from '../Templates/actions';
19
20
  import Email from '../Email';
20
21
  import CmsTemplatesComponent from '../../v2Components/CmsTemplatesComponent';
21
22
  import messages from './messages';
23
+ import { CHANNEL_CREATE_TRACK_MAPPING } from '../App/constants'
24
+
22
25
  const CapRadioCardWithLabel = ComponentWithLabelHOC(CapRadioCard);
26
+ const { timeTracker } = GA;
23
27
  const CardContainer = styled.div`
24
28
  margin-top: 16px;
25
29
  .ant-radio-group{
@@ -106,7 +110,16 @@ export class EmailWrapper extends React.Component { // eslint-disable-line react
106
110
  };
107
111
  CapNotification.error(message);
108
112
  }
113
+ stopTimerGA=()=>{
114
+ // stop timer
115
+ timeTracker.stopTimer(CHANNEL_CREATE_TRACK_MAPPING['email'], {
116
+ category: 'Creatives',
117
+ action: 'Create',
118
+ label: 'uploadZip',
119
+ });
120
+ }
109
121
  handleFileUpload = (file) => {
122
+
110
123
  const { templatesActions, intl, showNextStep, isUploading } = this.props;
111
124
  if (!isUploading) {
112
125
  const fileExtension = file.name.split('.').pop();
@@ -116,7 +129,9 @@ export class EmailWrapper extends React.Component { // eslint-disable-line react
116
129
  if (supportedZipFormats.indexOf(fileExtension.toLowerCase()) !== -1) {
117
130
  templatesActions.handleZipUpload(file.originFileObj, () => { //handle upload success
118
131
  this.setState({ modeContent: { file } }, showNextStep);
132
+ stopTimerGA();
119
133
  }, this.handleZipUploadError);
134
+
120
135
  } else if (fileExtension === 'html' || fileExtension === 'htm') {
121
136
  const reader = new FileReader();
122
137
  reader.onload = () => {
@@ -124,7 +139,6 @@ export class EmailWrapper extends React.Component { // eslint-disable-line react
124
139
  this.setState({ modeContent: { file } }, () => {
125
140
  templatesActions.handleHtmlUpload(text);
126
141
  });
127
-
128
142
  // showNextStep();
129
143
  };
130
144
  reader.readAsText(file.originFileObj);
@@ -0,0 +1,75 @@
1
+ import { css } from 'styled-components';
2
+ import {
3
+ CAP_SPACE_44,
4
+ CAP_SPACE_28,
5
+ CAP_SPACE_20,
6
+ CAP_SPACE_12,
7
+ CAP_SPACE_08,
8
+ CAP_G07,
9
+ CAP_G12,
10
+ CAP_G06,
11
+ FONT_COLOR_05,
12
+ } from '@capillarytech/cap-ui-library/styled/variables';
13
+
14
+ export default css`
15
+ margin-left: ${CAP_SPACE_44};
16
+
17
+ .is-scrollable-section {
18
+ height: calc(100vh - 220px);
19
+ overflow-y: auto;
20
+ }
21
+
22
+ .account-details-wrapper {
23
+ border-bottom: 1px solid ${CAP_G12};
24
+ margin-bottom: ${CAP_SPACE_20};
25
+ }
26
+
27
+ .account-name-section {
28
+ margin-bottom: ${CAP_SPACE_28};
29
+ }
30
+
31
+ .account-section-container {
32
+ margin-bottom: ${CAP_SPACE_20};
33
+ max-width: 564px;
34
+ }
35
+
36
+ .ad-section {
37
+ margin-bottom: ${CAP_SPACE_44};
38
+ }
39
+
40
+ .dynamic-content-section {
41
+ max-width: 140px;
42
+
43
+ .facebook-dynamic-content {
44
+ color: ${CAP_G06};
45
+ }
46
+ }
47
+
48
+ .parameter-list {
49
+ padding: ${CAP_SPACE_08} ${CAP_SPACE_28};
50
+
51
+ .parameter-list-item {
52
+ list-style: none;
53
+ margin-bottom: ${CAP_SPACE_28};
54
+
55
+ .parameter-index {
56
+ float: left;
57
+ margin-right: ${CAP_SPACE_12};
58
+ background: ${CAP_G07};
59
+ padding: 6px 10px;
60
+ border-radius: 50%;
61
+ }
62
+ }
63
+ }
64
+
65
+ .cap-button-v2 {
66
+ &.marketingObjective-change-button {
67
+ color: ${FONT_COLOR_05};
68
+ height: ${CAP_SPACE_20};
69
+ }
70
+ }
71
+ `;
72
+
73
+ export const CampaignDetailCapColumn = css`
74
+ margin-bottom: ${CAP_SPACE_20};
75
+ `;
@@ -0,0 +1,13 @@
1
+ /*
2
+ *
3
+ * Facebook actions
4
+ *
5
+ */
6
+
7
+ import {
8
+ GET_MARKETING_OBJECTIVES,
9
+ } from './constants';
10
+
11
+ export const getMarketingObjectives = () => ({
12
+ type: GET_MARKETING_OBJECTIVES,
13
+ });
@@ -0,0 +1,24 @@
1
+ /*
2
+ *
3
+ * Facebook constants
4
+ *
5
+ */
6
+
7
+ const prefix = 'app/Facebook';
8
+
9
+ export const GET_MARKETING_OBJECTIVES = `${prefix}/GET_MARKETING_OBJECTIVES`;
10
+ export const SET_MARKETING_OBJECTIVES_SUCCESS = `${prefix}/SET_MARKETING_OBJECTIVES_SUCCESS`;
11
+ export const SET_MARKETING_OBJECTIVES_FAILURE = `${prefix}/SET_MARKETING_OBJECTIVES_FAILURE`;
12
+
13
+ // marketing objectives constants
14
+
15
+ export const VALUE_APP_INSTALLS = 'VALUE_APP_INSTALLS';
16
+ export const VALUE_BRAND_AWARENESS = 'VALUE_BRAND_AWARENESS';
17
+ export const VALUE_CONVERSIONS = 'VALUE_CONVERSIONS';
18
+ export const VALUE_LEAD_GENERATION = 'VALUE_LEAD_GENERATION';
19
+ export const VALUE_LOCAL_AWARENESS = 'VALUE_LOCAL_AWARENESS';
20
+ export const VALUE_MESSAGES = 'VALUE_MESSAGES';
21
+ export const VALUE_PRODUCT_CATALOG_SALES = 'VALUE_PRODUCT_CATALOG_SALES';
22
+ export const VALUE_REACH = 'VALUE_REACH';
23
+ export const VALUE_VIDEO_VIEWS = 'VALUE_VIDEO_VIEWS';
24
+ export const VALUE_LINK_CLICKS = 'VALUE_LINK_CLICKS';