@capillarytech/creatives-library 7.10.39 → 7.10.40

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 (37) 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 +24 -2
  14. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -0
  15. package/v2Containers/CreativesContainer/constants.js +1 -0
  16. package/v2Containers/Email/index.js +1 -3
  17. package/v2Containers/Line/Container/index.js +1 -1
  18. package/v2Containers/Templates/_templates.scss +43 -0
  19. package/v2Containers/Templates/actions.js +18 -10
  20. package/v2Containers/Templates/constants.js +3 -1
  21. package/v2Containers/Templates/index.js +259 -43
  22. package/v2Containers/Templates/messages.js +32 -0
  23. package/v2Containers/Templates/reducer.js +2 -0
  24. package/v2Containers/TemplatesV2/index.js +2 -0
  25. package/v2Containers/TemplatesV2/messages.js +4 -0
  26. package/v2Containers/Whatsapp/actions.js +17 -0
  27. package/v2Containers/Whatsapp/constants.js +98 -0
  28. package/v2Containers/Whatsapp/index.js +469 -0
  29. package/v2Containers/Whatsapp/index.scss +36 -0
  30. package/v2Containers/Whatsapp/messages.js +184 -0
  31. package/v2Containers/Whatsapp/reducer.js +39 -0
  32. package/v2Containers/Whatsapp/sagas.js +48 -0
  33. package/v2Containers/Whatsapp/selectors.js +25 -0
  34. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +123185 -0
  35. package/v2Containers/Whatsapp/tests/index.test.js +132 -0
  36. package/v2Containers/Whatsapp/tests/mockData.js +10 -0
  37. 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';
@@ -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)) {
@@ -463,12 +480,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
463
480
  this.setState({previewTemplate: nextProps.Templates.templateDetails});
464
481
  }
465
482
 
466
- if (nextProps.Templates.weCrmAccounts !== undefined && nextProps.Templates.weCrmAccounts.length === 1 && this.state.defaultAccount && (['wechat'].indexOf(this.state.channel.toLowerCase()) > -1)) {
483
+ if (nextProps.Templates.weCrmAccounts !== undefined && nextProps.Templates.weCrmAccounts.length === 1 && this.state.defaultAccount && (['wechat', WHATSAPP_LOWERCASE].indexOf(this.state.channel.toLowerCase()) > -1 )) {
467
484
  const paramsDefault = {
468
485
  name: this.state.searchText,
469
486
  sortBy: this.state.sortBy,
470
487
  };
471
- if (this.state.channel.toLowerCase() === 'wechat') {
488
+ if (['wechat', WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase())) {
472
489
  paramsDefault.wecrmId = nextProps.Templates.weCrmAccounts[0].configs.wecrm_app_id;
473
490
  paramsDefault.wecrmToken = nextProps.Templates.weCrmAccounts[0].configs.wecrm_token;
474
491
  paramsDefault.originalId = nextProps.Templates.weCrmAccounts[0].sourceAccountIdentifier;
@@ -512,8 +529,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
512
529
  } else {
513
530
  this.setState({selectedAccountError: false});
514
531
  }
515
- } else if (this.state.channel.toLowerCase() === 'line') {
516
- this.props.actions.setLineAccount(this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
532
+ } else if (this.state.channel.toLowerCase() === LINE.toLowerCase()) {
533
+ this.props.actions.setChannelAccount(LINE, this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
534
+ } else if (this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE) {
535
+ this.props.actions.setChannelAccount(WHATSAPP, this.props.Templates.weCrmAccounts.find((item) => item.name === this.state.selectedAccount));
517
536
  }
518
537
 
519
538
  this.setState({activeMode: TEMPLATES_MODE});
@@ -525,8 +544,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
525
544
  this.getAllTemplates({getNextPage: true});
526
545
  }
527
546
  onCreateComplete = (isReloadTemplates) => {
547
+ const isWhatsappEdit = this.state.routeParams?.pathname?.includes(`${WHATSAPP_LOWERCASE}/edit`);
528
548
  this.setState({routeParams: null, previewOpen: false}, () => {
529
- if (this.isFullMode() && isReloadTemplates) {
549
+ if (this.isFullMode() && (isReloadTemplates || isWhatsappEdit)) {
530
550
  this.getAllTemplates({params: {}}, true);
531
551
  }
532
552
  });
@@ -546,7 +566,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
546
566
  const { pathname } = routeParams;
547
567
  if (pathname.includes('create')) {
548
568
  creativesParams.mode = 'create';
549
- } else if (pathname.includes('edit')) {
569
+ } else if (pathname.includes('edit') || pathname.includes('overview')) {
550
570
  creativesParams.mode = 'edit';
551
571
  if (pathname.includes('richmedia')) {
552
572
  creativesParams._id = pathname.split('/')[4];
@@ -654,6 +674,32 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
654
674
  return templates;
655
675
  }
656
676
  }
677
+
678
+ isStatusMatching = (status, selectedWhatsappStatus) => {
679
+ if ( [WHATSAPP_STATUSES.unsubmitted, WHATSAPP_STATUSES.pending].includes(status) ) {
680
+ return selectedWhatsappStatus === WHATSAPP_STATUSES.awaitingApproval;
681
+ }
682
+ return selectedWhatsappStatus === status;
683
+ }
684
+
685
+ filterWhatsappTemplates = (templates) => {
686
+ const { selectedWhatsappStatus, selectedWhatsappCategory } = this.state;
687
+ return templates.filter((template) => {
688
+ const { [WHATSAPP_CATEGORY]: category, [WHATSAPP_STATUS]: status } = template?.versions?.base?.content?.[WHATSAPP_LOWERCASE] || {};
689
+
690
+ // if status or category filter is applied, filter templates which match these filters
691
+ return (
692
+ selectedWhatsappStatus
693
+ ? this.isStatusMatching(status, selectedWhatsappStatus)
694
+ : true
695
+ ) && (
696
+ selectedWhatsappCategory
697
+ ? category === selectedWhatsappCategory
698
+ : true
699
+ );
700
+ });
701
+ }
702
+
657
703
  filterSMSTemplates = (templates) => {
658
704
  const { smsFilter } = this.state;
659
705
  if (SMS_FILTERS.ALL === smsFilter) {
@@ -662,22 +708,44 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
662
708
  return templates.filter((template) => template.versions.base.type?.toLowerCase()?.includes(smsFilter));
663
709
  }
664
710
 
711
+ getCreativesEditText = (channel) => {
712
+ if (channel === WHATSAPP_LOWERCASE) {
713
+ return messages.whatsappOverview;
714
+ }
715
+ return messages.creativesTemplatesEdit;
716
+ }
717
+
665
718
  getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
666
719
  const currentChannel = channel.toUpperCase();
667
720
  const {channel: stateChannel} = this.state;
668
721
  const channelLowerCase = stateChannel.toLowerCase();
669
722
  let filteredTemplates = templates;
670
723
  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();
724
+ switch (currentChannel) {
725
+ case WECHAT:
726
+ filteredTemplates = this.filterWechatTemplates(templates);
727
+ break;
728
+ case WHATSAPP:
729
+ filteredTemplates = this.filterWhatsappTemplates(templates);
730
+ break;
731
+ case LINE:
732
+ filteredTemplates = this.filterLineTemplates(templates);
733
+ break;
734
+ case SMS:
735
+ filteredTemplates = this.filterSMSTemplates(templates);
736
+ isTraiDltFeature = this.checkDLTfeatureEnable();
737
+ break;
738
+ default:
739
+ break;
680
740
  }
741
+
742
+ const hoverButtonText =
743
+ this.props.intl.formatMessage(
744
+ this.props.isFullMode
745
+ ? this.getCreativesEditText(channelLowerCase)
746
+ : messages.selectButton
747
+ );
748
+
681
749
  const cardDataList = filteredTemplates && filteredTemplates.length ? map(filteredTemplates, (template) => {
682
750
  const templateData =
683
751
  {
@@ -691,13 +759,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
691
759
  onClick={() => { if (this.props.isFullMode) { handlers.handlePreviewClick(template); } else { this.props.handlePeviewTemplate(template); } }}
692
760
  />,
693
761
  ],
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>,
762
+ 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
763
  };
696
764
  if (this.props.isFullMode) {
697
765
  templateData.extra.push(<CapDropdown
698
766
  overlay={
699
767
  <CapMenu>
700
- {this.state.channel.toUpperCase() !== WECHAT && !isTraiDltFeature && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
768
+ { ![WECHAT, WHATSAPP].includes(this.state.channel.toUpperCase()) && !isTraiDltFeature && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
701
769
  <FormattedMessage {...messages.duplicateButton} />
702
770
  </CapMenu.Item>}
703
771
  <CapMenu.Item
@@ -834,6 +902,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
834
902
  }
835
903
  break;
836
904
  }
905
+ case WHATSAPP:
906
+ templateData.title = (
907
+ <div className="whatsapp-card-title">
908
+ <span title={template.name} >{template.name}</span>
909
+ {getWhatsappStatusContainer(template)}
910
+ </div>
911
+ );
912
+
913
+ templateData.content = getWhatsappContent(template);
914
+ break;
837
915
  default:
838
916
  templateData.content = "";
839
917
  }
@@ -842,8 +920,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
842
920
  const accountId = get(this.props, 'Templates.selectedWeChatAccount.uuid');
843
921
  const accounts = get(this.props, 'Templates.weCrmAccounts');
844
922
  return (<div>
845
- { [WECHAT, MOBILE_PUSH].includes(currentChannel) && this.showAccountName() }
923
+ {[WECHAT, MOBILE_PUSH].includes(currentChannel) && this.showAccountName()}
846
924
  {filterContent}
925
+ {[WHATSAPP].includes(currentChannel) && this.selectedFilters()}
847
926
  <CapSpin spinning={isLoading} tip={loadingTip} style={{width: '100%'}}>
848
927
  {<div>
849
928
  {!isEmpty(filteredTemplates) || !isEmpty(this.state.searchText) || !isEmpty(this.props.Templates.templateError) ? (
@@ -865,7 +944,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
865
944
  </div>
866
945
  }
867
946
 
868
- { !isEmpty(this.props.Templates.selectedWeChatAccount) && this.state.channel.toLowerCase() === "mobilepush" && this.state.selectedAccountError ?
947
+ { !isEmpty(this.props.Templates.selectedWeChatAccount) && ["mobilepush", WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase()) && this.state.selectedAccountError ?
869
948
  <div style={{ height: '65vh', textAlign: 'center', lineHeight: '65vh'}}>
870
949
  <CapHeading type="h6" >
871
950
  {this.props.intl.formatMessage(messages.accountConfigError)}
@@ -874,7 +953,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
874
953
  </InlineDiv>
875
954
  </CapHeading>
876
955
  </div> : (
877
- ["mobilepush"].includes(this.state.channel.toLowerCase()) &&
956
+ ["mobilepush", WHATSAPP_LOWERCASE].includes(this.state.channel.toLowerCase()) &&
878
957
  isEmpty(filteredTemplates) &&
879
958
  isEmpty(this.state.searchText) &&
880
959
  <div style={this.isFullMode() ? { height: "calc(100vh - 325px)", overflow: 'auto'} : {}}>
@@ -1456,6 +1535,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1456
1535
  pathName = `/wechat/edit/${id}`;
1457
1536
  }
1458
1537
  }
1538
+ if (this.state.channel.toUpperCase() === WHATSAPP) {
1539
+ pathName = `/whatsapp/edit/${id}/`;
1540
+ }
1459
1541
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
1460
1542
  let data = id;
1461
1543
  if (this.props.fbAdManager) {
@@ -1716,7 +1798,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1716
1798
  if (this.props.location.query.type === "embedded" && this.props.location.query.module === "library") {
1717
1799
  switch (property) {
1718
1800
  case "showAccountSelection":
1719
- return (this.props.route && (this.props.route.name.toLowerCase() === "wechat" || this.props.route.name.toLowerCase() === "mobilepush"));
1801
+ return (this.props.route && (['wechat', 'mobilepush', WHATSAPP_LOWERCASE].includes(this.props.route.name.toLowerCase())));
1720
1802
  case "setLocale":
1721
1803
  return true;
1722
1804
  case "callCreateFromProps":
@@ -1734,9 +1816,13 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1734
1816
  isCreateDisabled = () => {
1735
1817
  let isDisabled = this.isLoading();
1736
1818
  const channel = this.state.channel.toUpperCase();
1737
- if (channel === MOBILE_PUSH || channel === WECHAT) {
1819
+ if (channel === MOBILE_PUSH || channel === WECHAT || channel === WHATSAPP) {
1738
1820
  isDisabled = isDisabled || isEmpty(this.props.Templates.selectedWeChatAccount);
1739
1821
  }
1822
+ if (channel === WHATSAPP) {
1823
+ // limit on max number of templates allowed for whatsapp
1824
+ isDisabled = this.state.templatesCount >= MAX_WHATSAPP_TEMPLATES;
1825
+ }
1740
1826
  if (channel === MOBILE_PUSH) {
1741
1827
  isDisabled = this.checkSearchDisabled();
1742
1828
  }
@@ -1758,6 +1844,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1758
1844
  const facebookLoader = (getAllTemplatesInProgress && this.state.channel.toLowerCase() === FACEBOOK_CHANNEL);
1759
1845
 
1760
1846
  const wechatLoader = (((this.state.selectedAccount !== '' && getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === 'wechat');
1847
+ const whatsappLoader = (((this.state.selectedAccount !== '' && getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE);
1761
1848
  const mobilePushLoader = (((getAllTemplatesInProgress) || (fetchingWeCrmAccounts)) && this.state.channel.toLowerCase() === 'mobilepush');
1762
1849
  const emailLoader = (
1763
1850
  (getAllTemplatesInProgress ||
@@ -1766,6 +1853,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1766
1853
  EmailCreate.duplicateTemplateInProgress) && this.state.channel.toLowerCase() === 'email');
1767
1854
  const ebillLoader = ((getAllTemplatesInProgress || sendingFile) && this.state.channel.toLowerCase() === 'ebill');
1768
1855
  const isLoading = ((wechatLoader !== undefined) ? wechatLoader : false) ||
1856
+ (whatsappLoader !== undefined ? whatsappLoader : false) ||
1769
1857
  (smsLoader !== undefined ? smsLoader : false) ||
1770
1858
  (emailLoader !== undefined ? emailLoader : false) ||
1771
1859
  (ebillLoader !== undefined ? ebillLoader : false) ||
@@ -1801,6 +1889,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1801
1889
  illustrationImage: pushIllustration,
1802
1890
  title: <FormattedMessage {...messages.pushTitleIllustartion} values={{ template: templateText }} />,
1803
1891
  };
1892
+ case WHATSAPP:
1893
+ return {
1894
+ buttonLabel: <FormattedMessage {...messages.newWhatsappTemplate} values={{ template: templateText }} />,
1895
+ onClick: this.createTemplate,
1896
+ illustrationImage: whatsappIllustration,
1897
+ title: <FormattedMessage {...messages.whatsappTitleIllustration} values={{ template: templateText }} />,
1898
+ description: <FormattedMessage {...messages.whatsappDescIllustration} />,
1899
+ descriptionPosition: 'bottom',
1900
+ descriptionClassName: 'whatsapp-illustration-desc',
1901
+ };
1804
1902
  default:
1805
1903
  return {};
1806
1904
  }
@@ -1852,13 +1950,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1852
1950
  const { weCrmAccounts = [], fetchingWeCrmAccounts = false, campaignSettings = {} } = templates;
1853
1951
  const isWechatChannel = channel === WECHAT;
1854
1952
  const isMobilePushChannel = channel === MOBILE_PUSH;
1855
- const isLineChannel = channel === LINE;
1856
- const isFacebookChannel = channel === FACEBOOK;
1857
-
1953
+ const isFacebookChannel = channel === FACEBOOK;
1858
1954
 
1859
- if ((isWechatChannel || isMobilePushChannel || isLineChannel) && !isEmpty(weCrmAccounts) && !isFacebookChannel ) {
1955
+ if ([WECHAT, MOBILE_PUSH, LINE, WHATSAPP].includes(channel) && !isEmpty(weCrmAccounts) && !isFacebookChannel) {
1860
1956
  forEach(weCrmAccounts, (account) => {
1861
- if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || isMobilePushChannel || isLineChannel) {
1957
+ if ((isWechatChannel && account.configs && account.configs.is_wecrm_enabled) || [MOBILE_PUSH, LINE, WHATSAPP].includes(channel)) {
1862
1958
  if (query.type === 'embedded' && (!query.module || (query.module && query.module !== 'library'))) {
1863
1959
  if (query.source_account_id && account.sourceAccountIdentifier === query.source_account_id) {
1864
1960
  accountOptions.push(
@@ -1922,6 +2018,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1922
2018
  noAccountHeader = messages.noAccountsPresentFacebook;
1923
2019
  break;
1924
2020
  }
2021
+ case WHATSAPP: {
2022
+ accountHeader = formatMessage(messages.whatsappAccount);
2023
+ noAccountHeader = messages.noAccountsPresentWhatsapp;
2024
+ break;
2025
+ }
1925
2026
  default:
1926
2027
  break;
1927
2028
  }
@@ -1977,27 +2078,97 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1977
2078
  this.setState({activeMode: TEMPLATES_MODE});
1978
2079
  }
1979
2080
 
2081
+ accountNameMessage = (channel) => {
2082
+ const {
2083
+ intl: {
2084
+ formatMessage,
2085
+ },
2086
+ } = this.props;
2087
+ switch (channel?.toUpperCase()) {
2088
+ case MOBILE_PUSH:
2089
+ return formatMessage(messages.mobilepushAccount);
2090
+ case WECHAT:
2091
+ return formatMessage(messages.wechatAccount);
2092
+ case WHATSAPP:
2093
+ return formatMessage(messages.whatsappAccount);
2094
+ default:
2095
+ return '';
2096
+ }
2097
+ }
2098
+
1980
2099
  showAccountName = () => {
1981
2100
  const { activeMode, selectedAccount, channel } = this.state;
1982
2101
  const { Templates: templates = {}} = this.props;
1983
- const { weCrmAccounts = []} = templates;
1984
- const isMobilePushChannel = channel === MOBILE_PUSH;
2102
+ const { weCrmAccounts = [] } = templates;
1985
2103
  return (activeMode !== ACCOUNT_CHANGE_MODE &&
1986
2104
  <CapHeader
1987
- title={<CapHeading type="h4">{isMobilePushChannel ? this.props.intl.formatMessage(messages.mobilepushAccount) : this.props.intl.formatMessage(messages.wechatAccount)}</CapHeading>}
2105
+ title={<CapHeading type="h4">{this.accountNameMessage(channel)}</CapHeading>}
1988
2106
  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
2107
  size="regular"
1990
2108
  />);
1991
2109
  }
1992
2110
 
2111
+ removeWhatsappFilter = (filter) => {
2112
+ switch (filter) {
2113
+ case WHATSAPP_STATUS:
2114
+ this.setState({ selectedWhatsappStatus: null });
2115
+ break;
2116
+ case WHATSAPP_CATEGORY:
2117
+ this.setState({ selectedWhatsappCategory: null });
2118
+ break;
2119
+ default:
2120
+ break;
2121
+ }
2122
+ }
2123
+
2124
+ selectedFilters = () => {
2125
+ const { selectedWhatsappStatus, selectedWhatsappCategory } = this.state;
2126
+ return (
2127
+ <CapRow type="flex" align="middle" className="selected-whatsapp-filters">
2128
+ {
2129
+ selectedWhatsappStatus ? (
2130
+ <CapTag closable onClose={() => this.removeWhatsappFilter(WHATSAPP_STATUS)}>
2131
+ Status: {getWhatsappStatus(selectedWhatsappStatus)?.label || selectedWhatsappStatus}
2132
+ </CapTag>
2133
+ ) : null
2134
+ }
2135
+ {
2136
+ selectedWhatsappCategory ? (
2137
+ <CapTag closable onClose={() => this.removeWhatsappFilter(WHATSAPP_CATEGORY)}>
2138
+ Category: {getWhatsappCategory(selectedWhatsappCategory)?.label || selectedWhatsappCategory}
2139
+ </CapTag>
2140
+ ) : null
2141
+ }
2142
+ {
2143
+ (selectedWhatsappStatus || selectedWhatsappCategory) ? (
2144
+ <CapLink
2145
+ onClick={() => {
2146
+ this.removeWhatsappFilter(WHATSAPP_STATUS);
2147
+ this.removeWhatsappFilter(WHATSAPP_CATEGORY);
2148
+ }}
2149
+ title={this.props.intl.formatMessage(messages.clearAll)}
2150
+ />
2151
+ ) : null
2152
+ }
2153
+ </CapRow>
2154
+ );
2155
+ }
2156
+
1993
2157
  setWechatFilter = (e) => this.setState({wechatFilter: e.target.value});
2158
+ setWhatsappStatus = (value) => this.setState({selectedWhatsappStatus: value?.item?.props?.value});
2159
+ setWhatsappCategory = (value) => this.setState({selectedWhatsappCategory: value?.item?.props?.value});
1994
2160
  setLineFilter = (e) => this.setState({lineFilter: e.target.value});
1995
2161
  setSMSFilter = (e) => this.setState({smsFilter: e.target.value});
1996
2162
 
1997
2163
 
1998
2164
  render() {
1999
2165
  const isFullMode = this.isFullMode();
2000
- const { activeMode, channel, wechatFilter, lineFilter, smsFilter } = this.state;
2166
+ const { activeMode, channel, wechatFilter, lineFilter, smsFilter, templatesCount } = this.state;
2167
+ const {
2168
+ intl: {
2169
+ formatMessage,
2170
+ } = {},
2171
+ } = this.props || {};
2001
2172
  let showFbAds = false;
2002
2173
  if (channel.toLowerCase() === FACEBOOK_CHANNEL) {
2003
2174
  const {campaignSettings, fetchedOrgLevelCampaignSettings} = this?.props?.Templates || {};
@@ -2061,6 +2232,19 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2061
2232
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
2062
2233
  const channelLowerCase = (channel || '').toLowerCase();
2063
2234
  const isTraiDltFeature = this.checkDLTfeatureEnable();
2235
+
2236
+ const createButton = (<CapButton
2237
+ className={`create-new-${channelLowerCase}`}
2238
+ type={"primary"}
2239
+ disabled={this.isCreateDisabled()}
2240
+ style={{marginLeft: '8px', marginBottom: '12px'}}
2241
+ onClick={this.createTemplate} >
2242
+ { this.props.intl.formatMessage((isTraiDltFeature && channel.toUpperCase() === SMS ) ? messages.uploadTemplate : messages.createNewActionButton) }
2243
+ </CapButton>);
2244
+
2245
+ const isWhatsappCountExeeded = templatesCount >= MAX_WHATSAPP_TEMPLATES;
2246
+ const whatsappCountExceedText = <FormattedMessage {...messages.whatsappMaxTemplates} values={{ maxCount: MAX_WHATSAPP_TEMPLATES }}/>;
2247
+
2064
2248
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH].includes(this.state.channel.toUpperCase())) && <div className="action-container">
2065
2249
  {isfilterContentVisisble && <CapInput.Search
2066
2250
  className="search-text"
@@ -2085,6 +2269,31 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2085
2269
  </CapLabel></CapRadio.Button>
2086
2270
  </CapRadio.CapRadioGroup>
2087
2271
  }
2272
+ {
2273
+ channel.toUpperCase() === WHATSAPP && (
2274
+ <div className="whatsapp-filters">
2275
+ <CapSelectFilter
2276
+ placement="bottomLeft"
2277
+ data={STATUS_OPTIONS}
2278
+ onSelect={this.setWhatsappStatus}
2279
+ selectedValue={this.state.selectedWhatsappStatus}
2280
+ placeholder="Status"
2281
+ showBadge={true}
2282
+ width="90px"
2283
+ />
2284
+ <CapSelectFilter
2285
+ placement="bottomLeft"
2286
+ data={CATEGORY_OPTIONS}
2287
+ onSelect={this.setWhatsappCategory}
2288
+ selectedValue={this.state.selectedWhatsappCategory}
2289
+ placeholder="Category"
2290
+ showBadge={true}
2291
+ width="105px"
2292
+ />
2293
+ </div>
2294
+
2295
+ )
2296
+ }
2088
2297
  {
2089
2298
  channel.toUpperCase() === LINE &&
2090
2299
  isFullMode
@@ -2152,14 +2361,16 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2152
2361
  : () => null
2153
2362
  }
2154
2363
  <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>}
2364
+ {
2365
+ this.state.channel.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
2366
+ <CapTooltip title={whatsappCountExceedText}>
2367
+ <div className="button-disabled-tooltip-wrapper">
2368
+ {createButton}
2369
+ </div>
2370
+ </CapTooltip>
2371
+ )
2372
+ : isfilterContentVisisble && !isWechatEmbedded && createButton
2373
+ }
2163
2374
  </div>
2164
2375
 
2165
2376
  </div>);
@@ -2193,6 +2404,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2193
2404
  <div className={`creatives-templates-list ${this.props.isFullMode ? 'full-mode' : 'library-mode'}`}>
2194
2405
  <input type="file" id="filename" style={{ display: 'none'}} accept=".zip, .html, .htm" onChange={(event) => this.handleFileUpload(event, {files: event.target.files})} />
2195
2406
 
2407
+ {
2408
+ channel.toLowerCase() === WHATSAPP_LOWERCASE && isWhatsappCountExeeded ? (
2409
+ <CapAlert message={whatsappCountExceedText} type="info" />
2410
+ ) : null
2411
+ }
2196
2412
 
2197
2413
  <CapRow>
2198
2414
  <Pagination onPageChange={templates.length ? this.onPaginationChange : () => {}} paginationSelector="pagination-container">
@@ -94,6 +94,10 @@ export default defineMessages({
94
94
  id: `${scope}.editButton`,
95
95
  defaultMessage: "Edit",
96
96
  },
97
+ "whatsappOverview": {
98
+ id: `${scope}.whatsappOverview`,
99
+ defaultMessage: "Overview",
100
+ },
97
101
  "duplicateButton": {
98
102
  id: `${scope}.duplicateButton`,
99
103
  defaultMessage: "Duplicate",
@@ -130,6 +134,10 @@ export default defineMessages({
130
134
  id: `${scope}.noAccountsPresentLine`,
131
135
  defaultMessage: "Line accounts are not setup for your brand",
132
136
  },
137
+ "noAccountsPresentWhatsapp": {
138
+ id: `${scope}.noAccountsPresentWhatsapp`,
139
+ defaultMessage: "Whatsapp accounts are not setup for your brand",
140
+ },
133
141
  "noTemplatesMessage": {
134
142
  id: `${scope}.noTemplatesMessage`,
135
143
  defaultMessage: "No templates available",
@@ -306,6 +314,10 @@ export default defineMessages({
306
314
  id: `${scope}.newEmailTemplate`,
307
315
  defaultMessage: 'New email {template}',
308
316
  },
317
+ "newWhatsappTemplate": {
318
+ id: `${scope}.newWhatsappTemplate`,
319
+ defaultMessage: 'Add new Whatsapp {template}',
320
+ },
309
321
  "newNotificationTemplate": {
310
322
  id: `${scope}.newNotificationTemplate`,
311
323
  defaultMessage: 'New notification {template}',
@@ -318,6 +330,14 @@ export default defineMessages({
318
330
  id: `${scope}.emailTitleIllustartion`,
319
331
  defaultMessage: 'Create an email {template}',
320
332
  },
333
+ "whatsappTitleIllustration": {
334
+ id: `${scope}.whatsappTitleIllustration`,
335
+ defaultMessage: 'Add a new Whatsapp creative {template}',
336
+ },
337
+ "whatsappDescIllustration": {
338
+ id: `${scope}.whatsappDescIllustration`,
339
+ defaultMessage: 'These templates can be reused when creating a\nnew message content.',
340
+ },
321
341
  "smsTitleIllustartion": {
322
342
  id: `${scope}.smsTitleIllustartion`,
323
343
  defaultMessage: 'Create an SMS {template}',
@@ -346,6 +366,10 @@ export default defineMessages({
346
366
  id: `${scope}.lineAccount`,
347
367
  defaultMessage: 'Line account',
348
368
  },
369
+ "whatsappAccount": {
370
+ id: `${scope}.whatsappAccount`,
371
+ defaultMessage: 'Whatsapp account',
372
+ },
349
373
  "all": {
350
374
  id: `${scope}.all`,
351
375
  defaultMessage: 'All',
@@ -390,4 +414,12 @@ export default defineMessages({
390
414
  id: `${scope}.uploadTemplate`,
391
415
  defaultMessage: 'Upload template',
392
416
  },
417
+ "clearAll": {
418
+ id: `${scope}.clearAll`,
419
+ defaultMessage: 'Clear all',
420
+ },
421
+ "whatsappMaxTemplates": {
422
+ id: `${scope}.whatsappMaxTemplates`,
423
+ defaultMessage: 'WhatsApp allows {maxCount} approved templates. To accommodate new approved templates either delete the existing one or reach out to Capillary POC to increase the limit.',
424
+ },
393
425
  });
@@ -85,6 +85,8 @@ function templatesReducer(state = initialState, action) {
85
85
  return state.set('selectedLineAccount', fromJS(action.lineAccount));
86
86
  case types.SET_VIBER_ACCOUNT:
87
87
  return state.set('selectedViberAccount', fromJS(action.viberAccount));
88
+ case types.SET_WHATSAPP_ACCOUNT:
89
+ return state.set('selectedWhatsappAccount', fromJS(action.whatsappAccount));
88
90
  case types.SET_FACEBOOK_ACCOUNT:
89
91
  return state.set('selectedFacebookAccount', fromJS(action.faceBookAccount));
90
92
  case types.RESET_ACCOUNT:
@@ -23,6 +23,7 @@ import Templates from '../Templates';
23
23
  import CallTask from '../CallTask';
24
24
  import Facebook from '../Facebook';
25
25
  import Viber from '../Viber';
26
+ import Whatsapp from '../Whatsapp';
26
27
  import Line from '../Line/Container';
27
28
  import FTP from '../FTP';
28
29
  import Gallery from '../Assets/Gallery';
@@ -60,6 +61,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
60
61
  line: {content: <></>, tab: intl.formatMessage(messages.line), key: 'line'},
61
62
  viber: {content: <></>, tab: intl.formatMessage(messages.viber), key: 'viber'},
62
63
  facebook: {content: <div></div>, tab: intl.formatMessage(messages.facebook), key: 'facebook'},
64
+ whatsapp: { content: <></>, tab: intl.formatMessage(messages.whatsapp), key: 'whatsapp' },
63
65
  };
64
66
  let filteredPanes = Object.keys(defaultPanes)
65
67
  .filter((key) => !channelsToHide.includes(key)).reduce((obj = [], key) => {
@@ -54,6 +54,10 @@ export default defineMessages({
54
54
  id: 'creatives.containersV2.TemplatesV2.viber',
55
55
  defaultMessage: 'Viber',
56
56
  },
57
+ whatsapp: {
58
+ id: 'creatives.containersV2.TemplatesV2.whatsapp',
59
+ defaultMessage: 'WhatsApp',
60
+ },
57
61
  gallery: {
58
62
  id: 'creatives.containersV2.TemplatesV2.gallery',
59
63
  defaultMessage: 'Gallery',