mirror of
https://github.com/strapi/strapi.git
synced 2025-12-12 15:32:42 +00:00
Migrate filters to qs lib
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
586781e12e
commit
944e34043c
@ -1,4 +1,4 @@
|
||||
import React, { memo, useMemo, useReducer } from 'react';
|
||||
import React, { memo, useCallback, useMemo, useReducer, useRef } from 'react';
|
||||
import { withRouter } from 'react-router';
|
||||
import PropTypes from 'prop-types';
|
||||
import { capitalize, get } from 'lodash';
|
||||
@ -9,9 +9,11 @@ import {
|
||||
getFilterType,
|
||||
useUser,
|
||||
findMatchingPermissions,
|
||||
useGlobalContext,
|
||||
} from 'strapi-helper-plugin';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
import { formatFiltersToQuery, getTrad } from '../../utils';
|
||||
import useListView from '../../hooks/useListView';
|
||||
import Container from '../Container';
|
||||
import FilterPickerOption from '../FilterPickerOption';
|
||||
@ -21,8 +23,9 @@ import reducer, { initialState } from './reducer';
|
||||
|
||||
const NOT_ALLOWED_FILTERS = ['json', 'component', 'relation', 'media', 'richtext', 'dynamiczone'];
|
||||
|
||||
function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState }) {
|
||||
const { schema, filters, slug } = useListView();
|
||||
function FilterPicker({ contentType, isOpen, name, toggleFilterPickerState, setQuery, slug }) {
|
||||
const { emitEvent } = useGlobalContext();
|
||||
const emitEventRef = useRef(emitEvent);
|
||||
const userPermissions = useUser();
|
||||
const readActionAllowedFields = useMemo(() => {
|
||||
const matchingPermissions = findMatchingPermissions(userPermissions, [
|
||||
@ -34,15 +37,32 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
|
||||
return get(matchingPermissions, ['0', 'fields'], []);
|
||||
}, [userPermissions, slug]);
|
||||
let timestamps = get(schema, ['options', 'timestamps']);
|
||||
|
||||
let timestamps = get(contentType, ['options', 'timestamps']);
|
||||
|
||||
if (!Array.isArray(timestamps)) {
|
||||
timestamps = [];
|
||||
}
|
||||
|
||||
const allowedAttributes = Object.keys(get(schema, ['attributes']), {})
|
||||
const actions = [
|
||||
{
|
||||
label: getTrad('components.FiltersPickWrapper.PluginHeader.actions.clearAll'),
|
||||
kind: 'secondary',
|
||||
onClick: () => {
|
||||
toggleFilterPickerState();
|
||||
setQuery({ _where: [] }, 'remove');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: getTrad('components.FiltersPickWrapper.PluginHeader.actions.apply'),
|
||||
kind: 'primary',
|
||||
type: 'submit',
|
||||
},
|
||||
];
|
||||
|
||||
const allowedAttributes = Object.keys(get(contentType, ['attributes']), {})
|
||||
.filter(attr => {
|
||||
const current = get(schema, ['attributes', attr], {});
|
||||
const current = get(contentType, ['attributes', attr], {});
|
||||
|
||||
if (!readActionAllowedFields.includes(attr) && attr !== 'id' && !timestamps.includes(attr)) {
|
||||
return false;
|
||||
@ -52,7 +72,7 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
})
|
||||
.sort()
|
||||
.map(attr => {
|
||||
const current = get(schema, ['attributes', attr], {});
|
||||
const current = get(contentType, ['attributes', attr], {});
|
||||
|
||||
return { name: attr, type: current.type, options: current.enum || null };
|
||||
});
|
||||
@ -81,8 +101,7 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
</FormattedMessage>
|
||||
);
|
||||
|
||||
// Generate the first filter for adding a new one or at initial state
|
||||
const getInitialFilter = () => {
|
||||
const initialFilter = useMemo(() => {
|
||||
const type = get(allowedAttributes, [0, 'type'], '');
|
||||
const [filter] = getFilterType(type);
|
||||
|
||||
@ -103,36 +122,44 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
};
|
||||
|
||||
return initFilter;
|
||||
};
|
||||
}, [allowedAttributes]);
|
||||
|
||||
// Set the filters when the collapse is opening
|
||||
const handleEntering = () => {
|
||||
const currentFilters = filters;
|
||||
const initialFilters = currentFilters.length > 0 ? currentFilters : [getInitialFilter()];
|
||||
/* eslint-disable indent */
|
||||
const initialFilters = currentFilters.length ? currentFilters : [initialFilter];
|
||||
|
||||
dispatch({
|
||||
type: 'SET_FILTERS',
|
||||
initialFilters,
|
||||
attributes: get(schema, 'attributes', {}),
|
||||
attributes: get(contentType, 'attributes', {}),
|
||||
});
|
||||
};
|
||||
|
||||
const addFilter = () => {
|
||||
dispatch({
|
||||
type: 'ADD_FILTER',
|
||||
filter: getInitialFilter(),
|
||||
filter: initialFilter,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
e => {
|
||||
e.preventDefault();
|
||||
const nextFilters = formatFiltersToQuery(modifiedData);
|
||||
|
||||
emitEventRef.current('didFilterEntries');
|
||||
setQuery(nextFilters);
|
||||
toggleFilterPickerState();
|
||||
},
|
||||
[modifiedData, setQuery, toggleFilterPickerState]
|
||||
);
|
||||
|
||||
return (
|
||||
<Collapse isOpen={isOpen} onEntering={handleEntering}>
|
||||
<Container style={{ backgroundColor: 'white', paddingBottom: 0 }}>
|
||||
<form
|
||||
onSubmit={e => {
|
||||
e.preventDefault();
|
||||
|
||||
onSubmit(modifiedData);
|
||||
}}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<PluginHeader
|
||||
actions={actions}
|
||||
title={renderTitle}
|
||||
@ -161,7 +188,7 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
index,
|
||||
});
|
||||
}}
|
||||
type={get(schema, ['attributes', filter.name, 'type'], '')}
|
||||
type={get(contentType, ['attributes', filter.name, 'type'], '')}
|
||||
showAddButton={key === modifiedData.length - 1}
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={key}
|
||||
@ -181,19 +208,15 @@ function FilterPicker({ actions, isOpen, name, onSubmit, toggleFilterPickerState
|
||||
}
|
||||
|
||||
FilterPicker.defaultProps = {
|
||||
actions: [],
|
||||
isOpen: false,
|
||||
name: '',
|
||||
};
|
||||
|
||||
FilterPicker.propTypes = {
|
||||
actions: PropTypes.array,
|
||||
isOpen: PropTypes.bool,
|
||||
location: PropTypes.shape({
|
||||
search: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
contentType: PropTypes.object.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
name: PropTypes.string,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
setQuery: PropTypes.func.isRequired,
|
||||
slug: PropTypes.string.isRequired,
|
||||
toggleFilterPickerState: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import React from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, toString } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { FilterButton } from 'strapi-helper-plugin';
|
||||
import dateFormats from '../../utils/dateFormats';
|
||||
import { dateFormats, formatFiltersToQuery } from '../../utils';
|
||||
|
||||
function Filter({
|
||||
changeParams,
|
||||
filter,
|
||||
contentType,
|
||||
filterName,
|
||||
filters,
|
||||
index,
|
||||
name,
|
||||
schema,
|
||||
value,
|
||||
toggleFilterPickerState,
|
||||
isFilterPickerOpen,
|
||||
setQuery,
|
||||
}) {
|
||||
const type = get(schema, ['attributes', name, 'type'], 'string');
|
||||
const type = get(contentType, ['attributes', name, 'type'], 'string');
|
||||
let displayedValue = toString(value);
|
||||
|
||||
if (type.includes('date') || type.includes('timestamp')) {
|
||||
@ -36,26 +36,24 @@ function Filter({
|
||||
.format(format);
|
||||
}
|
||||
|
||||
console.log({ name });
|
||||
const label = {
|
||||
name,
|
||||
filter,
|
||||
filter: filterName,
|
||||
value: displayedValue,
|
||||
};
|
||||
|
||||
return (
|
||||
<FilterButton
|
||||
onClick={() => {
|
||||
const updatedFilters = filters.slice().filter((_, i) => i !== index);
|
||||
const handleClick = useCallback(() => {
|
||||
const updatedFilters = filters.slice().filter((_, i) => i !== index);
|
||||
|
||||
if (isFilterPickerOpen) {
|
||||
toggleFilterPickerState();
|
||||
}
|
||||
changeParams({ target: { name: 'filters', value: updatedFilters } });
|
||||
}}
|
||||
label={label}
|
||||
type={type}
|
||||
/>
|
||||
);
|
||||
if (isFilterPickerOpen) {
|
||||
toggleFilterPickerState();
|
||||
}
|
||||
|
||||
setQuery(formatFiltersToQuery(updatedFilters));
|
||||
}, [filters, index, isFilterPickerOpen, setQuery, toggleFilterPickerState]);
|
||||
|
||||
return <FilterButton onClick={handleClick} label={label} type={type} />;
|
||||
}
|
||||
|
||||
Filter.defaultProps = {
|
||||
@ -64,15 +62,15 @@ Filter.defaultProps = {
|
||||
};
|
||||
|
||||
Filter.propTypes = {
|
||||
changeParams: PropTypes.func.isRequired,
|
||||
filter: PropTypes.string.isRequired,
|
||||
contentType: PropTypes.shape({ attributes: PropTypes.object.isRequired }).isRequired,
|
||||
filterName: PropTypes.string.isRequired,
|
||||
filters: PropTypes.array.isRequired,
|
||||
index: PropTypes.number.isRequired,
|
||||
isFilterPickerOpen: PropTypes.bool.isRequired,
|
||||
name: PropTypes.string,
|
||||
schema: PropTypes.object.isRequired,
|
||||
setQuery: PropTypes.func.isRequired,
|
||||
toggleFilterPickerState: PropTypes.func.isRequired,
|
||||
value: PropTypes.any,
|
||||
};
|
||||
|
||||
export default Filter;
|
||||
export default memo(Filter);
|
||||
|
||||
@ -18,13 +18,18 @@ import {
|
||||
import pluginId from '../../pluginId';
|
||||
import pluginPermissions from '../../permissions';
|
||||
import { useQueryParams } from '../../hooks';
|
||||
import { generatePermissionsObject, getRequestUrl, getTrad } from '../../utils';
|
||||
import {
|
||||
formatFiltersFromQuery,
|
||||
generatePermissionsObject,
|
||||
getRequestUrl,
|
||||
getTrad,
|
||||
} from '../../utils';
|
||||
|
||||
import DisplayedFieldsDropdown from '../../components/DisplayedFieldsDropdown';
|
||||
import Container from '../../components/Container';
|
||||
import CustomTable from '../../components/CustomTable';
|
||||
|
||||
// import FilterPicker from '../../components/FilterPicker';
|
||||
import FilterPicker from '../../components/FilterPicker';
|
||||
import Search from '../../components/Search';
|
||||
import ListViewProvider from '../ListViewProvider';
|
||||
import { AddFilterCta, FilterIcon, Wrapper } from './components';
|
||||
@ -53,7 +58,7 @@ import { getAllAllowedHeaders, getFirstSortableHeader } from './utils';
|
||||
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
|
||||
const FilterPicker = () => <div>FILTER</div>;
|
||||
// const FilterPicker = () => <div>FILTER</div>;
|
||||
|
||||
function ListView({
|
||||
count,
|
||||
@ -105,12 +110,12 @@ function ListView({
|
||||
} = layout;
|
||||
|
||||
const { emitEvent } = useGlobalContext();
|
||||
const emitEventRef = useRef(emitEvent);
|
||||
const viewPermissions = useMemo(() => generatePermissionsObject(slug), [slug]);
|
||||
const {
|
||||
isLoading: isLoadingForPermissions,
|
||||
allowedActions: { canCreate, canRead, canUpdate, canDelete },
|
||||
} = useUserPermissions(viewPermissions);
|
||||
// const query = useQuery();
|
||||
const defaultSort = `${defaultSortBy}:${defaultSortOrder}`;
|
||||
const [{ query, rawQuery }, setQuery] = useQueryParams({
|
||||
page: 1,
|
||||
@ -132,7 +137,9 @@ function ListView({
|
||||
|
||||
const allAllowedHeaders = getAllAllowedHeaders(attributes);
|
||||
|
||||
const filters = query._where || [];
|
||||
const filters = useMemo(() => {
|
||||
return formatFiltersFromQuery(query);
|
||||
}, [query]);
|
||||
|
||||
const _limit = parseInt(query.pageSize, 10);
|
||||
const _page = parseInt(query.page, 10);
|
||||
@ -142,18 +149,6 @@ function ListView({
|
||||
const label = contentType.info.label;
|
||||
|
||||
const searchToSendForRequest = rawQuery;
|
||||
// const searchToSendForRequest = useMemo(() => {
|
||||
// const currentSearch = new URLSearchParams(search);
|
||||
|
||||
// currentSearch.set('_limit', _limit);
|
||||
// currentSearch.set('_sort', _sort);
|
||||
// currentSearch.set('_start', _start);
|
||||
// currentSearch.delete('_page');
|
||||
|
||||
// return currentSearch.toString();
|
||||
// }, [_limit, _sort, _start, search]);
|
||||
|
||||
// const getDataActionRef = useRef(getData);
|
||||
const getDataSucceededRef = useRef(getDataSucceeded);
|
||||
|
||||
const shouldSendRequest = useMemo(() => {
|
||||
@ -327,24 +322,6 @@ function ListView({
|
||||
[displayedHeaders, onChangeListHeaders]
|
||||
);
|
||||
|
||||
// const handleChangeFilters = ({ target: { value } }) => {
|
||||
// const newSearch = new URLSearchParams();
|
||||
|
||||
// // Set the default params
|
||||
// newSearch.set('_limit', _limit);
|
||||
// newSearch.set('_sort', _sort);
|
||||
// newSearch.set('_page', 1);
|
||||
|
||||
// value.forEach(({ filter, name, value: filterValue }) => {
|
||||
// const filterType = filter === '=' ? '' : filter;
|
||||
// const filterName = `${name}${filterType}`;
|
||||
|
||||
// newSearch.append(filterName, filterValue);
|
||||
// });
|
||||
|
||||
// push({ search: newSearch.toString() });
|
||||
// };
|
||||
|
||||
const handleChangeSearch = async ({ target: { name, value } }) => {
|
||||
const currentSearch = new URLSearchParams(searchToSendForRequest);
|
||||
|
||||
@ -373,36 +350,15 @@ function ListView({
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (filters = []) => {
|
||||
emitEvent('didFilterEntries');
|
||||
toggleFilterPickerState();
|
||||
// handleChangeFilters({ target: { name: 'filters', value: filters } });
|
||||
};
|
||||
const toggleFilterPickerState = useCallback(() => {
|
||||
setFilterPickerState(prevState => {
|
||||
if (!prevState) {
|
||||
emitEventRef.current('willFilterEntries');
|
||||
}
|
||||
|
||||
const toggleFilterPickerState = () => {
|
||||
if (!isFilterPickerOpen) {
|
||||
emitEvent('willFilterEntries');
|
||||
}
|
||||
|
||||
setFilterPickerState(prevState => !prevState);
|
||||
};
|
||||
|
||||
const filterPickerActions = [
|
||||
{
|
||||
label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.clearAll`,
|
||||
kind: 'secondary',
|
||||
onClick: () => {
|
||||
toggleFilterPickerState();
|
||||
// Delete all filters
|
||||
// handleChangeFilters({ target: { name: 'filters', value: [] } });
|
||||
},
|
||||
},
|
||||
{
|
||||
label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.apply`,
|
||||
kind: 'primary',
|
||||
type: 'submit',
|
||||
},
|
||||
];
|
||||
return !prevState;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const headerAction = useMemo(
|
||||
() => {
|
||||
@ -494,11 +450,13 @@ function ListView({
|
||||
firstSortableHeader={firstSortableHeader}
|
||||
>
|
||||
<FilterPicker
|
||||
actions={filterPickerActions}
|
||||
contentType={contentType}
|
||||
isOpen={isFilterPickerOpen}
|
||||
name={label}
|
||||
toggleFilterPickerState={toggleFilterPickerState}
|
||||
onSubmit={handleSubmit}
|
||||
setQuery={setQuery}
|
||||
filters={filters}
|
||||
slug={slug}
|
||||
/>
|
||||
<Container className="container-fluid">
|
||||
{!isFilterPickerOpen && <Header {...headerProps} isLoading={isLoading && canRead} />}
|
||||
@ -516,16 +474,17 @@ function ListView({
|
||||
<FilterIcon />
|
||||
<FormattedMessage id="app.utils.filters" />
|
||||
</AddFilterCta>
|
||||
{filters.map((filter, key) => (
|
||||
{filters.map(({ filter: filterName, name }, key) => (
|
||||
<Filter
|
||||
{...filter}
|
||||
// changeParams={handleChangeFilters}
|
||||
contentType={contentType}
|
||||
filterName={filterName}
|
||||
filters={filters}
|
||||
index={key}
|
||||
schema={{}}
|
||||
key={key}
|
||||
name={name}
|
||||
toggleFilterPickerState={toggleFilterPickerState}
|
||||
isFilterPickerOpen={isFilterPickerOpen}
|
||||
setQuery={setQuery}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
const findAppliedFilter = str => {
|
||||
let filter = '=';
|
||||
let name = str;
|
||||
|
||||
const filters = [
|
||||
'_ne',
|
||||
'_lt',
|
||||
'_lte',
|
||||
'_gt',
|
||||
'_gte',
|
||||
'_contains',
|
||||
'_containss',
|
||||
'_ncontains',
|
||||
'_in',
|
||||
'_nin',
|
||||
];
|
||||
|
||||
filters.forEach(filterName => {
|
||||
const split = str.split(filterName);
|
||||
|
||||
if (split[1] === '') {
|
||||
filter = filterName;
|
||||
name = split[0];
|
||||
}
|
||||
});
|
||||
|
||||
return { filter, name };
|
||||
};
|
||||
|
||||
const formatFiltersFromQuery = ({ _where }) => {
|
||||
if (!_where) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _where.map(obj => {
|
||||
const [key] = Object.keys(obj);
|
||||
const { filter, name } = findAppliedFilter(key);
|
||||
|
||||
const value = obj[key];
|
||||
|
||||
return { name, filter, value };
|
||||
});
|
||||
};
|
||||
|
||||
export default formatFiltersFromQuery;
|
||||
export { findAppliedFilter };
|
||||
@ -0,0 +1,13 @@
|
||||
const formatFiltersToQuery = array => {
|
||||
const nextFilters = array.map(({ name, filter, value }) => {
|
||||
if (filter === '=') {
|
||||
return { [name]: value };
|
||||
}
|
||||
|
||||
return { [`${name}${filter}`]: value };
|
||||
});
|
||||
|
||||
return { _where: nextFilters };
|
||||
};
|
||||
|
||||
export default formatFiltersToQuery;
|
||||
@ -2,6 +2,8 @@ export { default as checkIfAttributeIsDisplayable } from './checkIfAttributeIsDi
|
||||
export { default as createDefaultForm } from './createDefaultForm';
|
||||
export { default as dateFormats } from './dateFormats';
|
||||
export { default as formatComponentData } from './formatComponentData';
|
||||
export { default as formatFiltersFromQuery } from './formatFiltersFromQuery';
|
||||
export { default as formatFiltersToQuery } from './formatFiltersToQuery';
|
||||
export { default as generatePermissionsObject } from './generatePermissionsObject';
|
||||
export { default as getInjectedComponents } from './getComponents';
|
||||
export { default as getMaxTempKey } from './getMaxTempKey';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user