diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index d1105a9f..87ec64f2 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -141,10 +141,10 @@ const Home = () => { setFilterData((prev) => ({ ...prev, search: v })); setSearch(v); }} - onCitiesChange={(v) => { - setFilterData((prev) => ({ ...prev, cities: v })); + onFilterBadgeClicked={(newFilterData) => { + setFilterData((prev) => ({ ...prev, ...newFilterData })); const searchQuery = qs.stringify( - { ...filterData, cities: v }, + { ...filterData, ...newFilterData }, { skipNulls: true, } diff --git a/src/pages/Home/components/Filter/Filter.tsx b/src/pages/Home/components/Filter/Filter.tsx index f71d98b7..eb4c71a2 100644 --- a/src/pages/Home/components/Filter/Filter.tsx +++ b/src/pages/Home/components/Filter/Filter.tsx @@ -26,7 +26,7 @@ import CitiesFilter from './CitiesFilter'; import { IUseSuppliesData } from '@/hooks/useSupplies/types'; import { SupplyPriority } from '@/service/supply/types'; -const ShelterAvailabilityStatusMapped: Record< +export const ShelterAvailabilityStatusMapped: Record< ShelterAvailabilityStatus, string > = { @@ -43,23 +43,27 @@ const priorityOpts = Object.entries(priorityOptions).reduce( {} as Record ); +const dataKeyToIdValueToObj = + ( + prev: Record, + current: ISupplyCategory | IUseSuppliesData + ) => + ({ ...prev, [current.id]: current }); + + +export const mapSupplyCategories = (supplyCategories: ISupplyCategory[]) => + supplyCategories.reduce(dataKeyToIdValueToObj, {} as Record); + +export const mapSupplies = (supplies: IUseSuppliesData[]) => + supplies.reduce(dataKeyToIdValueToObj, {} as Record); + const Filter = (props: IFilterProps) => { const { data, onClose, onSubmit, open } = props; const { data: supplies, loading: loadingSupplies } = useSupplies(); const { data: supplyCategories, loading: loadingSupplyCategories } = useSupplyCategories(); - const mappedSupplyCategories = useMemo(() => { - return supplyCategories.reduce( - (prev, current) => ({ ...prev, [current.id]: current }), - {} as Record - ); - }, [supplyCategories]); - const mappedSupplies = useMemo(() => { - return supplies.reduce( - (prev, current) => ({ ...prev, [current.id]: current }), - {} as Record - ); - }, [supplies]); + const mappedSupplyCategories = useMemo(() => mapSupplyCategories(supplyCategories), [supplyCategories]); + const mappedSupplies = useMemo(() => mapSupplies(supplies), [supplies]); const { handleSubmit, values, setFieldValue } = useFormik( { diff --git a/src/pages/Home/components/Filter/types.ts b/src/pages/Home/components/Filter/types.ts index 347633f8..3847822f 100644 --- a/src/pages/Home/components/Filter/types.ts +++ b/src/pages/Home/components/Filter/types.ts @@ -12,6 +12,7 @@ export interface IFilterFormProps { supplyIds: string[]; shelterStatus: ShelterAvailabilityStatus[]; cities: string[]; + [key: string]: string | string[]; } export interface IFilterFormikProps { diff --git a/src/pages/Home/components/FilterBadges/FilterBadges.tsx b/src/pages/Home/components/FilterBadges/FilterBadges.tsx new file mode 100644 index 00000000..ce6ef3bc --- /dev/null +++ b/src/pages/Home/components/FilterBadges/FilterBadges.tsx @@ -0,0 +1,88 @@ +import React, { useMemo } from 'react'; +import { X } from 'lucide-react'; + +import { getSupplyPriorityProps } from '@/lib/utils'; +import { useSupplyCategories } from '@/hooks/useSupplyCategories'; +import { useSupplies } from '@/hooks/useSupplies'; +import { ShelterAvailabilityStatusMapped, mapSupplies, mapSupplyCategories } from '../Filter/Filter'; +import { IFilterBadgesProps } from './types'; +import { IFilterFormProps, ShelterAvailabilityStatus } from '../Filter/types'; + +function removeFilter({ from: filterData, filter: item } : { from: IFilterFormProps, filter: string }): IFilterFormProps { + const filterFormPropsListNames = Object.keys(filterData); + const onlyArrayProps = filterFormPropsListNames.filter((prop) => Array.isArray(filterData[prop as keyof IFilterFormProps])); + const newFilterData: IFilterFormProps = { + search: "", + priority: [], + supplyCategoryIds: [], + supplyIds: [], + shelterStatus: [], + cities: [] + } + + onlyArrayProps.forEach((property: string) => { + const propertyValue = filterData[property as keyof IFilterFormProps]; + + if (Array.isArray(propertyValue)) { + const newValues = propertyValue.filter((it) => it !== item) as typeof propertyValue; + newFilterData[property] = [...newValues]; + } + }); + + return newFilterData; +} + +const ensureDataIsArray = (data: string[] | ShelterAvailabilityStatus[] | string): string[] | ShelterAvailabilityStatus[] => { + + try { + if (Array.isArray(data)) { + return data; + } + + throw new Error("Filter URI query is not an array."); + } catch (error) { + console.error(error); + } + + return []; +}; + +const FilterBadges = React.forwardRef< + HTMLDivElement, + IFilterBadgesProps +>((props, ref) => { + const { filterData, onBadgeClicked } = props; + + const { data: supplyCategories } = useSupplyCategories(); + const mappedSupplyCategories = useMemo(() => + mapSupplyCategories(supplyCategories), [supplyCategories]); + const { data: supplies } = useSupplies(); + const mappedSupplies = useMemo(() => mapSupplies(supplies), [supplies]); + + const renderFilterBadge = (filterKey: string, filterLabel: string) => { + return
+ onBadgeClicked?.(removeFilter({ from: filterData, filter: filterKey }))} + > + {filterLabel} +
; + }; + + return ( +
+ {ensureDataIsArray(filterData.cities)?.map((city) => renderFilterBadge(city, city))} + {ensureDataIsArray(filterData.priority)?.map((priorityLevel) => + renderFilterBadge(priorityLevel, getSupplyPriorityProps(+priorityLevel).label))} + {ensureDataIsArray(filterData.supplyCategoryIds)?.map((supplyCategoryId) => + renderFilterBadge(supplyCategoryId, mappedSupplyCategories[supplyCategoryId]?.name))} + {ensureDataIsArray(filterData.supplyIds)?.map((supplyId) => + renderFilterBadge(supplyId, mappedSupplies[supplyId]?.name))} + {ensureDataIsArray(filterData.shelterStatus)?.map((statusLabel) => + renderFilterBadge(statusLabel, ShelterAvailabilityStatusMapped[statusLabel as ShelterAvailabilityStatus]))} +
+ ); +}); + +export { FilterBadges }; diff --git a/src/pages/Home/components/FilterBadges/index.ts b/src/pages/Home/components/FilterBadges/index.ts new file mode 100644 index 00000000..c07d416f --- /dev/null +++ b/src/pages/Home/components/FilterBadges/index.ts @@ -0,0 +1,3 @@ +import { FilterBadges } from './FilterBadges'; + +export { FilterBadges }; diff --git a/src/pages/Home/components/FilterBadges/types.ts b/src/pages/Home/components/FilterBadges/types.ts new file mode 100644 index 00000000..df1e7829 --- /dev/null +++ b/src/pages/Home/components/FilterBadges/types.ts @@ -0,0 +1,8 @@ +import { IFilterFormProps } from "../Filter/types"; + + +export interface IFilterBadgesProps + extends React.ComponentPropsWithoutRef<'div'> { + filterData: IFilterFormProps; + onBadgeClicked?: (v: IFilterFormProps) => void; +} diff --git a/src/pages/Home/components/ShelterListView/ShelterListView.tsx b/src/pages/Home/components/ShelterListView/ShelterListView.tsx index ec57fc7b..0109277d 100644 --- a/src/pages/Home/components/ShelterListView/ShelterListView.tsx +++ b/src/pages/Home/components/ShelterListView/ShelterListView.tsx @@ -1,5 +1,5 @@ import React, { Fragment } from 'react'; -import { CircleAlert, ListFilter, X } from 'lucide-react'; +import { CircleAlert, ListFilter } from 'lucide-react'; import { Alert, @@ -12,6 +12,7 @@ import { cn } from '@/lib/utils'; import { IShelterListViewProps } from './types'; import { useSearchParams } from 'react-router-dom'; import { LoadingSkeleton } from '@/components/LoadingSkeleton'; +import { FilterBadges } from '../FilterBadges'; const ShelterListView = React.forwardRef( (props, ref) => { @@ -22,7 +23,7 @@ const ShelterListView = React.forwardRef( searchValue = '', hasMoreItems = false, onSearchValueChange, - onCitiesChange, + onFilterBadgeClicked, onFetchMoreData, className = '', onOpenModal, @@ -52,19 +53,9 @@ const ShelterListView = React.forwardRef( : undefined } /> -
- {filterData.cities?.map((item) => ( -
- onCitiesChange?.(filterData.cities.filter((it) => it !== item)) - } - > - {item} -
- ))} -
+ + +