@truedat/qx 5.17.2 → 5.18.0
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/package.json +3 -3
- package/src/api.js +24 -1
- package/src/components/QxRoutes.js +12 -5
- package/src/components/common/ClauseViewer.js +129 -0
- package/src/components/common/ResourceSelector.js +7 -2
- package/src/components/common/expressions/Clauses.js +15 -3
- package/src/components/common/expressions/Condition.js +8 -6
- package/src/components/common/expressions/ShapeSelector.js +18 -8
- package/src/components/common/expressions/__tests__/ShapeSelector.spec.js +2 -2
- package/src/components/dataViews/DataViewEditor.js +4 -3
- package/src/components/dataViews/__tests__/__snapshots__/DataViewEditor.spec.js.snap +2 -2
- package/src/components/dataViews/queryableFunctions.js +15 -9
- package/src/components/functions/FunctionEditor.js +3 -2
- package/src/components/functions/__tests__/__snapshots__/FunctionEditor.spec.js.snap +4 -4
- package/src/components/qualityControls/EditQualityControl.js +73 -0
- package/src/components/qualityControls/NewDraftQualityControl.js +77 -0
- package/src/components/qualityControls/NewQualityControl.js +81 -0
- package/src/components/qualityControls/QualityControl.js +93 -0
- package/src/components/qualityControls/QualityControlActions.js +67 -0
- package/src/components/qualityControls/QualityControlCrumbs.js +23 -0
- package/src/components/qualityControls/QualityControlEditor.js +271 -0
- package/src/components/qualityControls/QualityControlHeader.js +64 -0
- package/src/components/qualityControls/QualityControlHistory.js +81 -0
- package/src/components/qualityControls/QualityControlRoutes.js +84 -0
- package/src/components/qualityControls/QualityControlRow.js +24 -0
- package/src/components/qualityControls/QualityControlTabs.js +34 -0
- package/src/components/qualityControls/QualityControls.js +66 -0
- package/src/components/qualityControls/QualityControlsTable.js +139 -0
- package/src/components/qualityControls/ResultCriteria.js +120 -0
- package/src/components/qualityControls/ResultType.js +57 -0
- package/src/components/qualityControls/resultCriterias/Deviation.js +89 -0
- package/src/components/qualityControls/resultCriterias/ErrorsNumber.js +88 -0
- package/src/components/qualityControls/resultCriterias/Percentage.js +89 -0
- package/src/components/search/FilterDropdown.js +76 -0
- package/src/components/search/FilterItem.js +49 -0
- package/src/components/search/FilterMultilevelDropdown.js +200 -0
- package/src/components/search/HierarchyFilterDropdown.js +116 -0
- package/src/components/search/QualityControlFilters.js +60 -0
- package/src/components/search/QualityControlSelectedFilters.js +56 -0
- package/src/components/search/QualityControlsSearch.js +30 -0
- package/src/components/search/SearchContext.js +180 -0
- package/src/hooks/useQualityControls.js +74 -0
- package/src/styles/Expression.less +39 -4
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useContext } from "react";
|
|
3
|
+
import { FormattedMessage } from "react-intl";
|
|
4
|
+
import { Table, Header, Icon } from "semantic-ui-react";
|
|
5
|
+
import { useIntl } from "react-intl";
|
|
6
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
7
|
+
|
|
8
|
+
import QxContext from "../QxContext";
|
|
9
|
+
|
|
10
|
+
const translateDecorator = (id) =>
|
|
11
|
+
id ? <FormattedMessage id={id} defaultMessage={id} /> : null;
|
|
12
|
+
|
|
13
|
+
export default function QualityControlsHistory() {
|
|
14
|
+
const { qualityControl, loading } = useContext(QxContext);
|
|
15
|
+
const versions = _.propOr([], "versions")(qualityControl);
|
|
16
|
+
|
|
17
|
+
const columns = [
|
|
18
|
+
{
|
|
19
|
+
name: "name",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "version",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "status",
|
|
26
|
+
fieldSelector: ({ status }) =>
|
|
27
|
+
translateDecorator(`quality_control.status.${status}`),
|
|
28
|
+
width: 2,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const { formatMessage } = useIntl();
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
{!_.isEmpty(versions) && (
|
|
36
|
+
<Table sortable>
|
|
37
|
+
<Table.Header>
|
|
38
|
+
<Table.Row>
|
|
39
|
+
{columns
|
|
40
|
+
? columns.map((column, key) => (
|
|
41
|
+
<Table.HeaderCell
|
|
42
|
+
key={key}
|
|
43
|
+
width={column.width}
|
|
44
|
+
content={formatMessage({
|
|
45
|
+
id: `quality_control.form.${
|
|
46
|
+
column.header || column.name
|
|
47
|
+
}`,
|
|
48
|
+
defaultMessage: column.name,
|
|
49
|
+
})}
|
|
50
|
+
className={_.path("sort.name")(column) ? "" : "disabled"}
|
|
51
|
+
/>
|
|
52
|
+
))
|
|
53
|
+
: null}
|
|
54
|
+
</Table.Row>
|
|
55
|
+
</Table.Header>
|
|
56
|
+
<Table.Body>
|
|
57
|
+
{versions.map((version, i) => (
|
|
58
|
+
<Table.Row key={i}>
|
|
59
|
+
{columns.map((column, i) => (
|
|
60
|
+
<Table.Cell
|
|
61
|
+
key={i}
|
|
62
|
+
textAlign={column.textAlign}
|
|
63
|
+
content={columnDecorator(column)(version)}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</Table.Row>
|
|
67
|
+
))}
|
|
68
|
+
</Table.Body>
|
|
69
|
+
</Table>
|
|
70
|
+
)}
|
|
71
|
+
{_.isEmpty(versions) && !loading && (
|
|
72
|
+
<Header as="h4">
|
|
73
|
+
<Icon name="search" />
|
|
74
|
+
<Header.Content>
|
|
75
|
+
{formatMessage({ id: "ruleImplementations.search.results.empty" })}
|
|
76
|
+
</Header.Content>
|
|
77
|
+
</Header>
|
|
78
|
+
)}
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Route, Switch } from "react-router-dom";
|
|
3
|
+
import { Unauthorized } from "@truedat/core/components";
|
|
4
|
+
import { useAuthorized } from "@truedat/core/hooks";
|
|
5
|
+
import {
|
|
6
|
+
QUALITY_CONTROLS,
|
|
7
|
+
QUALITY_CONTROL_NEW,
|
|
8
|
+
QUALITY_CONTROL_EDIT,
|
|
9
|
+
QUALITY_CONTROL_NEW_DRAFT,
|
|
10
|
+
QUALITY_CONTROL,
|
|
11
|
+
} from "@truedat/core/routes";
|
|
12
|
+
import { QUALITY_CONTROL_HISTORY } from "../../../../core/src/routes";
|
|
13
|
+
import { SearchContextProvider } from "../search/SearchContext";
|
|
14
|
+
import QualityControls from "./QualityControls";
|
|
15
|
+
import QualityControl from "./QualityControl";
|
|
16
|
+
import QualityControlHeader from "./QualityControlHeader";
|
|
17
|
+
import QualityControlHistory from "./QualityControlHistory";
|
|
18
|
+
import NewQualityControl from "./NewQualityControl";
|
|
19
|
+
import EditQualityControl from "./EditQualityControl";
|
|
20
|
+
import NewDraftQualityControl from "./NewDraftQualityControl";
|
|
21
|
+
|
|
22
|
+
export default function QxRoutes() {
|
|
23
|
+
const authorized = useAuthorized("quality_control");
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<SearchContextProvider
|
|
27
|
+
initialSortColumn="updated_at"
|
|
28
|
+
initialSortDirection="descending"
|
|
29
|
+
>
|
|
30
|
+
<Switch>
|
|
31
|
+
<Route
|
|
32
|
+
exact
|
|
33
|
+
path={QUALITY_CONTROLS}
|
|
34
|
+
render={() => (authorized ? <QualityControls /> : <Unauthorized />)}
|
|
35
|
+
/>
|
|
36
|
+
<Route
|
|
37
|
+
exact
|
|
38
|
+
path={QUALITY_CONTROL_NEW}
|
|
39
|
+
render={() => (authorized ? <NewQualityControl /> : <Unauthorized />)}
|
|
40
|
+
/>
|
|
41
|
+
<Route
|
|
42
|
+
exact
|
|
43
|
+
path={QUALITY_CONTROL_EDIT}
|
|
44
|
+
render={() =>
|
|
45
|
+
authorized ? <EditQualityControl /> : <Unauthorized />
|
|
46
|
+
}
|
|
47
|
+
/>
|
|
48
|
+
<Route
|
|
49
|
+
exact
|
|
50
|
+
path={QUALITY_CONTROL_NEW_DRAFT}
|
|
51
|
+
render={() =>
|
|
52
|
+
authorized ? <NewDraftQualityControl /> : <Unauthorized />
|
|
53
|
+
}
|
|
54
|
+
/>
|
|
55
|
+
<Route
|
|
56
|
+
exact
|
|
57
|
+
path={QUALITY_CONTROL}
|
|
58
|
+
render={() =>
|
|
59
|
+
authorized ? (
|
|
60
|
+
<QualityControlHeader>
|
|
61
|
+
<QualityControl />
|
|
62
|
+
</QualityControlHeader>
|
|
63
|
+
) : (
|
|
64
|
+
<Unauthorized />
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
68
|
+
<Route
|
|
69
|
+
exact
|
|
70
|
+
path={QUALITY_CONTROL_HISTORY}
|
|
71
|
+
render={() =>
|
|
72
|
+
authorized ? (
|
|
73
|
+
<QualityControlHeader>
|
|
74
|
+
<QualityControlHistory />
|
|
75
|
+
</QualityControlHeader>
|
|
76
|
+
) : (
|
|
77
|
+
<Unauthorized />
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
/>
|
|
81
|
+
</Switch>
|
|
82
|
+
</SearchContextProvider>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { Table } from "semantic-ui-react";
|
|
5
|
+
import { columnDecorator } from "@truedat/core/services";
|
|
6
|
+
|
|
7
|
+
export default function QualityControlRow({ columns, qualityControl }) {
|
|
8
|
+
return _.isEmpty(columns) || _.isEmpty(qualityControl) ? null : (
|
|
9
|
+
<Table.Row>
|
|
10
|
+
{columns.map((column, i) => (
|
|
11
|
+
<Table.Cell
|
|
12
|
+
key={i}
|
|
13
|
+
textAlign={column.textAlign}
|
|
14
|
+
content={columnDecorator(column)(qualityControl)}
|
|
15
|
+
/>
|
|
16
|
+
))}
|
|
17
|
+
</Table.Row>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
QualityControlRow.propTypes = {
|
|
22
|
+
columns: PropTypes.array,
|
|
23
|
+
qualityControl: PropTypes.object,
|
|
24
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Menu } from "semantic-ui-react";
|
|
3
|
+
import { Link, useRouteMatch, useParams } from "react-router-dom";
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import {
|
|
6
|
+
QUALITY_CONTROL,
|
|
7
|
+
QUALITY_CONTROL_HISTORY,
|
|
8
|
+
linkTo,
|
|
9
|
+
} from "@truedat/core/routes";
|
|
10
|
+
|
|
11
|
+
export default function QualityControlTabs() {
|
|
12
|
+
const match = useRouteMatch();
|
|
13
|
+
const { id } = useParams();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Menu attached="top" pointing secondary tabular>
|
|
17
|
+
<Menu.Item
|
|
18
|
+
active={match.path === QUALITY_CONTROL}
|
|
19
|
+
as={Link}
|
|
20
|
+
to={linkTo.QUALITY_CONTROL({ id })}
|
|
21
|
+
>
|
|
22
|
+
<FormattedMessage id="quality_control.tabs.quality_control" />
|
|
23
|
+
</Menu.Item>
|
|
24
|
+
|
|
25
|
+
<Menu.Item
|
|
26
|
+
active={match.path === QUALITY_CONTROL_HISTORY}
|
|
27
|
+
as={Link}
|
|
28
|
+
to={linkTo.QUALITY_CONTROL_HISTORY({ id })}
|
|
29
|
+
>
|
|
30
|
+
<FormattedMessage id="quality_control.tabs.history" />
|
|
31
|
+
</Menu.Item>
|
|
32
|
+
</Menu>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
Container,
|
|
8
|
+
Header,
|
|
9
|
+
Icon,
|
|
10
|
+
Segment,
|
|
11
|
+
Grid,
|
|
12
|
+
} from "semantic-ui-react";
|
|
13
|
+
import { QUALITY_CONTROL_NEW } from "@truedat/core/routes";
|
|
14
|
+
import useAuthorizedAction from "@truedat/core/hooks/useAuthorizedAction";
|
|
15
|
+
import Loading from "@truedat/core/src/components/Loading";
|
|
16
|
+
import QualityControlsSearch from "../search/QualityControlsSearch";
|
|
17
|
+
import QualityControlsTable from "./QualityControlsTable";
|
|
18
|
+
|
|
19
|
+
export default function QualityControls() {
|
|
20
|
+
const { formatMessage } = useIntl();
|
|
21
|
+
|
|
22
|
+
const { data, loading } = useAuthorizedAction({
|
|
23
|
+
action: "createQualityControls",
|
|
24
|
+
});
|
|
25
|
+
const canCreate = _.prop("has_any_domain")(data);
|
|
26
|
+
return (
|
|
27
|
+
<Segment loading={false}>
|
|
28
|
+
<Header as="h2">
|
|
29
|
+
<Icon circular name="archive" />
|
|
30
|
+
<Header.Content>
|
|
31
|
+
<FormattedMessage id="quality_controls.header" />
|
|
32
|
+
<Header.Subheader>
|
|
33
|
+
<FormattedMessage id="quality_controls.subheader" />
|
|
34
|
+
</Header.Subheader>
|
|
35
|
+
</Header.Content>
|
|
36
|
+
</Header>
|
|
37
|
+
<Grid>
|
|
38
|
+
<Grid.Column width={8}>
|
|
39
|
+
<QualityControlsSearch />
|
|
40
|
+
</Grid.Column>
|
|
41
|
+
|
|
42
|
+
<Grid.Column width={8}>
|
|
43
|
+
<Container textAlign="right">
|
|
44
|
+
{loading ? (
|
|
45
|
+
<Loading />
|
|
46
|
+
) : (
|
|
47
|
+
<>
|
|
48
|
+
{canCreate ? (
|
|
49
|
+
<Button
|
|
50
|
+
primary
|
|
51
|
+
as={Link}
|
|
52
|
+
to={QUALITY_CONTROL_NEW}
|
|
53
|
+
content={formatMessage({
|
|
54
|
+
id: "quality_controls.actions.create",
|
|
55
|
+
})}
|
|
56
|
+
/>
|
|
57
|
+
) : null}
|
|
58
|
+
</>
|
|
59
|
+
)}
|
|
60
|
+
</Container>
|
|
61
|
+
</Grid.Column>
|
|
62
|
+
</Grid>
|
|
63
|
+
<QualityControlsTable />
|
|
64
|
+
</Segment>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
|
+
import { sortColumn as sortHandler } from "@truedat/core/services/sort";
|
|
6
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
7
|
+
import { Table, Header, Icon } from "semantic-ui-react";
|
|
8
|
+
import { linkTo } from "@truedat/core/routes";
|
|
9
|
+
import Moment from "react-moment";
|
|
10
|
+
|
|
11
|
+
import { useSearchContext } from "../search/SearchContext";
|
|
12
|
+
import QualityControlRow from "./QualityControlRow";
|
|
13
|
+
|
|
14
|
+
const translateDecorator = (id) =>
|
|
15
|
+
id ? <FormattedMessage id={id} defaultMessage={id} /> : null;
|
|
16
|
+
|
|
17
|
+
const DateDecorator = ({ date }) => {
|
|
18
|
+
const { locale } = useIntl();
|
|
19
|
+
return date ? (
|
|
20
|
+
<Moment locale={locale} date={date} format="YYYY-MM-DD HH:mm" />
|
|
21
|
+
) : null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
DateDecorator.propTypes = {
|
|
25
|
+
date: PropTypes.string,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default function QualityControlsTable() {
|
|
29
|
+
const {
|
|
30
|
+
qualityControls,
|
|
31
|
+
loadingSearch: loading,
|
|
32
|
+
sortColumn,
|
|
33
|
+
sortDirection,
|
|
34
|
+
setSortColumn,
|
|
35
|
+
setSortDirection,
|
|
36
|
+
} = useSearchContext();
|
|
37
|
+
const { formatMessage } = useIntl();
|
|
38
|
+
|
|
39
|
+
const columns = [
|
|
40
|
+
{
|
|
41
|
+
name: "name",
|
|
42
|
+
fieldSelector: _.pick(["id", "name"]),
|
|
43
|
+
fieldDecorator: ({ id, name }) => (
|
|
44
|
+
<Link to={linkTo.QUALITY_CONTROL({ id })}>{name}</Link>
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "df_type",
|
|
49
|
+
sort: { name: "df_type" },
|
|
50
|
+
width: 2,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "result_type",
|
|
54
|
+
sort: { name: "result_type" },
|
|
55
|
+
fieldSelector: ({ result_type }) =>
|
|
56
|
+
result_type
|
|
57
|
+
? translateDecorator(`quality_control.result_type.${result_type}`)
|
|
58
|
+
: "-",
|
|
59
|
+
width: 2,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "status",
|
|
63
|
+
sort: { name: "status" },
|
|
64
|
+
fieldSelector: ({ status }) =>
|
|
65
|
+
translateDecorator(`quality_control.status.${status}`),
|
|
66
|
+
width: 2,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "updated_at",
|
|
70
|
+
sort: { name: "updated_at" },
|
|
71
|
+
fieldSelector: ({ updated_at }) => ({
|
|
72
|
+
date: updated_at,
|
|
73
|
+
}),
|
|
74
|
+
width: 2,
|
|
75
|
+
fieldDecorator: DateDecorator,
|
|
76
|
+
textAlign: "center",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<>
|
|
82
|
+
{!_.isEmpty(qualityControls) && (
|
|
83
|
+
<Table sortable>
|
|
84
|
+
<Table.Header>
|
|
85
|
+
<Table.Row>
|
|
86
|
+
{columns
|
|
87
|
+
? columns.map((column, key) => (
|
|
88
|
+
<Table.HeaderCell
|
|
89
|
+
key={key}
|
|
90
|
+
width={column.width}
|
|
91
|
+
content={formatMessage({
|
|
92
|
+
id: `quality_control.form.${
|
|
93
|
+
column.header || column.name
|
|
94
|
+
}`,
|
|
95
|
+
defaultMessage: column.name,
|
|
96
|
+
})}
|
|
97
|
+
sorted={
|
|
98
|
+
_.path("sort.name")(column) === sortColumn
|
|
99
|
+
? sortDirection
|
|
100
|
+
: null
|
|
101
|
+
}
|
|
102
|
+
className={_.path("sort.name")(column) ? "" : "disabled"}
|
|
103
|
+
onClick={() =>
|
|
104
|
+
sortHandler(
|
|
105
|
+
column,
|
|
106
|
+
() => {},
|
|
107
|
+
setSortDirection,
|
|
108
|
+
setSortColumn,
|
|
109
|
+
sortDirection,
|
|
110
|
+
sortColumn
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
))
|
|
115
|
+
: null}
|
|
116
|
+
</Table.Row>
|
|
117
|
+
</Table.Header>
|
|
118
|
+
<Table.Body>
|
|
119
|
+
{qualityControls.map((qc, i) => (
|
|
120
|
+
<QualityControlRow
|
|
121
|
+
key={i}
|
|
122
|
+
qualityControl={qc}
|
|
123
|
+
columns={columns}
|
|
124
|
+
/>
|
|
125
|
+
))}
|
|
126
|
+
</Table.Body>
|
|
127
|
+
</Table>
|
|
128
|
+
)}
|
|
129
|
+
{_.isEmpty(qualityControls) && !loading && (
|
|
130
|
+
<Header as="h4">
|
|
131
|
+
<Icon name="search" />
|
|
132
|
+
<Header.Content>
|
|
133
|
+
{formatMessage({ id: "ruleImplementations.search.results.empty" })}
|
|
134
|
+
</Header.Content>
|
|
135
|
+
</Header>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { FormattedMessage } from "react-intl";
|
|
4
|
+
import { Header, Icon, Segment, List } from "semantic-ui-react";
|
|
5
|
+
|
|
6
|
+
export default function ResultCriteria({ qualityControl }) {
|
|
7
|
+
const { result_criteria, result_type } = qualityControl;
|
|
8
|
+
const resultCriteriaForType = {
|
|
9
|
+
percentage: <ResultCriteriaPercentage resultCriteria={result_criteria} />,
|
|
10
|
+
deviation: <ResultCriteriaDeviation resultCriteria={result_criteria} />,
|
|
11
|
+
errors_number: (
|
|
12
|
+
<ResultCriteriaErrorsNumber resultCriteria={result_criteria} />
|
|
13
|
+
),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
<Header as="h3">
|
|
19
|
+
<Icon name="flag checkered" size="small" />
|
|
20
|
+
<Header.Content>
|
|
21
|
+
<FormattedMessage id="quality_control.result_criteria" />
|
|
22
|
+
</Header.Content>
|
|
23
|
+
</Header>
|
|
24
|
+
<Segment>
|
|
25
|
+
{result_criteria ? (
|
|
26
|
+
<List>
|
|
27
|
+
<List.Item>
|
|
28
|
+
<List.Header>
|
|
29
|
+
<FormattedMessage id="quality.thresholds" />
|
|
30
|
+
</List.Header>
|
|
31
|
+
<List.Content>
|
|
32
|
+
<List.Description>
|
|
33
|
+
<FormattedMessage
|
|
34
|
+
id={`quality_control.result_type.${result_type}`}
|
|
35
|
+
/>
|
|
36
|
+
</List.Description>
|
|
37
|
+
</List.Content>
|
|
38
|
+
</List.Item>
|
|
39
|
+
{resultCriteriaForType[result_type]}
|
|
40
|
+
</List>
|
|
41
|
+
) : (
|
|
42
|
+
<FormattedMessage id="quality_control.result_criteria.empty" />
|
|
43
|
+
)}
|
|
44
|
+
</Segment>
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ResultCriteriaPercentage = ({ resultCriteria }) => (
|
|
50
|
+
<>
|
|
51
|
+
<List.Item>
|
|
52
|
+
<Icon name="circle" color="green" />
|
|
53
|
+
<List.Content>
|
|
54
|
+
<List.Header>
|
|
55
|
+
<FormattedMessage id="quality.goal" />
|
|
56
|
+
</List.Header>
|
|
57
|
+
<List.Description>{resultCriteria.goal}%</List.Description>
|
|
58
|
+
</List.Content>
|
|
59
|
+
</List.Item>
|
|
60
|
+
<List.Item>
|
|
61
|
+
<Icon name="circle" color="yellow" />
|
|
62
|
+
<List.Content>
|
|
63
|
+
<List.Header>
|
|
64
|
+
<FormattedMessage id="quality.threshold" />
|
|
65
|
+
</List.Header>
|
|
66
|
+
<List.Description>{resultCriteria.minimum}%</List.Description>
|
|
67
|
+
</List.Content>
|
|
68
|
+
</List.Item>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const ResultCriteriaDeviation = ({ resultCriteria }) => (
|
|
73
|
+
<>
|
|
74
|
+
<List.Item>
|
|
75
|
+
<Icon name="circle" color="green" />
|
|
76
|
+
<List.Content>
|
|
77
|
+
<List.Header>
|
|
78
|
+
<FormattedMessage id="quality.goal" />
|
|
79
|
+
</List.Header>
|
|
80
|
+
<List.Description>{resultCriteria.goal}%</List.Description>
|
|
81
|
+
</List.Content>
|
|
82
|
+
</List.Item>
|
|
83
|
+
<List.Item>
|
|
84
|
+
<Icon name="circle" color="yellow" />
|
|
85
|
+
<List.Content>
|
|
86
|
+
<List.Header>
|
|
87
|
+
<FormattedMessage id="quality.threshold" />
|
|
88
|
+
</List.Header>
|
|
89
|
+
<List.Description>{resultCriteria.maximum}%</List.Description>
|
|
90
|
+
</List.Content>
|
|
91
|
+
</List.Item>
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const ResultCriteriaErrorsNumber = ({ resultCriteria }) => (
|
|
96
|
+
<>
|
|
97
|
+
<List.Item>
|
|
98
|
+
<Icon name="circle" color="green" />
|
|
99
|
+
<List.Content>
|
|
100
|
+
<List.Header>
|
|
101
|
+
<FormattedMessage id="quality.goal" />
|
|
102
|
+
</List.Header>
|
|
103
|
+
<List.Description>{resultCriteria.goal}</List.Description>
|
|
104
|
+
</List.Content>
|
|
105
|
+
</List.Item>
|
|
106
|
+
<List.Item>
|
|
107
|
+
<Icon name="circle" color="yellow" />
|
|
108
|
+
<List.Content>
|
|
109
|
+
<List.Header>
|
|
110
|
+
<FormattedMessage id="quality.threshold" />
|
|
111
|
+
</List.Header>
|
|
112
|
+
<List.Description>{resultCriteria.maximum}</List.Description>
|
|
113
|
+
</List.Content>
|
|
114
|
+
</List.Item>
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
ResultCriteria.propTypes = {
|
|
119
|
+
qualityControl: PropTypes.object,
|
|
120
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useContext } from "react";
|
|
3
|
+
import { useIntl } from "react-intl";
|
|
4
|
+
import { Controller, useFormContext } from "react-hook-form";
|
|
5
|
+
import { Dropdown, Form, Segment } from "semantic-ui-react";
|
|
6
|
+
import QxContext from "@truedat/qx/components/QxContext";
|
|
7
|
+
|
|
8
|
+
import Deviation from "./resultCriterias/Deviation";
|
|
9
|
+
import ErrorsNumber from "./resultCriterias/ErrorsNumber";
|
|
10
|
+
import Percentage from "./resultCriterias/Percentage";
|
|
11
|
+
|
|
12
|
+
export default function ResultType() {
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
const { control, watch } = useFormContext();
|
|
15
|
+
const context = useContext(QxContext);
|
|
16
|
+
|
|
17
|
+
const resultType = watch("result_type");
|
|
18
|
+
const resultTypeOptions = _.map((v) => ({
|
|
19
|
+
key: v,
|
|
20
|
+
value: v,
|
|
21
|
+
text: formatMessage({ id: `quality_control.result_type.${v}` }),
|
|
22
|
+
}))(["percentage", "deviation", "errors_number"]);
|
|
23
|
+
|
|
24
|
+
const resultCriteriaForType = {
|
|
25
|
+
deviation: <Deviation />,
|
|
26
|
+
errors_number: <ErrorsNumber />,
|
|
27
|
+
percentage: <Percentage />,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Segment>
|
|
32
|
+
<Controller
|
|
33
|
+
name={"result_type"}
|
|
34
|
+
control={control}
|
|
35
|
+
rules={{ required: true }}
|
|
36
|
+
render={({ field: { onBlur, onChange, value } }) => (
|
|
37
|
+
<Form.Field required>
|
|
38
|
+
<label>
|
|
39
|
+
{formatMessage({ id: "quality_control.result_type" })}
|
|
40
|
+
</label>
|
|
41
|
+
<Dropdown
|
|
42
|
+
selection
|
|
43
|
+
placeholder={formatMessage({ id: "quality_control.result_type" })}
|
|
44
|
+
onBlur={onBlur}
|
|
45
|
+
options={resultTypeOptions}
|
|
46
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
47
|
+
value={value}
|
|
48
|
+
/>
|
|
49
|
+
</Form.Field>
|
|
50
|
+
)}
|
|
51
|
+
/>
|
|
52
|
+
<QxContext.Provider value={{ ...context, field: "result_criteria" }}>
|
|
53
|
+
{resultType ? resultCriteriaForType[resultType] : null}
|
|
54
|
+
</QxContext.Provider>
|
|
55
|
+
</Segment>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
3
|
+
import { Controller, useFormContext } from "react-hook-form";
|
|
4
|
+
import { Form } from "semantic-ui-react";
|
|
5
|
+
import QxContext from "@truedat/qx/components/QxContext";
|
|
6
|
+
import { FieldLabel } from "@truedat/core/components";
|
|
7
|
+
import { numberRules } from "@truedat/core/services/formRules";
|
|
8
|
+
|
|
9
|
+
export default function Deviation() {
|
|
10
|
+
const { formatMessage } = useIntl();
|
|
11
|
+
const { field } = useContext(QxContext);
|
|
12
|
+
const { control, watch } = useFormContext();
|
|
13
|
+
|
|
14
|
+
const maximumField = `${field}.maximum`;
|
|
15
|
+
const goalField = `${field}.goal`;
|
|
16
|
+
const maximum = watch(maximumField);
|
|
17
|
+
const goal = watch(goalField);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<Controller
|
|
22
|
+
name={goalField}
|
|
23
|
+
control={control}
|
|
24
|
+
rules={numberRules({
|
|
25
|
+
formatMessage,
|
|
26
|
+
minValue: 0,
|
|
27
|
+
maxValue: maximum,
|
|
28
|
+
required: true,
|
|
29
|
+
})}
|
|
30
|
+
render={({
|
|
31
|
+
field: { onBlur, onChange, value },
|
|
32
|
+
fieldState: { error },
|
|
33
|
+
}) => (
|
|
34
|
+
<FieldLabel
|
|
35
|
+
label={formatMessage({
|
|
36
|
+
id: "quality_control.result_criteria.deviation.goal",
|
|
37
|
+
})}
|
|
38
|
+
required
|
|
39
|
+
error={error?.message}
|
|
40
|
+
>
|
|
41
|
+
<Form.Input
|
|
42
|
+
autoComplete="off"
|
|
43
|
+
placeholder={formatMessage({
|
|
44
|
+
id: "quality_control.result_criteria.deviation.goal",
|
|
45
|
+
})}
|
|
46
|
+
error={!!error}
|
|
47
|
+
onBlur={onBlur}
|
|
48
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
49
|
+
value={value}
|
|
50
|
+
/>
|
|
51
|
+
</FieldLabel>
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
<Controller
|
|
55
|
+
name={maximumField}
|
|
56
|
+
control={control}
|
|
57
|
+
rules={numberRules({
|
|
58
|
+
formatMessage,
|
|
59
|
+
minValue: goal,
|
|
60
|
+
maxValue: 100,
|
|
61
|
+
required: true,
|
|
62
|
+
})}
|
|
63
|
+
render={({
|
|
64
|
+
field: { onBlur, onChange, value },
|
|
65
|
+
fieldState: { error },
|
|
66
|
+
}) => (
|
|
67
|
+
<FieldLabel
|
|
68
|
+
label={formatMessage({
|
|
69
|
+
id: "quality_control.result_criteria.deviation.maximum",
|
|
70
|
+
})}
|
|
71
|
+
required
|
|
72
|
+
error={error?.message}
|
|
73
|
+
>
|
|
74
|
+
<Form.Input
|
|
75
|
+
autoComplete="off"
|
|
76
|
+
placeholder={formatMessage({
|
|
77
|
+
id: "quality_control.result_criteria.deviation.maximum",
|
|
78
|
+
})}
|
|
79
|
+
error={!!error}
|
|
80
|
+
onBlur={onBlur}
|
|
81
|
+
onChange={(_e, { value }) => onChange(value)}
|
|
82
|
+
value={value}
|
|
83
|
+
/>
|
|
84
|
+
</FieldLabel>
|
|
85
|
+
)}
|
|
86
|
+
/>
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
}
|