@capillarytech/creatives-library 6.14.0 → 6.14.1

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.
@@ -0,0 +1,517 @@
1
+ /* eslint-disable react/sort-comp */
2
+ /* eslint-disable dot-notation */
3
+ /* eslint-disable no-undef */
4
+ /*
5
+ *
6
+ * MobilepushWrapper
7
+ *
8
+ */
9
+ import PropTypes from 'prop-types';
10
+ import React from 'react';
11
+ import { bindActionCreators } from 'redux';
12
+ import { FormattedMessage, intlShape } from 'react-intl';
13
+ import { createStructuredSelector } from 'reselect';
14
+ import {
15
+ CapRadioCard,
16
+ CapSpin,
17
+ CapHeading,
18
+ CapHeader,
19
+ CapButton,
20
+ CapIcon,
21
+ CapLink,
22
+ CapRow,
23
+ CapColumn,
24
+ CapInput,
25
+ CapLabel,
26
+ CapTag,
27
+ CapTreeSelect,
28
+ } from '@capillarytech/cap-ui-library';
29
+ import { find, cloneDeep, findIndex, isEmpty, isEqual, concat, filter } from 'lodash';
30
+ import * as actions from './actions';
31
+ import { makeSelectFTP, makeSelectMetaEntities } from './selectors';
32
+ import withCreatives from '../../hoc/withCreatives';
33
+ import messages from './messages';
34
+ import './_FTP.scss';
35
+ import * as globalActions from '../../containers/Cap/actions';
36
+ import { TagList } from '../TagList';
37
+ import { NO_COMMUNICATION } from '../App/constants';
38
+ import { getTreeStructuredTags } from '../../utils/common';
39
+
40
+ const UIView = {
41
+ serverSelection: 'serverSelection',
42
+ configureMessage: 'configureMessage',
43
+ };
44
+ const { CapLabelInline } = CapLabel;
45
+ export class FTP extends React.Component { // eslint-disable-line react/prefer-stateless-function
46
+ constructor(props) {
47
+ super(props);
48
+ this.state = this.getInitialState();
49
+ }
50
+ getInitialState = () => {
51
+ const mode = this.props.mode || 'create';
52
+ let stateObj = {};
53
+ if (mode === 'create') {
54
+ const { formatMessage } = this.props.intl;
55
+ const headerTagList = [{
56
+ header: formatMessage(messages.customerID),
57
+ tag: '{{user_id}}',
58
+ }];
59
+ stateObj = {
60
+ mode,
61
+ selectedServer: null,
62
+ viewMode: UIView.serverSelection,
63
+ headerTagList,
64
+ messageContent: '',
65
+ tagsTree: [],
66
+ };
67
+ } else {
68
+ const { messageContent = {} } = this.props.messageDetails || {};
69
+ const messageContent1 = (messageContent['message_content_id_1'] || {}).messageBody;
70
+ const headerTagList = (messageContent['message_content_id_1'] || {}).headerTagList || [];
71
+ stateObj = {
72
+ mode,
73
+ selectedServer: null,
74
+ viewMode: UIView.configureMessage,
75
+ headerTagList,
76
+ messageContent: messageContent1,
77
+ tagsTree: [],
78
+ };
79
+ }
80
+ return stateObj;
81
+ };
82
+
83
+ setFTPServerInState = (FTPServers) => {
84
+ const { deliverySetting = {} } = this.props.messageDetails;
85
+ const configId = deliverySetting.channelSetting[NO_COMMUNICATION].endPointConfigs.ftpConfigId;
86
+ const selectedServer = find(FTPServers, {id: configId});
87
+ this.setState({ selectedServer });
88
+ };
89
+
90
+ componentDidMount() {
91
+ this.props.actions.getFTPServers();
92
+ const query = {
93
+ layout: 'SMS',
94
+ type: 'TAG',
95
+ context: 'outbound',
96
+ embedded: 'embedded',
97
+ };
98
+ this.props.globalActions.fetchSchemaForEntity(query);
99
+ }
100
+
101
+ componentWillReceiveProps(nextProps) {
102
+ if (
103
+ !isEmpty(((nextProps.metaEntities || {}).tags || {}).standard) &&
104
+ !isEqual(((nextProps.metaEntities || {}).tags || {}).standard, ((this.props.metaEntities || {}).tags || {}).standard)
105
+ ) {
106
+ const tags = getTreeStructuredTags(nextProps.metaEntities.tags.standard);
107
+ const tagsWithOffers = this.mergeOfferToTags({ tags, offerDetails: nextProps.selectedOfferDetails });
108
+ this.setState({ tagsTree: tagsWithOffers}, () => this.disableUsedColumns());
109
+ }
110
+
111
+ if (
112
+ nextProps.mode === 'edit' &&
113
+ (nextProps.FTP || {}).FTPServers.length &&
114
+ !isEqual((nextProps.FTP || {}).FTPServers, (this.props.FTP || {}).FTPServers)
115
+ ) {
116
+ this.setFTPServerInState(nextProps.FTP.FTPServers);
117
+ }
118
+ }
119
+ mergeOfferToTags = ({ tags, offerDetails = []}) => {
120
+ const offerTags = [];
121
+ offerDetails.map((offer) => {
122
+ const { couponName, couponId, couponSeriesId, description } = offer;
123
+ const obj = {
124
+ title: couponName || description,
125
+ value: `{{voucher(${couponId || couponSeriesId})}}`,
126
+ };
127
+ offerTags.push(obj);
128
+ return null;
129
+ });
130
+ return concat(tags, offerTags);
131
+ };
132
+
133
+ onChangeFTPServer = (e) => {
134
+ const { value } = e.target;
135
+ const { FTPServers = [] } = this.props.FTP || {};
136
+ const selectedServer = find(FTPServers, {id: value});
137
+ this.setState({ selectedServer, viewMode: UIView.configureMessage});
138
+ };
139
+
140
+ onTagSelect = (data) => {
141
+ this.insertAtCursor(`{{${data}}}`);
142
+ };
143
+
144
+ setInputRef = (textarea) => {
145
+ this.textArea = textarea;
146
+ };
147
+
148
+ getServerSelectionContent = () => {
149
+ const { getFTPServersInProgress } = this.props.FTP || {};
150
+ const { formatMessage } = this.props.intl;
151
+ const serverOptions = this.getServerOptions();
152
+ const { selectedServer } = this.state;
153
+ return (
154
+ <CapSpin spinning={getFTPServersInProgress}>
155
+ <CapHeading
156
+ type="h3"
157
+ className="select-account-header"
158
+ >
159
+ {formatMessage(messages.ftpServer)}
160
+ </CapHeading>
161
+ <CapHeading type="h6" className="select-account-header">
162
+ {formatMessage(messages.ftpServerDesc)}
163
+ <CapLink
164
+ title={formatMessage(messages.manageFTPSettings)}
165
+ className="select-server"
166
+ onClick={this.handleManageFTPSettings}
167
+ />
168
+ </CapHeading>
169
+ <CapRadioCard
170
+ className="select-account"
171
+ onChange={this.onChangeFTPServer}
172
+ panes={serverOptions}
173
+ cardHeight="48px"
174
+ cardWidth="276px"
175
+ size="small"
176
+ defaultValue={selectedServer}
177
+ selected={selectedServer}
178
+ />
179
+ </CapSpin>
180
+ );
181
+ };
182
+
183
+ getServerOptions = () => {
184
+ const { FTPServers = [] } = this.props.FTP || {};
185
+ return (FTPServers).map(({
186
+ // eslint-disable-next-line camelcase
187
+ ftp_tag = '',
188
+ id,
189
+ }) => ({
190
+ key: ftp_tag,
191
+ value: id,
192
+ title: ftp_tag,
193
+ icon: <CapIcon.CapIconAvatar text={ftp_tag[0]} width="auto" height="2rem"/>,
194
+ }));
195
+ };
196
+
197
+ getMessageContent = () => {
198
+ const { messageContent } = this.state;
199
+ const { formatMessage } = this.props.intl;
200
+ const { metaEntities, selectedOfferDetails } = this.props;
201
+ const tagsRaw = metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
202
+ return (
203
+ <CapRow>
204
+ <CapColumn span={11}>
205
+ <div className="message-content-title">
206
+ <TagList
207
+ className="FTP-tagList"
208
+ label={formatMessage(messages.addLabel)}
209
+ tags={tagsRaw}
210
+ onTagSelect={this.onTagSelect}
211
+ selectedOfferDetails={selectedOfferDetails}
212
+ />
213
+ <CapInput.TextArea
214
+ setInputRef={this.setInputRef}
215
+ label={formatMessage(messages.messageHeader)}
216
+ onChange={this.updateMessageBody}
217
+ value={messageContent}
218
+ />
219
+ </div>
220
+ </CapColumn>
221
+ </CapRow>
222
+ );
223
+ };
224
+
225
+ getCsvColumns = () => {
226
+ const { headerTagList = [], tagsTree = [] } = this.state;
227
+ const { formatMessage } = this.props.intl;
228
+ const headerTagElements = headerTagList.map((item, index) => (
229
+ index !== 0 && <div className="column-selection-container">
230
+ <CapTreeSelect
231
+ dropdownClassName="select-csv-col-dropdown"
232
+ treeData={tagsTree}
233
+ className="select-csv-column"
234
+ value={!isEmpty(item.header) ? item.header : formatMessage(messages.selectTag)}
235
+ onChange={(option, label) => this.selectCsvColumn(option, label, index)}
236
+ />
237
+ <CapIcon
238
+ type="close"
239
+ size="m"
240
+ onClick={() => this.removeCsvColumn(item)}
241
+ className="remove-csv-column"
242
+ />
243
+ </div>
244
+ ));
245
+ headerTagElements.unshift(
246
+ <CapLabelInline
247
+ className="column-selection-container-cid"
248
+ type="label2"
249
+ style={{fontSize: 14, fontWeight: 500}}
250
+ >
251
+ <FormattedMessage {...messages.customerID}/>
252
+ </CapLabelInline>
253
+ );
254
+ return headerTagElements;
255
+ };
256
+
257
+ getConfigureCsvContent = () => {
258
+ const { formatMessage } = this.props.intl;
259
+ return (
260
+ <>
261
+ <CapRow className="configure-csv-container">
262
+ <CapColumn span={8} className="configure-csv">
263
+ <CapHeader
264
+ title={formatMessage(messages.configureCsv)}
265
+ size="regular"
266
+ className="select-account-header"
267
+ />
268
+ </CapColumn>
269
+ <CapColumn span={3} offset={13} className="add-column">
270
+ <CapButton
271
+ type="flat"
272
+ isAddBtn
273
+ onClick={this.addNewColumn}
274
+ >
275
+ <FormattedMessage {...messages.addColumn} />
276
+ </CapButton>
277
+ </CapColumn>
278
+ </CapRow>
279
+ {this.getCsvColumns()}
280
+ </>
281
+ );
282
+ }
283
+
284
+ getConfigureMessageContent = () => {
285
+ const { formatMessage } = this.props.intl;
286
+ const { selectedServer = {}, headerTagList = [] } = this.state;
287
+ const emptyHeaderTagList = filter(headerTagList, {tag: ''});
288
+ const disableDoneButton = isEmpty(selectedServer) || emptyHeaderTagList.length;
289
+ return (
290
+ <CapSpin spinning={false}>
291
+ <div className="scrollable-section">
292
+ <CapHeader
293
+ title={formatMessage(messages.ftpServer)}
294
+ size="regular"
295
+ className="select-account-header"
296
+ />
297
+ <div className="selected-server-name">
298
+ <CapIcon.CapIconAvatar
299
+ className="server-avtar"
300
+ text={((selectedServer || {}).ftp_tag || [])[0]}
301
+ width="auto"
302
+ height="2rem"
303
+ />
304
+ <CapHeading
305
+ type="h4"
306
+ className="server-name"
307
+ >
308
+ {(selectedServer || {}).ftp_tag}
309
+ </CapHeading>
310
+ <CapLink
311
+ title={formatMessage(messages.change)}
312
+ className="server-change"
313
+ onClick={this.showAccountSelection}
314
+ />
315
+ </div>
316
+ {this.getConfigureCsvContent()}
317
+ <div className="ftp-message-container">
318
+ <CapHeader
319
+ title={formatMessage(messages.messageContent)}
320
+ description={formatMessage(messages.optional)}
321
+ size="regular"
322
+ inline
323
+ />
324
+ {this.getMessageContent()}
325
+ </div>
326
+ </div>
327
+ <CapButton
328
+ type="primary"
329
+ disabled={disableDoneButton}
330
+ onClick={this.saveFtpMessage}
331
+ >
332
+ <FormattedMessage {...messages.done} />
333
+ </CapButton>
334
+ </CapSpin>
335
+ );
336
+ };
337
+
338
+ getColumnsHeaderPreview = (headerTagList) => (
339
+ <div className="ftp-preview-data-col-container">
340
+ { headerTagList.map((tag) => (
341
+ <CapTag type="static" className="ftp-preview-data-col-tag">
342
+ {tag.header}
343
+ </CapTag>
344
+ ))}
345
+ </div>
346
+ );
347
+
348
+ getPreviewModeContents = () => {
349
+ const { contents = [] } = this.props;
350
+ const { messageBody = '', headerTagList = [] } = contents[0] || {};
351
+ return (
352
+ <div className="ftp-preview-container">
353
+ <CapHeading className="ftp-preview-message-title" type="h3">
354
+ <FormattedMessage {...messages.messageContent}/>
355
+ </CapHeading>
356
+ <CapLabel type="label2" className="ftp-preview-message-content">
357
+ {messageBody}
358
+ </CapLabel>
359
+ <CapHeading className="ftp-preview-data-title" type="h3">
360
+ <FormattedMessage {...messages.dataPreview}/>
361
+ </CapHeading>
362
+ {this.getColumnsHeaderPreview(headerTagList)}
363
+ </div>
364
+ );
365
+ };
366
+
367
+ showAccountSelection = () => {
368
+ this.setState({ viewMode: UIView.serverSelection, selectedServer: null, headerTagList: [], messageContent: ''});
369
+ };
370
+
371
+ selectCsvColumn = (option, label, index) => {
372
+ const { headerTagList } = this.state;
373
+ const clonedHeaderTagList = cloneDeep(headerTagList);
374
+ const obj = {
375
+ header: label[0],
376
+ tag: option,
377
+ };
378
+ clonedHeaderTagList[index] = obj;
379
+ this.setState({ headerTagList: clonedHeaderTagList}, () => this.disableUsedColumns());
380
+ };
381
+
382
+ disableUsedColumns = () => {
383
+ const { headerTagList = [] } = this.state;
384
+ const { selectedOfferDetails = [], metaEntities = {} } = this.props;
385
+ const tags = getTreeStructuredTags((metaEntities.tags || {}).standard);
386
+ const tagsWithOffers = this.mergeOfferToTags({ tags, offerDetails: selectedOfferDetails });
387
+ const clonnedTagsTree = cloneDeep(tagsWithOffers);
388
+ headerTagList.map((headerTag) => {
389
+ const pos = findIndex(clonnedTagsTree, {value: headerTag.tag});
390
+ if (pos > -1) {
391
+ clonnedTagsTree[pos] = {
392
+ disabled: true,
393
+ ...clonnedTagsTree[pos],
394
+ };
395
+ }
396
+ return null;
397
+ });
398
+ this.setState({tagsTree: clonnedTagsTree});
399
+ };
400
+
401
+ handleManageFTPSettings = () => {
402
+ const path = `/exports/configurations`;
403
+ window.open(path, '_self');
404
+ };
405
+
406
+ removeCsvColumn = (item) => {
407
+ const { headerTagList } = this.state;
408
+ const clonedHeaderTagList = cloneDeep(headerTagList);
409
+ const index = findIndex(headerTagList, (val) => val.tag === item.tag);
410
+ clonedHeaderTagList.splice(index, 1);
411
+ this.setState({ headerTagList: clonedHeaderTagList}, () => this.disableUsedColumns());
412
+ };
413
+
414
+ addNewColumn = () => {
415
+ const { headerTagList } = this.state;
416
+ const clonedHeaderTagList = cloneDeep(headerTagList);
417
+ const newCsvObj = {
418
+ header: '',
419
+ tag: '',
420
+ };
421
+ clonedHeaderTagList.push(newCsvObj);
422
+ this.setState({ headerTagList: clonedHeaderTagList});
423
+ };
424
+
425
+ insertAtCursor = (myValue) => {
426
+ this.textArea.focus();
427
+ const textAreaRef = this.textArea.textAreaRef;
428
+ let pos = textAreaRef.selectionEnd + myValue.length;
429
+ //IE support
430
+ let newMessage = textAreaRef.value + myValue;
431
+ if (document.selection) {
432
+ this.textArea.focus();
433
+ const sel = document.selection.createRange();
434
+ sel.text = myValue;
435
+ } else if (textAreaRef.selectionStart || textAreaRef.selectionStart === '0') { //MOZILLA and others
436
+ const startPos = textAreaRef.selectionStart;
437
+ const endPos = textAreaRef.selectionEnd;
438
+ newMessage = textAreaRef.value.substring(0, startPos) + myValue + textAreaRef.value.substring(endPos, textAreaRef.value.length);
439
+ pos = startPos + myValue.length;
440
+ }
441
+ this.setState({
442
+ messageContent: newMessage,
443
+ }, () => {
444
+ textAreaRef.selectionStart = pos;
445
+ textAreaRef.selectionEnd = pos;
446
+ });
447
+ };
448
+
449
+ updateMessageBody = (e) => {
450
+ this.setState({
451
+ messageContent: e.target.value,
452
+ });
453
+ };
454
+
455
+ saveFtpMessage = () => {
456
+ const { onFTPSubmit } = this.props;
457
+ const { FTPServers = [] } = this.props.FTP || {};
458
+ const { selectedServer = {}, headerTagList = [], messageContent = '' } = this.state;
459
+ onFTPSubmit({
460
+ channel: "FTP",
461
+ endPointConfigs: {
462
+ endPointType: "FTP",
463
+ ftpConfigId: selectedServer.id,
464
+ },
465
+ headerTagList,
466
+ messageContent,
467
+ FTPServers,
468
+ });
469
+ };
470
+
471
+ render() {
472
+ const { viewMode } = this.state;
473
+ const { serverSelection } = UIView;
474
+ const { mode } = this.props;
475
+ return (
476
+ mode === 'preview' ?
477
+ this.getPreviewModeContents() :
478
+ (viewMode === serverSelection ?
479
+ this.getServerSelectionContent() :
480
+ this.getConfigureMessageContent()
481
+ )
482
+ );
483
+ }
484
+ }
485
+
486
+ FTP.propTypes = {
487
+ actions: PropTypes.object.isRequired,
488
+ FTP: PropTypes.object,
489
+ intl: intlShape.isRequired,
490
+ globalActions: PropTypes.object,
491
+ onFTPSubmit: PropTypes.func,
492
+ messageDetails: PropTypes.object,
493
+ mode: PropTypes.string,
494
+ contents: PropTypes.array,
495
+ metaEntities: PropTypes.object,
496
+ selectedOfferDetails: PropTypes.array,
497
+ };
498
+
499
+
500
+ function mapDispatchToProps(dispatch) {
501
+ return {
502
+ actions: bindActionCreators(actions, dispatch),
503
+ globalActions: bindActionCreators(globalActions, dispatch),
504
+ };
505
+ }
506
+
507
+ const mapStateToProps = createStructuredSelector({
508
+ FTP: makeSelectFTP(),
509
+ metaEntities: makeSelectMetaEntities(),
510
+ });
511
+
512
+ export default withCreatives({
513
+ WrappedComponent: FTP,
514
+ mapStateToProps,
515
+ mapDispatchToProps,
516
+ suserAuth: true,
517
+ });
@@ -0,0 +1,60 @@
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ export default defineMessages({
4
+ ftpServerDesc: {
5
+ id: 'creatives.containersV2.FTP.ftpServerDesc',
6
+ defaultMessage: 'Select the FTP server to send the message data. To add another FTP location, ',
7
+ },
8
+ ftpServer: {
9
+ id: 'creatives.containersV2.FTP.ftpServer',
10
+ defaultMessage: 'FTP Server',
11
+ },
12
+ configureCsv: {
13
+ id: 'creatives.containersV2.FTP.configureCsv',
14
+ defaultMessage: 'Configure CSV',
15
+ },
16
+ addColumn: {
17
+ id: 'creatives.containersV2.FTP.addColumn',
18
+ defaultMessage: 'Add column',
19
+ },
20
+ selectTag: {
21
+ id: 'creatives.containersV2.FTP.selectTag',
22
+ defaultMessage: 'Select tag',
23
+ },
24
+ done: {
25
+ id: 'creatives.containersV2.FTP.done',
26
+ defaultMessage: 'Done',
27
+ },
28
+ customerID: {
29
+ id: 'creatives.containersV2.FTP.customerID',
30
+ defaultMessage: 'Customer ID',
31
+ },
32
+ change: {
33
+ id: 'creatives.containersV2.FTP.change',
34
+ defaultMessage: 'Change',
35
+ },
36
+ manageFTPSettings: {
37
+ id: 'creatives.containersV2.FTP.manageFTPSettings',
38
+ defaultMessage: 'manage FTP settings',
39
+ },
40
+ addLabel: {
41
+ id: 'creatives.containersV2.FTP.addLabel',
42
+ defaultMessage: 'Add Labels',
43
+ },
44
+ messageHeader: {
45
+ id: 'creatives.containersV2.FTP.messageHeader',
46
+ defaultMessage: 'Message',
47
+ },
48
+ messageContent: {
49
+ id: 'creatives.containersV2.FTP.messageContent',
50
+ defaultMessage: 'Message Content',
51
+ },
52
+ optional: {
53
+ id: 'creatives.containersV2.FTP.optional',
54
+ defaultMessage: '(optional)',
55
+ },
56
+ dataPreview: {
57
+ id: 'creatives.containersV2.FTP.dataPreview',
58
+ defaultMessage: 'Data Preview',
59
+ },
60
+ });
@@ -0,0 +1,32 @@
1
+ import { fromJS } from 'immutable';
2
+ import * as types from './constants';
3
+
4
+ const initialState = fromJS({
5
+ getFTPServersInProgress: false,
6
+ FTPServers: [],
7
+ getFTPServersError: false,
8
+ });
9
+
10
+ function createReducer(state = initialState, action) {
11
+ switch (action.type) {
12
+ case types.GET_FTP_SERVERS_REQUEST:
13
+ return state
14
+ .set('getFTPServersInProgress', true)
15
+ .set('FTPServers', [])
16
+ .set('getFTPServersInProgress', true);
17
+ case types.GET_FTP_SERVERS_SUCCESS:
18
+ return state
19
+ .set('getFTPServersInProgress', false)
20
+ .set('FTPServers', action.data)
21
+ .set('getFTPServersInProgress', false);
22
+ case types.GET_FTP_SERVERS_FAILURE:
23
+ return state
24
+ .set('getFTPServersInProgress', false)
25
+ .set('FTPServers', [])
26
+ .set('getFTPServersInProgress', true);
27
+ default:
28
+ return state;
29
+ }
30
+ }
31
+
32
+ export default createReducer;
@@ -0,0 +1,26 @@
1
+ import { take, cancel, call, put, takeLatest } from 'redux-saga/effects';
2
+ import { LOCATION_CHANGE } from 'react-router-redux';
3
+ import * as Api from '../../services/api';
4
+ import * as types from './constants';
5
+
6
+ // Individual exports for testing
7
+ export function* getFTPServers() {
8
+ try {
9
+ const result = yield call(Api.getFTPServers);
10
+ yield put({ type: types.GET_FTP_SERVERS_SUCCESS, data: result.response });
11
+ } catch (error) {
12
+ yield put({ type: types.GET_FTP_SERVERS_FAILURE, error });
13
+ }
14
+ }
15
+
16
+
17
+ function* watchGetFTPServers() {
18
+ const watcher = yield takeLatest(types.GET_FTP_SERVERS_REQUEST, getFTPServers);
19
+ yield take(LOCATION_CHANGE);
20
+ yield cancel(watcher);
21
+ }
22
+
23
+ // All sagas to be loaded
24
+ export default [
25
+ watchGetFTPServers,
26
+ ];
@@ -0,0 +1,20 @@
1
+ import { createSelector } from 'reselect';
2
+ import { fromJS } from 'immutable';
3
+
4
+ const selectFTPMessageCreateDomain = () => (state) => state.get('FTP') || fromJS({});
5
+ const selectCapDomain = (state) => state.get('cap');
6
+
7
+ const makeSelectFTP = () => createSelector(
8
+ selectFTPMessageCreateDomain(),
9
+ (substate = fromJS({})) => substate.toJS()
10
+ );
11
+ const makeSelectMetaEntities = () => createSelector(
12
+ selectCapDomain,
13
+ (globalState = fromJS({})) => globalState.get('metaEntities')
14
+ );
15
+
16
+
17
+ export {
18
+ makeSelectFTP,
19
+ makeSelectMetaEntities,
20
+ };