Skip to content

Commit 072ef4b

Browse files
feature/dep315: Refactored filters and got them working, switched landing area to context data, updated landing design, other small fixes.
1 parent d12bcbc commit 072ef4b

18 files changed

Lines changed: 389 additions & 294 deletions

File tree

api/src/api/models/engagement.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def _filter_by_engagement_status(query, search_options):
275275
for status in search_options.get('engagement_status', [])]
276276
if not statuses:
277277
return query
278-
278+
279279
allowed_statuses = [
280280
Status.Draft.value,
281281
Status.Published.value,
@@ -296,9 +296,18 @@ def _filter_by_engagement_status(query, search_options):
296296
status_filter.append(
297297
and_(
298298
Engagement.status_id == Status.Published.value,
299-
Engagement.start_date <= datetime.now()
299+
Engagement.start_date <= datetime.now(),
300+
Engagement.end_date > datetime.now()
301+
)
302+
)
303+
if EngagementDisplayStatus.Closed.value in statuses:
304+
status_filter.append(
305+
and_(
306+
Engagement.status_id == Status.Closed.value,
307+
Engagement.end_date < datetime.now()
300308
)
301309
)
310+
302311
if EngagementDisplayStatus.Unpublished.value in statuses:
303312
status_filter.append(Engagement.status_id ==
304313
Status.Unpublished.value)

web/src/components/engagement/admin/create/authoring/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// Determines whether a string is JSON parseable and returns the JSON if it is.
2-
export const tryParse = (json: string) => {
2+
export const tryParse = (json: string | null | undefined) => {
3+
if (!json || typeof json !== 'string') {
4+
return false;
5+
}
36
try {
47
const object = JSON.parse(json);
58
if (object && typeof object === 'object') {

web/src/components/engagement/preview/EngagementPreview.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState, useRef, useMemo } from 'react';
2-
import { useBlocker, useRouteLoaderData, useParams, useRevalidator } from 'react-router';
2+
import { useBlocker, useParams, useRevalidator, useLoaderData } from 'react-router';
33
import { Box, Modal } from '@mui/material';
44
import PreviewControlBar from './PreviewControlBar';
55
import PreviewContent from './PreviewContent';
@@ -186,7 +186,7 @@ const MeasurementBar: React.FC = () => {
186186
* modifying the actual engagement.
187187
*/
188188
export const EngagementPreview: React.FC = () => {
189-
const loaderData = useRouteLoaderData('engagement-preview') as EngagementLoaderPublicData;
189+
const loaderData = useLoaderData() as EngagementLoaderPublicData;
190190
const { engagement, widgets, details, metadata, suggestions } = loaderData ?? {};
191191
const { engagementId, languageCode } = useParams();
192192
const revalidator = useRevalidator();

web/src/components/landing/EngagementSearch.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import LandingSection from './LandingSection';
22
import React from 'react';
33
import FilterBlock from './FilterBlock';
44
import TileBlock from './TileBlock';
5-
import { EngagementSearchProps } from './types';
65

7-
const EngagementSearch = (props: EngagementSearchProps) => {
6+
const EngagementSearch = () => {
87
return (
98
<LandingSection>
10-
<FilterBlock {...props} />
11-
<TileBlock {...props} />
9+
<FilterBlock />
10+
<TileBlock />
1211
</LandingSection>
1312
);
1413
};

web/src/components/landing/FilterBlock.tsx

Lines changed: 75 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useRef, useState } from 'react';
1+
import React, { useContext, useEffect, useRef, useState } from 'react';
22
import { IconButton, Stack, useTheme } from '@mui/material';
33
import Grid from '@mui/material/Grid2';
44
import { DeletableFilterChip } from './DeletableFilterChip';
@@ -9,21 +9,28 @@ import { faXmark } from '@fortawesome/pro-regular-svg-icons/faXmark';
99
import { faSliders } from '@fortawesome/pro-regular-svg-icons/faSliders';
1010
import { MetadataFilter } from 'components/metadataManagement/types';
1111
import { debounce } from 'lodash';
12-
import { EngagementDisplayStatus } from 'constants/engagementStatus';
1312
import { useAppTranslation } from 'hooks';
1413
import { Button } from 'components/common/Input/Button';
1514
import { colors } from '../common';
1615
import { CustomTextField, Select } from 'components/common/Input';
1716
import { When } from 'react-if';
1817
import { BodyText } from 'components/common/Typography/Body';
19-
import { FilterBlockProps } from './types';
18+
import { LandingDataContext } from '.';
19+
import { tryParse } from 'engagements/admin/create/authoring/utils';
20+
import { getSearchParamObject, updateSearchParams } from './utils';
21+
import { EngagementDisplayStatus } from 'constants/engagementStatus';
2022

21-
const FilterBlock = (props: FilterBlockProps) => {
22-
const { searchFilters, setSearchFilters, clearFilters, setFiltersOpen } = props;
23-
const selectedValue = searchFilters.engagement_status.length === 0 ? -1 : searchFilters.engagement_status[0];
23+
const FilterBlock = () => {
24+
const { searchParams, setSearchParams, clearFilters, setFiltersOpen } = useContext(LandingDataContext);
25+
26+
const selectedValue =
27+
getSearchParamObject('engagement_status', searchParams)?.length === 0
28+
? -1
29+
: Number(getSearchParamObject('engagement_status', searchParams)?.[0]);
2430

2531
const tileBlockRef = useRef<HTMLDivElement>(null);
2632
const [didMount, setDidMount] = useState(false);
33+
const [searchText, setSearchText] = useState('');
2734

2835
const theme = useTheme();
2936
const { t: translate } = useAppTranslation();
@@ -37,10 +44,11 @@ const FilterBlock = (props: FilterBlockProps) => {
3744

3845
const debounceSetSearchFilters = useRef(
3946
debounce((searchText: string) => {
40-
setSearchFilters({
41-
...searchFilters,
47+
const newSearchParams = new URLSearchParams({
48+
...searchParams,
4249
search_text: searchText,
4350
});
51+
setSearchParams(newSearchParams);
4452
}, 300),
4553
).current;
4654

@@ -54,12 +62,13 @@ const FilterBlock = (props: FilterBlockProps) => {
5462
const yOffset = tileBlockRef?.current?.offsetTop;
5563
globalThis.scrollTo({ top: yOffset || 0, behavior: 'smooth' });
5664
}
57-
}, [searchFilters.page]);
58-
59-
const [searchText, setSearchText] = useState('');
65+
}, [searchParams.get('page')]);
6066

6167
const handleDeleteFilterChip = (taxonId: number, value: string) => {
62-
const newMetadataFilters = searchFilters.metadata
68+
const metaParams = searchParams.get('meta_filters');
69+
const metaFilters = metaParams && tryParse(metaParams) ? JSON.parse(metaParams) : {};
70+
if (!metaFilters) return;
71+
const newMetaFilters = metaFilters
6372
.map((filter: MetadataFilter) => {
6473
if (filter.taxon_id === taxonId) {
6574
// Remove the value
@@ -69,8 +78,12 @@ const FilterBlock = (props: FilterBlockProps) => {
6978
return filter;
7079
})
7180
.filter((filter: MetadataFilter) => filter.values.length > 0); // Remove any filters with no values left
72-
73-
setSearchFilters({ ...searchFilters, metadata: newMetadataFilters, page: 1 });
81+
const newMetaFilterParams = new URLSearchParams({
82+
...searchParams,
83+
meta_filters: JSON.stringify(newMetaFilters),
84+
page: '1',
85+
});
86+
setSearchParams(newMetaFilterParams);
7487
};
7588

7689
return (
@@ -120,10 +133,7 @@ const FilterBlock = (props: FilterBlockProps) => {
120133
title="Clear search"
121134
sx={{ color: '#9F9D9C' }}
122135
onClick={() => {
123-
setSearchFilters({
124-
...searchFilters,
125-
search_text: '',
126-
});
136+
setSearchParams(updateSearchParams({ search_text: '' }, searchParams));
127137
setSearchText('');
128138
}}
129139
>
@@ -162,41 +172,55 @@ const FilterBlock = (props: FilterBlockProps) => {
162172
rowGap={1}
163173
width="100%"
164174
>
165-
<Select
166-
style={{ margin: 0 }}
167-
value={selectedValue}
168-
id="status-filter"
169-
aria-label={`Filtering by ${selectableStatuses.get(
170-
selectedValue,
171-
)}. Change this filter value by expanding to view all options.`}
172-
onChange={(event) => {
173-
const selectedValue = Number(event.target.value);
174-
setSearchFilters({
175-
...searchFilters,
176-
engagement_status: selectedValue === -1 ? [] : [selectedValue],
177-
page: 1,
178-
});
179-
}}
180-
renderValue={(value) => selectableStatuses.get(value as number) ?? ''}
181-
displayEmpty
182-
inputProps={{
183-
'aria-label': `Status Filter - ${selectableStatuses.get(selectedValue) ?? ''}`,
184-
}}
185-
options={Array.from(selectableStatuses).map(([status, label]) => ({
186-
value: status,
187-
label: label,
188-
}))}
189-
/>
190-
{searchFilters.metadata.map((filter: MetadataFilter) =>
191-
filter.values.map((value) => (
192-
<DeletableFilterChip
193-
key={`${filter.taxon_id}-${value}`}
194-
name={value}
195-
onDelete={() => handleDeleteFilterChip(filter.taxon_id, value)}
175+
{selectedValue !== undefined && selectableStatuses && (
176+
<>
177+
<Select
178+
style={{ margin: 0 }}
179+
value={selectedValue}
180+
id="status-filter"
181+
aria-label={`Filtering by ${selectableStatuses.get(
182+
selectedValue,
183+
)}. Change this filter value by expanding to view all options.`}
184+
onChange={(event) => {
185+
const selectedValue = Number(event.target.value);
186+
const newParams = updateSearchParams(
187+
{
188+
engagement_status: selectedValue === -1 ? [] : [selectedValue],
189+
page: 1,
190+
},
191+
searchParams,
192+
);
193+
setSearchParams(newParams);
194+
}}
195+
renderValue={(value) => {
196+
return selectableStatuses.get(value as number) ?? '';
197+
}}
198+
displayEmpty
199+
inputProps={{
200+
'aria-label': `Status Filter - ${selectableStatuses.get(selectedValue) ?? ''}`,
201+
}}
202+
options={Array.from(selectableStatuses).map(([status, label]) => ({
203+
value: status,
204+
label: label,
205+
}))}
196206
/>
197-
)),
207+
{getSearchParamObject('meta_filters', searchParams)?.map((filter: MetadataFilter) =>
208+
filter.values.map((value) => (
209+
<DeletableFilterChip
210+
key={`${filter.taxon_id}-${value}`}
211+
name={value}
212+
onDelete={() => handleDeleteFilterChip(filter.taxon_id, value)}
213+
/>
214+
)),
215+
)}
216+
</>
198217
)}
199-
<When condition={searchFilters.engagement_status.length || searchFilters.metadata.length}>
218+
<When
219+
condition={
220+
getSearchParamObject('engagement_status', searchParams)?.length ||
221+
getSearchParamObject('meta_filters', searchParams)?.length
222+
}
223+
>
200224
<Button
201225
variant="tertiary"
202226
onClick={clearFilters}

0 commit comments

Comments
 (0)