@truedat/dq 4.43.0 → 4.43.3

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 (52) hide show
  1. package/CHANGELOG.md +22 -3
  2. package/package.json +5 -5
  3. package/src/api.js +2 -0
  4. package/src/components/ConditionSummary.js +1 -0
  5. package/src/components/FieldSummary.js +86 -0
  6. package/src/components/ImplementationSummary.js +11 -1
  7. package/src/components/NewRemediation.js +27 -20
  8. package/src/components/NewRuleImplementation.js +42 -6
  9. package/src/components/RemediationPlan.js +47 -78
  10. package/src/components/RuleImplementationProperties.js +2 -1
  11. package/src/components/RuleImplementationResultTabs.js +113 -0
  12. package/src/components/RuleImplementationResults.js +1 -1
  13. package/src/components/RuleResult.js +108 -0
  14. package/src/components/{ExecutionDetails.js → RuleResultDetails.js} +4 -21
  15. package/src/components/RuleResultRemediationLoader.js +44 -0
  16. package/src/components/RuleResultRemediations.js +47 -0
  17. package/src/components/RuleResultRow.js +0 -3
  18. package/src/components/RuleResultSegmentRow.js +83 -0
  19. package/src/components/RuleResultSegments.js +102 -0
  20. package/src/components/RuleResultSegmentsLoader.js +34 -0
  21. package/src/components/RuleResultsRoutes.js +73 -0
  22. package/src/components/RuleRoutes.js +4 -14
  23. package/src/components/RuleSelector.js +5 -2
  24. package/src/components/__test_samples__/NewRuleImplementationProps.js +22 -2
  25. package/src/components/__tests__/ExecutionDetails.spec.js +5 -5
  26. package/src/components/__tests__/NewRuleImplementation.spec.js +27 -7
  27. package/src/components/__tests__/RuleResultSegmentsLoader.spec.js +32 -0
  28. package/src/components/__tests__/__snapshots__/ExecutionDetails.spec.js.snap +2 -30
  29. package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +1211 -1217
  30. package/src/components/__tests__/__snapshots__/RuleImplementationProperties.spec.js.snap +1 -0
  31. package/src/components/index.js +2 -2
  32. package/src/components/ruleImplementationForm/DatasetForm.js +4 -1
  33. package/src/components/ruleImplementationForm/FieldsGrid.js +57 -0
  34. package/src/components/ruleImplementationForm/FieldsGroup.js +107 -0
  35. package/src/components/ruleImplementationForm/RuleImplementationForm.js +45 -0
  36. package/src/components/ruleImplementationForm/SegmentsForm.js +35 -0
  37. package/src/components/ruleImplementationForm/__tests__/DataSetForm.spec.js +0 -1
  38. package/src/components/ruleImplementationForm/__tests__/RuleImplementationForm.spec.js +27 -10
  39. package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +315 -91
  40. package/src/messages/en.js +7 -0
  41. package/src/messages/es.js +7 -0
  42. package/src/reducers/__tests__/segmentResult.spec.js +46 -0
  43. package/src/reducers/index.js +2 -0
  44. package/src/reducers/ruleImplementation.js +1 -0
  45. package/src/reducers/segmentResults.js +19 -0
  46. package/src/routines.js +3 -0
  47. package/src/sagas/__tests__/fetchSegmentResults.spec.js +78 -0
  48. package/src/sagas/fetchSegmentResults.js +30 -0
  49. package/src/sagas/index.js +3 -0
  50. package/src/services/encodeRawContent.js +5 -5
  51. package/src/styles/remediationPlan.less +5 -0
  52. package/src/styles/ruleImplementationForm/DatasetForm.less +5 -0
@@ -0,0 +1,113 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Menu } from "semantic-ui-react";
5
+ import { Link, withRouter } from "react-router-dom";
6
+ import { compose } from "redux";
7
+ import { connect } from "react-redux";
8
+ import { FormattedMessage } from "react-intl";
9
+ import {
10
+ RULE_IMPLEMENTATION_RESULT_DETAILS,
11
+ RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
12
+ //TODO: change name route
13
+ RULE_IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN,
14
+ linkTo,
15
+ } from "@truedat/core/routes";
16
+
17
+ import RuleResultRemediationLoader from "./RuleResultRemediationLoader";
18
+ const TemplatesLoader = React.lazy(() =>
19
+ import("@truedat/df/templates/components/TemplatesLoader")
20
+ );
21
+
22
+ export const RuleImplementationResultTabs = ({
23
+ rule,
24
+ ruleImplementation,
25
+ ruleResult,
26
+ match,
27
+ authCreateRemediation,
28
+ templates,
29
+ }) => {
30
+ const renderSegmentResult = () => {
31
+ return ruleResult.has_segments ? (
32
+ <Menu.Item
33
+ active={match.path === RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS}
34
+ as={Link}
35
+ to={linkTo.RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS({
36
+ id: rule.id,
37
+ implementation_id: ruleImplementation.id,
38
+ rule_result_id: ruleResult.id,
39
+ })}
40
+ >
41
+ <FormattedMessage id="tabs.dq.ruleImplementationResult.segmentResult" />
42
+ </Menu.Item>
43
+ ) : null;
44
+ };
45
+
46
+ const renderRemediationPlan = () => {
47
+ const canCreateRemediation = authCreateRemediation && !_.isEmpty(templates);
48
+ const renderCondition = ruleResult.has_remediation || canCreateRemediation;
49
+
50
+ return renderCondition ? (
51
+ <Menu.Item
52
+ active={
53
+ match.path === RULE_IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN
54
+ }
55
+ as={Link}
56
+ to={linkTo.RULE_IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN({
57
+ id: rule.id,
58
+ implementation_id: ruleImplementation.id,
59
+ rule_result_id: ruleResult.id,
60
+ })}
61
+ >
62
+ <FormattedMessage id="tabs.dq.ruleImplementationResult.remediationPlan" />
63
+ </Menu.Item>
64
+ ) : null;
65
+ };
66
+
67
+ return _.isEmpty(ruleImplementation) || _.isEmpty(rule) ? null : (
68
+ <>
69
+ <RuleResultRemediationLoader propRuleResultId={ruleResult.id} />
70
+ <TemplatesLoader scope="remediation" />
71
+ <Menu attached="top" pointing secondary tabular>
72
+ <Menu.Item
73
+ active={match.path === RULE_IMPLEMENTATION_RESULT_DETAILS}
74
+ as={Link}
75
+ to={linkTo.RULE_IMPLEMENTATION_RESULT_DETAILS({
76
+ id: rule.id,
77
+ implementation_id: ruleImplementation.id,
78
+ rule_result_id: ruleResult.id,
79
+ })}
80
+ >
81
+ <FormattedMessage id="tabs.dq.ruleImplementationResult.info" />
82
+ </Menu.Item>
83
+ {renderSegmentResult()}
84
+ {renderRemediationPlan()}
85
+ </Menu>
86
+ </>
87
+ );
88
+ };
89
+
90
+ RuleImplementationResultTabs.propTypes = {
91
+ rule: PropTypes.object,
92
+ ruleResult: PropTypes.object,
93
+ ruleImplementation: PropTypes.object,
94
+ match: PropTypes.object,
95
+ authCreateRemediation: PropTypes.bool,
96
+ templates: PropTypes.array,
97
+ };
98
+
99
+ const mapStateToProps = (
100
+ { rule, ruleImplementation, remediationActions, templates },
101
+ ownProps
102
+ ) => ({
103
+ rule,
104
+ ruleResult: ownProps.ruleResult,
105
+ ruleImplementation,
106
+ authCreateRemediation: _.has("create")(remediationActions),
107
+ templates,
108
+ });
109
+
110
+ export default compose(
111
+ withRouter,
112
+ connect(mapStateToProps)
113
+ )(RuleImplementationResultTabs);
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useEffect } from "react";
2
+ import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { useIntl } from "react-intl";
5
5
  import { connect } from "react-redux";
@@ -0,0 +1,108 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { connect } from "react-redux";
6
+ import { useParams } from "react-router-dom";
7
+ import { Header, Table, Icon } from "semantic-ui-react";
8
+ import { DateTime } from "@truedat/core/components";
9
+ import { FormattedMessage } from "react-intl";
10
+ import { selectColor } from "../functions/selectors";
11
+ import RuleImplementationResultTabs from "./RuleImplementationResultTabs";
12
+
13
+ const GeneralInformation = ({ ruleImplementation, ruleResult }) => {
14
+ const { formatMessage, formatNumber: _formatNumber } = useIntl();
15
+ const formatNumber = (num) => (_.isNil(num) ? num : _formatNumber(num));
16
+ return (
17
+ <>
18
+ <Table.Row>
19
+ <Table.Cell>
20
+ {formatMessage({ id: "ruleResult.props.quality" })}
21
+ </Table.Cell>
22
+ <Table.Cell>
23
+ <Icon
24
+ name="circle"
25
+ color={selectColor({ ...ruleImplementation, ...ruleResult })}
26
+ />
27
+ {`${parseFloat(ruleResult.result)} %`}
28
+ </Table.Cell>
29
+ </Table.Row>
30
+
31
+ <Table.Row>
32
+ <Table.Cell>
33
+ {formatMessage({ id: "ruleResult.props.date" })}
34
+ </Table.Cell>
35
+ <Table.Cell>
36
+ <DateTime value={ruleResult.date} />
37
+ </Table.Cell>
38
+ </Table.Row>
39
+
40
+ <Table.Row>
41
+ <Table.Cell>
42
+ {formatMessage({ id: "ruleResult.props.records" })}
43
+ </Table.Cell>
44
+ <Table.Cell>{formatNumber(ruleResult.records)}</Table.Cell>
45
+ </Table.Row>
46
+
47
+ <Table.Row>
48
+ <Table.Cell>
49
+ {formatMessage({ id: "ruleResult.props.errors" })}
50
+ </Table.Cell>
51
+ <Table.Cell>{formatNumber(ruleResult.errors)}</Table.Cell>
52
+ </Table.Row>
53
+ </>
54
+ );
55
+ };
56
+
57
+ GeneralInformation.propTypes = {
58
+ ruleImplementation: PropTypes.object,
59
+ ruleResult: PropTypes.object,
60
+ };
61
+
62
+ export const RuleResult = ({ ruleImplementation, ruleResultId, children }) => {
63
+ const { rule_result_id: paramsId } = useParams();
64
+
65
+ const resultId = _.defaultTo(_.toNumber(paramsId))(ruleResultId);
66
+
67
+ const ruleResult = _.find(_.propEq("id", resultId))(
68
+ ruleImplementation.results
69
+ );
70
+
71
+ const renderRuleResult = () => {
72
+ return (
73
+ <>
74
+ <Header as="h2">
75
+ <Icon circular name="chart pie" />
76
+ <Header.Content>
77
+ <FormattedMessage id="ruleResult" />
78
+ </Header.Content>
79
+ </Header>
80
+ <RuleImplementationResultTabs ruleResult={ruleResult} />
81
+ {children}
82
+ </>
83
+ );
84
+ };
85
+
86
+ return _.isEmpty(ruleResult) ? null : renderRuleResult();
87
+ };
88
+
89
+ RuleResult.propTypes = {
90
+ ruleImplementation: PropTypes.object,
91
+ ruleResultId: PropTypes.number,
92
+ children: PropTypes.oneOfType([
93
+ PropTypes.node,
94
+ PropTypes.arrayOf(PropTypes.node),
95
+ ]),
96
+ };
97
+
98
+ const mapStateToProps = (
99
+ { ruleImplementation, remediationLoading, templatesLoading },
100
+ ownProps
101
+ ) => ({
102
+ ruleImplementation,
103
+ remediationLoading,
104
+ templatesLoading,
105
+ ...ownProps,
106
+ });
107
+
108
+ export default connect(mapStateToProps)(RuleResult);
@@ -4,11 +4,9 @@ import PropTypes from "prop-types";
4
4
  import { useIntl } from "react-intl";
5
5
  import { connect } from "react-redux";
6
6
  import { useParams } from "react-router-dom";
7
- import { Header, Table, Icon } from "semantic-ui-react";
7
+ import { Table, Icon } from "semantic-ui-react";
8
8
  import { DateTime } from "@truedat/core/components";
9
- import { FormattedMessage } from "react-intl";
10
9
  import { selectColor } from "../functions/selectors";
11
- import RemediationPlan from "./RemediationPlan";
12
10
  import "../styles/executionDetails.less";
13
11
 
14
12
  const GeneralInformation = ({ ruleImplementation, ruleResult }) => {
@@ -84,11 +82,7 @@ const DetailRow = ({ details }) => {
84
82
 
85
83
  DetailRow.propTypes = { details: PropTypes.object };
86
84
 
87
- const TemplatesLoader = React.lazy(() =>
88
- import("@truedat/df/templates/components/TemplatesLoader")
89
- );
90
-
91
- export const ExecutionDetails = ({ ruleImplementation, ruleResultId }) => {
85
+ export const RuleResultDetails = ({ ruleImplementation, ruleResultId }) => {
92
86
  const { formatMessage } = useIntl();
93
87
  const { rule_result_id: paramsId } = useParams();
94
88
 
@@ -100,12 +94,6 @@ export const ExecutionDetails = ({ ruleImplementation, ruleResultId }) => {
100
94
 
101
95
  return _.isEmpty(ruleResult) ? null : (
102
96
  <>
103
- <Header as="h2">
104
- <Icon circular name="chart pie" />
105
- <Header.Content>
106
- <FormattedMessage id="ruleResult" />
107
- </Header.Content>
108
- </Header>
109
97
  <Table className="implementation-results medium">
110
98
  <Table.Header>
111
99
  <Table.Row>
@@ -141,16 +129,11 @@ export const ExecutionDetails = ({ ruleImplementation, ruleResultId }) => {
141
129
  </>
142
130
  ) : null}
143
131
  </Table>
144
- <TemplatesLoader scope="remediation" />
145
- <RemediationPlan
146
- className="execution-details-remediation"
147
- latestResultId={resultId}
148
- />
149
132
  </>
150
133
  );
151
134
  };
152
135
 
153
- ExecutionDetails.propTypes = {
136
+ RuleResultDetails.propTypes = {
154
137
  ruleImplementation: PropTypes.object,
155
138
  ruleResultId: PropTypes.number,
156
139
  };
@@ -159,4 +142,4 @@ const mapStateToProps = ({ ruleImplementation }) => ({
159
142
  ruleImplementation,
160
143
  });
161
144
 
162
- export default connect(mapStateToProps)(ExecutionDetails);
145
+ export default connect(mapStateToProps)(RuleResultDetails);
@@ -0,0 +1,44 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useParams } from "react-router-dom";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+
6
+ import { fetchRemediation, clearRemediation } from "../routines";
7
+
8
+ export const RuleResultRemediationLoader = ({
9
+ fetchRemediation,
10
+ clearRemediation,
11
+ propRuleResultId,
12
+ }) => {
13
+ const [ruleResultId, setRuleResultId] = useState(null);
14
+
15
+ const { rule_result_id: paramsRuleResultId } = useParams();
16
+
17
+ useEffect(() => {
18
+ setRuleResultId(paramsRuleResultId || propRuleResultId);
19
+ }, [paramsRuleResultId, propRuleResultId]);
20
+
21
+ useEffect(() => {
22
+ ruleResultId && fetchRemediation({ rule_result_id: ruleResultId });
23
+ }, [fetchRemediation, ruleResultId]);
24
+
25
+ useEffect(
26
+ () => () => {
27
+ clearRemediation();
28
+ },
29
+ [clearRemediation]
30
+ );
31
+
32
+ return null;
33
+ };
34
+
35
+ RuleResultRemediationLoader.propTypes = {
36
+ clearRemediation: PropTypes.func,
37
+ fetchRemediation: PropTypes.func,
38
+ propRuleResultId: PropTypes.number,
39
+ };
40
+
41
+ export default connect(null, {
42
+ fetchRemediation,
43
+ clearRemediation,
44
+ })(RuleResultRemediationLoader);
@@ -0,0 +1,47 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { useParams } from "react-router-dom";
6
+ import RemediationPlan from "./RemediationPlan";
7
+ import "../styles/executionDetails.less";
8
+
9
+ import RuleResultRemediationLoader from "./RuleResultRemediationLoader";
10
+ const TemplatesLoader = React.lazy(() =>
11
+ import("@truedat/df/templates/components/TemplatesLoader")
12
+ );
13
+
14
+ export const RuleResultRemediations = ({
15
+ ruleImplementation,
16
+ ruleResultId,
17
+ }) => {
18
+ const { rule_result_id: paramsId } = useParams();
19
+
20
+ const resultId = _.defaultTo(_.toNumber(paramsId))(ruleResultId);
21
+
22
+ const ruleResult = _.find(_.propEq("id", resultId))(
23
+ ruleImplementation.results
24
+ );
25
+
26
+ return _.isEmpty(ruleResult) ? null : (
27
+ <>
28
+ <RuleResultRemediationLoader propRuleResultId={ruleResult.id} />
29
+ <TemplatesLoader scope="remediation" />
30
+ <RemediationPlan
31
+ className="execution-details-remediation"
32
+ latestResultId={resultId}
33
+ />
34
+ </>
35
+ );
36
+ };
37
+
38
+ RuleResultRemediations.propTypes = {
39
+ ruleImplementation: PropTypes.object,
40
+ ruleResultId: PropTypes.number,
41
+ };
42
+
43
+ const mapStateToProps = ({ ruleImplementation }) => ({
44
+ ruleImplementation,
45
+ });
46
+
47
+ export default connect(mapStateToProps)(RuleResultRemediations);
@@ -37,9 +37,6 @@ export const RuleResultRow = ({
37
37
  })}
38
38
  >
39
39
  <DateTime className="rule-result-date" value={ruleResult.date} />
40
- {!_.isEmpty(ruleResult.details) ? (
41
- <Icon circular inverted name="info" color="blue" size="small" />
42
- ) : null}
43
40
  {ruleResult.has_remediation ? (
44
41
  <Icon circular inverted name="rain" color="blue" size="small" />
45
42
  ) : null}
@@ -0,0 +1,83 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { useIntl } from "react-intl";
6
+ import { Table, Icon } from "semantic-ui-react";
7
+ import { ConfirmModal } from "@truedat/core/components";
8
+ import { columnDecorator } from "@truedat/core/services";
9
+ import { selectColor } from "../functions/selectors";
10
+ import { deleteRuleResult } from "../routines";
11
+ import "../styles/ruleResultRow.less";
12
+
13
+ export const RuleResultSegmentRow = ({
14
+ deleteRuleResult,
15
+ optionalColumns,
16
+ segmentResult,
17
+ customColumns,
18
+ isAdmin,
19
+ ruleImplementation,
20
+ rule,
21
+ }) => {
22
+ const { formatMessage, formatNumber: _formatNumber } = useIntl();
23
+ const formatNumber = (num) => (_.isNil(num) ? num : _formatNumber(num));
24
+
25
+ return (
26
+ <Table.Row>
27
+ <Table.Cell>{_.prop("name")(segmentResult.params)}</Table.Cell>
28
+ <Table.Cell>
29
+ <Icon
30
+ name="circle"
31
+ color={selectColor({ ...ruleImplementation, ...segmentResult })}
32
+ />
33
+ {`${parseFloat(segmentResult.result)} %`}
34
+ </Table.Cell>
35
+ {_.includes("records")(optionalColumns) ? (
36
+ <Table.Cell>{formatNumber(segmentResult.records)}</Table.Cell>
37
+ ) : null}
38
+ {_.includes("errors")(optionalColumns) ? (
39
+ <Table.Cell>{formatNumber(segmentResult.errors)}</Table.Cell>
40
+ ) : null}
41
+ {customColumns?.map((column, index) => (
42
+ <Table.Cell key={index}>
43
+ {columnDecorator(column)(segmentResult)}
44
+ </Table.Cell>
45
+ ))}
46
+ {isAdmin ? (
47
+ <Table.Cell onClick={(e) => e.stopPropagation()}>
48
+ <ConfirmModal
49
+ icon="trash"
50
+ trigger={<Icon name="trash alternate outline" color="red" />}
51
+ header={formatMessage({
52
+ id: "rule_result.actions.delete.confirmation.header",
53
+ })}
54
+ content={formatMessage({
55
+ id: "rule_result.actions.delete.confirmation.content",
56
+ })}
57
+ onConfirm={() =>
58
+ deleteRuleResult({
59
+ id: segmentResult.id,
60
+ rule_id: rule.id,
61
+ rule_implementation_id: ruleImplementation.id,
62
+ })
63
+ }
64
+ />
65
+ </Table.Cell>
66
+ ) : null}
67
+ </Table.Row>
68
+ );
69
+ };
70
+
71
+ RuleResultSegmentRow.propTypes = {
72
+ customColumns: PropTypes.array,
73
+ deleteRuleResult: PropTypes.func,
74
+ isAdmin: PropTypes.bool,
75
+ optionalColumns: PropTypes.array,
76
+ rule: PropTypes.object,
77
+ ruleImplementation: PropTypes.object,
78
+ ruleResult: PropTypes.object,
79
+ };
80
+
81
+ const mapDispatchToProps = { deleteRuleResult };
82
+
83
+ export default connect(null, mapDispatchToProps)(RuleResultSegmentRow);
@@ -0,0 +1,102 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { connect } from "react-redux";
6
+ import { Table, Message, Divider } from "semantic-ui-react";
7
+ import { columnDecorator } from "@truedat/core/services";
8
+ import RuleResultSegmentRow from "./RuleResultSegmentRow";
9
+
10
+ export const getCustomColumnsWithData = (segmentResults, columns) =>
11
+ _.filter((column) =>
12
+ _.any(_.flow(columnDecorator(column), _.negate(_.isEmpty)))(segmentResults)
13
+ )(columns);
14
+
15
+ export const RuleResultSegments = ({
16
+ rule,
17
+ isAdmin,
18
+ ruleImplementation,
19
+ segmentResults,
20
+ }) => {
21
+ const { formatMessage } = useIntl();
22
+ const customColumns = [];
23
+ const optionalColumns = ["errors", "records", "details"];
24
+
25
+ if (_.isEmpty(rule) || _.isEmpty(segmentResults)) return null;
26
+
27
+ return (
28
+ <>
29
+ {_.isEmpty(segmentResults) ? (
30
+ <Message
31
+ style={{ marginTop: "14px" }}
32
+ header={formatMessage({
33
+ id: "rule.ruleImplementation.results.empty",
34
+ })}
35
+ />
36
+ ) : (
37
+ <Table className="implementation-results small">
38
+ <Table.Header>
39
+ <Table.Row>
40
+ <Table.HeaderCell
41
+ content={formatMessage({ id: "ruleResultSegment.props.name" })}
42
+ />
43
+ <Table.HeaderCell
44
+ content={formatMessage({ id: "ruleResult.props.quality" })}
45
+ />
46
+ {_.includes("records")(optionalColumns) ? (
47
+ <Table.HeaderCell
48
+ content={formatMessage({ id: "ruleResult.props.records" })}
49
+ />
50
+ ) : null}
51
+ {_.includes("errors")(optionalColumns) ? (
52
+ <Table.HeaderCell
53
+ content={formatMessage({ id: "ruleResult.props.errors" })}
54
+ />
55
+ ) : null}
56
+ {customColumns.map((column, index) => (
57
+ <Table.HeaderCell
58
+ key={index}
59
+ content={formatMessage({
60
+ id: `ruleResult.props.${column.name}`,
61
+ defaultMessage: column.name,
62
+ })}
63
+ />
64
+ ))}
65
+ {<Table.HeaderCell />}
66
+ {isAdmin ? <Table.HeaderCell /> : null}
67
+ </Table.Row>
68
+ </Table.Header>
69
+ <Table.Body>
70
+ {segmentResults.map((result, i) => (
71
+ <RuleResultSegmentRow
72
+ key={i}
73
+ optionalColumns={optionalColumns}
74
+ segmentResult={result}
75
+ customColumns={customColumns}
76
+ isAdmin={isAdmin}
77
+ ruleImplementation={ruleImplementation}
78
+ rule={rule}
79
+ />
80
+ ))}
81
+ </Table.Body>
82
+ </Table>
83
+ )}
84
+ </>
85
+ );
86
+ };
87
+
88
+ RuleResultSegments.propTypes = {
89
+ ruleImplementation: PropTypes.object,
90
+ rule: PropTypes.object,
91
+ segmentResults: PropTypes.object,
92
+ isAdmin: PropTypes.bool,
93
+ };
94
+
95
+ const mapStateToProps = (state) => ({
96
+ rule: state.rule,
97
+ ruleImplementation: state.ruleImplementation,
98
+ segmentResults: state.segmentResults,
99
+ isAdmin: state.authentication.role === "admin",
100
+ });
101
+
102
+ export default connect(mapStateToProps)(RuleResultSegments);
@@ -0,0 +1,34 @@
1
+ import { useEffect } from "react";
2
+ import { useParams } from "react-router-dom";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { clearSegmentResults, fetchSegmentResults } from "../routines";
6
+
7
+ export const RuleResultSegmentsLoader = ({
8
+ clearSegmentResults,
9
+ fetchSegmentResults,
10
+ }) => {
11
+ const { rule_result_id } = useParams();
12
+ useEffect(() => {
13
+ fetchSegmentResults({ rule_result_id });
14
+ }, [rule_result_id, fetchSegmentResults]);
15
+
16
+ useEffect(() => {
17
+ return () => {
18
+ clearSegmentResults();
19
+ };
20
+ }, [clearSegmentResults]);
21
+
22
+ return null;
23
+ };
24
+
25
+ RuleResultSegmentsLoader.propTypes = {
26
+ clearSegmentResults: PropTypes.func,
27
+ fetchSegmentResults: PropTypes.func,
28
+ rule_result_id: PropTypes.number,
29
+ };
30
+
31
+ export default connect(null, {
32
+ clearSegmentResults,
33
+ fetchSegmentResults,
34
+ })(RuleResultSegmentsLoader);
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+ import { Route } from "react-router-dom";
3
+ import { Segment } from "semantic-ui-react";
4
+ import {
5
+ RULE_IMPLEMENTATION_RESULT_DETAILS,
6
+ RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
7
+ RULE_IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN,
8
+ } from "@truedat/core/routes";
9
+
10
+ import RuleResult from "./RuleResult";
11
+ import RuleResultDetails from "./RuleResultDetails";
12
+ import RuleResultSegments from "./RuleResultSegments";
13
+ import RuleResultRemediations from "./RuleResultRemediations";
14
+ import RuleCrumbs from "./RuleCrumbs";
15
+ import RuleResultSegmentsLoader from "./RuleResultSegmentsLoader";
16
+
17
+ export const RuleResultsRoutes = ({}) => {
18
+ return (
19
+ <>
20
+ <Route
21
+ exact
22
+ path={RULE_IMPLEMENTATION_RESULT_DETAILS}
23
+ render={() => (
24
+ <>
25
+ <Segment>
26
+ <>
27
+ <RuleCrumbs />
28
+ <RuleResult>
29
+ <RuleResultDetails />
30
+ </RuleResult>
31
+ </>
32
+ </Segment>
33
+ </>
34
+ )}
35
+ />
36
+ <Route
37
+ exact
38
+ path={RULE_IMPLEMENTATION_RESULT_SEGMENTS_RESULTS}
39
+ render={() => (
40
+ <>
41
+ <Segment>
42
+ <>
43
+ <RuleCrumbs />
44
+ <RuleResult>
45
+ <RuleResultSegmentsLoader />
46
+ <RuleResultSegments />
47
+ </RuleResult>
48
+ </>
49
+ </Segment>
50
+ </>
51
+ )}
52
+ />
53
+ <Route
54
+ exact
55
+ path={RULE_IMPLEMENTATION_RESULT_DETAILS_REMEDIATION_PLAN}
56
+ render={() => (
57
+ <>
58
+ <Segment>
59
+ <>
60
+ <RuleCrumbs />
61
+ <RuleResult>
62
+ <RuleResultRemediations />
63
+ </RuleResult>
64
+ </>
65
+ </Segment>
66
+ </>
67
+ )}
68
+ />
69
+ </>
70
+ );
71
+ };
72
+
73
+ export default RuleResultsRoutes;