@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.
- package/CHANGELOG.md +22 -3
- package/package.json +5 -5
- package/src/api.js +2 -0
- package/src/components/ConditionSummary.js +1 -0
- package/src/components/FieldSummary.js +86 -0
- package/src/components/ImplementationSummary.js +11 -1
- package/src/components/NewRemediation.js +27 -20
- package/src/components/NewRuleImplementation.js +42 -6
- package/src/components/RemediationPlan.js +47 -78
- package/src/components/RuleImplementationProperties.js +2 -1
- package/src/components/RuleImplementationResultTabs.js +113 -0
- package/src/components/RuleImplementationResults.js +1 -1
- package/src/components/RuleResult.js +108 -0
- package/src/components/{ExecutionDetails.js → RuleResultDetails.js} +4 -21
- package/src/components/RuleResultRemediationLoader.js +44 -0
- package/src/components/RuleResultRemediations.js +47 -0
- package/src/components/RuleResultRow.js +0 -3
- package/src/components/RuleResultSegmentRow.js +83 -0
- package/src/components/RuleResultSegments.js +102 -0
- package/src/components/RuleResultSegmentsLoader.js +34 -0
- package/src/components/RuleResultsRoutes.js +73 -0
- package/src/components/RuleRoutes.js +4 -14
- package/src/components/RuleSelector.js +5 -2
- package/src/components/__test_samples__/NewRuleImplementationProps.js +22 -2
- package/src/components/__tests__/ExecutionDetails.spec.js +5 -5
- package/src/components/__tests__/NewRuleImplementation.spec.js +27 -7
- package/src/components/__tests__/RuleResultSegmentsLoader.spec.js +32 -0
- package/src/components/__tests__/__snapshots__/ExecutionDetails.spec.js.snap +2 -30
- package/src/components/__tests__/__snapshots__/NewRuleImplementation.spec.js.snap +1211 -1217
- package/src/components/__tests__/__snapshots__/RuleImplementationProperties.spec.js.snap +1 -0
- package/src/components/index.js +2 -2
- package/src/components/ruleImplementationForm/DatasetForm.js +4 -1
- package/src/components/ruleImplementationForm/FieldsGrid.js +57 -0
- package/src/components/ruleImplementationForm/FieldsGroup.js +107 -0
- package/src/components/ruleImplementationForm/RuleImplementationForm.js +45 -0
- package/src/components/ruleImplementationForm/SegmentsForm.js +35 -0
- package/src/components/ruleImplementationForm/__tests__/DataSetForm.spec.js +0 -1
- package/src/components/ruleImplementationForm/__tests__/RuleImplementationForm.spec.js +27 -10
- package/src/components/ruleImplementationForm/__tests__/__snapshots__/RuleImplementationForm.spec.js.snap +315 -91
- package/src/messages/en.js +7 -0
- package/src/messages/es.js +7 -0
- package/src/reducers/__tests__/segmentResult.spec.js +46 -0
- package/src/reducers/index.js +2 -0
- package/src/reducers/ruleImplementation.js +1 -0
- package/src/reducers/segmentResults.js +19 -0
- package/src/routines.js +3 -0
- package/src/sagas/__tests__/fetchSegmentResults.spec.js +78 -0
- package/src/sagas/fetchSegmentResults.js +30 -0
- package/src/sagas/index.js +3 -0
- package/src/services/encodeRawContent.js +5 -5
- package/src/styles/remediationPlan.less +5 -0
- 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);
|
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
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)(
|
|
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;
|