@truedat/cx 4.41.2 → 4.41.5

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 (108) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +4 -4
  3. package/src/jobs/components/Job.js +11 -11
  4. package/src/jobs/components/JobRoutes.js +2 -2
  5. package/src/jobs/components/JobRow.js +7 -3
  6. package/src/jobs/components/Jobs.js +16 -28
  7. package/src/jobs/components/JobsTable.js +80 -92
  8. package/src/jobs/components/JobsView.js +23 -0
  9. package/src/jobs/components/SourceJobs.js +29 -0
  10. package/src/jobs/components/__tests__/JobRow.spec.js +44 -23
  11. package/src/jobs/components/__tests__/Jobs.spec.js +33 -8
  12. package/src/jobs/components/__tests__/JobsTable.spec.js +50 -81
  13. package/src/jobs/components/__tests__/JobsView.spec.js +39 -0
  14. package/src/jobs/components/__tests__/SourceJobs.spec.js +38 -0
  15. package/src/jobs/components/__tests__/__snapshots__/Job.spec.js.snap +3 -4
  16. package/src/jobs/components/__tests__/__snapshots__/JobRow.spec.js.snap +40 -32
  17. package/src/jobs/components/__tests__/__snapshots__/Jobs.spec.js.snap +136 -35
  18. package/src/jobs/components/__tests__/__snapshots__/JobsTable.spec.js.snap +104 -227
  19. package/src/jobs/components/__tests__/__snapshots__/JobsView.spec.js.snap +167 -0
  20. package/src/jobs/components/__tests__/__snapshots__/SourceJobs.spec.js.snap +115 -0
  21. package/src/jobs/components/index.js +1 -3
  22. package/src/jobs/selectors/index.js +4 -2
  23. package/src/jobs/selectors/{getJobColumns.js → jobColumnsSelector.js} +11 -27
  24. package/src/jobs/selectors/jobRowsSelector.js +14 -0
  25. package/src/jobs/selectors/sourceJobColumnsSelector.js +35 -0
  26. package/src/messages/en.js +9 -1
  27. package/src/messages/es.js +9 -1
  28. package/src/reducers/cxMessage.js +15 -1
  29. package/src/sources/api/fragments.js +48 -0
  30. package/src/sources/api/mutations.js +51 -0
  31. package/src/sources/api/queries.js +52 -0
  32. package/src/sources/api.js +1 -2
  33. package/src/sources/components/EditSource.js +5 -29
  34. package/src/sources/components/JobForm.js +2 -2
  35. package/src/sources/components/NewJob.js +10 -23
  36. package/src/sources/components/Source.js +12 -76
  37. package/src/sources/components/SourceActions.js +13 -21
  38. package/src/sources/components/SourceBreadcrumbs.js +1 -1
  39. package/src/sources/components/SourceConfiguration.js +60 -0
  40. package/src/sources/components/SourceDetail.js +90 -0
  41. package/src/sources/components/SourceForm.js +50 -53
  42. package/src/sources/components/SourceHeader.js +29 -0
  43. package/src/sources/components/SourceRoutes.js +25 -89
  44. package/src/sources/components/SourceSelector.js +53 -44
  45. package/src/sources/components/SourceTabs.js +54 -0
  46. package/src/sources/components/Sources.js +7 -20
  47. package/src/sources/components/SourcesTable.js +3 -4
  48. package/src/sources/components/__tests__/EditSource.spec.js +1 -2
  49. package/src/sources/components/__tests__/Source.spec.js +1 -1
  50. package/src/sources/components/__tests__/SourceActions.spec.js +3 -1
  51. package/src/sources/components/__tests__/SourceDetail.spec.js +45 -0
  52. package/src/sources/components/__tests__/SourceForm.spec.js +16 -31
  53. package/src/sources/components/__tests__/SourceHeader.spec.js +16 -0
  54. package/src/sources/components/__tests__/SourceSelector.spec.js +44 -0
  55. package/src/sources/components/__tests__/SourceTabs.spec.js +21 -0
  56. package/src/sources/components/__tests__/Sources.spec.js +5 -2
  57. package/src/sources/components/__tests__/__snapshots__/EditSource.spec.js.snap +12 -35
  58. package/src/sources/components/__tests__/__snapshots__/NewSource.spec.js.snap +1 -1
  59. package/src/sources/components/__tests__/__snapshots__/Source.spec.js.snap +23 -65
  60. package/src/sources/components/__tests__/__snapshots__/SourceActions.spec.js.snap +1 -1
  61. package/src/sources/components/__tests__/__snapshots__/SourceDetail.spec.js.snap +154 -0
  62. package/src/sources/components/__tests__/__snapshots__/SourceForm.spec.js.snap +37 -14
  63. package/src/sources/components/__tests__/__snapshots__/SourceHeader.spec.js.snap +85 -0
  64. package/src/sources/components/__tests__/__snapshots__/SourceSelector.spec.js.snap +68 -0
  65. package/src/sources/components/__tests__/__snapshots__/SourceTabs.spec.js.snap +22 -0
  66. package/src/sources/components/__tests__/__snapshots__/Sources.spec.js.snap +2 -2
  67. package/src/sources/components/__tests__/__snapshots__/SourcesTable.spec.js.snap +5 -5
  68. package/src/sources/components/index.js +1 -4
  69. package/src/sources/reducers/__tests__/sourceRedirect.spec.js +26 -21
  70. package/src/sources/reducers/__tests__/sourceUpdateStatus.spec.js +9 -9
  71. package/src/sources/reducers/index.js +1 -12
  72. package/src/sources/reducers/sourceRedirect.js +20 -10
  73. package/src/sources/reducers/sourceUpdateStatus.js +5 -5
  74. package/src/sources/routines.js +0 -3
  75. package/src/sources/sagas/__tests__/createSource.spec.js +35 -20
  76. package/src/sources/sagas/__tests__/deleteSource.spec.js +29 -14
  77. package/src/sources/sagas/__tests__/disableSource.spec.js +2 -8
  78. package/src/sources/sagas/__tests__/enableSource.spec.js +2 -8
  79. package/src/sources/sagas/__tests__/updateSource.spec.js +33 -15
  80. package/src/sources/sagas/createSource.js +24 -14
  81. package/src/sources/sagas/deleteSource.js +14 -15
  82. package/src/sources/sagas/disableSource.js +1 -17
  83. package/src/sources/sagas/enableSource.js +1 -17
  84. package/src/sources/sagas/index.js +0 -9
  85. package/src/sources/sagas/updateSource.js +18 -15
  86. package/src/sources/components/DynamicSourceForm.js +0 -60
  87. package/src/sources/components/SourceLoader.js +0 -59
  88. package/src/sources/components/SourcesLoader.js +0 -39
  89. package/src/sources/components/__tests__/DynamicSourceForm.spec.js +0 -89
  90. package/src/sources/components/__tests__/SourceLoader.spec.js +0 -54
  91. package/src/sources/components/__tests__/SourcesLoader.spec.js +0 -53
  92. package/src/sources/components/__tests__/__snapshots__/DynamicSourceForm.spec.js.snap +0 -8
  93. package/src/sources/components/__tests__/__snapshots__/SourceLoader.spec.js.snap +0 -3
  94. package/src/sources/components/__tests__/__snapshots__/SourcesLoader.spec.js.snap +0 -3
  95. package/src/sources/reducers/__tests__/source.spec.js +0 -44
  96. package/src/sources/reducers/__tests__/sourceLoading.spec.js +0 -30
  97. package/src/sources/reducers/__tests__/sources.spec.js +0 -37
  98. package/src/sources/reducers/__tests__/sourcesLoading.spec.js +0 -30
  99. package/src/sources/reducers/source.js +0 -35
  100. package/src/sources/reducers/sourceLoading.js +0 -16
  101. package/src/sources/reducers/sources.js +0 -28
  102. package/src/sources/reducers/sourcesLoading.js +0 -16
  103. package/src/sources/sagas/__tests__/fetchSource.spec.js +0 -71
  104. package/src/sources/sagas/__tests__/fetchSources.spec.js +0 -69
  105. package/src/sources/sagas/__tests__/updateSourceConfig.spec.js +0 -73
  106. package/src/sources/sagas/fetchSource.js +0 -30
  107. package/src/sources/sagas/fetchSources.js +0 -26
  108. package/src/sources/sagas/updateSourceConfig.js +0 -29
@@ -0,0 +1,60 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { updateSource } from "../routines";
6
+
7
+ const DynamicFormViewer = React.lazy(() =>
8
+ import("@truedat/df/components/DynamicFormViewer")
9
+ );
10
+
11
+ export const SourceConfiguration = ({
12
+ source,
13
+ updateSource,
14
+ sourceUpdateStatus,
15
+ }) => {
16
+ const [editingField, setEditingField] = useState();
17
+ const [sourceConfig, setSourceConfig] = useState();
18
+ useEffect(() => {
19
+ if (source) {
20
+ setSourceConfig(source?.config);
21
+ }
22
+ }, [source]);
23
+ const template = source?.template;
24
+ const editFunctions = {
25
+ onChange: (e, { name, value }) => {
26
+ e && e.preventDefault();
27
+ setSourceConfig({ ...sourceConfig, [name]: value });
28
+ },
29
+ onCancel: () => setSourceConfig(source?.config),
30
+ onSubmit: (field) =>
31
+ updateSource({
32
+ source: {
33
+ id: source?.id,
34
+ config: _.pick(field)(sourceConfig),
35
+ merge: true,
36
+ },
37
+ }),
38
+ editingField,
39
+ setEditingField,
40
+ updateStatus: sourceUpdateStatus,
41
+ };
42
+
43
+ return _.isEmpty(source) ? null : (
44
+ <DynamicFormViewer
45
+ template={template}
46
+ content={sourceConfig}
47
+ editFunctions={editFunctions}
48
+ />
49
+ );
50
+ };
51
+
52
+ SourceConfiguration.propTypes = {
53
+ source: PropTypes.object.isRequired,
54
+ sourceUpdateStatus: PropTypes.string,
55
+ updateSource: PropTypes.func.isRequired,
56
+ };
57
+
58
+ const mapStateToProps = ({ sourceUpdateStatus }) => ({ sourceUpdateStatus });
59
+
60
+ export default connect(mapStateToProps, { updateSource })(SourceConfiguration);
@@ -0,0 +1,90 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Switch, Route } from "react-router-dom";
4
+ import { Segment } from "semantic-ui-react";
5
+ import {
6
+ SOURCE_EDIT,
7
+ SOURCE_JOB,
8
+ SOURCE_JOBS_NEW,
9
+ SOURCE_JOBS,
10
+ SOURCE,
11
+ } from "@truedat/core/routes";
12
+ import Job from "../../jobs/components/Job";
13
+ import JobLoader from "../../jobs/components/JobLoader";
14
+ import SourceJobs from "../../jobs/components/SourceJobs";
15
+ import EditSource from "./EditSource";
16
+ import NewJob from "./NewJob";
17
+ import SourceConfiguration from "./SourceConfiguration";
18
+ import SourceTabs from "./SourceTabs";
19
+
20
+ export const SourceDetail = ({ source }) => (
21
+ <Switch>
22
+ <Route
23
+ path={SOURCE_JOBS_NEW}
24
+ exact
25
+ render={() => (
26
+ <>
27
+ <SourceTabs source={source} />
28
+ <Segment attached="bottom">
29
+ <NewJob source={source} />
30
+ </Segment>
31
+ </>
32
+ )}
33
+ />
34
+ <Route
35
+ path={SOURCE_JOB}
36
+ exact
37
+ render={() => (
38
+ <>
39
+ <JobLoader />
40
+ <SourceTabs source={source} />
41
+ <Segment attached="bottom">
42
+ <Job />
43
+ </Segment>
44
+ </>
45
+ )}
46
+ />
47
+ <Route
48
+ path={SOURCE_JOBS}
49
+ exact
50
+ render={() => (
51
+ <>
52
+ <SourceTabs source={source} />
53
+ <Segment attached="bottom">
54
+ <SourceJobs source={source} />
55
+ </Segment>
56
+ </>
57
+ )}
58
+ />
59
+ <Route
60
+ path={SOURCE_EDIT}
61
+ exact
62
+ render={() => (
63
+ <>
64
+ <SourceTabs source={source} />
65
+ <Segment attached="bottom">
66
+ <EditSource source={source} />
67
+ </Segment>
68
+ </>
69
+ )}
70
+ />
71
+ <Route
72
+ path={SOURCE}
73
+ exact
74
+ render={() => (
75
+ <>
76
+ <SourceTabs source={source} />
77
+ <Segment attached="bottom">
78
+ <SourceConfiguration source={source} />
79
+ </Segment>
80
+ </>
81
+ )}
82
+ />
83
+ </Switch>
84
+ );
85
+
86
+ SourceDetail.propTypes = {
87
+ source: PropTypes.object.isRequired,
88
+ };
89
+
90
+ export default SourceDetail;
@@ -2,13 +2,16 @@ import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Button, Form, Label } from "semantic-ui-react";
5
- import { compose } from "redux";
6
- import { connect } from "react-redux";
7
5
  import { injectIntl, FormattedMessage } from "react-intl";
8
- import { HistoryBackButton } from "@truedat/core/components";
9
- import { selectTemplate } from "@truedat/df/routines";
6
+ import { useQuery } from "@apollo/client";
7
+ import { HistoryBackButton, Loading } from "@truedat/core/components";
8
+ import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
10
9
  import { applyTemplate, validateContent } from "@truedat/df/utils";
11
- import DynamicSourceForm from "./DynamicSourceForm";
10
+ import { TEMPLATES_QUERY } from "../api/queries";
11
+
12
+ const DynamicForm = React.lazy(() =>
13
+ import("@truedat/df/components/DynamicForm")
14
+ );
12
15
 
13
16
  const staticFields = ["external_id"];
14
17
 
@@ -29,13 +32,10 @@ const isValid = _.conforms({
29
32
 
30
33
  export class SourceForm extends React.Component {
31
34
  static propTypes = {
32
- applyTemplate: PropTypes.func,
33
35
  intl: PropTypes.object,
34
36
  source: PropTypes.object,
35
- template: PropTypes.object,
36
37
  templates: PropTypes.array,
37
- templatesLoaded: PropTypes.bool,
38
- selectTemplate: PropTypes.func,
38
+ templatesLoading: PropTypes.bool,
39
39
  onSubmit: PropTypes.func,
40
40
  };
41
41
 
@@ -47,7 +47,8 @@ export class SourceForm extends React.Component {
47
47
  if (!_.isEmpty(source)) {
48
48
  const template = _.find(_.propEq("name")(source.type))(templates);
49
49
  this.setState({
50
- external_id: source.external_id,
50
+ id: source.id,
51
+ external_id: source.externalId,
51
52
  type: source.type,
52
53
  content: applyTemplate(template)(source.config),
53
54
  template: template,
@@ -55,22 +56,15 @@ export class SourceForm extends React.Component {
55
56
  }
56
57
  }
57
58
 
58
- componentDidUpdate() {
59
- const { applyTemplate, template } = this.props;
60
- const { type, content } = this.state;
61
-
62
- if (_.has("name")(template) && !_.propEq("name", type)(template))
63
- this.setState({
64
- type: _.prop("name")(template),
65
- content: applyTemplate(content),
66
- contentErrors: validateContent(template)(content),
67
- });
68
- }
69
-
70
59
  handleTemplateSelected = (e, data) => {
71
60
  const { value } = data;
72
- const { selectTemplate } = this.props;
73
- selectTemplate({ id: value });
61
+ const content = this.state.content;
62
+ const template = _.find(_.propEq("name", value))(this.props.templates);
63
+ this.setState({
64
+ type: value,
65
+ template: template,
66
+ content: applyTemplate(template)(content),
67
+ });
74
68
  };
75
69
 
76
70
  handleChange = (e, { name, value }) => {
@@ -82,7 +76,7 @@ export class SourceForm extends React.Component {
82
76
  };
83
77
 
84
78
  handleContentChange = (content) => {
85
- const contentErrors = validateContent(this.props.template)(content);
79
+ const contentErrors = validateContent(this.state.template)(content);
86
80
  this.setState({ content, contentErrors });
87
81
  };
88
82
 
@@ -90,9 +84,13 @@ export class SourceForm extends React.Component {
90
84
 
91
85
  handleSubmit = (e) => {
92
86
  e.preventDefault();
93
- const { applyTemplate, onSubmit } = this.props;
87
+ const { onSubmit } = this.props;
94
88
  const source = _.pick(["id", "external_id", "type"])(this.state);
95
- const config = _.flow(_.prop("content"), applyTemplate)(this.state);
89
+ const template = this.state.template;
90
+ const config = _.flow(
91
+ _.prop("content"),
92
+ applyTemplate(template)
93
+ )(this.state);
96
94
  onSubmit({ source: { ...source, config: config } });
97
95
  };
98
96
 
@@ -100,18 +98,16 @@ export class SourceForm extends React.Component {
100
98
  const {
101
99
  intl: { formatMessage },
102
100
  source,
103
- templatesLoaded,
101
+ templatesLoading,
104
102
  templates,
105
- template,
106
103
  } = this.props;
107
104
 
108
- const { external_id, content, type } = this.state;
105
+ const { external_id, content, template, type } = this.state;
109
106
 
110
- const typeOptions = templates.map(({ name, id }, i) => ({
111
- key: i,
112
- value: id,
113
- text: name,
114
- }));
107
+ const typeOptions = _.flow(
108
+ _.map(({ id, label, name }) => ({ key: id, value: name, text: label })),
109
+ _.sortBy(accentInsensitivePathOrder("text"))
110
+ )(templates);
115
111
 
116
112
  return (
117
113
  <Form>
@@ -142,23 +138,22 @@ export class SourceForm extends React.Component {
142
138
  selection
143
139
  required
144
140
  options={typeOptions}
145
- loading={!templatesLoaded}
141
+ loading={templatesLoading}
146
142
  onChange={this.handleTemplateSelected}
147
- value={_.prop("id")(template)}
143
+ value={_.prop("name")(template)}
148
144
  disabled={!_.isEmpty(source)}
149
145
  />
150
146
  </Form.Field>
151
147
 
152
- {type && templatesLoaded && (
148
+ {type && !templatesLoading && (
153
149
  <Form.Field>
154
150
  <label className="label">
155
151
  <FormattedMessage id="source.config.label" />
156
152
  </label>
157
-
158
- <DynamicSourceForm
159
- name="dynamicForm"
160
- handleContentChange={this.handleContentChange}
161
- dfContent={content}
153
+ <DynamicForm
154
+ onChange={this.handleContentChange}
155
+ content={content}
156
+ template={template}
162
157
  />
163
158
  </Form.Field>
164
159
  )}
@@ -180,14 +175,16 @@ export class SourceForm extends React.Component {
180
175
  }
181
176
  }
182
177
 
183
- const mapStateToProps = ({ templatesLoading, templates, template }) => ({
184
- applyTemplate: applyTemplate(template),
185
- templatesLoaded: !templatesLoading,
186
- templates,
187
- template,
188
- });
178
+ export const SourceFormLoader = (props) => {
179
+ const { loading, error, data } = useQuery(TEMPLATES_QUERY, {
180
+ variables: { scope: "cx" },
181
+ });
182
+ if (error) return null;
183
+ if (loading) return <Loading />;
184
+ const templates = data?.templates || [];
185
+ return (
186
+ <SourceForm templatesLoading={loading} templates={templates} {...props} />
187
+ );
188
+ };
189
189
 
190
- export default compose(
191
- injectIntl,
192
- connect(mapStateToProps, { selectTemplate })
193
- )(SourceForm);
190
+ export default injectIntl(SourceFormLoader);
@@ -0,0 +1,29 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Header, Icon, Grid } from "semantic-ui-react";
5
+ import SourceActions from "./SourceActions";
6
+
7
+ export const SourceHeader = ({ source }) =>
8
+ _.isEmpty(source) ? null : (
9
+ <Grid>
10
+ <Grid.Column width={8}>
11
+ <Header as="h2">
12
+ <Icon circular name="plug" />
13
+ <Header.Content>
14
+ {source.externalId}
15
+ <Header.Subheader>{source.type}</Header.Subheader>
16
+ </Header.Content>
17
+ </Header>
18
+ </Grid.Column>
19
+ <Grid.Column width={8} textAlign="right">
20
+ <SourceActions source={source} />
21
+ </Grid.Column>
22
+ </Grid>
23
+ );
24
+
25
+ SourceHeader.propTypes = {
26
+ source: PropTypes.object.isRequired,
27
+ };
28
+
29
+ export default SourceHeader;
@@ -1,108 +1,44 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
- import PropTypes from "prop-types";
4
- import { connect } from "react-redux";
5
- import { Segment } from "semantic-ui-react";
6
- import { Route, Switch } from "react-router-dom";
7
- import { Unauthorized } from "@truedat/core/components";
3
+ import { Route, Switch, useParams } from "react-router-dom";
4
+ import { useQuery } from "@apollo/client";
5
+ import { Unauthorized, Loading } from "@truedat/core/components";
8
6
  import { useAuthorized } from "@truedat/core/hooks";
9
- import {
10
- SOURCE,
11
- SOURCES,
12
- SOURCES_NEW,
13
- SOURCE_EDIT,
14
- SOURCE_JOBS_NEW,
15
- } from "@truedat/core/routes";
7
+ import { SOURCE, SOURCES, SOURCES_NEW } from "@truedat/core/routes";
8
+ import { SOURCE_QUERY } from "../api/queries";
16
9
  import NewSource from "./NewSource";
17
- import NewJob from "./NewJob";
18
- import EditSource from "./EditSource";
19
- import Sources from "./Sources";
20
- import SourceLoader from "./SourceLoader";
21
10
  import Source from "./Source";
11
+ import Sources from "./Sources";
22
12
 
23
- const TemplateLoader = React.lazy(() =>
24
- import("@truedat/df/templates/components/TemplateLoader")
25
- );
13
+ export const SourceLoader = () => {
14
+ const { sourceId } = useParams();
15
+ const { loading, error, data } = useQuery(SOURCE_QUERY, {
16
+ variables: { sourceId },
17
+ });
18
+ if (error) return null;
19
+ if (loading) return <Loading />;
20
+ const source = data?.source;
21
+ return _.isEmpty(source) ? null : <Source source={source} />;
22
+ };
26
23
 
27
- const TemplatesLoader = React.lazy(() =>
28
- import("@truedat/df/templates/components/TemplatesLoader")
24
+ export const AuthorizedSourcesRoutes = () => (
25
+ <Switch>
26
+ <Route exact path={SOURCES_NEW} render={() => <NewSource />} />
27
+ <Route exact path={SOURCES} render={() => <Sources />} />
28
+ <Route path={SOURCE} render={() => <SourceLoader />} />
29
+ </Switch>
29
30
  );
30
31
 
31
- export const SourceRoutes = ({ source, sourceLoading, templatesLoading }) => {
32
+ export const SourceRoutes = () => {
32
33
  const authorized = useAuthorized();
33
34
  return (
34
35
  <Route
35
36
  path={SOURCES}
36
37
  render={() =>
37
- authorized ? (
38
- <Switch>
39
- <Route
40
- exact
41
- path={SOURCE_JOBS_NEW}
42
- render={() => (
43
- <>
44
- <SourceLoader />
45
- <NewJob />
46
- </>
47
- )}
48
- />
49
- <Route
50
- exact
51
- path={SOURCES_NEW}
52
- render={() => (
53
- <>
54
- <TemplatesLoader scope="cx" />
55
- <TemplateLoader />
56
- <NewSource />
57
- </>
58
- )}
59
- />
60
- <Route
61
- exact
62
- path={SOURCE_EDIT}
63
- render={() => (
64
- <>
65
- <TemplatesLoader scope="cx" />
66
- <SourceLoader />
67
- {!_.isEmpty(source) &&
68
- !sourceLoading &&
69
- !templatesLoading && <EditSource />}
70
- </>
71
- )}
72
- />
73
- <Route
74
- exact
75
- path={SOURCE}
76
- render={() => (
77
- <>
78
- <SourceLoader />
79
- <Segment>
80
- <TemplatesLoader scope="cx" />
81
- {!templatesLoading && <Source />}
82
- </Segment>
83
- </>
84
- )}
85
- />
86
- <Route exact path={SOURCES} render={() => <Sources />} />
87
- </Switch>
88
- ) : (
89
- <Unauthorized />
90
- )
38
+ authorized ? <AuthorizedSourcesRoutes /> : <Unauthorized />
91
39
  }
92
40
  />
93
41
  );
94
42
  };
95
43
 
96
- SourceRoutes.propTypes = {
97
- source: PropTypes.object,
98
- sourceLoading: PropTypes.bool,
99
- templatesLoading: PropTypes.bool,
100
- };
101
-
102
- const mapStateToProps = ({ source, sourceLoading, templatesLoading }) => ({
103
- source,
104
- sourceLoading,
105
- templatesLoading,
106
- });
107
-
108
- export default connect(mapStateToProps)(SourceRoutes);
44
+ export default SourceRoutes;
@@ -1,62 +1,71 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
- import { connect } from "react-redux";
5
4
  import { useIntl } from "react-intl";
6
5
  import { Form } from "semantic-ui-react";
7
-
8
- const SourcesLoader = React.lazy(() =>
9
- import("@truedat/cx/sources/components/SourcesLoader")
10
- );
6
+ import { useQuery } from "@apollo/client";
7
+ import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
8
+ import { SOURCE_OPTIONS_QUERY } from "../api/queries";
11
9
 
12
10
  export const SourceSelector = ({
13
- value,
14
- onChange,
15
- onBlur,
16
- error,
17
11
  disabled,
18
- sources,
12
+ error,
13
+ label,
14
+ onBlur,
15
+ onChange,
16
+ placeholder,
17
+ required,
18
+ value,
19
+ variables,
19
20
  }) => {
20
21
  const { formatMessage } = useIntl();
21
- const options = _.flow(
22
- _.orderBy([({ name }) => name.toLowerCase()], ["asc"]),
23
- _.map(({ name: text, id: key }) => ({
24
- key,
25
- value: key,
26
- text,
27
- }))
28
- )(sources);
22
+ const {
23
+ loading,
24
+ error: queryError,
25
+ data,
26
+ } = useQuery(SOURCE_OPTIONS_QUERY, { variables });
27
+ if (queryError) return null;
28
+ const options = loading
29
+ ? []
30
+ : _.flow(
31
+ _.prop("sources"),
32
+ _.sortBy(accentInsensitivePathOrder("externalId")),
33
+ _.map(({ id, externalId }) => ({
34
+ key: id,
35
+ value: id,
36
+ text: externalId,
37
+ }))
38
+ )(data);
29
39
  return (
30
- <>
31
- <SourcesLoader />
32
- <Form.Dropdown
33
- disabled={disabled}
34
- error={error}
35
- onBlur={onBlur}
36
- placeholder={formatMessage({ id: "source.search.placeholder" })}
37
- search
38
- selection
39
- options={options}
40
- onChange={onChange}
41
- value={value}
42
- />
43
- </>
40
+ <Form.Dropdown
41
+ disabled={disabled}
42
+ error={error}
43
+ label={label}
44
+ loading={loading}
45
+ onBlur={onBlur}
46
+ onChange={onChange}
47
+ options={options}
48
+ placeholder={
49
+ placeholder || formatMessage({ id: "source.search.placeholder" })
50
+ }
51
+ required={required}
52
+ search
53
+ selection
54
+ value={value}
55
+ />
44
56
  );
45
57
  };
46
58
 
47
59
  SourceSelector.propTypes = {
48
- onChange: PropTypes.func.isRequired,
49
- value: PropTypes.string,
50
- onBlur: PropTypes.func,
51
- error: PropTypes.bool,
52
60
  disabled: PropTypes.bool,
53
- sources: PropTypes.array,
61
+ error: PropTypes.bool,
62
+ label: PropTypes.node,
63
+ onBlur: PropTypes.func,
64
+ onChange: PropTypes.func,
65
+ placeholder: PropTypes.string,
66
+ required: PropTypes.bool,
67
+ value: PropTypes.string,
68
+ variables: PropTypes.object,
54
69
  };
55
70
 
56
- const mapStateToProps = ({ sources }) => ({
57
- sources: _.map((source) => ({ name: source.external_id, id: source.id }))(
58
- sources
59
- ),
60
- });
61
-
62
- export default connect(mapStateToProps)(SourceSelector);
71
+ export default SourceSelector;
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { Link } from "react-router-dom";
4
+ import { FormattedMessage } from "react-intl";
5
+ import { Menu } from "semantic-ui-react";
6
+ import { usePath } from "@truedat/core/hooks";
7
+ import {
8
+ linkTo,
9
+ SOURCE_EDIT,
10
+ SOURCE_JOB,
11
+ SOURCE_JOBS_NEW,
12
+ SOURCE_JOBS,
13
+ SOURCE,
14
+ } from "@truedat/core/routes";
15
+
16
+ export const SourceTabs = ({ source }) => {
17
+ const path = usePath();
18
+ const { id } = source;
19
+ return (
20
+ <>
21
+ <Menu attached="top" secondary pointing tabular>
22
+ <Menu.Item
23
+ active={path === SOURCE || path === SOURCE_EDIT}
24
+ as={Link}
25
+ to={linkTo.SOURCE({ sourceId: id })}
26
+ >
27
+ <FormattedMessage id="source.config.label" />
28
+ </Menu.Item>
29
+ <Menu.Item
30
+ active={path === SOURCE_JOBS || path === SOURCE_JOB}
31
+ as={Link}
32
+ to={linkTo.SOURCE_JOBS({ sourceId: id })}
33
+ >
34
+ <FormattedMessage id="navigation.admin.jobs" />
35
+ </Menu.Item>
36
+ {source?.active ? (
37
+ <Menu.Item
38
+ active={path === SOURCE_JOBS_NEW}
39
+ as={Link}
40
+ to={linkTo.SOURCE_JOBS_NEW({ sourceId: id })}
41
+ >
42
+ <FormattedMessage id="jobs.actions.create" />
43
+ </Menu.Item>
44
+ ) : null}
45
+ </Menu>
46
+ </>
47
+ );
48
+ };
49
+
50
+ SourceTabs.propTypes = {
51
+ source: PropTypes.object.isRequired,
52
+ };
53
+
54
+ export default SourceTabs;