@blaze-cms/plugin-data-ui 0.146.0-translations.8 → 0.147.0-rc-eagle.4

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.
Files changed (211) hide show
  1. package/lib/components/EntityDataListing/EntityDataListing.js +21 -19
  2. package/lib/components/EntityDataListing/EntityDataListing.js.map +1 -1
  3. package/lib/components/EntityDataListing/index.js +2 -3
  4. package/lib/components/EntityDataListing/index.js.map +1 -1
  5. package/lib/components/EntityManager/Entity/EntitiyNavLinks/EntityNavLinks.js +18 -19
  6. package/lib/components/EntityManager/Entity/EntitiyNavLinks/EntityNavLinks.js.map +1 -1
  7. package/lib/components/EntityManager/Entity/EntitiyNavLinks/index.js +2 -3
  8. package/lib/components/EntityManager/Entity/EntitiyNavLinks/index.js.map +1 -1
  9. package/lib/components/EntityManager/Entity/Entity.js +284 -457
  10. package/lib/components/EntityManager/Entity/Entity.js.map +1 -1
  11. package/lib/components/EntityManager/Entity/EntityHeader/EntityHeader.js +62 -66
  12. package/lib/components/EntityManager/Entity/EntityHeader/EntityHeader.js.map +1 -1
  13. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/HeaderPreviewButton.js +32 -56
  14. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/HeaderPreviewButton.js.map +1 -1
  15. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/index.js +2 -3
  16. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/index.js.map +1 -1
  17. package/lib/components/EntityManager/Entity/EntityHeader/index.js +2 -3
  18. package/lib/components/EntityManager/Entity/EntityHeader/index.js.map +1 -1
  19. package/lib/components/EntityManager/Entity/SideBarRelations/container/CustomSidebarInfoContainer.js +20 -26
  20. package/lib/components/EntityManager/Entity/SideBarRelations/container/CustomSidebarInfoContainer.js.map +1 -1
  21. package/lib/components/EntityManager/Entity/SideBarRelations/helpers/build-dynamic-query.js +20 -17
  22. package/lib/components/EntityManager/Entity/SideBarRelations/helpers/build-dynamic-query.js.map +1 -1
  23. package/lib/components/EntityManager/Entity/SideBarRelations/hooks/useCustomSidebarData.js +34 -32
  24. package/lib/components/EntityManager/Entity/SideBarRelations/hooks/useCustomSidebarData.js.map +1 -1
  25. package/lib/components/EntityManager/Entity/SideBarRelations/index.js +95 -158
  26. package/lib/components/EntityManager/Entity/SideBarRelations/index.js.map +1 -1
  27. package/lib/components/EntityManager/Entity/SideBarRelations/presentational/CustomSidebarInfo.js +23 -30
  28. package/lib/components/EntityManager/Entity/SideBarRelations/presentational/CustomSidebarInfo.js.map +1 -1
  29. package/lib/components/EntityManager/Entity/actions-handlers/create/index.js +2 -3
  30. package/lib/components/EntityManager/Entity/actions-handlers/create/index.js.map +1 -1
  31. package/lib/components/EntityManager/Entity/actions-handlers/create/submit.js +42 -56
  32. package/lib/components/EntityManager/Entity/actions-handlers/create/submit.js.map +1 -1
  33. package/lib/components/EntityManager/Entity/actions-handlers/delete/delete.js +29 -45
  34. package/lib/components/EntityManager/Entity/actions-handlers/delete/delete.js.map +1 -1
  35. package/lib/components/EntityManager/Entity/actions-handlers/delete/index.js +2 -3
  36. package/lib/components/EntityManager/Entity/actions-handlers/delete/index.js.map +1 -1
  37. package/lib/components/EntityManager/Entity/actions-handlers/index.js +7 -8
  38. package/lib/components/EntityManager/Entity/actions-handlers/index.js.map +1 -1
  39. package/lib/components/EntityManager/Entity/actions-handlers/shared/index.js +2 -3
  40. package/lib/components/EntityManager/Entity/actions-handlers/shared/index.js.map +1 -1
  41. package/lib/components/EntityManager/Entity/actions-handlers/shared/publish.js +36 -57
  42. package/lib/components/EntityManager/Entity/actions-handlers/shared/publish.js.map +1 -1
  43. package/lib/components/EntityManager/Entity/actions-handlers/update/index.js +2 -3
  44. package/lib/components/EntityManager/Entity/actions-handlers/update/index.js.map +1 -1
  45. package/lib/components/EntityManager/Entity/actions-handlers/update/submit.js +47 -66
  46. package/lib/components/EntityManager/Entity/actions-handlers/update/submit.js.map +1 -1
  47. package/lib/components/EntityManager/Entity/index.js +2 -3
  48. package/lib/components/EntityManager/Entity/index.js.map +1 -1
  49. package/lib/components/EntityManager/EntityManager.js +91 -166
  50. package/lib/components/EntityManager/EntityManager.js.map +1 -1
  51. package/lib/components/EntityManager/index.js +2 -3
  52. package/lib/components/EntityManager/index.js.map +1 -1
  53. package/lib/components/EntityManager/utils/RecordEditContext/index.js +3 -4
  54. package/lib/components/EntityManager/utils/RecordEditContext/index.js.map +1 -1
  55. package/lib/components/EntityManager/utils/data-mappers.js +34 -46
  56. package/lib/components/EntityManager/utils/data-mappers.js.map +1 -1
  57. package/lib/components/EntityManager/utils/entity.js +35 -80
  58. package/lib/components/EntityManager/utils/entity.js.map +1 -1
  59. package/lib/components/EntityManager/utils/entityAvailableActions.js +51 -110
  60. package/lib/components/EntityManager/utils/entityAvailableActions.js.map +1 -1
  61. package/lib/components/EntityManager/utils/is-form-empty.js +6 -10
  62. package/lib/components/EntityManager/utils/is-form-empty.js.map +1 -1
  63. package/lib/components/EntityManager/utils/query.js +17 -10
  64. package/lib/components/EntityManager/utils/query.js.map +1 -1
  65. package/lib/components/InfoBoxes/InfoBoxTooltip.js +19 -16
  66. package/lib/components/InfoBoxes/InfoBoxTooltip.js.map +1 -1
  67. package/lib/components/InfoBoxes/InfoBoxes.js +19 -19
  68. package/lib/components/InfoBoxes/InfoBoxes.js.map +1 -1
  69. package/lib/components/InfoBoxes/container/InfoBoxContainer.js +26 -38
  70. package/lib/components/InfoBoxes/container/InfoBoxContainer.js.map +1 -1
  71. package/lib/components/InfoBoxes/helpers/build-dynamic-query.js +21 -16
  72. package/lib/components/InfoBoxes/helpers/build-dynamic-query.js.map +1 -1
  73. package/lib/components/InfoBoxes/hooks/useData.js +33 -30
  74. package/lib/components/InfoBoxes/hooks/useData.js.map +1 -1
  75. package/lib/components/InfoBoxes/hooks/useInfoBox.js +10 -10
  76. package/lib/components/InfoBoxes/hooks/useInfoBox.js.map +1 -1
  77. package/lib/components/InfoBoxes/index.js +2 -3
  78. package/lib/components/InfoBoxes/index.js.map +1 -1
  79. package/lib/components/InfoBoxes/presentational/InfoBox.js +27 -34
  80. package/lib/components/InfoBoxes/presentational/InfoBox.js.map +1 -1
  81. package/lib/components/InfoBoxes/presentational/InfoBoxLabel.js +11 -12
  82. package/lib/components/InfoBoxes/presentational/InfoBoxLabel.js.map +1 -1
  83. package/lib/components/InfoBoxes/presentational/InfoBoxValue.js +12 -12
  84. package/lib/components/InfoBoxes/presentational/InfoBoxValue.js.map +1 -1
  85. package/lib/components/ListingTable/ListingTable.js +155 -296
  86. package/lib/components/ListingTable/ListingTable.js.map +1 -1
  87. package/lib/components/ListingTable/ListingTableContent/ListingTableContent.js +53 -58
  88. package/lib/components/ListingTable/ListingTableContent/ListingTableContent.js.map +1 -1
  89. package/lib/components/ListingTable/ListingTableContent/index.js +2 -3
  90. package/lib/components/ListingTable/ListingTableContent/index.js.map +1 -1
  91. package/lib/components/ListingTable/SearchFilter/SearchContainer.js +436 -262
  92. package/lib/components/ListingTable/SearchFilter/SearchContainer.js.map +1 -1
  93. package/lib/components/ListingTable/SearchFilter/SearchFilter.js +30 -37
  94. package/lib/components/ListingTable/SearchFilter/SearchFilter.js.map +1 -1
  95. package/lib/components/ListingTable/SearchFilter/helpers.js +82 -71
  96. package/lib/components/ListingTable/SearchFilter/helpers.js.map +1 -1
  97. package/lib/components/ListingTable/SearchFilter/querys.js +5 -5
  98. package/lib/components/ListingTable/SearchFilter/querys.js.map +1 -1
  99. package/lib/components/ListingTable/TableActions/TableActions.js +27 -31
  100. package/lib/components/ListingTable/TableActions/TableActions.js.map +1 -1
  101. package/lib/components/ListingTable/TableActions/index.js +2 -3
  102. package/lib/components/ListingTable/TableActions/index.js.map +1 -1
  103. package/lib/components/ListingTable/index.js +2 -3
  104. package/lib/components/ListingTable/index.js.map +1 -1
  105. package/lib/components/ListingTable/mappers/populate-rows.js +103 -122
  106. package/lib/components/ListingTable/mappers/populate-rows.js.map +1 -1
  107. package/lib/components/ListingTable/service/index.js +66 -83
  108. package/lib/components/ListingTable/service/index.js.map +1 -1
  109. package/lib/components/Tabs/index.js +26 -42
  110. package/lib/components/Tabs/index.js.map +1 -1
  111. package/lib/components/hooks/useCallbackDebounce.js +5 -9
  112. package/lib/components/hooks/useCallbackDebounce.js.map +1 -1
  113. package/lib/constants.js +30 -31
  114. package/lib/constants.js.map +1 -1
  115. package/lib/icons/ContentIcon.js +17 -20
  116. package/lib/icons/ContentIcon.js.map +1 -1
  117. package/lib/icons/SettingsIcon.js +12 -15
  118. package/lib/icons/SettingsIcon.js.map +1 -1
  119. package/lib/icons/TaxonomyIcon.js +15 -18
  120. package/lib/icons/TaxonomyIcon.js.map +1 -1
  121. package/lib/icons/UsersIcon.js +11 -14
  122. package/lib/icons/UsersIcon.js.map +1 -1
  123. package/lib/icons/index.js +8 -9
  124. package/lib/icons/index.js.map +1 -1
  125. package/lib/index.js +88 -121
  126. package/lib/index.js.map +1 -1
  127. package/lib/utils/add-content-menu-items.js +67 -93
  128. package/lib/utils/add-content-menu-items.js.map +1 -1
  129. package/lib/utils/build-create-entity-mutation.js +12 -9
  130. package/lib/utils/build-create-entity-mutation.js.map +1 -1
  131. package/lib/utils/build-delete-entity-mutation.js +11 -8
  132. package/lib/utils/build-delete-entity-mutation.js.map +1 -1
  133. package/lib/utils/build-listing-query.js +17 -14
  134. package/lib/utils/build-listing-query.js.map +1 -1
  135. package/lib/utils/build-update-data-query.js +15 -9
  136. package/lib/utils/build-update-data-query.js.map +1 -1
  137. package/lib/utils/build-update-publish-unpublish-mutation.js +15 -8
  138. package/lib/utils/build-update-publish-unpublish-mutation.js.map +1 -1
  139. package/lib/utils/get-default-query-params.js +7 -6
  140. package/lib/utils/get-default-query-params.js.map +1 -1
  141. package/lib/utils/hoc/withContext.js +4 -6
  142. package/lib/utils/hoc/withContext.js.map +1 -1
  143. package/lib/utils/hooks/useToggle.js +4 -12
  144. package/lib/utils/hooks/useToggle.js.map +1 -1
  145. package/lib/utils/index.js +2 -3
  146. package/lib/utils/index.js.map +1 -1
  147. package/lib-es/components/EntityManager/Entity/Entity.js +8 -20
  148. package/lib-es/components/EntityManager/Entity/Entity.js.map +1 -1
  149. package/lib-es/components/EntityManager/Entity/EntityHeader/EntityHeader.js.map +1 -1
  150. package/lib-es/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/HeaderPreviewButton.js.map +1 -1
  151. package/lib-es/components/EntityManager/Entity/SideBarRelations/helpers/build-dynamic-query.js.map +1 -1
  152. package/lib-es/components/EntityManager/Entity/SideBarRelations/hooks/useCustomSidebarData.js.map +1 -1
  153. package/lib-es/components/EntityManager/Entity/SideBarRelations/index.js +6 -3
  154. package/lib-es/components/EntityManager/Entity/SideBarRelations/index.js.map +1 -1
  155. package/lib-es/components/EntityManager/Entity/SideBarRelations/presentational/CustomSidebarInfo.js.map +1 -1
  156. package/lib-es/components/EntityManager/Entity/actions-handlers/create/submit.js.map +1 -1
  157. package/lib-es/components/EntityManager/Entity/actions-handlers/shared/publish.js.map +1 -1
  158. package/lib-es/components/EntityManager/EntityManager.js.map +1 -1
  159. package/lib-es/components/EntityManager/utils/entity.js.map +1 -1
  160. package/lib-es/components/EntityManager/utils/query.js.map +1 -1
  161. package/lib-es/components/InfoBoxes/InfoBoxTooltip.js.map +1 -1
  162. package/lib-es/components/InfoBoxes/container/InfoBoxContainer.js.map +1 -1
  163. package/lib-es/components/InfoBoxes/helpers/build-dynamic-query.js.map +1 -1
  164. package/lib-es/components/InfoBoxes/hooks/useData.js.map +1 -1
  165. package/lib-es/components/InfoBoxes/presentational/InfoBox.js.map +1 -1
  166. package/lib-es/components/InfoBoxes/presentational/InfoBoxLabel.js.map +1 -1
  167. package/lib-es/components/ListingTable/ListingTable.js +2 -2
  168. package/lib-es/components/ListingTable/ListingTable.js.map +1 -1
  169. package/lib-es/components/ListingTable/ListingTableContent/ListingTableContent.js +20 -17
  170. package/lib-es/components/ListingTable/ListingTableContent/ListingTableContent.js.map +1 -1
  171. package/lib-es/components/ListingTable/SearchFilter/SearchContainer.js +323 -51
  172. package/lib-es/components/ListingTable/SearchFilter/SearchContainer.js.map +1 -1
  173. package/lib-es/components/ListingTable/SearchFilter/SearchFilter.js +7 -3
  174. package/lib-es/components/ListingTable/SearchFilter/SearchFilter.js.map +1 -1
  175. package/lib-es/components/ListingTable/SearchFilter/helpers.js +39 -8
  176. package/lib-es/components/ListingTable/SearchFilter/helpers.js.map +1 -1
  177. package/lib-es/components/ListingTable/SearchFilter/querys.js.map +1 -1
  178. package/lib-es/components/ListingTable/mappers/populate-rows.js.map +1 -1
  179. package/lib-es/components/ListingTable/service/index.js.map +1 -1
  180. package/lib-es/components/Tabs/index.js +6 -7
  181. package/lib-es/components/Tabs/index.js.map +1 -1
  182. package/lib-es/index.js +12 -3
  183. package/lib-es/index.js.map +1 -1
  184. package/lib-es/utils/add-content-menu-items.js.map +1 -1
  185. package/lib-es/utils/build-create-entity-mutation.js.map +1 -1
  186. package/lib-es/utils/build-delete-entity-mutation.js.map +1 -1
  187. package/lib-es/utils/build-listing-query.js.map +1 -1
  188. package/lib-es/utils/build-update-data-query.js.map +1 -1
  189. package/lib-es/utils/build-update-publish-unpublish-mutation.js.map +1 -1
  190. package/lib-es/utils/hoc/withContext.js +1 -1
  191. package/lib-es/utils/hoc/withContext.js.map +1 -1
  192. package/package.json +27 -22
  193. package/src/components/EntityManager/Entity/Entity.js +59 -77
  194. package/src/components/EntityManager/Entity/EntityHeader/EntityHeader.js +1 -2
  195. package/src/components/EntityManager/Entity/SideBarRelations/hooks/useCustomSidebarData.js +9 -7
  196. package/src/components/EntityManager/Entity/SideBarRelations/index.js +64 -59
  197. package/src/components/EntityManager/EntityManager.js +52 -45
  198. package/src/components/InfoBoxes/container/InfoBoxContainer.js +3 -6
  199. package/src/components/InfoBoxes/hooks/useData.js +6 -1
  200. package/src/components/ListingTable/ListingTable.js +13 -21
  201. package/src/components/ListingTable/ListingTableContent/ListingTableContent.js +38 -44
  202. package/src/components/ListingTable/SearchFilter/SearchContainer.js +411 -134
  203. package/src/components/ListingTable/SearchFilter/SearchFilter.js +5 -2
  204. package/src/components/ListingTable/SearchFilter/helpers.js +56 -8
  205. package/src/components/ListingTable/service/index.js +2 -3
  206. package/src/components/Tabs/index.js +17 -21
  207. package/src/index.js +11 -7
  208. package/src/utils/hoc/withContext.js +5 -4
  209. package/CHANGELOG.md +0 -1947
  210. package/babel.config.js +0 -3
  211. package/jest.config.js +0 -5
@@ -1,10 +1,21 @@
1
1
  import React, { useState, useMemo, useEffect, useCallback } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import Select from '@blaze-react/select';
4
+ import MultiSelect from '@blaze-react/multiselect';
5
+ import debounce from 'lodash.debounce';
6
+ import unionBy from 'lodash.unionby';
7
+
4
8
  import { useQuery, useApolloClient } from '@apollo/client';
5
- import { getDynamicQuery, getQuery } from '@blaze-cms/admin-ui-utils';
9
+ import {
10
+ getDynamicQuery,
11
+ getQuery,
12
+ fetchFilterByDataFromElastic,
13
+ fetchAllEntities
14
+ } from '@blaze-cms/admin-ui-utils';
15
+ import { getStringTypeProps } from '@blaze-cms/utils';
16
+
6
17
  import { BsArrowCounterclockwise, BsSearch } from 'react-icons/bs';
7
- import { updateListFilters, buildQueryFields } from './helpers';
18
+ import { updateListFilters, buildQueryFields, getTidyLabel } from './helpers';
8
19
  import { NOOP_QUERY } from './querys';
9
20
  import { ENTER_KEY, DEFAULT_FILTER_OPTION_LABEL } from '../../../constants';
10
21
 
@@ -12,175 +23,403 @@ const SearchContainer = ({
12
23
  setListFilters,
13
24
  keywordFiltersToUse = [],
14
25
  selectFiltersToDisplay = [],
26
+ multiSelectFiltersToDisplay = [],
15
27
  schema,
16
- index
28
+ index,
29
+ values
17
30
  }) => {
18
- const apolloClient = useApolloClient();
31
+ const client = useApolloClient();
32
+
19
33
  const [searchTerm, setSearchTerm] = useState('');
20
34
  const [selectedFilters, setSelectedFilters] = useState({});
21
35
  const [selectOptions, setSelectOptions] = useState([]);
36
+ const [multiSelectOptions, setMultiSelectOptions] = useState([]);
37
+ const [allEntitySchemas, setAllEntitySchemas] = useState([]);
22
38
 
23
39
  const displaySearchFilter = keywordFiltersToUse.length > 0;
24
40
  const displaySelectFilters = selectFiltersToDisplay.length > 0;
41
+ const displayMultiSelectFilters = multiSelectFiltersToDisplay.length > 0;
42
+ const displayAggregations = displaySelectFilters || displayMultiSelectFilters;
25
43
 
26
- const { keywordSearchProperties, keywordSearchLabels } = useMemo(
27
- () => {
28
- const labels = [];
29
- const properties = [];
30
-
31
- keywordFiltersToUse.forEach(([property, { label }]) => {
32
- if (label) labels.push(label);
33
- properties.push(property);
34
- });
44
+ useEffect(() => {
45
+ (async () => {
46
+ try {
47
+ const all = await fetchAllEntities({ client });
48
+ setAllEntitySchemas(all || []);
49
+ } catch (error) {
50
+ setAllEntitySchemas([]);
51
+ }
52
+ })();
53
+ }, [client]);
35
54
 
36
- return { keywordSearchProperties: properties, keywordSearchLabels: labels };
37
- },
38
- [keywordFiltersToUse]
55
+ const multiSelectPropertySet = useMemo(
56
+ () => new Set(multiSelectFiltersToDisplay.map(([property]) => property)),
57
+ [multiSelectFiltersToDisplay]
39
58
  );
40
59
 
60
+ const { keywordSearchProperties, keywordSearchLabels } = useMemo(() => {
61
+ const labels = [];
62
+ const properties = [];
63
+
64
+ keywordFiltersToUse.forEach(([property, { label }]) => {
65
+ if (label) labels.push(label);
66
+ properties.push(property);
67
+ });
68
+
69
+ return { keywordSearchProperties: properties, keywordSearchLabels: labels };
70
+ }, [keywordFiltersToUse]);
71
+
41
72
  const { gqlFields, rawQuery } = useMemo(
42
- () => buildQueryFields(selectFiltersToDisplay, schema.id),
43
- [selectFiltersToDisplay, schema.id]
73
+ () => buildQueryFields([...selectFiltersToDisplay, ...multiSelectFiltersToDisplay], schema.id),
74
+ [selectFiltersToDisplay, multiSelectFiltersToDisplay, schema.id]
44
75
  );
45
- const action = displaySelectFilters
76
+
77
+ const action = displayAggregations
46
78
  ? getDynamicQuery('ADMIN_SEARCH')([schema], gqlFields, false, index)
47
79
  : NOOP_QUERY;
48
80
 
49
81
  const { data } = useQuery(action, {
50
- skip: !displaySelectFilters,
82
+ skip: !displayAggregations,
51
83
  variables: { where: rawQuery, limit: 0 },
52
84
  fetchPolicy: 'cache-and-network'
53
85
  });
54
86
 
55
- const filterData = useMemo(
56
- () => {
57
- const { searchResults: { rawResults: { aggregations = {} } = {} } = {} } = data || {};
58
- const proccessedData = [];
59
- Object.entries(aggregations).forEach(([key, { buckets }]) => {
60
- proccessedData.push(
61
- Array.isArray(buckets) ? [key, buckets.map(b => b.key).filter(Boolean)] : []
62
- );
87
+ const buildRelationsIndex = useCallback(schemaObj => {
88
+ const items = [];
89
+
90
+ if (schemaObj.properties) {
91
+ Object.entries(schemaObj.properties).forEach(([propName, definition]) => {
92
+ const { relation } = definition || {};
93
+ if (!relation) return;
94
+
95
+ const { localField: relationLocalField, entityIdentifier } = relation;
96
+
97
+ const localField = relationLocalField || propName;
98
+
99
+ if (localField) items.push({ localField, entityIdentifier });
63
100
  });
101
+ }
64
102
 
65
- return proccessedData;
66
- },
67
- [data]
103
+ if (Array.isArray(schemaObj.relations)) {
104
+ schemaObj.relations.forEach(relation => {
105
+ if (!relation.localField) return;
106
+
107
+ const { localField, entityIdentifier } = relation;
108
+
109
+ items.push({ localField, entityIdentifier });
110
+ });
111
+ }
112
+
113
+ const byKey = new Map();
114
+ items.forEach(item => {
115
+ const key = `${item.localField}|${item.entityIdentifier || ''}`;
116
+ if (!byKey.has(key)) byKey.set(key, item);
117
+ });
118
+
119
+ return Array.from(byKey.values());
120
+ }, []);
121
+
122
+ const relationsIndex = useMemo(() => buildRelationsIndex(schema), [schema, buildRelationsIndex]);
123
+
124
+ const stringTypeProps = useMemo(
125
+ () => getStringTypeProps(schema.id, relationsIndex, allEntitySchemas),
126
+ [schema.id, relationsIndex, allEntitySchemas]
68
127
  );
69
128
 
70
- useEffect(
71
- () => {
72
- if (!filterData.length) {
73
- setSelectOptions([]);
74
- return;
75
- }
129
+ const aggregationsData = useMemo(() => {
130
+ const { searchResults: { rawResults: { aggregations = {} } = {} } = {} } = data || {};
131
+ const processed = [];
132
+ Object.entries(aggregations).forEach(([key, { buckets }]) => {
133
+ processed.push(
134
+ Array.isArray(buckets) ? [key, buckets.map(bucket => bucket.key).filter(Boolean)] : []
135
+ );
136
+ });
137
+ return processed;
138
+ }, [data]);
76
139
 
77
- (async () => {
78
- const results = await Promise.all(
79
- filterData.map(async ([property, ids]) => {
80
- try {
81
- if (!ids || !ids.length) return {};
140
+ const findFilterConfig = useCallback(
141
+ property =>
142
+ selectFiltersToDisplay.find(([prop]) => prop === property)?.[1] ||
143
+ multiSelectFiltersToDisplay.find(([prop]) => prop === property)?.[1],
144
+ [selectFiltersToDisplay, multiSelectFiltersToDisplay]
145
+ );
82
146
 
83
- const [, selectionDetails] =
84
- selectFiltersToDisplay.find(([selectProperty]) => selectProperty === property) ||
85
- [];
147
+ useEffect(() => {
148
+ if (!aggregationsData.length) {
149
+ setSelectOptions([]);
150
+ setMultiSelectOptions([]);
151
+ return;
152
+ }
86
153
 
87
- if (!selectionDetails) return null;
154
+ (async () => {
155
+ const results = await Promise.all(
156
+ aggregationsData.map(async ([property, ids]) => {
157
+ try {
158
+ if (!ids || !ids.length) return null;
88
159
 
89
- if (!selectionDetails.relation) {
90
- return {
91
- id: property,
92
- label: selectionDetails.label,
93
- options: ids.map(value => [value, value])
94
- };
95
- }
96
- const query = getQuery('GET_ENTITY_SCHEMA');
97
- const {
98
- data: {
99
- getEntitySchemas: [relationSchema]
100
- }
101
- } = await apolloClient.query({
102
- query,
103
- variables: { identifier: selectionDetails.relation.entityIdentifier }
104
- });
160
+ const config = findFilterConfig(property);
161
+ if (!config) return null;
105
162
 
106
- const {
107
- data: { result: selected = [] }
108
- } = await apolloClient
109
- .query({
110
- query: getDynamicQuery('GET_ALL_ENTITIES')(relationSchema),
111
- variables: {
112
- where: {
113
- id: {
114
- _in: ids
115
- }
116
- },
117
- sort: [{ property: 'name', direction: 'asc' }]
118
- }
119
- })
120
- // eslint-disable-next-line no-console
121
- .catch(e => console.error(e));
163
+ const { adminListingOptions: { tidyLabel } = {} } = config;
122
164
 
165
+ if (!config.relation) {
166
+ const baseOptions = ids.map(value => [value, getTidyLabel(value, tidyLabel)]);
123
167
  return {
124
- id: property,
125
- label: selectionDetails.label,
126
- options: selected.map(item => [item.id, item.name])
168
+ property,
169
+ label: config.label,
170
+ type: multiSelectPropertySet.has(property) ? 'multi' : 'single',
171
+ options: baseOptions,
172
+ selected: values && values[property]
127
173
  };
128
- } catch {
129
- return null;
130
174
  }
131
- })
132
- );
133
175
 
134
- setSelectOptions(results.filter(Boolean));
135
- })();
136
- },
137
- [apolloClient, filterData, schema, selectFiltersToDisplay]
138
- );
176
+ const schemaQuery = getQuery('GET_ENTITY_SCHEMA');
177
+ const {
178
+ data: {
179
+ getEntitySchemas: [relationSchema]
180
+ }
181
+ } = await client.query({
182
+ query: schemaQuery,
183
+ variables: { identifier: config.relation.entityIdentifier }
184
+ });
185
+
186
+ const {
187
+ data: { result: selected = [] }
188
+ } = await client
189
+ .query({
190
+ query: getDynamicQuery('GET_ALL_ENTITIES')(relationSchema),
191
+ variables: {
192
+ where: { id: { _in: ids } },
193
+ sort: [{ property: 'name', direction: 'asc' }]
194
+ }
195
+ })
196
+ // eslint-disable-next-line no-console
197
+ .catch(error => console.error(error));
198
+
199
+ const baseOptions = selected.map(item => [item.id, getTidyLabel(item.name, tidyLabel)]);
200
+
201
+ return {
202
+ property,
203
+ label: config.label || config.relation.label,
204
+ type: multiSelectPropertySet.has(property) ? 'multi' : 'single',
205
+ options: baseOptions,
206
+ selected: values && values[property]
207
+ };
208
+ } catch {
209
+ return null;
210
+ }
211
+ })
212
+ );
213
+
214
+ const valid = results.filter(Boolean);
215
+
216
+ setSelectOptions(
217
+ valid
218
+ .filter(option => option.type === 'single')
219
+ .map(({ property, label, options, selected }) => ({
220
+ id: property,
221
+ label,
222
+ options,
223
+ selected
224
+ }))
225
+ );
226
+
227
+ setMultiSelectOptions(
228
+ valid
229
+ .filter(option => option.type === 'multi')
230
+ .map(({ property, label, options, selected }) => ({
231
+ id: property,
232
+ label,
233
+ items: options.map(([id, name]) => ({ id, name })),
234
+ selected
235
+ }))
236
+ );
237
+ })();
238
+ }, [client, aggregationsData, findFilterConfig, multiSelectPropertySet, values]);
139
239
 
140
240
  const updateFilters = useCallback(
141
241
  (term, filters) => updateListFilters(term, filters, setListFilters, keywordSearchProperties),
142
242
  [setListFilters, keywordSearchProperties]
143
243
  );
144
244
 
145
- const handleSearch = useCallback(() => updateFilters(searchTerm, selectedFilters), [
146
- searchTerm,
147
- selectedFilters,
148
- updateFilters
149
- ]);
150
- const handleSearchChange = useCallback(e => setSearchTerm(e.target.value), []);
245
+ const debouncedUpdateFilters = useMemo(
246
+ () => debounce((term, filters) => updateFilters(term, filters), 300),
247
+ [updateFilters]
248
+ );
249
+
250
+ useEffect(() => () => debouncedUpdateFilters.cancel(), [debouncedUpdateFilters]);
251
+
252
+ const handleSearch = useCallback(
253
+ () => updateFilters(searchTerm, selectedFilters),
254
+ [searchTerm, selectedFilters, updateFilters]
255
+ );
256
+
257
+ const handleSearchChange = useCallback(({ target: { value } }) => setSearchTerm(value), []);
258
+
151
259
  const handleEnterKey = useCallback(
152
- e => {
153
- if (e.key === ENTER_KEY) handleSearch();
260
+ ({ key }) => {
261
+ if (key === ENTER_KEY) handleSearch();
154
262
  },
155
263
  [handleSearch]
156
264
  );
157
- const handleReset = useCallback(
158
- () => {
159
- setSearchTerm('');
160
- setSelectedFilters(
161
- Object.keys(selectedFilters).reduce(
162
- (acc, key) => ({ ...acc, [key]: DEFAULT_FILTER_OPTION_LABEL }),
163
- {}
164
- )
165
- );
166
- updateFilters('', {});
167
- },
168
- [selectedFilters, updateFilters]
169
- );
265
+
266
+ const handleReset = useCallback(() => {
267
+ setSearchTerm('');
268
+ debouncedUpdateFilters.cancel();
269
+ setSelectedFilters(prev =>
270
+ Object.keys(prev).reduce((acc, key) => {
271
+ acc[key] = multiSelectPropertySet.has(key) ? [] : DEFAULT_FILTER_OPTION_LABEL;
272
+ return acc;
273
+ }, {})
274
+ );
275
+ updateFilters('', {});
276
+ }, [multiSelectPropertySet, updateFilters, debouncedUpdateFilters]);
277
+
170
278
  const handleSelect = useCallback(
171
- (property, event) => {
172
- const { value } = event;
279
+ (property, { value }) => {
173
280
  const next = { ...selectedFilters, [property]: value };
174
281
  setSelectedFilters(next);
175
- updateFilters(searchTerm, next);
282
+ debouncedUpdateFilters(searchTerm, next);
176
283
  },
177
- [searchTerm, selectedFilters, updateFilters]
284
+ [searchTerm, selectedFilters, debouncedUpdateFilters]
285
+ );
286
+
287
+ const handleMultiSelect = useCallback(
288
+ (property, ids) => {
289
+ const next = { ...selectedFilters, [property]: ids };
290
+ setSelectedFilters(next);
291
+ debouncedUpdateFilters(searchTerm, next);
292
+ },
293
+ [searchTerm, selectedFilters, debouncedUpdateFilters]
294
+ );
295
+
296
+ const findRelationByLocalField = useCallback(
297
+ localField => relationsIndex.find(relation => relation.localField === localField),
298
+ [relationsIndex]
299
+ );
300
+
301
+ const resolveAggProp = useCallback((property, config) => {
302
+ if (!config.relation) return property;
303
+ if (property.includes('.')) return property;
304
+ const localField = config.relation.localField || property;
305
+ return `${localField}.name`;
306
+ }, []);
307
+
308
+ const resolveEntityIdentifier = useCallback(
309
+ (property, config) => {
310
+ if (!config.relation) return undefined;
311
+ if (config.relation.entityIdentifier) return config.relation.entityIdentifier;
312
+
313
+ const localField = property.includes('.')
314
+ ? property.split('.')[0]
315
+ : config.relation.localField || property;
316
+
317
+ return findRelationByLocalField(localField).entityIdentifier;
318
+ },
319
+ [findRelationByLocalField]
320
+ );
321
+
322
+ const addEntitySuffixIfNeeded = useCallback((aggProp, entityIdentifier, config) => {
323
+ if (!config.relation || !entityIdentifier) return aggProp;
324
+ return `${aggProp}/${entityIdentifier}`;
325
+ }, []);
326
+
327
+ const makeOnSearchChange = useCallback(
328
+ property =>
329
+ async ({ event: { value }, currentOffset }) => {
330
+ try {
331
+ if (!value || currentOffset) return [];
332
+
333
+ const config = findFilterConfig(property);
334
+ if (!config) return [];
335
+
336
+ let aggProp = resolveAggProp(property, config);
337
+
338
+ if (config.relation && !config.relation.localField && !property.includes('.')) {
339
+ const matchingRelation = findRelationByLocalField(property);
340
+ if (matchingRelation.localField) aggProp = `${matchingRelation.localField}.name`;
341
+ }
342
+
343
+ const entityIdentifier =
344
+ resolveEntityIdentifier(property, config) || config.relation?.entityIdentifier;
345
+
346
+ const filterByEntry = addEntitySuffixIfNeeded(aggProp, entityIdentifier, config);
347
+
348
+ const currentValues = {
349
+ entity: schema.id,
350
+ filterByProperty: [filterByEntry]
351
+ };
352
+
353
+ const aggregations = await fetchFilterByDataFromElastic(
354
+ client,
355
+ currentValues,
356
+ relationsIndex,
357
+ stringTypeProps,
358
+ value
359
+ );
360
+
361
+ const key = aggProp;
362
+ const buckets = aggregations?.[key]?.buckets || [];
363
+
364
+ let items = [];
365
+ if (config.relation) {
366
+ const names = buckets.map(bucket => bucket.key).filter(Boolean);
367
+ if (names.length) {
368
+ const schemaQuery = getQuery('GET_ENTITY_SCHEMA');
369
+ const {
370
+ data: {
371
+ getEntitySchemas: [relationSchema]
372
+ }
373
+ } = await client.query({
374
+ query: schemaQuery,
375
+ variables: { identifier: entityIdentifier }
376
+ });
377
+
378
+ const {
379
+ data: { result: selected = [] }
380
+ } = await client
381
+ .query({
382
+ query: getDynamicQuery('GET_ALL_ENTITIES')(relationSchema),
383
+ variables: {
384
+ where: { name: { _in: names } },
385
+ sort: [{ property: 'name', direction: 'asc' }]
386
+ }
387
+ })
388
+ // eslint-disable-next-line no-console
389
+ .catch(error => console.error(error));
390
+
391
+ items = selected.map(item => ({ id: item.id, name: item.name }));
392
+ }
393
+ } else {
394
+ items = buckets
395
+ .map(bucket => ({ id: bucket.key, name: bucket.key }))
396
+ .filter(option => option.id != null);
397
+ }
398
+
399
+ const existingItems = multiSelectOptions.find(opt => opt.id === property)?.items || [];
400
+ return unionBy(items, existingItems, 'id');
401
+ } catch (error) {
402
+ return [];
403
+ }
404
+ },
405
+ [
406
+ client,
407
+ schema.id,
408
+ relationsIndex,
409
+ stringTypeProps,
410
+ multiSelectOptions,
411
+ findFilterConfig,
412
+ resolveAggProp,
413
+ resolveEntityIdentifier,
414
+ addEntitySuffixIfNeeded,
415
+ findRelationByLocalField
416
+ ]
178
417
  );
179
418
 
180
419
  const placeholderText = `Search ${keywordSearchLabels.join(', ')}`;
181
420
 
182
421
  return (
183
- <div className="search-container">
422
+ <div className="search-container ms-filter">
184
423
  {displaySearchFilter && (
185
424
  <div className="search-container__search-input-container">
186
425
  <input
@@ -198,24 +437,60 @@ const SearchContainer = ({
198
437
  )}
199
438
 
200
439
  {displaySelectFilters &&
201
- selectOptions.map(({ id, options, label }) => (
202
- <>
203
- {options &&
204
- !!options.length && (
205
- <div className="search-container__select-wrapper">
206
- <Select
207
- label={label}
208
- id={id}
209
- name={id}
210
- onChange={event => handleSelect(id, event)}
211
- options={options}
212
- value={selectedFilters[id]}
213
- defaultTextValue={DEFAULT_FILTER_OPTION_LABEL}
214
- />
215
- </div>
216
- )}
217
- </>
218
- ))}
440
+ selectOptions.map(({ id, options, label, selected }) =>
441
+ options && options.length ? (
442
+ <div className="search-container__select-wrapper" key={`select-${id}`}>
443
+ <Select
444
+ label={label}
445
+ id={id}
446
+ name={id}
447
+ onChange={({ value }) => handleSelect(id, { value })}
448
+ options={options}
449
+ value={selectedFilters[id] !== undefined ? selectedFilters[id] : selected}
450
+ defaultTextValue={DEFAULT_FILTER_OPTION_LABEL}
451
+ />
452
+ </div>
453
+ ) : null
454
+ )}
455
+
456
+ {displayMultiSelectFilters &&
457
+ multiSelectOptions.map(({ id, label, items }) => {
458
+ const selectedIds = selectedFilters[id] ? selectedFilters[id] : [];
459
+ const multiSelectData = {
460
+ filterBy: ['name'],
461
+ identification: 'id',
462
+ keyValue: 'name',
463
+ data: items.map(({ id: itemId, name }) => ({
464
+ id: itemId,
465
+ name,
466
+ description: '',
467
+ checked: selectedIds.includes(itemId),
468
+ show: true
469
+ }))
470
+ };
471
+
472
+ return (
473
+ <div className="search-container__select-wrapper" key={`multiselect-${id}`}>
474
+ <MultiSelect
475
+ onChange={({ value, currentOffset }) =>
476
+ makeOnSearchChange(id)({ event: { value }, currentOffset })
477
+ }
478
+ name={id}
479
+ label={label}
480
+ data={multiSelectData}
481
+ limit={10}
482
+ getSelected={({
483
+ event: {
484
+ target: { value }
485
+ }
486
+ }) => handleMultiSelect(id, value)}
487
+ required={false}
488
+ checkedPreviewCount={1}
489
+ formatMoreLabel={n => `${n} more`}
490
+ />
491
+ </div>
492
+ );
493
+ })}
219
494
 
220
495
  <div className="search-container__reset-button">
221
496
  <span role="button" onClick={handleReset}>
@@ -230,6 +505,7 @@ SearchContainer.propTypes = {
230
505
  setListFilters: PropTypes.func.isRequired,
231
506
  keywordFiltersToUse: PropTypes.array,
232
507
  selectFiltersToDisplay: PropTypes.array,
508
+ multiSelectFiltersToDisplay: PropTypes.array,
233
509
  schema: PropTypes.object.isRequired,
234
510
  index: PropTypes.string
235
511
  };
@@ -237,6 +513,7 @@ SearchContainer.propTypes = {
237
513
  SearchContainer.defaultProps = {
238
514
  keywordFiltersToUse: [],
239
515
  selectFiltersToDisplay: [],
516
+ multiSelectFiltersToDisplay: [],
240
517
  index: 'admin'
241
518
  };
242
519
 
@@ -1,9 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { getKeywordSearchFilters, findSelectFilters } from './helpers';
3
+ import { getKeywordSearchFilters, findSelectFilters, findMultiSelectFilters } from './helpers';
4
4
  import SearchContainer from './SearchContainer';
5
5
 
6
- const SearchFilter = ({ schema, setListFilters }) => {
6
+ const SearchFilter = ({ schema, setListFilters, values }) => {
7
7
  const { displayProperties = {} } = schema || {};
8
8
  const {
9
9
  adminListings: { dataSource: { source, index } = {}, disableListingFilter = false } = {}
@@ -11,6 +11,7 @@ const SearchFilter = ({ schema, setListFilters }) => {
11
11
 
12
12
  const keywordFiltersToUse = useMemo(() => getKeywordSearchFilters(schema), [schema]);
13
13
  const selectFiltersToDisplay = useMemo(() => findSelectFilters(schema), [schema]);
14
+ const multiSelectFiltersToDisplay = useMemo(() => findMultiSelectFilters(schema), [schema]);
14
15
 
15
16
  if (disableListingFilter) return null;
16
17
 
@@ -25,7 +26,9 @@ const SearchFilter = ({ schema, setListFilters }) => {
25
26
  setListFilters={setListFilters}
26
27
  keywordFiltersToUse={keywordFiltersToUse}
27
28
  selectFiltersToDisplay={selectFiltersToDisplay}
29
+ multiSelectFiltersToDisplay={multiSelectFiltersToDisplay}
28
30
  schema={schema}
31
+ values={values}
29
32
  />
30
33
  );
31
34
  };