Skip to content

Commit

Permalink
feat: add new property filters
Browse files Browse the repository at this point in the history
  • Loading branch information
frederic-maury committed Dec 23, 2024
1 parent 2b0bac6 commit 04779be
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 17 deletions.
9 changes: 7 additions & 2 deletions frontend/src/components/Filtering/FilteringOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from "react";
import { setDisplayGnosis, setDisplayRmm } from "../../store/filtering/filteringReducer";
import { setDisplayGnosis, setDisplayRmm, setPropertyTypes } from "../../store/filtering/filteringReducer";
import { useTranslation } from "react-i18next";
import { CheckboxProps } from "@mantine/core";
import MapIcon from '@mui/icons-material/Map';
Expand All @@ -12,7 +12,9 @@ import { analyticsEvent } from "../../services/analytics";
import { SettingsPanelSection } from "../Settings/SettingsPanelSection";
import { Option } from "../Settings/Settings/Option";
import { selectFiltering } from "../../store/filtering/filteringSelector";
import { useMediaQuery } from "@mantine/hooks";
import { PropertyType } from "./PropertyType";
import { PropertyOccupations } from "./PropertyOccupations";
import { PropertyYields } from "./PropertyYields";

export function FilteringOptions() {
const { t: tMapOptions } = useTranslation('common', { keyPrefix: 'mapOptions' });
Expand Down Expand Up @@ -84,6 +86,9 @@ export function FilteringOptions() {
checked={filteringOptions.displayAll}
disabled={walletAddresses.length === 0}
onChange={(e) => onDisplayAll(e)} />
<PropertyType />
<PropertyOccupations />
<PropertyYields />
</SettingsPanelSection>
</>
)
Expand Down
45 changes: 45 additions & 0 deletions frontend/src/components/Filtering/PropertyOccupations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { NumberInput } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { useMediaQuery } from "@mantine/hooks";
import { selectPropertyOccupations } from "../../store/filtering/filteringSelector";
import { useAppDispatch, useAppSelector } from "../../hooks/useInitStore";
import { setPropertyOccupations } from "../../store/filtering/filteringReducer";

export function PropertyOccupations() {
const mediaQuerySize = useMediaQuery('(max-width: 768px)') ? 'xl' : 'md';
const { t } = useTranslation('common', { keyPrefix: 'filtering' });

const dispatch = useAppDispatch();
const propertyOccupations = useAppSelector(selectPropertyOccupations);

function onPropertyOccupations(min?: number, max?: number) {
const newOccupations = {
min: min ?? propertyOccupations.min,
max: max ?? propertyOccupations.max,
};
dispatch(setPropertyOccupations(newOccupations));
}

return <>
<NumberInput
id="propertyOccupations.min"
label={t('propertyOccupations.min') + ' (%)'}
value={propertyOccupations.min}
min={0}
max={propertyOccupations.max}
allowDecimal={false}
size={mediaQuerySize}
onChange={(e) => onPropertyOccupations(e as number)}
/>
<NumberInput
id="propertyOccupations.max"
label={t('propertyOccupations.max') + ' (%)'}
value={propertyOccupations.max}
min={propertyOccupations.min}
max={100}
allowDecimal={false}
size={mediaQuerySize}
onChange={(e) => onPropertyOccupations(undefined, e as number)}
/>
</>
}
41 changes: 41 additions & 0 deletions frontend/src/components/Filtering/PropertyType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useAppSelector } from "../../hooks/useInitStore";
import { useTranslation } from "react-i18next";
import { analyticsEvent } from "../../services/analytics";
import { useAppDispatch } from "../../hooks/useInitStore";
import { selectPropertyTypes } from "../../store/filtering/filteringSelector";
import { ComboboxData, MultiSelect } from "@mantine/core";
import { setPropertyTypes } from "../../store/filtering/filteringReducer";
import { useMemo } from "react";
import { useMediaQuery } from "@mantine/hooks";

export function PropertyType() {
const mediaQuerySize = useMediaQuery('(max-width: 768px)') ? 'xl' : 'md';
const { t } = useTranslation('common', { keyPrefix: 'filtering' });
const { t: tPropertyType } = useTranslation('common', { keyPrefix: 'propertyType' });
const dispatch = useAppDispatch();
const propertyTypes = useAppSelector(selectPropertyTypes);

const data: ComboboxData = useMemo(() => [1, 2, 3, 4, 6, 8, 9, 10, 11, 12].map(type => ({
value: type.toString(),
label: tPropertyType(type.toString()),
})), [tPropertyType]);

function onPropertyTypes(value: string[]) {
analyticsEvent({
category: 'Settings',
action: 'Property Types',
label: value.join(', '),
});
dispatch(setPropertyTypes(value));
}

return (
<MultiSelect
id="propertyTypes"
label={t('propertyTypes')}
data={data}
value={propertyTypes}
size={mediaQuerySize}
onChange={(e) => onPropertyTypes(e)} />
)
}
47 changes: 47 additions & 0 deletions frontend/src/components/Filtering/PropertyYields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NumberInput } from "@mantine/core";
import { useTranslation } from "react-i18next";
import { useMediaQuery } from "@mantine/hooks";
import { selectPropertyYields } from "../../store/filtering/filteringSelector";
import { useAppDispatch, useAppSelector } from "../../hooks/useInitStore";
import { setPropertyYields } from "../../store/filtering/filteringReducer";

export function PropertyYields() {
const mediaQuerySize = useMediaQuery('(max-width: 768px)') ? 'xl' : 'md';
const { t } = useTranslation('common', { keyPrefix: 'filtering' });

const dispatch = useAppDispatch();
const propertyYields = useAppSelector(selectPropertyYields);

function onPropertyYields(min?: number, max?: number) {
const newYields = {
min: min ?? propertyYields.min ?? 0,
max: max as unknown as string === ''
? undefined
: max ?? propertyYields.max ?? undefined,
};
dispatch(setPropertyYields(newYields));
}

return <>
<NumberInput
id="propertyYields.min"
label={t('propertyYields.min') + ' (%)'}
value={propertyYields.min}
min={0}
max={propertyYields.max ?? undefined}
allowDecimal={false}
size={mediaQuerySize}
onChange={(e) => onPropertyYields(e as number)}
/>
<NumberInput
id="propertyYields.max"
label={t('propertyYields.max') + ' (%)'}
value={propertyYields.max ?? undefined}
min={propertyYields.min}
max={100}
allowDecimal={false}
size={mediaQuerySize}
onChange={(e) => onPropertyYields(undefined, e as number)}
/>
</>
}
36 changes: 30 additions & 6 deletions frontend/src/components/Map/Markers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { selectedMarker } from '../../store/marker/markerSelector';
import { selectedProperty } from '../../store/urlQuery/urlQuery.selector';
import { useTranslation } from 'react-i18next';
import { LeafletMouseEvent } from 'leaflet';
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { setSelected } from '../../store/marker/markerReducer';
import { setSelectedProperty } from '../../store/urlQuery/urlQuery.reducer';
import { filterProperties } from '../../utils/properties';
import { selectFiltering } from '../../store/filtering/filteringSelector';
import { selectFiltering, selectPropertyTypes, selectPropertyOccupations, selectPropertyYields } from '../../store/filtering/filteringSelector';

export function Markers(props: { properties: Property[] }) {
const { t } = useTranslation('common');
Expand All @@ -29,6 +29,12 @@ export function Markers(props: { properties: Property[] }) {
} = useAppSelector(selectFiltering);
const selectedMarkerProperty = useAppSelector(selectedMarker);
const selectedUrlParam = useAppSelector(selectedProperty);
const propertyTypes = useAppSelector(selectPropertyTypes);
const propertyOccupations = useAppSelector(selectPropertyOccupations);
const propertyYields = useAppSelector(selectPropertyYields);

const [properties, setProperties] = useState<Property[]>([]);
const [debouncedFilterTimeout, setDebouncedFilterTimeout] = useState<NodeJS.Timeout>();

const onMarkerClicked = useCallback((event: LeafletMouseEvent, property: Property) => {
dispatch(setSelected({
Expand Down Expand Up @@ -73,13 +79,31 @@ export function Markers(props: { properties: Property[] }) {
});
}, [selectedMarkerProperty, map]);

const filteredProperties = useMemo(() => {
return filterProperties(props.properties, displayAll, displayGnosis, displayRmm, selectedUrlParam);
}, [props.properties, displayAll, displayGnosis, displayRmm, selectedUrlParam]);
useEffect(() => {
clearTimeout(debouncedFilterTimeout);

const timeoutId = setTimeout(() => {
const filtered = filterProperties(
props.properties,
displayAll,
displayGnosis,
displayRmm,
selectedUrlParam,
propertyTypes,
propertyOccupations,
propertyYields,
);
setProperties(filtered);
}, 300);

setDebouncedFilterTimeout(timeoutId);

return () => clearTimeout(timeoutId);
}, [props.properties, displayAll, displayGnosis, displayRmm, selectedUrlParam, propertyTypes, propertyOccupations, propertyYields]);

return (
<>
{filteredProperties.map((property) => (
{properties.map((property) => (
<Marker
key={property.address}
position={property.coordinates}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Settings/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function SearchBar() {
const [searchValue, setSearchValue] = useState<string>('');

useEffect(() => {
setFilteredProperties(filterProperties(properties, displayAll, displayGnosis, displayRmm, null));
setFilteredProperties(filterProperties(properties, displayAll, displayGnosis, displayRmm));
}, [properties, displayAll, displayGnosis, displayRmm]);

let timeout: NodeJS.Timeout;
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/i18next/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@
},
"filtering": {
"filtering": "Filtering",
"filteringOptions": "Filtering Options"
"filteringOptions": "Filtering Options",
"propertyTypes": "Property Types",
"propertyOccupations": {
"min": "Min Occupations",
"max": "Max Occupations"
},
"propertyYields": {
"min": "Min Yield",
"max": "Max Yield"
}
},
"settings": {
"language": "Language",
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/i18next/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@
},
"filtering": {
"filtering": "Filtros",
"filteringOptions": "Opciones de filtros"
"filteringOptions": "Opciones de filtros",
"propertyTypes": "Tipos de propiedades",
"propertyOccupations": {
"min": "Mínimo de ocupaciones",
"max": "Máximo de ocupaciones"
},
"propertyYields": {
"min": "Mínimo de rendimiento",
"max": "Máximo de rendimiento"
}
},
"settings": {
"language": "Idioma",
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/i18next/locales/fr/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@
},
"filtering": {
"filtering": "Filtres",
"filteringOptions": "Options de filtres"
"filteringOptions": "Options de filtres",
"propertyTypes": "Types de propriétés",
"propertyOccupations": {
"min": "Min Occupations",
"max": "Max Occupations"
},
"propertyYields": {
"min": "Min Yield",
"max": "Max Yield"
}
},
"settings": {
"language": "Langue",
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/store/filtering/filteringReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,29 @@ interface FilteringState {
displayAll: boolean;
displayGnosis: boolean;
displayRmm: boolean;
propertyTypes: string[];
propertyOccupations: {
min: number;
max: number;
};
propertyYields: {
min: number;
max?: number;
};
}

const initialState: FilteringState = {
displayAll: true,
displayGnosis: true,
displayRmm: true,
propertyTypes: [],
propertyOccupations: {
min: 0,
max: 100,
},
propertyYields: {
min: 0,
},
};

export const filteringSlice = createSlice({
Expand All @@ -32,13 +49,28 @@ export const filteringSlice = createSlice({
state.displayRmm = action.payload;
setItem<FilteringState>(LOCAL_STORAGE_NAME, state);
},
setPropertyTypes: (state, action: PayloadAction<string[]>) => {
state.propertyTypes = action.payload;
setItem<FilteringState>(LOCAL_STORAGE_NAME, state);
},
setPropertyOccupations: (state, action: PayloadAction<{ min: number; max: number }>) => {
state.propertyOccupations = action.payload;
setItem<FilteringState>(LOCAL_STORAGE_NAME, state);
},
setPropertyYields: (state, action: PayloadAction<{ min: number; max?: number }>) => {
state.propertyYields = action.payload;
setItem<FilteringState>(LOCAL_STORAGE_NAME, state);
},
}
});

export const {
setDisplayAll,
setDisplayGnosis,
setDisplayRmm,
setPropertyTypes,
setPropertyOccupations,
setPropertyYields,
} = filteringSlice.actions;

export default filteringSlice.reducer;
8 changes: 7 additions & 1 deletion frontend/src/store/filtering/filteringSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ export const selectDisplayAll = (state: RootState) => state.filtering.displayAll

export const selectDisplayGnosis = (state: RootState) => state.filtering.displayGnosis;

export const selectDisplayRmm = (state: RootState) => state.filtering.displayRmm;
export const selectDisplayRmm = (state: RootState) => state.filtering.displayRmm;

export const selectPropertyTypes = (state: RootState) => state.filtering.propertyTypes;

export const selectPropertyOccupations = (state: RootState) => state.filtering.propertyOccupations;

export const selectPropertyYields = (state: RootState) => state.filtering.propertyYields;
Loading

0 comments on commit 04779be

Please sign in to comment.