From be9ed4dc2b2f84ad041a6765c8d0507da5a60b7a Mon Sep 17 00:00:00 2001 From: Andrea Gueugnaut Date: Thu, 7 Nov 2024 13:24:42 +0100 Subject: [PATCH 1/4] feat(frontend): implement a new hook for filters --- frontend/src/hooks/useFiltersNext.ts | 46 +++++++++++++++++ frontend/src/views/Group/GroupView.tsx | 49 +++++++------------ .../src/views/HousingList/HousingListView.tsx | 18 ++++--- 3 files changed, 74 insertions(+), 39 deletions(-) create mode 100644 frontend/src/hooks/useFiltersNext.ts diff --git a/frontend/src/hooks/useFiltersNext.ts b/frontend/src/hooks/useFiltersNext.ts new file mode 100644 index 000000000..0a6e296d5 --- /dev/null +++ b/frontend/src/hooks/useFiltersNext.ts @@ -0,0 +1,46 @@ +import { useMap } from 'react-use'; + +import { HousingFiltersDTO } from '@zerologementvacant/models'; +import { useToggle } from './useToggle'; + +export interface UseFiltersOptions { + initialFilters: HousingFiltersDTO; +} + +export function useFilters(options: UseFiltersOptions) { + const [filters, { setAll, remove, reset }] = useMap( + options.initialFilters + ); + const { + active: expand, + setActive: setExpand, + toggle: toggleExpand + } = useToggle(false); + + function setFilters(value: HousingFiltersDTO): void { + setAll({ + ...filters, + ...value + }); + } + + function removeFilters(value: HousingFiltersDTO): void { + Object.keys(value).forEach((key) => { + remove(key as keyof HousingFiltersDTO); + }); + } + + function resetFilters(): void { + reset(); + } + + return { + expand, + filters, + setExpand, + toggleExpand, + setFilters, + removeFilters, + resetFilters + }; +} diff --git a/frontend/src/views/Group/GroupView.tsx b/frontend/src/views/Group/GroupView.tsx index c5471de3a..439e1c5b6 100644 --- a/frontend/src/views/Group/GroupView.tsx +++ b/frontend/src/views/Group/GroupView.tsx @@ -1,5 +1,4 @@ import Alert from '@codegouvfr/react-dsfr/Alert'; -import { useMatomo } from '@jonkoops/matomo-tracker-react'; import Grid from '@mui/material/Unstable_Grid2'; import { useHistory, useParams } from 'react-router-dom'; @@ -10,12 +9,7 @@ import { } from '../../services/group.service'; import Group from '../../components/Group/Group'; import { useDocumentTitle } from '../../hooks/useDocumentTitle'; -import { useFilters } from '../../hooks/useFilters'; import HousingListFiltersSidemenu from '../../components/HousingListFilters/HousingListFiltersSidemenu'; -import { - TrackEventActions, - TrackEventCategories -} from '../../models/TrackEvent'; import HousingFiltersBadges from '../../components/HousingFiltersBadges/HousingFiltersBadges'; import HousingListMap from '../HousingList/HousingListMap'; import HousingListTabs from '../HousingList/HousingListTabs'; @@ -26,9 +20,13 @@ import authService from '../../services/auth.service'; import { GroupPayload } from '../../models/GroupPayload'; import AppSearchBar from '../../components/_app/AppSearchBar/AppSearchBar'; import { HousingDisplaySwitch } from '../../components/HousingDisplaySwitch/HousingDisplaySwitch'; -import { useCampaignList } from '../../hooks/useCampaignList'; -import { useCreateCampaignFromGroupMutation } from '../../services/campaign.service'; +import { + useCreateCampaignFromGroupMutation, + useFindCampaignsQuery +} from '../../services/campaign.service'; import NotFoundView from '../NotFoundView'; +import { useFilters } from '../../hooks/useFiltersNext'; +import { useEffect } from 'react'; interface RouterState { alert?: string; @@ -40,35 +38,24 @@ function GroupView() { useDocumentTitle(group ? `Groupe - ${group?.title}` : 'Page non trouvée'); - const { trackEvent } = useMatomo(); const { filters, - setFilters, expand, - removeFilter, + removeFilters, setExpand, - onChangeFilters, - onResetFilters + setFilters: onChangeFilters, + resetFilters: onResetFilters } = useFilters({ - storage: 'state', - initialState: { + initialFilters: { groupIds: [id] } }); - const { view } = useAppSelector((state) => state.housing); + useEffect(() => { + onChangeFilters({ groupIds: [id] }); + }, [id, onChangeFilters]); - function searchWithQuery(query: string): void { - trackEvent({ - category: TrackEventCategories.Group, - action: TrackEventActions.HousingList.Search, - name: query - }); - setFilters({ - ...filters, - query - }); - } + const { view } = useAppSelector((state) => state.housing); const router = useHistory(); const alert = router.location.state?.alert ?? ''; @@ -115,14 +102,14 @@ function GroupView() { } } - const campaigns = useCampaignList({ + const { data: campaigns } = useFindCampaignsQuery({ filters: { groupIds: [id] } }); if (isLoadingGroup) { - return <>; + return null; } if (!group || !!group.archivedAt) { @@ -163,7 +150,7 @@ function GroupView() { onChangeFilters({ query })} initialQuery={filters.query} placeholder="Rechercher (propriétaire, identifiant fiscal, ref. cadastrale...)" /> @@ -174,7 +161,7 @@ function GroupView() { - + {view === 'map' ? ( diff --git a/frontend/src/views/HousingList/HousingListView.tsx b/frontend/src/views/HousingList/HousingListView.tsx index 8bb545973..2914077fc 100644 --- a/frontend/src/views/HousingList/HousingListView.tsx +++ b/frontend/src/views/HousingList/HousingListView.tsx @@ -11,7 +11,6 @@ import { TrackEventCategories } from '../../models/TrackEvent'; import AppSearchBar from '../../components/_app/AppSearchBar/AppSearchBar'; -import { useFilters } from '../../hooks/useFilters'; import { useDocumentTitle } from '../../hooks/useDocumentTitle'; import { useAppSelector } from '../../hooks/useStore'; import HousingListFiltersSidemenu from '../../components/HousingListFilters/HousingListFiltersSidemenu'; @@ -20,20 +19,23 @@ import HousingListMap from './HousingListMap'; import { HousingDisplaySwitch } from '../../components/HousingDisplaySwitch/HousingDisplaySwitch'; import HousingCreationModal from '../../components/modals/HousingCreationModal/HousingCreationModal'; import { useUser } from '../../hooks/useUser'; +import { useFilters } from '../../hooks/useFiltersNext'; +import { initialHousingFilters } from '../../store/reducers/housingReducer'; const HousingListView = () => { useDocumentTitle('Parc de logements'); const { trackEvent } = useMatomo(); const { - filters, - setFilters, expand, - onChangeFilters, - onResetFilters, + filters, setExpand, - removeFilter - } = useFilters(); + setFilters, + resetFilters: onResetFilters, + removeFilters: removeFilter + } = useFilters({ + initialFilters: initialHousingFilters + }); const { view } = useAppSelector((state) => state.housing); @@ -64,7 +66,7 @@ const HousingListView = () => { setExpand(false)} /> From 4ff486c0ce7ff3cd186cd0774bc06e712abaff05 Mon Sep 17 00:00:00 2001 From: Andrea Gueugnaut Date: Tue, 12 Nov 2024 16:54:50 +0100 Subject: [PATCH 2/4] =?UTF-8?q?fix(frontend):=20group=E2=80=99s=20housings?= =?UTF-8?q?=20should=20refresh=20correctly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .talismanrc | 1 + frontend/src/hooks/useFiltersNext.ts | 22 ++++++++++--------- frontend/src/views/Group/GroupView.tsx | 11 ++++++---- .../src/views/HousingList/HousingListView.tsx | 8 +++---- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/.talismanrc b/.talismanrc index fb1bd5736..b673ae6ae 100644 --- a/.talismanrc +++ b/.talismanrc @@ -31,4 +31,5 @@ allowed_patterns: - keyof - \[key\] - key= +- "key:" version: "1.0" diff --git a/frontend/src/hooks/useFiltersNext.ts b/frontend/src/hooks/useFiltersNext.ts index 0a6e296d5..cad29527f 100644 --- a/frontend/src/hooks/useFiltersNext.ts +++ b/frontend/src/hooks/useFiltersNext.ts @@ -1,4 +1,4 @@ -import { useMap } from 'react-use'; +import { useState } from 'react'; import { HousingFiltersDTO } from '@zerologementvacant/models'; import { useToggle } from './useToggle'; @@ -8,22 +8,24 @@ export interface UseFiltersOptions { } export function useFilters(options: UseFiltersOptions) { - const [filters, { setAll, remove, reset }] = useMap( + const [filters, setFilters] = useState( options.initialFilters ); + + function remove(key: keyof HousingFiltersDTO): void { + setFilters((filters: HousingFiltersDTO) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [key]: _, ...rest } = filters; + return rest; + }); + } + const { active: expand, setActive: setExpand, toggle: toggleExpand } = useToggle(false); - function setFilters(value: HousingFiltersDTO): void { - setAll({ - ...filters, - ...value - }); - } - function removeFilters(value: HousingFiltersDTO): void { Object.keys(value).forEach((key) => { remove(key as keyof HousingFiltersDTO); @@ -31,7 +33,7 @@ export function useFilters(options: UseFiltersOptions) { } function resetFilters(): void { - reset(); + setFilters(options.initialFilters); } return { diff --git a/frontend/src/views/Group/GroupView.tsx b/frontend/src/views/Group/GroupView.tsx index 439e1c5b6..eac07d391 100644 --- a/frontend/src/views/Group/GroupView.tsx +++ b/frontend/src/views/Group/GroupView.tsx @@ -1,5 +1,6 @@ import Alert from '@codegouvfr/react-dsfr/Alert'; import Grid from '@mui/material/Unstable_Grid2'; +import { useEffect } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import { @@ -26,14 +27,15 @@ import { } from '../../services/campaign.service'; import NotFoundView from '../NotFoundView'; import { useFilters } from '../../hooks/useFiltersNext'; -import { useEffect } from 'react'; interface RouterState { alert?: string; } function GroupView() { + const router = useHistory(); const { id } = useParams<{ id: string }>(); + const { data: group, isLoading: isLoadingGroup } = useGetGroupQuery(id); useDocumentTitle(group ? `Groupe - ${group?.title}` : 'Page non trouvée'); @@ -52,12 +54,13 @@ function GroupView() { }); useEffect(() => { - onChangeFilters({ groupIds: [id] }); - }, [id, onChangeFilters]); + onChangeFilters({ + groupIds: [id] + }); + }, [onChangeFilters, id]); const { view } = useAppSelector((state) => state.housing); - const router = useHistory(); const alert = router.location.state?.alert ?? ''; const [removeGroup] = useRemoveGroupMutation(); async function onGroupRemove(): Promise { diff --git a/frontend/src/views/HousingList/HousingListView.tsx b/frontend/src/views/HousingList/HousingListView.tsx index 2914077fc..87cd5ef69 100644 --- a/frontend/src/views/HousingList/HousingListView.tsx +++ b/frontend/src/views/HousingList/HousingListView.tsx @@ -19,7 +19,7 @@ import HousingListMap from './HousingListMap'; import { HousingDisplaySwitch } from '../../components/HousingDisplaySwitch/HousingDisplaySwitch'; import HousingCreationModal from '../../components/modals/HousingCreationModal/HousingCreationModal'; import { useUser } from '../../hooks/useUser'; -import { useFilters } from '../../hooks/useFiltersNext'; +import { useFilters } from '../../hooks/useFilters'; import { initialHousingFilters } from '../../store/reducers/housingReducer'; const HousingListView = () => { @@ -31,10 +31,10 @@ const HousingListView = () => { filters, setExpand, setFilters, - resetFilters: onResetFilters, - removeFilters: removeFilter + onResetFilters, + removeFilter } = useFilters({ - initialFilters: initialHousingFilters + initialState: initialHousingFilters }); const { view } = useAppSelector((state) => state.housing); From b9b66b1e5a5f2b49bf8b62e0c13a2433bc1af7d9 Mon Sep 17 00:00:00 2001 From: Andrea Gueugnaut Date: Tue, 12 Nov 2024 17:46:00 +0100 Subject: [PATCH 3/4] refactor(frontend): remove useFiltersNext; refactor the current useFilters --- frontend/src/hooks/useFilters.tsx | 42 ++-------------- frontend/src/hooks/useFiltersNext.ts | 48 ------------------- frontend/src/views/Group/GroupView.tsx | 15 +++--- .../src/views/HousingList/HousingListView.tsx | 4 +- 4 files changed, 16 insertions(+), 93 deletions(-) delete mode 100644 frontend/src/hooks/useFiltersNext.ts diff --git a/frontend/src/hooks/useFilters.tsx b/frontend/src/hooks/useFilters.tsx index 0ca338598..a75b19d62 100644 --- a/frontend/src/hooks/useFilters.tsx +++ b/frontend/src/hooks/useFilters.tsx @@ -1,10 +1,8 @@ import { useMemo, useState } from 'react'; import housingSlice, { - initialHousingFilters, + initialHousingFilters } from '../store/reducers/housingReducer'; import { HousingFilters } from '../models/HousingFilters'; -import { TrackEventActions, TrackEventCategories } from '../models/TrackEvent'; -import { useMatomo } from '@jonkoops/matomo-tracker-react'; import { useAppDispatch, useAppSelector } from './useStore'; interface FiltersOptions { @@ -17,7 +15,6 @@ interface FiltersOptions { export function useFilters(opts?: FiltersOptions) { const dispatch = useAppDispatch(); - const { trackEvent } = useMatomo(); const initialState = opts?.initialState ?? initialHousingFilters; const storage = opts?.storage ?? 'store'; @@ -31,10 +28,6 @@ export function useFilters(opts?: FiltersOptions) { const [filters, setFilters] = storage === 'store' ? [store.filters, changeFilters] : state; - const establishment = useAppSelector( - (state) => state.authentication.authUser?.establishment, - ); - const { filtersExpanded: expand } = useAppSelector((state) => state.housing); const { expandFilters } = housingSlice.actions; @@ -45,42 +38,17 @@ export function useFilters(opts?: FiltersOptions) { function removeFilter(removed: HousingFilters) { setFilters({ ...filters, - ...removed, + ...removed }); } const length = useMemo(() => Object.keys(filters).length, [filters]); - function onChange(changed: HousingFilters, filterLabel?: string): void { + function onChange(changed: HousingFilters): void { setFilters({ ...filters, - ...changed, + ...changed }); - if (filterLabel) { - trackNewFilter(changed, filterLabel); - } - } - - function trackNewFilter(changedFilters: HousingFilters, filterLabel: string) { - const filterEntry = Object.entries(changedFilters)[0]; - const prevFilterEntry = Object.entries(filters).find( - (_) => _[0] === filterEntry[0], - ); - const filterValues = filterEntry[1] as Array; - const prevFilterValues = prevFilterEntry - ? (prevFilterEntry[1] as Array) - : []; - const newValues = filterValues.filter - ? filterValues.filter((_) => prevFilterValues?.indexOf(_) === -1) - : []; - if (newValues.length) { - trackEvent({ - category: TrackEventCategories.Filter, - action: TrackEventActions.Filter(filterLabel), - name: newValues.toString(), - value: establishment?.siren, - }); - } } function onReset(): void { @@ -95,6 +63,6 @@ export function useFilters(opts?: FiltersOptions) { removeFilter, onChangeFilters: onChange, onResetFilters: onReset, - setExpand, + setExpand }; } diff --git a/frontend/src/hooks/useFiltersNext.ts b/frontend/src/hooks/useFiltersNext.ts deleted file mode 100644 index cad29527f..000000000 --- a/frontend/src/hooks/useFiltersNext.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useState } from 'react'; - -import { HousingFiltersDTO } from '@zerologementvacant/models'; -import { useToggle } from './useToggle'; - -export interface UseFiltersOptions { - initialFilters: HousingFiltersDTO; -} - -export function useFilters(options: UseFiltersOptions) { - const [filters, setFilters] = useState( - options.initialFilters - ); - - function remove(key: keyof HousingFiltersDTO): void { - setFilters((filters: HousingFiltersDTO) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { [key]: _, ...rest } = filters; - return rest; - }); - } - - const { - active: expand, - setActive: setExpand, - toggle: toggleExpand - } = useToggle(false); - - function removeFilters(value: HousingFiltersDTO): void { - Object.keys(value).forEach((key) => { - remove(key as keyof HousingFiltersDTO); - }); - } - - function resetFilters(): void { - setFilters(options.initialFilters); - } - - return { - expand, - filters, - setExpand, - toggleExpand, - setFilters, - removeFilters, - resetFilters - }; -} diff --git a/frontend/src/views/Group/GroupView.tsx b/frontend/src/views/Group/GroupView.tsx index eac07d391..b6eeba9f3 100644 --- a/frontend/src/views/Group/GroupView.tsx +++ b/frontend/src/views/Group/GroupView.tsx @@ -26,14 +26,13 @@ import { useFindCampaignsQuery } from '../../services/campaign.service'; import NotFoundView from '../NotFoundView'; -import { useFilters } from '../../hooks/useFiltersNext'; +import { useFilters } from '../../hooks/useFilters'; interface RouterState { alert?: string; } function GroupView() { - const router = useHistory(); const { id } = useParams<{ id: string }>(); const { data: group, isLoading: isLoadingGroup } = useGetGroupQuery(id); @@ -42,13 +41,14 @@ function GroupView() { const { filters, - expand, - removeFilters, - setExpand, + removeFilter: removeFilters, setFilters: onChangeFilters, - resetFilters: onResetFilters + onResetFilters, + expand, + setExpand } = useFilters({ - initialFilters: { + storage: 'state', + initialState: { groupIds: [id] } }); @@ -61,6 +61,7 @@ function GroupView() { const { view } = useAppSelector((state) => state.housing); + const router = useHistory(); const alert = router.location.state?.alert ?? ''; const [removeGroup] = useRemoveGroupMutation(); async function onGroupRemove(): Promise { diff --git a/frontend/src/views/HousingList/HousingListView.tsx b/frontend/src/views/HousingList/HousingListView.tsx index 87cd5ef69..08804184d 100644 --- a/frontend/src/views/HousingList/HousingListView.tsx +++ b/frontend/src/views/HousingList/HousingListView.tsx @@ -31,9 +31,11 @@ const HousingListView = () => { filters, setExpand, setFilters, + onChangeFilters, onResetFilters, removeFilter } = useFilters({ + storage: 'store', initialState: initialHousingFilters }); @@ -66,7 +68,7 @@ const HousingListView = () => { setExpand(false)} /> From 4f6747282e966d8ce3e11cf9320db21c57994575 Mon Sep 17 00:00:00 2001 From: Andrea Gueugnaut Date: Wed, 13 Nov 2024 13:14:46 +0100 Subject: [PATCH 4/4] fix(frontend): use the correct filter handlers --- frontend/src/views/Group/GroupView.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/Group/GroupView.tsx b/frontend/src/views/Group/GroupView.tsx index b6eeba9f3..abafd311c 100644 --- a/frontend/src/views/Group/GroupView.tsx +++ b/frontend/src/views/Group/GroupView.tsx @@ -42,7 +42,8 @@ function GroupView() { const { filters, removeFilter: removeFilters, - setFilters: onChangeFilters, + setFilters, + onChangeFilters, onResetFilters, expand, setExpand @@ -54,10 +55,10 @@ function GroupView() { }); useEffect(() => { - onChangeFilters({ + setFilters({ groupIds: [id] }); - }, [onChangeFilters, id]); + }, [setFilters, id]); const { view } = useAppSelector((state) => state.housing);