@capillarytech/creatives-library 7.10.39 → 7.10.41

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 (38) hide show
  1. package/containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +0 -2
  2. package/containers/Email/index.js +2 -3
  3. package/index.js +6 -0
  4. package/package.json +1 -1
  5. package/routes.js +5 -0
  6. package/services/api.js +6 -1
  7. package/v2Components/TemplatePreview/_templatePreview.scss +7 -0
  8. package/v2Components/TemplatePreview/assets/images/whatsapp_mobile_android.svg +2041 -0
  9. package/v2Components/TemplatePreview/index.js +57 -3
  10. package/v2Components/TemplatePreview/messages.js +8 -0
  11. package/v2Containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +0 -2
  12. package/v2Containers/Assets/images/whatsappIllustration.png +0 -0
  13. package/v2Containers/CreativesContainer/SlideBoxContent.js +31 -2
  14. package/v2Containers/CreativesContainer/SlideBoxHeader.js +31 -3
  15. package/v2Containers/CreativesContainer/constants.js +1 -0
  16. package/v2Containers/CreativesContainer/messages.js +4 -0
  17. package/v2Containers/Email/index.js +1 -3
  18. package/v2Containers/Line/Container/index.js +1 -1
  19. package/v2Containers/Templates/_templates.scss +43 -0
  20. package/v2Containers/Templates/actions.js +18 -10
  21. package/v2Containers/Templates/constants.js +3 -1
  22. package/v2Containers/Templates/index.js +296 -44
  23. package/v2Containers/Templates/messages.js +32 -0
  24. package/v2Containers/Templates/reducer.js +3 -1
  25. package/v2Containers/TemplatesV2/index.js +2 -0
  26. package/v2Containers/TemplatesV2/messages.js +4 -0
  27. package/v2Containers/Whatsapp/actions.js +34 -0
  28. package/v2Containers/Whatsapp/constants.js +125 -0
  29. package/v2Containers/Whatsapp/index.js +831 -0
  30. package/v2Containers/Whatsapp/index.scss +56 -0
  31. package/v2Containers/Whatsapp/messages.js +221 -0
  32. package/v2Containers/Whatsapp/reducer.js +54 -0
  33. package/v2Containers/Whatsapp/sagas.js +77 -0
  34. package/v2Containers/Whatsapp/selectors.js +12 -0
  35. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +120831 -0
  36. package/v2Containers/Whatsapp/tests/index.test.js +132 -0
  37. package/v2Containers/Whatsapp/tests/mockData.js +10 -0
  38. package/v2Containers/Whatsapp/utils.js +106 -0
@@ -22,7 +22,7 @@ import find from 'lodash/find';
22
22
  import map from 'lodash/map';
23
23
  import { bindActionCreators } from 'redux';
24
24
  import { Modal, Form} from 'antd';
25
- import {CapMenu, CapDropdown, CapButton, CapInput, CapIcon, CapRadioCard, CapPopover, CapSpin, CapCustomCard, CapRow, CapSlideBox, CapLink, CapHeading, CapNotification, CapIllustration, CapHeader, CapLabel, CapRadio} from '@capillarytech/cap-ui-library';
25
+ import {CapMenu, CapDropdown, CapButton, CapInput, CapIcon, CapRadioCard, CapPopover, CapSpin, CapCustomCard, CapRow, CapSlideBox, CapLink, CapHeading, CapNotification, CapIllustration, CapHeader, CapLabel, CapRadio, CapSelectFilter, CapTag, CapTooltip, CapAlert } from '@capillarytech/cap-ui-library';
26
26
  import { makeSelectTemplates, makeSelectTemplatesResponse } from './selectors';
27
27
  import { makeSelectCreate as makeSelectCreateSms} from '../Sms/Create/selectors';
28
28
  import { makeSelectCreate as makeSelectCreateMobilePush } from '../MobilePush/Create/selectors';
@@ -43,7 +43,7 @@ import * as emailActions from '../Email/actions';
43
43
  import * as lineActions from '../Line/Container/actions';
44
44
  import * as viberActions from '../Viber/actions';
45
45
  import * as facebookActions from '../Facebook/actions';
46
-
46
+ import * as whatsappActions from '../Whatsapp/actions';
47
47
  import CardGrid from '../../v2Components/CardGrid';
48
48
  import config from '../../config/app';
49
49
  import './_templates.scss';
@@ -60,13 +60,23 @@ import {
60
60
  VIBER as VIBER_CHANNEL,
61
61
  FACEBOOK as FACEBOOK_CHANNEL,
62
62
  } from '../App/constants';
63
- import { MOBILE_PUSH, WECHAT, SMS, EMAIL, EBILL, LINE, VIBER, FACEBOOK } from '../CreativesContainer/constants';
63
+ import { MAX_WHATSAPP_TEMPLATES } from './constants';
64
+ import {
65
+ CATEGORY_OPTIONS,
66
+ STATUS_OPTIONS,
67
+ CATEGORY as WHATSAPP_CATEGORY,
68
+ STATUS as WHATSAPP_STATUS,
69
+ WHATSAPP_STATUSES,
70
+ } from '../Whatsapp/constants';
71
+ import { getWhatsappContent, getWhatsappStatusContainer, getWhatsappStatus, getWhatsappCategory } from '../Whatsapp/utils';
72
+ import { MOBILE_PUSH, WECHAT, SMS, EMAIL, EBILL, LINE, VIBER, FACEBOOK, WHATSAPP } from '../CreativesContainer/constants';
64
73
 
65
74
  import {CREATIVE} from '../Facebook/constants';
66
75
 
67
76
  import emailIllustration from '../Assets/images/emailIllustration.svg';
68
77
  import smsIllustration from '../Assets/images/smsIllustration.svg';
69
78
  import pushIllustration from '../Assets/images/pushIllustration.svg';
79
+ import whatsappIllustration from '../Assets/images/whatsappIllustration.png';
70
80
  import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
71
81
  import { GA } from '@capillarytech/cap-ui-utils';
72
82
  const { timeTracker } = GA;
@@ -105,6 +115,7 @@ const SMS_FILTERS = {
105
115
  SERVICE_IMPLICIT: 'implicit',
106
116
  };
107
117
 
118
+ const WHATSAPP_LOWERCASE = WHATSAPP.toLowerCase();
108
119
  export class Templates extends React.Component { // eslint-disable-line react/prefer-stateless-function
109
120
  constructor(props) {
110
121
  super(props);
@@ -133,6 +144,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
133
144
  wechatFilter: WECHAT_FILTERS.ALL,
134
145
  lineFilter: LINE_FILTERS.ALL,
135
146
  smsFilter: SMS_FILTERS.ALL,
147
+ selectedWhatsappStatus: '',
148
+ selectedWhatsappCategory: ''
136
149
  };
137
150
 
138
151
  this.handleOnHoverItem = this.handleOnHoverItem.bind(this);
@@ -198,6 +211,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
198
211
  activeMode = ACCOUNT_SELECTION_MODE;
199
212
  this.props.actions.getAccountsSettings();
200
213
  break;
214
+ case WHATSAPP:
215
+ channel = 'Whatsapp';
216
+ activeMode = ACCOUNT_SELECTION_MODE;
217
+ this.props.actions.getWeCrmAccounts(channel);
218
+ break;
201
219
  default:
202
220
  channel = '';
203
221
  }
@@ -245,9 +263,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
245
263
  };
246
264
  const { totalCount: nextPropsTotalTemplatesCount } = nextProps?.TemplatesList?.[0] || {};
247
265
  const { totalCount: propsTotalTemplatesCount } = this.props?.TemplatesList?.[0] || {};
248
- const { templatesCount } = this.state || {};
249
266
 
250
- if (nextPropsTotalTemplatesCount && nextPropsTotalTemplatesCount !== propsTotalTemplatesCount && templatesCount === 0) {
267
+ if (nextPropsTotalTemplatesCount && nextPropsTotalTemplatesCount !== propsTotalTemplatesCount) {
251
268
  this.setState({ templatesCount: nextPropsTotalTemplatesCount });
252
269
  }
253
270
  if (!isEqual(nextProps.route.name, this.props.route.name)) {
@@ -422,6 +439,8 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
422
439
  this.props.viberActions.clearCreateResponse();
423
440
  } else if (this.state.channel.toLowerCase() === FACEBOOK_CHANNEL) {
424
441
  this.props.facebookActions.clearCreateResponse();
442
+ }else if (this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE) {
443
+ this.props.whatsappActions.clearCreateResponse();
425
444
  }
426
445
  }
427
446
  }
@@ -463,16 +482,25 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
463
482
  this.setState({previewTemplate: nextProps.Templates.templateDetails});
464
483
  }
465
484
 
466
- if (nextProps.Templates.weCrmAccounts !== undefined && nextProps.Templates.weCrmAccounts.length === 1 && this.state.defaultAccount && (['wechat'].indexOf(this.state.channel.toLowerCase()) > -1)) {
485
+ if (nextProps.Templates.weCrmAccounts !== undefined && nextProps.Templates.weCrmAccounts.length === 1 && this.state.defaultAccount && (['wechat', WHATSAPP_LOWERCASE].indexOf(this.state.channel.toLowerCase()) > -1 )) {
467
486
  const paramsDefault = {
468
487
  name: this.state.searchText,
469
488
  sortBy: this.state.sortBy,
470
489
  };
471
- if (this.state.channel.toLowerCase() === 'wechat') {
490
+ if (['wechat', WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) {
472
491
  paramsDefault.wecrmId = nextProps.Templates.weCrmAccounts[0].configs.wecrm_app_id;
473
492
  paramsDefault.wecrmToken = nextProps.Templates.weCrmAccounts[0].configs.wecrm_token;
474
493
  paramsDefault.originalId = nextProps.Templates.weCrmAccounts[0].sourceAccountIdentifier;
475
- nextProps.actions.setWeChatAccount(nextProps.Templates.weCrmAccounts[0]);
494
+ if (WHATSAPP_LOWERCASE === this.state.channel.toLowerCase()) {
495
+ nextProps.actions.setChannelAccount(
496
+ WHATSAPP,
497
+ nextProps.Templates.weCrmAccounts[0],
498
+ );
499
+ } else {
500
+ nextProps.actions.setWeChatAccount(
501
+ nextProps.Templates.weCrmAccounts[0],
502
+ );
503
+ }
476
504
  }
477
505
  this.setState({defaultAccount: false, activeMode: TEMPLATES_MODE, selectedAccount: nextProps.Templates.weCrmAccounts[0].name });
478
506
  this.getAllTemplates({params: paramsDefault}, true);
@@ -512,8 +540,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
512
540
  } else {
513
541
  this.setState({selectedAccountError: false});
514
542
  }
515
- } else if (this.state.channel.toLowerCase() === 'line') {
516
- this.props.actions.setLineAccount(this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
543
+ } else if (this.state.channel.toLowerCase() === LINE.toLowerCase()) {
544
+ this.props.actions.setChannelAccount(LINE, this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
545
+ } else if (this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE) {
546
+ this.props.actions.setChannelAccount(WHATSAPP, this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
517
547
  }
518
548
 
519
549
  this.setState({activeMode: TEMPLATES_MODE});
@@ -525,8 +555,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
525
555
  this.getAllTemplates({getNextPage: true});
526
556
  }
527
557
  onCreateComplete = (isReloadTemplates) => {
558
+ const isWhatsappEdit = this.state.routeParams?.pathname?.includes(`${WHATSAPP_LOWERCASE}/edit`);
528
559
  this.setState({routeParams: null, previewOpen: false}, () => {
529
- if (this.isFullMode() && isReloadTemplates) {
560
+ if (this.isFullMode() && (isReloadTemplates || isWhatsappEdit)) {
530
561
  this.getAllTemplates({params: {}}, true);
531
562
  }
532
563
  });
@@ -546,7 +577,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
546
577
  const { pathname } = routeParams;
547
578
  if (pathname.includes('create')) {
548
579
  creativesParams.mode = 'create';
549
- } else if (pathname.includes('edit')) {
580
+ } else if (pathname.includes('edit') || pathname.includes('overview')) {
550
581
  creativesParams.mode = 'edit';
551
582
  if (pathname.includes('richmedia')) {
552
583
  creativesParams._id = pathname.split('/')[4];
@@ -557,6 +588,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
557
588
  creativesParams._id = pathname.split('/')[3];
558
589
  creativesParams.modeType = pathname.split('/')[4];
559
590
  creativesParams.account = pathname.split('/')[5];
591
+ if(this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE){
592
+ const whatsappSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
593
+ creativesParams.whatsappTemplateName = get(whatsappSelectedTemplateData, `name`, '');
594
+ creativesParams.whatsappTemplateCategory = get(whatsappSelectedTemplateData, `versions.base.content.whatsapp.category`, '');
595
+ }
560
596
  }
561
597
  creativesParams.type = this.state.channel.toUpperCase();
562
598
  }
@@ -654,6 +690,32 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
654
690
  return templates;
655
691
  }
656
692
  }
693
+
694
+ isStatusMatching = (status, selectedWhatsappStatus) => {
695
+ if ( [WHATSAPP_STATUSES.unsubmitted, WHATSAPP_STATUSES.pending].includes(status) ) {
696
+ return selectedWhatsappStatus === WHATSAPP_STATUSES.awaitingApproval;
697
+ }
698
+ return selectedWhatsappStatus === status;
699
+ }
700
+
701
+ filterWhatsappTemplates = (templates) => {
702
+ const { selectedWhatsappStatus, selectedWhatsappCategory } = this.state;
703
+ return templates.filter((template) => {
704
+ const { [WHATSAPP_CATEGORY]: category, [WHATSAPP_STATUS]: status } = template?.versions?.base?.content?.[WHATSAPP_LOWERCASE] || {};
705
+
706
+ // if status or category filter is applied, filter templates which match these filters
707
+ return (
708
+ selectedWhatsappStatus
709
+ ? this.isStatusMatching(status, selectedWhatsappStatus)
710
+ : true
711
+ ) && (
712
+ selectedWhatsappCategory
713
+ ? category === selectedWhatsappCategory
714
+ : true
715
+ );
716
+ });
717
+ }
718
+
657
719
  filterSMSTemplates = (templates) => {
658
720
  const { smsFilter } = this.state;
659
721
  if (SMS_FILTERS.ALL === smsFilter) {
@@ -662,22 +724,44 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
662
724
  return templates.filter((template) => template.versions.base.type?.toLowerCase()?.includes(smsFilter));
663
725
  }
664
726
 
727
+ getCreativesEditText = (channel) => {
728
+ if (channel === WHATSAPP_LOWERCASE) {
729
+ return messages.whatsappOverview;
730
+ }
731
+ return messages.creativesTemplatesEdit;
732
+ }
733
+
665
734
  getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
666
735
  const currentChannel = channel.toUpperCase();
667
736
  const {channel: stateChannel} = this.state;
668
737
  const channelLowerCase = stateChannel.toLowerCase();
669
738
  let filteredTemplates = templates;
670
739
  let isTraiDltFeature = false;
671
- if (currentChannel === WECHAT) {
672
- filteredTemplates = this.filterWechatTemplates(templates);
673
- }
674
- if (currentChannel === LINE) {
675
- filteredTemplates = this.filterLineTemplates(templates);
676
- }
677
- if (currentChannel === SMS) {
678
- filteredTemplates = this.filterSMSTemplates(templates);
679
- isTraiDltFeature = this.checkDLTfeatureEnable();
740
+ switch (currentChannel) {
741
+ case WECHAT:
742
+ filteredTemplates = this.filterWechatTemplates(templates);
743
+ break;
744
+ case WHATSAPP:
745
+ filteredTemplates = this.filterWhatsappTemplates(templates);
746
+ break;
747
+ case LINE:
748
+ filteredTemplates = this.filterLineTemplates(templates);
749
+ break;
750
+ case SMS:
751
+ filteredTemplates = this.filterSMSTemplates(templates);
752
+ isTraiDltFeature = this.checkDLTfeatureEnable();
753
+ break;
754
+ default:
755
+ break;
680
756
  }
757
+
758
+ const hoverButtonText =
759
+ this.props.intl.formatMessage(
760
+ this.props.isFullMode
761
+ ? this.getCreativesEditText(channelLowerCase)
762
+ : messages.selectButton
763
+ );
764
+
681
765
  const cardDataList = filteredTemplates && filteredTemplates.length ? map(filteredTemplates, (template) => {
682
766
  const templateData =
683
767
  {
@@ -691,13 +775,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
691
775
  onClick={() => { if (this.props.isFullMode) { handlers.handlePreviewClick(template); } else { this.props.handlePeviewTemplate(template); } }}
692
776
  />,
693
777
  ],
694
- 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>,
778
+ hoverOption: <CapButton className={this.props.isFullMode ? `edit-${channelLowerCase}` : `select-${channelLowerCase}`} onClick={(e) => handlers.handleEditClick(e, template, undefined, undefined, {account: this.state.selectedAccount})}>{hoverButtonText}</CapButton>,
695
779
  };
696
780
  if (this.props.isFullMode) {
697
781
  templateData.extra.push(<CapDropdown
698
782
  overlay={
699
783
  <CapMenu>
700
- {this.state.channel.toUpperCase() !== WECHAT && !isTraiDltFeature && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
784
+ { ![WECHAT, WHATSAPP].includes(this.state.channel.toUpperCase()) && !isTraiDltFeature && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
701
785
  <FormattedMessage {...messages.duplicateButton} />
702
786
  </CapMenu.Item>}
703
787
  <CapMenu.Item
@@ -834,6 +918,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
834
918
  }
835
919
  break;
836
920
  }
921
+ case WHATSAPP:
922
+ templateData.title = (
923
+ <div className="whatsapp-card-title">
924
+ <span title={template.name} >{template.name}</span>
925
+ {getWhatsappStatusContainer(template)}
926
+ </div>
927
+ );
928
+
929
+ templateData.content = getWhatsappContent(template);
930
+ break;
837
931
  default:
838
932
  templateData.content = "";
839
933
  }
@@ -842,8 +936,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
842
936
  const accountId = get(this.props, 'Templates.selectedWeChatAccount.uuid');
843
937
  const accounts = get(this.props, 'Templates.weCrmAccounts');
844
938
  return (<div>
845
- { [WECHAT, MOBILE_PUSH].includes(currentChannel) && this.showAccountName() }
939
+ {[WECHAT, MOBILE_PUSH].includes(currentChannel) && this.showAccountName()}
846
940
  {filterContent}
941
+ {[WHATSAPP].includes(currentChannel) && this.selectedFilters()}
847
942
  <CapSpin spinning={isLoading} tip={loadingTip} style={{width: '100%'}}>
848
943
  {<div>
849
944
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -865,7 +960,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
865
960
  </div>
866
961
  }
867
962
 
868
- { !isEmpty(this.props.Templates.selectedWeChatAccount) && this.state.channel.toLowerCase() === "mobilepush" && this.state.selectedAccountError ?
963
+ { !isEmpty(this.props.Templates.selectedWeChatAccount) && ["mobilepush"].includes(this.state.channel.toLowerCase()) && this.state.selectedAccountError ?
869
964
  <div style={{ height: '65vh', textAlign: 'center', lineHeight: '65vh'}}>
870
965
  <CapHeading type="h6" >
871
966
  {this.props.intl.formatMessage(messages.accountConfigError)}
@@ -882,6 +977,24 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
882
977
  </div>
883
978
  )
884
979
  }
980
+ { !isEmpty(this.props.Templates.selectedWhatsappAccount) && [WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase()) && this.state.selectedAccountError ?
981
+ <div style={{ height: '65vh', textAlign: 'center', lineHeight: '65vh'}}>
982
+ <CapHeading type="h6" >
983
+ {this.props.intl.formatMessage(messages.accountConfigError)}
984
+ <InlineDiv>
985
+ <CapLink title="here" style={{display: 'inline'}} onClick={() => { window.open(config.production.accountConfig`${accountId}`); }}/>
986
+ </InlineDiv>
987
+ </CapHeading>
988
+ </div> : (
989
+ [WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase()) &&
990
+ isEmpty(filteredTemplates) &&
991
+ isEmpty(this.state.searchText) &&
992
+ !isLoading &&
993
+ <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto'} : {}}>
994
+ <CapIllustration buttonClassName={`create-new-${channelLowerCase}`} {...this.getChannelTypeIllustrationInfo(currentChannel)} />
995
+ </div>
996
+ )
997
+ }
885
998
  </div>
886
999
  }
887
1000
 
@@ -1456,6 +1569,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1456
1569
  pathName = `/wechat/edit/${id}`;
1457
1570
  }
1458
1571
  }
1572
+ if (this.state.channel.toUpperCase() === WHATSAPP) {
1573
+ pathName = `/whatsapp/edit/${id}/`;
1574
+ }
1459
1575
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
1460
1576
  let data = id;
1461
1577
  if (this.props.fbAdManager) {
@@ -1716,7 +1832,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1716
1832
  if (this.props.location.query.type === "embedded" && this.props.location.query.module === "library") {
1717
1833
  switch (property) {
1718
1834
  case "showAccountSelection":
1719
- return (this.props.route && (this.props.route.name.toLowerCase() === "wechat" || this.props.route.name.toLowerCase() === "mobilepush"));
1835
+ return (this.props.route && (['wechat', 'mobilepush', WHATSAPP_LOWERCASE].includes(this.props.route.name.toLowerCase())));
1720
1836
  case "setLocale":
1721
1837
  return true;
1722
1838
  case "callCreateFromProps":
@@ -1734,9 +1850,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1734
1850
  isCreateDisabled = () => {
1735
1851
  let isDisabled = this.isLoading();
1736
1852
  const channel = this.state.channel.toUpperCase();
1737
- if (channel === MOBILE_PUSH || channel === WECHAT) {
1853
+ if (channel === MOBILE_PUSH || channel === WECHAT || channel === WHATSAPP) {
1738
1854
  isDisabled = isDisabled || isEmpty(this.props.Templates.selectedWeChatAccount);
1739
1855
  }
1856
+ if (channel === WHATSAPP) {
1857
+ // limit on max number of templates allowed for whatsapp
1858
+ isDisabled = this.state.templatesCount >= MAX_WHATSAPP_TEMPLATES;
1859
+ }
1740
1860
  if (channel === MOBILE_PUSH) {
1741
1861
  isDisabled = this.checkSearchDisabled();
1742
1862
  }
@@ -1758,6 +1878,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1758
1878
  const facebookLoader = (getAllTemplatesInProgress && this.state.channel.toLowerCase() === FACEBOOK_CHANNEL);
1759
1879
 
1760
1880
  const wechatLoader = (((this.state.selectedAccount !== '' && getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === 'wechat');
1881
+ const whatsappLoader = (((this.state.selectedAccount !== '' && getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE);
1761
1882
  const mobilePushLoader = (((getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === 'mobilepush');
1762
1883
  const emailLoader = (
1763
1884
  (getAllTemplatesInProgress ||
@@ -1766,6 +1887,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1766
1887
  EmailCreate.duplicateTemplateInProgress) && this.state.channel.toLowerCase() === 'email');
1767
1888
  const ebillLoader = ((getAllTemplatesInProgress || sendingFile) && this.state.channel.toLowerCase() === 'ebill');
1768
1889
  const isLoading = ((wechatLoader !== undefined) ? wechatLoader : false) ||
1890
+ (whatsappLoader !== undefined ? whatsappLoader : false) ||
1769
1891
  (smsLoader !== undefined ? smsLoader : false) ||
1770
1892
  (emailLoader !== undefined ? emailLoader : false) ||
1771
1893
  (ebillLoader !== undefined ? ebillLoader : false) ||
@@ -1801,6 +1923,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1801
1923
  illustrationImage: pushIllustration,
1802
1924
  title: <FormattedMessage {...messages.pushTitleIllustartion} values={{ template: templateText }} />,
1803
1925
  };
1926
+ case WHATSAPP:
1927
+ return {
1928
+ buttonLabel: <FormattedMessage {...messages.newWhatsappTemplate} values={{ template: templateText }} />,
1929
+ onClick: this.createTemplate,
1930
+ illustrationImage: whatsappIllustration,
1931
+ title: <FormattedMessage {...messages.whatsappTitleIllustration} values={{ template: templateText }} />,
1932
+ description: <FormattedMessage {...messages.whatsappDescIllustration} />,
1933
+ descriptionPosition: 'bottom',
1934
+ descriptionClassName: 'whatsapp-illustration-desc',
1935
+ };
1804
1936
  default:
1805
1937
  return {};
1806
1938
  }
@@ -1852,13 +1984,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1852
1984
  const { weCrmAccounts = [], fetchingWeCrmAccounts = false, campaignSettings = {} } = templates;
1853
1985
  const isWechatChannel = channel === WECHAT;
1854
1986
  const isMobilePushChannel = channel === MOBILE_PUSH;
1855
- const isLineChannel = channel === LINE;
1856
- const isFacebookChannel = channel === FACEBOOK;
1857
-
1987
+ const isFacebookChannel = channel === FACEBOOK;
1858
1988
 
1859
- if ((isWechatChannel || isMobilePushChannel || isLineChannel) && !isEmpty(weCrmAccounts) && !isFacebookChannel ) {
1989
+ if ([WECHAT, MOBILE_PUSH, LINE, WHATSAPP].includes(channel) && !isEmpty(weCrmAccounts) && !isFacebookChannel) {
1860
1990
  forEach(weCrmAccounts, (account) => {
1861
- if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || isMobilePushChannel || isLineChannel) {
1991
+ if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || [MOBILE_PUSH, LINE, WHATSAPP].includes(channel)) {
1862
1992
  if (query.type === 'embedded' && (!query.module || (query.module && query.module !== 'library'))) {
1863
1993
  if (query.source_account_id && account.sourceAccountIdentifier === query.source_account_id) {
1864
1994
  accountOptions.push(
@@ -1922,6 +2052,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1922
2052
  noAccountHeader = messages.noAccountsPresentFacebook;
1923
2053
  break;
1924
2054
  }
2055
+ case WHATSAPP: {
2056
+ accountHeader = formatMessage(messages.whatsappAccount);
2057
+ noAccountHeader = messages.noAccountsPresentWhatsapp;
2058
+ break;
2059
+ }
1925
2060
  default:
1926
2061
  break;
1927
2062
  }
@@ -1977,27 +2112,97 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1977
2112
  this.setState({activeMode: TEMPLATES_MODE});
1978
2113
  }
1979
2114
 
2115
+ accountNameMessage = (channel) => {
2116
+ const {
2117
+ intl: {
2118
+ formatMessage,
2119
+ },
2120
+ } = this.props;
2121
+ switch (channel?.toUpperCase()) {
2122
+ case MOBILE_PUSH:
2123
+ return formatMessage(messages.mobilepushAccount);
2124
+ case WECHAT:
2125
+ return formatMessage(messages.wechatAccount);
2126
+ case WHATSAPP:
2127
+ return formatMessage(messages.whatsappAccount);
2128
+ default:
2129
+ return '';
2130
+ }
2131
+ }
2132
+
1980
2133
  showAccountName = () => {
1981
2134
  const { activeMode, selectedAccount, channel } = this.state;
1982
2135
  const { Templates: templates = {}} = this.props;
1983
- const { weCrmAccounts = []} = templates;
1984
- const isMobilePushChannel = channel === MOBILE_PUSH;
2136
+ const { weCrmAccounts = [] } = templates;
1985
2137
  return (activeMode !== ACCOUNT_CHANGE_MODE &&
1986
2138
  <CapHeader
1987
- title={<CapHeading type="h4">{isMobilePushChannel ? this.props.intl.formatMessage(messages.mobilepushAccount) : this.props.intl.formatMessage(messages.wechatAccount)}</CapHeading>}
2139
+ title={<CapHeading type="h4">{this.accountNameMessage(channel)}</CapHeading>}
1988
2140
  description={<><CapHeading type="h3">{selectedAccount} {weCrmAccounts.length > 1 && <CapLink onClick={this.setAccountChangeMode} style={{display: 'inline-block', position: 'relative', top: '3px'}} fontWeight="regular" fontSize="14px" title="change"/>} </CapHeading></>}
1989
2141
  size="regular"
1990
2142
  />);
1991
2143
  }
1992
2144
 
2145
+ removeWhatsappFilter = (filter) => {
2146
+ switch (filter) {
2147
+ case WHATSAPP_STATUS:
2148
+ this.setState({ selectedWhatsappStatus: null });
2149
+ break;
2150
+ case WHATSAPP_CATEGORY:
2151
+ this.setState({ selectedWhatsappCategory: null });
2152
+ break;
2153
+ default:
2154
+ break;
2155
+ }
2156
+ }
2157
+
2158
+ selectedFilters = () => {
2159
+ const { selectedWhatsappStatus, selectedWhatsappCategory } = this.state;
2160
+ return (
2161
+ <CapRow type="flex" align="middle" className="selected-whatsapp-filters">
2162
+ {
2163
+ selectedWhatsappStatus ? (
2164
+ <CapTag closable onClose={() => this.removeWhatsappFilter(WHATSAPP_STATUS)}>
2165
+ Status: {getWhatsappStatus(selectedWhatsappStatus)?.label || selectedWhatsappStatus}
2166
+ </CapTag>
2167
+ ) : null
2168
+ }
2169
+ {
2170
+ selectedWhatsappCategory ? (
2171
+ <CapTag closable onClose={() => this.removeWhatsappFilter(WHATSAPP_CATEGORY)}>
2172
+ Category: {getWhatsappCategory(selectedWhatsappCategory)?.label || selectedWhatsappCategory}
2173
+ </CapTag>
2174
+ ) : null
2175
+ }
2176
+ {
2177
+ (selectedWhatsappStatus || selectedWhatsappCategory) ? (
2178
+ <CapLink
2179
+ onClick={() => {
2180
+ this.removeWhatsappFilter(WHATSAPP_STATUS);
2181
+ this.removeWhatsappFilter(WHATSAPP_CATEGORY);
2182
+ }}
2183
+ title={this.props.intl.formatMessage(messages.clearAll)}
2184
+ />
2185
+ ) : null
2186
+ }
2187
+ </CapRow>
2188
+ );
2189
+ }
2190
+
1993
2191
  setWechatFilter = (e) => this.setState({wechatFilter: e.target.value});
2192
+ setWhatsappStatus = (value) => this.setState({selectedWhatsappStatus: value?.item?.props?.value});
2193
+ setWhatsappCategory = (value) => this.setState({selectedWhatsappCategory: value?.item?.props?.value});
1994
2194
  setLineFilter = (e) => this.setState({lineFilter: e.target.value});
1995
2195
  setSMSFilter = (e) => this.setState({smsFilter: e.target.value});
1996
2196
 
1997
2197
 
1998
2198
  render() {
1999
2199
  const isFullMode = this.isFullMode();
2000
- const { activeMode, channel, wechatFilter, lineFilter, smsFilter } = this.state;
2200
+ const { activeMode, channel, wechatFilter, lineFilter, smsFilter, templatesCount } = this.state;
2201
+ const {
2202
+ intl: {
2203
+ formatMessage,
2204
+ } = {},
2205
+ } = this.props || {};
2001
2206
  let showFbAds = false;
2002
2207
  if (channel.toLowerCase() === FACEBOOK_CHANNEL) {
2003
2208
  const {campaignSettings, fetchedOrgLevelCampaignSettings} = this?.props?.Templates || {};
@@ -2061,6 +2266,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2061
2266
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
2062
2267
  const channelLowerCase = (channel || '').toLowerCase();
2063
2268
  const isTraiDltFeature = this.checkDLTfeatureEnable();
2269
+
2270
+ const createButton = (<CapButton
2271
+ className={`create-new-${channelLowerCase}`}
2272
+ type={"primary"}
2273
+ disabled={this.isCreateDisabled()}
2274
+ style={{marginLeft: '8px', marginBottom: '12px'}}
2275
+ onClick={this.createTemplate} >
2276
+ { this.props.intl.formatMessage((isTraiDltFeature && channel.toUpperCase() === SMS ) ? messages.uploadTemplate : messages.createNewActionButton) }
2277
+ </CapButton>);
2278
+
2279
+ const isWhatsappCountExeeded = templatesCount >= MAX_WHATSAPP_TEMPLATES;
2280
+ const whatsappCountExceedText = <FormattedMessage {...messages.whatsappMaxTemplates} values={{ maxCount: MAX_WHATSAPP_TEMPLATES }}/>;
2281
+
2064
2282
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH].includes(this.state.channel.toUpperCase())) && <div className="action-container">
2065
2283
  {isfilterContentVisisble && <CapInput.Search
2066
2284
  className="search-text"
@@ -2085,6 +2303,31 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2085
2303
  </CapLabel></CapRadio.Button>
2086
2304
  </CapRadio.CapRadioGroup>
2087
2305
  }
2306
+ {
2307
+ channel.toUpperCase() === WHATSAPP && (
2308
+ <div className="whatsapp-filters">
2309
+ <CapSelectFilter
2310
+ placement="bottomLeft"
2311
+ data={STATUS_OPTIONS}
2312
+ onSelect={this.setWhatsappStatus}
2313
+ selectedValue={this.state.selectedWhatsappStatus}
2314
+ placeholder="Status"
2315
+ showBadge={true}
2316
+ width="90px"
2317
+ />
2318
+ <CapSelectFilter
2319
+ placement="bottomLeft"
2320
+ data={CATEGORY_OPTIONS}
2321
+ onSelect={this.setWhatsappCategory}
2322
+ selectedValue={this.state.selectedWhatsappCategory}
2323
+ placeholder="Category"
2324
+ showBadge={true}
2325
+ width="105px"
2326
+ />
2327
+ </div>
2328
+
2329
+ )
2330
+ }
2088
2331
  {
2089
2332
  channel.toUpperCase() === LINE &&
2090
2333
  isFullMode
@@ -2152,14 +2395,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2152
2395
  : () => null
2153
2396
  }
2154
2397
  <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
2155
- {isfilterContentVisisble && !isWechatEmbedded && <CapButton
2156
- className={`create-new-${channelLowerCase}`}
2157
- type={"primary"}
2158
- disabled={this.isCreateDisabled()}
2159
- style={{marginLeft: '8px', marginBottom: '12px'}}
2160
- onClick={this.createTemplate} >
2161
- { this.props.intl.formatMessage((isTraiDltFeature && channel.toUpperCase() === SMS ) ? messages.uploadTemplate : messages.createNewActionButton) }
2162
- </CapButton>}
2398
+ {
2399
+ this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
2400
+ <CapTooltip title={whatsappCountExceedText}>
2401
+ <div className="button-disabled-tooltip-wrapper">
2402
+ {createButton}
2403
+ </div>
2404
+ </CapTooltip>
2405
+ )
2406
+ : isfilterContentVisisble && !isWechatEmbedded && createButton
2407
+ }
2163
2408
  </div>
2164
2409
 
2165
2410
  </div>);
@@ -2193,6 +2438,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2193
2438
  <div className={`creatives-templates-list ${this.props.isFullMode ? 'full-mode' : 'library-mode'}`}>
2194
2439
  <input type="file" id="filename" style={{ display: 'none'}} accept=".zip, .html, .htm" onChange={(event) => this.handleFileUpload(event, {files: event.target.files})} />
2195
2440
 
2441
+ {
2442
+ channel.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
2443
+ <CapAlert message={whatsappCountExceedText} type="info" />
2444
+ ) : null
2445
+ }
2196
2446
 
2197
2447
  <CapRow>
2198
2448
  <Pagination onPageChange={templates.length ? this.onPaginationChange : () => {}} paginationSelector="pagination-container">
@@ -2277,6 +2527,7 @@ Templates.propTypes = {
2277
2527
  router: PropTypes.object,
2278
2528
  viberActions: PropTypes.object,
2279
2529
  facebookActions: PropTypes.object,
2530
+ whatsappActions: PropTypes.object,
2280
2531
  smsRegister: PropTypes.any,
2281
2532
  };
2282
2533
 
@@ -2305,6 +2556,7 @@ function mapDispatchToProps(dispatch) {
2305
2556
  lineActions: bindActionCreators(lineActions, dispatch),
2306
2557
  viberActions: bindActionCreators(viberActions, dispatch),
2307
2558
  facebookActions: bindActionCreators(facebookActions, dispatch),
2559
+ whatsappActions: bindActionCreators(whatsappActions, dispatch),
2308
2560
  };
2309
2561
  }
2310
2562