@truedat/core 6.3.3 → 6.3.5

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/core",
3
- "version": "6.3.3",
3
+ "version": "6.3.5",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -36,7 +36,7 @@
36
36
  "@testing-library/react": "^12.0.0",
37
37
  "@testing-library/react-hooks": "^8.0.1",
38
38
  "@testing-library/user-event": "^13.2.1",
39
- "@truedat/test": "6.3.3",
39
+ "@truedat/test": "6.3.4",
40
40
  "babel-jest": "^28.1.0",
41
41
  "babel-plugin-dynamic-import-node": "^2.3.3",
42
42
  "babel-plugin-lodash": "^3.3.4",
@@ -118,5 +118,5 @@
118
118
  "react-dom": ">= 16.8.6 < 17",
119
119
  "semantic-ui-react": ">= 2.0.3 < 2.2"
120
120
  },
121
- "gitHead": "ee40175932833d26f2d0ca297ce5a3a26fb71cb2"
121
+ "gitHead": "2cacdf1fc99746cb4f8dca7d770662c0d96c5bb7"
122
122
  }
package/src/api.js CHANGED
@@ -6,5 +6,8 @@ export const API_MESSAGE = "/api/messages/:id";
6
6
  export const API_MESSAGES = "/api/messages";
7
7
  export const API_REINDEX_GRANTS = "/api/grants/search/reindex_all";
8
8
  export const API_REINDEX_STRUCTURES = "/api/data_structures/search/reindex_all";
9
+ export const API_USER_FILTERS = "/api/:type";
10
+ export const API_USER_FILTER = "/api/:type/:id";
11
+ export const API_GET_USER_FILTERS = "/api/:type/user/me";
9
12
  export const API_ACL_ENTRY = "/api/acl_entries/:id";
10
13
  export const API_ACL_RESOURCE_ENTRIES = "/api/acl_entries/:type/:id";
@@ -16,6 +16,11 @@ const FilterItemText = ({ filterName, text }) =>
16
16
  </i>
17
17
  );
18
18
 
19
+ FilterItemText.propTypes = {
20
+ filterName: PropTypes.string,
21
+ text: PropTypes.string,
22
+ };
23
+
19
24
  const preventDefault = (e, callback) => {
20
25
  e && e.preventDefault();
21
26
  e && e.stopPropagation();
@@ -41,7 +41,7 @@ export const ModalSaveFilter = ({ saveFilters, activeFilters, scope }) => {
41
41
  e.preventDefault();
42
42
  saveFilters({
43
43
  filters: activeFilters,
44
- filterName: filterName,
44
+ filterName,
45
45
  scope,
46
46
  isGlobal,
47
47
  });
@@ -145,7 +145,7 @@ SearchFilterDropdown.propTypes = {
145
145
  filter: PropTypes.string,
146
146
  FilterDataLoader: PropTypes.node,
147
147
  loaderProps: PropTypes.object,
148
- FilterItem: PropTypes.object,
148
+ FilterItem: PropTypes.func,
149
149
  openFilter: PropTypes.func,
150
150
  removeFilter: PropTypes.func,
151
151
  toggleFilterValue: PropTypes.func,
@@ -0,0 +1,103 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Label, Icon } from "semantic-ui-react";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { useAuthorized } from "@truedat/core/hooks";
7
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
8
+ import { useUserFiltersDelete } from "../hooks/useUserFilters";
9
+ import { ConfirmModal } from "./ConfirmModal";
10
+
11
+ export const UserFilter = ({
12
+ userFilter,
13
+ disabled,
14
+ selectedUserFilter,
15
+ mutate,
16
+ setSelectedUserFilter,
17
+ }) => {
18
+ const authorized = useAuthorized();
19
+ const { userFiltersType, resetFilters, setAllActiveFilters } =
20
+ useSearchContext();
21
+
22
+ const isGlobal = _.prop("is_global")(userFilter);
23
+
24
+ const { trigger: deleteUserFilter } = useUserFiltersDelete(
25
+ userFilter.id,
26
+ userFiltersType
27
+ );
28
+
29
+ const handleDelete = () => {
30
+ deleteUserFilter().then(() => {
31
+ mutate();
32
+ });
33
+ };
34
+
35
+ return (
36
+ <Label
37
+ basic={selectedUserFilter == userFilter.name ? false : true}
38
+ circular
39
+ className={isGlobal ? "global" : null}
40
+ >
41
+ <span
42
+ onClick={
43
+ disabled
44
+ ? null
45
+ : (e) => {
46
+ e.preventDefault();
47
+ e.stopPropagation();
48
+ if (selectedUserFilter == userFilter.name) {
49
+ resetFilters();
50
+ setSelectedUserFilter("");
51
+ } else {
52
+ setAllActiveFilters(userFilter.filters);
53
+ setSelectedUserFilter(userFilter.name);
54
+ }
55
+ }
56
+ }
57
+ className="userFilter"
58
+ >
59
+ {userFilter.name}
60
+ </span>
61
+
62
+ {!isGlobal || authorized ? (
63
+ <ConfirmModal
64
+ icon="trash"
65
+ trigger={
66
+ <Icon
67
+ className="selectable"
68
+ color="grey"
69
+ size="small"
70
+ name="trash alternate outline"
71
+ />
72
+ }
73
+ header={
74
+ <FormattedMessage id="search.filters.actions.delete.confirmation.header" />
75
+ }
76
+ content={
77
+ <FormattedMessage
78
+ id="search.filters.actions.delete.confirmation.content"
79
+ values={{
80
+ name: (
81
+ <b>
82
+ <i>{userFilter.name}</i>
83
+ </b>
84
+ ),
85
+ }}
86
+ />
87
+ }
88
+ onConfirm={() => handleDelete()}
89
+ />
90
+ ) : null}
91
+ </Label>
92
+ );
93
+ };
94
+
95
+ UserFilter.propTypes = {
96
+ userFilter: PropTypes.object,
97
+ disabled: PropTypes.bool,
98
+ mutate: PropTypes.func,
99
+ selectedUserFilter: PropTypes.func,
100
+ resetFilters: PropTypes.func,
101
+ };
102
+
103
+ export default UserFilter;
@@ -0,0 +1,39 @@
1
+ import _ from "lodash/fp";
2
+ import { compile } from "path-to-regexp";
3
+ import useSWR from "swr";
4
+ import useSWRMutations from "swr/mutation";
5
+ import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
6
+ import {
7
+ apiJson,
8
+ apiJsonDelete,
9
+ apiJsonPost,
10
+ } from "@truedat/core/services/api";
11
+
12
+ import {
13
+ API_USER_FILTERS,
14
+ API_USER_FILTER,
15
+ API_GET_USER_FILTERS,
16
+ } from "../api";
17
+
18
+ export const useUserFilters = (type, scope) => {
19
+ const url = _.flow(
20
+ () => compile(API_GET_USER_FILTERS)({ type }),
21
+ _.concat(_.isUndefined(scope) ? "" : `?scope=${scope}`),
22
+ _.compact,
23
+ _.join("")
24
+ )("");
25
+ const { data, error, mutate } = useSWR(url, apiJson);
26
+ const userFilters = data?.data?.data;
27
+ _.sortBy(accentInsensitivePathOrder("name"))(userFilters);
28
+ return { userFilters, error, loading: !error && !data, mutate };
29
+ };
30
+
31
+ export const useUserFiltersCreate = (type) => {
32
+ const url = compile(API_USER_FILTERS)({ type });
33
+ return useSWRMutations(url, (url, { arg }) => apiJsonPost(url, arg));
34
+ };
35
+
36
+ export const useUserFiltersDelete = (id, type) => {
37
+ const url = compile(API_USER_FILTER)({ id, type });
38
+ return useSWRMutations(url, (url, { arg }) => apiJsonDelete(url, arg));
39
+ };
@@ -15,7 +15,6 @@ export default function FilterDropdown() {
15
15
  filter,
16
16
  options,
17
17
  activeFilterSelectedValues,
18
-
19
18
  openFilter,
20
19
  closeFilter,
21
20
  removeFilter,
@@ -21,7 +21,6 @@ export default function FilterMultilevelDropdown() {
21
21
  filter,
22
22
  options,
23
23
  activeFilterSelectedValues,
24
-
25
24
  openFilter,
26
25
  closeFilter,
27
26
  removeFilter,
@@ -13,6 +13,7 @@ const PopulatedHierarchyFilterDropdown = () => {
13
13
 
14
14
  const { filter, options, toggleFilterValue, activeFilterSelectedValues } =
15
15
  context;
16
+
16
17
  const hierarchyId = _.flow(
17
18
  _.reject((item) => _.values(item).includes(undefined)),
18
19
  _.first,
@@ -57,11 +58,13 @@ const PopulatedHierarchyFilterDropdown = () => {
57
58
  const descendentKeys = _.map("key")(descendents);
58
59
  return [key, ...descendentKeys];
59
60
  };
61
+
60
62
  const newValue = _.flow(
61
63
  _.flatMap(getChildrenKeys),
62
64
  _.uniq,
63
65
  _.reject(_.isNil)
64
66
  )(value);
67
+
65
68
  toggleFilterValue({ filter, value: newValue });
66
69
  };
67
70
 
@@ -72,8 +75,8 @@ const PopulatedHierarchyFilterDropdown = () => {
72
75
  name: "hierarchyFilterDropdown",
73
76
  filter,
74
77
  options: filteredOptions,
75
- activeFilterSelectedValues: idActiveValues,
76
78
  toggleFilterValue: handleToggleFilterValue,
79
+ activeFilterSelectedValues: idActiveValues,
77
80
  }}
78
81
  key={filter}
79
82
  >
@@ -84,7 +87,6 @@ const PopulatedHierarchyFilterDropdown = () => {
84
87
 
85
88
  const HierarchyFilterDropdown = () => {
86
89
  const { options } = useSearchContext();
87
-
88
90
  return _.isEmpty(options) ? (
89
91
  <FilterMultilevelDropdown />
90
92
  ) : (
@@ -24,6 +24,11 @@ export const SearchContextProvider = (props) => {
24
24
  const useSearch = _.prop("useSearch")(props);
25
25
  const useFilters = _.prop("useFilters")(props);
26
26
  const pageSize = _.propOr(20, "pageSize")(props);
27
+ const userFiltersType = _.prop("userFiltersType")(props);
28
+ const userFilterScope = _.prop("userFilterScope")(props);
29
+ const omitFilters = _.propOr([], "omitFilters")(props);
30
+ const translations = _.propOr(() => ({}), "translations")(props);
31
+ const filtersGroup = _.propOr([], "filtersGroup")(props);
27
32
 
28
33
  const { formatMessage } = useIntl();
29
34
 
@@ -37,6 +42,7 @@ export const SearchContextProvider = (props) => {
37
42
  const [allActiveFilters, setAllActiveFilters] = useState({});
38
43
  const [hiddenFilters, setHiddenFilters] = useState({});
39
44
 
45
+ const [filterParams, setFilterParams] = useState({});
40
46
  const [sortColumn, setSortColumn] = useState(initialSortColumn);
41
47
  const [sortDirection, setSortDirection] = useState(initialSortDirection);
42
48
  const [page, setPage] = useState(1);
@@ -95,16 +101,13 @@ export const SearchContextProvider = (props) => {
95
101
 
96
102
  const filters = _.flow(
97
103
  _.propOr({}, "data"),
104
+ _.omit(omitFilters),
98
105
  _.omitBy(_.flow(_.propOr([], "values"), (values) => _.size(values) < 2))
99
106
  )(filtersPayload);
100
107
 
101
108
  const availableFilters = _.flow(_.keys, _.without(selectedFilters))(filters);
102
109
  const filterTypes = _.mapValues("type")(filters);
103
110
 
104
- const translations = (formatMessage) => ({
105
- "status.raw": (v) => formatMessage({ id: v, defaultMessage: v }),
106
- });
107
-
108
111
  const activeFilterValues = _.flow(
109
112
  _.propOr({ values: [] }, activeFilterName),
110
113
  ({ values, type }) => ({
@@ -152,7 +155,9 @@ export const SearchContextProvider = (props) => {
152
155
  : null,
153
156
  [sortColumn, sortDirection]
154
157
  );
158
+
155
159
  const { trigger: triggerFilters } = useFilters();
160
+
156
161
  useEffect(() => {
157
162
  setLoadingFilters(true);
158
163
 
@@ -164,9 +169,10 @@ export const SearchContextProvider = (props) => {
164
169
  setFiltersPayload(data);
165
170
  setLoadingFilters(false);
166
171
  });
167
- }, [query, filterMust, triggerFilters]);
172
+ }, [query, filterMust]);
168
173
 
169
174
  const { trigger: triggerSearch } = useSearch();
175
+
170
176
  useEffect(() => {
171
177
  setLoading(true);
172
178
 
@@ -177,18 +183,47 @@ export const SearchContextProvider = (props) => {
177
183
  page: page - 1,
178
184
  size,
179
185
  };
186
+ setFilterParams(filterParam);
187
+
180
188
  triggerSearch(filterParam).then(({ data, headers }) => {
181
189
  setSearchData(data);
182
190
  setCountData(headers);
183
191
  setLoading(false);
184
192
  });
185
- }, [query, searchMust, sort, triggerSearch, defaultFilters, page]);
193
+ }, [query, searchMust, sort, defaultFilters, page, size]);
194
+
195
+ const makeFiltersGroup = (filters, groups) =>
196
+ _.groupBy((filter) =>
197
+ _.flow(
198
+ _.find(([_group, fields]) => _.contains(filter)(fields)),
199
+ _.prop("[0]")
200
+ )(groups)
201
+ )(filters);
202
+
203
+ const filtersByGroup = makeFiltersGroup(availableFilters, filtersGroup);
204
+
205
+ const groupWithoutFilters = (groups) =>
206
+ _.flow(
207
+ _.prop("[0]"),
208
+ (groupName) => _.prop(groupName)(filtersByGroup),
209
+ _.isEmpty
210
+ )(groups);
211
+
212
+ const availableGroupedFilters = _.flow(
213
+ (groups) => _.concat(groups, [[undefined, []]]),
214
+ _.reject(groupWithoutFilters),
215
+ _.map(([groupName, _filters]) => [
216
+ groupName,
217
+ _.prop(groupName)(filtersByGroup),
218
+ ])
219
+ )(filtersGroup);
186
220
 
187
221
  const context = {
188
222
  disabled: false,
189
223
  loadingFilters,
190
224
 
191
225
  availableFilters,
226
+ availableGroupedFilters,
192
227
  selectedFilters,
193
228
  filterTypes,
194
229
 
@@ -209,6 +244,9 @@ export const SearchContextProvider = (props) => {
209
244
  toggleHiddenFilterValue,
210
245
  searchMust,
211
246
  setQuery,
247
+ setAllActiveFilters,
248
+ allActiveFilters,
249
+ filters,
212
250
 
213
251
  searchData,
214
252
  loading,
@@ -222,6 +260,11 @@ export const SearchContextProvider = (props) => {
222
260
  count,
223
261
  page,
224
262
  size,
263
+ sort,
264
+ filterParams,
265
+
266
+ userFiltersType,
267
+ userFilterScope,
225
268
  };
226
269
 
227
270
  return (
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { Fragment } from "react";
3
3
  import { Dropdown } from "semantic-ui-react";
4
4
  import { FormattedMessage, useIntl } from "react-intl";
5
5
  import { i18nOrder } from "@truedat/core/services/sort";
@@ -10,7 +10,7 @@ const removePrefix = _.replace(/^.*\./, "");
10
10
  export default function SearchFilters() {
11
11
  const {
12
12
  disabled,
13
- availableFilters,
13
+ availableGroupedFilters,
14
14
  addFilter,
15
15
  resetFilters,
16
16
  loadingFilters: loading,
@@ -40,20 +40,36 @@ export default function SearchFilters() {
40
40
  />
41
41
  </em>
42
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)}
43
+ {_.map.convert({ cap: false })(([groupName, filters], idx) => (
44
+ <Fragment key={idx}>
45
+ {idx == 0 ? null : <Dropdown.Divider />}
46
+ {groupName ? (
47
+ <Dropdown.Header key={groupName}>
48
+ <b>
49
+ <FormattedMessage
50
+ id={`filters.group.header.${groupName}`}
51
+ defaultMessage={groupName}
52
+ />
53
+ </b>
54
+ </Dropdown.Header>
55
+ ) : null}
56
+
57
+ {_.flow(
58
+ _.defaultTo([]),
59
+ _.sortBy(i18nOrder(formatMessage, "filters")),
60
+ _.map((filter) => (
61
+ <Dropdown.Item
62
+ key={filter}
63
+ text={formatMessage({
64
+ id: `filters.${filter}`,
65
+ defaultMessage: removePrefix(filter),
66
+ })}
67
+ onClick={() => addFilter({ filter })}
68
+ />
69
+ ))
70
+ )(filters)}
71
+ </Fragment>
72
+ ))(availableGroupedFilters)}
57
73
  </Dropdown.Menu>
58
74
  </Dropdown>
59
75
  );
@@ -1,6 +1,9 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { useState } from "react";
3
3
  import { FormattedMessage } from "react-intl";
4
+ import { useUserFilters, useUserFiltersCreate } from "../hooks/useUserFilters";
5
+ import ModalSaveFilter from "../components/ModalSaveFilter";
6
+ import UserFilters from "./UserFilters";
4
7
  import FilterDropdown from "./FilterDropdown";
5
8
  import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
6
9
  import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
@@ -14,10 +17,37 @@ export default function SearchSelectedFilters() {
14
17
  filterTypes,
15
18
  activeFilterName,
16
19
  activeFilterValues,
20
+ allActiveFilters,
21
+ userFiltersType,
22
+ userFilterScope,
17
23
  } = context;
24
+ const [selectedUserFilter, setSelectedUserFilter] = useState();
25
+ const { trigger: saveFilters } = useUserFiltersCreate(userFiltersType);
26
+ const { userFilters, loading, mutate } = useUserFilters(
27
+ userFiltersType,
28
+ userFilterScope
29
+ );
30
+
31
+ const handleSubmit = ({ filterName, filters, isGlobal }) => {
32
+ const requestData = {
33
+ user_search_filter: { name: filterName, filters, is_global: isGlobal },
34
+ };
35
+ saveFilters(requestData).then(() => {
36
+ mutate();
37
+ });
38
+ };
18
39
 
19
40
  return (
20
41
  <>
42
+ {!_.isEmpty(userFilters) ? (
43
+ <UserFilters
44
+ mutate={mutate}
45
+ userFilters={userFilters}
46
+ disabled={loading}
47
+ setSelectedUserFilter={setSelectedUserFilter}
48
+ selectedUserFilter={selectedUserFilter}
49
+ />
50
+ ) : null}
21
51
  <div className="selectedFilters">
22
52
  {_.isEmpty(selectedFilters) ? null : (
23
53
  <>
@@ -29,6 +59,7 @@ export default function SearchSelectedFilters() {
29
59
  const options = _.isEqual(filter, activeFilterName)
30
60
  ? activeFilterValues
31
61
  : null;
62
+
32
63
  return (
33
64
  <SearchContext.Provider
34
65
  value={{ ...context, filter, options }}
@@ -44,9 +75,20 @@ export default function SearchSelectedFilters() {
44
75
  </SearchContext.Provider>
45
76
  );
46
77
  })}
47
- <a className="resetFilters" onClick={() => resetFilters()}>
78
+ <a
79
+ className="resetFilters"
80
+ onClick={() => {
81
+ resetFilters();
82
+ setSelectedUserFilter("");
83
+ }}
84
+ >
48
85
  <FormattedMessage id="search.clear_filters" />
49
86
  </a>
87
+ <ModalSaveFilter
88
+ saveFilters={handleSubmit}
89
+ activeFilters={allActiveFilters}
90
+ scope={userFilterScope}
91
+ />
50
92
  </>
51
93
  )}
52
94
  </div>
@@ -1,10 +1,9 @@
1
1
  import React from "react";
2
2
  import { Input } from "semantic-ui-react";
3
3
  import { useIntl } from "react-intl";
4
- import SearchFilters from "@truedat/core/search/SearchFilters";
5
- import SearchSelectedFilters from "@truedat/core/search/SearchSelectedFilters";
6
-
7
- import { useSearchContext } from "@truedat/core/search/SearchContext";
4
+ import SearchFilters from "./SearchFilters";
5
+ import SearchSelectedFilters from "./SearchSelectedFilters";
6
+ import { useSearchContext } from "./SearchContext";
8
7
 
9
8
  export default function SearchWidget() {
10
9
  const { formatMessage } = useIntl();
@@ -0,0 +1,103 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Label, Icon } from "semantic-ui-react";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { useAuthorized } from "@truedat/core/hooks";
7
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
8
+ import { useUserFiltersDelete } from "../hooks/useUserFilters";
9
+ import { ConfirmModal } from "./ConfirmModal";
10
+
11
+ export const UserFilter = ({
12
+ userFilter,
13
+ disabled,
14
+ selectedUserFilter,
15
+ mutate,
16
+ setSelectedUserFilter,
17
+ }) => {
18
+ const authorized = useAuthorized();
19
+ const { userFiltersType, resetFilters, setAllActiveFilters } =
20
+ useSearchContext();
21
+
22
+ const isGlobal = _.prop("is_global")(userFilter);
23
+
24
+ const { trigger: deleteUserFilter } = useUserFiltersDelete(
25
+ userFilter.id,
26
+ userFiltersType
27
+ );
28
+
29
+ const handleDelete = () => {
30
+ deleteUserFilter().then(() => {
31
+ mutate();
32
+ });
33
+ };
34
+
35
+ return (
36
+ <Label
37
+ basic={selectedUserFilter == userFilter.name ? false : true}
38
+ circular
39
+ className={isGlobal ? "global" : null}
40
+ >
41
+ <span
42
+ onClick={
43
+ disabled
44
+ ? null
45
+ : (e) => {
46
+ e.preventDefault();
47
+ e.stopPropagation();
48
+ if (selectedUserFilter == userFilter.name) {
49
+ resetFilters();
50
+ setSelectedUserFilter("");
51
+ } else {
52
+ setAllActiveFilters(userFilter.filters);
53
+ setSelectedUserFilter(userFilter.name);
54
+ }
55
+ }
56
+ }
57
+ className="userFilter"
58
+ >
59
+ {userFilter.name}
60
+ </span>
61
+
62
+ {!isGlobal || authorized ? (
63
+ <ConfirmModal
64
+ icon="trash"
65
+ trigger={
66
+ <Icon
67
+ className="selectable"
68
+ color="grey"
69
+ size="small"
70
+ name="trash alternate outline"
71
+ />
72
+ }
73
+ header={
74
+ <FormattedMessage id="search.filters.actions.delete.confirmation.header" />
75
+ }
76
+ content={
77
+ <FormattedMessage
78
+ id="search.filters.actions.delete.confirmation.content"
79
+ values={{
80
+ name: (
81
+ <b>
82
+ <i>{userFilter.name}</i>
83
+ </b>
84
+ ),
85
+ }}
86
+ />
87
+ }
88
+ onConfirm={() => handleDelete()}
89
+ />
90
+ ) : null}
91
+ </Label>
92
+ );
93
+ };
94
+
95
+ UserFilter.propTypes = {
96
+ userFilter: PropTypes.object,
97
+ disabled: PropTypes.bool,
98
+ mutate: PropTypes.func,
99
+ selectedUserFilter: PropTypes.func,
100
+ resetFilters: PropTypes.func,
101
+ };
102
+
103
+ export default UserFilter;
@@ -0,0 +1,136 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Label, Icon } from "semantic-ui-react";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { useAuthorized } from "@truedat/core/hooks";
7
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
8
+ import { useUserFiltersDelete } from "../hooks/useUserFilters";
9
+ import { ConfirmModal } from "../components/ConfirmModal";
10
+
11
+ export const DeleteModal = ({ userFilter, userFiltersType, mutate }) => {
12
+ const { trigger: deleteUserFilter } = useUserFiltersDelete(
13
+ userFilter.id,
14
+ userFiltersType
15
+ );
16
+ const handleDelete = () => {
17
+ deleteUserFilter().then(() => {
18
+ mutate();
19
+ });
20
+ };
21
+ return (
22
+ <ConfirmModal
23
+ icon="trash"
24
+ trigger={
25
+ <Icon
26
+ className="selectable"
27
+ color="grey"
28
+ size="small"
29
+ name="trash alternate outline"
30
+ />
31
+ }
32
+ header={
33
+ <FormattedMessage id="search.filters.actions.delete.confirmation.header" />
34
+ }
35
+ content={
36
+ <FormattedMessage
37
+ id="search.filters.actions.delete.confirmation.content"
38
+ values={{
39
+ name: (
40
+ <b>
41
+ <i>{userFilter.name}</i>
42
+ </b>
43
+ ),
44
+ }}
45
+ />
46
+ }
47
+ onConfirm={() => {
48
+ handleDelete();
49
+ }}
50
+ />
51
+ );
52
+ };
53
+
54
+ DeleteModal.propTypes = {
55
+ userFilter: PropTypes.object,
56
+ userFiltersType: PropTypes.string,
57
+ mutate: PropTypes.func,
58
+ };
59
+
60
+ export const UserFilters = ({
61
+ mutate,
62
+ userFilters,
63
+ disabled,
64
+ setSelectedUserFilter,
65
+ selectedUserFilter,
66
+ }) => {
67
+ const authorized = useAuthorized();
68
+ const { userFiltersType, resetFilters, setAllActiveFilters } =
69
+ useSearchContext();
70
+
71
+ const sortedUserFilters = _.orderBy(
72
+ ["is_global", "id"],
73
+ ["desc", "asc"]
74
+ )(userFilters);
75
+
76
+ return _.isEmpty(userFilters) ? null : (
77
+ <div className="selectedFilters">
78
+ {sortedUserFilters.map((userFilter, key) => {
79
+ const isGlobal = _.prop("is_global")(userFilter);
80
+
81
+ return (
82
+ <Label
83
+ basic={selectedUserFilter == userFilter.name ? false : true}
84
+ circular
85
+ className={isGlobal ? "global" : null}
86
+ key={key}
87
+ >
88
+ <span
89
+ onClick={
90
+ disabled
91
+ ? null
92
+ : (e) => {
93
+ e.preventDefault();
94
+ e.stopPropagation();
95
+ if (selectedUserFilter == userFilter.name) {
96
+ resetFilters();
97
+ setSelectedUserFilter(null);
98
+ } else {
99
+ setAllActiveFilters(
100
+ _.propOr({}, "filters")(userFilter)
101
+ );
102
+ setSelectedUserFilter(userFilter.name);
103
+ }
104
+ }
105
+ }
106
+ className="userFilter"
107
+ >
108
+ {userFilter.name}
109
+ </span>
110
+
111
+ {!isGlobal || authorized ? (
112
+ <DeleteModal
113
+ userFilter={userFilter}
114
+ userFiltersType={userFiltersType}
115
+ mutate={mutate}
116
+ />
117
+ ) : null}
118
+ </Label>
119
+ );
120
+ })}
121
+ </div>
122
+ );
123
+ };
124
+
125
+ UserFilters.propTypes = {
126
+ selectedUserFilter: PropTypes.string,
127
+ userFilters: PropTypes.array,
128
+ disabled: PropTypes.bool,
129
+ userFilterScope: PropTypes.string,
130
+ mutate: PropTypes.func,
131
+ applyUserFilter: PropTypes.func,
132
+ deleteUserFilter: PropTypes.func,
133
+ resetFilters: PropTypes.func,
134
+ };
135
+
136
+ export default UserFilters;