@capillarytech/creatives-library 3.8.11 → 3.9.1

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/index.js CHANGED
@@ -43,6 +43,10 @@ import Facebook from './v2Containers/Facebook';
43
43
  import facebookReducer from './v2Containers/Facebook/reducer';
44
44
  import facebookSaga from './v2Containers/Facebook/sagas';
45
45
 
46
+ import Gallery from './v2Containers/Assets/Gallery';
47
+ import galleryReducer from './v2Containers/Assets/Gallery/reducer';
48
+ import gallerySagas from './v2Containers/Assets/Gallery/sagas';
49
+
46
50
  import MobilepushCreate from './v2Containers/MobilePush/Create';
47
51
  import mobilepushCreateReducer from './v2Containers/MobilePush/Create/reducer';
48
52
  import mobilepushCreateSaga from './v2Containers/MobilePush/Create/sagas';
@@ -86,6 +90,7 @@ const LineCreateContainer = {LineCreate, lineCreateReducer, lineCreateSaga};
86
90
  const LineEditContainer = {LineEdit, lineEditReducer, lineEditSaga};
87
91
  const TemplatesV2Container = {TemplatesV2, templateReducer, templateSaga};
88
92
  const FacebookContainer = {Facebook, facebookReducer, facebookSaga};
93
+ const GalleryContainer = {Gallery, galleryReducer, gallerySagas};
89
94
 
90
95
  export {default as TagList} from './v2Containers/TagList/index';
91
96
  export {default as TagListSaga} from './v2Containers/TagList/sagas';
@@ -123,4 +128,5 @@ export { CapContainer,
123
128
  CreativesContainer,
124
129
  updateCharCount,
125
130
  FacebookContainer,
131
+ GalleryContainer,
126
132
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "3.8.11",
4
+ "version": "3.9.1",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -1091,6 +1091,9 @@
1091
1091
  "creatives.containersV2.Line.Create.uploadSuccess": "Photo uploaded successfully",
1092
1092
  "creatives.containersV2.Line.Create.uploadingImage": "Uploading image...",
1093
1093
  "creatives.containersV2.Line.Create.valueCantEmpty": "value cannot be empty.",
1094
+ "creatives.containersV2.Line.CreateWrapper.accountLineListDescription": "Choose the account to send content",
1095
+ "creatives.containersV2.Line.CreateWrapper.accountLineListTitle": "Line accounts",
1096
+ "creatives.containersV2.Line.CreateWrapper.accountLineNoListTitle": "No line accounts present",
1094
1097
  "creatives.containersV2.Line.CreateWrapper.continue": "Continue",
1095
1098
  "creatives.containersV2.Line.CreateWrapper.hybridTemplateDescription": "Used to create combined text & image or video content",
1096
1099
  "creatives.containersV2.Line.CreateWrapper.hybridTemplateTitle": "Hybrid",
@@ -1326,6 +1329,7 @@
1326
1329
  "creatives.containersV2.Templates.imageTemplate": "Image",
1327
1330
  "creatives.containersV2.Templates.invalidUploadFileError": "File cannot be uploaded. Please select another file with valid file format.",
1328
1331
  "creatives.containersV2.Templates.layoutSelection": "Select layout",
1332
+ "creatives.containersV2.Templates.lineAccount": "Line account",
1329
1333
  "creatives.containersV2.Templates.lineHeader": "Line",
1330
1334
  "creatives.containersV2.Templates.mapTemplate": "Map template",
1331
1335
  "creatives.containersV2.Templates.mapTemplates": "Map templates",
@@ -1338,6 +1342,7 @@
1338
1342
  "creatives.containersV2.Templates.newTemplate": "New template",
1339
1343
  "creatives.containersV2.Templates.noAccountMessage": "Please select an account to proceed",
1340
1344
  "creatives.containersV2.Templates.noAccountsPresent": "Push notifications are not setup for your brand",
1345
+ "creatives.containersV2.Templates.noAccountsPresentLine": "Line accounts are not setup for your brand",
1341
1346
  "creatives.containersV2.Templates.noAccountsPresentWeChat": "Wechat accounts are not setup for your brand",
1342
1347
  "creatives.containersV2.Templates.noTemplatesFound": "No templates found",
1343
1348
  "creatives.containersV2.Templates.noTemplatesMessage": "No templates available",
@@ -1091,6 +1091,9 @@
1091
1091
  "creatives.containersV2.Line.Create.uploadSuccess": "",
1092
1092
  "creatives.containersV2.Line.Create.uploadingImage": "",
1093
1093
  "creatives.containersV2.Line.Create.valueCantEmpty": "",
1094
+ "creatives.containersV2.Line.CreateWrapper.accountLineListDescription": "",
1095
+ "creatives.containersV2.Line.CreateWrapper.accountLineListTitle": "",
1096
+ "creatives.containersV2.Line.CreateWrapper.accountLineNoListTitle": "",
1094
1097
  "creatives.containersV2.Line.CreateWrapper.continue": "",
1095
1098
  "creatives.containersV2.Line.CreateWrapper.hybridTemplateDescription": "",
1096
1099
  "creatives.containersV2.Line.CreateWrapper.hybridTemplateTitle": "",
@@ -1326,6 +1329,7 @@
1326
1329
  "creatives.containersV2.Templates.imageTemplate": "",
1327
1330
  "creatives.containersV2.Templates.invalidUploadFileError": "",
1328
1331
  "creatives.containersV2.Templates.layoutSelection": "",
1332
+ "creatives.containersV2.Templates.lineAccount": "",
1329
1333
  "creatives.containersV2.Templates.lineHeader": "",
1330
1334
  "creatives.containersV2.Templates.mapTemplate": "",
1331
1335
  "creatives.containersV2.Templates.mapTemplates": "",
@@ -1338,6 +1342,7 @@
1338
1342
  "creatives.containersV2.Templates.newTemplate": "",
1339
1343
  "creatives.containersV2.Templates.noAccountMessage": "",
1340
1344
  "creatives.containersV2.Templates.noAccountsPresent": "",
1345
+ "creatives.containersV2.Templates.noAccountsPresentLine": "",
1341
1346
  "creatives.containersV2.Templates.noAccountsPresentWeChat": "",
1342
1347
  "creatives.containersV2.Templates.noTemplatesFound": "",
1343
1348
  "creatives.containersV2.Templates.noTemplatesMessage": "",
@@ -2596,6 +2596,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2596
2596
  image: undefined,
2597
2597
  imagePreview: undefined,
2598
2598
  },
2599
+ reUpload: true,
2599
2600
  });
2600
2601
  }
2601
2602
 
@@ -2835,7 +2836,14 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
2835
2836
  );
2836
2837
  break;
2837
2838
  case 'upload':
2838
- const isImage = self.state && !!self.state.formData[`${self.state.currentTab - 1}`].image;
2839
+ const {
2840
+ formData: {
2841
+ [`${self.state.currentTab - 1}`]: {
2842
+ image,
2843
+ } = {},
2844
+ } = {},
2845
+ } = self.state;
2846
+ const isImage = !!image;
2839
2847
  ifError = (this.state.checkValidation || this.props.startValidation) && (isVersionEnable ? this.state.errorData[`${this.state.currentTab - 1}`][val.id] : this.state.errorData[val.id]);
2840
2848
  const ImageComponent = (props) => (
2841
2849
  <div key={`${val.id}-${this.state.currentTab}`}>
@@ -245,6 +245,7 @@ export class Gallery extends React.Component { // eslint-disable-line react/pref
245
245
  const fileParams = {
246
246
  width: this.width,
247
247
  height: this.height,
248
+ isGeneratePreview: true,
248
249
  };
249
250
  that.props.actions.uploadAsset(files[file], 'image', fileParams);
250
251
  };
@@ -75,6 +75,8 @@ function SlideBoxContent(props) {
75
75
  onTestContentClicked,
76
76
  onPreviewContentClicked,
77
77
  onFacebookSubmit,
78
+ saveMessage,
79
+ selectedAccount,
78
80
  } = props;
79
81
  const type = messageDetails.type.toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
80
82
  const query = { type: !isFullMode && 'embedded', module: isFullMode ? 'default' : 'library', isEditFromCampaigns: (templateData || {}).isEditFromCampaigns};
@@ -374,7 +376,12 @@ function SlideBoxContent(props) {
374
376
  }}
375
377
  route={{ name: (templateData.modeType || (templateData.definition && templateData.definition.mode)) === 'image' ? 'edit_image' : 'edit_text' }}
376
378
  onCreateComplete={onCreateComplete}
377
- isFullMode={props.isFullMode}
379
+ isFullMode={isFullMode}
380
+ saveMessage={saveMessage}
381
+ getFormSubscriptionData={getFormData}
382
+ isGetFormData={isGetFormData}
383
+ templateData={templateData}
384
+ selectedAccount={selectedAccount}
378
385
  />
379
386
  )
380
387
  }
@@ -383,7 +390,12 @@ function SlideBoxContent(props) {
383
390
  <CreateLineWrapper
384
391
  location={creativesLocationProps}
385
392
  onCreateComplete={onCreateComplete}
386
- isFullMode={props.isFullMode}
393
+ isFullMode={isFullMode}
394
+ isGetFormData={isGetFormData}
395
+ saveMessage={saveMessage}
396
+ selectedAccount={selectedAccount}
397
+ templateData={templateData}
398
+ getFormSubscriptionData={getFormData}
387
399
  />
388
400
  )
389
401
  }
@@ -428,5 +440,7 @@ SlideBoxContent.propTypes = {
428
440
  weChatMaptemplateStep: PropTypes.string,
429
441
  onFacebookSubmit: PropTypes.func,
430
442
  onCreateComplete: PropTypes.func,
443
+ saveMessage: PropTypes.func,
444
+ selectedAccount: PropTypes.object,
431
445
  };
432
446
  export default SlideBoxContent;
@@ -12,7 +12,7 @@ import { bindActionCreators } from 'redux';
12
12
  import styled from 'styled-components';
13
13
 
14
14
  import * as actions from './actions';
15
- import {selectEmailLayout, uploadSelector, templateUserList} from '../Templates/selectors';
15
+ import { selectEmailLayout, uploadSelector, templateUserList, makeSelectTemplates } from '../Templates/selectors';
16
16
  import SlideBoxContent from './SlideBoxContent';
17
17
  import SlideBoxHeader from './SlideBoxHeader';
18
18
  import SlideBoxFooter from './SlideBoxFooter';
@@ -215,6 +215,33 @@ class Creatives extends React.Component {
215
215
  };
216
216
  break;
217
217
  }
218
+ case constants.LINE: {
219
+ const { messageBody } = templateData;
220
+ const {
221
+ messages,
222
+ to,
223
+ messages: [{
224
+ type,
225
+ }] = [{}],
226
+ } = JSON.parse(messageBody);
227
+ creativesTemplateData = {
228
+ type: constants.LINE,
229
+ edit: true,
230
+ definition: {
231
+ mode: type,
232
+ },
233
+ name: "Campaign message Line content",
234
+ versions: {
235
+ base: {
236
+ content: {
237
+ messages,
238
+ to,
239
+ },
240
+ },
241
+ },
242
+ };
243
+ break;
244
+ }
218
245
  default:
219
246
  break;
220
247
  }
@@ -280,6 +307,18 @@ class Creatives extends React.Component {
280
307
  templateData = { channel, accountId, messageBody: JSON.stringify(messageBody), previewContent};
281
308
  }
282
309
  break;
310
+ case constants.LINE:
311
+ if (template.value) {
312
+ const { versions } = template.value;
313
+ const { Templates } = this.props;
314
+ const accountDetails = Templates && Templates.selectedLineAccount ? JSON.stringify(Templates.selectedLineAccount) : '';
315
+ templateData = {
316
+ channel,
317
+ accountDetails,
318
+ messageBody: JSON.stringify(versions.base.content),
319
+ };
320
+ }
321
+ break;
283
322
  default:
284
323
  break;
285
324
  }
@@ -393,6 +432,11 @@ class Creatives extends React.Component {
393
432
  showFooter = showFooter && !this.state.isLoadingContent;
394
433
  }
395
434
  }
435
+
436
+ if (channel === constants.LINE) {
437
+ showFooter = showFooter && isFullMode;
438
+ }
439
+
396
440
  return showFooter;
397
441
  }
398
442
  shouldShowDoneFooter = () => {
@@ -542,6 +586,8 @@ class Creatives extends React.Component {
542
586
  onPreviewContentClicked={onPreviewContentClicked}
543
587
  onTestContentClicked={onTestContentClicked}
544
588
  onFacebookSubmit={getCreativesData}
589
+ saveMessage={this.saveMessage}
590
+ selectedAccount={this.props.selectedAccount}
545
591
  />
546
592
  }
547
593
  footer={this.shouldShowFooter() &&
@@ -591,12 +637,15 @@ Creatives.propTypes = {
591
637
  selectedOfferDetails: PropTypes.array,
592
638
  channelsToDisable: PropTypes.array,
593
639
  selectedWeChatAccount: PropTypes.object,
640
+ Templates: PropTypes.object,
641
+ selectedAccount: PropTypes.object,
594
642
  };
595
643
  const mapStatesToProps = () => createStructuredSelector({
596
644
  isLoading: isLoadingSelector(),
597
645
  templateUserList: templateUserList(),
598
646
  EmailLayout: selectEmailLayout(),
599
647
  isUploading: uploadSelector(),
648
+ Templates: makeSelectTemplates(),
600
649
  });
601
650
  function mapDispatchToProps(dispatch) {
602
651
  return {
@@ -47,11 +47,12 @@ export function clearAsset() {
47
47
  // FOR EDIT
48
48
 
49
49
 
50
- export function editTemplate(template) {
50
+ export function editTemplate(template, callback) {
51
51
  console.log('action trigereed editTemplate line @@@');
52
52
  return {
53
53
  type: types.EDIT_TEMPLATE_REQUEST,
54
54
  template,
55
+ callback,
55
56
  };
56
57
  }
57
58
 
@@ -1,18 +1,18 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { Component } from 'react';
3
3
  import get from 'lodash/get';
4
+ import set from 'lodash/set';
4
5
  import { bindActionCreators } from 'redux';
5
6
  import { intlShape } from 'react-intl';
6
7
  import { createStructuredSelector } from 'reselect';
7
- import { Row, Col, Spin } from 'antd';
8
8
  import _ from 'lodash';
9
- import { CapHeading } from '@capillarytech/cap-ui-library';
9
+ import { CapSpin, CapHeading, CapRow, CapColumn } from '@capillarytech/cap-ui-library';
10
10
  import messages from './messages';
11
11
  import * as actions from './actions';
12
12
  import Gallery from '../../Assets/Gallery';
13
13
  import FormBuilder from '../../../v2Components/FormBuilder';
14
14
  //import { makeSelectCreate, makeSelectCreateResponse, makeSelectEdit, makeSelectEditResponse, makeSelectTemplateDetailsResponse } from './selectors';
15
- import {makeSelectCreateLine} from './selectors';
15
+ import {makeSelectCreateLine, makeSelectLine} from './selectors';
16
16
  import { makeSelectMetaEntities, setInjectedTags } from '../../Cap/selectors';
17
17
  import {getMessageObject} from '../../../utils/messageUtils';
18
18
  import { makeSelectTemplates } from '../../Templates/selectors';
@@ -44,7 +44,7 @@ export class Line extends Component {
44
44
  'title-tagList': {
45
45
  onTagSelect: this.onTagSelectTitle,
46
46
  },
47
- pane: {
47
+ 'pane': {
48
48
  onTabChange: this.onTabChange,
49
49
  },
50
50
  'line-template': {
@@ -53,7 +53,7 @@ export class Line extends Component {
53
53
  'image-upload-line': {
54
54
  onUpload: this.uploadImage,
55
55
  },
56
- tagList: {
56
+ 'tagList': {
57
57
  onTagSelect: this.onTagSelect,
58
58
  },
59
59
  'line-editor': {
@@ -88,7 +88,7 @@ export class Line extends Component {
88
88
  isReady: false,
89
89
  modeType: null,
90
90
  isDrawerRequired: false,
91
- galleryPage: 1,
91
+ startValidation: false,
92
92
  };
93
93
  }
94
94
 
@@ -127,8 +127,26 @@ export class Line extends Component {
127
127
  if (modeType) {
128
128
  this.setState({ modeType, schema: {} });
129
129
  }
130
- if (nextProps.isGetFormData) {
131
- nextProps.getFormLibraryData(this.getFormData());
130
+ const {
131
+ isGetFormData,
132
+ templateData,
133
+ } = nextProps;
134
+ if (isGetFormData) {
135
+ const {
136
+ lineData,
137
+ getFormSubscriptionData,
138
+ } = nextProps;
139
+ let selectedAccount = {};
140
+ if (lineData) {
141
+ const {
142
+ weCrmAccounts = [],
143
+ } = lineData;
144
+ selectedAccount = weCrmAccounts.filter((account) => account === selectedAccount);
145
+ }
146
+ getFormSubscriptionData({ ...this.getFormData(), accountDetails: selectedAccount});
147
+ }
148
+ if (templateData && templateData.edit) {
149
+ this.setEditState(templateData);
132
150
  }
133
151
  if (isSchemaReady) {
134
152
  // set schema value as per mode type. Supported mode (text & image)
@@ -221,24 +239,6 @@ export class Line extends Component {
221
239
  this.props.actions.clearData();
222
240
  }
223
241
 
224
- const isEditSucceed =
225
- this.state.isEdit &&
226
- nextProps.Line &&
227
- nextProps.Line.editResponse &&
228
- nextProps.Line.editResponse.templateId;
229
- if (isEditSucceed) {
230
- this.setState({ formData: {} });
231
- const message = getMessageObject(
232
- 'success',
233
- this.props.intl.formatMessage(
234
- messages['Line Template Edited Successfully']
235
- ),
236
- true
237
- );
238
- this.props.globalActions.addMessageToQueue(message);
239
- this.props.actions.clearEditResponse();
240
- }
241
-
242
242
  if (
243
243
  nextProps.Line.editTemplateError &&
244
244
  !_.isEqual(
@@ -272,6 +272,40 @@ export class Line extends Component {
272
272
  }
273
273
  }
274
274
 
275
+ setEditSuccessMessage = () => {
276
+ this.setState({ formData: {} });
277
+ const message = getMessageObject(
278
+ 'success',
279
+ this.props.intl.formatMessage(
280
+ messages['Line Template Edited Successfully']
281
+ ),
282
+ true
283
+ );
284
+ this.props.globalActions.addMessageToQueue(message);
285
+ this.props.actions.clearEditResponse();
286
+ }
287
+
288
+ setEditState = (data) => {
289
+ const { formData: previousFormData } = this.state;
290
+ const { text, originalContentUrl, imagePreview, image } = get(data, "versions.base.content.messages[0]");
291
+ const retrievedData = {
292
+ ...previousFormData[0],
293
+ };
294
+ if (text) {
295
+ retrievedData["message-editor"] = text;
296
+ }
297
+ const lineImage = originalContentUrl || image;
298
+ if (lineImage) {
299
+ retrievedData.image = lineImage;
300
+ retrievedData.imagePreview = imagePreview;
301
+ }
302
+ const formData = {
303
+ 0: retrievedData,
304
+ base: retrievedData,
305
+ };
306
+ this.setState({ formData });
307
+ }
308
+
275
309
  componentWillUnmount() {
276
310
  window.removeEventListener('message', this.handleFrameTasks);
277
311
  }
@@ -297,7 +331,7 @@ export class Line extends Component {
297
331
  }
298
332
 
299
333
  onFormDataChange = (formData, tabCount, currentTab) => {
300
- this.setState({ formData });
334
+ this.setState({ formData: { ...formData } });
301
335
  if (tabCount) {
302
336
  this.setState({ tabCount });
303
337
  }
@@ -468,13 +502,30 @@ export class Line extends Component {
468
502
  return;
469
503
  }
470
504
  }
471
- if (this.state.isEdit) {
472
- this.props.actions.editTemplate(obj, this.props.onCreateComplete);
473
- } else {
474
- this.props.actions.createTemplate(
475
- obj,
476
- this.props.onCreateComplete
477
- );
505
+ if (!this.state.startValidation) {
506
+ this.setState({
507
+ startValidation: true,
508
+ }, () => {
509
+ const {
510
+ isFullMode,
511
+ saveMessage,
512
+ actions,
513
+ onCreateComplete,
514
+ } = this.props;
515
+ if (!isFullMode) {
516
+ saveMessage();
517
+ } else if (this.state.isEdit) {
518
+ actions.editTemplate(obj, () => {
519
+ this.setEditSuccessMessage();
520
+ onCreateComplete();
521
+ });
522
+ } else {
523
+ actions.createTemplate(
524
+ obj,
525
+ onCreateComplete
526
+ );
527
+ }
528
+ });
478
529
  }
479
530
  };
480
531
 
@@ -763,16 +814,24 @@ export class Line extends Component {
763
814
 
764
815
  removeStandAlone = () => {
765
816
  const schema = _.cloneDeep(this.state.schema);
766
- if (
767
- this.props.location.query.type === 'embedded' &&
768
- this.props.location.query.module === 'loyalty'
769
- ) {
817
+ const {
818
+ location: {
819
+ query: {
820
+ type,
821
+ mode,
822
+ module,
823
+ },
824
+ },
825
+ } = this.props;
826
+ if (type === 'embedded' && module === 'loyalty') {
770
827
  schema.standalone.sections.splice(0, 1);
771
- } else if (
772
- this.props.location.query.type === 'embedded' &&
773
- typeof this.props.location.query.mode !== 'undefined'
774
- ) {
775
- delete schema.standalone;
828
+ } else if (type === 'embedded' && typeof mode !== 'undefined') {
829
+ delete schema.standalone;
830
+ } else if (!this.props.isFullMode) {
831
+ const childSections = get(schema, 'standalone.sections[0].childSections');
832
+ const fields = get(childSections, '[1].childSections[0].childSections');//removing template name section
833
+ fields.splice(0, 1);
834
+ set(childSections, '[1].childSections[0].childSections', fields);
776
835
  } else {
777
836
  schema.standalone.sections.splice(1, 1);
778
837
  }
@@ -910,18 +969,31 @@ export class Line extends Component {
910
969
  };
911
970
 
912
971
  render() {
913
- if (
914
- this.state.formData &&
915
- this.state.formData['0'] &&
916
- this.state.formData['0'].image
917
- ) {
918
- console.log('before render@@@', this.state.formData['0'].image);
919
- }
920
- let loading = this.props.Line.createTemplateInProgress
921
- ? this.props.Line.createTemplateInProgress
922
- : false;
923
- if (this.props.Line.assetUploading) {
924
- loading = true;
972
+ const {
973
+ Line: {
974
+ createTemplateInProgress,
975
+ assetUploading,
976
+ } = {},
977
+ templateData: {
978
+ edit,
979
+ mode,
980
+ orgId,
981
+ } = {},
982
+ params: {
983
+ id,
984
+ } = {},
985
+ } = this.props;
986
+ let loading = createTemplateInProgress || false;
987
+ loading = !!assetUploading;
988
+ if (edit || mode === 'edit' || (orgId && id)) {
989
+ const {
990
+ base: {
991
+ 'message-editor': msgEditor,
992
+ image: uploadedAsset,
993
+ } = {},
994
+ reUpload,
995
+ } = this.state.formData;
996
+ loading = typeof reUpload === 'undefined' ? !(msgEditor || uploadedAsset) : !reUpload;
925
997
  }
926
998
  let tags =
927
999
  this.props.metaEntities && this.props.metaEntities.tags
@@ -931,9 +1003,9 @@ export class Line extends Component {
931
1003
  tags = this.props.supportedTags;
932
1004
  }
933
1005
  return (
934
- <Spin spinning={loading}>
935
- <Row>
936
- <Col>
1006
+ <CapSpin spinning={loading}>
1007
+ <CapRow>
1008
+ <CapColumn>
937
1009
  <FormBuilder
938
1010
  key={'form builder'}
939
1011
  schema={this.state.schema}
@@ -965,10 +1037,11 @@ export class Line extends Component {
965
1037
  setDrawerVisibility={(drawervisibleFlag) =>
966
1038
  this.setState({ isDrawerRequired: drawervisibleFlag })
967
1039
  }
1040
+ startValidation={this.state.startValidation}
968
1041
  />
969
- </Col>
970
- </Row>
971
- </Spin>
1042
+ </CapColumn>
1043
+ </CapRow>
1044
+ </CapSpin>
972
1045
  );
973
1046
  }
974
1047
  }
@@ -989,10 +1062,13 @@ Line.propTypes = {
989
1062
  injectedTags: PropTypes.object,
990
1063
  onCreateComplete: PropTypes.func,
991
1064
  isFullMode: PropTypes.bool,
1065
+ saveMessage: PropTypes.func,
1066
+ templateData: PropTypes.object,
992
1067
  };
993
1068
 
994
1069
  const mapStateToProps = createStructuredSelector({
995
1070
  Line: makeSelectCreateLine(),
1071
+ lineData: makeSelectLine(),
996
1072
  Templates: makeSelectTemplates(),
997
1073
  metaEntities: makeSelectMetaEntities(),
998
1074
  injectedTags: setInjectedTags(),
@@ -16,7 +16,7 @@ export function* createTemplate({ template, callback }) {
16
16
  }
17
17
  yield put({ type: types.CREATE_TEMPLATE_SUCCESS, data: result.response, statusCode: result.status ? result.status.code : '', errorMsg });
18
18
  if (callback) {
19
- callback();
19
+ callback(result.response);
20
20
  }
21
21
  } catch (error) {
22
22
  yield put({ type: types.CREATE_TEMPLATE_FAILURE, error, errorMsg });
@@ -56,7 +56,7 @@ export function* editTemplate({ template, callback }) {
56
56
  errorMsg = result.message;
57
57
  }
58
58
  if (callback) {
59
- callback();
59
+ callback(result.response);
60
60
  }
61
61
  yield put({ type: types.EDIT_TEMPLATE_SUCCESS, data: result.response, statusCode: result.status ? result.status.code : '', errorMsg });
62
62
  } catch (error) {
@@ -17,19 +17,14 @@ const selectLineMessageEditDomain = () => (state) => state.get('lineCreate');
17
17
 
18
18
  const makeSelectLine = () => createSelector(
19
19
  selectLineDomain(),
20
- (substate) => substate.toJS()
20
+ (substate) => substate && substate.toJS()
21
21
  );
22
22
 
23
23
  const makeSelectCreateLine = () => createSelector(
24
24
  selectLineMessageEditDomain(),
25
- (substate) => substate.toJS()
25
+ (substate) => substate && substate.toJS()
26
26
  );
27
27
 
28
- // const makeSelectDuplicate = () => createSelector(
29
- // selectLineDuplicateDomain(),
30
- // (substate) => substate.toJS()
31
- // );
32
-
33
28
  export {
34
29
  makeSelectLine, makeSelectCreateLine,
35
30
  };
@@ -5,7 +5,8 @@
5
5
  */
6
6
  import React, {useState} from 'react';
7
7
  import { injectIntl, FormattedMessage, intlShape } from 'react-intl';
8
- import {CapRadioCard, CapLabel, CapButton} from '@capillarytech/cap-ui-library';
8
+ import PropTypes from 'prop-types';
9
+ import { CapRadioCard, CapLabel, CapButton } from '@capillarytech/cap-ui-library';
9
10
  import ComponentWithLabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
10
11
  import styled from 'styled-components';
11
12
  import { CAP_SPACE_04, CAP_SPACE_08, CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
@@ -33,7 +34,7 @@ const CardContainer = styled.div`
33
34
  `;
34
35
 
35
36
  const CreateLineWrapper = (props) => {
36
- const { intl: { formatMessage } } = props;
37
+ const { intl: { formatMessage }, selectedAccount } = props;
37
38
 
38
39
  const getModes = (modeList) => (
39
40
  modeList.map(({ label, imageSrc, type, title }) => ({
@@ -103,12 +104,14 @@ const CreateLineWrapper = (props) => {
103
104
  {...props}
104
105
  params={{mode: selectedLineTemplateOption}}
105
106
  route={{name: selectedLineTemplateOption}}
107
+ selectedAccount={selectedAccount}
106
108
  />
107
109
  );
108
110
  };
109
111
 
110
112
  CreateLineWrapper.propTypes = {
111
113
  intl: intlShape.isRequired,
114
+ selectedAccount: PropTypes.object,
112
115
  };
113
116
 
114
117
  export default injectIntl(CreateLineWrapper);
@@ -40,4 +40,16 @@ export default defineMessages({
40
40
  id: `${prefix}.continue`,
41
41
  defaultMessage: 'Continue',
42
42
  },
43
+ "accountLineListTitle": {
44
+ id: `${prefix}.accountLineListTitle`,
45
+ defaultMessage: 'Line accounts',
46
+ },
47
+ "accountLineListDescription": {
48
+ id: `${prefix}.accountLineListDescription`,
49
+ defaultMessage: 'Choose the account to send content',
50
+ },
51
+ "accountLineNoListTitle": {
52
+ id: `${prefix}.accountLineNoListTitle`,
53
+ defaultMessage: 'No line accounts present',
54
+ },
43
55
  });
@@ -13,23 +13,27 @@
13
13
  &:nth-child(4n+4) { //every 4th child
14
14
  margin-right: unset;
15
15
  }
16
+ }
17
+ }
18
+ }
19
+ }
16
20
 
17
- .LINE {
18
- .ant-card-body {
19
- background-color: $CAP_G09;
20
- height: 320px;
21
- }
22
-
23
- &.no-image .ant-card-meta {
24
- height: 276px;
25
- background: $CAP_WHITE;
26
- padding: $CAP_SPACE_16 $CAP_SPACE_12;
27
-
28
- .ant-card-meta-description {
29
- height: 250px;
30
- word-wrap: break-word;
31
- }
32
- }
21
+ .creatives-templates-list {
22
+ .pagination-container {
23
+ .LINE {
24
+ .ant-card-body {
25
+ background-color: $CAP_G09;
26
+ height: 320px;
27
+ }
28
+
29
+ &.no-image .ant-card-meta {
30
+ height: 276px;
31
+ background: $CAP_WHITE;
32
+ padding: $CAP_SPACE_16 $CAP_SPACE_12;
33
+
34
+ .ant-card-meta-description {
35
+ height: 250px;
36
+ word-wrap: break-word;
33
37
  }
34
38
  }
35
39
  }
@@ -52,6 +52,13 @@
52
52
  };
53
53
  }
54
54
 
55
+ export function setLineAccount(lineAccount) {
56
+ return {
57
+ type: types.SET_LINE_ACCOUNT,
58
+ lineAccount,
59
+ };
60
+ }
61
+
55
62
  export function handleHtmlUpload(selectedTemplate) {
56
63
  return {
57
64
  type: types.SET_SELECTED_LAYOUT,
@@ -43,3 +43,5 @@ export const RESET_TEMPLATE = "app/v2Containers/Templates/RESET_TEMPLATE";
43
43
  export const RESET_TEMPLATE_DATA = "app/v2Containers/Templates/RESET_TEMPLATE_DATA";
44
44
  export const RESET_UPLOAD_DATA = "app/v2Containers/Templates/RESET_UPLOAD_DATA";
45
45
  export const CLEAR_TEMPLATE_STORE_DATA = "app/v2Containers/Templates/CLEAR_TEMPLATE_STORE_DATA";
46
+
47
+ export const SET_LINE_ACCOUNT = "app/v2Containers/Templates/SET_LINE_ACCOUNT";
@@ -160,6 +160,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
160
160
  break;
161
161
  case LINE:
162
162
  channel = "Line";
163
+ if (!this.props.isFullMode) {
164
+ activeMode = ACCOUNT_SELECTION_MODE;
165
+ this.props.actions.getWeCrmAccounts(channel);
166
+ }
163
167
  break;
164
168
  default:
165
169
  channel = '';
@@ -239,6 +243,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
239
243
  channel = 'Ebill';
240
244
  } else if (nextProps.route.name.toLowerCase() === 'line') {
241
245
  channel = 'Line';
246
+ if (!this.props.isFullMode) {
247
+ this.setState({defaultAccount: true});
248
+ nextProps.actions.getWeCrmAccounts(channel);
249
+ }
242
250
  }
243
251
  this.setState({ channel }, () => {
244
252
  if (this.state.channel.toLowerCase() === 'line' || this.state.channel.toLowerCase() === 'sms' || this.state.channel.toLowerCase() === 'email' || this.state.channel.toLowerCase() === 'ebill' || (this.state.channel.toLowerCase() === 'wechat' && !isEmpty(nextProps.Templates.selectedWeChatAccount))) {
@@ -421,6 +429,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
421
429
  } else {
422
430
  this.setState({selectedAccountError: false});
423
431
  }
432
+ } else if (this.state.channel.toLowerCase() === 'line') {
433
+ this.props.actions.setLineAccount(this.props.Templates.weCrmAccounts.find(item => item.name === this.state.selectedAccount));
424
434
  }
425
435
  this.setState({activeMode: TEMPLATES_MODE});
426
436
  console.log("getAllTemplates params: ", params);
@@ -460,6 +470,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
460
470
  } else {
461
471
  creativesParams._id = pathname.split('/')[3];
462
472
  creativesParams.modeType = pathname.split('/')[4];
473
+ creativesParams.account = pathname.split('/')[5];
463
474
  }
464
475
  creativesParams.type = this.state.channel.toUpperCase();
465
476
  }
@@ -577,7 +588,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
577
588
  onClick={() => { if (this.props.isFullMode) { handlers.handlePreviewClick(template); } else { this.props.handlePeviewTemplate(template); } }}
578
589
  />,
579
590
  ],
580
- hoverOption: <CapButton className={this.props.isFullMode ? `edit-${channelLowerCase}` : `select-${channelLowerCase}`} onClick={(e) => handlers.handleEditClick(e, template)}>{this.props.intl.formatMessage(this.props.isFullMode ? messages.creativesTemplatesEdit : messages.selectButton)}</CapButton>,
591
+ hoverOption: <CapButton className={this.props.isFullMode ? `edit-${channelLowerCase}` : `select-${channelLowerCase}`} onClick={(e) => handlers.handleEditClick(e, template, undefined, undefined, {account: this.state.selectedAccount})}>{this.props.intl.formatMessage(this.props.isFullMode ? messages.creativesTemplatesEdit : messages.selectButton)}</CapButton>,
581
592
  };
582
593
  if (this.props.isFullMode) {
583
594
  templateData.extra.push(<CapDropdown
@@ -1119,7 +1130,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1119
1130
  } else if (this.state.channel.toLowerCase() === "email") {
1120
1131
  this.props.emailActions.duplicateTemplate(template._id, "EMAIL");
1121
1132
  } else if (this.state.channel.toLowerCase() === "line") {
1122
- this.props.lineActions.createTemplate(duplicateObj);
1133
+ this.props.lineActions.createTemplate(duplicateObj, (response) => {
1134
+ if (response && response.templateId) {
1135
+ const message = `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`;
1136
+ CapNotification.success({key: 'duplicateSucess', message});
1137
+ this.getAllTemplates({params, resetPage: true});
1138
+ this.props.lineActions.clearCreateResponse();
1139
+ }
1140
+ });
1123
1141
  }
1124
1142
  }
1125
1143
 
@@ -1161,7 +1179,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1161
1179
  }
1162
1180
  };
1163
1181
 
1164
- handleEditClick(e, template, modeType, path) {
1182
+ handleEditClick(e, template, modeType, path, options) {
1165
1183
  timeTracker.startTimer(CHANNEL_EDIT_TRACK_MAPPING[this.state.channel.toLowerCase()]);
1166
1184
  if (modeType && modeType !== undefined) {
1167
1185
  this.setState({modeType});
@@ -1197,6 +1215,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1197
1215
  const { messages } = template.versions.base.content;
1198
1216
  const type = messages && messages[0] && messages[0].type;
1199
1217
  pathName = `/line/edit/${id}/${type}`;
1218
+ if (options && options.account) {
1219
+ pathName += `/${options.account}`;
1220
+ }
1200
1221
  }
1201
1222
  if (this.state.channel.toUpperCase() === WECHAT) {
1202
1223
  if (get(template, 'definition.msgcontent', '') === "RICH_MEDIA_WECHAT") {
@@ -1577,10 +1598,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1577
1598
  const { weCrmAccounts = [], fetchingWeCrmAccounts = false } = templates;
1578
1599
  const isWechatChannel = channel === WECHAT;
1579
1600
  const isMobilePushChannel = channel === MOBILE_PUSH;
1601
+ const isLineChannel = channel === LINE;
1580
1602
 
1581
- if ((isWechatChannel || isMobilePushChannel) && !isEmpty(weCrmAccounts)) {
1603
+ if ((isWechatChannel || isMobilePushChannel || isLineChannel) && !isEmpty(weCrmAccounts)) {
1582
1604
  forEach(weCrmAccounts, (account) => {
1583
- if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || isMobilePushChannel) {
1605
+ if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || isMobilePushChannel || isLineChannel) {
1584
1606
  if (query.type === 'embedded' && (!query.module || (query.module && query.module !== 'library'))) {
1585
1607
  if (query.source_account_id && account.sourceAccountIdentifier === query.source_account_id) {
1586
1608
  accountOptions.push(
@@ -1613,11 +1635,37 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1613
1635
  }
1614
1636
  });
1615
1637
  }
1638
+ const {
1639
+ intl: {
1640
+ formatMessage,
1641
+ },
1642
+ } = this.props;
1643
+ let accountHeader = formatMessage(messages.mobilepushAccount);
1644
+ let noAccountHeader = messages.noAccountsPresent;
1645
+ switch (channel) {
1646
+ case MOBILE_PUSH: {
1647
+ accountHeader = formatMessage(messages.mobilepushAccount);
1648
+ noAccountHeader = messages.noAccountsPresent;
1649
+ break;
1650
+ }
1651
+ case WECHAT: {
1652
+ accountHeader = formatMessage(messages.noAccountsPresentWeChat);
1653
+ noAccountHeader = messages.noAccountsPresent;
1654
+ break;
1655
+ }
1656
+ case LINE: {
1657
+ accountHeader = formatMessage(messages.lineAccount);
1658
+ noAccountHeader = messages.noAccountsPresentLine;
1659
+ break;
1660
+ }
1661
+ default:
1662
+ break;
1663
+ }
1616
1664
  return (<CapSpin spinning={fetchingWeCrmAccounts}>
1617
- {isEmpty(weCrmAccounts) && !fetchingWeCrmAccounts ? <FormattedMessage {...(channel === WECHAT ? messages.noAccountsPresentWeChat : messages.noAccountsPresent)} /> :
1665
+ {isEmpty(weCrmAccounts) && !fetchingWeCrmAccounts ? <FormattedMessage {...noAccountHeader} /> :
1618
1666
  <div style={{ overflowX: "auto", paddingBottom: CAP_SPACE_16 }}>
1619
1667
  <CapHeader
1620
- title={isMobilePushChannel ? this.props.intl.formatMessage(messages.mobilepushAccount) : this.props.intl.formatMessage(messages.wechatAccount)}
1668
+ title={accountHeader}
1621
1669
  description={this.props.intl.formatMessage(messages.chooseAccount)}
1622
1670
  size="regular"
1623
1671
  className="select-account-header"
@@ -1798,6 +1846,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1798
1846
  channel={route.name.toUpperCase()}
1799
1847
  handleCloseCreatives={this.onCreateComplete}
1800
1848
  selectedWeChatAccount={get(this.props, 'Templates.selectedWeChatAccount', {})}
1849
+ selectedAccount={this.state.selectedAccount}
1801
1850
  />
1802
1851
  }
1803
1852
  </CapRow>
@@ -126,6 +126,10 @@ export default defineMessages({
126
126
  id: `${scope}.noAccountsPresentWeChat`,
127
127
  defaultMessage: "Wechat accounts are not setup for your brand",
128
128
  },
129
+ "noAccountsPresentLine": {
130
+ id: `${scope}.noAccountsPresentLine`,
131
+ defaultMessage: "Line accounts are not setup for your brand",
132
+ },
129
133
  "noTemplatesMessage": {
130
134
  id: `${scope}.noTemplatesMessage`,
131
135
  defaultMessage: "No templates available",
@@ -326,6 +330,10 @@ export default defineMessages({
326
330
  id: `${scope}.mobilepushAccount`,
327
331
  defaultMessage: 'Mobilepush account',
328
332
  },
333
+ "lineAccount": {
334
+ id: `${scope}.lineAccount`,
335
+ defaultMessage: 'Line account',
336
+ },
329
337
  "all": {
330
338
  id: `${scope}.all`,
331
339
  defaultMessage: 'All',
@@ -71,6 +71,8 @@ function templatesReducer(state = initialState, action) {
71
71
  case types.SET_WECHAT_ACCOUNT:
72
72
  return state
73
73
  .set('selectedWeChatAccount', fromJS(action.weChatAccount));
74
+ case types.SET_LINE_ACCOUNT:
75
+ return state.set('selectedLineAccount', fromJS(action.lineAccount));
74
76
  case types.RESET_ACCOUNT:
75
77
  return state
76
78
  .remove('selectedWeChatAccount')