@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,56 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
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
|
+
import {
|
|
11
|
+
toFilterValues,
|
|
12
|
+
formatFilterValues,
|
|
13
|
+
} from "@truedat/core/services/filters";
|
|
14
|
+
import { makeOption } from "@truedat/core/services/i18n";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
useQualityControlsSearch,
|
|
18
|
+
useQualityControlsFilters,
|
|
19
|
+
} from "../../hooks/useQualityControls";
|
|
20
|
+
|
|
21
|
+
const SearchContext = createContext();
|
|
22
|
+
|
|
23
|
+
export const SearchContextProvider = (props) => {
|
|
24
|
+
const children = _.prop("children")(props);
|
|
25
|
+
const initialSortColumn = _.prop("initialSortColumn")(props);
|
|
26
|
+
const initialSortDirection = _.prop("initialSortDirection")(props);
|
|
27
|
+
const { formatMessage } = useIntl();
|
|
28
|
+
|
|
29
|
+
const [loadingSearch, setLoadingSearch] = useState(true);
|
|
30
|
+
const [qualityControls, setQualityControls] = useState([]);
|
|
31
|
+
|
|
32
|
+
const [filtersPayload, setFiltersPayload] = useState([]);
|
|
33
|
+
const [loadingFilters, setLoadingFilters] = useState(true);
|
|
34
|
+
const [query, setQuery] = useState("");
|
|
35
|
+
const [activeFilterName, setActiveFilterName] = useState([]);
|
|
36
|
+
const [allActiveFilters, setAllActiveFilters] = useState({});
|
|
37
|
+
|
|
38
|
+
const [sortColumn, setSortColumn] = useState(initialSortColumn);
|
|
39
|
+
const [sortDirection, setSortDirection] = useState(initialSortDirection);
|
|
40
|
+
|
|
41
|
+
//STATE FUNCTIONS
|
|
42
|
+
const addFilter = ({ filter }) => {
|
|
43
|
+
setAllActiveFilters({ ...allActiveFilters, [filter]: [] });
|
|
44
|
+
setActiveFilterName(filter);
|
|
45
|
+
};
|
|
46
|
+
const resetFilters = () => setAllActiveFilters({});
|
|
47
|
+
|
|
48
|
+
const openFilter = ({ filter }) => setActiveFilterName(filter);
|
|
49
|
+
const closeFilter = () => {
|
|
50
|
+
setActiveFilterName(null);
|
|
51
|
+
setAllActiveFilters(_.pickBy(_.negate(_.isEmpty))(allActiveFilters));
|
|
52
|
+
};
|
|
53
|
+
const removeFilter = ({ filter }) => {
|
|
54
|
+
setAllActiveFilters(_.omit(filter)(allActiveFilters));
|
|
55
|
+
setActiveFilterName(activeFilterName == filter ? null : activeFilterName);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const toggleFilterValue = ({ filter, value }) => {
|
|
59
|
+
const values = _.propOr([], filter)(allActiveFilters);
|
|
60
|
+
const newValue = _.isArray(value)
|
|
61
|
+
? value
|
|
62
|
+
: _.includes(value)(values)
|
|
63
|
+
? _.without([value])(values)
|
|
64
|
+
: _.union([value])(values);
|
|
65
|
+
|
|
66
|
+
setAllActiveFilters({ ...allActiveFilters, [filter]: newValue });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
//CALCULATIONS ON STATE
|
|
70
|
+
const selectedFilters = _.keys(allActiveFilters);
|
|
71
|
+
|
|
72
|
+
const filters = _.flow(
|
|
73
|
+
_.propOr({}, "data"),
|
|
74
|
+
_.omitBy(_.flow(_.propOr([], "values"), (values) => _.size(values) < 2))
|
|
75
|
+
)(filtersPayload);
|
|
76
|
+
|
|
77
|
+
// getAvailableFilters selector
|
|
78
|
+
const availableFilters = _.flow(_.keys, _.without(selectedFilters))(filters);
|
|
79
|
+
|
|
80
|
+
// getFilterTypes selector
|
|
81
|
+
const filterTypes = _.mapValues("type")(filters);
|
|
82
|
+
|
|
83
|
+
const translations = (formatMessage) => ({
|
|
84
|
+
"status.raw": (v) => formatMessage({ id: v, defaultMessage: v }),
|
|
85
|
+
});
|
|
86
|
+
const activeFilterValues = _.flow(
|
|
87
|
+
_.propOr({ values: [] }, activeFilterName),
|
|
88
|
+
({ values, type }) => ({
|
|
89
|
+
values: _.flow(
|
|
90
|
+
_.concat(_.prop(activeFilterName)(allActiveFilters)),
|
|
91
|
+
_.uniq
|
|
92
|
+
)(values),
|
|
93
|
+
type,
|
|
94
|
+
}),
|
|
95
|
+
formatFilterValues,
|
|
96
|
+
_.map(makeOption(translations(formatMessage), activeFilterName))
|
|
97
|
+
)(filters);
|
|
98
|
+
const activeFilterSelectedValues = _.flow(
|
|
99
|
+
_.propOr([], activeFilterName),
|
|
100
|
+
toFilterValues
|
|
101
|
+
)(allActiveFilters);
|
|
102
|
+
|
|
103
|
+
const searchMust = useMemo(
|
|
104
|
+
() => _.pickBy(_.negate(_.isEmpty))(allActiveFilters),
|
|
105
|
+
[allActiveFilters]
|
|
106
|
+
);
|
|
107
|
+
const filterMust = useMemo(
|
|
108
|
+
() =>
|
|
109
|
+
_.flow(
|
|
110
|
+
_.pickBy(_.negate(_.isEmpty)),
|
|
111
|
+
_.omit(activeFilterName)
|
|
112
|
+
)(allActiveFilters),
|
|
113
|
+
[allActiveFilters, activeFilterName]
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const sort = useMemo(
|
|
117
|
+
() =>
|
|
118
|
+
sortColumn
|
|
119
|
+
? {
|
|
120
|
+
[sortColumn]: sortDirection === "ascending" ? "asc" : "desc",
|
|
121
|
+
}
|
|
122
|
+
: null,
|
|
123
|
+
[sortColumn, sortDirection]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const { trigger: triggerFilters } = useQualityControlsFilters();
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
setLoadingFilters(true);
|
|
129
|
+
triggerFilters({ query, must: filterMust }).then(({ data }) => {
|
|
130
|
+
setFiltersPayload(data);
|
|
131
|
+
setLoadingFilters(false);
|
|
132
|
+
});
|
|
133
|
+
}, [query, filterMust, triggerFilters]);
|
|
134
|
+
|
|
135
|
+
const { trigger: triggerSearch } = useQualityControlsSearch();
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
setLoadingSearch(true);
|
|
138
|
+
triggerSearch({ query, must: searchMust, sort }).then(({ data }) => {
|
|
139
|
+
setQualityControls(data?.data);
|
|
140
|
+
setLoadingSearch(false);
|
|
141
|
+
});
|
|
142
|
+
}, [query, searchMust, sort, triggerSearch]);
|
|
143
|
+
|
|
144
|
+
const context = {
|
|
145
|
+
disabled: false,
|
|
146
|
+
loadingFilters,
|
|
147
|
+
|
|
148
|
+
availableFilters,
|
|
149
|
+
selectedFilters,
|
|
150
|
+
filterTypes,
|
|
151
|
+
|
|
152
|
+
activeFilterName,
|
|
153
|
+
activeFilterSelectedValues,
|
|
154
|
+
activeFilterValues,
|
|
155
|
+
query,
|
|
156
|
+
|
|
157
|
+
addFilter,
|
|
158
|
+
resetFilters,
|
|
159
|
+
openFilter,
|
|
160
|
+
closeFilter,
|
|
161
|
+
removeFilter,
|
|
162
|
+
toggleFilterValue,
|
|
163
|
+
setQuery,
|
|
164
|
+
|
|
165
|
+
qualityControls,
|
|
166
|
+
loadingSearch,
|
|
167
|
+
|
|
168
|
+
sortColumn,
|
|
169
|
+
sortDirection,
|
|
170
|
+
setSortColumn,
|
|
171
|
+
setSortDirection,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<SearchContext.Provider value={context}>{children}</SearchContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const useSearchContext = () => useContext(SearchContext);
|
|
180
|
+
export default SearchContext;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { compile } from "path-to-regexp";
|
|
2
|
+
import useSWR from "swr";
|
|
3
|
+
import useSWRMutations from "swr/mutation";
|
|
4
|
+
import {
|
|
5
|
+
apiJson,
|
|
6
|
+
apiJsonPost,
|
|
7
|
+
apiJsonPatch,
|
|
8
|
+
apiJsonDelete,
|
|
9
|
+
} from "@truedat/core/services/api";
|
|
10
|
+
import {
|
|
11
|
+
API_QUALITY_CONTROLS,
|
|
12
|
+
API_QUALITY_CONTROL,
|
|
13
|
+
// API_QUALITY_CONTROL_VERSIONS,
|
|
14
|
+
// API_QUALITY_CONTROL_PUBLISHED,
|
|
15
|
+
API_QUALITY_CONTROL_DRAFT,
|
|
16
|
+
API_QUALITY_CONTROL_STATUS,
|
|
17
|
+
API_QUALITY_CONTROL_DOMAINS,
|
|
18
|
+
API_QUALITY_CONTROL_SEARCH,
|
|
19
|
+
API_QUALITY_CONTROL_FILTERS,
|
|
20
|
+
} from "../api";
|
|
21
|
+
|
|
22
|
+
export const useQualityControls = () => {
|
|
23
|
+
const { data, error, mutate } = useSWR(API_QUALITY_CONTROLS, apiJson);
|
|
24
|
+
return { data: data?.data, error, loading: !error && !data, mutate };
|
|
25
|
+
};
|
|
26
|
+
export const useQualityControl = (id) => {
|
|
27
|
+
const url = compile(API_QUALITY_CONTROL)({ id });
|
|
28
|
+
const { data, error, mutate } = useSWR(url, apiJson);
|
|
29
|
+
return { data: data?.data, error, loading: !error && !data, mutate };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const useQualityControlCreate = () => {
|
|
33
|
+
return useSWRMutations(API_QUALITY_CONTROLS, (url, { arg }) =>
|
|
34
|
+
apiJsonPost(url, arg)
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const useQualityControlCreateDraft = (id) => {
|
|
39
|
+
const url = compile(API_QUALITY_CONTROL_DRAFT)({ id });
|
|
40
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonPost(url, arg));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const useQualityControlUpdateDraft = (id) => {
|
|
44
|
+
const url = compile(API_QUALITY_CONTROL_DRAFT)({ id });
|
|
45
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonPatch(url, arg));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const useQualityControlUpdateStatus = (id) => {
|
|
49
|
+
const url = compile(API_QUALITY_CONTROL_STATUS)({ id });
|
|
50
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonPatch(url, arg));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const useQualityControlUpdateDomains = (id) => {
|
|
54
|
+
const url = compile(API_QUALITY_CONTROL_DOMAINS)({ id });
|
|
55
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonPatch(url, arg));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const useQualityControlDelete = (qualityControl) => {
|
|
59
|
+
const id = qualityControl?.id || 0;
|
|
60
|
+
const url = compile(API_QUALITY_CONTROL)({ id });
|
|
61
|
+
return useSWRMutations(url, (url, { arg }) => apiJsonDelete(url, arg));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const useQualityControlsSearch = () => {
|
|
65
|
+
return useSWRMutations(API_QUALITY_CONTROL_SEARCH, (url, { arg }) =>
|
|
66
|
+
apiJsonPost(url, arg)
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const useQualityControlsFilters = () => {
|
|
71
|
+
return useSWRMutations(API_QUALITY_CONTROL_FILTERS, (url, { arg }) =>
|
|
72
|
+
apiJsonPost(url, arg)
|
|
73
|
+
);
|
|
74
|
+
};
|
|
@@ -45,6 +45,12 @@
|
|
|
45
45
|
justify-content: center;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
.shape-icon {
|
|
49
|
+
font-size: 12px;
|
|
50
|
+
font-weight: bold;
|
|
51
|
+
font-family: monospace;
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
.function-params-label {
|
|
49
55
|
display: flex;
|
|
50
56
|
flex: 1;
|
|
@@ -153,10 +159,6 @@ ul.function-tree {
|
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
162
|
.join_type{
|
|
161
163
|
display:inline-flex;
|
|
162
164
|
left: 25%;
|
|
@@ -249,4 +251,37 @@ ul.function-tree {
|
|
|
249
251
|
.join-type-dropdown-item {
|
|
250
252
|
display: flex !important;
|
|
251
253
|
align-items: center;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.float-right {
|
|
257
|
+
float: right;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.text-align-left {
|
|
261
|
+
text-align: left;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.display-flex {
|
|
265
|
+
display: flex;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.clause-viewer-function {
|
|
269
|
+
padding-left: 8px;
|
|
270
|
+
margin-top: 2px;
|
|
271
|
+
}
|
|
272
|
+
.divider-compact {
|
|
273
|
+
font-size: smaller !important;
|
|
274
|
+
margin: 5px !important;
|
|
275
|
+
}
|
|
276
|
+
.no-margin {
|
|
277
|
+
margin: 0px !important;
|
|
278
|
+
}
|
|
279
|
+
.font-big {
|
|
280
|
+
font-size: x-large;
|
|
281
|
+
margin: 4px;
|
|
282
|
+
}
|
|
283
|
+
.condition-function-viewer {
|
|
284
|
+
display: flex;
|
|
285
|
+
justify-content: center;
|
|
286
|
+
align-items: center;
|
|
252
287
|
}
|