@capillarytech/creatives-library 2.0.44 → 2.0.46

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 (91) hide show
  1. package/components/CmsTemplatesComponent/index.js +1 -0
  2. package/components/EmailMobilePreview/index.js +108 -0
  3. package/components/EmailMobilePreview/index.scss +66 -0
  4. package/components/FormBuilder/_formBuilder.scss +2 -14
  5. package/components/FormBuilder/index.js +106 -93
  6. package/components/Header/index.js +41 -0
  7. package/components/Header/messages.js +13 -0
  8. package/components/Header/tests/index.test.js +10 -0
  9. package/components/PreviewSideBar/index.js +7 -3
  10. package/components/TemplatePreview/index.js +6 -7
  11. package/config/app.js +2 -2
  12. package/containers/Cap/index.js +1 -1
  13. package/containers/Cap/selectors.js +11 -1
  14. package/containers/CreativesContainer/SlideBoxContent.js +5 -1
  15. package/containers/CreativesContainer/SlideBoxFooter.js +5 -9
  16. package/containers/CreativesContainer/index.js +59 -16
  17. package/containers/Email/actions.js +2 -3
  18. package/containers/Email/index.js +32 -26
  19. package/containers/Email/sagas.js +1 -0
  20. package/containers/EmailWrapper/index.js +43 -13
  21. package/containers/EmailWrapper/messages.js +8 -0
  22. package/containers/MobilePush/Create/index.js +1 -1
  23. package/containers/Sms/Create/actions.js +4 -4
  24. package/containers/Sms/Create/index.js +28 -17
  25. package/containers/Sms/Create/sagas.js +1 -2
  26. package/containers/Sms/Edit/actions.js +4 -5
  27. package/containers/Sms/Edit/index.js +31 -5
  28. package/containers/Sms/Edit/sagas.js +1 -3
  29. package/containers/Templates/actions.js +3 -1
  30. package/containers/Templates/index.js +219 -84
  31. package/containers/Templates/messages.js +8 -0
  32. package/containers/Templates/sagas.js +16 -4
  33. package/containers/Templates/selectors.js +2 -0
  34. package/containers/TemplatesV2/index.js +9 -4
  35. package/containers/TemplatesV2/reducer.js +34 -0
  36. package/containers/TemplatesV2/sagas.js +32 -0
  37. package/containers/WeChat/MapTemplates/_mapTemplates.scss +8 -0
  38. package/containers/WeChat/MapTemplates/actions.js +52 -0
  39. package/containers/WeChat/MapTemplates/constants.js +28 -0
  40. package/containers/WeChat/MapTemplates/index.js +1401 -0
  41. package/containers/WeChat/MapTemplates/messages.js +73 -0
  42. package/containers/WeChat/MapTemplates/reducer.js +74 -0
  43. package/containers/WeChat/MapTemplates/sagas.js +86 -0
  44. package/containers/WeChat/MapTemplates/selectors.js +25 -0
  45. package/containers/WeChat/MapTemplates/tests/actions.test.js +18 -0
  46. package/containers/WeChat/MapTemplates/tests/index.test.js +10 -0
  47. package/containers/WeChat/MapTemplates/tests/reducer.test.js +9 -0
  48. package/containers/WeChat/MapTemplates/tests/sagas.test.js +15 -0
  49. package/containers/WeChat/MapTemplates/tests/selectors.test.js +10 -0
  50. package/containers/WeChat/RichmediaTemplates/Create/_createRichmedia.scss +13 -0
  51. package/containers/WeChat/RichmediaTemplates/Create/actions.js +35 -0
  52. package/containers/WeChat/RichmediaTemplates/Create/constants.js +14 -0
  53. package/containers/WeChat/RichmediaTemplates/Create/index.js +679 -0
  54. package/containers/WeChat/RichmediaTemplates/Create/messages.js +13 -0
  55. package/containers/WeChat/RichmediaTemplates/Create/reducer.js +49 -0
  56. package/containers/WeChat/RichmediaTemplates/Create/sagas.js +44 -0
  57. package/containers/WeChat/RichmediaTemplates/Create/selectors.js +32 -0
  58. package/containers/WeChat/RichmediaTemplates/Create/tests/actions.test.js +18 -0
  59. package/containers/WeChat/RichmediaTemplates/Create/tests/index.test.js +10 -0
  60. package/containers/WeChat/RichmediaTemplates/Create/tests/reducer.test.js +9 -0
  61. package/containers/WeChat/RichmediaTemplates/Create/tests/sagas.test.js +15 -0
  62. package/containers/WeChat/RichmediaTemplates/Create/tests/selectors.test.js +10 -0
  63. package/containers/WeChat/RichmediaTemplates/Create/testschema.js +379 -0
  64. package/containers/WeChat/RichmediaTemplates/Edit/actions.js +20 -0
  65. package/containers/WeChat/RichmediaTemplates/Edit/constants.js +10 -0
  66. package/containers/WeChat/RichmediaTemplates/Edit/index.js +98 -0
  67. package/containers/WeChat/RichmediaTemplates/Edit/messages.js +13 -0
  68. package/containers/WeChat/RichmediaTemplates/Edit/reducer.js +28 -0
  69. package/containers/WeChat/RichmediaTemplates/Edit/sagas.js +32 -0
  70. package/containers/WeChat/RichmediaTemplates/Edit/selectors.js +25 -0
  71. package/containers/WeChat/RichmediaTemplates/Edit/tests/actions.test.js +18 -0
  72. package/containers/WeChat/RichmediaTemplates/Edit/tests/index.test.js +10 -0
  73. package/containers/WeChat/RichmediaTemplates/Edit/tests/reducer.test.js +9 -0
  74. package/containers/WeChat/RichmediaTemplates/Edit/tests/sagas.test.js +15 -0
  75. package/containers/WeChat/RichmediaTemplates/Edit/tests/selectors.test.js +10 -0
  76. package/containers/WeChat/RichmediaTemplates/View/actions.js +15 -0
  77. package/containers/WeChat/RichmediaTemplates/View/constants.js +7 -0
  78. package/containers/WeChat/RichmediaTemplates/View/index.js +45 -0
  79. package/containers/WeChat/RichmediaTemplates/View/messages.js +13 -0
  80. package/containers/WeChat/RichmediaTemplates/View/reducer.js +23 -0
  81. package/containers/WeChat/RichmediaTemplates/View/sagas.js +11 -0
  82. package/containers/WeChat/RichmediaTemplates/View/selectors.js +25 -0
  83. package/containers/WeChat/RichmediaTemplates/View/tests/actions.test.js +18 -0
  84. package/containers/WeChat/RichmediaTemplates/View/tests/index.test.js +10 -0
  85. package/containers/WeChat/RichmediaTemplates/View/tests/reducer.test.js +9 -0
  86. package/containers/WeChat/RichmediaTemplates/View/tests/sagas.test.js +15 -0
  87. package/containers/WeChat/RichmediaTemplates/View/tests/selectors.test.js +10 -0
  88. package/index.js +3 -3
  89. package/package.json +1 -1
  90. package/routes.js +53 -13
  91. package/services/api.js +6 -1
@@ -14,7 +14,7 @@ import moment from "moment";
14
14
  import _ from 'lodash';
15
15
  import { bindActionCreators } from 'redux';
16
16
  import { Row, Select, Dropdown, Menu, Modal} from 'antd';
17
- import {CapButton, CapInput, CapIcon, CapSelect, CapPopover} from '@capillarytech/cap-ui-library';
17
+ import {CapButton, CapInput, CapIcon, CapSelect, CapPopover, CapSpin, CapCustomCard} from '@capillarytech/cap-ui-library';
18
18
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
19
19
  import { makeSelectCreate as makeSelectCreateSms} from '../Sms/Create/selectors';
20
20
  import { makeSelectCreate as makeSelectCreateMobilePush } from '../MobilePush/Create/selectors';
@@ -45,6 +45,7 @@ import EmailPreview from '../../components/EmailPreview';
45
45
  import CreativesContainer from '../CreativesContainer';
46
46
  const MenuItem = Menu.Item;
47
47
  const Option = Select.Option;
48
+ const {CapCustomCardList} = CapCustomCard;
48
49
 
49
50
  export class Templates extends React.Component { // eslint-disable-line react/prefer-stateless-function
50
51
  constructor(props) {
@@ -69,6 +70,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
69
70
  searchedTemplates: false,
70
71
  defaultAccount: true,
71
72
  modeType: null,
73
+ routeParams: null,
72
74
  };
73
75
  this.handleOnHoverItem = this.handleOnHoverItem.bind(this);
74
76
  this.handleOnItemClick = this.handleOnItemClick.bind(this);
@@ -84,10 +86,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
84
86
  this.handleFrameTasks = this.handleFrameTasks.bind(this);
85
87
  this.startTemplateCreation = this.startTemplateCreation.bind(this);
86
88
  this.startLoading = this.startLoading.bind(this);
87
- this.mapWechatTemplate = this.mapWechatTemplate.bind(this);
88
89
  this.prepareWeChatPreviewData = this.prepareWeChatPreviewData.bind(this);
90
+ this.prepareWeChatRichmediaPreviewData = this.prepareWeChatRichmediaPreviewData.bind(this);
91
+ this.prepareWeChatMappedPreviewData = this.prepareWeChatMappedPreviewData.bind(this);
89
92
  this.onAccountSelect = this.onAccountSelect.bind(this);
90
93
  this.prepareMobilePushPreviewData = this.prepareMobilePushPreviewData.bind(this);
94
+ this.menuOnClickEvent = this.menuOnClickEvent.bind(this);
91
95
  }
92
96
 
93
97
  componentWillMount() {
@@ -237,12 +241,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
237
241
  this.getAllTemplates({params});
238
242
  }
239
243
  }
240
- if (this.state.channel.toLowerCase() === "sms" && nextProps.Create && nextProps.Create.response && nextProps.Create.response.templateId && nextProps.Create.response.templateId !== '') {
241
- const message = getMessageObject('success', `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`, true);
242
- this.props.globalActions.addMessageToQueue(message);
243
- this.getAllTemplates({params, resetPage: true});
244
- this.props.smsActions.clearCreateResponse();
245
- }
246
244
  if (this.state.channel.toLowerCase() === "mobilepush" && nextProps.CreateMobilePush && nextProps.CreateMobilePush.response && nextProps.CreateMobilePush.response.templateId && nextProps.CreateMobilePush.response.templateId !== '') {
247
245
  if (this.state.channel.toLowerCase() === "mobilepush" && !_.isEmpty(nextProps.Templates.selectedWeChatAccount)) {
248
246
  params.accountId = nextProps.Templates.selectedWeChatAccount.id;
@@ -372,8 +370,49 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
372
370
  });
373
371
  }
374
372
  onPaginationChange = () => { this.getAllTemplates({getNextPage: true}); }
373
+ onCreateComplete = () => {
374
+ this.setState({routeParams: null});
375
+ }
376
+ getCurrentWindow(e) {
377
+ const response = {
378
+ action: e.action,
379
+ value: 'templates',
380
+ direction: e.value,
381
+ };
382
+ parent.postMessage(JSON.stringify(response), '*');
383
+ }
384
+ getCreativesParams = () => {
385
+ const routeParams = this.state.routeParams;
386
+ const creativesParams = {mode: ''};
387
+ if (!_.isEmpty(routeParams)) {
388
+ if (routeParams.pathname.indexOf('create') > -1) {
389
+ creativesParams.mode = 'create';
390
+ } else if (routeParams.pathname.indexOf('edit') > -1) {
391
+ creativesParams.mode = 'edit';
392
+ creativesParams._id = routeParams.pathname.split('/')[3];
393
+ creativesParams.type = this.state.channel.toUpperCase();
394
+ }
395
+ }
396
+ return creativesParams;
397
+ }
398
+ getMappedLocale = (locale) => {
399
+ const map = {
400
+ 'en': 'en',
401
+ 'zh-cn': 'zh',
402
+ };
403
+ return map[locale];
404
+ };
405
+ setLocale = () => {
406
+ const user = localStorage.getItem('user');
407
+ let locale = 'en';
408
+ if (user) {
409
+ locale = JSON.parse(user).lang;
410
+ }
411
+ locale = this.getMappedLocale(locale);
412
+ //locale = 'zh';
375
413
 
376
-
414
+ moment.locale(locale);
415
+ };
377
416
  getAllTemplates = ({params, getNextPage, resetPage}, resetTemplates) => {
378
417
  let queryParams = params;
379
418
  let page = this.state.page;
@@ -418,41 +457,32 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
418
457
  });
419
458
  }
420
459
  };
421
- getMappedLocale = (locale) => {
422
- const map = {
423
- 'en': 'en',
424
- 'zh-cn': 'zh',
425
- };
426
- return map[locale];
427
- };
428
- setLocale = () => {
429
- const user = localStorage.getItem('user');
430
- let locale = 'en';
431
- if (user) {
432
- locale = JSON.parse(user).lang;
433
- }
434
- locale = this.getMappedLocale(locale);
435
- //locale = 'zh';
436
-
437
- moment.locale(locale);
438
- };
439
- getCurrentWindow(e) {
440
- const response = {
441
- action: e.action,
442
- value: 'templates',
443
- direction: e.value,
444
- };
445
- parent.postMessage(JSON.stringify(response), '*');
446
- }
447
- getCreativesMode = () => {
448
- const routeParams = this.state.routeParams;
449
- let creativesMode = '';
450
- if (routeParams) {
451
- if (routeParams.pathname.indexOf('create') > -1) {
452
- creativesMode = 'create';
460
+ getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
461
+ const currentChannel = channel.toUpperCase();
462
+ const cardDataList = templates.map((template) => {
463
+ const templateData =
464
+ {
465
+ key: `${currentChannel}-card-${template.name}`,
466
+ title: <span title={template.name} >{template.name}</span>,
467
+ extra: [<CapIcon type="eye" onClick={() => { if (this.props.isFullMode) { handlers.handlePreviewClick(template); } else { this.props.handlePeviewTemplate(template); } }} />],
468
+ hoverOption: <CapButton onClick={(e) => handlers.handleEditClick(e, template._id)}>{this.props.intl.formatMessage(this.props.isFullMode ? messages.creativesTemplatesEdit : messages.selectButton)}</CapButton>,
469
+ };
470
+ if (currentChannel === "SMS") {
471
+ templateData.content = template.versions.base['sms-editor'];
472
+ } else if (currentChannel === "EMAIL") {
473
+ templateData.url = template.versions.base.preview_http_url;
453
474
  }
454
- }
455
- return creativesMode;
475
+ return templateData;
476
+ });
477
+ return (<div>
478
+ {filterContent}
479
+ <CapSpin spinning={isLoading} tip={loadingTip}>
480
+ <div className="pagination-container">
481
+ <CapCustomCardList key={`${currentChannel}-card-list`} cardList={cardDataList} type={currentChannel} />
482
+ </div>
483
+ </CapSpin>
484
+
485
+ </div>);
456
486
  }
457
487
  skipTemplateSelection = (e) => {
458
488
  const type = this.props.location.query.type;
@@ -556,17 +586,17 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
556
586
  }
557
587
  };
558
588
 
559
- mapWechatTemplate() {
560
- // const index = _.findIndex(this.props.Templates.weCRMtemplates, {template_id: key});
561
- //
562
- const type = this.props.location.query.type;
563
- this.setState({routeParams: {
564
- pathname: `/wechat/create`,
565
- query: type === 'embedded' ? {type: 'embedded'} : {},
566
- }});
589
+ prepareWeChatPreviewData(template) {
590
+ if (template && template.versions && template.versions.base && !_.isEmpty(template.versions.base)) {
591
+ if (template.definition && template.definition.msgcontent && template.definition.msgcontent === "RICH_MEDIA_WECHAT") {
592
+ return this.prepareWeChatRichmediaPreviewData(template.versions.base);
593
+ }
594
+ return this.prepareWeChatMappedPreviewData(template.versions.base.content, template.versions.base.Tag, template.versions.base.data);
595
+ }
596
+ return 'wrong template';
567
597
  }
568
598
 
569
- prepareWeChatPreviewData(content, templateTags, tagData) {
599
+ prepareWeChatMappedPreviewData(content, templateTags, tagData) {
570
600
  let formattedContent = _.cloneDeep(content);
571
601
  _.forEach(templateTags, (tag) => {
572
602
  if (tagData[tag].value !== undefined) {
@@ -575,6 +605,39 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
575
605
  });
576
606
  return formattedContent;
577
607
  }
608
+
609
+ prepareWeChatRichmediaPreviewData(content) {
610
+ const returnObj = [];
611
+ if (content.mediaList) {
612
+ _.forEach(content.mediaList, (mediaData, mediaIndex) => {
613
+ if (mediaIndex === 0) {
614
+ returnObj.push(
615
+ <div style={{ width: 272, height: 160 }}>
616
+ <CapRow style={{ height: 110 }}>
617
+ <img alt="default" width="272px" height="110px" src={mediaData.image_url} />
618
+ </CapRow>
619
+ <CapRow style={{ height: 32 }}>
620
+ {mediaData.digest ? mediaData.digest : ""}
621
+ </CapRow>
622
+ </div>
623
+ );
624
+ } else {
625
+ returnObj.push(
626
+ <CapRow style={{ width: 272, height: 86 }}>
627
+ <CapColumn span={18}>
628
+ {mediaData.title}
629
+ </CapColumn>
630
+ <CapColumn span={6}>
631
+ <img alt="default" width="60px" height="60px" src={mediaData.image_url} />
632
+ </CapColumn>
633
+ </CapRow>
634
+ );
635
+ }
636
+ });
637
+ }
638
+ return returnObj;
639
+ }
640
+
578
641
  prepareMobilePushPreviewData(content) {
579
642
  let result;
580
643
  if (content.ANDROID) {
@@ -740,6 +803,40 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
740
803
  this.setState({routeParams});
741
804
  }
742
805
 
806
+ menuOnClickEvent(ev) {
807
+ let type = this.props.location.query.type;
808
+ // const module = this.props.location.query.module ? this.props.location.query.module : 'default';
809
+ const channel = this.state.channel.toLowerCase();
810
+ switch (channel) {
811
+ case 'wechat':
812
+ if (ev && ev.key && ev.key.toLowerCase() === "map_template") {
813
+ type = this.props.location.query.type;
814
+ this.props.router.push({
815
+ pathname: `/wechat/create`,
816
+ query: type === 'embedded' ? {type: 'embedded'} : {},
817
+ });
818
+ } else if (ev && ev.key && ev.key.toLowerCase() === "richmedia_template") {
819
+ type = this.props.location.query.type;
820
+ if (this.props.Templates && this.props.Templates.selectedWeChatAccount && this.props.Templates.selectedWeChatAccount.configs && (_.isEmpty(this.props.Templates.selectedWeChatAccount.configs.wechat_app_id) || _.isEmpty(this.props.Templates.selectedWeChatAccount.configs.wechat_app_secret))) {
821
+ const errorMessage = this.props.intl.formatMessage(messages.wechatAccountNotConfigured);
822
+ const message = getMessageObject('error', errorMessage, true);
823
+ this.props.globalActions.addMessageToQueue(message);
824
+ } else {
825
+ this.props.router.push({
826
+ pathname: `/wechat/richmedia/create`,
827
+ query: type === 'embedded' ? {type: 'embedded'} : {},
828
+ });
829
+ }
830
+ }
831
+ break;
832
+ default:
833
+ this.props.router.push({
834
+ pathname: `*`,
835
+ });
836
+ break;
837
+ }
838
+ }
839
+
743
840
  searchTemplate(data) {
744
841
  this.setState({searchText: data}, () => {
745
842
  const params = {
@@ -815,8 +912,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
815
912
  const duplicateObj = _.cloneDeep(template);
816
913
  duplicateObj.name = `Copy of ${template.name} ${moment().format('MM-DD-YYYY hh:mma')}`;
817
914
  delete duplicateObj._id;
915
+ const params = {
916
+ name: this.state.searchText,
917
+ sortBy: this.state.sortBy,
918
+ };
818
919
  if (this.state.channel.toLowerCase() === "sms") {
819
- this.props.smsActions.createTemplate(duplicateObj);
920
+ this.props.smsActions.createTemplate(duplicateObj, (response) => {
921
+ if (response && response.templateId) {
922
+ const message = getMessageObject('success', `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`, true);
923
+ this.props.globalActions.addMessageToQueue(message);
924
+ this.getAllTemplates({params, resetPage: true});
925
+ this.props.smsActions.clearCreateResponse();
926
+ }
927
+ });
820
928
  } else if (this.state.channel.toLowerCase() === "mobilepush") {
821
929
  duplicateObj.definition.accountId = this.props.Templates.selectedWeChatAccount.id;
822
930
  this.props.mobilepushActions.createTemplate(duplicateObj);
@@ -867,7 +975,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
867
975
  }
868
976
  };
869
977
 
870
- handleEditClick(e, id, modeType) {
978
+ handleEditClick(e, id, modeType, path) {
871
979
  if (modeType && modeType !== undefined) {
872
980
  this.setState({modeType});
873
981
  }
@@ -876,11 +984,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
876
984
  const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : false;
877
985
  const isEdmSupport = (this.props.location.query.isEdmSupport !== "false") || false;
878
986
  let getQuery = "";
879
-
987
+ let pathName = path;
880
988
  e.stopPropagation();
881
989
 
882
990
  this.props.actions.resetTemplateData();
883
- let pathName = `/${this.state.channel === 'wechat' ? 'wechat' : 'sms'}/edit/${id}`;
991
+ if (this.state.channel.toLowerCase() === 'sms') {
992
+ pathName = `/sms/edit/${id}`;
993
+ }
884
994
  if (this.state.channel.toLowerCase() === 'mobilepush') {
885
995
  pathName = `/mobilepush/edit/${id}`;
886
996
  }
@@ -919,7 +1029,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
919
1029
  }
920
1030
  templateInfo.updatedByName = commonUtil.getUserNameById(parseInt(template.updatedBy, 10), commonUtil.getMergedUserList(this.props.Templates.userList));
921
1031
  if (this.state.channel === 'wechat') {
922
- templateInfo.content = this.prepareWeChatPreviewData(template.versions.base.content, template.versions.base.Tag, template.versions.base.data);
1032
+ templateInfo.content = this.prepareWeChatPreviewData(template);
923
1033
  } else if (this.state.channel.toLowerCase() === "mobilepush") {
924
1034
  templateInfo.content = template.versions.base;
925
1035
  templateInfo.appName = this.props.Templates.selectedWeChatAccount.name;
@@ -1006,16 +1116,25 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1006
1116
  let content = '';
1007
1117
  temp.id = template._id;
1008
1118
  if (this.state.channel === 'wechat') {
1009
- content = this.prepareWeChatPreviewData(template.versions.base.content, template.versions.base.Tag, template.versions.base.data);
1010
- temp.content = (
1011
- <div
1012
- className="sms-template-content">
1013
- <div className={`sms-text ${this.state.hoveredItem === template._id ? 'sms-text-blurred' : ''}`}>
1014
- {content || ''}
1119
+ let path = '';
1120
+ if (template && template.versions && template.versions.base && !_.isEmpty(template.versions.base)) {
1121
+ if (template.definition && template.definition.msgcontent && template.definition.msgcontent === "RICH_MEDIA_WECHAT") {
1122
+ path = `/wechat/richmedia/edit/${template._id}`;
1123
+ } else {
1124
+ path = `/wechat/edit/${template._id}`;
1125
+ }
1126
+ content = this.prepareWeChatPreviewData(template);
1127
+ temp.content = (
1128
+ <div
1129
+ className="sms-template-content">
1130
+ <div className={`sms-text ${this.state.hoveredItem === template._id ? 'sms-text-blurred' : ''}`}>
1131
+ {content || ''}
1132
+ </div>
1133
+ {template._id === this.state.hoveredItem ? <CapButton onClick={(e) => this.handleEditClick(e, template._id, null, path)} className="edit-button" type="secondary">{type === 'embedded' ? this.props.intl.formatMessage(messages.selectButton) : this.props.intl.formatMessage(messages.editButton)}</CapButton> : ''}
1134
+ {template._id === this.state.hoveredItem ? <CapButton onClick={() => this.handlePreviewClick(template)} className="preview-button" type="cancel">{this.props.intl.formatMessage(messages.previewButton)}</CapButton> : ''}
1015
1135
  </div>
1016
- {template._id === this.state.hoveredItem ? <CapButton onClick={(e) => this.handleEditClick(e, template._id)} className="edit-button" type="secondary">{type === 'embedded' ? this.props.intl.formatMessage(messages.selectButton) : this.props.intl.formatMessage(messages.editButton)}</CapButton> : ''}
1017
- {template._id === this.state.hoveredItem ? <CapButton onClick={() => this.handlePreviewClick(template)} className="preview-button" type="cancel">{this.props.intl.formatMessage(messages.previewButton)}</CapButton> : ''}
1018
- </div>);
1136
+ );
1137
+ }
1019
1138
  } else if (this.state.channel.toLowerCase() === 'email' || this.state.channel.toLowerCase() === 'ebill') {
1020
1139
  //
1021
1140
 
@@ -1081,6 +1200,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1081
1200
  } else if (template.versions && template.versions.base && template.versions.base.IOS) {
1082
1201
  availableTemplates = 'iOS';
1083
1202
  }
1203
+ let deleteOption = this.props.intl.formatMessage(messages.deleteButton);
1204
+ if (this.state.channel.toLowerCase() === 'wechat' && !template.definition) {
1205
+ deleteOption = this.props.intl.formatMessage(messages.unMapButton);
1206
+ }
1084
1207
  if (!layoutSelection) {
1085
1208
  temp.footer = (
1086
1209
  <div className="footer-container">
@@ -1097,7 +1220,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1097
1220
  <span onClick={() => this.duplicateTemplate(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.props.intl.formatMessage(messages.duplicateButton)}</span>
1098
1221
  </div>}
1099
1222
  <div className="popover-action-container">
1100
- <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{this.state.channel !== 'wechat' ? this.props.intl.formatMessage(messages.deleteButton) : this.props.intl.formatMessage(messages.unMapButton)}</span>
1223
+ <span onClick={() => this.toggleDeleteTemplateModal(template)} className="popover-action" style={{cursor: 'pointer', padding: '8px 0px'}}>{deleteOption}</span>
1101
1224
  </div>
1102
1225
  </div>
1103
1226
  }
@@ -1131,7 +1254,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1131
1254
  changePreviewDevice = (device) => {
1132
1255
  this.setState({ device });
1133
1256
  };
1134
-
1135
1257
  //Gets called only when creatives gets used as node library
1136
1258
  isEnabledInLibraryModule = (property) => {
1137
1259
  if (this.props.location.query.type === "embedded" && this.props.location.query.module === "library") {
@@ -1188,7 +1310,6 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1188
1310
  />
1189
1311
  );
1190
1312
  }
1191
-
1192
1313
  render() {
1193
1314
  const accountOptions = [];
1194
1315
  if ((this.state.channel.toUpperCase() === 'WECHAT' || this.state.channel.toUpperCase() === "MOBILEPUSH") && !_.isEmpty(this.props.Templates.weCrmAccounts)) {
@@ -1231,16 +1352,26 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1231
1352
  let actionButton = '';
1232
1353
  if (this.props.location.query.type !== 'embedded') {
1233
1354
  if (this.state.channel.toUpperCase() === 'WECHAT') {
1234
- actionButton = (
1235
- <CapButton disabled={_.isEmpty(this.props.Templates.selectedWeChatAccount)} onClick={this.mapWechatTemplate} style={{paddingLeft: '24px', paddingRight: '4px'}}>
1355
+ const createPushMenu = (
1356
+ <Menu onClick={this.menuOnClickEvent}>
1357
+ <MenuItem key="map_template">
1358
+ {this.props.intl.formatMessage(messages.mapTemplate)}
1359
+ </MenuItem>
1360
+ <MenuItem key="richmedia_template">
1361
+ {this.props.intl.formatMessage(messages.weChatRichmediaTemplate)}
1362
+ </MenuItem>
1363
+ </Menu>
1364
+ );
1365
+ actionButton = (<Dropdown disabled={_.isEmpty(this.props.Templates.selectedWeChatAccount)} overlay={createPushMenu} trigger={['click']}>
1366
+ <CapButton style={{paddingLeft: '24px', paddingRight: '4px'}}>
1236
1367
  <div style={{display: 'inline-flex'}}>
1237
- <p style={{ lineHeight: '28px', paddingRight: '24px' }}><FormattedMessage {...messages.mapTemplate} /></p>
1238
- {/* <p style={{ height: '28px', borderLeft: '1px solid #fff' }}>
1239
- <i style={{ color: '#fff', paddingTop: '2px', margin: '0'}} className="material-icons">arrow_drop_down</i>
1240
- </p> */}
1368
+ <p style={{ lineHeight: '28px'}}>{this.props.intl.formatMessage(messages.newTemplate)}</p>
1369
+ <p style={{ height: '28px'}}>
1370
+ <i style={{ color: '#fff', paddingTop: '2px', margin: '0', paddingRight: '8px'}} className="material-icons">arrow_drop_down</i>
1371
+ </p>
1241
1372
  </div>
1242
1373
  </CapButton>
1243
- );
1374
+ </Dropdown>);
1244
1375
  } else if (this.state.channel.toUpperCase() === 'EMAIL' || this.state.channel.toUpperCase() === 'EBILL') {
1245
1376
  const menuItems = [];
1246
1377
  menuItems.push(<MenuItem key={'uploadFile'}>{this.props.intl.formatMessage(messages.uploadFile)}</MenuItem>);
@@ -1342,11 +1473,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1342
1473
  );
1343
1474
  const filterContent = (
1344
1475
  <div className="action-container">
1345
- <CapInput
1476
+ <CapInput.Search
1346
1477
  className="search-text"
1347
1478
  style={{width: '210px'}}
1348
1479
  placeholder={this.props.intl.formatMessage(messages.searchText)}
1349
- prefix={<CapIcon type="search" size="s"/>}
1350
1480
  value={this.state.searchText}
1351
1481
  onChange={(e) => this.searchTemplate(e.target.value)}
1352
1482
  />
@@ -1371,7 +1501,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1371
1501
  const templateList = this.populateTemplatesList(this.props.TemplatesList, true);
1372
1502
 
1373
1503
  const isSearch = this.props.Templates.isSearch;
1374
- const creativesMode = this.getCreativesMode();
1504
+ const creativesParams = this.getCreativesParams();
1375
1505
  const {isFullMode, route} = this.props;
1376
1506
  const loadingTip = (this.props.Templates.sendingFile && (this.state.channel.toLowerCase() === 'email' || this.state.channel.toLowerCase() === 'ebill')) ? this.props.intl.formatMessage(messages.uploadingFile) : this.props.intl.formatMessage(messages.gettingAllTemplates);
1377
1507
  return (
@@ -1384,9 +1514,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1384
1514
 
1385
1515
 
1386
1516
  <Row>
1387
- {this.props.renderNewCardGrid && templateList && templateList.length > 0 &&
1517
+ { templateList && templateList.length > 0 &&
1388
1518
  <Pagination onPageChange={this.onPaginationChange} paginationSelector="pagination-container">
1389
- {this.props.renderNewCardGrid({isLoading, loadingTip, channel: this.state.channel, templates: this.props.TemplatesList, filterContent, handlers: {handlePreviewClick: this.handlePreviewClick, handleEditClick: this.handleEditClick}})}
1519
+ {this.getTemplateDataForGrid({isLoading, loadingTip, channel: this.state.channel, templates: this.props.TemplatesList, filterContent, handlers: {handlePreviewClick: this.handlePreviewClick, handleEditClick: this.handleEditClick}})}
1390
1520
  </Pagination>
1391
1521
  }
1392
1522
  {!this.props.renderNewCardGrid && ((templateList && templateList.length > 0) || isSearch) &&
@@ -1411,13 +1541,17 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1411
1541
  </div>}
1412
1542
  </Row>
1413
1543
  <Row>
1414
- {!_.isEmpty(creativesMode) && isFullMode &&
1544
+ {!_.isEmpty(creativesParams.mode) && isFullMode && //creatives container for fullmode
1415
1545
  <CreativesContainer
1546
+ key={creativesParams.mode}
1416
1547
  messageDetails={{type: "default"}}
1417
- creativesMode={creativesMode}
1548
+ creativesMode={creativesParams.mode}
1418
1549
  messages={messages}
1419
1550
  isFullMode={isFullMode}
1420
- channel={route.name.toUpperCase()}/>
1551
+ templateData={creativesParams}
1552
+ getCreativesData={this.onCreateComplete}
1553
+ channel={route.name.toUpperCase()}
1554
+ handleCloseCreatives={this.onCreateComplete}/>
1421
1555
  }
1422
1556
  <PreviewSideBar
1423
1557
  showPreview={this.state.previewOpen}
@@ -1477,6 +1611,7 @@ Templates.propTypes = {
1477
1611
  lineActions: PropTypes.object,
1478
1612
  isFullMode: PropTypes.bool,
1479
1613
  renderNewCardGrid: PropTypes.func,
1614
+ handlePeviewTemplate: PropTypes.func,
1480
1615
  };
1481
1616
 
1482
1617
  const mapStateToProps = createStructuredSelector({
@@ -22,6 +22,10 @@ export default defineMessages({
22
22
  id: `${scope}.mapTemplate`,
23
23
  defaultMessage: `Map Template`,
24
24
  },
25
+ "weChatRichmediaTemplate": {
26
+ id: `${scope}.weChatRichmediaTemplate`,
27
+ defaultMessage: 'Richmedia Template',
28
+ },
25
29
  "somethingWentWrong": {
26
30
  id: `${scope}.somethingWentWrong`,
27
31
  defaultMessage: `Something Went Wrong!!`,
@@ -262,4 +266,8 @@ export default defineMessages({
262
266
  id: `${scope}.continue`,
263
267
  defaultMessage: `Continue`,
264
268
  },
269
+ "wechatAccountNotConfigured": {
270
+ id: `${scope}.wechatAccountNotConfigured`,
271
+ defaultMessage: 'Selected wechat account is not configured to create wechat templates',
272
+ },
265
273
  });
@@ -7,11 +7,19 @@ import * as types from './constants';
7
7
  // Individual exports for testing
8
8
  export function* getAllTemplates(channel, queryParams) {
9
9
  try {
10
- console.log('saga getAllTemplates', channel, queryParams);
11
10
  const result = yield call(Api.getAllTemplates, channel, queryParams);
12
- const channelTemplates = (channel.channel === 'wechat') ? {templates: result.response.mapped} : result.response;
11
+ const channelTemplates = (channel.channel === 'wechat') ? {templates: [...result.response.mapped, ...result.response.richmedia]} : result.response;
13
12
  // const sidebar = result.response.sidebar;
14
- yield put({ type: types.GET_ALL_TEMPLATES_SUCCESS, data: channelTemplates, weCRMTemplate: result.response.unMapped, isReset: channel.queryParams.page === 1});
13
+ if (channel.channel === 'wechat' && channel.queryParams && channel.queryParams.sortBy && channel.queryParams.sortBy.toLocaleLowerCase() === ("Most Recent").toLocaleLowerCase()) {
14
+ channelTemplates.templates.sort((a, b) => {
15
+ const dateA = new Date(a.updatedAt);
16
+ const dateB = new Date(b.updatedAt);
17
+ return dateB - dateA;
18
+ });
19
+ } else if (channel.channel === 'wechat' && channel.queryParams && channel.queryParams.sortBy && channel.queryParams.sortBy.toLocaleLowerCase() === ("Alphabetically").toLocaleLowerCase()) {
20
+ channelTemplates.templates.sort((a, b) => b.name - a.name);
21
+ }
22
+ yield put({ type: types.GET_ALL_TEMPLATES_SUCCESS, data: channelTemplates, weCRMTemplate: result.response.unMapped, isReset: channel.queryParams.page === 1 });
15
23
  } catch (error) {
16
24
  yield put({ type: types.GET_ALL_TEMPLATES_FAILURE, error });
17
25
  }
@@ -66,13 +74,15 @@ export function* fetchWeCrmAccounts(action) {
66
74
  }
67
75
  }
68
76
 
69
- export function* sendZippedFile({selectedFile}) {
77
+ export function* sendZippedFile({selectedFile, errorHandler, successHandler}) {
70
78
  let errorMessage = "";
71
79
  try {
72
80
  const result = yield call(Api.sendZippedFile, selectedFile);
73
81
  if (result.status.isError) {
74
82
  errorMessage = result.message;
83
+ yield errorHandler(errorMessage);
75
84
  }
85
+
76
86
  console.log("Got Result ", result);
77
87
  console.log("Got decoded result", decodeURIComponent(result.response.metaEntity.htmlContent));
78
88
  yield [
@@ -81,7 +91,9 @@ export function* sendZippedFile({selectedFile}) {
81
91
  selectedTemplate: decodeURIComponent(result.response.metaEntity.htmlContent),
82
92
  }),
83
93
  ];
94
+ yield successHandler();
84
95
  } catch (error) {
96
+ yield errorHandler();
85
97
  yield [
86
98
  put({
87
99
  type: types.SEND_ZIPPED_FILE_FAILURE,
@@ -47,6 +47,7 @@ const selectEmailLayout = () => createSelector(makeSelectTemplates(), (templates
47
47
  }
48
48
  return null;
49
49
  });
50
+ const uploadSelector = () => createSelector(makeSelectTemplates(), (templates) => templates.sendingFile ? templates.sendingFile : false );
50
51
 
51
52
  const selectCmsTemplates = () => createSelector(makeSelectTemplates(), (templates) => {
52
53
  const {getCmsTemplatesInProgress, cmsTemplates} = templates;
@@ -61,6 +62,7 @@ const selectEdmEditor = () => createSelector(makeSelectTemplates(), (templates)
61
62
  return edmTemplate || null;
62
63
  });
63
64
  export {
65
+ uploadSelector,
64
66
  templateUserList,
65
67
  makeSelectTemplates, makeSelectTemplatesResponse, selectTemplateContent, selectEmailLayout, selectCmsTemplates, selectEdmEditor,
66
68
  };
@@ -13,7 +13,7 @@ import { createStructuredSelector } from 'reselect';
13
13
  import { bindActionCreators } from 'redux';
14
14
  import styled from 'styled-components';
15
15
  import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon } from '@capillarytech/cap-ui-library';
16
- import { find } from 'lodash';
16
+ import { find, get } from 'lodash';
17
17
  import Helmet from 'react-helmet';
18
18
 
19
19
  import {CAP_SPACE_24, CAP_SPACE_16} from '@capillarytech/cap-ui-library/styled/variables';
@@ -48,12 +48,12 @@ const MarginTop = styled.div`
48
48
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
49
49
  constructor(props) {
50
50
  super(props);
51
- const defaultChannel = this.props.channel || 'sms';
51
+ const defaultChannel = get(this, 'props.channel') || get(this, 'props.params.channel') || 'sms';
52
52
  const deafaultPanes = [
53
53
  {content: <div></div>, tab: 'Sms', key: 'sms'},
54
54
  {content: <div></div>, tab: 'Email', key: 'email'},
55
55
  // {content: this.getTemplatesComponent('wechat'), tab: 'Wechat', key: 'wechat'},
56
- // {content: this.getTemplatesComponent('mobilepush'), tab: 'Mpush', key: 'mobilepush'},
56
+ // {content: <div></div>, tab: 'Mpush', key: 'mobilepush'},
57
57
  ];
58
58
  const panes = this.setChannelContent(defaultChannel, deafaultPanes);
59
59
  this.state = {
@@ -109,6 +109,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
109
109
  createNew={this.props.createNew}
110
110
  onSelectTemplate={(id) => this.props.onSelectTemplate(this.selectTemplate(id))}
111
111
  renderNewCardGrid={this.getTemplateDataForGrid}
112
+ handlePeviewTemplate={this.props.handlePeviewTemplate}
112
113
  />);
113
114
  }
114
115
 
@@ -116,7 +117,11 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
116
117
  const panes = argsPanes.map((p) => {
117
118
  const pane = p;
118
119
  if (pane.key === selectedChannel) {
119
- pane.content = this.getTemplatesComponent(pane.tab.toLowerCase());
120
+ let channel = pane.tab.toLowerCase();
121
+ if (channel === "mpush") {
122
+ channel = "mobilepush";
123
+ }
124
+ pane.content = this.getTemplatesComponent(channel);
120
125
  return pane;
121
126
  }
122
127
  return {...pane, content: <div></div>};
@@ -0,0 +1,34 @@
1
+ /*
2
+ *
3
+ * TemplatesV2 reducer
4
+ *
5
+ */
6
+
7
+ import { fromJS } from 'immutable';
8
+ import * as types from './constants';
9
+
10
+ const initialState = fromJS({
11
+ loadingTemplates: true,
12
+ smsTemplates: [],
13
+ emailTemplates: [],
14
+ wechatTemplates: [],
15
+ mobilepushTemplates: [],
16
+ });
17
+
18
+ function templatesV2Reducer(state = initialState, action) {
19
+ const channelTemplates = `${action.channel}Templates`;
20
+ switch (action.type) {
21
+ case types.DEFAULT_ACTION:
22
+ return state;
23
+ case types.GET_TEMPLATES_REQUEST:
24
+ return state.set('loadingTemplates', true);
25
+ case types.GET_TEMPLATES_SUCESS:
26
+ return state.set('loadingTemplates', false).set(channelTemplates, action.templates);
27
+ case types.GET_TEMPLATES_FAILURE:
28
+ return state.set('loadingTemplates', false).set('error', action.error);
29
+ default:
30
+ return state;
31
+ }
32
+ }
33
+
34
+ export default templatesV2Reducer;