@capillarytech/creatives-library 2.0.53 → 2.0.56

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 (46) hide show
  1. package/components/CallTaskPreview/_callTaskPreview.scss +17 -0
  2. package/components/CallTaskPreview/images/Footer.png +0 -0
  3. package/components/CallTaskPreview/images/Header.png +0 -0
  4. package/components/CallTaskPreview/index.js +37 -0
  5. package/components/CallTaskPreview/messages.js +13 -0
  6. package/components/CallTaskPreview/tests/index.test.js +15 -0
  7. package/components/CapTagList/index.js +1 -1
  8. package/components/EmailPreviewV2/index.js +3 -1
  9. package/components/NewCallTask/_newCallTask.scss +9 -0
  10. package/components/NewCallTask/index.js +152 -0
  11. package/components/NewCallTask/messages.js +29 -0
  12. package/components/NewCallTask/tests/index.test.js +10 -0
  13. package/containers/CallTask/_callTask.scss +5 -0
  14. package/containers/CallTask/actions.js +15 -0
  15. package/containers/CallTask/constants.js +7 -0
  16. package/containers/CallTask/index.js +141 -0
  17. package/containers/CallTask/messages.js +21 -0
  18. package/containers/CallTask/reducer.js +23 -0
  19. package/containers/CallTask/sagas.js +11 -0
  20. package/containers/CallTask/selectors.js +25 -0
  21. package/containers/CallTask/tests/actions.test.js +18 -0
  22. package/containers/CallTask/tests/index.test.js +10 -0
  23. package/containers/CallTask/tests/reducer.test.js +9 -0
  24. package/containers/CallTask/tests/sagas.test.js +15 -0
  25. package/containers/CallTask/tests/selectors.test.js +10 -0
  26. package/containers/CreativesContainer/SlideBoxContent.js +22 -0
  27. package/containers/CreativesContainer/constants.js +1 -0
  28. package/containers/CreativesContainer/index.js +34 -6
  29. package/containers/Email/_email.scss +0 -1
  30. package/containers/Email/index.js +4 -3
  31. package/containers/Line/Edit/_lineEdit.scss +0 -4
  32. package/containers/MobilePush/Create/_mobilePushCreate.scss +0 -4
  33. package/containers/MobilePush/Edit/_mobilePushCreate.scss +0 -4
  34. package/containers/Sms/Create/_smsCreate.scss +0 -1
  35. package/containers/Sms/Edit/actions.js +6 -0
  36. package/containers/Sms/Edit/constants.js +1 -0
  37. package/containers/Sms/Edit/index.js +1 -0
  38. package/containers/Sms/Edit/reducer.js +2 -0
  39. package/containers/TagList/index.js +3 -3
  40. package/containers/TemplatesV2/index.js +21 -10
  41. package/index.js +7 -0
  42. package/package.json +2 -2
  43. package/routes.js +7 -2
  44. package/styles/containers/layout/_layoutPage.scss +1 -2
  45. package/containers/TemplatesV2/reducer.js +0 -34
  46. package/containers/TemplatesV2/sagas.js +0 -32
@@ -0,0 +1,17 @@
1
+ .call-task-preview {
2
+ .call-task-subject-container {
3
+ padding: 8px;
4
+ background: #444;
5
+ color: white;
6
+ font-size: 12px;
7
+ font-weight: 500;
8
+ }
9
+ .call-task-message-container {
10
+ overflow-y: auto;
11
+ height: 150px;
12
+ width: 100%;
13
+ font-size: 12px;
14
+ background: #eee;
15
+ padding: 8px;
16
+ }
17
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ *
3
+ * CallTaskPreview
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import HeaderPng from './images/Header.png';
10
+ import FooterPng from './images/Footer.png';
11
+
12
+ import './_callTaskPreview.scss';
13
+
14
+ class CallTaskPreview extends React.Component { // eslint-disable-line react/prefer-stateless-function
15
+ render() {
16
+ const { subject, messageBody } = this.props;
17
+ return (
18
+ <div className="call-task-preview">
19
+ <img style={{ width: '100%' }} src={HeaderPng} alt="header" />
20
+ <div className="call-task-subject-container">
21
+ {subject}
22
+ </div>
23
+ <div className="call-task-message-container">
24
+ {messageBody}
25
+ </div>
26
+ <img style={{ width: '100%' }} src={FooterPng} alt="footer" />
27
+ </div>
28
+ );
29
+ }
30
+ }
31
+
32
+ CallTaskPreview.propTypes = {
33
+ subject: PropTypes.string,
34
+ messageBody: PropTypes.string,
35
+ };
36
+
37
+ export default CallTaskPreview;
@@ -0,0 +1,13 @@
1
+ /*
2
+ * CallTaskPreview Messages
3
+ *
4
+ * This contains all the text for the CallTaskPreview component.
5
+ */
6
+ import { defineMessages } from 'react-intl';
7
+
8
+ export default defineMessages({
9
+ header: {
10
+ id: 'app.components.CallTaskPreview.header',
11
+ defaultMessage: 'This is the CallTaskPreview component !',
12
+ },
13
+ });
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { shallow } from "enzyme";
3
+ import CallTaskPreview from "../index";
4
+
5
+ describe("<CallTaskPreview />", () => {
6
+ it("Test if Call task component renders", () => {
7
+ const component = shallow(
8
+ <CallTaskPreview
9
+ subject="this is test"
10
+ body="this is test message body"
11
+ />
12
+ );
13
+ expect(component).toMatchSnapshot();
14
+ });
15
+ });
@@ -81,8 +81,8 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
81
81
  tagValue = tagValue.replace("(N)", `${days}`);
82
82
  const resultArray = [];
83
83
  resultArray.push(tagValue);
84
- this.props.onSelect(resultArray);
85
84
  this.setState({showModal: false, dynamicDateValue: ''});
85
+ this.props.onSelect(resultArray);
86
86
  };
87
87
 
88
88
  handleCancel = () => {
@@ -54,7 +54,9 @@ class EmailPreviewV2 extends React.Component {
54
54
  this.props.templateActions.getTemplateDetails(templateData._id); // templateContent: html data to be shown in iframe
55
55
  }
56
56
  }
57
-
57
+ componentWillUnmount() {
58
+ this.props.templateActions.resetTemplateData();
59
+ }
58
60
  changePreviewDevice = (ev) => {
59
61
  this.setState({currentDevice: ev.currentTarget.id});
60
62
  }
@@ -0,0 +1,9 @@
1
+ .new-call-task-done-button {
2
+ margin-top: 24px;
3
+ }
4
+
5
+ .new-call-task-tagList {
6
+ position: absolute;
7
+ right: 0;
8
+ top: -10px;
9
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ *
3
+ * NewCallTask
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import PropTypes from 'prop-types';
9
+ import { CapRow, CapColumn, CapInput, CapButton, CapSpin } from '@capillarytech/cap-ui-library';
10
+ import { injectIntl, intlShape } from 'react-intl';
11
+ import TagList from '../../containers/TagList';
12
+ import CallTaskPreview from '../CallTaskPreview';
13
+ import messages from './messages';
14
+ import './_newCallTask.scss';
15
+
16
+ class NewCallTask extends React.Component { // eslint-disable-line react/prefer-stateless-function
17
+ constructor(props) {
18
+ super(props);
19
+ this.state = this.getInitialState();
20
+ }
21
+
22
+ getInitialState = () => {
23
+ const { templateData } = this.props;
24
+ return {
25
+ subject: templateData && templateData.subject ? templateData.subject : '',
26
+ messageBody: templateData && templateData.messageBody ? templateData.messageBody : '',
27
+ description: templateData && templateData.description ? templateData.description : '',
28
+ };
29
+ }
30
+
31
+ onTagSelect = (data) => {
32
+ this.insertAtCursor(`{{${data}}}`);
33
+ }
34
+
35
+ setInputRef = (textarea) => {
36
+ this.textArea = textarea;
37
+ }
38
+
39
+ insertAtCursor = (myValue) => {
40
+ this.textArea.focus();
41
+ const textAreaRef = this.textArea.textAreaRef;
42
+ let pos = textAreaRef.selectionEnd + myValue.length;
43
+ //IE support
44
+ let newMessage = textAreaRef.value + myValue;
45
+ if (document.selection) {
46
+ this.textArea.focus();
47
+ const sel = document.selection.createRange();
48
+ sel.text = myValue;
49
+ } else if (textAreaRef.selectionStart || textAreaRef.selectionStart === '0') { //MOZILLA and others
50
+ const startPos = textAreaRef.selectionStart;
51
+ const endPos = textAreaRef.selectionEnd;
52
+ newMessage = textAreaRef.value.substring(0, startPos) + myValue + textAreaRef.value.substring(endPos, textAreaRef.value.length);
53
+ pos = startPos + myValue.length;
54
+ }
55
+ this.setState({
56
+ messageBody: newMessage,
57
+ }, () => {
58
+ textAreaRef.selectionStart = pos;
59
+ textAreaRef.selectionEnd = pos;
60
+ });
61
+ };
62
+
63
+ updateSubject = (e) => {
64
+ this.setState({
65
+ subject: e.target.value,
66
+ });
67
+ }
68
+
69
+ updateMessageBody = (e) => {
70
+ this.setState({
71
+ messageBody: e.target.value,
72
+ });
73
+ }
74
+
75
+ updateDescription = (e) => {
76
+ this.setState({
77
+ description: e.target.value,
78
+ });
79
+ }
80
+
81
+ submitForm = () => {
82
+ this.props.onCallTaskSubmit(this.state);
83
+ }
84
+
85
+ render() {
86
+ const { intl, isLoading, metaEntities } = this.props;
87
+ const { formatMessage } = intl;
88
+ const { subject, messageBody, description } = this.state;
89
+ const tags = metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
90
+ return (
91
+ <CapSpin spinning={isLoading}>
92
+ <CapRow gutter={16}>
93
+ <CapColumn span={12}>
94
+ <div style={{ height: 'calc(100vh - 187px)', overflowY: 'auto', paddingRight: "8px" }}>
95
+ <div style={{ marginBottom: '34px' }}>
96
+ <CapInput
97
+ label={formatMessage(messages.callTaskTitle)}
98
+ onChange={this.updateSubject}
99
+ value={subject}
100
+ isRequired
101
+ />
102
+ </div>
103
+ <div style={{ position: 'relative', marginBottom: '34px' }}>
104
+ <TagList
105
+ className="new-call-task-tagList"
106
+ label={formatMessage(messages.personalisationTags)}
107
+ tags={tags}
108
+ onTagSelect={this.onTagSelect}
109
+ />
110
+ <CapInput.TextArea
111
+ setInputRef={this.setInputRef}
112
+ label={formatMessage(messages.messageHeader)}
113
+ onChange={this.updateMessageBody}
114
+ value={messageBody}
115
+ isRequired
116
+ />
117
+ </div>
118
+ <CapInput.TextArea
119
+ label={formatMessage(messages.description)}
120
+ onChange={this.updateDescription}
121
+ value={description}
122
+ isRequired
123
+ />
124
+ </div>
125
+ <CapButton
126
+ disabled={!subject || !messageBody || !description}
127
+ className="new-call-task-done-button"
128
+ onClick={this.submitForm}>
129
+ {formatMessage(messages.done)}
130
+ </CapButton>
131
+ </CapColumn>
132
+ <CapColumn span={12}>
133
+ <CallTaskPreview
134
+ subject={subject}
135
+ messageBody={messageBody}
136
+ />
137
+ </CapColumn>
138
+ </CapRow>
139
+ </CapSpin>
140
+ );
141
+ }
142
+ }
143
+
144
+ NewCallTask.propTypes = {
145
+ intl: intlShape.isRequired,
146
+ onCallTaskSubmit: PropTypes.func,
147
+ templateData: PropTypes.object,
148
+ isLoading: PropTypes.bool,
149
+ metaEntities: PropTypes.object,
150
+ };
151
+
152
+ export default injectIntl(NewCallTask);
@@ -0,0 +1,29 @@
1
+ /*
2
+ * NewCallTask Messages
3
+ *
4
+ * This contains all the text for the NewCallTask component.
5
+ */
6
+ import { defineMessages } from 'react-intl';
7
+
8
+ export default defineMessages({
9
+ callTaskTitle: {
10
+ id: 'app.components.NewCallTask.callTaskTitle',
11
+ defaultMessage: 'Call Task title',
12
+ },
13
+ messageHeader: {
14
+ id: 'app.components.NewCallTask.messageHeader',
15
+ defaultMessage: 'Message',
16
+ },
17
+ description: {
18
+ id: 'app.components.NewCallTask.description',
19
+ defaultMessage: 'Description',
20
+ },
21
+ done: {
22
+ id: 'app.components.NewCallTask.done',
23
+ defaultMessage: 'Done',
24
+ },
25
+ personalisationTags: {
26
+ id: 'app.components.NewCallTask.personalisationTags',
27
+ defaultMessage: 'Personalisation tags',
28
+ },
29
+ });
@@ -0,0 +1,10 @@
1
+ // import React from 'react';
2
+ // import { shallow } from 'enzyme';
3
+
4
+ // import NewCallTask from '../index';
5
+
6
+ describe('<NewCallTask />', () => {
7
+ it('Expect to have unit tests specified', () => {
8
+ expect(false).toEqual(false);
9
+ });
10
+ });
@@ -0,0 +1,5 @@
1
+ .call-task-action-button {
2
+ display: flex;
3
+ justify-content: center;
4
+ margin-top: 40px;
5
+ }
@@ -0,0 +1,15 @@
1
+ /*
2
+ *
3
+ * CallTask actions
4
+ *
5
+ */
6
+
7
+ import {
8
+ DEFAULT_ACTION,
9
+ } from './constants';
10
+
11
+ export function defaultAction() {
12
+ return {
13
+ type: DEFAULT_ACTION,
14
+ };
15
+ }
@@ -0,0 +1,7 @@
1
+ /*
2
+ *
3
+ * CallTask constants
4
+ *
5
+ */
6
+
7
+ export const DEFAULT_ACTION = 'app/CallTask/DEFAULT_ACTION';
@@ -0,0 +1,141 @@
1
+ /*
2
+ *
3
+ * CallTask
4
+ *
5
+ */
6
+ import PropTypes from 'prop-types';
7
+ import React from 'react';
8
+ import { bindActionCreators } from "redux";
9
+ import { connect } from 'react-redux';
10
+ import {
11
+ //CapSlideBox,
12
+ CapButton,
13
+ //CapIcon,
14
+ //CapHeader,
15
+ CapLabel,
16
+ } from '@capillarytech/cap-ui-library';
17
+ import { injectIntl, intlShape } from 'react-intl';
18
+ import { createStructuredSelector } from 'reselect';
19
+ import { makeSelectMetaEntities, isLoadingMetaEntities } from '../Cap/selectors';
20
+ import * as globalActions from '../../containers/Cap/actions';
21
+ import * as actions from "./actions";
22
+ import makeSelectCallTask from './selectors';
23
+ import NewCallTask from '../../components/NewCallTask';
24
+ import messages from './messages';
25
+ import './_callTask.scss';
26
+
27
+ export class CallTask extends React.Component { // eslint-disable-line react/prefer-stateless-function
28
+ constructor(props) {
29
+ super(props);
30
+ this.state = {
31
+ showCallTaskSlidebox: false,
32
+ mode: props.mode || '', // supported modes are create | edit
33
+ };
34
+ }
35
+
36
+ componentDidMount() {
37
+ const query = {
38
+ layout: 'SMS',
39
+ type: 'TAG',
40
+ context: 'outbound',
41
+ embedded: 'embedded',
42
+ };
43
+ this.props.globalActions.fetchSchemaForEntity(query);
44
+ }
45
+
46
+ showNewCallTask = () => {
47
+ this.setState({
48
+ showCallTaskSlidebox: true,
49
+ mode: 'create',
50
+ });
51
+ }
52
+
53
+ closeSlideBox = () => {
54
+ this.setState({
55
+ showCallTaskSlidebox: false,
56
+ mode: '',
57
+ });
58
+ }
59
+
60
+ render() {
61
+ const { intl, onCallTaskSubmit, templateData, loadingMetaEntities, metaEntities, showContentInSlidebox, onCreateNew } = this.props;
62
+ const { formatMessage } = intl;
63
+ const { mode } = this.state;
64
+ return (
65
+ <div>
66
+ {mode ?
67
+ <NewCallTask
68
+ metaEntities={metaEntities}
69
+ isLoading={loadingMetaEntities}
70
+ mode={mode}
71
+ templateData={templateData}
72
+ onCallTaskSubmit={onCallTaskSubmit}
73
+ />
74
+ :
75
+ <div>
76
+ <CapLabel type="label1">
77
+ {formatMessage(messages.callTaskInfo)}
78
+ </CapLabel>
79
+ <div className="call-task-action-button">
80
+ <CapButton onClick={showContentInSlidebox ? this.showNewCallTask : onCreateNew}>
81
+ {formatMessage(messages.addNewCallTask)}
82
+ </CapButton>
83
+ </div>
84
+ {/* {
85
+ showCallTaskSlidebox && showContentInSlidebox &&
86
+ <CapSlideBox
87
+ header={
88
+ <CapHeader
89
+ title={formatMessage(messages.newMessage)}
90
+ prefix={
91
+ <CapIcon style={{ marginRight: '16px' }} type={"back"} onClick={this.closeSlideBox} />
92
+ } />
93
+ }
94
+ show={showCallTaskSlidebox}
95
+ size="size-l"
96
+ handleClose={this.closeSlideBox}
97
+ content={
98
+ <NewCallTask
99
+ metaEntities={metaEntities}
100
+ isLoading={loadingMetaEntities}
101
+ mode={mode}
102
+ onCallTaskSubmit={onCallTaskSubmit}
103
+ />
104
+ }
105
+ />
106
+ } */}
107
+ </div>
108
+ }
109
+ </div>
110
+ );
111
+ }
112
+ }
113
+
114
+ CallTask.propTypes = {
115
+ intl: intlShape.isRequired,
116
+ onCallTaskSubmit: PropTypes.func,
117
+ mode: PropTypes.string,
118
+ templateData: PropTypes.object,
119
+ globalActions: PropTypes.object,
120
+ metaEntities: PropTypes.object,
121
+ loadingMetaEntities: PropTypes.bool,
122
+ showContentInSlidebox: PropTypes.bool,
123
+ onCreateNew: PropTypes.func,
124
+ };
125
+
126
+
127
+ const mapStateToProps = createStructuredSelector({
128
+ CallTask: makeSelectCallTask(),
129
+ metaEntities: makeSelectMetaEntities(),
130
+ loadingMetaEntities: isLoadingMetaEntities(),
131
+ });
132
+
133
+ function mapDispatchToProps(dispatch) {
134
+ return {
135
+ globalActions: bindActionCreators(globalActions, dispatch),
136
+ actions: bindActionCreators(actions, dispatch),
137
+ };
138
+ }
139
+
140
+
141
+ export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(CallTask));
@@ -0,0 +1,21 @@
1
+ /*
2
+ * CallTask Messages
3
+ *
4
+ * This contains all the text for the CallTask component.
5
+ */
6
+ import { defineMessages } from 'react-intl';
7
+
8
+ export default defineMessages({
9
+ addNewCallTask: {
10
+ id: 'app.containers.CallTask.addNewCallTask',
11
+ defaultMessage: 'Add New Call Task',
12
+ },
13
+ newMessage: {
14
+ id: 'app.containers.CallTask.newMessage',
15
+ defaultMessage: 'New Message',
16
+ },
17
+ callTaskInfo: {
18
+ id: 'app.containers.CallTask.callTaskInfo',
19
+ defaultMessage: 'Call Tasks are message sent to store staff. The messages are displayed on the Point of Sale (POS) machines for store staff to take action.',
20
+ },
21
+ });
@@ -0,0 +1,23 @@
1
+ /*
2
+ *
3
+ * CallTask reducer
4
+ *
5
+ */
6
+
7
+ import { fromJS } from 'immutable';
8
+ import {
9
+ DEFAULT_ACTION,
10
+ } from './constants';
11
+
12
+ const initialState = fromJS({});
13
+
14
+ function callTaskReducer(state = initialState, action) {
15
+ switch (action.type) {
16
+ case DEFAULT_ACTION:
17
+ return state;
18
+ default:
19
+ return state;
20
+ }
21
+ }
22
+
23
+ export default callTaskReducer;
@@ -0,0 +1,11 @@
1
+ // import { take, call, put, select } from 'redux-saga/effects';
2
+
3
+ // Individual exports for testing
4
+ export function* defaultSaga() {
5
+ // See example in containers/HomePage/sagas.js
6
+ }
7
+
8
+ // All sagas to be loaded
9
+ export default [
10
+ defaultSaga,
11
+ ];
@@ -0,0 +1,25 @@
1
+ import { createSelector } from 'reselect';
2
+
3
+ /**
4
+ * Direct selector to the callTask state domain
5
+ */
6
+ const selectCallTaskDomain = () => (state) => state.get('callTask');
7
+
8
+ /**
9
+ * Other specific selectors
10
+ */
11
+
12
+
13
+ /**
14
+ * Default selector used by CallTask
15
+ */
16
+
17
+ const makeSelectCallTask = () => createSelector(
18
+ selectCallTaskDomain(),
19
+ (substate) => substate.toJS()
20
+ );
21
+
22
+ export default makeSelectCallTask;
23
+ export {
24
+ selectCallTaskDomain,
25
+ };
@@ -0,0 +1,18 @@
1
+
2
+ import {
3
+ defaultAction,
4
+ } from '../actions';
5
+ import {
6
+ DEFAULT_ACTION,
7
+ } from '../constants';
8
+
9
+ describe('CallTask actions', () => {
10
+ describe('Default Action', () => {
11
+ it('has a type of DEFAULT_ACTION', () => {
12
+ const expected = {
13
+ type: DEFAULT_ACTION,
14
+ };
15
+ expect(defaultAction()).toEqual(expected);
16
+ });
17
+ });
18
+ });
@@ -0,0 +1,10 @@
1
+ // import React from 'react';
2
+ // import { shallow } from 'enzyme';
3
+
4
+ // import { CallTask } from '../index';
5
+
6
+ describe('<CallTask />', () => {
7
+ it('Expect to have unit tests specified', () => {
8
+ expect(false).toEqual(false);
9
+ });
10
+ });
@@ -0,0 +1,9 @@
1
+
2
+ import { fromJS } from 'immutable';
3
+ import callTaskReducer from '../reducer';
4
+
5
+ describe('callTaskReducer', () => {
6
+ it('returns the initial state', () => {
7
+ expect(callTaskReducer(undefined, {})).toEqual(fromJS({}));
8
+ });
9
+ });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Test sagas
3
+ */
4
+
5
+ /* eslint-disable redux-saga/yield-effects */
6
+ // import { take, call, put, select } from 'redux-saga/effects';
7
+ // import { defaultSaga } from '../sagas';
8
+
9
+ // const generator = defaultSaga();
10
+
11
+ describe('defaultSaga Saga', () => {
12
+ it('Expect to have unit tests specified', () => {
13
+ expect(false).toEqual(false);
14
+ });
15
+ });
@@ -0,0 +1,10 @@
1
+ // import { fromJS } from 'immutable';
2
+ // import { makeSelectCallTaskDomain } from '../selectors';
3
+
4
+ // const selector = makeSelectCallTaskDomain();
5
+
6
+ describe('makeSelectCallTaskDomain', () => {
7
+ it('Expect to have unit tests specified', () => {
8
+ expect(false).toEqual(false);
9
+ });
10
+ });
@@ -8,6 +8,7 @@ import SmsEdit from '../Sms/Edit';
8
8
  import Email from '../Email';
9
9
  import EmailWrapper from '../EmailWrapper';
10
10
  import EmailPreviewV2 from '../../components/EmailPreviewV2';
11
+ import CallTask from '../CallTask';
11
12
  import * as constants from './constants';
12
13
  const CreativesWrapper = styled.div`
13
14
  .ant-popover,
@@ -36,6 +37,7 @@ function SlideBoxContent(props) {
36
37
  cap,
37
38
  onResetStep,
38
39
  isFullMode,
40
+ onCallTaskSubmit,
39
41
  setIsLoadingContent,
40
42
  } = props;
41
43
  const type = messageDetails.type.toLowerCase(); // type is context in get tags values : outbound | dvs | referral | loyalty | coupons
@@ -47,15 +49,18 @@ function SlideBoxContent(props) {
47
49
  };
48
50
  let channel = currentChannel;//for create mode
49
51
  const isCreateSms = slidBoxContent === 'createTemplate' && channel === constants.SMS;
52
+ const isCreateCallTask = slidBoxContent === 'createTemplate' && channel === 'CALLTASK';
50
53
  let isEditSms = false;
51
54
  let isEditEmailWithId = false;
52
55
  let isEmailEditWithContent = false;
53
56
  let isPreview = false;
54
57
  let isEmailPreview = false;
58
+ let isEditCallTask = false;
55
59
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL;
56
60
  if (templateData && channel) {
57
61
  channel = templateData.type;// for edit mode with template data
58
62
  isEditSms = slidBoxContent === 'editTemplate' && channel === constants.SMS;
63
+ isEditCallTask = slidBoxContent === 'editTemplate' && channel === constants.CALL_TASK;
59
64
  isEditEmailWithId = slidBoxContent === 'editTemplate' && channel === constants.EMAIL && templateData._id;
60
65
  isEmailEditWithContent = slidBoxContent === 'editTemplate' && channel === constants.EMAIL && !templateData._id;
61
66
  isPreview = slidBoxContent === 'preview' && channel !== constants.EMAIL;
@@ -73,6 +78,7 @@ function SlideBoxContent(props) {
73
78
  createNew={onCreateNew}
74
79
  channel={channel.toLowerCase()}
75
80
  onChannelChange={onChannelChange}
81
+ onCallTaskSubmit={onCallTaskSubmit}
76
82
  cap={cap}
77
83
  />
78
84
  )}
@@ -103,6 +109,21 @@ function SlideBoxContent(props) {
103
109
  subscriptionTemplateDetails={templateData} // used only in library mode
104
110
  />
105
111
  )}
112
+ {
113
+ isEditCallTask &&
114
+ <CallTask
115
+ mode={'edit'}
116
+ templateData={templateData}
117
+ onCallTaskSubmit={onCallTaskSubmit}
118
+ />
119
+ }
120
+ {
121
+ isCreateCallTask &&
122
+ <CallTask
123
+ mode={'create'}
124
+ onCallTaskSubmit={onCallTaskSubmit}
125
+ />
126
+ }
106
127
  {isCreateSms && (
107
128
  <SmsCreate
108
129
  setIsLoadingContent={setIsLoadingContent}
@@ -172,6 +193,7 @@ SlideBoxContent.propTypes = {
172
193
  onEmailNextStep: PropTypes.func,
173
194
  cap: PropTypes.object,
174
195
  onResetStep: PropTypes.func,
196
+ onCallTaskSubmit: PropTypes.func,
175
197
  isFullMode: PropTypes.bool,
176
198
  setIsLoadingContent: PropTypes.func,
177
199
  };
@@ -7,5 +7,6 @@
7
7
  export const DEFAULT_ACTION = 'app/CreativesContainer/DEFAULT_ACTION';
8
8
  export const EMAIL = "EMAIL";
9
9
  export const SMS = "SMS";
10
+ export const CALL_TASK = "CALL_TASK";
10
11
  export const SHOW_CONTANER_LOADER = "app/CreativesContainer/SHOW_CONTANER_LOADER";
11
12
  export const HIDE_CONTAINER_LOADER = "app/CreativesContainer/HIDE_CONTAINER_LOADER";
@@ -66,20 +66,37 @@ class Creatives extends React.Component {
66
66
  };
67
67
 
68
68
  onShowTemplates = () => {
69
- this.setState({ slidBoxContent: 'templates', showSlideBox: true });
69
+ this.setState({ slidBoxContent: 'templates', showSlideBox: true, isGetFormData: false });
70
70
  };
71
71
  onChannelChange = (channel) => {
72
72
  this.setState({currentChannel: channel});
73
73
  }
74
74
  onEmailNextStep = () => {
75
- this.setState((prevState) => ({
76
- templateStep: prevState.templateStep + 1,
77
- }));
75
+ this.setState((prevState) => {
76
+ let templateStep = prevState.templateStep + 1;
77
+ const {emailCreateMode, currentChannel} = prevState;
78
+ if (currentChannel.toUpperCase() === constants.EMAIL && emailCreateMode === "upload") {
79
+ templateStep = prevState.templateStep + 2;
80
+ }
81
+
82
+ return {
83
+ templateStep,
84
+ };
85
+ });
78
86
  }
79
87
 
80
88
  onEmailModeChange = (mode) => {
81
89
  this.setState({ emailCreateMode: mode});
82
90
  }
91
+
92
+
93
+ onCallTaskSubmit = (data) => {
94
+ this.props.getCreativesData({
95
+ channel: 'CALL_TASK',
96
+ ...data,
97
+ });
98
+ }
99
+
83
100
  getTemplateData = (templateData) => { //from consumers to creatives
84
101
  const {isFullMode} = this.props;
85
102
  if (!isFullMode && templateData) { // for component mode
@@ -111,6 +128,14 @@ class Creatives extends React.Component {
111
128
  edit: true,
112
129
  type: channel,
113
130
  };
131
+ } else if (channel === constants.CALL_TASK) {
132
+ creativesTeplateData = {
133
+ subject: templateData.subject,
134
+ messageBody: templateData.messageBody,
135
+ description: templateData.description,
136
+ edit: true,
137
+ type: channel,
138
+ };
114
139
  }
115
140
  return creativesTeplateData;
116
141
  }
@@ -138,6 +163,7 @@ class Creatives extends React.Component {
138
163
  }
139
164
  return creativesMode;
140
165
  }
166
+
141
167
  getFormData = (template) => {
142
168
  if (template.validity) {
143
169
  this.setState(
@@ -180,7 +206,7 @@ class Creatives extends React.Component {
180
206
  this.setState({ isGetFormData: true });
181
207
  };
182
208
  shouldShowFooter = () => {
183
- const {isFullMode} = this.props;
209
+ const {isFullMode } = this.props;
184
210
  const {slidBoxContent, currentChannel, emailCreateMode, templateStep} = this.state;
185
211
  const channel = currentChannel.toUpperCase();
186
212
  const currentStep = this.creativesTemplateSteps[templateStep];
@@ -188,7 +214,8 @@ class Creatives extends React.Component {
188
214
  if (!isFullMode ) {
189
215
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL && currentStep !== 'createTemplateContent';
190
216
  const isCreate = (slidBoxContent === 'editTemplate' || slidBoxContent === 'createTemplate') && !isEmailCreate;
191
- showFooter = (isCreate || (isEmailCreate && emailCreateMode) || slidBoxContent === 'preview');
217
+ const isCallTask = channel === 'CALLTASK';
218
+ showFooter = (isCreate || (isEmailCreate && emailCreateMode) || slidBoxContent === 'preview') && !isCallTask;
192
219
  } else if (channel === constants.EMAIL && isFullMode) {
193
220
  const isEmailCreate = slidBoxContent === 'createTemplate' && channel === constants.EMAIL && currentStep !== 'createTemplateContent';
194
221
  showFooter = isEmailCreate && emailCreateMode;
@@ -250,6 +277,7 @@ class Creatives extends React.Component {
250
277
  location={this.props.location}
251
278
  messageDetails={this.props.messageDetails}
252
279
  getFormData={this.getFormData}
280
+ onCallTaskSubmit={this.onCallTaskSubmit}
253
281
  onCreateNew={this.onCreateNew}
254
282
  isGetFormData={isGetFormData}
255
283
  isFullMode={isFullMode}
@@ -77,7 +77,6 @@
77
77
  .ant-tabs-extra-content{
78
78
  flex: 1;
79
79
  align-self: flex-end;
80
- padding: 4px;
81
80
  height: 42px;
82
81
  }
83
82
 
@@ -390,7 +390,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
390
390
  const message = getMessageObject('error', (nextProps.Email.createTemplateErrorMessage && nextProps.Email.createTemplateErrorMessage !== '') ? nextProps.Email.createTemplateErrorMessage : this.props.intl.formatMessage(messages.somethingWentWrong), true);
391
391
  this.props.globalActions.addMessageToQueue(message);
392
392
  }
393
- if (!nextProps.Email.fetchingCmsData && !_.isEqual(nextProps.Email.cmsData, this.props.Email.cmsData)) {
393
+ if (!nextProps.Email.fetchingCmsData && !_.isEqual(nextProps.Email.cmsData, this.props.Email.cmsData) && !_.isEmpty(nextProps.Email.cmsData)) {
394
394
  const formData = _.cloneDeep(this.state.formData);
395
395
  if (nextProps.Email.langId && nextProps.Email.langId !== 'undefined') {
396
396
  const langId = nextProps.Email.langId;
@@ -2540,6 +2540,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2540
2540
  onItemClick={this.handleOnItemClick}
2541
2541
  colNumber={2}
2542
2542
  enablePagination
2543
+ isLoading={this.props.Email.fetchingAllAssets}
2543
2544
  />
2544
2545
  </Pagination>
2545
2546
  </div>
@@ -2619,7 +2620,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2619
2620
  />}
2620
2621
  <ImagePreview
2621
2622
  header={imagePreviewHeader}
2622
- content={imagePreviewContent}
2623
+ content={imagePreviewContent}
2623
2624
  show={this.state.showImageSelectionBox}
2624
2625
  handleClose={this.handleImagePreviewClose}
2625
2626
  isLoading={this.props.Email.fetchingAllAssets}
@@ -2671,4 +2672,4 @@ function mapDispatchToProps(dispatch) {
2671
2672
  };
2672
2673
  }
2673
2674
 
2674
- export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(injectIntl(Email)));
2675
+ export default UserIsAuthenticated(connect(mapStateToProps, mapDispatchToProps)(injectIntl(Email)));
@@ -19,7 +19,3 @@
19
19
  padding: 16px 16px 16px 0;
20
20
  border: solid 1px #D6D6D6;
21
21
  }
22
- .ant-radio-wrapper{
23
- font-size: 14px;
24
- font-family: 'open-sans';
25
- }
@@ -19,7 +19,3 @@
19
19
  padding: 16px 16px 16px 0;
20
20
  border: solid 1px #D6D6D6;
21
21
  }
22
- .ant-radio-wrapper{
23
- font-size: 14px;
24
- font-family: 'open-sans';
25
- }
@@ -19,7 +19,3 @@
19
19
  padding: 16px 16px 16px 0;
20
20
  border: solid 1px #D6D6D6;
21
21
  }
22
- .ant-radio-wrapper{
23
- font-size: 14px;
24
- font-family: 'open-sans';
25
- }
@@ -32,7 +32,6 @@
32
32
  .ant-tabs-extra-content{
33
33
  flex: 1;
34
34
  align-self: flex-end;
35
- padding: 4px;
36
35
  }
37
36
 
38
37
  .version-popover {
@@ -37,3 +37,9 @@ export function clearEditResponse() {
37
37
  type: types.CLEAR_EDIT_RESPONSE_REQUEST,
38
38
  };
39
39
  }
40
+
41
+ export function resetEditTemplate() {
42
+ return {
43
+ type: types.RESET_EDIT_TEMPLATE,
44
+ };
45
+ }
@@ -15,3 +15,4 @@ export const EDIT_TEMPLATE_SUCCESS = 'app/containers/Sms/Edit/EDIT_TEMPLATE_SUCC
15
15
  export const EDIT_TEMPLATE_FAILURE = 'app/containers/Sms/Edit/EDIT_TEMPLATE_FAILURE';
16
16
 
17
17
  export const CLEAR_EDIT_RESPONSE_REQUEST = 'app/containers/Sms/Create/CLEAR_EDIT_RESPONSE_REQUEST';
18
+ export const RESET_EDIT_TEMPLATE = 'app/containers/Sms/Create/RESET_EDIT_TEMPLATE';
@@ -194,6 +194,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
194
194
  if (this.props.setIsLoadingContent) {
195
195
  this.props.setIsLoadingContent(true); // setting isLoading of CreativesContainer so that slidebox foot can be hidden till content is loaded
196
196
  }
197
+ this.props.actions.resetEditTemplate();
197
198
  window.removeEventListener("message", this.handleFrameTasks);
198
199
  }
199
200
 
@@ -42,6 +42,8 @@ function editReducer(state = initialState, action) {
42
42
  return state.set('getTemplateDetailsInProgress', false);
43
43
  case types.CLEAR_EDIT_RESPONSE_REQUEST:
44
44
  return state.set('editResponse', {});
45
+ case types.RESET_EDIT_TEMPLATE:
46
+ return state.set('templateDetails', {});
45
47
  default:
46
48
  return state;
47
49
  }
@@ -82,9 +82,9 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
82
82
  console.log('Inside ', tag.label);
83
83
  mainTags[tag.value] = {
84
84
  'tag-header': true,
85
- name: tag.label[this.props.userLocale] ? tag.label[this.props.userLocale] : tag.label.en,
86
- desc: tag.desc ? tag.desc : tag.label.en,
87
- subtags: tag.subtags,
85
+ "name": tag.label[this.props.userLocale] ? tag.label[this.props.userLocale] : tag.label.en,
86
+ "desc": tag.desc ? tag.desc : tag.label.en,
87
+ "subtags": tag.subtags,
88
88
  };
89
89
  }
90
90
  });
@@ -22,6 +22,7 @@ import { makeSelectTemplates, makeSelectTemplatesResponse } from '../Templates/s
22
22
  import messages from './messages';
23
23
  import * as actions from './actions';
24
24
  import Templates from '../Templates';
25
+ import CallTask from '../CallTask';
25
26
  import './_templatesV2.scss';
26
27
 
27
28
  const {CapCustomCardList} = CapCustomCard;
@@ -52,6 +53,7 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
52
53
  const deafaultPanes = [
53
54
  {content: <div></div>, tab: 'Sms', key: 'sms'},
54
55
  {content: <div></div>, tab: 'Email', key: 'email'},
56
+ // {content: <div></div>, tab: 'Call Task', key: 'calltask'},
55
57
  // {content: this.getTemplatesComponent('wechat'), tab: 'Wechat', key: 'wechat'},
56
58
  // {content: <div></div>, tab: 'Mpush', key: 'mobilepush'},
57
59
  ];
@@ -94,22 +96,27 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
94
96
 
95
97
  </div>);
96
98
  }
99
+
100
+ getCallTaskComponent = () => <CallTask onCreateNew={this.props.createNew} onCallTaskSubmit={this.props.onCallTaskSubmit}/>
101
+
97
102
  getTemplatesComponent(channel) {
98
103
  // const templates = this.props.Templates[`${channel}Templates`];
99
104
  let query = {};
100
105
  if (!this.props.isFullMode) {
101
106
  query = {type: 'embedded', module: 'library'};
102
107
  }
103
- return (<Templates
104
- key={channel}
105
- location={{pathname: `/${channel}`, search: '', query}}
106
- route={{name: channel}}
107
- router={this.props.router}
108
- isFullMode={this.props.isFullMode}
109
- createNew={this.props.createNew}
110
- onSelectTemplate={(id) => this.props.onSelectTemplate(this.selectTemplate(id))}
111
- renderNewCardGrid={this.getTemplateDataForGrid}
112
- handlePeviewTemplate={this.props.handlePeviewTemplate}
108
+ return (channel === 'calltask' ?
109
+ this.getCallTaskComponent() :
110
+ <Templates
111
+ key={channel}
112
+ location={{pathname: `/${channel}`, search: '', query}}
113
+ route={{name: channel}}
114
+ router={this.props.router}
115
+ isFullMode={this.props.isFullMode}
116
+ createNew={this.props.createNew}
117
+ onSelectTemplate={(id) => this.props.onSelectTemplate(this.selectTemplate(id))}
118
+ renderNewCardGrid={this.getTemplateDataForGrid}
119
+ handlePeviewTemplate={this.props.handlePeviewTemplate}
113
120
  />);
114
121
  }
115
122
 
@@ -121,6 +128,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
121
128
  if (channel === "mpush") {
122
129
  channel = "mobilepush";
123
130
  }
131
+ if (channel === 'call task') {
132
+ channel = "calltask";
133
+ }
124
134
  pane.content = this.getTemplatesComponent(channel);
125
135
  return pane;
126
136
  }
@@ -175,6 +185,7 @@ TemplatesV2.propTypes = {
175
185
  Templates: PropTypes.object,
176
186
  TemplatesList: PropTypes.arrayOf(PropTypes.object),
177
187
  handlePeviewTemplate: PropTypes.func,
188
+ onCallTaskSubmit: PropTypes.func,
178
189
  intl: intlShape,
179
190
  onChannelChange: PropTypes.func,
180
191
  };
package/index.js CHANGED
@@ -19,6 +19,10 @@ import Email from './containers/Email/index';
19
19
  import emailReducer from './containers/Email/reducer';
20
20
  import emailSaga from './containers/Email/sagas';
21
21
 
22
+ import CallTask from './containers/CallTask/index';
23
+ import callTaskReducer from './containers/CallTask/reducer';
24
+ import callTaskSaga from './containers/CallTask/sagas';
25
+
22
26
  import WeChat from './containers/WeChat/MapTemplates';
23
27
  import wechatReduce from './containers/WeChat/MapTemplates/reducer';
24
28
  import wechatSaga from './containers/WeChat/MapTemplates/sagas';
@@ -55,6 +59,7 @@ const TemplatesContainer = {Templates, templateReducer, templateSaga};
55
59
  const SmsCreateContainer = {SmsCreate, smsCreateReducer, smsCreateSaga};
56
60
  const SmsEditContainer = {SmsEdit, smsEditReducer, smsEditSaga};
57
61
  const EmailContainer = {Email, emailReducer, emailSaga};
62
+ const CallTaskContainer = {CallTask, callTaskReducer, callTaskSaga};
58
63
  const WechatContainer = {WeChat, wechatReduce, wechatSaga};
59
64
  const MobilePushCreateContainer = {MobilepushCreate, mobilepushCreateReducer, mobilepushCreateSaga};
60
65
  const MobilePushEditContainer = {MobilepushEdit, mobilepushEditReducer, mobilepushEditSaga};
@@ -68,6 +73,7 @@ export {default as TagListReducer} from './containers/TagList/reducer';
68
73
  export {default as TemplatePreview} from './components/TemplatePreview';
69
74
  export {default as EmailPreview} from './components/EmailPreview';
70
75
  export {default as EmailPreviewV2} from './components/EmailPreviewV2';
76
+ export {default as CallTaskPreview} from './components/CallTaskPreview';
71
77
  export {default as EmailMobilePreview} from './components/EmailMobilePreview';
72
78
 
73
79
  CreativesContainer.CreativesContainerReducer = CreativesContainerReducer;
@@ -77,6 +83,7 @@ export { CapContainer,
77
83
  SmsCreateContainer,
78
84
  SmsEditContainer,
79
85
  EmailContainer,
86
+ CallTaskContainer,
80
87
  WechatContainer,
81
88
  Cap,
82
89
  Templates,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "2.0.53",
4
+ "version": "2.0.56",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "license": "LicenseRef-LICENSE",
14
14
  "dependencies": {
15
- "@capillarytech/cap-ui-library": "^1.0.86",
15
+ "@capillarytech/cap-ui-library": "^1.0.96",
16
16
  "antd": "^3.15.0",
17
17
  "axios": "^0.16.2",
18
18
  "babel-polyfill": "6.20.0",
package/routes.js CHANGED
@@ -607,21 +607,26 @@ export default function createRoutes(store) {
607
607
  import('containers/Sms/Edit/reducer'),
608
608
  import('containers/Sms/Edit/sagas'),
609
609
  import('containers/Email/reducer'),
610
- import('containers/Email/sagas')]);
610
+ import('containers/Email/sagas'),
611
+ import('containers/CallTask/reducer'),
612
+ import('containers/CallTask/sagas'),
613
+ ]);
611
614
 
612
615
  const renderRoute = loadModule(cb);
613
616
 
614
- importModules.then(([reducer, sagas, component, emailContainerSagas, creativesContainerReducer, smsCreateReducer, smsCreateSaga, smsEditReducer, smsEditSaga, emailReducer, emailSaga]) => {
617
+ importModules.then(([reducer, sagas, component, emailContainerSagas, creativesContainerReducer, smsCreateReducer, smsCreateSaga, smsEditReducer, smsEditSaga, emailReducer, emailSaga, callTaskReducer, callTaskSagas]) => {
615
618
  injectReducer('templates', reducer.default);
616
619
  injectReducer('create', smsCreateReducer.default);
617
620
  injectReducer('creativesContainer', creativesContainerReducer.default);
618
621
  injectReducer('edit', smsEditReducer.default);
619
622
  injectReducer('email', emailReducer.default);
623
+ injectReducer('callTask', callTaskReducer.default);
620
624
  injectSagas(sagas.default);
621
625
  injectSagas(emailContainerSagas.default);
622
626
  injectSagas(smsCreateSaga.default);
623
627
  injectSagas(smsEditSaga.default);
624
628
  injectSagas(emailSaga.default);
629
+ injectSagas(callTaskSagas.default);
625
630
  renderRoute(component);
626
631
  });
627
632
  importModules.catch(errorLoading);
@@ -7,10 +7,9 @@
7
7
  margin-right: 0rem !important;
8
8
  }
9
9
 
10
- .wrapper{
10
+ .wrapper {
11
11
  height: 100%;
12
12
  position: relative;
13
- font-family: "proxima-nova","open-sans", sans-serif !important;
14
13
  }
15
14
 
16
15
  .main{
@@ -1,34 +0,0 @@
1
- /*
2
- *
3
- * TemplatesV2 reducer
4
- *
5
- */
6
-
7
- import { fromJS } from 'immutable';
8
- import * as types from './constants';
9
-
10
- const initialState = fromJS({
11
- loadingTemplates: true,
12
- smsTemplates: [],
13
- emailTemplates: [],
14
- wechatTemplates: [],
15
- mobilepushTemplates: [],
16
- });
17
-
18
- function templatesV2Reducer(state = initialState, action) {
19
- const channelTemplates = `${action.channel}Templates`;
20
- switch (action.type) {
21
- case types.DEFAULT_ACTION:
22
- return state;
23
- case types.GET_TEMPLATES_REQUEST:
24
- return state.set('loadingTemplates', true);
25
- case types.GET_TEMPLATES_SUCESS:
26
- return state.set('loadingTemplates', false).set(channelTemplates, action.templates);
27
- case types.GET_TEMPLATES_FAILURE:
28
- return state.set('loadingTemplates', false).set('error', action.error);
29
- default:
30
- return state;
31
- }
32
- }
33
-
34
- export default templatesV2Reducer;
@@ -1,32 +0,0 @@
1
- import { take, takeLatest, call, put, cancel } from 'redux-saga/effects';
2
- import { LOCATION_CHANGE } from 'react-router-redux';
3
- import * as Api from '../../services/api';
4
-
5
-
6
- import * as types from './constants';
7
- // Individual exports for testing
8
- export function* defaultSaga() {
9
- // See example in containers/HomePage/sagas.js
10
- }
11
- function* getTemplates(action) {
12
- try {
13
- const req = {
14
- channel: action.channel,
15
- queryParams: action.query,
16
- };
17
- const res = yield call(Api.getAllTemplates, req);
18
- yield put({type: types.GET_TEMPLATES_SUCESS, templates: res.response.templates, channel: action.channel});
19
- } catch (error) {
20
- yield put({type: types.GET_TEMPLATES_FAILURE, error});
21
- }
22
- }
23
- function* getTemplatesWatcher() {
24
- const watcher = yield takeLatest(types.GET_TEMPLATES_REQUEST, getTemplates);
25
- yield take(LOCATION_CHANGE);
26
- yield cancel(watcher);
27
- }
28
- // All sagas to be loaded
29
- export default [
30
- defaultSaga,
31
- getTemplatesWatcher,
32
- ];