@truedat/qx 6.1.0 → 6.1.1
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 +4 -4
- package/src/components/executions/ExecutionGroupsTable.js +9 -12
- package/src/components/qualityControls/QualityControlRoutes.js +124 -102
- package/src/components/qualityControls/QualityControls.js +23 -16
- package/src/components/qualityControls/QualityControlsTable.js +5 -3
- package/src/hooks/useExecutionGroups.js +4 -8
- package/src/components/search/FilterDropdown.js +0 -76
- package/src/components/search/FilterItem.js +0 -49
- package/src/components/search/FilterMultilevelDropdown.js +0 -200
- package/src/components/search/HierarchyFilterDropdown.js +0 -116
- package/src/components/search/QualityControlFilters.js +0 -60
- package/src/components/search/QualityControlSelectedFilters.js +0 -56
- package/src/components/search/QualityControlsSearch.js +0 -30
- package/src/components/search/SearchContext.js +0 -210
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/qx",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.1",
|
|
4
4
|
"description": "Truedat Web Quality Experience package",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@testing-library/react": "^12.0.0",
|
|
36
36
|
"@testing-library/react-hooks": "^8.0.1",
|
|
37
37
|
"@testing-library/user-event": "^13.2.1",
|
|
38
|
-
"@truedat/test": "6.
|
|
38
|
+
"@truedat/test": "6.1.1",
|
|
39
39
|
"babel-jest": "^28.1.0",
|
|
40
40
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
41
41
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@truedat/core": "6.1.
|
|
87
|
+
"@truedat/core": "6.1.1",
|
|
88
88
|
"prop-types": "^15.8.1",
|
|
89
89
|
"react-hook-form": "^7.45.4",
|
|
90
90
|
"react-intl": "^5.20.10",
|
|
@@ -97,5 +97,5 @@
|
|
|
97
97
|
"react-dom": ">= 16.8.6 < 17",
|
|
98
98
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
99
99
|
},
|
|
100
|
-
"gitHead": "
|
|
100
|
+
"gitHead": "8ba2e1d68380956d654d6927a6117734f1c50076"
|
|
101
101
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React
|
|
2
|
+
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
|
-
import { FormattedMessage
|
|
4
|
+
import { FormattedMessage } from "react-intl";
|
|
5
5
|
import { Table, Header, Icon } from "semantic-ui-react";
|
|
6
6
|
import { columnDecorator } from "@truedat/core/services";
|
|
7
|
-
import {
|
|
7
|
+
import { useExecutionGroupsIndex } from "../../hooks/useExecutionGroups";
|
|
8
8
|
import ExecutionGroupLink from "./ExecutionGroupLink";
|
|
9
9
|
|
|
10
10
|
export const HeaderRow = ({ columns }) => (
|
|
@@ -38,6 +38,8 @@ ExecutionGroupRow.propTypes = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export default function ExecutionGroupsTable() {
|
|
41
|
+
const { data: executionGroups } = useExecutionGroupsIndex();
|
|
42
|
+
|
|
41
43
|
const columns = [
|
|
42
44
|
{
|
|
43
45
|
name: "quality_controls.table.header.created",
|
|
@@ -71,12 +73,6 @@ export default function ExecutionGroupsTable() {
|
|
|
71
73
|
},
|
|
72
74
|
];
|
|
73
75
|
|
|
74
|
-
const { listExecutionGroups, executionGroups } = useSearchContext();
|
|
75
|
-
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
listExecutionGroups();
|
|
78
|
-
}, []);
|
|
79
|
-
|
|
80
76
|
return (
|
|
81
77
|
<>
|
|
82
78
|
{!_.isEmpty(executionGroups) && (
|
|
@@ -85,9 +81,10 @@ export default function ExecutionGroupsTable() {
|
|
|
85
81
|
<HeaderRow columns={columns} />
|
|
86
82
|
</Table.Header>
|
|
87
83
|
<Table.Body>
|
|
88
|
-
{executionGroups
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
{executionGroups &&
|
|
85
|
+
executionGroups.map((props, key) => (
|
|
86
|
+
<ExecutionGroupRow key={key} columns={columns} {...props} />
|
|
87
|
+
))}
|
|
91
88
|
</Table.Body>
|
|
92
89
|
</Table>
|
|
93
90
|
)}
|
|
@@ -15,11 +15,15 @@ import {
|
|
|
15
15
|
QUALITY_CONTROL_HISTORY,
|
|
16
16
|
} from "@truedat/core/routes";
|
|
17
17
|
|
|
18
|
-
import { SearchContextProvider } from "
|
|
18
|
+
import { SearchContextProvider } from "@truedat/core/search/SearchContext";
|
|
19
19
|
import ExecutionGroupsTable from "../executions/ExecutionGroupsTable";
|
|
20
20
|
import ExecutionGroupsHeader from "../executions/ExecutionGroupsHeader";
|
|
21
21
|
import ExecutionGroupDetail from "../executions/executionGroupDetail";
|
|
22
22
|
|
|
23
|
+
import {
|
|
24
|
+
useQualityControlsSearch,
|
|
25
|
+
useQualityControlsFilters,
|
|
26
|
+
} from "../../hooks/useQualityControls";
|
|
23
27
|
import QualityControls from "./QualityControls";
|
|
24
28
|
import QualityControl from "./QualityControl";
|
|
25
29
|
import QualityControlHeader from "./QualityControlHeader";
|
|
@@ -31,108 +35,126 @@ import NewDraftQualityControl from "./NewDraftQualityControl";
|
|
|
31
35
|
|
|
32
36
|
export default function QxRoutes() {
|
|
33
37
|
const authorized = useAuthorized("quality_control");
|
|
38
|
+
const searchProps = {
|
|
39
|
+
initialSortColumn: "updated_at",
|
|
40
|
+
initialSortDirection: "descending",
|
|
41
|
+
useSearch: useQualityControlsSearch,
|
|
42
|
+
useFilters: useQualityControlsFilters,
|
|
43
|
+
};
|
|
34
44
|
|
|
35
45
|
return (
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<QualityControls
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
46
|
+
<Switch>
|
|
47
|
+
<Route
|
|
48
|
+
exact
|
|
49
|
+
path={QUALITY_CONTROLS_EXECUTION_GROUPS}
|
|
50
|
+
render={() =>
|
|
51
|
+
authorized ? (
|
|
52
|
+
<ExecutionGroupsHeader>
|
|
53
|
+
<ExecutionGroupsTable />
|
|
54
|
+
</ExecutionGroupsHeader>
|
|
55
|
+
) : (
|
|
56
|
+
<Unauthorized />
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
/>
|
|
60
|
+
<Route
|
|
61
|
+
exact
|
|
62
|
+
path={QUALITY_CONTROLS_EXECUTION_GROUP}
|
|
63
|
+
render={() =>
|
|
64
|
+
authorized ? <ExecutionGroupDetail /> : <Unauthorized />
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
<Route
|
|
68
|
+
exact
|
|
69
|
+
path={QUALITY_CONTROLS_PUBLISHED}
|
|
70
|
+
render={() =>
|
|
71
|
+
authorized ? (
|
|
72
|
+
<SearchContextProvider
|
|
73
|
+
{...searchProps}
|
|
74
|
+
defaultFilters={{ status: "published" }}
|
|
75
|
+
>
|
|
76
|
+
<QualityControls />
|
|
77
|
+
</SearchContextProvider>
|
|
78
|
+
) : (
|
|
79
|
+
<Unauthorized />
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
/>
|
|
83
|
+
<Route
|
|
84
|
+
exact
|
|
85
|
+
path={QUALITY_CONTROLS_DEPRECATED}
|
|
86
|
+
render={() =>
|
|
87
|
+
authorized ? (
|
|
88
|
+
<SearchContextProvider
|
|
89
|
+
{...searchProps}
|
|
90
|
+
defaultFilters={{ status: "deprecated" }}
|
|
91
|
+
>
|
|
92
|
+
<QualityControls />
|
|
93
|
+
</SearchContextProvider>
|
|
94
|
+
) : (
|
|
95
|
+
<Unauthorized />
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
/>
|
|
99
|
+
<Route
|
|
100
|
+
exact
|
|
101
|
+
path={QUALITY_CONTROLS_DRAFTS}
|
|
102
|
+
render={() =>
|
|
103
|
+
authorized ? (
|
|
104
|
+
<SearchContextProvider
|
|
105
|
+
{...searchProps}
|
|
106
|
+
defaultFilters={{ status: "draft" }}
|
|
107
|
+
>
|
|
108
|
+
<QualityControls />
|
|
109
|
+
</SearchContextProvider>
|
|
110
|
+
) : (
|
|
111
|
+
<Unauthorized />
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
/>
|
|
115
|
+
<Route
|
|
116
|
+
exact
|
|
117
|
+
path={QUALITY_CONTROL_NEW}
|
|
118
|
+
render={() => (authorized ? <NewQualityControl /> : <Unauthorized />)}
|
|
119
|
+
/>
|
|
120
|
+
<Route
|
|
121
|
+
exact
|
|
122
|
+
path={QUALITY_CONTROL_EDIT}
|
|
123
|
+
render={() => (authorized ? <EditQualityControl /> : <Unauthorized />)}
|
|
124
|
+
/>
|
|
125
|
+
<Route
|
|
126
|
+
exact
|
|
127
|
+
path={QUALITY_CONTROL_NEW_DRAFT}
|
|
128
|
+
render={() =>
|
|
129
|
+
authorized ? <NewDraftQualityControl /> : <Unauthorized />
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
<Route
|
|
133
|
+
exact
|
|
134
|
+
path={QUALITY_CONTROL}
|
|
135
|
+
render={() =>
|
|
136
|
+
authorized ? (
|
|
137
|
+
<QualityControlHeader>
|
|
138
|
+
<QualityControl />
|
|
139
|
+
</QualityControlHeader>
|
|
140
|
+
) : (
|
|
141
|
+
<Unauthorized />
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
/>
|
|
145
|
+
<Route
|
|
146
|
+
exact
|
|
147
|
+
path={QUALITY_CONTROL_HISTORY}
|
|
148
|
+
render={() =>
|
|
149
|
+
authorized ? (
|
|
150
|
+
<QualityControlHeader>
|
|
151
|
+
<QualityControlHistory />
|
|
152
|
+
</QualityControlHeader>
|
|
153
|
+
) : (
|
|
154
|
+
<Unauthorized />
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
/>
|
|
158
|
+
</Switch>
|
|
137
159
|
);
|
|
138
160
|
}
|
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
} from "semantic-ui-react";
|
|
14
14
|
import { linkTo, QUALITY_CONTROL_NEW } from "@truedat/core/routes";
|
|
15
15
|
import useAuthorizedAction from "@truedat/core/hooks/useAuthorizedAction";
|
|
16
|
-
import Loading from "@truedat/core/
|
|
17
|
-
import { useSearchContext } from "
|
|
18
|
-
import
|
|
16
|
+
import Loading from "@truedat/core/components/Loading";
|
|
17
|
+
import { useSearchContext } from "@truedat/core/search/SearchContext";
|
|
18
|
+
import SearchWidget from "@truedat/core/search/SearchWidget";
|
|
19
19
|
import { useExecutionGroupsCreate } from "../../hooks/useExecutionGroups";
|
|
20
20
|
import ExecutionPopup from "./ExecutionPopup";
|
|
21
21
|
import QualityControlsTable from "./QualityControlsTable";
|
|
22
22
|
|
|
23
|
-
export const QualityControls = (
|
|
23
|
+
export const QualityControls = () => {
|
|
24
24
|
const { formatMessage } = useIntl();
|
|
25
25
|
const history = useHistory();
|
|
26
26
|
|
|
@@ -33,13 +33,21 @@ export const QualityControls = ({ status }) => {
|
|
|
33
33
|
query,
|
|
34
34
|
searchMust,
|
|
35
35
|
loadingFilters: loading,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
qualityControls,
|
|
36
|
+
hiddenFilters,
|
|
37
|
+
toggleHiddenFilterValue,
|
|
38
|
+
searchData,
|
|
39
|
+
defaultFilters,
|
|
41
40
|
} = useSearchContext();
|
|
42
41
|
|
|
42
|
+
const canExecuteQualityControl = _.matches({ executable: [true] })(
|
|
43
|
+
hiddenFilters
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const status = _.prop("status")(defaultFilters);
|
|
47
|
+
|
|
48
|
+
const qualityControls = searchData?.data;
|
|
49
|
+
const qualityControlsActions = searchData?.actions;
|
|
50
|
+
|
|
43
51
|
const { trigger: triggerCreateExecutionGroup } = useExecutionGroupsCreate();
|
|
44
52
|
|
|
45
53
|
const handleSubmit = (df_content) => {
|
|
@@ -53,10 +61,6 @@ export const QualityControls = ({ status }) => {
|
|
|
53
61
|
});
|
|
54
62
|
};
|
|
55
63
|
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
setQualityControlsStatus(status);
|
|
58
|
-
}, [status, setQualityControlsStatus]);
|
|
59
|
-
|
|
60
64
|
return (
|
|
61
65
|
<Segment loading={false}>
|
|
62
66
|
<Header as="h2">
|
|
@@ -70,7 +74,7 @@ export const QualityControls = ({ status }) => {
|
|
|
70
74
|
</Header>
|
|
71
75
|
<Grid>
|
|
72
76
|
<Grid.Column width={8}>
|
|
73
|
-
<
|
|
77
|
+
<SearchWidget />
|
|
74
78
|
</Grid.Column>
|
|
75
79
|
|
|
76
80
|
<Grid.Column width={8}>
|
|
@@ -88,7 +92,10 @@ export const QualityControls = ({ status }) => {
|
|
|
88
92
|
className="bgOrange"
|
|
89
93
|
toggle
|
|
90
94
|
onChange={() =>
|
|
91
|
-
|
|
95
|
+
toggleHiddenFilterValue({
|
|
96
|
+
filter: "executable",
|
|
97
|
+
value: true,
|
|
98
|
+
})
|
|
92
99
|
}
|
|
93
100
|
checked={canExecuteQualityControl}
|
|
94
101
|
style={{ top: "6px", marginRight: "7.5px" }}
|
|
@@ -115,7 +122,7 @@ export const QualityControls = ({ status }) => {
|
|
|
115
122
|
</Container>
|
|
116
123
|
</Grid.Column>
|
|
117
124
|
</Grid>
|
|
118
|
-
<QualityControlsTable
|
|
125
|
+
<QualityControlsTable />
|
|
119
126
|
</Segment>
|
|
120
127
|
);
|
|
121
128
|
};
|
|
@@ -8,7 +8,7 @@ import { Table, Header, Icon } from "semantic-ui-react";
|
|
|
8
8
|
import { linkTo } from "@truedat/core/routes";
|
|
9
9
|
import Moment from "react-moment";
|
|
10
10
|
|
|
11
|
-
import { useSearchContext } from "
|
|
11
|
+
import { useSearchContext } from "@truedat/core/search/SearchContext";
|
|
12
12
|
import QualityControlRow from "./QualityControlRow";
|
|
13
13
|
|
|
14
14
|
const translateDecorator = (id) =>
|
|
@@ -27,8 +27,8 @@ DateDecorator.propTypes = {
|
|
|
27
27
|
|
|
28
28
|
export default function QualityControlsTable() {
|
|
29
29
|
const {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
searchData,
|
|
31
|
+
loading,
|
|
32
32
|
sortColumn,
|
|
33
33
|
sortDirection,
|
|
34
34
|
setSortColumn,
|
|
@@ -36,6 +36,8 @@ export default function QualityControlsTable() {
|
|
|
36
36
|
} = useSearchContext();
|
|
37
37
|
const { formatMessage } = useIntl();
|
|
38
38
|
|
|
39
|
+
const qualityControls = searchData?.data;
|
|
40
|
+
|
|
39
41
|
const columns = [
|
|
40
42
|
{
|
|
41
43
|
name: "name",
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { compile } from "path-to-regexp";
|
|
2
2
|
import useSWR from "swr";
|
|
3
3
|
import useSWRMutations from "swr/mutation";
|
|
4
|
-
import {
|
|
5
|
-
apiJson,
|
|
6
|
-
apiJsonPost,
|
|
7
|
-
apiJsonPatch,
|
|
8
|
-
apiJsonDelete,
|
|
9
|
-
} from "@truedat/core/services/api";
|
|
4
|
+
import { apiJson, apiJsonPost } from "@truedat/core/services/api";
|
|
10
5
|
import {
|
|
11
6
|
API_QUALITY_CONTROL_EXECUTION_GROUPS_CREATE,
|
|
12
7
|
API_QUALITY_CONTROL_EXECUTION_GROUPS_INDEX,
|
|
@@ -21,10 +16,11 @@ export const useExecutionGroupsCreate = () => {
|
|
|
21
16
|
};
|
|
22
17
|
|
|
23
18
|
export const useExecutionGroupsIndex = () => {
|
|
24
|
-
|
|
19
|
+
const { data, error, mutate } = useSWR(
|
|
25
20
|
API_QUALITY_CONTROL_EXECUTION_GROUPS_INDEX,
|
|
26
|
-
|
|
21
|
+
apiJson
|
|
27
22
|
);
|
|
23
|
+
return { data: data?.data?.data, error, loading: !error && !data, mutate };
|
|
28
24
|
};
|
|
29
25
|
|
|
30
26
|
export const useExecutionGroupsShow = (id) => {
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { FormattedMessage } from "react-intl";
|
|
4
|
-
import { Label, Icon, Dropdown, Dimmer, Loader } from "semantic-ui-react";
|
|
5
|
-
import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
|
|
6
|
-
import FilterItem from "./FilterItem";
|
|
7
|
-
|
|
8
|
-
import { useSearchContext } from "./SearchContext";
|
|
9
|
-
|
|
10
|
-
const removePrefix = _.replace(/^.*\./, "");
|
|
11
|
-
|
|
12
|
-
export default function FilterDropdown() {
|
|
13
|
-
const {
|
|
14
|
-
loadingFilters: loading,
|
|
15
|
-
filter,
|
|
16
|
-
options,
|
|
17
|
-
activeFilterSelectedValues,
|
|
18
|
-
|
|
19
|
-
openFilter,
|
|
20
|
-
closeFilter,
|
|
21
|
-
removeFilter,
|
|
22
|
-
toggleFilterValue,
|
|
23
|
-
} = useSearchContext();
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<Dropdown
|
|
27
|
-
item
|
|
28
|
-
floating
|
|
29
|
-
scrolling
|
|
30
|
-
icon={false}
|
|
31
|
-
upward={false}
|
|
32
|
-
trigger={
|
|
33
|
-
<Label key={filter}>
|
|
34
|
-
<FormattedMessage
|
|
35
|
-
id={`filters.${filter}`}
|
|
36
|
-
defaultMessage={removePrefix(filter)}
|
|
37
|
-
/>
|
|
38
|
-
<Icon
|
|
39
|
-
name="delete"
|
|
40
|
-
onClick={(e) => {
|
|
41
|
-
e.preventDefault();
|
|
42
|
-
e.stopPropagation();
|
|
43
|
-
removeFilter({ filter });
|
|
44
|
-
}}
|
|
45
|
-
/>
|
|
46
|
-
</Label>
|
|
47
|
-
}
|
|
48
|
-
onOpen={() => openFilter({ filter })}
|
|
49
|
-
onClose={() => closeFilter({ filter })}
|
|
50
|
-
open={!_.isEmpty(options)}
|
|
51
|
-
>
|
|
52
|
-
<Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
|
|
53
|
-
{options &&
|
|
54
|
-
_.flow(
|
|
55
|
-
_.sortBy(accentInsensitivePathOrder("text")),
|
|
56
|
-
_.map.convert({ cap: false })((option, i) => (
|
|
57
|
-
<FilterItem
|
|
58
|
-
key={i}
|
|
59
|
-
filter={filter}
|
|
60
|
-
option={option}
|
|
61
|
-
toggleFilterValue={toggleFilterValue}
|
|
62
|
-
active={_.includes(_.prop("value")(option))(
|
|
63
|
-
activeFilterSelectedValues
|
|
64
|
-
)}
|
|
65
|
-
/>
|
|
66
|
-
))
|
|
67
|
-
)(options)}
|
|
68
|
-
{loading && (
|
|
69
|
-
<Dimmer active inverted>
|
|
70
|
-
<Loader size="tiny" />
|
|
71
|
-
</Dimmer>
|
|
72
|
-
)}
|
|
73
|
-
</Dimmer.Dimmable>
|
|
74
|
-
</Dropdown>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import { FormattedMessage } from "react-intl";
|
|
5
|
-
import { Icon, Dropdown } from "semantic-ui-react";
|
|
6
|
-
|
|
7
|
-
const FilterItemText = ({ filterName, text }) =>
|
|
8
|
-
_.trim(text) ? (
|
|
9
|
-
<FormattedMessage
|
|
10
|
-
id={`filters.${filterName}.${_.trim(text)}`}
|
|
11
|
-
defaultMessage={_.trim(text)}
|
|
12
|
-
/>
|
|
13
|
-
) : (
|
|
14
|
-
<i>
|
|
15
|
-
<FormattedMessage id="filter.empty" />
|
|
16
|
-
</i>
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const preventDefault = (e, callback) => {
|
|
20
|
-
e && e.preventDefault();
|
|
21
|
-
e && e.stopPropagation();
|
|
22
|
-
callback();
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const FilterItem = ({
|
|
26
|
-
active,
|
|
27
|
-
filter,
|
|
28
|
-
toggleFilterValue,
|
|
29
|
-
option: { text, value },
|
|
30
|
-
}) => (
|
|
31
|
-
<Dropdown.Item
|
|
32
|
-
onClick={(e) =>
|
|
33
|
-
preventDefault(e, () => toggleFilterValue({ filter, value }))
|
|
34
|
-
}
|
|
35
|
-
active={active}
|
|
36
|
-
>
|
|
37
|
-
<Icon name={active ? "check square outline" : "square outline"} />
|
|
38
|
-
<FilterItemText filterName={filter} text={text} />
|
|
39
|
-
</Dropdown.Item>
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
FilterItem.propTypes = {
|
|
43
|
-
filterName: PropTypes.string,
|
|
44
|
-
text: PropTypes.string,
|
|
45
|
-
toggleFilterValue: PropTypes.func,
|
|
46
|
-
option: PropTypes.object,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export default FilterItem;
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React, { useState, useEffect } from "react";
|
|
3
|
-
import { FormattedMessage } from "react-intl";
|
|
4
|
-
import {
|
|
5
|
-
Label,
|
|
6
|
-
Icon,
|
|
7
|
-
Input,
|
|
8
|
-
Dropdown,
|
|
9
|
-
Dimmer,
|
|
10
|
-
Loader,
|
|
11
|
-
} from "semantic-ui-react";
|
|
12
|
-
import { lowerDeburr } from "@truedat/core/services/sort";
|
|
13
|
-
import DropdownMenuItem from "@truedat/core/components/DropdownMenuItem";
|
|
14
|
-
|
|
15
|
-
import { useSearchContext } from "./SearchContext";
|
|
16
|
-
|
|
17
|
-
export default function FilterMultilevelDropdown() {
|
|
18
|
-
const {
|
|
19
|
-
loadingFilters: loading,
|
|
20
|
-
filter,
|
|
21
|
-
options,
|
|
22
|
-
activeFilterSelectedValues,
|
|
23
|
-
|
|
24
|
-
openFilter,
|
|
25
|
-
closeFilter,
|
|
26
|
-
removeFilter,
|
|
27
|
-
toggleFilterValue,
|
|
28
|
-
} = useSearchContext();
|
|
29
|
-
|
|
30
|
-
const [selected, setSelected] = useState();
|
|
31
|
-
const [query, setQuery] = useState();
|
|
32
|
-
const [open, setOpen] = useState([]);
|
|
33
|
-
const [displayed, setDisplayed] = useState([]);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const activeOptions = _.filter((option) =>
|
|
37
|
-
_.includes(option.id)(activeFilterSelectedValues)
|
|
38
|
-
)(options);
|
|
39
|
-
|
|
40
|
-
_.flow(
|
|
41
|
-
_.reduce(
|
|
42
|
-
(acc, option) => [
|
|
43
|
-
...acc,
|
|
44
|
-
option.id,
|
|
45
|
-
..._.map("id")(option.descendents),
|
|
46
|
-
],
|
|
47
|
-
[]
|
|
48
|
-
),
|
|
49
|
-
_.uniq,
|
|
50
|
-
setSelected
|
|
51
|
-
)(activeOptions);
|
|
52
|
-
if (_.isEmpty(open) && _.isEmpty(displayed)) {
|
|
53
|
-
const withAncestors = _.flow(
|
|
54
|
-
_.reduce(
|
|
55
|
-
(acc, option) => [...acc, ..._.map("id")(option.ancestors)],
|
|
56
|
-
[]
|
|
57
|
-
),
|
|
58
|
-
_.uniq
|
|
59
|
-
)(activeOptions);
|
|
60
|
-
const newDisplayed = [..._.map("id")(activeOptions), ...withAncestors];
|
|
61
|
-
!_.isEqual(open, withAncestors) && setOpen(withAncestors);
|
|
62
|
-
!_.isEqual(displayed, newDisplayed) && setDisplayed(newDisplayed);
|
|
63
|
-
}
|
|
64
|
-
}, [activeFilterSelectedValues, options, open, displayed]);
|
|
65
|
-
|
|
66
|
-
const handleOpen = (selection) => {
|
|
67
|
-
const option = _.find({ id: selection })(options);
|
|
68
|
-
const isOpen = _.contains(selection)(open);
|
|
69
|
-
const children = _.map("id")(option.children);
|
|
70
|
-
const descendents = _.map("id")(option.descendents);
|
|
71
|
-
|
|
72
|
-
if (isOpen) {
|
|
73
|
-
setOpen(_.without([selection, ...descendents])(open));
|
|
74
|
-
setDisplayed(_.without(descendents)(displayed));
|
|
75
|
-
} else {
|
|
76
|
-
setOpen(_.union([selection])(open));
|
|
77
|
-
setDisplayed(_.union(children)(displayed));
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const handleClick = (e, selection) => {
|
|
82
|
-
const option = _.find({ id: selection })(options);
|
|
83
|
-
const ancestorsToDelete = _.intersection(_.map("id")(option.ancestors))(
|
|
84
|
-
activeFilterSelectedValues
|
|
85
|
-
);
|
|
86
|
-
if (!_.isEmpty(ancestorsToDelete)) {
|
|
87
|
-
const active = _.union([selection])(activeFilterSelectedValues);
|
|
88
|
-
const value = _.without(ancestorsToDelete)(active);
|
|
89
|
-
toggleFilterValue({ filter, value });
|
|
90
|
-
} else if (_.includes(selection)(activeFilterSelectedValues)) {
|
|
91
|
-
const descendents = _.map("id")(option.descendents);
|
|
92
|
-
const value = _.without([selection, ...descendents])(
|
|
93
|
-
activeFilterSelectedValues
|
|
94
|
-
);
|
|
95
|
-
toggleFilterValue({ filter, value });
|
|
96
|
-
} else {
|
|
97
|
-
const descendents = _.map("id")(option.descendents);
|
|
98
|
-
const active = _.without(descendents)(activeFilterSelectedValues);
|
|
99
|
-
const value = _.union([selection])(active);
|
|
100
|
-
toggleFilterValue({ filter, value });
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const displayAll = () => {
|
|
105
|
-
const ids = _.map("id")(options);
|
|
106
|
-
setOpen(ids);
|
|
107
|
-
setDisplayed(ids);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const handleSearch = (e, { value }) => {
|
|
111
|
-
e.preventDefault();
|
|
112
|
-
setQuery(lowerDeburr(value));
|
|
113
|
-
if (!_.isEmpty(value)) {
|
|
114
|
-
displayAll();
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
const match = (name, query) => _.contains(query)(lowerDeburr(name));
|
|
118
|
-
const filterSearch = (all) => {
|
|
119
|
-
if (query) {
|
|
120
|
-
return _.filter(
|
|
121
|
-
(domain) =>
|
|
122
|
-
match(domain.name, query) ||
|
|
123
|
-
_.some((descendent) => match(descendent.name, query))(
|
|
124
|
-
domain.descendents
|
|
125
|
-
)
|
|
126
|
-
)(all);
|
|
127
|
-
}
|
|
128
|
-
return all;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const filterDisplayed = (all) =>
|
|
132
|
-
_.filter((domain) => domain.level == 0 || _.contains(domain.id)(displayed))(
|
|
133
|
-
all
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const filteredOptions = _.flow(filterSearch, filterDisplayed)(options);
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<Dropdown
|
|
140
|
-
item
|
|
141
|
-
floating
|
|
142
|
-
icon={false}
|
|
143
|
-
upward={false}
|
|
144
|
-
onOpen={() => openFilter({ filter })}
|
|
145
|
-
onClose={() => closeFilter({ filter })}
|
|
146
|
-
trigger={
|
|
147
|
-
<Label key={filter}>
|
|
148
|
-
<FormattedMessage id={`filters.${filter}`} defaultMessage={filter} />
|
|
149
|
-
<Icon
|
|
150
|
-
name="delete"
|
|
151
|
-
onClick={(e) => {
|
|
152
|
-
e.preventDefault();
|
|
153
|
-
e.stopPropagation();
|
|
154
|
-
removeFilter({ filter });
|
|
155
|
-
}}
|
|
156
|
-
/>
|
|
157
|
-
</Label>
|
|
158
|
-
}
|
|
159
|
-
open={!_.isEmpty(options)}
|
|
160
|
-
>
|
|
161
|
-
<Dimmer.Dimmable dimmed={loading} as={Dropdown.Menu}>
|
|
162
|
-
<>
|
|
163
|
-
<Input
|
|
164
|
-
icon="search"
|
|
165
|
-
iconPosition="left"
|
|
166
|
-
className="search"
|
|
167
|
-
onKeyDown={(e) => {
|
|
168
|
-
if (e.key === " ") {
|
|
169
|
-
e.stopPropagation();
|
|
170
|
-
}
|
|
171
|
-
}}
|
|
172
|
-
onChange={handleSearch}
|
|
173
|
-
onClick={(e) => {
|
|
174
|
-
e.preventDefault();
|
|
175
|
-
e.stopPropagation();
|
|
176
|
-
}}
|
|
177
|
-
/>
|
|
178
|
-
<Dropdown.Menu scrolling>
|
|
179
|
-
{_.map.convert({ cap: false })((option, i) => (
|
|
180
|
-
<DropdownMenuItem
|
|
181
|
-
key={i}
|
|
182
|
-
onOpen={handleOpen}
|
|
183
|
-
onClick={handleClick}
|
|
184
|
-
open={_.contains(option.id)(open)}
|
|
185
|
-
canOpen={_.negate(_.isEmpty)(option.children)}
|
|
186
|
-
selected={_.contains(option.id)(selected)}
|
|
187
|
-
{...option}
|
|
188
|
-
/>
|
|
189
|
-
))(filteredOptions)}
|
|
190
|
-
</Dropdown.Menu>
|
|
191
|
-
</>
|
|
192
|
-
{loading && (
|
|
193
|
-
<Dimmer active inverted>
|
|
194
|
-
<Loader size="tiny" />
|
|
195
|
-
</Dimmer>
|
|
196
|
-
)}
|
|
197
|
-
</Dimmer.Dimmable>
|
|
198
|
-
</Dropdown>
|
|
199
|
-
);
|
|
200
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import PropTypes from "prop-types";
|
|
4
|
-
import { useHierarchy } from "@truedat/df/hooks/useHierarchies";
|
|
5
|
-
import {
|
|
6
|
-
getHierarchyOptions,
|
|
7
|
-
getKeyAndParents,
|
|
8
|
-
} from "@truedat/core/services/getHierarchyOptions";
|
|
9
|
-
import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
|
|
10
|
-
|
|
11
|
-
const PopulatedHierarchyFilterDropdown = (props) => {
|
|
12
|
-
const {
|
|
13
|
-
activeValues,
|
|
14
|
-
closeFilter,
|
|
15
|
-
filter,
|
|
16
|
-
loading,
|
|
17
|
-
openFilter,
|
|
18
|
-
options,
|
|
19
|
-
removeFilter,
|
|
20
|
-
toggleFilterValue,
|
|
21
|
-
} = props;
|
|
22
|
-
const hierarchyId = _.flow(
|
|
23
|
-
_.first,
|
|
24
|
-
_.prop("value"),
|
|
25
|
-
(value) => value.split("_"),
|
|
26
|
-
_.first
|
|
27
|
-
)(options);
|
|
28
|
-
|
|
29
|
-
const { data, error, loading: hierarchyLoading } = useHierarchy(hierarchyId);
|
|
30
|
-
if (error) return null;
|
|
31
|
-
if (hierarchyLoading) return null;
|
|
32
|
-
|
|
33
|
-
const hierarchyOptions = getHierarchyOptions(data?.nodes);
|
|
34
|
-
|
|
35
|
-
const includedKeys = _.flow(
|
|
36
|
-
_.map("value"),
|
|
37
|
-
_.flatMap(getKeyAndParents(hierarchyOptions)),
|
|
38
|
-
_.uniq
|
|
39
|
-
)(options);
|
|
40
|
-
|
|
41
|
-
const filterIncludedKeys = _.filter(({ key }) =>
|
|
42
|
-
_.includes(key)(includedKeys)
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const filteredChildren = _.flow(_.prop("children"), filterIncludedKeys);
|
|
46
|
-
|
|
47
|
-
const filteredOptions = _.flow(
|
|
48
|
-
filterIncludedKeys,
|
|
49
|
-
_.map((node) => ({ ...node, children: filteredChildren(node) }))
|
|
50
|
-
)(hierarchyOptions);
|
|
51
|
-
|
|
52
|
-
const idActiveValues = _.map((key) =>
|
|
53
|
-
_.flow(_.find({ key }), _.prop("id"))(filteredOptions)
|
|
54
|
-
)(activeValues);
|
|
55
|
-
|
|
56
|
-
const handleToggleFilterValue = ({ filter, value }) => {
|
|
57
|
-
const getChildrenKeys = (id) => {
|
|
58
|
-
const { key, descendents } = _.flow(
|
|
59
|
-
_.find({ id }),
|
|
60
|
-
_.pick(["key", "descendents"])
|
|
61
|
-
)(filteredOptions);
|
|
62
|
-
const descendentKeys = _.map("key")(descendents);
|
|
63
|
-
return [key, ...descendentKeys];
|
|
64
|
-
};
|
|
65
|
-
const newValue = _.flow(
|
|
66
|
-
_.flatMap(getChildrenKeys),
|
|
67
|
-
_.uniq,
|
|
68
|
-
_.reject(_.isNil)
|
|
69
|
-
)(value);
|
|
70
|
-
toggleFilterValue({ filter, value: newValue });
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<FilterMultilevelDropdown
|
|
75
|
-
activeValues={idActiveValues}
|
|
76
|
-
closeFilter={closeFilter}
|
|
77
|
-
filter={filter}
|
|
78
|
-
loading={loading}
|
|
79
|
-
openFilter={openFilter}
|
|
80
|
-
options={filteredOptions}
|
|
81
|
-
removeFilter={removeFilter}
|
|
82
|
-
toggleFilterValue={handleToggleFilterValue}
|
|
83
|
-
/>
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
PopulatedHierarchyFilterDropdown.propTypes = {
|
|
88
|
-
activeValues: PropTypes.array,
|
|
89
|
-
closeFilter: PropTypes.func,
|
|
90
|
-
filter: PropTypes.string,
|
|
91
|
-
loading: PropTypes.bool,
|
|
92
|
-
openFilter: PropTypes.func,
|
|
93
|
-
options: PropTypes.array,
|
|
94
|
-
removeFilter: PropTypes.func,
|
|
95
|
-
toggleFilterValue: PropTypes.func,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const HierarchyFilterDropdown = (props) =>
|
|
99
|
-
_.isEmpty(props.options) ? (
|
|
100
|
-
<FilterMultilevelDropdown {...props} />
|
|
101
|
-
) : (
|
|
102
|
-
<PopulatedHierarchyFilterDropdown {...props} />
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
HierarchyFilterDropdown.propTypes = {
|
|
106
|
-
activeValues: PropTypes.array,
|
|
107
|
-
closeFilter: PropTypes.func,
|
|
108
|
-
filter: PropTypes.string,
|
|
109
|
-
loading: PropTypes.bool,
|
|
110
|
-
openFilter: PropTypes.func,
|
|
111
|
-
options: PropTypes.array,
|
|
112
|
-
removeFilter: PropTypes.func,
|
|
113
|
-
toggleFilterValue: PropTypes.func,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export default HierarchyFilterDropdown;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { Dropdown } from "semantic-ui-react";
|
|
4
|
-
import { FormattedMessage, useIntl } from "react-intl";
|
|
5
|
-
import { i18nOrder } from "@truedat/core/services/sort";
|
|
6
|
-
import { useSearchContext } from "./SearchContext";
|
|
7
|
-
|
|
8
|
-
const removePrefix = _.replace(/^.*\./, "");
|
|
9
|
-
|
|
10
|
-
export default function QualityControlFilters() {
|
|
11
|
-
const {
|
|
12
|
-
disabled,
|
|
13
|
-
availableFilters,
|
|
14
|
-
addFilter,
|
|
15
|
-
resetFilters,
|
|
16
|
-
loadingFilters: loading,
|
|
17
|
-
} = useSearchContext();
|
|
18
|
-
|
|
19
|
-
const { formatMessage } = useIntl();
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<Dropdown
|
|
23
|
-
button
|
|
24
|
-
className="icon"
|
|
25
|
-
disabled={disabled}
|
|
26
|
-
floating
|
|
27
|
-
icon="filter"
|
|
28
|
-
labeled
|
|
29
|
-
loading={loading}
|
|
30
|
-
scrolling
|
|
31
|
-
text={formatMessage({ id: "filters", defaultMessage: "Filters" })}
|
|
32
|
-
upward={false}
|
|
33
|
-
>
|
|
34
|
-
<Dropdown.Menu>
|
|
35
|
-
<Dropdown.Item onClick={resetFilters}>
|
|
36
|
-
<em>
|
|
37
|
-
<FormattedMessage
|
|
38
|
-
id="filters.reset"
|
|
39
|
-
defaultMessage="(reset filters)"
|
|
40
|
-
/>
|
|
41
|
-
</em>
|
|
42
|
-
</Dropdown.Item>
|
|
43
|
-
{_.flow(
|
|
44
|
-
_.defaultTo([]),
|
|
45
|
-
_.sortBy(i18nOrder(formatMessage, "filters")),
|
|
46
|
-
_.map((filter) => (
|
|
47
|
-
<Dropdown.Item
|
|
48
|
-
key={filter}
|
|
49
|
-
text={formatMessage({
|
|
50
|
-
id: `filters.${filter}`,
|
|
51
|
-
defaultMessage: removePrefix(filter),
|
|
52
|
-
})}
|
|
53
|
-
onClick={() => addFilter({ filter })}
|
|
54
|
-
/>
|
|
55
|
-
))
|
|
56
|
-
)(availableFilters)}
|
|
57
|
-
</Dropdown.Menu>
|
|
58
|
-
</Dropdown>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { FormattedMessage } from "react-intl";
|
|
4
|
-
import FilterDropdown from "./FilterDropdown";
|
|
5
|
-
import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
|
|
6
|
-
import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
|
|
7
|
-
|
|
8
|
-
import SearchContext, { useSearchContext } from "./SearchContext";
|
|
9
|
-
|
|
10
|
-
export default function QualityControlSelectedFilters() {
|
|
11
|
-
const context = useSearchContext();
|
|
12
|
-
const {
|
|
13
|
-
selectedFilters,
|
|
14
|
-
resetFilters,
|
|
15
|
-
filterTypes,
|
|
16
|
-
activeFilterName,
|
|
17
|
-
activeFilterValues,
|
|
18
|
-
} = context;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<>
|
|
22
|
-
<div className="selectedFilters">
|
|
23
|
-
{_.isEmpty(selectedFilters) ? null : (
|
|
24
|
-
<>
|
|
25
|
-
<div className="appliedFilters">
|
|
26
|
-
<FormattedMessage id="search.applied_filters" />
|
|
27
|
-
</div>
|
|
28
|
-
{selectedFilters.map((filter) => {
|
|
29
|
-
const filterType = _.prop(filter)(filterTypes);
|
|
30
|
-
const options = _.isEqual(filter, activeFilterName)
|
|
31
|
-
? activeFilterValues
|
|
32
|
-
: null;
|
|
33
|
-
return (
|
|
34
|
-
<SearchContext.Provider
|
|
35
|
-
value={{ ...context, filter, options }}
|
|
36
|
-
key={filter}
|
|
37
|
-
>
|
|
38
|
-
{filterType === "domain" ? (
|
|
39
|
-
<FilterMultilevelDropdown />
|
|
40
|
-
) : filterType === "hierarchy" ? (
|
|
41
|
-
<HierarchyFilterDropdown />
|
|
42
|
-
) : (
|
|
43
|
-
<FilterDropdown />
|
|
44
|
-
)}
|
|
45
|
-
</SearchContext.Provider>
|
|
46
|
-
);
|
|
47
|
-
})}
|
|
48
|
-
<a className="resetFilters" onClick={() => resetFilters()}>
|
|
49
|
-
<FormattedMessage id="search.clear_filters" />
|
|
50
|
-
</a>
|
|
51
|
-
</>
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
</>
|
|
55
|
-
);
|
|
56
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Input } from "semantic-ui-react";
|
|
3
|
-
import { useIntl } from "react-intl";
|
|
4
|
-
import QualityControlFilters from "./QualityControlFilters";
|
|
5
|
-
import QualityControlSelectedFilters from "./QualityControlSelectedFilters";
|
|
6
|
-
|
|
7
|
-
import { useSearchContext } from "./SearchContext";
|
|
8
|
-
|
|
9
|
-
export default function QualityControlsSearch() {
|
|
10
|
-
const { formatMessage } = useIntl();
|
|
11
|
-
|
|
12
|
-
const { query, setQuery, loadingFilters: loading } = useSearchContext();
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<>
|
|
16
|
-
<Input
|
|
17
|
-
value={query}
|
|
18
|
-
onChange={(_e, data) => setQuery(data.value)}
|
|
19
|
-
icon={{ name: "search", link: true }}
|
|
20
|
-
iconPosition="left"
|
|
21
|
-
action={<QualityControlFilters />}
|
|
22
|
-
placeholder={formatMessage({
|
|
23
|
-
id: "quality_controls.search.placeholder",
|
|
24
|
-
})}
|
|
25
|
-
loading={loading}
|
|
26
|
-
/>
|
|
27
|
-
<QualityControlSelectedFilters />
|
|
28
|
-
</>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React, {
|
|
3
|
-
useState,
|
|
4
|
-
useEffect,
|
|
5
|
-
useContext,
|
|
6
|
-
createContext,
|
|
7
|
-
useMemo,
|
|
8
|
-
} from "react";
|
|
9
|
-
import { useIntl } from "react-intl";
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
toFilterValues,
|
|
13
|
-
formatFilterValues,
|
|
14
|
-
} from "@truedat/core/services/filters";
|
|
15
|
-
import { makeOption } from "@truedat/core/services/i18n";
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
useQualityControlsSearch,
|
|
19
|
-
useQualityControlsFilters,
|
|
20
|
-
} from "../../hooks/useQualityControls";
|
|
21
|
-
|
|
22
|
-
import { useExecutionGroupsIndex } from "../../hooks/useExecutionGroups";
|
|
23
|
-
|
|
24
|
-
const SearchContext = createContext();
|
|
25
|
-
|
|
26
|
-
export const SearchContextProvider = (props) => {
|
|
27
|
-
const children = _.prop("children")(props);
|
|
28
|
-
const initialSortColumn = _.prop("initialSortColumn")(props);
|
|
29
|
-
const initialSortDirection = _.prop("initialSortDirection")(props);
|
|
30
|
-
const { formatMessage } = useIntl();
|
|
31
|
-
|
|
32
|
-
const [qualityControlsActions, setQualityControlsActions] = useState({});
|
|
33
|
-
const [executionGroups, setExecutionGroups] = useState([]);
|
|
34
|
-
const [qualityControlsStatus, setQualityControlsStatus] = useState();
|
|
35
|
-
|
|
36
|
-
const [canExecuteQualityControl, setCanExecuteQualityControl] =
|
|
37
|
-
useState(false);
|
|
38
|
-
|
|
39
|
-
const [loadingSearch, setLoadingSearch] = useState(true);
|
|
40
|
-
const [qualityControls, setQualityControls] = useState([]);
|
|
41
|
-
|
|
42
|
-
const [filtersPayload, setFiltersPayload] = useState([]);
|
|
43
|
-
const [loadingFilters, setLoadingFilters] = useState(true);
|
|
44
|
-
const [query, setQuery] = useState("");
|
|
45
|
-
const [activeFilterName, setActiveFilterName] = useState([]);
|
|
46
|
-
const [allActiveFilters, setAllActiveFilters] = useState({});
|
|
47
|
-
|
|
48
|
-
const [sortColumn, setSortColumn] = useState(initialSortColumn);
|
|
49
|
-
const [sortDirection, setSortDirection] = useState(initialSortDirection);
|
|
50
|
-
|
|
51
|
-
//STATE FUNCTIONS
|
|
52
|
-
const addFilter = ({ filter }) => {
|
|
53
|
-
setAllActiveFilters({ ...allActiveFilters, [filter]: [] });
|
|
54
|
-
setActiveFilterName(filter);
|
|
55
|
-
};
|
|
56
|
-
const resetFilters = () => setAllActiveFilters({});
|
|
57
|
-
|
|
58
|
-
const openFilter = ({ filter }) => setActiveFilterName(filter);
|
|
59
|
-
const closeFilter = () => {
|
|
60
|
-
setActiveFilterName(null);
|
|
61
|
-
setAllActiveFilters(_.pickBy(_.negate(_.isEmpty))(allActiveFilters));
|
|
62
|
-
};
|
|
63
|
-
const removeFilter = ({ filter }) => {
|
|
64
|
-
setAllActiveFilters(_.omit(filter)(allActiveFilters));
|
|
65
|
-
setActiveFilterName(activeFilterName == filter ? null : activeFilterName);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const toggleFilterValue = ({ filter, value }) => {
|
|
69
|
-
const values = _.propOr([], filter)(allActiveFilters);
|
|
70
|
-
const newValue = _.isArray(value)
|
|
71
|
-
? value
|
|
72
|
-
: _.includes(value)(values)
|
|
73
|
-
? _.without([value])(values)
|
|
74
|
-
: _.union([value])(values);
|
|
75
|
-
|
|
76
|
-
setAllActiveFilters({ ...allActiveFilters, [filter]: newValue });
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
//CALCULATIONS ON STATE
|
|
80
|
-
const selectedFilters = _.keys(allActiveFilters);
|
|
81
|
-
|
|
82
|
-
const filters = _.flow(
|
|
83
|
-
_.propOr({}, "data"),
|
|
84
|
-
_.omitBy(_.flow(_.propOr([], "values"), (values) => _.size(values) < 2))
|
|
85
|
-
)(filtersPayload);
|
|
86
|
-
|
|
87
|
-
// getAvailableFilters selector
|
|
88
|
-
const availableFilters = _.flow(_.keys, _.without(selectedFilters))(filters);
|
|
89
|
-
|
|
90
|
-
// getFilterTypes selector
|
|
91
|
-
const filterTypes = _.mapValues("type")(filters);
|
|
92
|
-
|
|
93
|
-
const translations = (formatMessage) => ({
|
|
94
|
-
"status.raw": (v) => formatMessage({ id: v, defaultMessage: v }),
|
|
95
|
-
});
|
|
96
|
-
const activeFilterValues = _.flow(
|
|
97
|
-
_.propOr({ values: [] }, activeFilterName),
|
|
98
|
-
({ values, type }) => ({
|
|
99
|
-
values: _.flow(
|
|
100
|
-
_.concat(_.prop(activeFilterName)(allActiveFilters)),
|
|
101
|
-
_.uniq
|
|
102
|
-
)(values),
|
|
103
|
-
type,
|
|
104
|
-
}),
|
|
105
|
-
formatFilterValues,
|
|
106
|
-
_.map(makeOption(translations(formatMessage), activeFilterName))
|
|
107
|
-
)(filters);
|
|
108
|
-
const activeFilterSelectedValues = _.flow(
|
|
109
|
-
_.propOr([], activeFilterName),
|
|
110
|
-
toFilterValues
|
|
111
|
-
)(allActiveFilters);
|
|
112
|
-
const searchMust = useMemo(
|
|
113
|
-
() => ({
|
|
114
|
-
..._.pickBy(_.negate(_.isEmpty))(allActiveFilters),
|
|
115
|
-
status: [qualityControlsStatus],
|
|
116
|
-
...(canExecuteQualityControl && { executable: ["true"] }),
|
|
117
|
-
}),
|
|
118
|
-
[allActiveFilters, canExecuteQualityControl, qualityControlsStatus]
|
|
119
|
-
);
|
|
120
|
-
const filterMust = useMemo(
|
|
121
|
-
() =>
|
|
122
|
-
_.flow(
|
|
123
|
-
_.pickBy(_.negate(_.isEmpty)),
|
|
124
|
-
_.omit(activeFilterName)
|
|
125
|
-
)(allActiveFilters),
|
|
126
|
-
[allActiveFilters, activeFilterName]
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const sort = useMemo(
|
|
130
|
-
() =>
|
|
131
|
-
sortColumn
|
|
132
|
-
? {
|
|
133
|
-
[sortColumn]: sortDirection === "ascending" ? "asc" : "desc",
|
|
134
|
-
}
|
|
135
|
-
: null,
|
|
136
|
-
[sortColumn, sortDirection]
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const { trigger: triggerFilters } = useQualityControlsFilters();
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
setLoadingFilters(true);
|
|
142
|
-
triggerFilters({ query, must: filterMust }).then(({ data }) => {
|
|
143
|
-
setFiltersPayload(data);
|
|
144
|
-
setLoadingFilters(false);
|
|
145
|
-
});
|
|
146
|
-
}, [query, filterMust, triggerFilters]);
|
|
147
|
-
|
|
148
|
-
const { trigger: triggerSearch } = useQualityControlsSearch();
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
setLoadingSearch(true);
|
|
151
|
-
|
|
152
|
-
!_.isEmpty(qualityControlsStatus) &&
|
|
153
|
-
triggerSearch({ query, must: searchMust, sort }).then(({ data }) => {
|
|
154
|
-
setQualityControlsActions(data?.actions);
|
|
155
|
-
setQualityControls(data?.data);
|
|
156
|
-
setLoadingSearch(false);
|
|
157
|
-
});
|
|
158
|
-
}, [query, searchMust, sort, triggerSearch, qualityControlsStatus]);
|
|
159
|
-
|
|
160
|
-
const { trigger: triggerListExecutionGroups } = useExecutionGroupsIndex();
|
|
161
|
-
const listExecutionGroups = () => {
|
|
162
|
-
triggerListExecutionGroups().then(({ data }) => {
|
|
163
|
-
setExecutionGroups(data?.data);
|
|
164
|
-
});
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const context = {
|
|
168
|
-
disabled: false,
|
|
169
|
-
loadingFilters,
|
|
170
|
-
|
|
171
|
-
availableFilters,
|
|
172
|
-
selectedFilters,
|
|
173
|
-
filterTypes,
|
|
174
|
-
|
|
175
|
-
activeFilterName,
|
|
176
|
-
activeFilterSelectedValues,
|
|
177
|
-
activeFilterValues,
|
|
178
|
-
query,
|
|
179
|
-
|
|
180
|
-
addFilter,
|
|
181
|
-
resetFilters,
|
|
182
|
-
openFilter,
|
|
183
|
-
closeFilter,
|
|
184
|
-
removeFilter,
|
|
185
|
-
toggleFilterValue,
|
|
186
|
-
searchMust,
|
|
187
|
-
setQuery,
|
|
188
|
-
|
|
189
|
-
canExecuteQualityControl,
|
|
190
|
-
setCanExecuteQualityControl,
|
|
191
|
-
qualityControls,
|
|
192
|
-
qualityControlsActions,
|
|
193
|
-
loadingSearch,
|
|
194
|
-
listExecutionGroups,
|
|
195
|
-
executionGroups,
|
|
196
|
-
|
|
197
|
-
sortColumn,
|
|
198
|
-
sortDirection,
|
|
199
|
-
setSortColumn,
|
|
200
|
-
setSortDirection,
|
|
201
|
-
setQualityControlsStatus,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
return (
|
|
205
|
-
<SearchContext.Provider value={context}>{children}</SearchContext.Provider>
|
|
206
|
-
);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
export const useSearchContext = () => useContext(SearchContext);
|
|
210
|
-
export default SearchContext;
|