@capillarytech/creatives-library 3.2.4 → 3.3.0

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 (68) hide show
  1. package/app.js +1 -1
  2. package/components/FormBuilder/index.js +1 -1
  3. package/components/Header/index.js +4 -3
  4. package/components/Header/messages.js +4 -0
  5. package/config/app.js +2 -0
  6. package/containers/Assets/Gallery/index.js +1 -1
  7. package/containers/Cap/index.js +4 -5
  8. package/containers/Email/index.js +2 -2
  9. package/containers/Email/messages.js +8 -0
  10. package/containers/LanguageProvider/index.js +28 -4
  11. package/containers/Templates/index.js +2 -2
  12. package/containers/Templates/messages.js +24 -4
  13. package/containers/WeChat/RichmediaTemplates/Create/messages.js +24 -0
  14. package/i18n.js +16 -9
  15. package/index.js +6 -0
  16. package/package.json +1 -4
  17. package/reducers.js +1 -1
  18. package/routes.js +2 -1
  19. package/services/api.js +11 -2
  20. package/translations/en.json +111 -63
  21. package/translations/zh.json +108 -60
  22. package/utils/smsCharCountV2.js +1 -1
  23. package/v2Components/CapTagList/index.js +2 -2
  24. package/v2Components/FormBuilder/index.js +7 -4
  25. package/v2Components/NavigationBar/index.js +9 -10
  26. package/v2Components/NewCallTask/index.js +3 -1
  27. package/v2Components/TopBar/index.js +3 -0
  28. package/v2Components/TopBar/messages.js +8 -0
  29. package/v2Containers/Assets/Gallery/index.js +1 -1
  30. package/v2Containers/Assets/Gallery/messages.js +4 -0
  31. package/v2Containers/CallTask/index.js +3 -1
  32. package/v2Containers/Cap/index.js +3 -2
  33. package/v2Containers/CreativesContainer/SlideBoxContent.js +14 -1
  34. package/v2Containers/CreativesContainer/index.js +6 -1
  35. package/v2Containers/Email/index.js +10 -7
  36. package/v2Containers/Email/messages.js +8 -0
  37. package/v2Containers/EmailWrapper/index.js +12 -9
  38. package/v2Containers/LanguageProvider/actions.js +9 -4
  39. package/v2Containers/LanguageProvider/constants.js +7 -1
  40. package/v2Containers/LanguageProvider/index.js +49 -12
  41. package/v2Containers/LanguageProvider/reducer.js +11 -9
  42. package/v2Containers/LanguageProvider/selectors.js +1 -1
  43. package/v2Containers/MobilePush/Create/index.js +2 -1
  44. package/v2Containers/MobilePush/Create/messages.js +4 -0
  45. package/v2Containers/MobilePush/Edit/index.js +2 -1
  46. package/v2Containers/MobilePush/Edit/messages.js +4 -0
  47. package/v2Containers/Sms/Create/index.js +3 -1
  48. package/v2Containers/Sms/Create/messages.js +16 -0
  49. package/v2Containers/Sms/Edit/index.js +3 -1
  50. package/v2Containers/Sms/Edit/messages.js +16 -0
  51. package/v2Containers/TagList/index.js +43 -0
  52. package/v2Containers/Templates/_templates.scss +9 -1
  53. package/v2Containers/Templates/index.js +65 -52
  54. package/v2Containers/TemplatesV2/index.js +54 -54
  55. package/v2Containers/WeChat/MapTemplates/index.js +0 -1
  56. package/v2Containers/WeChat/MapTemplates/messages.js +1 -1
  57. package/v2Containers/WeChat/RichmediaTemplates/Create/_createRichmedia.scss +10 -0
  58. package/v2Containers/WeChat/RichmediaTemplates/Create/actions.js +9 -0
  59. package/v2Containers/WeChat/RichmediaTemplates/Create/constants.js +3 -0
  60. package/v2Containers/WeChat/RichmediaTemplates/Create/index.js +146 -4
  61. package/v2Containers/WeChat/RichmediaTemplates/Create/messages.js +86 -57
  62. package/v2Containers/WeChat/RichmediaTemplates/Create/reducer.js +24 -1
  63. package/v2Containers/WeChat/RichmediaTemplates/Create/sagas.js +45 -0
  64. package/v2Containers/WeChat/RichmediaTemplates/Edit/constants.js +4 -0
  65. package/v2Containers/WeChat/RichmediaTemplates/Edit/index.js +8 -1
  66. package/v2Containers/WeChat/Wrapper/_wrapper.scss +9 -1
  67. package/v2Containers/WeChat/Wrapper/messages.js +5 -5
  68. package/v2Containers/TemplatesV2/_templatesV2.scss +0 -5
@@ -54,6 +54,7 @@ import { MOBILE_PUSH, WECHAT, SMS, EMAIL, EBILL, LINE } from '../CreativesContai
54
54
  import emailIllustration from '../Assets/images/emailIllustration.svg';
55
55
  import smsIllustration from '../Assets/images/smsIllustration.svg';
56
56
  import pushIllustration from '../Assets/images/pushIllustration.svg';
57
+ import { CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
57
58
 
58
59
 
59
60
  const {CapCustomCardList} = CapCustomCard;
@@ -67,6 +68,7 @@ const InlineDiv = styled.div`
67
68
  }
68
69
  `;
69
70
  const ACCOUNT_SELECTION_MODE = "accountSelectionMode";
71
+ const ACCOUNT_CHANGE_MODE = 'accountChangeMode';
70
72
  const TEMPLATES_MODE = "templatesMode";
71
73
  const WECHAT_FILTERS = {
72
74
  ALL: 'all',
@@ -1525,6 +1527,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1525
1527
 
1526
1528
  renderAccountSelection = () => {
1527
1529
  const accountOptions = [];
1530
+ const { selectedAccount } = this.state;
1528
1531
  let { channel = '' } = this.state;
1529
1532
  channel = channel.toUpperCase();
1530
1533
  const { Templates: templates = {}, location = {} } = this.props;
@@ -1570,7 +1573,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1570
1573
  }
1571
1574
  return (<CapSpin spinning={fetchingWeCrmAccounts}>
1572
1575
  {isEmpty(weCrmAccounts) && !fetchingWeCrmAccounts ? <FormattedMessage {...messages.noAccountsPresentWeChat} /> :
1573
- <div style={{overflowX: "auto"}}>
1576
+ <div style={{ overflowX: "auto", paddingBottom: CAP_SPACE_16 }}>
1574
1577
  <CapHeader
1575
1578
  title={isMobilePushChannel ? this.props.intl.formatMessage(messages.mobilepushAccount) : this.props.intl.formatMessage(messages.wechatAccount)}
1576
1579
  description={this.props.intl.formatMessage(messages.chooseAccount)}
@@ -1584,7 +1587,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1584
1587
  cardHeight="48px"
1585
1588
  cardWidth="276px"
1586
1589
  size="small"
1587
- />
1590
+ defaultValue={selectedAccount}
1591
+ selected={selectedAccount}
1592
+ />
1588
1593
  </div>
1589
1594
  }
1590
1595
  </CapSpin>);
@@ -1594,18 +1599,23 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1594
1599
  this.setState({activeMode: ACCOUNT_SELECTION_MODE});
1595
1600
  }
1596
1601
 
1602
+ setAccountChangeMode = () => {
1603
+ this.setState({activeMode: ACCOUNT_CHANGE_MODE});
1604
+ }
1605
+
1597
1606
  setTemplatesMode = () => {
1598
1607
  this.setState({activeMode: TEMPLATES_MODE});
1599
1608
  }
1600
1609
 
1601
1610
  showAccountName = () => {
1602
- const { selectedAccount, channel } = this.state;
1611
+ const { activeMode, selectedAccount, channel } = this.state;
1603
1612
  const { Templates: templates = {}} = this.props;
1604
1613
  const { weCrmAccounts = []} = templates;
1605
1614
  const isMobilePushChannel = channel === MOBILE_PUSH;
1606
- return (<CapHeader
1615
+ return (activeMode !== ACCOUNT_CHANGE_MODE &&
1616
+ <CapHeader
1607
1617
  title={<CapHeading type="h4">{isMobilePushChannel ? this.props.intl.formatMessage(messages.mobilepushAccount) : this.props.intl.formatMessage(messages.wechatAccount)}</CapHeading>}
1608
- description={<><CapHeading type="h3">{selectedAccount} {weCrmAccounts.length > 1 && <CapLink onClick={this.setAccountSelectionMode} style={{display: 'inline-block', position: 'relative', top: '3px'}} fontWeight="regular" fontSize="14px" title="change"/>} </CapHeading></>}
1618
+ 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></>}
1609
1619
  size="regular"
1610
1620
  />);
1611
1621
  }
@@ -1691,63 +1701,66 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1691
1701
  const {route} = this.props;
1692
1702
  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);
1693
1703
  return (
1694
- <div className={`creatives-templates-list ${this.props.isFullMode ? 'full-mode' : 'library-mode'}`}>
1695
- <input type="file" id="filename" style={{ display: 'none'}} accept=".zip, .html, .htm" onChange={(event) => this.handleFileUpload(event, {files: event.target.files})} />
1704
+ <>
1705
+ {activeMode === ACCOUNT_CHANGE_MODE && this.renderAccountSelection()}
1706
+ <div className={`creatives-templates-list ${this.props.isFullMode ? 'full-mode' : 'library-mode'}`}>
1707
+ <input type="file" id="filename" style={{ display: 'none'}} accept=".zip, .html, .htm" onChange={(event) => this.handleFileUpload(event, {files: event.target.files})} />
1696
1708
 
1697
1709
 
1698
- <CapRow>
1699
- <Pagination onPageChange={templates.length ? this.onPaginationChange : () => {}} paginationSelector="pagination-container">
1700
- {this.getTemplateDataForGrid({isLoading, loadingTip, channel: this.state.channel, templates: this.props.TemplatesList, filterContent, handlers: {handlePreviewClick: this.handlePreviewClick, handleEditClick: this.handleEditClick}})}
1701
- </Pagination>
1702
- {(((this.state.selectedAccount !== '' && !isEmpty(this.props.Templates.selectedWeChatAccount) && this.state.channel.toLowerCase() === 'wechat') || this.state.channel.toLowerCase() !== 'wechat') && isEmpty(this.props.TemplatesList)) && !this.props.Templates.getAllTemplatesInProgress && !isEmpty(this.state.searchText) &&
1710
+ <CapRow>
1711
+ <Pagination onPageChange={templates.length ? this.onPaginationChange : () => {}} paginationSelector="pagination-container">
1712
+ {this.getTemplateDataForGrid({isLoading, loadingTip, channel: this.state.channel, templates: this.props.TemplatesList, filterContent, handlers: {handlePreviewClick: this.handlePreviewClick, handleEditClick: this.handleEditClick}})}
1713
+ </Pagination>
1714
+ {(((this.state.selectedAccount !== '' && !isEmpty(this.props.Templates.selectedWeChatAccount) && this.state.channel.toLowerCase() === 'wechat') || this.state.channel.toLowerCase() !== 'wechat') && isEmpty(this.props.TemplatesList)) && !this.props.Templates.getAllTemplatesInProgress && !isEmpty(this.state.searchText) &&
1703
1715
  <div style={{ height: '65vh', textAlign: 'center', lineHeight: '65vh'}}>
1704
1716
  <CapHeading type="h6">{this.props.intl.formatMessage(messages.noTemplatesFound)}</CapHeading>
1705
1717
  </div>}
1706
- </CapRow>
1707
-
1708
-
1709
- <CapRow>
1710
- {!isEmpty(creativesParams.mode) && isFullMode && //creatives container for fullmode
1711
- <CreativesContainer
1712
- key={creativesParams.mode}
1713
- messageDetails={{type: "default"}}
1714
- creativesMode={creativesParams.mode}
1715
- messages={messages}
1716
- isFullMode={isFullMode}
1717
- templateData={creativesParams}
1718
- getCreativesData={() => { this.onCreateComplete(true); }}
1719
- channel={route.name.toUpperCase()}
1720
- handleCloseCreatives={this.onCreateComplete}
1721
- selectedWeChatAccount={get(this.props, 'Templates.selectedWeChatAccount', {})}
1718
+ </CapRow>
1719
+
1720
+
1721
+ <CapRow>
1722
+ {!isEmpty(creativesParams.mode) && isFullMode && //creatives container for fullmode
1723
+ <CreativesContainer
1724
+ key={creativesParams.mode}
1725
+ messageDetails={{type: "default"}}
1726
+ creativesMode={creativesParams.mode}
1727
+ messages={messages}
1728
+ isFullMode={isFullMode}
1729
+ templateData={creativesParams}
1730
+ getCreativesData={() => { this.onCreateComplete(true); }}
1731
+ channel={route.name.toUpperCase()}
1732
+ handleCloseCreatives={this.onCreateComplete}
1733
+ selectedWeChatAccount={get(this.props, 'Templates.selectedWeChatAccount', {})}
1722
1734
  />
1723
1735
  }
1724
- </CapRow>
1725
- <CapRow>
1726
-
1727
- {(this.state.channel.toLowerCase() === 'ebill' || this.state.channel.toLowerCase() === 'email') && htmlPreviewContent && <EmailPreview
1728
- header={previewHeader}
1729
- content={htmlPreviewContent}
1730
- show={this.state.showHtmlPreview}
1731
- device={this.state.device}
1732
- handleClose={this.toggleEmailPreview}
1733
- changePreviewDevice={this.changePreviewDevice}
1734
- versions={get(this.state, 'previewTemplate.versions', {})}
1735
- lastModified={this.state.previewTemplate ? this.state.previewTemplate.updatedAt : ''}
1736
- updatedByName={commonUtil.getUserNameById(parseInt(this.state.previewTemplate.updatedBy, 10), commonUtil.getMergedUserList(this.props.Templates.userList))}
1736
+ </CapRow>
1737
+ <CapRow>
1738
+
1739
+ {(this.state.channel.toLowerCase() === 'ebill' || this.state.channel.toLowerCase() === 'email') && htmlPreviewContent && <EmailPreview
1740
+ header={previewHeader}
1741
+ content={htmlPreviewContent}
1742
+ show={this.state.showHtmlPreview}
1743
+ device={this.state.device}
1744
+ handleClose={this.toggleEmailPreview}
1745
+ changePreviewDevice={this.changePreviewDevice}
1746
+ versions={get(this.state, 'previewTemplate.versions', {})}
1747
+ lastModified={this.state.previewTemplate ? this.state.previewTemplate.updatedAt : ''}
1748
+ updatedByName={commonUtil.getUserNameById(parseInt(this.state.previewTemplate.updatedBy, 10), commonUtil.getMergedUserList(this.props.Templates.userList))}
1737
1749
  />}
1738
- <CapSlideBox
1739
- header={(<h3>{this.props.intl.formatMessage(messages.layoutSelection)}</h3>)}
1740
- width={60}
1741
- content={cmsTemplateSelectionContent}
1742
- show={this.state.showEdmEmailTemplates}
1743
- handleClose={this.toggleEdmEmailTemplateSelection}
1744
- loadingText={"Loading EDM Templates"}
1745
- isLoading={this.props.Templates.getCmsTemplatesInProgress}
1750
+ <CapSlideBox
1751
+ header={(<h3>{this.props.intl.formatMessage(messages.layoutSelection)}</h3>)}
1752
+ width={60}
1753
+ content={cmsTemplateSelectionContent}
1754
+ show={this.state.showEdmEmailTemplates}
1755
+ handleClose={this.toggleEdmEmailTemplateSelection}
1756
+ loadingText={"Loading EDM Templates"}
1757
+ isLoading={this.props.Templates.getCmsTemplatesInProgress}
1746
1758
  />
1747
- {this.renderDeleteConfirmationModal()}
1748
- </CapRow>
1759
+ {this.renderDeleteConfirmationModal()}
1760
+ </CapRow>
1749
1761
 
1750
- </div>
1762
+ </div>
1763
+ </>
1751
1764
  );
1752
1765
  }
1753
1766
 
@@ -5,18 +5,16 @@
5
5
  */
6
6
 
7
7
  import PropTypes from 'prop-types';
8
-
9
8
  import React from 'react';
10
9
  import { connect } from 'react-redux';
11
10
  import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
12
11
  import { createStructuredSelector } from 'reselect';
13
12
  import { bindActionCreators } from 'redux';
14
- import styled from 'styled-components';
15
13
  import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon } from '@capillarytech/cap-ui-library';
16
14
  import { find, get } from 'lodash';
15
+ import isEmpty from 'lodash/isEmpty';
17
16
  import Helmet from 'react-helmet';
18
17
 
19
- import {CAP_SPACE_24, CAP_SPACE_16} from '@capillarytech/cap-ui-library/styled/variables';
20
18
  import { UserIsAuthenticated } from '../../utils/authWrapper';
21
19
  import { makeSelectTemplates, makeSelectTemplatesResponse } from '../Templates/selectors';
22
20
  import messages from './messages';
@@ -24,33 +22,17 @@ import * as actions from './actions';
24
22
  import Templates from '../Templates';
25
23
  import CallTask from '../CallTask';
26
24
  import Gallery from '../Assets/Gallery';
27
- import './_templatesV2.scss';
25
+ import withStyles from '../../hoc/withStyles';
26
+ import styles, { CapTabStyle } from './TemplatesV2.style';
28
27
 
29
28
  const {CapCustomCardList} = CapCustomCard;
30
29
 
31
- const ComponentWrapper = styled.div`
32
- ${(props) => props.isFullMode ? `
33
- max-width: 1140px;
34
- margin: 0 auto;
35
- width: 100%;
36
- padding: ${CAP_SPACE_24} 0;
37
- .ant-tabs-content{
38
- margin-top: ${CAP_SPACE_16}
39
- }
40
- ` : `.cap-tab-v2 {
41
- height: calc(100vh - 118px);
42
- } `}
43
-
44
-
45
- `;
46
- const MarginTop = styled.div`
47
- ${(props) => props.isFullMode ? `margin-top: ${CAP_SPACE_24}` : ``
48
- }
49
- `;
30
+ const StyledCapTab = withStyles(CapTab, CapTabStyle);
31
+
50
32
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
51
33
  constructor(props) {
52
34
  super(props);
53
- const defaultChannel = get(this, 'props.channel') || get(this, 'props.params.channel') || 'sms';
35
+ let defaultChannel = get(this, 'props.channel') || get(this, 'props.params.channel') || 'sms';
54
36
  const defaultPanes = {
55
37
  sms: {content: <div></div>, tab: props.intl.formatMessage(messages.sms), key: 'sms'},
56
38
  email: {content: <div></div>, tab: props.intl.formatMessage(messages.email), key: 'email'},
@@ -59,8 +41,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
59
41
  mPush: {content: <div></div>, tab: props.intl.formatMessage(messages.pushNotification), key: 'mobilepush'},
60
42
  };
61
43
  const channelsToHide = this.props.channelsToHide || [];
62
-
63
- const filteredPanes = Object.keys(defaultPanes)
44
+ const channelsToDisable = this.props.channelsToDisable || [];
45
+ let filteredPanes = Object.keys(defaultPanes)
64
46
  .filter((key) => !channelsToHide.includes(key)).reduce((obj = [], key) => {
65
47
  obj.push(defaultPanes[key]);
66
48
  return obj;
@@ -72,11 +54,31 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
72
54
  filteredPanes.push({content: <div></div>, tab: props.intl.formatMessage(messages.callTask), key: 'call_task'});
73
55
  }
74
56
 
57
+ filteredPanes = filteredPanes.map( (pane) => {
58
+ if (channelsToDisable.includes(pane.key)) {
59
+ pane.disabled = true;
60
+ }
61
+ return pane;
62
+ });
63
+
64
+ const channel = ['sms', 'email', 'mobilepush', 'call_task'];
65
+ if (!isEmpty(channelsToDisable)) {
66
+ channel.some((ch) => {
67
+ if (!channelsToDisable.includes(ch)) {
68
+ defaultChannel = ch;
69
+ return true;
70
+ }
71
+ return false;
72
+ });
73
+ }
74
+
75
75
  const panes = this.setChannelContent(defaultChannel, filteredPanes);
76
+
76
77
  this.state = {
77
78
  selectedChannel: defaultChannel,
78
79
  panes,
79
80
  };
81
+ !props.isFullMode && this.props.onChannelChange(defaultChannel);
80
82
  }
81
83
  componentWillReceiveProps(nextProps) {
82
84
  if (this.props.channel !== nextProps.channel) {
@@ -147,21 +149,20 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
147
149
  }
148
150
  }
149
151
 
150
- setChannelContent = (selectedChannel, argsPanes) => {
151
- const panes = argsPanes.map((p) => {
152
- const pane = p;
152
+ setChannelContent = (selectedChannel, argsPanes) => (
153
+ argsPanes.map((pane) => {
154
+ let content = null;
153
155
  if (pane.key === selectedChannel) {
154
- let channel = selectedChannel;
155
- if (channel === "mpush") {
156
- channel = "mobilepush";
157
- }
158
- pane.content = this.getTemplatesComponent(channel);
159
- return pane;
156
+ const channel = selectedChannel === "mpush" ? "mobilepush" : selectedChannel;
157
+ content = this.getTemplatesComponent(channel);
160
158
  }
161
- return {...pane, content: <div></div>};
162
- });
163
- return panes;
164
- }
159
+ return {
160
+ ...pane,
161
+ content,
162
+ };
163
+ })
164
+ );
165
+
165
166
  channelChange = (selectedChannel) => {
166
167
  const panes = this.setChannelContent(selectedChannel, this.state.panes);
167
168
  this.setState({panes, selectedChannel}, () => { if (!this.props.isFullMode) this.props.onChannelChange(selectedChannel); });
@@ -172,28 +173,25 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
172
173
  return this.props.Templates.loadingTemplates;
173
174
  }
174
175
  render() {
175
- const {isFullMode} = this.props;
176
+ const { isFullMode, className } = this.props;
176
177
  return (
177
- <div className={`creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`}>
178
+ <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`}>
178
179
  {isFullMode && <Helmet
179
180
  title={this.props.intl.formatMessage(messages.creatives)}
180
181
  meta={[
181
182
  { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
182
183
  ]}
183
184
  />}
184
- <ComponentWrapper isFullMode={isFullMode}>
185
-
185
+ <div className="component-wrapper">
186
186
  {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
187
- <MarginTop isFullMode={isFullMode}>
188
- <CapTab
189
- panes={this.state.panes}
190
- onChange={this.channelChange}
191
- activeKey={this.state.selectedChannel}
192
- defaultActiveKey={this.state.selectedChannel}/>
193
- </MarginTop>
194
-
195
-
196
- </ComponentWrapper>
187
+ <StyledCapTab
188
+ panes={this.state.panes}
189
+ onChange={this.channelChange}
190
+ activeKey={this.state.selectedChannel}
191
+ defaultActiveKey={this.state.selectedChannel}
192
+ isFullMode={isFullMode}
193
+ />
194
+ </div>
197
195
  </div>
198
196
 
199
197
  );
@@ -213,6 +211,8 @@ TemplatesV2.propTypes = {
213
211
  intl: intlShape,
214
212
  onChannelChange: PropTypes.func,
215
213
  channelsToHide: PropTypes.array,
214
+ className: PropTypes.string.isRequired,
215
+ channelsToDisable: PropTypes.array,
216
216
  };
217
217
 
218
218
  TemplatesV2.defaultProps = {
@@ -230,4 +230,4 @@ function mapDispatchToProps(dispatch) {
230
230
  };
231
231
  }
232
232
 
233
- export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(injectIntl(TemplatesV2)));
233
+ export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(injectIntl(withStyles(TemplatesV2, styles))));
@@ -426,7 +426,6 @@ export class MapTemplates extends React.Component { // eslint-disable-line react
426
426
 
427
427
  getFormData(formData) {
428
428
  let { editTemplateData} = this.state;
429
- const { formData: stateFormData } = this.state;
430
429
  const selectedWeChatAccount = (!isEmpty(this.props.Templates.selectedWeChatAccount) ? this.props.Templates.selectedWeChatAccount : this.props.WeChat.selectedWeChatAccount);
431
430
  if (isEmpty(editTemplateData)) {
432
431
  editTemplateData = formData;
@@ -60,7 +60,7 @@ export default defineMessages({
60
60
  },
61
61
  "wechatEditSuccess": {
62
62
  id: 'creatives.containersV2.WeChat.wechatEditSuccess',
63
- defaultMessage: "WeChat template unmapped successfully",
63
+ defaultMessage: "WeChat template edited successfully",
64
64
  },
65
65
  "wechatCreateSuccess": {
66
66
  id: 'creatives.containersV2.WeChat.wechatCreateSuccess',
@@ -59,6 +59,16 @@
59
59
  border-bottom-left-radius: 4px;
60
60
  border-bottom-right-radius: 4px;
61
61
  }
62
+
63
+ .existing-template-title-card {
64
+ padding: 12px;
65
+ background-color: white;
66
+ height: 44px;
67
+ width: 244px;
68
+ border-bottom-left-radius: 4px;
69
+ border-bottom-right-radius: 4px;
70
+ }
71
+
62
72
  .custom-image {
63
73
  position: relative;
64
74
  .name-container {
@@ -10,6 +10,15 @@ export function defaultAction() {
10
10
  type: types.DEFAULT_ACTION,
11
11
  };
12
12
  }
13
+
14
+ export function getAllTemplates(channel, queryParams) {
15
+ return {
16
+ type: types.GET_ALL_TEMPLATES_REQUEST,
17
+ channel,
18
+ queryParams,
19
+ };
20
+ }
21
+
13
22
  export function uploadAsset(file, assetType, fileParams, mode, wechatParams, contentId, uploadId) {
14
23
  return {
15
24
  type: types.UPLOAD_ASSET_REQUEST,
@@ -13,3 +13,6 @@ export const SAVE_TEMPLATE_REQUEST = 'app/v2Containers/Wechat/RichmediaTemplates
13
13
  export const SAVE_TEMPLATE_SUCCESS = 'app/v2Containers/Wechat/RichmediaTemplates/Create/SAVE_TEMPLATE_SUCCESS';
14
14
  export const SAVE_TEMPLATE_FAILURE = 'app/v2Containers/Wechat/RichmediaTemplates/Create/SAVE_TEMPLATE_FAILURE';
15
15
  export const CLEAR_CONTENT_ASSET = 'app/v2Containers/Wechat/RichmediaTemplates/Create/CLEAR_CONTENT_ASSET';
16
+ export const GET_ALL_TEMPLATES_REQUEST = 'app/v2Containers/Wechat/RichmediaTemplates/Create/GET_ALL_TEMPLATES_REQUEST';
17
+ export const GET_ALL_TEMPLATES_SUCCESS = 'app/v2Containers/Wechat/RichmediaTemplates/Create/GET_ALL_TEMPLATES_SUCCESS';
18
+ export const GET_ALL_TEMPLATES_FAILURE = 'app/v2Containers/Wechat/RichmediaTemplates/Create/GET_ALL_TEMPLATES_FAILURE';
@@ -17,7 +17,7 @@ import cloneDeep from 'lodash/cloneDeep';
17
17
  import forEach from 'lodash/forEach';
18
18
  import isEqual from 'lodash/isEqual';
19
19
  import get from 'lodash/get';
20
- import { CapRow, CapColumn, CapButton, CapCard, CapSpin, CapInput, CapHeading, CapDivider, CapIcon, CapLink, CapLabel, CapNotification } from '@capillarytech/cap-ui-library';
20
+ import { CapRow, CapColumn, CapButton, CapCard, CapSpin, CapInput, CapHeading, CapDivider, CapIcon, CapLink, CapLabel, CapNotification, CapSlideBox } from '@capillarytech/cap-ui-library';
21
21
  import makeSelectCreate, {makeSelectTemplates, getAssetDetails, getContentAssetDetails} from './selectors';
22
22
  import * as actions from './actions';
23
23
  import richmediaschema from './richmediaschema';
@@ -26,6 +26,8 @@ import messages from './messages';
26
26
  import FormBuilder from '../../../../v2Components/FormBuilder';
27
27
  import WechatRichmediaTemplatePreview from '../../../../v2Components/TemplatePreview/WechatRichmediaTemplatePreview';
28
28
  import noImage from '../../../../assets/noImage.png';
29
+ import Pagination from '../../../../v2Components/Pagination';
30
+ import { CAP_SPACE_08, CAP_SPACE_12, CAP_SPACE_20, CAP_SPACE_16 } from '@capillarytech/cap-ui-library/styled/variables';
29
31
 
30
32
  export class Create extends React.Component { // eslint-disable-line react/prefer-stateless-function
31
33
  constructor(props) {
@@ -102,6 +104,13 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
102
104
  type: 'confirm',
103
105
  id: 'template-back-confirm-modal',
104
106
  },
107
+ showExistingTemplates: false,
108
+ existingTemplatesPageNo: 1,
109
+ existingTemplatesPageLimit: 25,
110
+ sortBy: 'Most Recent',
111
+ searchText: '',
112
+ pagination: true,
113
+ scrollDebounce: true,
105
114
  };
106
115
  this.onFormDataChange = this.onFormDataChange.bind(this);
107
116
  this.cancelHandler = this.cancelHandler.bind(this);
@@ -836,6 +845,126 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
836
845
  });
837
846
  return true;
838
847
  }
848
+
849
+ onChooseExistingTemplate = (template, e) => {
850
+ e.stopPropagation();
851
+ const content = get(template, 'versions.base.mediaList[0]', {});
852
+ let postCommentsValue = 'No one';
853
+ const templateData = cloneDeep(this.state.templateData);
854
+
855
+ if (content.only_fans_can_comment && content.need_open_comment) {
856
+ postCommentsValue = 'Only followers';
857
+ } else if (content.need_open_comment) {
858
+ postCommentsValue = 'Anyone';
859
+ }
860
+
861
+ templateData[this.state.currentContentId] = {
862
+ formData: {
863
+ 'title-value': content.title,
864
+ 'author-value': content.author,
865
+ 'cover-abstract-value': content.digest,
866
+ 'content-value': content.content,
867
+ 'source-link-value': content.content_source_url,
868
+ 'post-comments-value': postCommentsValue,
869
+ 'show-cover-pic-value': content.show_cover_pic,
870
+ },
871
+ imageUrl: content.image_url,
872
+ mediaId: content.thumb_media_id,
873
+ };
874
+ this.setState({ templateData });
875
+ this.onCloseExistingTemplatesSlideBox();
876
+ }
877
+
878
+ getExistingTemplates = () => {
879
+ const { searchText, sortBy, existingTemplatesPageNo, existingTemplatesPageLimit } = this.state;
880
+ const { selectedWeChatAccount } = this.props.Templates;
881
+
882
+ this.setState({showExistingTemplates: true});
883
+ const queryParams = {
884
+ name: searchText || '',
885
+ sortBy: sortBy,
886
+ wecrmId: selectedWeChatAccount.configs.wecrm_app_id,
887
+ wecrmToken: selectedWeChatAccount.configs.wecrm_token,
888
+ originalId: selectedWeChatAccount.sourceAccountIdentifier,
889
+ page: existingTemplatesPageNo,
890
+ perPage: existingTemplatesPageLimit,
891
+ };
892
+ this.props.actions.getAllTemplates('wechat', queryParams);
893
+ }
894
+
895
+ searchTemplate = (data) => {
896
+ this.setState({searchText: data}, () => {
897
+ this.getExistingTemplates();
898
+ });
899
+ }
900
+
901
+ onPaginationChange = () => {
902
+ if (this.props.Create.pagination && this.state.scrollDebounce) {
903
+ this.setState((prevState) => ({
904
+ existingTemplatesPageNo: prevState.existingTemplatesPageNo + 1,
905
+ scrollDebounce: false,
906
+ }),
907
+ () => {
908
+ this.getExistingTemplates();
909
+ setTimeout(() => {
910
+ this.setState({ scrollDebounce: true })
911
+ }, 500);
912
+ });
913
+ }
914
+ }
915
+
916
+ existingTemplatesContent = () => {
917
+ const { getAllTemplatesInProgress, templates = [] } = this.props.Create;
918
+ return (
919
+ <CapSpin style={{textAlign: "center"}} spinning={getAllTemplatesInProgress}>
920
+ <CapInput.Search
921
+ className="search-text"
922
+ style={{ width: '100%', marginBottom: CAP_SPACE_16 }}
923
+ placeholder={this.props.intl.formatMessage(messages.searchText)}
924
+ value={this.state.searchText}
925
+ onChange={(e) => this.searchTemplate(e.target.value)}
926
+ onClear={() => this.searchTemplate('')}
927
+ />
928
+ <Pagination style={{ height: 'calc(100vh - 150px)', overflowY: 'auto' }} onPageChange={templates.length && this.onPaginationChange}>
929
+ { templates.map((template) => (
930
+ <CapCard
931
+ key={template._id}
932
+ bordered={false}
933
+ bodyStyle={{
934
+ padding: 0,
935
+ borderRadius: '4px',
936
+ width: '276px',
937
+ border: '16px solid #f4f5f7',
938
+ }}
939
+ onClick={(e) => this.onChooseExistingTemplate(template, e)}
940
+ style={{ paddingBottom: CAP_SPACE_12 }}
941
+ >
942
+ <div>
943
+ <div className="custom-image">
944
+ <img alt="" width="244px" height="136px" src={template.versions.base.mediaList[0].image_url} />
945
+ </div>
946
+ <div className="existing-template-title-card">
947
+ <CapHeading type="h5" className="overflow-wrap-fields">{template.name || "Title"}</CapHeading>
948
+ </div>
949
+ </div>
950
+ </CapCard>
951
+ ))
952
+ }
953
+ </Pagination>
954
+ </CapSpin>
955
+ );
956
+ }
957
+
958
+ onCloseExistingTemplatesSlideBox = () => {
959
+ this.setState({
960
+ showExistingTemplates: false,
961
+ existingTemplatesPageNo: 1,
962
+ existingTemplatesPageLimit: 25,
963
+ searchText: '',
964
+ pagination: true,
965
+ });
966
+ }
967
+
839
968
  updateContentData = () => {
840
969
  this.state.editorInstanse.insertHtml(`<img src='${this.props.ContentImageData.metaInfo.wechatUrl}' alt='Capillary Image'>`);
841
970
  this.props.actions.clearContentImageStore();
@@ -845,8 +974,9 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
845
974
  const addRichMediaMenu = (
846
975
  <>
847
976
  <div style={{width: '292px'}}><CapDivider /></div>
848
- <CapHeading type="h5" style={{marginBottom: '8px'}}>Add rich media content</CapHeading>
849
- <CapButton type="dashed" onClick={this.menuOnClickEvent} prefix={<CapIcon size="s" type="add-media" />} style={{backgroundColor: 'white'}}>New media</CapButton>
977
+ <CapHeading type="h5" style={{marginBottom: '8px'}}>{this.props.intl.formatMessage(messages.addRichMediaContent)}</CapHeading>
978
+ <CapButton type="dashed" onClick={this.getExistingTemplates} prefix={<CapIcon size="s" type="media" />} style={{backgroundColor: 'white', width: '142px', marginRight: CAP_SPACE_08, paddingLeft: CAP_SPACE_12}}>{this.props.intl.formatMessage(messages.existingMedia)}</CapButton>
979
+ <CapButton type="dashed" onClick={this.menuOnClickEvent} prefix={<CapIcon size="s" type="add-media" />} style={{backgroundColor: 'white'}}>{this.props.intl.formatMessage(messages.newMedia)}</CapButton>
850
980
  </>
851
981
  );
852
982
  return addRichMediaMenu;
@@ -902,6 +1032,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
902
1032
  const schema = this.returnCurrentSchema();
903
1033
  const formData = this.returnCurrentFormData();
904
1034
  const { isIframe } = this.state;
1035
+ const { intl } = this.props;
905
1036
  this.updateImageData();
906
1037
  if (this.props.ContentImageData && this.props.ContentImageData.metaInfo && this.props.ContentImageData.metaInfo.wechatUrl) {
907
1038
  this.updateContentData();
@@ -927,7 +1058,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
927
1058
  </CapColumn>
928
1059
  <CapColumn span={15}>
929
1060
  { !isIframe && <CapInput
930
- label="Name of the template"
1061
+ label={this.props.intl.formatMessage(messages.nameOfTemplate)}
931
1062
  value={this.state.templateName}
932
1063
  onChange={this.templateNameHandler}
933
1064
  size="large"
@@ -956,6 +1087,17 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
956
1087
  </CapRow>
957
1088
  </div>
958
1089
  </div>
1090
+ <CapSlideBox
1091
+ header={(
1092
+ <CapHeading type="h3">
1093
+ {intl.formatMessage(messages.existingMediaHeader)}
1094
+ </CapHeading>
1095
+ )}
1096
+ content={this.existingTemplatesContent()}
1097
+ show={this.state.showExistingTemplates}
1098
+ handleClose={this.onCloseExistingTemplatesSlideBox}
1099
+ size="size-s"
1100
+ />
959
1101
  </CapSpin>
960
1102
  </div>
961
1103
  );