diff --git a/src/modules/advanced/components/AdvancedFilter/index.js b/src/modules/advanced/components/AdvancedFilter/index.js index 54f10b8e..b273aca3 100644 --- a/src/modules/advanced/components/AdvancedFilter/index.js +++ b/src/modules/advanced/components/AdvancedFilter/index.js @@ -1,5 +1,4 @@ import { Checkbox } from '../../../reusable'; -import { DateRangeInput } from '../../../core'; import NarrowSearchTo from '../NarrowSearchTo'; import PropTypes from 'prop-types'; import React from 'react'; @@ -18,82 +17,6 @@ const getIsCheckboxFilterChecked = ({ advancedFilter }) => { return false; }; -const getDateRangeValue = ({ beginDateQuery, endDateQuery, selectedRange }) => { - switch (selectedRange) { - case 'Before': - if (endDateQuery) { - return `before ${endDateQuery}`; - } - return null; - case 'After': - if (beginDateQuery) { - return `after ${beginDateQuery}`; - } - return null; - case 'Between': - if (beginDateQuery && endDateQuery) { - return `${beginDateQuery} to ${endDateQuery}`; - } - return null; - case 'In': - if (beginDateQuery) { - return beginDateQuery; - } - return null; - default: - return null; - } -}; - -const getStateDateRangeValues = ({ advancedFilter }) => { - if (advancedFilter.activeFilters?.length > 0) { - const [filterValue] = advancedFilter.activeFilters; - - // Before - if (filterValue.indexOf('before') !== -1) { - const values = filterValue.split('before'); - - return { - stateEndQuery: values[1], - stateSelectedRangeOption: 0 - }; - } - - // After - if (filterValue.indexOf('after') !== -1) { - const values = filterValue.split('after'); - - return { - stateBeginQuery: values[1], - stateSelectedRangeOption: 1 - }; - } - - // Between - if (filterValue.indexOf('to') !== -1) { - const values = filterValue.split('to'); - - return { - stateBeginQuery: values[0], - stateEndQuery: values[1], - stateSelectedRangeOption: 2 - }; - } - - // In or other - return { - stateBeginQuery: filterValue, - stateSelectedRangeOption: 3 - }; - } - - return { - stateBeginQuery: '', - stateEndQuery: '', - stateSelectedRangeOption: 0 - }; -}; - const AdvancedFilter = ({ advancedFilter, changeAdvancedFilter }) => { if (advancedFilter.type === 'scope_down') { return ( @@ -127,24 +50,6 @@ const AdvancedFilter = ({ advancedFilter, changeAdvancedFilter }) => { /> ); } - if (advancedFilter.type === 'date_range_input') { - const { stateSelectedRangeOption, stateBeginQuery, stateEndQuery } = getStateDateRangeValues({ advancedFilter }); - - return ( - { - return changeAdvancedFilter({ - filterGroupUid: advancedFilter.uid, - filterType: advancedFilter.type, - filterValue: getDateRangeValue({ beginDateQuery, endDateQuery, selectedRange }) - }); - }} - /> - ); - } return null; }; diff --git a/src/modules/advanced/components/FiltersContainer/index.js b/src/modules/advanced/components/FiltersContainer/index.js index 9a6b8839..d0dd5247 100644 --- a/src/modules/advanced/components/FiltersContainer/index.js +++ b/src/modules/advanced/components/FiltersContainer/index.js @@ -1,15 +1,18 @@ import { AdvancedSearchSubmit, setAdvancedFilter } from '../../../advanced'; +import { DateRangeInput, Multiselect } from '../../../core'; import { useDispatch, useSelector } from 'react-redux'; import ActiveAdvancedFilters from '../ActiveAdvancedFilters'; import AdvancedFilter from '../AdvancedFilter'; import getFilters from './getFilters'; -import { Multiselect } from '../../../core'; import PropTypes from 'prop-types'; import React from 'react'; const FiltersContainer = ({ datastoreUid }) => { const dispatch = useDispatch(); - const { activeFilters, filters: filterGroups } = useSelector((state) => { + const { [datastoreUid]: urlFilters = {} } = useSelector((state) => { + return state.filters.active; + }); + const { activeFilters = {}, filters: filterGroups } = useSelector((state) => { return state.advanced[datastoreUid] || {}; }); const advancedDatastoreFilters = getFilters({ activeFilters, filterGroups }); @@ -43,7 +46,6 @@ const FiltersContainer = ({ datastoreUid }) => { })); break; case 'checkbox': - case 'date_range_input': dispatch(setAdvancedFilter({ datastoreUid, filterGroupUid, @@ -74,13 +76,24 @@ const FiltersContainer = ({ datastoreUid }) => { ? ( advancedDatastoreFilters[filterGroup].map((advancedFilter, index) => { const { filters, name, type, uid } = advancedFilter; + const currentAdvancedFilters = activeFilters[uid] || []; + const currentURLFilters = urlFilters[uid] || []; + // Make sure the URL filters and the advanced filters match on load + const currentFilters = [ + ...currentURLFilters.filter((currentURLFilter) => { + return !currentAdvancedFilters.includes(currentURLFilter); + }), + ...currentAdvancedFilters.filter((currentAdvancedFilter) => { + return !currentURLFilters.includes(currentAdvancedFilter); + }) + ]; return (

{name}

- {type === 'multiple_select' - ? - : } + {type === 'multiple_select' && } + {type === 'date_range_input' && } + {!['multiple_select', 'date_range_input'].includes(type) && }
); diff --git a/src/modules/core/components/DateRangeInput/index.js b/src/modules/core/components/DateRangeInput/index.js index dfadd974..5985473f 100644 --- a/src/modules/core/components/DateRangeInput/index.js +++ b/src/modules/core/components/DateRangeInput/index.js @@ -1,114 +1,134 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; +import { setAdvancedFilter } from '../../../advanced'; +import { useDispatch } from 'react-redux'; -const YearInput = ({ point = 'start', query, setQuery }) => { - return ( -
- - - Please enter this format: YYYY -
- ); +const options = ['before', 'after', 'between', 'in']; + +const extractYears = (dateString) => { + return dateString.match(/\d+/gu) || ['']; }; -YearInput.propTypes = { - point: PropTypes.string, - query: PropTypes.string, - setQuery: PropTypes.func +const extractRange = (dateString) => { + const years = extractYears(dateString); + if (!years[0]) { + return 'before'; + } + if (years.length > 1) { + return 'between'; + } + return ['before', 'after'].find((prefix) => { + return dateString.startsWith(prefix); + }) || 'in'; }; -const dateRangeOptions = ['Before', 'After', 'Between', 'In']; +const minValue = 1000; +const maxValue = new Date().getFullYear(); +const minValues = [minValue, minValue + 1]; +const maxValues = [maxValue - 1, maxValue]; -const DateRangeInput = ({ beginQuery, endQuery, selectedRangeOption, handleSelection }) => { - const [beginQueryState, setBeginQuery] = useState(beginQuery || ''); - const [endQueryState, setEndQuery] = useState(endQuery || ''); - const [selectedRangeOptionState, setSelectedRangeOption] = useState(selectedRangeOption || 0); +const DateRangeInput = ({ currentFilter = '', datastoreUid, filterGroupUid }) => { + const dispatch = useDispatch(); + const [range, setRange] = useState(extractRange(currentFilter)); + const [years, setYears] = useState(extractYears(currentFilter)); + const [min, setMin] = useState(minValues); + const [max, setMax] = useState(maxValues); - const handleStateChange = (beginQueryVal, endQueryVal, selectedRange) => { - handleSelection({ - beginDateQuery: beginQueryVal, - endDateQuery: endQueryVal, - selectedRange - }); - }; + const updateFilter = useCallback((filterValue) => { + dispatch(setAdvancedFilter({ datastoreUid, filterGroupUid, filterValue, onlyOneFilterValue: true })); + }, [dispatch, datastoreUid, filterGroupUid]); useEffect(() => { - const selectedRange = dateRangeOptions[selectedRangeOptionState]; - handleStateChange(beginQueryState, endQueryState, selectedRange); - }, [beginQueryState, endQueryState, selectedRangeOptionState]); - - const handleBeginQueryChange = (query) => { - setBeginQuery(query); - }; + updateFilter(currentFilter); + }, [currentFilter, updateFilter]); - const handleEndQueryChange = (query) => { - setEndQuery(query); - }; + useEffect(() => { + let filterValue = ''; + if (years.some(Boolean)) { + if (range === 'between') { + filterValue = years.filter(Number).join(' to '); + } else { + filterValue = ['before', 'after'].includes(range) ? `${range} ${years[0]}` : years[0]; + } + } + updateFilter(filterValue); + }, [range, years, updateFilter]); - const rangeOption = dateRangeOptions[selectedRangeOptionState]; + const handleYearChange = useCallback((index, value) => { + if (range === 'between') { + const newYears = [...years]; + newYears[index] = value; + const newMin = [...min]; + const newMax = [...max]; + if (index === 0) { + newYears[1] = value ? newYears[1] : ''; + newMin[1] = value && value < maxValues[1] - 1 ? Math.max(minValues[1], Number(value) + 1) : minValues[1]; + } else { + newMax[0] = value && value > minValues[0] + 1 ? Math.min(maxValues[0], Number(value) - 1) : maxValues[0]; + } + setYears(newYears); + setMin(newMin); + setMax(newMax); + } else { + setYears([value]); + } + }, [range, years, min, max]); return (
Select the type of date range to search on - {dateRangeOptions.map((option, index) => { + {options.map((option, index) => { return ( ); })}
-
- { - rangeOption !== 'Before' && ( - { - return handleBeginQueryChange(event.target.value); - }} - /> - ) - } - { - ['Before', 'Between'].includes(rangeOption) && ( - { - return handleEndQueryChange(event.target.value); - }} - point='end' - /> - ) - } +
+ {Array(range === 'between' ? 2 : 1).fill('').map((input, index) => { + const label = (index === 1 || range === 'before') ? 'End' : 'Start'; + const id = `date-range-${label.toLowerCase()}-date`; + return ( +
+ + 0 && !years[index - 1]} + min={range === 'between' ? min[index] : minValue} + max={range === 'between' ? max[index] : maxValue} + onChange={(event) => { + return handleYearChange(index, event.target.value); + }} + autoComplete='on' + /> + Please enter this format: YYYY +
+ ); + })}
); }; DateRangeInput.propTypes = { - beginQuery: PropTypes.string, - endQuery: PropTypes.string, - handleSelection: PropTypes.func, - selectedRangeOption: PropTypes.number + currentFilter: PropTypes.string, + datastoreUid: PropTypes.string.isRequired, + filterGroupUid: PropTypes.string.isRequired }; export default DateRangeInput; diff --git a/src/modules/core/components/Multiselect/index.js b/src/modules/core/components/Multiselect/index.js index 69eb968e..0a51a795 100644 --- a/src/modules/core/components/Multiselect/index.js +++ b/src/modules/core/components/Multiselect/index.js @@ -1,21 +1,14 @@ import './styles.css'; import React, { useEffect, useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { setAdvancedFilter } from '../../../advanced'; +import { useDispatch } from 'react-redux'; -const Multiselect = ({ datastoreUid, filterGroupUid, filters = {}, name }) => { +const Multiselect = ({ currentFilters, datastoreUid, filterGroupUid, filters = {}, name }) => { const dispatch = useDispatch(); const [filterQuery, setFilterQuery] = useState(''); const [showOnlySelectedOptions, setShowOnlySelectedOptions] = useState(false); - const { [filterGroupUid]: urlFilters = [] } = useSelector((state) => { - return state.filters.active[datastoreUid] || {}; - }); - const { [filterGroupUid]: advancedFilters = [] } = useSelector((state) => { - return state.advanced[datastoreUid].activeFilters || {}; - }); - const options = useMemo(() => { return filters.map(({ isActive, value }) => { return { @@ -31,15 +24,6 @@ const Multiselect = ({ datastoreUid, filterGroupUid, filters = {}, name }) => { } useEffect(() => { - // Make sure the URL filters and the advanced filters match on load - const currentFilters = [ - ...urlFilters.filter((urlFilter) => { - return !advancedFilters.includes(urlFilter); - }), - ...advancedFilters.filter((advancedFilter) => { - return !urlFilters.includes(advancedFilter); - }) - ]; currentFilters.forEach((filterValue) => { dispatch(setAdvancedFilter({ datastoreUid, @@ -129,6 +113,7 @@ const Multiselect = ({ datastoreUid, filterGroupUid, filters = {}, name }) => { }; Multiselect.propTypes = { + currentFilters: PropTypes.array, datastoreUid: PropTypes.string, filterGroupUid: PropTypes.string, filters: PropTypes.array, diff --git a/src/modules/records/components/Pagination/styles.css b/src/modules/records/components/Pagination/styles.css index 0f6169b4..2ee32428 100644 --- a/src/modules/records/components/Pagination/styles.css +++ b/src/modules/records/components/Pagination/styles.css @@ -32,12 +32,9 @@ justify-self: end; } .pagination-input { - border: solid 1px rgba(0, 0, 0, 0.3); - border-radius: 4px; - box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.08); display: flex; - margin: 0 0.5rem; - padding: 0.15rem; + margin: 0 0.5rem!important; + padding: 0.15rem!important; text-align: center; - width: 5rem; + width: 5rem!important; } diff --git a/src/stylesheets/main.css b/src/stylesheets/main.css index 5b963fa0..95db1aa7 100644 --- a/src/stylesheets/main.css +++ b/src/stylesheets/main.css @@ -329,15 +329,17 @@ input[type="url"], input[type="search"], input[type="email"], input[type="tel"], -input[type="date"] { +input[type="date"], +input[type="number"] { + background: white; + border: solid 1px var(--search-color-grey-400); + border-radius: 4px; + box-shadow: inset 0 1px 4px var(--search-color-grey-300); + font-size: 1rem; + line-height: 1.4; margin: 0; padding: 0; - font-size: 1rem; - border: solid 1px rgba(0, 0, 0, 0.3); - box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.08); - border-radius: 4px; padding: 0.5rem 0.75rem; - line-height: 1.4; width: 100%; } @@ -354,9 +356,11 @@ input[type="tel"]:read-only, input[type="text"]:disabled, input[type="text"]:read-only, input[type="url"]:disabled, -input[type="url"]:read-only { - background: #FAFAFA; - border-color: #CCC; +input[type="url"]:read-only, +input[type="number"]:disabled, +input[type="number"]:read-only { + background: var(--search-color-grey-200); + border-color: var(--search-color-grey-400); color: var(--search-color-grey-600); cursor: not-allowed; } @@ -1956,16 +1960,6 @@ body { padding-right: 0.5rem; } -.date-range-input .switch-options { - justify-content: flex-start; -} - -.date-range-container { - display: flex; - gap: 1rem; - margin-top: 0.5rem; -} - .chat-widget { position: fixed; right: 1.5%;