Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
30 changes: 17 additions & 13 deletions src/pages/Home/components/Filter/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
> = {
Expand All @@ -43,23 +43,27 @@ const priorityOpts = Object.entries(priorityOptions).reduce(
{} as Record<SupplyPriority, string>
);

const dataKeyToIdValueToObj =
(
prev: Record<string, IUseSuppliesData | ISupplyCategory>,
current: ISupplyCategory | IUseSuppliesData
) =>
({ ...prev, [current.id]: current });


export const mapSupplyCategories = (supplyCategories: ISupplyCategory[]) =>
supplyCategories.reduce(dataKeyToIdValueToObj, {} as Record<string, ISupplyCategory>);

export const mapSupplies = (supplies: IUseSuppliesData[]) =>
supplies.reduce(dataKeyToIdValueToObj, {} as Record<string, IUseSuppliesData>);

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<string, ISupplyCategory>
);
}, [supplyCategories]);
const mappedSupplies = useMemo(() => {
return supplies.reduce(
(prev, current) => ({ ...prev, [current.id]: current }),
{} as Record<string, IUseSuppliesData>
);
}, [supplies]);
const mappedSupplyCategories = useMemo(() => mapSupplyCategories(supplyCategories), [supplyCategories]);
const mappedSupplies = useMemo(() => mapSupplies(supplies), [supplies]);

const { handleSubmit, values, setFieldValue } = useFormik<IFilterFormikProps>(
{
Expand Down
1 change: 1 addition & 0 deletions src/pages/Home/components/Filter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface IFilterFormProps {
supplyIds: string[];
shelterStatus: ShelterAvailabilityStatus[];
cities: string[];
[key: string]: string | string[];
}

export interface IFilterFormikProps {
Expand Down
88 changes: 88 additions & 0 deletions src/pages/Home/components/FilterBadges/FilterBadges.tsx
Original file line number Diff line number Diff line change
@@ -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 <div
className="flex items-center px-4 py-1 font-normal text-sm md:text-md rounded-3xl bg-gray-300 justify-center cursor-pointer hover:opacity-80 transition-all duration-200"
key={filterKey}
onClick={() =>
onBadgeClicked?.(removeFilter({ from: filterData, filter: filterKey }))}
>
<span className="pr-1">{filterLabel}</span> <X className="h-4 w-4" />
</div>;
};

return (
<div ref={ref} className="flex flex-wrap gap-1 items-center">
{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]))}
</div>
);
});

export { FilterBadges };
3 changes: 3 additions & 0 deletions src/pages/Home/components/FilterBadges/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { FilterBadges } from './FilterBadges';

export { FilterBadges };
8 changes: 8 additions & 0 deletions src/pages/Home/components/FilterBadges/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IFilterFormProps } from "../Filter/types";


export interface IFilterBadgesProps
extends React.ComponentPropsWithoutRef<'div'> {
filterData: IFilterFormProps;
onBadgeClicked?: (v: IFilterFormProps) => void;
}
21 changes: 6 additions & 15 deletions src/pages/Home/components/ShelterListView/ShelterListView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
import { CircleAlert, ListFilter, X } from 'lucide-react';
import { CircleAlert, ListFilter } from 'lucide-react';

import {
Alert,
Expand All @@ -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<HTMLDivElement, IShelterListViewProps>(
(props, ref) => {
Expand All @@ -22,7 +23,7 @@ const ShelterListView = React.forwardRef<HTMLDivElement, IShelterListViewProps>(
searchValue = '',
hasMoreItems = false,
onSearchValueChange,
onCitiesChange,
onFilterBadgeClicked,
onFetchMoreData,
className = '',
onOpenModal,
Expand Down Expand Up @@ -52,19 +53,9 @@ const ShelterListView = React.forwardRef<HTMLDivElement, IShelterListViewProps>(
: undefined
}
/>
<div className="flex flex-wrap gap-1 items-center">
{filterData.cities?.map((item) => (
<div
className="flex items-center px-4 py-1 font-normal text-sm md:text-md rounded-3xl bg-gray-300 justify-center cursor-pointer hover:opacity-80 transition-all duration-200"
key={item}
onClick={() =>
onCitiesChange?.(filterData.cities.filter((it) => it !== item))
}
>
<span className="pr-1">{item}</span> <X className="h-4 w-4" />
</div>
))}
</div>

<FilterBadges filterData={filterData} onBadgeClicked={onFilterBadgeClicked} />

<div className="flex flex-row">
<Button
variant="ghost"
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Home/components/ShelterListView/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface IShelterListViewProps
onOpenModal?: () => void;
onClearSearch?: () => void;
onSearchValueChange?: (v: string) => void;
onCitiesChange?: (v: string[]) => void;
onFilterBadgeClicked?: (v: IFilterFormProps) => void;
onFetchMoreData?: () => void;
onSelectShelter?: (shelter: IUseSheltersData) => void;
filterData: IFilterFormProps;
Expand Down