@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/qx",
3
- "version": "6.1.0",
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.0.5",
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.0",
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": "0e2dd260284f95d075f53ec5df1111606b5ecd0d"
100
+ "gitHead": "8ba2e1d68380956d654d6927a6117734f1c50076"
101
101
  }
@@ -1,10 +1,10 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useEffect } from "react";
2
+ import React from "react";
3
3
  import PropTypes from "prop-types";
4
- import { FormattedMessage, useIntl } from "react-intl";
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 { useSearchContext } from "../search/SearchContext";
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?.map((props, key) => (
89
- <ExecutionGroupRow key={key} columns={columns} {...props} />
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 "../search/SearchContext";
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
- <SearchContextProvider
37
- initialSortColumn="updated_at"
38
- initialSortDirection="descending"
39
- >
40
- <Switch>
41
- <Route
42
- exact
43
- path={QUALITY_CONTROLS_EXECUTION_GROUPS}
44
- render={() =>
45
- authorized ? (
46
- <ExecutionGroupsHeader>
47
- <ExecutionGroupsTable />
48
- </ExecutionGroupsHeader>
49
- ) : (
50
- <Unauthorized />
51
- )
52
- }
53
- />
54
- <Route
55
- exact
56
- path={QUALITY_CONTROLS_EXECUTION_GROUP}
57
- render={() =>
58
- authorized ? <ExecutionGroupDetail /> : <Unauthorized />
59
- }
60
- />
61
- <Route
62
- exact
63
- path={QUALITY_CONTROLS_PUBLISHED}
64
- render={() =>
65
- authorized ? (
66
- <QualityControls status="published" />
67
- ) : (
68
- <Unauthorized />
69
- )
70
- }
71
- />
72
- <Route
73
- exact
74
- path={QUALITY_CONTROLS_DEPRECATED}
75
- render={() =>
76
- authorized ? (
77
- <QualityControls status="deprecated" />
78
- ) : (
79
- <Unauthorized />
80
- )
81
- }
82
- />
83
- <Route
84
- exact
85
- path={QUALITY_CONTROLS_DRAFTS}
86
- render={() =>
87
- authorized ? <QualityControls status="draft" /> : <Unauthorized />
88
- }
89
- />
90
- <Route
91
- exact
92
- path={QUALITY_CONTROL_NEW}
93
- render={() => (authorized ? <NewQualityControl /> : <Unauthorized />)}
94
- />
95
- <Route
96
- exact
97
- path={QUALITY_CONTROL_EDIT}
98
- render={() =>
99
- authorized ? <EditQualityControl /> : <Unauthorized />
100
- }
101
- />
102
- <Route
103
- exact
104
- path={QUALITY_CONTROL_NEW_DRAFT}
105
- render={() =>
106
- authorized ? <NewDraftQualityControl /> : <Unauthorized />
107
- }
108
- />
109
- <Route
110
- exact
111
- path={QUALITY_CONTROL}
112
- render={() =>
113
- authorized ? (
114
- <QualityControlHeader>
115
- <QualityControl />
116
- </QualityControlHeader>
117
- ) : (
118
- <Unauthorized />
119
- )
120
- }
121
- />
122
- <Route
123
- exact
124
- path={QUALITY_CONTROL_HISTORY}
125
- render={() =>
126
- authorized ? (
127
- <QualityControlHeader>
128
- <QualityControlHistory />
129
- </QualityControlHeader>
130
- ) : (
131
- <Unauthorized />
132
- )
133
- }
134
- />
135
- </Switch>
136
- </SearchContextProvider>
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/src/components/Loading";
17
- import { useSearchContext } from "../search/SearchContext";
18
- import QualityControlsSearch from "../search/QualityControlsSearch";
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 = ({ status }) => {
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
- canExecuteQualityControl,
37
- setCanExecuteQualityControl,
38
- qualityControlsActions,
39
- setQualityControlsStatus,
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
- <QualityControlsSearch />
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
- setCanExecuteQualityControl(!canExecuteQualityControl)
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 status={status} />
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 "../search/SearchContext";
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
- qualityControls,
31
- loadingSearch: loading,
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
- return useSWRMutations(
19
+ const { data, error, mutate } = useSWR(
25
20
  API_QUALITY_CONTROL_EXECUTION_GROUPS_INDEX,
26
- (url, { arg }) => apiJson(url, arg)
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;