@blaze-cms/plugin-data-ui 0.146.0-core-styles.60 → 0.146.0-core-styles.62

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 (64) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +36 -0
  3. package/lib/components/EntityManager/Entity/Entity.js +24 -24
  4. package/lib/components/EntityManager/Entity/Entity.js.map +1 -1
  5. package/lib/components/EntityManager/Entity/EntityHeader/EntityHeader.js +1 -1
  6. package/lib/components/EntityManager/Entity/EntityHeader/EntityHeader.js.map +1 -1
  7. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/HeaderPreviewButton.js +10 -10
  8. package/lib/components/EntityManager/Entity/EntityHeader/HeaderPreviewButton/HeaderPreviewButton.js.map +1 -1
  9. package/lib/components/EntityManager/Entity/SideBarRelations/index.js +23 -22
  10. package/lib/components/EntityManager/Entity/SideBarRelations/index.js.map +1 -1
  11. package/lib/components/EntityManager/Entity/actions-handlers/create/submit.js +12 -12
  12. package/lib/components/EntityManager/Entity/actions-handlers/create/submit.js.map +1 -1
  13. package/lib/components/EntityManager/Entity/actions-handlers/delete/delete.js +12 -12
  14. package/lib/components/EntityManager/Entity/actions-handlers/delete/delete.js.map +1 -1
  15. package/lib/components/EntityManager/Entity/actions-handlers/shared/publish.js +15 -15
  16. package/lib/components/EntityManager/Entity/actions-handlers/shared/publish.js.map +1 -1
  17. package/lib/components/EntityManager/Entity/actions-handlers/update/submit.js +11 -11
  18. package/lib/components/EntityManager/Entity/actions-handlers/update/submit.js.map +1 -1
  19. package/lib/components/EntityManager/EntityManager.js +20 -20
  20. package/lib/components/EntityManager/EntityManager.js.map +1 -1
  21. package/lib/components/EntityManager/utils/entity.js +4 -4
  22. package/lib/components/EntityManager/utils/entity.js.map +1 -1
  23. package/lib/components/EntityManager/utils/entityAvailableActions.js +14 -14
  24. package/lib/components/EntityManager/utils/entityAvailableActions.js.map +1 -1
  25. package/lib/components/ListingTable/ListingTable.js +35 -27
  26. package/lib/components/ListingTable/ListingTable.js.map +1 -1
  27. package/lib/components/ListingTable/SearchFilter/SearchContainer.js +312 -0
  28. package/lib/components/ListingTable/SearchFilter/SearchContainer.js.map +1 -0
  29. package/lib/components/ListingTable/SearchFilter/SearchFilter.js +58 -0
  30. package/lib/components/ListingTable/SearchFilter/SearchFilter.js.map +1 -0
  31. package/lib/components/ListingTable/SearchFilter/helpers.js +128 -0
  32. package/lib/components/ListingTable/SearchFilter/helpers.js.map +1 -0
  33. package/lib/components/ListingTable/SearchFilter/querys.js +13 -0
  34. package/lib/components/ListingTable/SearchFilter/querys.js.map +1 -0
  35. package/lib/components/ListingTable/service/index.js +6 -6
  36. package/lib/components/ListingTable/service/index.js.map +1 -1
  37. package/lib/constants.js +3 -1
  38. package/lib/constants.js.map +1 -1
  39. package/lib/index.js +5 -5
  40. package/lib/index.js.map +1 -1
  41. package/lib/utils/add-content-menu-items.js +6 -6
  42. package/lib/utils/add-content-menu-items.js.map +1 -1
  43. package/lib-es/components/EntityManager/Entity/EntityHeader/EntityHeader.js +1 -1
  44. package/lib-es/components/EntityManager/Entity/EntityHeader/EntityHeader.js.map +1 -1
  45. package/lib-es/components/ListingTable/ListingTable.js +10 -2
  46. package/lib-es/components/ListingTable/ListingTable.js.map +1 -1
  47. package/lib-es/components/ListingTable/SearchFilter/SearchContainer.js +207 -0
  48. package/lib-es/components/ListingTable/SearchFilter/SearchContainer.js.map +1 -0
  49. package/lib-es/components/ListingTable/SearchFilter/SearchFilter.js +40 -0
  50. package/lib-es/components/ListingTable/SearchFilter/SearchFilter.js.map +1 -0
  51. package/lib-es/components/ListingTable/SearchFilter/helpers.js +96 -0
  52. package/lib-es/components/ListingTable/SearchFilter/helpers.js.map +1 -0
  53. package/lib-es/components/ListingTable/SearchFilter/querys.js +8 -0
  54. package/lib-es/components/ListingTable/SearchFilter/querys.js.map +1 -0
  55. package/lib-es/constants.js +3 -1
  56. package/lib-es/constants.js.map +1 -1
  57. package/package.json +5 -5
  58. package/src/components/EntityManager/Entity/EntityHeader/EntityHeader.js +1 -1
  59. package/src/components/ListingTable/ListingTable.js +10 -1
  60. package/src/components/ListingTable/SearchFilter/SearchContainer.js +243 -0
  61. package/src/components/ListingTable/SearchFilter/SearchFilter.js +38 -0
  62. package/src/components/ListingTable/SearchFilter/helpers.js +99 -0
  63. package/src/components/ListingTable/SearchFilter/querys.js +9 -0
  64. package/src/constants.js +7 -1
@@ -0,0 +1,243 @@
1
+ import React, { useState, useMemo, useEffect, useCallback } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Select from '@blaze-react/select';
4
+ import { useQuery, useApolloClient } from '@apollo/client';
5
+ import { getDynamicQuery, getQuery } from '@blaze-cms/admin-ui-utils';
6
+ import { BsArrowCounterclockwise, BsSearch } from 'react-icons/bs';
7
+ import { updateListFilters, buildQueryFields } from './helpers';
8
+ import { NOOP_QUERY } from './querys';
9
+ import { ENTER_KEY, DEFAULT_FILTER_OPTION_LABEL } from '../../../constants';
10
+
11
+ const SearchContainer = ({
12
+ setListFilters,
13
+ keywordFiltersToUse = [],
14
+ selectFiltersToDisplay = [],
15
+ schema,
16
+ index
17
+ }) => {
18
+ const apolloClient = useApolloClient();
19
+ const [searchTerm, setSearchTerm] = useState('');
20
+ const [selectedFilters, setSelectedFilters] = useState({});
21
+ const [selectOptions, setSelectOptions] = useState([]);
22
+
23
+ const displaySearchFilter = keywordFiltersToUse.length > 0;
24
+ const displaySelectFilters = selectFiltersToDisplay.length > 0;
25
+
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
+ });
35
+
36
+ return { keywordSearchProperties: properties, keywordSearchLabels: labels };
37
+ },
38
+ [keywordFiltersToUse]
39
+ );
40
+
41
+ const { gqlFields, rawQuery } = useMemo(
42
+ () => buildQueryFields(selectFiltersToDisplay, schema.id),
43
+ [selectFiltersToDisplay, schema.id]
44
+ );
45
+ const action = displaySelectFilters
46
+ ? getDynamicQuery('ADMIN_SEARCH')([schema], gqlFields, false, index)
47
+ : NOOP_QUERY;
48
+
49
+ const { data } = useQuery(action, {
50
+ skip: !displaySelectFilters,
51
+ variables: { where: rawQuery, limit: 0 },
52
+ fetchPolicy: 'cache-and-network'
53
+ });
54
+
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
+ );
63
+ });
64
+
65
+ return proccessedData;
66
+ },
67
+ [data]
68
+ );
69
+
70
+ useEffect(
71
+ () => {
72
+ if (!filterData.length) {
73
+ setSelectOptions([]);
74
+ return;
75
+ }
76
+
77
+ (async () => {
78
+ const results = await Promise.all(
79
+ filterData.map(async ([property, ids]) => {
80
+ try {
81
+ if (!ids || !ids.length) return {};
82
+
83
+ const [, selectionDetails] =
84
+ selectFiltersToDisplay.find(([selectProperty]) => selectProperty === property) ||
85
+ [];
86
+
87
+ if (!selectionDetails) return null;
88
+
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
+ });
105
+
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));
122
+
123
+ return {
124
+ id: property,
125
+ label: selectionDetails.label,
126
+ options: selected.map(item => [item.id, item.name])
127
+ };
128
+ } catch {
129
+ return null;
130
+ }
131
+ })
132
+ );
133
+
134
+ setSelectOptions(results.filter(Boolean));
135
+ })();
136
+ },
137
+ [apolloClient, filterData, schema, selectFiltersToDisplay]
138
+ );
139
+
140
+ const updateFilters = useCallback(
141
+ (term, filters) => updateListFilters(term, filters, setListFilters, keywordSearchProperties),
142
+ [setListFilters, keywordSearchProperties]
143
+ );
144
+
145
+ const handleSearch = useCallback(() => updateFilters(searchTerm, selectedFilters), [
146
+ searchTerm,
147
+ selectedFilters,
148
+ updateFilters
149
+ ]);
150
+ const handleSearchChange = useCallback(e => setSearchTerm(e.target.value), []);
151
+ const handleEnterKey = useCallback(
152
+ e => {
153
+ if (e.key === ENTER_KEY) handleSearch();
154
+ },
155
+ [handleSearch]
156
+ );
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
+ );
170
+ const handleSelect = useCallback(
171
+ (property, event) => {
172
+ const { value } = event;
173
+ const next = { ...selectedFilters, [property]: value };
174
+ setSelectedFilters(next);
175
+ updateFilters(searchTerm, next);
176
+ },
177
+ [searchTerm, selectedFilters, updateFilters]
178
+ );
179
+
180
+ const placeholderText = `Search ${keywordSearchLabels.join(', ')}`;
181
+
182
+ return (
183
+ <div className="search-container">
184
+ {displaySearchFilter && (
185
+ <div className="search-container__search-input-container">
186
+ <input
187
+ type="search"
188
+ className="search-container__search-input-container__search-input"
189
+ placeholder={placeholderText}
190
+ value={searchTerm}
191
+ onChange={handleSearchChange}
192
+ onKeyDown={handleEnterKey}
193
+ />
194
+ <i role="button" onClick={handleSearch}>
195
+ <BsSearch />
196
+ </i>
197
+ </div>
198
+ )}
199
+
200
+ {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
+ ))}
219
+
220
+ <div className="search-container__reset-button">
221
+ <span role="button" onClick={handleReset}>
222
+ <BsArrowCounterclockwise />
223
+ </span>
224
+ </div>
225
+ </div>
226
+ );
227
+ };
228
+
229
+ SearchContainer.propTypes = {
230
+ setListFilters: PropTypes.func.isRequired,
231
+ keywordFiltersToUse: PropTypes.array,
232
+ selectFiltersToDisplay: PropTypes.array,
233
+ schema: PropTypes.object.isRequired,
234
+ index: PropTypes.string
235
+ };
236
+
237
+ SearchContainer.defaultProps = {
238
+ keywordFiltersToUse: [],
239
+ selectFiltersToDisplay: [],
240
+ index: 'admin'
241
+ };
242
+
243
+ export default SearchContainer;
@@ -0,0 +1,38 @@
1
+ import React, { useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { getKeywordSearchFilters, findSelectFilters } from './helpers';
4
+ import SearchContainer from './SearchContainer';
5
+
6
+ const SearchFilter = ({ schema, setListFilters }) => {
7
+ const { displayProperties = {} } = schema || {};
8
+ const {
9
+ adminListings: { dataSource: { source, index } = {}, disableListingFilter = false } = {}
10
+ } = displayProperties;
11
+
12
+ const keywordFiltersToUse = useMemo(() => getKeywordSearchFilters(schema), [schema]);
13
+ const selectFiltersToDisplay = useMemo(() => findSelectFilters(schema), [schema]);
14
+
15
+ if (disableListingFilter) return null;
16
+
17
+ const canSearch =
18
+ source === 'search' && (keywordFiltersToUse.length > 0 || selectFiltersToDisplay.length > 0);
19
+ if (!canSearch) return null;
20
+
21
+ return (
22
+ <SearchContainer
23
+ index={index}
24
+ key={schema.id}
25
+ setListFilters={setListFilters}
26
+ keywordFiltersToUse={keywordFiltersToUse}
27
+ selectFiltersToDisplay={selectFiltersToDisplay}
28
+ schema={schema}
29
+ />
30
+ );
31
+ };
32
+
33
+ SearchFilter.propTypes = {
34
+ setListFilters: PropTypes.func.isRequired,
35
+ schema: PropTypes.object.isRequired
36
+ };
37
+
38
+ export default SearchFilter;
@@ -0,0 +1,99 @@
1
+ import { DEFAULT_FILTER_OPTION_LABEL } from '../../../constants';
2
+
3
+ const updateListFilters = (searchTerm, selectedFilters, setListFilters, fields = []) => {
4
+ const filters = [];
5
+
6
+ if (searchTerm) {
7
+ filters.push({
8
+ simple_query_string: {
9
+ query: `${searchTerm}`,
10
+ default_operator: 'AND',
11
+ fields: fields.map(field => field)
12
+ }
13
+ });
14
+ }
15
+
16
+ Object.entries(selectedFilters).forEach(([filterKey, filterValue]) => {
17
+ if (!filterValue || filterValue === DEFAULT_FILTER_OPTION_LABEL) return;
18
+ filters.push({
19
+ match: {
20
+ [`${filterKey}.keyword`]: {
21
+ query: filterValue
22
+ }
23
+ }
24
+ });
25
+ });
26
+
27
+ setListFilters(filters);
28
+ };
29
+
30
+ const getKeywordSearchFilters = schema => {
31
+ if (!schema || !schema.properties) return [];
32
+
33
+ const { properties = {}, dynamicProperties = {} } = schema;
34
+
35
+ const propertyKeyword = Object.entries(properties).filter(isFilterKeywordItem);
36
+ const dynamicPropertyKeywords = Object.entries(dynamicProperties).filter(isFilterKeywordItem);
37
+
38
+ return [...propertyKeyword, ...dynamicPropertyKeywords];
39
+ };
40
+
41
+ const isFilterKeywordItem = ([, item]) =>
42
+ item.adminListingOptions && item.adminListingOptions.includeInKeywordSeach === true;
43
+
44
+ const findSelectFilters = schema => {
45
+ if (!schema || !schema.properties) return [];
46
+
47
+ const { properties = {}, dynamicProperties = {} } = schema;
48
+
49
+ const propertySelects = Object.entries(properties).filter(isFilterSelectItem);
50
+ const dynamicPropertySelects = Object.entries(dynamicProperties).filter(isFilterSelectItem);
51
+
52
+ return [...propertySelects, ...dynamicPropertySelects];
53
+ };
54
+
55
+ const isFilterSelectItem = ([, item]) =>
56
+ item.adminListingOptions && item.adminListingOptions.filterType === 'select';
57
+
58
+ const buildQueryFields = (selectFilters, entityId) => {
59
+ if (!Array.isArray(selectFilters) || selectFilters.length === 0) {
60
+ return { gqlFields: '', rawQuery: '{}' };
61
+ }
62
+
63
+ const aggs = {};
64
+ selectFilters.forEach(([property, details]) => {
65
+ aggs[property] = {
66
+ terms: {
67
+ field: `${property}.keyword`, // todo: handle different field types
68
+ size: 1000, // todo: customise size
69
+ order: {
70
+ _key: 'asc'
71
+ }
72
+ }
73
+ };
74
+ });
75
+
76
+ const docType = entityId;
77
+ const rawQueryObject = {
78
+ size: 0,
79
+ query: {
80
+ bool: {
81
+ filter: {
82
+ bool: {
83
+ must: [{ match: { docType } }]
84
+ }
85
+ }
86
+ }
87
+ },
88
+ aggs
89
+ };
90
+ const rawQuery = JSON.stringify(rawQueryObject);
91
+
92
+ const gqlFields = `
93
+ rawResults
94
+ `;
95
+
96
+ return { gqlFields, rawQuery };
97
+ };
98
+
99
+ export { buildQueryFields, updateListFilters, getKeywordSearchFilters, findSelectFilters };
@@ -0,0 +1,9 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ const NOOP_QUERY = gql`
4
+ query NoOp {
5
+ __typename
6
+ }
7
+ `;
8
+
9
+ export { NOOP_QUERY };
package/src/constants.js CHANGED
@@ -26,6 +26,8 @@ const PAGE_BUILDER_TAB_INDEX = 1;
26
26
  const EDITOR_VIEW_TAB = 'Editor view';
27
27
  const EDITOR_VIEW_TAB_INDEX = 0;
28
28
 
29
+ const ENTER_KEY = 'Enter';
30
+
29
31
  const ICON_SIZE = {
30
32
  WIDTH: '25px',
31
33
  HEIGHT: '25px'
@@ -36,6 +38,8 @@ const ICON_COLOR = {
36
38
  FILL: '#63779C'
37
39
  };
38
40
 
41
+ const DEFAULT_FILTER_OPTION_LABEL = 'Any';
42
+
39
43
  export {
40
44
  DATA_LISTING_PREFIX,
41
45
  ENTITY_PUBLISHED,
@@ -63,5 +67,7 @@ export {
63
67
  SAVE_BEFORE_PUBLISH_MESSAGE,
64
68
  ICON_SIZE,
65
69
  ICON_COLOR,
66
- PAGE_NOT_FOUND
70
+ PAGE_NOT_FOUND,
71
+ ENTER_KEY,
72
+ DEFAULT_FILTER_OPTION_LABEL
67
73
  };