@truedat/dq 4.40.7 → 4.40.8
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 +6 -0
- package/package.json +5 -5
- package/src/api.js +2 -0
- package/src/components/ExecutionDetails.js +65 -40
- package/src/components/ImplementationResultBar.js +1 -1
- package/src/components/NewRemediation.js +62 -0
- package/src/components/RemediationCrumbs.js +76 -0
- package/src/components/RemediationForm.js +129 -0
- package/src/components/RemediationPlan.js +161 -0
- package/src/components/RuleImplementation.js +1 -1
- package/src/components/RuleImplementationResults.js +2 -1
- package/src/components/RuleResultRow.js +4 -1
- package/src/components/RuleRoutes.js +324 -296
- package/src/components/__tests__/ExecutionDetails.spec.js +17 -5
- package/src/components/__tests__/RemediationForm.spec.js +68 -0
- package/src/components/__tests__/RemediationPlan.spec.js +106 -0
- package/src/components/__tests__/__snapshots__/ExecutionDetails.spec.js.snap +104 -0
- package/src/components/__tests__/__snapshots__/RemediationForm.spec.js.snap +19 -0
- package/src/components/__tests__/__snapshots__/RemediationPlan.spec.js.snap +3 -0
- package/src/messages/en.js +7 -0
- package/src/messages/es.js +7 -0
- package/src/reducers/__tests__/remediation.spec.js +83 -0
- package/src/reducers/__tests__/remediationActions.spec.js +46 -0
- package/src/reducers/__tests__/remediationLoading.spec.js +50 -0
- package/src/reducers/dqMessage.js +8 -2
- package/src/reducers/index.js +6 -0
- package/src/reducers/remediation.js +35 -0
- package/src/reducers/remediationActions.js +19 -0
- package/src/reducers/remediationLoading.js +29 -0
- package/src/routines.js +6 -0
- package/src/sagas/__tests__/createRemediation.spec.js +83 -0
- package/src/sagas/__tests__/deleteRemediation.spec.js +82 -0
- package/src/sagas/__tests__/fetchRemediation.spec.js +78 -0
- package/src/sagas/__tests__/fetchRuleImplementation.spec.js +1 -1
- package/src/sagas/__tests__/updateRemediation.spec.js +81 -0
- package/src/sagas/createRemediation.js +31 -0
- package/src/sagas/deleteRemediation.js +29 -0
- package/src/sagas/fetchRemediation.js +30 -0
- package/src/sagas/index.js +12 -0
- package/src/sagas/updateRemediation.js +29 -0
- package/src/styles/executionDetails.less +3 -0
- package/src/styles/remediationPlan.less +8 -0
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dq",
|
|
3
|
-
"version": "4.40.
|
|
3
|
+
"version": "4.40.8",
|
|
4
4
|
"description": "Truedat Web Data Quality Module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@babel/plugin-transform-modules-commonjs": "^7.15.0",
|
|
32
32
|
"@babel/preset-env": "^7.15.0",
|
|
33
33
|
"@babel/preset-react": "^7.14.5",
|
|
34
|
-
"@truedat/test": "4.40.
|
|
34
|
+
"@truedat/test": "4.40.8",
|
|
35
35
|
"babel-jest": "^27.0.6",
|
|
36
36
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
37
37
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@apollo/client": "^3.4.10",
|
|
85
|
-
"@truedat/core": "4.40.
|
|
86
|
-
"@truedat/df": "4.40.
|
|
85
|
+
"@truedat/core": "4.40.8",
|
|
86
|
+
"@truedat/df": "4.40.8",
|
|
87
87
|
"axios": "^0.19.2",
|
|
88
88
|
"graphql": "^15.5.3",
|
|
89
89
|
"path-to-regexp": "^1.7.0",
|
|
@@ -103,5 +103,5 @@
|
|
|
103
103
|
"react-dom": ">= 16.8.6 < 17",
|
|
104
104
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "b3570c4151f142384ee7201d05f72ed274f2eeb9"
|
|
107
107
|
}
|
package/src/api.js
CHANGED
|
@@ -2,6 +2,7 @@ const API_CONCEPT_RULES = "/api/rules/concept/:id";
|
|
|
2
2
|
const API_EXECUTION_GROUP = "/api/execution_groups/:id";
|
|
3
3
|
const API_EXECUTION_GROUPS = "/api/execution_groups";
|
|
4
4
|
const API_RULE = "/api/rules/:id";
|
|
5
|
+
const API_REMEDIATION_PLAN = "/api/rule_results/:rule_result_id/remediation";
|
|
5
6
|
const API_RULES = "/api/rules";
|
|
6
7
|
const API_RULES_SEARCH = "/api/rules/search";
|
|
7
8
|
const API_RULES_UPLOAD = "/api/rules/upload";
|
|
@@ -25,6 +26,7 @@ export {
|
|
|
25
26
|
API_CONCEPT_RULES,
|
|
26
27
|
API_EXECUTION_GROUP,
|
|
27
28
|
API_EXECUTION_GROUPS,
|
|
29
|
+
API_REMEDIATION_PLAN,
|
|
28
30
|
API_RULE,
|
|
29
31
|
API_RULES,
|
|
30
32
|
API_RULES_SEARCH,
|
|
@@ -4,9 +4,12 @@ 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 { Header, Table, Icon } from "semantic-ui-react";
|
|
8
8
|
import { DateTime } from "@truedat/core/components";
|
|
9
|
+
import { FormattedMessage } from "react-intl";
|
|
9
10
|
import { selectColor } from "../functions/selectors";
|
|
11
|
+
import RemediationPlan from "./RemediationPlan";
|
|
12
|
+
import "../styles/executionDetails.less";
|
|
10
13
|
|
|
11
14
|
const GeneralInformation = ({ ruleImplementation, ruleResult }) => {
|
|
12
15
|
const { formatMessage, formatNumber: _formatNumber } = useIntl();
|
|
@@ -81,52 +84,69 @@ const DetailRow = ({ details }) => {
|
|
|
81
84
|
|
|
82
85
|
DetailRow.propTypes = { details: PropTypes.object };
|
|
83
86
|
|
|
87
|
+
const TemplatesLoader = React.lazy(() =>
|
|
88
|
+
import("@truedat/df/templates/components/TemplatesLoader")
|
|
89
|
+
);
|
|
90
|
+
|
|
84
91
|
export const ExecutionDetails = ({ ruleImplementation, ruleResultId }) => {
|
|
85
92
|
const { formatMessage } = useIntl();
|
|
86
93
|
const { rule_result_id: paramsId } = useParams();
|
|
87
94
|
|
|
88
|
-
const
|
|
95
|
+
const resultId = _.defaultTo(_.toNumber(paramsId))(ruleResultId);
|
|
89
96
|
|
|
90
|
-
const ruleResult = _.find(_.propEq("id",
|
|
97
|
+
const ruleResult = _.find(_.propEq("id", resultId))(
|
|
91
98
|
ruleImplementation.results
|
|
92
99
|
);
|
|
93
100
|
|
|
94
|
-
return (
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
return _.isEmpty(ruleResult) ? null : (
|
|
102
|
+
<>
|
|
103
|
+
<Header as="h2">
|
|
104
|
+
<Icon circular name="chart pie" />
|
|
105
|
+
<Header.Content>
|
|
106
|
+
<FormattedMessage id="ruleResult" />
|
|
107
|
+
</Header.Content>
|
|
108
|
+
</Header>
|
|
109
|
+
<Table className="implementation-results medium">
|
|
110
|
+
<Table.Header>
|
|
111
|
+
<Table.Row>
|
|
112
|
+
<Table.HeaderCell
|
|
113
|
+
colSpan={2}
|
|
114
|
+
content={formatMessage({
|
|
115
|
+
id: "ruleResult.props.header.information",
|
|
116
|
+
})}
|
|
117
|
+
/>
|
|
118
|
+
</Table.Row>
|
|
119
|
+
</Table.Header>
|
|
120
|
+
<Table.Body>
|
|
121
|
+
<GeneralInformation
|
|
122
|
+
ruleImplementation={ruleImplementation}
|
|
123
|
+
ruleResult={ruleResult}
|
|
103
124
|
/>
|
|
104
|
-
</Table.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</Table>
|
|
125
|
+
</Table.Body>
|
|
126
|
+
{!_.isEmpty(ruleResult?.details) ? (
|
|
127
|
+
<>
|
|
128
|
+
<Table.Header>
|
|
129
|
+
<Table.Row>
|
|
130
|
+
<Table.HeaderCell
|
|
131
|
+
colSpan={2}
|
|
132
|
+
content={formatMessage({
|
|
133
|
+
id: "ruleResult.props.header.details",
|
|
134
|
+
})}
|
|
135
|
+
/>
|
|
136
|
+
</Table.Row>
|
|
137
|
+
</Table.Header>
|
|
138
|
+
<Table.Body>
|
|
139
|
+
<DetailRow details={ruleResult?.details} />
|
|
140
|
+
</Table.Body>
|
|
141
|
+
</>
|
|
142
|
+
) : null}
|
|
143
|
+
</Table>
|
|
144
|
+
<TemplatesLoader scope="remediation" />
|
|
145
|
+
<RemediationPlan
|
|
146
|
+
className="execution-details-remediation"
|
|
147
|
+
latestResultId={resultId}
|
|
148
|
+
/>
|
|
149
|
+
</>
|
|
130
150
|
);
|
|
131
151
|
};
|
|
132
152
|
|
|
@@ -135,8 +155,13 @@ ExecutionDetails.propTypes = {
|
|
|
135
155
|
ruleResultId: PropTypes.number,
|
|
136
156
|
};
|
|
137
157
|
|
|
138
|
-
const mapStateToProps = (
|
|
139
|
-
ruleImplementation
|
|
158
|
+
const mapStateToProps = ({
|
|
159
|
+
ruleImplementation,
|
|
160
|
+
templatesLoading,
|
|
161
|
+
templates,
|
|
162
|
+
}) => ({
|
|
163
|
+
templatesLoaded: !templatesLoading && !_.isEmpty(templates),
|
|
164
|
+
ruleImplementation: ruleImplementation,
|
|
140
165
|
});
|
|
141
166
|
|
|
142
167
|
export default connect(mapStateToProps)(ExecutionDetails);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { connect } from "react-redux";
|
|
5
|
+
import { Button, Header, Icon, Segment } from "semantic-ui-react";
|
|
6
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
7
|
+
import { createRemediation, fetchRuleImplementation } from "../routines";
|
|
8
|
+
import RemediationForm from "./RemediationForm";
|
|
9
|
+
|
|
10
|
+
export const NewRemediation = ({
|
|
11
|
+
manageRemediations,
|
|
12
|
+
onSaveRemediation,
|
|
13
|
+
templates,
|
|
14
|
+
}) => {
|
|
15
|
+
const { formatMessage } = useIntl();
|
|
16
|
+
const [isEdit, setIsEdit] = useState(false);
|
|
17
|
+
|
|
18
|
+
const onSave = () => {
|
|
19
|
+
setIsEdit(false);
|
|
20
|
+
onSaveRemediation();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return !manageRemediations || _.isEmpty(templates) ? null : (
|
|
24
|
+
<>
|
|
25
|
+
{!isEdit ? (
|
|
26
|
+
<Button
|
|
27
|
+
primary
|
|
28
|
+
onClick={() => setIsEdit(!isEdit)}
|
|
29
|
+
content={formatMessage({ id: "remediation.actions.create" })}
|
|
30
|
+
/>
|
|
31
|
+
) : (
|
|
32
|
+
<>
|
|
33
|
+
<Header as="h2">
|
|
34
|
+
<Icon name="plug" />
|
|
35
|
+
<Header.Content>
|
|
36
|
+
<FormattedMessage id="remediation.actions.create" />
|
|
37
|
+
</Header.Content>
|
|
38
|
+
</Header>
|
|
39
|
+
<Segment>
|
|
40
|
+
<RemediationForm onSave={onSave} />
|
|
41
|
+
</Segment>
|
|
42
|
+
</>
|
|
43
|
+
)}
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
NewRemediation.propTypes = {
|
|
49
|
+
onSaveRemediation: PropTypes.func,
|
|
50
|
+
manageRemediations: PropTypes.bool,
|
|
51
|
+
templates: PropTypes.array,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const mapStateToProps = ({ templates, remediationActions }) => ({
|
|
55
|
+
manageRemediations: _.has("create")(remediationActions),
|
|
56
|
+
templates,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export default connect(mapStateToProps, {
|
|
60
|
+
fetchRuleImplementation,
|
|
61
|
+
createRemediation,
|
|
62
|
+
})(NewRemediation);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { Breadcrumb } from "semantic-ui-react";
|
|
5
|
+
import { connect } from "react-redux";
|
|
6
|
+
import { Link, useParams } from "react-router-dom";
|
|
7
|
+
import { FormattedMessage } from "react-intl";
|
|
8
|
+
import { RULES, linkTo } from "@truedat/core/routes";
|
|
9
|
+
|
|
10
|
+
export const RemediationCrumbs = ({ rule, ruleImplementation }) => {
|
|
11
|
+
const { rule_result_id: ruleResultId } = useParams();
|
|
12
|
+
|
|
13
|
+
return rule?.name ? (
|
|
14
|
+
<Breadcrumb>
|
|
15
|
+
<Breadcrumb.Section as={Link} to={RULES} active={false}>
|
|
16
|
+
<FormattedMessage id="rules.crumbs.top" />
|
|
17
|
+
</Breadcrumb.Section>
|
|
18
|
+
{rule.name && (
|
|
19
|
+
<>
|
|
20
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
21
|
+
{ruleImplementation.id ? (
|
|
22
|
+
<Breadcrumb.Section
|
|
23
|
+
as={Link}
|
|
24
|
+
to={linkTo.RULE({ id: rule.id })}
|
|
25
|
+
active={_.isEmpty(ruleImplementation)}
|
|
26
|
+
>
|
|
27
|
+
{rule.name}
|
|
28
|
+
</Breadcrumb.Section>
|
|
29
|
+
) : (
|
|
30
|
+
<Breadcrumb.Section active>{rule.name}</Breadcrumb.Section>
|
|
31
|
+
)}
|
|
32
|
+
</>
|
|
33
|
+
)}
|
|
34
|
+
{ruleImplementation.id && (
|
|
35
|
+
<>
|
|
36
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
37
|
+
{ruleResultId ? (
|
|
38
|
+
<Breadcrumb.Section
|
|
39
|
+
as={Link}
|
|
40
|
+
to={linkTo.RULE_IMPLEMENTATION({
|
|
41
|
+
id: rule.id,
|
|
42
|
+
implementation_id: ruleImplementation.id,
|
|
43
|
+
})}
|
|
44
|
+
active={_.isEmpty(ruleImplementation)}
|
|
45
|
+
>
|
|
46
|
+
{ruleImplementation.implementation_key}
|
|
47
|
+
</Breadcrumb.Section>
|
|
48
|
+
) : (
|
|
49
|
+
<Breadcrumb.Section active>
|
|
50
|
+
{ruleImplementation.implementation_key}
|
|
51
|
+
</Breadcrumb.Section>
|
|
52
|
+
)}
|
|
53
|
+
</>
|
|
54
|
+
)}
|
|
55
|
+
{ruleResultId && (
|
|
56
|
+
<>
|
|
57
|
+
<Breadcrumb.Divider icon="right angle" />
|
|
58
|
+
<Breadcrumb.Section active>
|
|
59
|
+
<span>Result {ruleResultId}</span>
|
|
60
|
+
</Breadcrumb.Section>
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
</Breadcrumb>
|
|
64
|
+
) : null;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
RemediationCrumbs.propTypes = {
|
|
68
|
+
rule: PropTypes.object,
|
|
69
|
+
ruleImplementation: PropTypes.object,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const mapStateToProps = ({ rule, ruleImplementation }) => ({
|
|
73
|
+
rule,
|
|
74
|
+
ruleImplementation,
|
|
75
|
+
});
|
|
76
|
+
export default connect(mapStateToProps)(RemediationCrumbs);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { applyTemplate, validateContent } from "@truedat/df/utils";
|
|
6
|
+
import { connect } from "react-redux";
|
|
7
|
+
import { useParams } from "react-router-dom";
|
|
8
|
+
import { Button, Form } from "semantic-ui-react";
|
|
9
|
+
import { createRemediation, updateRemediation } from "../routines";
|
|
10
|
+
|
|
11
|
+
const DynamicForm = React.lazy(() =>
|
|
12
|
+
import("@truedat/df/components/DynamicForm")
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const TemplateSelector = React.lazy(() =>
|
|
16
|
+
import("@truedat/df/templates/components/TemplateSelector")
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export const RemediationForm = ({
|
|
20
|
+
createRemediation,
|
|
21
|
+
updateRemediation,
|
|
22
|
+
templates,
|
|
23
|
+
remediation,
|
|
24
|
+
onSave,
|
|
25
|
+
latestResultId,
|
|
26
|
+
}) => {
|
|
27
|
+
const { formatMessage } = useIntl();
|
|
28
|
+
const [template, setTemplate] = useState();
|
|
29
|
+
const [content, setContent] = useState();
|
|
30
|
+
const {
|
|
31
|
+
id,
|
|
32
|
+
implementation_id: implementationId,
|
|
33
|
+
rule_result_id: ruleResultId,
|
|
34
|
+
} = useParams();
|
|
35
|
+
|
|
36
|
+
const isValidContent = _.flow(validateContent(template), _.isEmpty);
|
|
37
|
+
const isValid = _.has("name")(template) && isValidContent(content);
|
|
38
|
+
|
|
39
|
+
const handleTemplateSelected = (e, { value }) => {
|
|
40
|
+
setTemplate(_.find({ id: value }, templates));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleContentChange = (content) => {
|
|
44
|
+
setContent(content);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleSubmit = () => {
|
|
48
|
+
const actionFunction = _.has("id")(remediation)
|
|
49
|
+
? updateRemediation
|
|
50
|
+
: createRemediation;
|
|
51
|
+
|
|
52
|
+
actionFunction({
|
|
53
|
+
remediation: {
|
|
54
|
+
...remediation,
|
|
55
|
+
df_name: template?.name,
|
|
56
|
+
df_content: template ? applyTemplate(template)(content) : {},
|
|
57
|
+
},
|
|
58
|
+
id,
|
|
59
|
+
implementation_id: implementationId,
|
|
60
|
+
rule_result_id: ruleResultId || latestResultId,
|
|
61
|
+
});
|
|
62
|
+
onSave();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
_.flow(
|
|
67
|
+
_.cond([
|
|
68
|
+
[(templates) => _.size(templates) === 1, _.head],
|
|
69
|
+
[
|
|
70
|
+
() => !_.isEmpty(remediation),
|
|
71
|
+
_.find(_.propEq("name")(remediation?.df_name)),
|
|
72
|
+
],
|
|
73
|
+
]),
|
|
74
|
+
(template) => {
|
|
75
|
+
setTemplate(template);
|
|
76
|
+
return template;
|
|
77
|
+
},
|
|
78
|
+
(template) => applyTemplate(template)(remediation?.df_content),
|
|
79
|
+
setContent
|
|
80
|
+
)(templates);
|
|
81
|
+
}, [remediation, templates]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Form aria-label="remediation-form">
|
|
85
|
+
{_.size(templates) > 1 && (
|
|
86
|
+
<TemplateSelector
|
|
87
|
+
name="template"
|
|
88
|
+
selectedValue={_.prop("id")(template)}
|
|
89
|
+
onChange={handleTemplateSelected}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
{!template ? null : (
|
|
94
|
+
<DynamicForm
|
|
95
|
+
template={template}
|
|
96
|
+
onChange={handleContentChange}
|
|
97
|
+
content={content || {}}
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
<Button
|
|
102
|
+
primary
|
|
103
|
+
disabled={!isValid}
|
|
104
|
+
onClick={handleSubmit}
|
|
105
|
+
content={formatMessage({
|
|
106
|
+
id: _.isEmpty(remediation) ? "actions.create" : "actions.save",
|
|
107
|
+
})}
|
|
108
|
+
/>
|
|
109
|
+
</Form>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
RemediationForm.propTypes = {
|
|
114
|
+
createRemediation: PropTypes.func.isRequired,
|
|
115
|
+
updateRemediation: PropTypes.func,
|
|
116
|
+
templates: PropTypes.array,
|
|
117
|
+
remediation: PropTypes.object,
|
|
118
|
+
onSave: PropTypes.func,
|
|
119
|
+
latestResultId: PropTypes.number,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const mapStateToProps = ({ templates }) => ({
|
|
123
|
+
templates,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export default connect(mapStateToProps, {
|
|
127
|
+
createRemediation,
|
|
128
|
+
updateRemediation,
|
|
129
|
+
})(RemediationForm);
|
|
@@ -0,0 +1,161 @@
|
|
|
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 { useIntl } from "react-intl";
|
|
6
|
+
import { useParams } from "react-router-dom";
|
|
7
|
+
import { Button, Header, Icon, Grid, Segment } from "semantic-ui-react";
|
|
8
|
+
import { FormattedMessage } from "react-intl";
|
|
9
|
+
import { Loading } from "@truedat/core/components";
|
|
10
|
+
import { ConfirmModal } from "@truedat/core/components";
|
|
11
|
+
import {
|
|
12
|
+
fetchRemediation,
|
|
13
|
+
clearRemediation,
|
|
14
|
+
deleteRemediation,
|
|
15
|
+
} from "../routines";
|
|
16
|
+
import NewRemediation from "./NewRemediation";
|
|
17
|
+
import RemediationForm from "./RemediationForm";
|
|
18
|
+
import "../styles/remediationPlan.less";
|
|
19
|
+
|
|
20
|
+
const DynamicFormViewer = React.lazy(() =>
|
|
21
|
+
import("@truedat/df/components/DynamicFormViewer")
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const RemediationPlan = ({
|
|
25
|
+
remediationLoading,
|
|
26
|
+
className,
|
|
27
|
+
deleteRemediation,
|
|
28
|
+
fetchRemediation,
|
|
29
|
+
clearRemediation,
|
|
30
|
+
remediation,
|
|
31
|
+
templates,
|
|
32
|
+
latestResultId,
|
|
33
|
+
manageRemediations,
|
|
34
|
+
}) => {
|
|
35
|
+
const {
|
|
36
|
+
implementation_id: implementationId,
|
|
37
|
+
rule_result_id: paramsRuleResultId,
|
|
38
|
+
} = useParams();
|
|
39
|
+
const { formatMessage } = useIntl();
|
|
40
|
+
const [isEdit, setIsEdit] = useState(false);
|
|
41
|
+
const [ruleResultId, setRuleResultId] = useState(null);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
setRuleResultId(paramsRuleResultId || latestResultId);
|
|
45
|
+
}, [paramsRuleResultId, latestResultId]);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
ruleResultId && fetchRemediation({ rule_result_id: ruleResultId });
|
|
49
|
+
}, [fetchRemediation, ruleResultId]);
|
|
50
|
+
|
|
51
|
+
useEffect(
|
|
52
|
+
() => () => {
|
|
53
|
+
clearRemediation();
|
|
54
|
+
},
|
|
55
|
+
[clearRemediation]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const onSave = () => {
|
|
59
|
+
setIsEdit(false);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return !ruleResultId ? null : remediationLoading ? (
|
|
63
|
+
<Loading />
|
|
64
|
+
) : _.isEmpty(remediation) && manageRemediations ? (
|
|
65
|
+
<NewRemediation onSaveRemediation={onSave} />
|
|
66
|
+
) : (
|
|
67
|
+
<>
|
|
68
|
+
<Header as="h2">
|
|
69
|
+
<Icon circular name="rain" />
|
|
70
|
+
<Header.Content>
|
|
71
|
+
<FormattedMessage
|
|
72
|
+
id={!isEdit ? "remediation" : "remediation.actions.edit"}
|
|
73
|
+
/>
|
|
74
|
+
</Header.Content>
|
|
75
|
+
</Header>
|
|
76
|
+
|
|
77
|
+
<Segment className={className}>
|
|
78
|
+
{!_.isEmpty(remediation) && manageRemediations ? (
|
|
79
|
+
<div className="ui actions remediation">
|
|
80
|
+
<ConfirmModal
|
|
81
|
+
icon="trash"
|
|
82
|
+
trigger={<Button secondary icon="trash" basic color="red" />}
|
|
83
|
+
header={
|
|
84
|
+
<FormattedMessage id="remediation.actions.delete.confirmation.header" />
|
|
85
|
+
}
|
|
86
|
+
content={
|
|
87
|
+
<FormattedMessage
|
|
88
|
+
id="remediation.actions.delete.confirmation.content"
|
|
89
|
+
values={{ ruleResultId: <b>{ruleResultId}</b> }}
|
|
90
|
+
/>
|
|
91
|
+
}
|
|
92
|
+
onConfirm={() =>
|
|
93
|
+
deleteRemediation({
|
|
94
|
+
implementation_id: implementationId,
|
|
95
|
+
rule_result_id: ruleResultId,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
/>
|
|
99
|
+
<Button
|
|
100
|
+
className="button-edit-remediation"
|
|
101
|
+
primary
|
|
102
|
+
onClick={() => setIsEdit(!isEdit)}
|
|
103
|
+
content={formatMessage({
|
|
104
|
+
id: !isEdit ? "actions.edit" : "actions.cancel",
|
|
105
|
+
})}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
) : null}
|
|
109
|
+
|
|
110
|
+
<Grid>
|
|
111
|
+
<Grid.Column width={8}>
|
|
112
|
+
{!isEdit && remediation?.df_name ? (
|
|
113
|
+
<DynamicFormViewer
|
|
114
|
+
template={_.find(_.propEq("name", remediation.df_name))(
|
|
115
|
+
templates
|
|
116
|
+
)}
|
|
117
|
+
content={remediation.df_content}
|
|
118
|
+
/>
|
|
119
|
+
) : (
|
|
120
|
+
<RemediationForm
|
|
121
|
+
onSave={onSave}
|
|
122
|
+
remediation={remediation}
|
|
123
|
+
latestResultId={latestResultId}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
</Grid.Column>
|
|
127
|
+
</Grid>
|
|
128
|
+
</Segment>
|
|
129
|
+
</>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
RemediationPlan.propTypes = {
|
|
134
|
+
remediationLoading: PropTypes.bool,
|
|
135
|
+
className: PropTypes.string,
|
|
136
|
+
deleteRemediation: PropTypes.func,
|
|
137
|
+
clearRemediation: PropTypes.func,
|
|
138
|
+
fetchRemediation: PropTypes.func,
|
|
139
|
+
remediation: PropTypes.object,
|
|
140
|
+
templates: PropTypes.array,
|
|
141
|
+
latestResultId: PropTypes.number,
|
|
142
|
+
manageRemediations: PropTypes.bool,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const mapStateToProps = ({
|
|
146
|
+
templates,
|
|
147
|
+
remediation,
|
|
148
|
+
remediationLoading,
|
|
149
|
+
remediationActions,
|
|
150
|
+
}) => ({
|
|
151
|
+
templates,
|
|
152
|
+
remediation,
|
|
153
|
+
remediationLoading,
|
|
154
|
+
manageRemediations: _.has("create")(remediationActions),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
export default connect(mapStateToProps, {
|
|
158
|
+
deleteRemediation,
|
|
159
|
+
fetchRemediation,
|
|
160
|
+
clearRemediation,
|
|
161
|
+
})(RemediationPlan);
|
|
@@ -13,7 +13,7 @@ import RuleImplementationTabs from "./RuleImplementationTabs";
|
|
|
13
13
|
import ImplementationResultBar from "./ImplementationResultBar";
|
|
14
14
|
|
|
15
15
|
const getAvailableActions = (props, formatMessage) => {
|
|
16
|
-
const contentActions = [
|
|
16
|
+
const contentActions = _.isEmpty(props?.ruleImplementation) ? [] : [
|
|
17
17
|
{
|
|
18
18
|
key: "delete",
|
|
19
19
|
value: "delete",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useEffect } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
5
|
import { connect } from "react-redux";
|
|
@@ -126,6 +126,7 @@ RuleImplementationResults.propTypes = {
|
|
|
126
126
|
rule: PropTypes.object,
|
|
127
127
|
customColumns: PropTypes.array,
|
|
128
128
|
isAdmin: PropTypes.bool,
|
|
129
|
+
ruleResult: PropTypes.object,
|
|
129
130
|
};
|
|
130
131
|
|
|
131
132
|
const mapStateToProps = (state) => ({
|
|
@@ -38,7 +38,10 @@ export const RuleResultRow = ({
|
|
|
38
38
|
>
|
|
39
39
|
<DateTime className="rule-result-date" value={ruleResult.date} />
|
|
40
40
|
{!_.isEmpty(ruleResult.details) ? (
|
|
41
|
-
<Icon name="info
|
|
41
|
+
<Icon circular inverted name="info" color="blue" size="small" />
|
|
42
|
+
) : null}
|
|
43
|
+
{ruleResult.has_remediation ? (
|
|
44
|
+
<Icon circular inverted name="rain" color="blue" size="small" />
|
|
42
45
|
) : null}
|
|
43
46
|
</Link>
|
|
44
47
|
</Table.Cell>
|