@capillarytech/creatives-library 7.9.13 → 7.9.15

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 (45) hide show
  1. package/assets/group.png +0 -0
  2. package/components/Card/index.js +1 -1
  3. package/components/Card/tests/__snapshots__/index.test.js.snap +22 -0
  4. package/components/Card/tests/index.test.js +19 -7
  5. package/containers/App/constants.js +2 -1
  6. package/helpers/intl-enzym-test-helpers.js +40 -0
  7. package/index.js +6 -0
  8. package/package.json +3 -2
  9. package/routes.js +5 -0
  10. package/services/api.js +9 -15
  11. package/utils/common.js +17 -4
  12. package/v2Components/CapTagList/index.js +38 -9
  13. package/v2Components/CapTagList/messages.js +4 -0
  14. package/v2Containers/Cap/constants.js +2 -0
  15. package/v2Containers/Cap/index.js +9 -2
  16. package/v2Containers/Cap/reducer.js +5 -1
  17. package/v2Containers/Cap/selectors.js +10 -1
  18. package/v2Containers/CreativesContainer/SlideBoxContent.js +10 -4
  19. package/v2Containers/CreativesContainer/SlideBoxHeader.js +6 -1
  20. package/v2Containers/CreativesContainer/messages.js +4 -0
  21. package/v2Containers/FTP/index.js +14 -4
  22. package/v2Containers/Sms/Create/index.js +5 -6
  23. package/v2Containers/Sms/Edit/sagas.js +1 -1
  24. package/v2Containers/SmsTrai/Create/actions.js +25 -0
  25. package/v2Containers/SmsTrai/Create/constants.js +74 -0
  26. package/v2Containers/SmsTrai/Create/index.js +681 -0
  27. package/v2Containers/SmsTrai/Create/index.scss +92 -0
  28. package/v2Containers/SmsTrai/Create/messages.js +116 -0
  29. package/v2Containers/SmsTrai/Create/reducer.js +43 -0
  30. package/v2Containers/SmsTrai/Create/sagas.js +36 -0
  31. package/v2Containers/SmsTrai/Create/selectors.js +26 -0
  32. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +10351 -0
  33. package/v2Containers/SmsTrai/Create/tests/index.test.js +77 -0
  34. package/v2Containers/SmsTrai/Create/tests/mockData.js +58 -0
  35. package/v2Containers/SmsTrai/Edit/constants.js +16 -0
  36. package/v2Containers/SmsTrai/Edit/index.js +483 -0
  37. package/v2Containers/SmsTrai/Edit/messages.js +58 -0
  38. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +16103 -0
  39. package/v2Containers/SmsTrai/Edit/tests/index.test.js +61 -0
  40. package/v2Containers/SmsTrai/Edit/tests/mockData.js +36 -0
  41. package/v2Containers/SmsWrapper/index.js +75 -0
  42. package/v2Containers/SmsWrapper/tests/index.test.js +10 -0
  43. package/v2Containers/Templates/_templates.scss +2 -0
  44. package/v2Containers/Templates/index.js +89 -27
  45. package/v2Containers/Templates/messages.js +16 -0
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { browserHistory } from 'react-router';
3
+ import configureStore from '../../../../store';
4
+ import { Provider } from 'react-redux';
5
+ import { mountWithIntl } from '../../../../../app/helpers/intl-enzym-test-helpers';
6
+ import { SmsTraiEdit } from '../index';
7
+ import { mockData } from './mockData';
8
+
9
+ let store;
10
+ beforeAll(() => {
11
+ store = configureStore({}, browserHistory);
12
+ });
13
+
14
+ describe('Creatives SmsTraiEdit test/>', () => {
15
+ let renderedComponent;
16
+ const getTemplateDetails = jest.fn();
17
+ const resetEditTemplate = jest.fn();
18
+ const clearEditResponse = jest.fn();
19
+ const editTemplate = jest.fn();
20
+ const fetchSchemaForEntity = jest.fn();
21
+ const formatMessage = jest.fn();
22
+ const handleClose = jest.fn();
23
+ const params = {
24
+ id: mockData.templateDetails._id,
25
+ };
26
+ const location = mockData.location;
27
+ const templateDetails = mockData.templateDetails;
28
+ beforeEach(() => {
29
+ renderedComponent = mountWithIntl(
30
+ <Provider store={store}>
31
+ <SmsTraiEdit
32
+ actions={{
33
+ getTemplateDetails,
34
+ resetEditTemplate,
35
+ clearEditResponse,
36
+ editTemplate,
37
+ }}
38
+ globalActions={{ fetchSchemaForEntity }}
39
+ handleClose={handleClose}
40
+ params={params}
41
+ templateDetails={templateDetails}
42
+ location={location}
43
+ intl={{ formatMessage }}
44
+ />
45
+ </Provider>,
46
+ );
47
+ });
48
+ it('should render', () => {
49
+ expect(renderedComponent).toMatchSnapshot();
50
+ });
51
+ it('should save', () => {
52
+ renderedComponent.find('button.create-msg').props().onClick();
53
+ renderedComponent.update();
54
+ expect(renderedComponent).toMatchSnapshot();
55
+ });
56
+ it('should cancel', () => {
57
+ renderedComponent.find('button.cancel-msg').props().onClick();
58
+ renderedComponent.update();
59
+ expect(renderedComponent).toMatchSnapshot();
60
+ });
61
+ });
@@ -0,0 +1,36 @@
1
+ export const mockData = {
2
+ templateDetails: {
3
+ _id: '6125eec5feae248f88e6a634',
4
+ name: 'CAP71871_4',
5
+ type: 'SMS',
6
+ versions: {
7
+ history: [],
8
+ base: {
9
+ template_id: "'1107160207324585172'",
10
+ template_name: 'CAP71871_4',
11
+ type: 'Service-Explicit',
12
+ header: 'VISHMM',
13
+ category: '--',
14
+ 'sms-editor':
15
+ 'LATEST FASHION@VISHAL\n{#var#}\n\nBUY1GET1-\n{#var#}\n{#var#}\n{#var#}\n\nFREE GIFTS-\n{#var#}\n{#var#}',
16
+ 'consent-type': 'Explicit',
17
+ 'unicode-validity': false,
18
+ tabKey: 1,
19
+ },
20
+ },
21
+ isActive: true,
22
+ orgId: 50146,
23
+ createdBy: '15000449',
24
+ updatedBy: '15000449',
25
+ createdAt: '2021-08-25T07:25:12.559Z',
26
+ updatedAt: '2021-08-30T05:54:59.951Z',
27
+ },
28
+ location: {
29
+ pathname: '/sms/edit',
30
+ query: {
31
+ type: false,
32
+ module: 'default',
33
+ },
34
+ search: '',
35
+ },
36
+ };
@@ -0,0 +1,75 @@
1
+ /*
2
+ *
3
+ * SmsWrapper
4
+ *
5
+ */
6
+ import PropTypes from 'prop-types';
7
+ import React from 'react';
8
+ import SmsCreate from '../Sms/Create';
9
+ import { hasTraiDltFeature } from '../../utils/common';
10
+ import SmsEdit from '../Sms/Edit';
11
+ import SmsTraiCreate from '../SmsTrai/Create';
12
+ import SmsTraiEdit from '../SmsTrai/Edit';
13
+ const SmsWrapper = (props) => {
14
+ const {
15
+ isCreateSms,
16
+ isEditSms,
17
+ setIsLoadingContent,
18
+ location,
19
+ isGetFormData,
20
+ getFormSubscriptionData,
21
+ isFullMode,
22
+ templateData,
23
+ getDefaultTags,
24
+ forwardedTags,
25
+ selectedOfferDetails,
26
+ onPreviewContentClicked,
27
+ onTestContentClicked,
28
+ onCreateComplete,
29
+ handleClose
30
+ } = props;
31
+
32
+ const smsProps = {
33
+ onCreateComplete,
34
+ setIsLoadingContent,
35
+ location,
36
+ route: { name: 'sms' },
37
+ isGetFormData,
38
+ getFormSubscriptionData,
39
+ getDefaultTags,
40
+ isFullMode,
41
+ forwardedTags,
42
+ selectedOfferDetails,
43
+ onPreviewContentClicked,
44
+ onTestContentClicked,
45
+ };
46
+ const isTraiDltFeature = hasTraiDltFeature();
47
+ return <>
48
+ {
49
+ isCreateSms && (isTraiDltFeature ?
50
+ <SmsTraiCreate
51
+ isComponent
52
+ {...smsProps}/> :
53
+ <SmsCreate
54
+ isComponent {
55
+ ...smsProps
56
+ }
57
+ />
58
+ )
59
+ }
60
+ {isEditSms && (isTraiDltFeature ?
61
+ <SmsTraiEdit
62
+ {...smsProps}
63
+ params={{id: templateData._id}}
64
+ templateData={templateData}
65
+ handleClose={handleClose}
66
+ />:<SmsEdit
67
+ {...smsProps}
68
+ params={{id: templateData._id}}
69
+ templateData={templateData}
70
+ />
71
+ )}
72
+ </>;
73
+ };
74
+
75
+ export default SmsWrapper;
@@ -0,0 +1,10 @@
1
+ // import React from 'react';
2
+ // import { shallow } from 'enzyme';
3
+
4
+ // import { SmsWrapper } from '../index';
5
+
6
+ describe('<SmsWrapper />', () => {
7
+ it('Expect to have unit tests specified', () => {
8
+ expect(true).toEqual(false);
9
+ });
10
+ });
@@ -481,6 +481,7 @@
481
481
  .line-filters {
482
482
  width: 100%;
483
483
  padding-left: 12px;
484
+ padding-bottom: 16px;
484
485
  .ant-radio-button-wrapper {
485
486
  line-height: 32px;
486
487
  border-radius: 16px;
@@ -494,6 +495,7 @@
494
495
  }
495
496
  .ant-radio-button-wrapper-checked {
496
497
  background-color: #ecece7;
498
+ border: 0px white;
497
499
  box-shadow: none;
498
500
  }
499
501
  }
@@ -98,6 +98,13 @@ const LINE_FILTERS = {
98
98
  IMAGE_MAP_VIDEO: 'video',
99
99
  };
100
100
 
101
+ const SMS_FILTERS = {
102
+ ALL: 'all',
103
+ PROMOTIONAL: 'promotional',
104
+ SERVICE_EXPLICIT: 'service-explicit',
105
+ SERVICE_IMPLICIT: 'service-implicit',
106
+ };
107
+
101
108
  export class Templates extends React.Component { // eslint-disable-line react/prefer-stateless-function
102
109
  constructor(props) {
103
110
  super(props);
@@ -125,6 +132,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
125
132
  activeMode: TEMPLATES_MODE,
126
133
  wechatFilter: WECHAT_FILTERS.ALL,
127
134
  lineFilter: LINE_FILTERS.ALL,
135
+ smsFilter: SMS_FILTERS.ALL,
128
136
  };
129
137
 
130
138
  this.handleOnHoverItem = this.handleOnHoverItem.bind(this);
@@ -147,7 +155,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
147
155
  this.prepareMobilePushPreviewData = this.prepareMobilePushPreviewData.bind(this);
148
156
  this.menuOnClickEvent = this.menuOnClickEvent.bind(this);
149
157
  }
150
-
158
+
151
159
  componentWillMount() {
152
160
  if (this.state.channel === '') {
153
161
  let channel = '';
@@ -207,29 +215,18 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
207
215
  if (this.props.location.query.type === 'embedded') {
208
216
  this.props.actions.resetAccount();
209
217
  }
210
- if ((['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) || ((['wechat', 'mobilepush'].includes(this.state.channel.toLowerCase()) && !isEmpty(this.props.Templates.selectedWeChatAccount)))) {
218
+ if (['line', VIBER_CHANNEL, FACEBOOK_CHANNEL, 'sms', 'email', 'ebill'].includes((this.state.channel || '').toLowerCase())) {
211
219
  const queryParams = {
212
220
  // name: this.state.searchText,
213
221
  // sortBy: this.state.sortBy,
214
222
  };
215
- if (this.state.channel.toLowerCase() === 'wechat' && get(this.props, 'Templates.selectedWeChatAccount.configs', false)) {
216
- queryParams.wecrmId = this.props.Templates.selectedWeChatAccount.configs.wecrm_app_id;
217
- queryParams.wecrmToken = this.props.Templates.selectedWeChatAccount.configs.wecrm_token;
218
- queryParams.originalId = this.props.Templates.selectedWeChatAccount.sourceAccountIdentifier;
219
- }
220
- if (this.state.channel.toLowerCase() === "mobilepush" && !isEmpty(this.props.Templates.selectedWeChatAccount)) {
221
- const account = this.props.Templates.selectedWeChatAccount;
222
- queryParams.accountId = account.id;
223
- const isAndroidSupported = get(account, "configs.android") === '1';
224
- const isIosSupported = get(account, "configs.ios") === '1';
225
- if (!(isAndroidSupported || isIosSupported)) {
226
- this.props.actions.resetAccount();
227
- return;
228
- }
229
- }
230
223
  queryParams.page = this.state.page;
231
224
  queryParams.perPage = this.state.perPageLimit;
232
225
  const channel = this.state.channel;
226
+ const isTraiDltFeature = commonUtil.hasTraiDltFeature();
227
+ if ((this.state.channel || '').toLowerCase() === "sms" && isTraiDltFeature) {
228
+ queryParams.traiEnable = true;
229
+ }
233
230
  this.props.actions.getAllTemplates(channel, queryParams);
234
231
  }
235
232
 
@@ -245,7 +242,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
245
242
  const { templatesCount } = this.state || {};
246
243
 
247
244
  if (nextPropsTotalTemplatesCount && nextPropsTotalTemplatesCount !== propsTotalTemplatesCount && templatesCount === 0) {
248
- this.setState({ templatesCount: nextPropsTotalTemplatesCount });
245
+ this.setState({ templatesCount: nextPropsTotalTemplatesCount });
249
246
  }
250
247
  if (!isEqual(nextProps.route.name, this.props.route.name)) {
251
248
  let channel = '';
@@ -518,7 +515,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
518
515
  this.getAllTemplates({params, resetPage: true});
519
516
  });
520
517
  }
521
- onPaginationChange = () => { this.getAllTemplates({getNextPage: true}); }
518
+ onPaginationChange = () => {
519
+ this.getAllTemplates({getNextPage: true});
520
+ }
522
521
  onCreateComplete = (isReloadTemplates) => {
523
522
  this.setState({routeParams: null, previewOpen: false}, () => {
524
523
  if (this.isFullMode() && isReloadTemplates) {
@@ -587,6 +586,10 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
587
586
  if (activeMode === ACCOUNT_SELECTION_MODE) {
588
587
  this.setTemplatesMode();
589
588
  }
589
+ const isTraiDltFeature = commonUtil.hasTraiDltFeature();
590
+ if ((this.state.channel || '').toLowerCase() === "sms" && isTraiDltFeature) {
591
+ queryParams.traiEnable = true;
592
+ }
590
593
  if (resetTemplates) {
591
594
  queryParams.name = this.state.searchText;
592
595
  queryParams.sortBy = this.state.sortBy;
@@ -625,6 +628,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
625
628
  }
626
629
  }
627
630
  }
631
+
628
632
  this.setState({page, templatesCount}, () => {
629
633
  queryParams.page = page;
630
634
  queryParams.perPage = this.state.perPageLimit;
@@ -644,18 +648,30 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
644
648
  return templates;
645
649
  }
646
650
  }
651
+ filterSMSTemplates = (templates) => {
652
+ const { smsFilter } = this.state;
653
+ if (SMS_FILTERS.ALL === smsFilter) {
654
+ return templates;
655
+ }
656
+ return templates.filter((template) => template.versions.base.type.toLowerCase() === smsFilter);
657
+ }
647
658
 
648
659
  getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
649
660
  const currentChannel = channel.toUpperCase();
650
661
  const {channel: stateChannel} = this.state;
651
662
  const channelLowerCase = stateChannel.toLowerCase();
652
663
  let filteredTemplates = templates;
664
+ let isTraiDltFeature = false;
653
665
  if (currentChannel === WECHAT) {
654
666
  filteredTemplates = this.filterWechatTemplates(templates);
655
667
  }
656
668
  if (currentChannel === LINE) {
657
669
  filteredTemplates = this.filterLineTemplates(templates);
658
670
  }
671
+ if (currentChannel === SMS) {
672
+ filteredTemplates = this.filterSMSTemplates(templates);
673
+ isTraiDltFeature = commonUtil.hasTraiDltFeature();
674
+ }
659
675
  const cardDataList = filteredTemplates && filteredTemplates.length ? map(filteredTemplates, (template) => {
660
676
  const templateData =
661
677
  {
@@ -675,7 +691,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
675
691
  templateData.extra.push(<CapDropdown
676
692
  overlay={
677
693
  <CapMenu>
678
- {this.state.channel.toUpperCase() !== WECHAT && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
694
+ {this.state.channel.toUpperCase() !== WECHAT && !isTraiDltFeature && <CapMenu.Item className={`duplicate-${channelLowerCase}`} onClick={() => this.duplicateTemplate(template)}>
679
695
  <FormattedMessage {...messages.duplicateButton} />
680
696
  </CapMenu.Item>}
681
697
  <CapMenu.Item
@@ -692,7 +708,20 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
692
708
  }
693
709
  switch (currentChannel) {
694
710
  case SMS:
695
- templateData.content = template.versions.base['sms-editor'];
711
+ templateData.content = isTraiDltFeature ? (
712
+ <>
713
+ <CapLabel type="label3" style={{ marginBottom: CAP_SPACE_16 }}>
714
+ {template.versions.base.type}
715
+ </CapLabel>
716
+ <CapLabel type="label1">
717
+ {template.versions.base['updated-sms-editor'] === ''
718
+ ? template.versions.base['sms-editor']
719
+ : template.versions.base['updated-sms-editor']?.join('')}
720
+ </CapLabel>
721
+ </>
722
+ ) : (
723
+ template.versions.base['sms-editor']
724
+ );
696
725
  break;
697
726
  case EMAIL: {
698
727
  const url = template.versions.base.preview_http_url;
@@ -1422,9 +1451,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1422
1451
  }
1423
1452
  }
1424
1453
  if (this.isEnabledInLibraryModule("callSelectFromProps")) {
1425
- let data =id;
1426
- if(this.props.fbAdManager){
1427
- data = this.selectTemplate(id)
1454
+ let data = id;
1455
+ if (this.props.fbAdManager) {
1456
+ data = this.selectTemplate(id);
1428
1457
  }
1429
1458
  this.props.onSelectTemplate(data, this.props.fbAdManager);
1430
1459
  } else {
@@ -1957,10 +1986,12 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1957
1986
 
1958
1987
  setWechatFilter = (e) => this.setState({wechatFilter: e.target.value});
1959
1988
  setLineFilter = (e) => this.setState({lineFilter: e.target.value});
1989
+ setSMSFilter = (e) => this.setState({smsFilter: e.target.value});
1990
+
1960
1991
 
1961
1992
  render() {
1962
1993
  const isFullMode = this.isFullMode();
1963
- const { activeMode, channel, wechatFilter, lineFilter } = this.state;
1994
+ const { activeMode, channel, wechatFilter, lineFilter, smsFilter } = this.state;
1964
1995
  let showFbAds = false;
1965
1996
  if (channel.toLowerCase() === FACEBOOK_CHANNEL) {
1966
1997
  const {campaignSettings, fetchedOrgLevelCampaignSettings} = this?.props?.Templates || {};
@@ -2023,6 +2054,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2023
2054
  const isfilterContentVisisble = true;
2024
2055
  const isWechatEmbedded = !this.props.isFullMode && channel.toUpperCase() === WECHAT;
2025
2056
  const channelLowerCase = (channel || '').toLowerCase();
2057
+ const isTraiDltFeature = commonUtil.hasTraiDltFeature();
2026
2058
  const filterContent = (( isfilterContentVisisble || [WECHAT, MOBILE_PUSH].includes(this.state.channel.toUpperCase())) && <div className="action-container">
2027
2059
  {isfilterContentVisisble && <CapInput.Search
2028
2060
  className="search-text"
@@ -2083,14 +2115,44 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
2083
2115
  )
2084
2116
  : () => null
2085
2117
  }
2118
+ {
2119
+ channel.toUpperCase() === SMS &&
2120
+ isTraiDltFeature
2121
+ ? (
2122
+ <CapRadio.CapRadioGroup className="line-filters" defaultValue={smsFilter} onChange={this.setSMSFilter}>
2123
+ {
2124
+ (() => {
2125
+ const { ALL, SERVICE_IMPLICIT, SERVICE_EXPLICIT, PROMOTIONAL } = SMS_FILTERS;
2126
+ return (
2127
+ <>
2128
+ <CapRadio.Button value={ALL}><CapLabel type="label2">
2129
+ <FormattedMessage {...messages.all} />
2130
+ </CapLabel></CapRadio.Button>
2131
+ <CapRadio.Button value={PROMOTIONAL}><CapLabel type="label2">
2132
+ <FormattedMessage {...messages.promotional} />
2133
+ </CapLabel></CapRadio.Button>
2134
+ <CapRadio.Button value={SERVICE_EXPLICIT}><CapLabel type="label2">
2135
+ <FormattedMessage {...messages.serviceExplicit} />
2136
+ </CapLabel></CapRadio.Button>
2137
+ <CapRadio.Button value={SERVICE_IMPLICIT}><CapLabel type="label2">
2138
+ <FormattedMessage {...messages.serviceImplicit} />
2139
+ </CapLabel></CapRadio.Button>
2140
+ </>
2141
+ );
2142
+ })()
2143
+ }
2144
+ </CapRadio.CapRadioGroup>
2145
+ )
2146
+ : () => null
2147
+ }
2086
2148
  <div style={{display: "flex", justifyContent: "space-between", alignItems: 'center'}}>
2087
2149
  {isfilterContentVisisble && !isWechatEmbedded && <CapButton
2088
2150
  className={`create-new-${channelLowerCase}`}
2089
2151
  type={"primary"}
2090
2152
  disabled={this.isCreateDisabled()}
2091
- style={{marginLeft: '8px'}}
2153
+ style={{marginLeft: '8px', marginBottom: '12px'}}
2092
2154
  onClick={this.createTemplate} >
2093
- { this.props.intl.formatMessage(messages.createNewActionButton) }
2155
+ { this.props.intl.formatMessage((isTraiDltFeature && channel.toUpperCase() === SMS ) ? messages.uploadTemplate : messages.createNewActionButton) }
2094
2156
  </CapButton>}
2095
2157
  </div>
2096
2158
 
@@ -374,4 +374,20 @@ export default defineMessages({
374
374
  id: `${scope}.noAccountsPresentFacebook`,
375
375
  defaultMessage: "Please setup the Facebook account & Facebook page to start creating content in Capillary's FB content creator.",
376
376
  },
377
+ "promotional": {
378
+ id: `${scope}.promotional`,
379
+ defaultMessage: 'Promotional',
380
+ },
381
+ "serviceExplicit": {
382
+ id: `${scope}.serviceExplicit`,
383
+ defaultMessage: 'Service explicit',
384
+ },
385
+ "serviceImplicit": {
386
+ id: `${scope}.serviceImplicit`,
387
+ defaultMessage: 'Service implicit',
388
+ },
389
+ "uploadTemplate": {
390
+ id: `${scope}.uploadTemplate`,
391
+ defaultMessage: 'Upload template',
392
+ },
377
393
  });