diff --git a/frontend/package.json b/frontend/package.json index 933d6656f..c96e644fe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,8 +40,8 @@ "@raruto/leaflet-elevation": "1.7.0", "@react-hook/resize-observer": "^1.2.6", "@sentry/nextjs": "^7.12.1", - "@tanstack/react-query": "^4.10.3", - "@tanstack/react-query-devtools": "^4.11.0", + "@tanstack/react-query": "^5.51.1", + "@tanstack/react-query-devtools": "^5.51.1", "@zeit/next-source-maps": "^0.0.4-canary.1", "autoprefixer": "^10.4.14", "axios": "0.25.0", diff --git a/frontend/src/components/ActivitySearchFilter/useActivitySearchFilter.ts b/frontend/src/components/ActivitySearchFilter/useActivitySearchFilter.ts index 7501210f2..481f12ac4 100644 --- a/frontend/src/components/ActivitySearchFilter/useActivitySearchFilter.ts +++ b/frontend/src/components/ActivitySearchFilter/useActivitySearchFilter.ts @@ -10,9 +10,10 @@ const { activityBar } = getHomePageConfig(); export const useActivitySearchFilter = () => { const language = useRouter().locale ?? getDefaultLanguage(); - const { data: activities } = useQuery(['homeActivities', language], () => - getActivityBarContent(language, activityBar.links), - ); + const { data: activities } = useQuery({ + queryKey: ['homeActivities', language], + queryFn: () => getActivityBarContent(language, activityBar.links), + }); const [expandedState, setExpandedState] = useState<'EXPANDED' | 'COLLAPSED'>('COLLAPSED'); diff --git a/frontend/src/components/Footer/useFooter.ts b/frontend/src/components/Footer/useFooter.ts index 3c098620b..2b79064da 100644 --- a/frontend/src/components/Footer/useFooter.ts +++ b/frontend/src/components/Footer/useFooter.ts @@ -20,13 +20,11 @@ export const useFooter = (): { config: FooterConfigOutput; intl: IntlShape } => const language = useRouter().locale ?? getDefaultLanguage(); const containsInformationIDLinks = links.some(link => 'informationID' in link); - const { data = [] } = useQuery( - ['footer', language], - () => getFlatPages(language), - { - enabled: containsInformationIDLinks, - }, - ); + const { data = [] } = useQuery({ + queryKey: ['footer', language], + queryFn: () => getFlatPages(language), + enabled: containsInformationIDLinks, + }); let nextLinks; // If the footer config contains `informationID` keys,the app retrieves "flatpages" to get the corresponding label/url if (containsInformationIDLinks === true) { diff --git a/frontend/src/components/Header/useHeader.ts b/frontend/src/components/Header/useHeader.ts index bc1be59d2..8c33f32c9 100644 --- a/frontend/src/components/Header/useHeader.ts +++ b/frontend/src/components/Header/useHeader.ts @@ -15,7 +15,10 @@ export const useHeader = (menuNode: MutableRefObject( - ['header', language], - () => (is2_104LowerOrEqualCurrentAPIVersion ? geMenuItems(language) : getFlatPages(language)), - { - enabled: is2_104LowerOrEqualCurrentAPIVersion !== null, - staleTime: ONE_DAY, - }, - ); + const { data: menuItems } = useQuery({ + queryKey: ['header', language], + queryFn: () => + is2_104LowerOrEqualCurrentAPIVersion ? geMenuItems(language) : getFlatPages(language), + enabled: is2_104LowerOrEqualCurrentAPIVersion !== null, + staleTime: ONE_DAY, + }); const intl = useIntl(); const { height = 0 } = useSize(menuNode) ?? {}; diff --git a/frontend/src/components/Map/hooks/usePopupResult.ts b/frontend/src/components/Map/hooks/usePopupResult.ts index 2b3d33d4b..242e1e280 100644 --- a/frontend/src/components/Map/hooks/usePopupResult.ts +++ b/frontend/src/components/Map/hooks/usePopupResult.ts @@ -28,11 +28,11 @@ export const usePopupResult = (id: string, shouldFetch: boolean, type: ContentTy throw new Error('Incorrect type'); }; - const { data: trekPopupResult, isLoading } = useQuery( - ['popupResult', id, language], - fetchData, - { enabled: type !== null && shouldFetch && commonDictionaries !== undefined }, - ); + const { data: trekPopupResult, isLoading } = useQuery({ + queryKey: ['popupResult', type, id, language], + queryFn: fetchData, + enabled: type !== null && shouldFetch && commonDictionaries !== undefined, + }); return { trekPopupResult, diff --git a/frontend/src/components/Map/hooks/useTrekGeometry.ts b/frontend/src/components/Map/hooks/useTrekGeometry.ts index f15aa97b1..bc9864d89 100644 --- a/frontend/src/components/Map/hooks/useTrekGeometry.ts +++ b/frontend/src/components/Map/hooks/useTrekGeometry.ts @@ -14,23 +14,18 @@ export const useObjectGeometry = ( ) => { const language = useRouter().locale ?? getDefaultLanguage(); - const func = () => { - if (type === 'TREK') { - return getTrekGeometryResult; - } - if (type === 'OUTDOOR_SITE') { - return getOutdoorSiteGeometryResult; - } - if (type === 'TOURISTIC_EVENT') { - return getTouristicEventGeometryResult; - } - return getTouristicContentGeometryResult; + const getterByType = { + TREK: getTrekGeometryResult, + OUTDOOR_SITE: getOutdoorSiteGeometryResult, + TOURISTIC_EVENT: getTouristicEventGeometryResult, + TOURISTIC_CONTENT: getTouristicContentGeometryResult, }; + const getter = getterByType[type]; - const { data: trekGeometry } = useQuery( - ['trekPopupResult', id, language], - () => func()(String(id), language), - ); + const { data: trekGeometry } = useQuery({ + queryKey: ['trekPopupResult', type, id, language], + queryFn: () => getter?.(String(id), language), + }); return { trekGeometry }; }; diff --git a/frontend/src/components/pages/details/useDetails.tsx b/frontend/src/components/pages/details/useDetails.tsx index 20e70b362..f09dd9658 100644 --- a/frontend/src/components/pages/details/useDetails.tsx +++ b/frontend/src/components/pages/details/useDetails.tsx @@ -3,11 +3,8 @@ import { Details, TrekFamily } from 'modules/details/interface'; import { getDetails, getTrekFamily } from 'modules/details/connector'; import { isUrlString } from 'modules/utils/string'; import { ONE_DAY } from 'services/constants/staleTime'; -import { isRessourceMissing } from 'services/routeUtils'; import { useCallback, useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; -import { useRouter } from 'next/router'; -import { routes } from 'services/routes'; import { useMediaPredicate } from 'react-media-hook'; import useSectionsReferences from 'hooks/useSectionsReferences'; import { useQueryCommonDictionaries } from 'modules/dictionaries/api'; @@ -43,7 +40,6 @@ export const useDetails = ( ) => { const id = isUrlString(slug) ? slug.split('-')[0] : ''; const path = isUrlString(slug) ? decodeURI(slug) : ''; - const router = useRouter(); const commonDictionaries = useQueryCommonDictionaries(language); @@ -51,31 +47,22 @@ export const useDetails = ( data: details, refetch, isLoading, - } = useQuery( - ['details', id, language], - () => getDetails(id, language, commonDictionaries), - { - enabled: isUrlString(slug) && commonDictionaries !== undefined, - onError: error => { - if (isRessourceMissing(error)) { - void router.push(routes.HOME); - } - }, - staleTime: ONE_DAY, - }, - ); + } = useQuery({ + queryKey: ['details', id, language], + queryFn: () => getDetails(id, language, commonDictionaries), + enabled: isUrlString(slug) && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const isMobile = useMediaPredicate('(max-width: 1024px)'); const parentIdString = isUrlString(parentId) ? parentId : ''; - const { data: trekFamily } = useQuery( - ['trekFamily', parentIdString, language], - () => getTrekFamily(isUrlString(parentId) ? parentId : '', language), - { - enabled: isUrlString(parentId), - staleTime: ONE_DAY, - }, - ); + const { data: trekFamily } = useQuery({ + queryKey: ['trekFamily', parentIdString, language], + queryFn: () => getTrekFamily(parentIdString, language), + enabled: isUrlString(parentId), + staleTime: ONE_DAY, + }); const { sections } = getDetailsConfig(language); diff --git a/frontend/src/components/pages/flatPage/useFlatPage.tsx b/frontend/src/components/pages/flatPage/useFlatPage.tsx index 2e674f22c..19cb14ae0 100644 --- a/frontend/src/components/pages/flatPage/useFlatPage.tsx +++ b/frontend/src/components/pages/flatPage/useFlatPage.tsx @@ -22,25 +22,23 @@ export const useFlatPage = (flatPageUrl: string | undefined) => { refetch, isLoading, error, - } = useQuery( - ['flatPageDetails', id, language], - () => getFlatPageDetails(id, language, commonDictionaries), - { - enabled: isUrlString(flatPageUrl) && commonDictionaries !== undefined, - staleTime: ONE_DAY, - }, - ); + } = useQuery({ + queryKey: ['flatPageDetails', id, language], + queryFn: () => getFlatPageDetails(id, language, commonDictionaries), + enabled: isUrlString(flatPageUrl) && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const suggestions = getSuggestionsFromContent(flatPage?.content ?? ''); const activitySuggestionIds = suggestions.flatMap(suggestion => 'ids' in suggestion ? suggestion.ids : [suggestion.type], ); - const { data: activitySuggestions = [] } = useQuery( - ['activitySuggestions', ...activitySuggestionIds, id, language], - () => getActivitySuggestions(suggestions, language, commonDictionaries), - { enabled: suggestions.length > 0 && commonDictionaries !== undefined }, - ); + const { data: activitySuggestions = [] } = useQuery({ + queryKey: ['activitySuggestions', activitySuggestionIds, language], + queryFn: () => getActivitySuggestions(suggestions, language, commonDictionaries), + enabled: suggestions.length > 0 && commonDictionaries !== undefined, + }); return { id, diff --git a/frontend/src/components/pages/home/useHome.ts b/frontend/src/components/pages/home/useHome.ts index 9f83e47c9..ccea4c93a 100644 --- a/frontend/src/components/pages/home/useHome.ts +++ b/frontend/src/components/pages/home/useHome.ts @@ -22,11 +22,11 @@ export const useHome = (): UseHome => { const commonDictionaries = useQueryCommonDictionaries(language); - const { data = [] } = useQuery( - ['activitySuggestions', `Suggestion-${activitySuggestionIds.join('-')}`, language], - () => getActivitySuggestions(suggestions, language, commonDictionaries), - { enabled: suggestions.length > 0 && commonDictionaries !== undefined }, - ); + const { data = [] } = useQuery({ + queryKey: ['activitySuggestions', activitySuggestionIds, language], + queryFn: () => getActivitySuggestions(suggestions, language, commonDictionaries), + enabled: suggestions.length > 0 && commonDictionaries !== undefined, + }); return { config: homePageConfig, suggestions: data }; }; diff --git a/frontend/src/components/pages/search/components/FilterBar/useFilterBar.tsx b/frontend/src/components/pages/search/components/FilterBar/useFilterBar.tsx index 8d84e705d..51a8ad8bd 100644 --- a/frontend/src/components/pages/search/components/FilterBar/useFilterBar.tsx +++ b/frontend/src/components/pages/search/components/FilterBar/useFilterBar.tsx @@ -21,10 +21,10 @@ import { FormattedMessage } from 'react-intl'; export const useFilterBar = () => { const { groupTreksAndOutdoorFilters, enableOutdoor } = getGlobalConfig(); - const { data: currentAPIVersion } = useQuery( - ['APIVersion'], - getAPIVersion, - ); + const { data: currentAPIVersion } = useQuery({ + queryKey: ['APIVersion'], + queryFn: getAPIVersion, + }); const is2_108LowerOrEqualCurrentAPIVersion = isLowerOrEqualCurrentAPIVersion( '2.108.0', diff --git a/frontend/src/components/pages/search/components/useFilters.ts b/frontend/src/components/pages/search/components/useFilters.ts index 9fa0bb11a..7bbce76cb 100644 --- a/frontend/src/components/pages/search/components/useFilters.ts +++ b/frontend/src/components/pages/search/components/useFilters.ts @@ -29,7 +29,10 @@ export const useFilter = () => { organizerEvent: FilterWithoutType | null; }, Error - >(['initialFilterState', language], () => getInitialFilters(language, initialOptions)); + >({ + queryKey: ['initialFilterState', language], + queryFn: () => getInitialFilters(language, initialOptions), + }); const initialFiltersState = data ? data.initialFiltersState : []; const touristicContentCategoryMapping = data ? data.touristicContentCategoryMapping : {}; diff --git a/frontend/src/components/pages/search/hooks/useCounter.ts b/frontend/src/components/pages/search/hooks/useCounter.ts index 988c198e8..d7764da16 100644 --- a/frontend/src/components/pages/search/hooks/useCounter.ts +++ b/frontend/src/components/pages/search/hooks/useCounter.ts @@ -19,9 +19,9 @@ interface CountResult { const useCounter = ({ language }: Args): CountResult => { const commonDictionaries = useQueryCommonDictionaries(language); - const result = useQuery( - ['counter'], - ({ + const result = useQuery({ + queryKey: ['counter'], + queryFn: ({ pageParam = { treks: 1, touristicContents: 1, @@ -40,12 +40,10 @@ const useCounter = ({ language }: Args): CountResult => { language, commonDictionaries, ), - { - refetchOnReconnect: false, - refetchOnWindowFocus: false, - staleTime: ONE_DAY, - }, - ); + refetchOnReconnect: false, + refetchOnWindowFocus: false, + staleTime: ONE_DAY, + }); return { treksCount: result?.data?.resultsNumberDetails?.treksCount ?? 0, diff --git a/frontend/src/components/pages/search/hooks/useMapResults.ts b/frontend/src/components/pages/search/hooks/useMapResults.ts index f70923008..78cecf09d 100644 --- a/frontend/src/components/pages/search/hooks/useMapResults.ts +++ b/frontend/src/components/pages/search/hooks/useMapResults.ts @@ -22,11 +22,17 @@ export const useMapResults = ( const parsedFiltersState = parseFilters(filtersState); - const { data: mapResults, isLoading: isMapLoading } = useQuery( - ['mapResults', parsedFiltersState, language, parseTextFilter(textFilterState), dateFilter], - () => + const { data: mapResults, isLoading: isMapLoading } = useQuery({ + queryKey: [ + 'mapResults', + parsedFiltersState, + language, + parseTextFilter(textFilterState), + dateFilter, + ], + queryFn: () => getMapResults({ filtersState: parsedFiltersState, textFilterState, dateFilter }, language), - ); + }); return { isMapLoading, diff --git a/frontend/src/components/pages/search/hooks/useTrekResults.ts b/frontend/src/components/pages/search/hooks/useTrekResults.ts index 90709ae07..d095d62a3 100644 --- a/frontend/src/components/pages/search/hooks/useTrekResults.ts +++ b/frontend/src/components/pages/search/hooks/useTrekResults.ts @@ -77,17 +77,17 @@ export const useTrekResults = ( hasNextPage, hasPreviousPage, isFetchingNextPage, - } = useInfiniteQuery( - [ + } = useInfiniteQuery({ + queryKey: [ 'trekResults', - JSON.stringify(parsedFiltersState), + parsedFiltersState, language, parseTextFilter(textFilterState), parseBboxFilter(bboxState), - JSON.stringify(dateFilter), + dateFilter, page, ], - ({ + queryFn: ({ pageParam = { treks: page, touristicContents: page, @@ -102,30 +102,34 @@ export const useTrekResults = ( commonDictionaries, ); }, - { - retry: false, - // We already have a fallback component to allow the user to refetch - // Leaving these on induced issues with our refetching only next page strategy - // When it refetched on reconnect/focus the infinite scroll then stopped working - refetchOnReconnect: false, - refetchOnWindowFocus: false, - // hasNextPage will be set to false if getNextPageParam returns undefined - getNextPageParam: lastPageResult => - lastPageResult.nextPages.treks !== null || - lastPageResult.nextPages.touristicContents !== null || - lastPageResult.nextPages.outdoorSites !== null || - lastPageResult.nextPages.touristicEvents !== null - ? lastPageResult.nextPages - : undefined, - getPreviousPageParam: lastPageResult => - lastPageResult.previousPages.treks !== null || - lastPageResult.previousPages.touristicContents !== null || - lastPageResult.previousPages.outdoorSites !== null || - lastPageResult.previousPages.touristicEvents !== null - ? lastPageResult.previousPages - : undefined, + initialPageParam: { + treks: page, + touristicContents: page, + outdoorSites: getGlobalConfig().enableOutdoor ? page : null, + touristicEvents: getGlobalConfig().enableTouristicEvents ? page : null, }, - ); + retry: false, + // We already have a fallback component to allow the user to refetch + // Leaving these on induced issues with our refetching only next page strategy + // When it refetched on reconnect/focus the infinite scroll then stopped working + refetchOnReconnect: false, + refetchOnWindowFocus: false, + // hasNextPage will be set to false if getNextPageParam returns undefined + getNextPageParam: lastPageResult => + lastPageResult.nextPages.treks !== null || + lastPageResult.nextPages.touristicContents !== null || + lastPageResult.nextPages.outdoorSites !== null || + lastPageResult.nextPages.touristicEvents !== null + ? lastPageResult.nextPages + : undefined, + getPreviousPageParam: lastPageResult => + lastPageResult.previousPages.treks !== null || + lastPageResult.previousPages.touristicContents !== null || + lastPageResult.previousPages.outdoorSites !== null || + lastPageResult.previousPages.touristicEvents !== null + ? lastPageResult.previousPages + : undefined, + }); useEffect(() => { const url = computeUrl(filtersState, textFilterState, dateFilter); diff --git a/frontend/src/components/pages/site/useOutdoorCourse.tsx b/frontend/src/components/pages/site/useOutdoorCourse.tsx index 4afbb87da..2d0325e2d 100644 --- a/frontend/src/components/pages/site/useOutdoorCourse.tsx +++ b/frontend/src/components/pages/site/useOutdoorCourse.tsx @@ -2,9 +2,6 @@ import useSectionsReferences from 'hooks/useSectionsReferences'; import { isUrlString } from 'modules/utils/string'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; -import { useRouter } from 'next/router'; -import { isRessourceMissing } from 'services/routeUtils'; -import { routes } from 'services/routes'; import { ONE_DAY } from 'services/constants/staleTime'; import { useQueryCommonDictionaries } from 'modules/dictionaries/api'; import { DetailsSections } from '../details/useDetails'; @@ -19,23 +16,14 @@ export const useOutdoorCourse = ( const id = isUrlString(outdoorCourseUrl) ? outdoorCourseUrl.split('-')[0] : ''; const path = isUrlString(outdoorCourseUrl) ? decodeURI(outdoorCourseUrl) : ''; - const router = useRouter(); - const commonDictionaries = useQueryCommonDictionaries(language); - const { data, refetch, isLoading } = useQuery( - ['outdoorCourseDetails', id, language], - () => getOutdoorCourseDetails(id, language, commonDictionaries), - { - enabled: isUrlString(outdoorCourseUrl) && commonDictionaries !== undefined, - onError: error => { - if (isRessourceMissing(error)) { - void router.push(routes.HOME); - } - }, - staleTime: ONE_DAY, - }, - ); + const { data, refetch, isLoading } = useQuery({ + queryKey: ['outdoorCourseDetails', id, language], + queryFn: () => getOutdoorCourseDetails(id, language, commonDictionaries), + enabled: isUrlString(outdoorCourseUrl) && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const { sections } = getDetailsConfig(language); diff --git a/frontend/src/components/pages/site/useOutdoorSite.tsx b/frontend/src/components/pages/site/useOutdoorSite.tsx index 2271cdf4f..a7a8c001e 100644 --- a/frontend/src/components/pages/site/useOutdoorSite.tsx +++ b/frontend/src/components/pages/site/useOutdoorSite.tsx @@ -3,9 +3,6 @@ import { isUrlString } from 'modules/utils/string'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { ONE_DAY } from 'services/constants/staleTime'; -import { isRessourceMissing } from 'services/routeUtils'; -import { useRouter } from 'next/router'; -import { routes } from 'services/routes'; import { useQueryCommonDictionaries } from 'modules/dictionaries/api'; import { getOutdoorSiteDetails } from '../../../modules/outdoorSite/connector'; import { OutdoorSiteDetails } from '../../../modules/outdoorSite/interface'; @@ -15,23 +12,15 @@ import { DetailsSections } from '../details/useDetails'; export const useOutdoorSite = (outdoorSiteUrl: string | string[] | undefined, language: string) => { const id = isUrlString(outdoorSiteUrl) ? outdoorSiteUrl.split('-')[0] : ''; const path = isUrlString(outdoorSiteUrl) ? decodeURI(outdoorSiteUrl) : ''; - const router = useRouter(); const commonDictionaries = useQueryCommonDictionaries(language); - const { data, refetch, isLoading } = useQuery( - ['outdoorSiteDetails', id, language], - () => getOutdoorSiteDetails(id, language, commonDictionaries), - { - enabled: isUrlString(outdoorSiteUrl) && commonDictionaries !== undefined, - onError: error => { - if (isRessourceMissing(error)) { - void router.push(routes.HOME); - } - }, - staleTime: ONE_DAY, - }, - ); + const { data, refetch, isLoading } = useQuery({ + queryKey: ['outdoorSiteDetails', id, language], + queryFn: () => getOutdoorSiteDetails(id, language, commonDictionaries), + enabled: isUrlString(outdoorSiteUrl) && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const { sections } = getDetailsConfig(language); diff --git a/frontend/src/components/pages/touristicContent/useTouristicContent.tsx b/frontend/src/components/pages/touristicContent/useTouristicContent.tsx index e4d44014a..0579ddc1e 100644 --- a/frontend/src/components/pages/touristicContent/useTouristicContent.tsx +++ b/frontend/src/components/pages/touristicContent/useTouristicContent.tsx @@ -3,10 +3,7 @@ import { TouristicContentDetails } from 'modules/touristicContent/interface'; import { isUrlString } from 'modules/utils/string'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; -import { isRessourceMissing } from 'services/routeUtils'; -import { useRouter } from 'next/router'; import { ONE_DAY } from 'services/constants/staleTime'; -import { routes } from 'services/routes'; import useSectionsReferences from 'hooks/useSectionsReferences'; import { useQueryCommonDictionaries } from 'modules/dictionaries/api'; import { DetailsSections } from '../details/useDetails'; @@ -20,23 +17,15 @@ export const useTouristicContent = ( const id = isTouristicContentUrlString ? touristicContentUrl.split('-')[0] : ''; const path = isTouristicContentUrlString ? decodeURI(touristicContentUrl) : ''; - const router = useRouter(); const commonDictionaries = useQueryCommonDictionaries(language); - const { data, refetch, isLoading } = useQuery( - ['touristicContentDetails', id, language], - () => getTouristicContentDetails(id, language, commonDictionaries), - { - enabled: isTouristicContentUrlString && commonDictionaries !== undefined, - onError: error => { - if (isRessourceMissing(error)) { - void router.push(routes.HOME); - } - }, - staleTime: ONE_DAY, - }, - ); + const { data, refetch, isLoading } = useQuery({ + queryKey: ['touristicContentDetails', id, language], + queryFn: () => getTouristicContentDetails(id, language, commonDictionaries), + enabled: isTouristicContentUrlString && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const { sections } = getDetailsConfig(language); const sectionsTouristicContent = sections.touristicEvent.filter( diff --git a/frontend/src/components/pages/touristicEvent/useTouristicEvent.tsx b/frontend/src/components/pages/touristicEvent/useTouristicEvent.tsx index d9e84823b..070ce2baf 100644 --- a/frontend/src/components/pages/touristicEvent/useTouristicEvent.tsx +++ b/frontend/src/components/pages/touristicEvent/useTouristicEvent.tsx @@ -1,10 +1,7 @@ import { useState } from 'react'; import { isUrlString } from 'modules/utils/string'; import { useQuery } from '@tanstack/react-query'; -import { isRessourceMissing } from 'services/routeUtils'; -import { useRouter } from 'next/router'; import { ONE_DAY } from 'services/constants/staleTime'; -import { routes } from 'services/routes'; import useSectionsReferences from 'hooks/useSectionsReferences'; import { useQueryCommonDictionaries } from 'modules/dictionaries/api'; import { getTouristicEventDetails } from '../../../modules/touristicEvent/connector'; @@ -18,23 +15,15 @@ export const useTouristicEvent = ( ) => { const id = isUrlString(touristicEventUrl) ? touristicEventUrl.split('-')[0] : ''; const path = isUrlString(touristicEventUrl) ? decodeURI(touristicEventUrl) : ''; - const router = useRouter(); const commonDictionaries = useQueryCommonDictionaries(language); - const { data, refetch, isLoading } = useQuery( - ['outdoorCourseDetails', id, language], - () => getTouristicEventDetails(id, language, commonDictionaries), - { - enabled: isUrlString(touristicEventUrl) && commonDictionaries !== undefined, - onError: error => { - if (isRessourceMissing(error)) { - void router.push(routes.HOME); - } - }, - staleTime: ONE_DAY, - }, - ); + const { data, refetch, isLoading } = useQuery({ + queryKey: ['outdoorCourseDetails', id, language], + queryFn: () => getTouristicEventDetails(id, language, commonDictionaries), + enabled: isUrlString(touristicEventUrl) && commonDictionaries !== undefined, + staleTime: ONE_DAY, + }); const { sections } = getDetailsConfig(language); const sectionsTouristicEvent = sections.touristicEvent.filter(({ display }) => display === true); diff --git a/frontend/src/modules/dictionaries/api.ts b/frontend/src/modules/dictionaries/api.ts index 8f1bf9f15..d75109086 100644 --- a/frontend/src/modules/dictionaries/api.ts +++ b/frontend/src/modules/dictionaries/api.ts @@ -4,12 +4,10 @@ import { CommonDictionaries } from './interface'; import { getCommonDictionaries } from './connector'; export const useQueryCommonDictionaries = (language: string) => { - const { data } = useQuery( - ['commonDictionaries', language], - () => getCommonDictionaries(language), - { - staleTime: ONE_DAY / 2, - }, - ); + const { data } = useQuery({ + queryKey: ['commonDictionaries', language], + queryFn: () => getCommonDictionaries(language), + staleTime: ONE_DAY / 2, + }); return data; }; diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index b1a9f6082..b81de304b 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -1,7 +1,7 @@ import { AppProps } from 'next/app'; import { Root } from 'components/pages/_app/Root'; -import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ONE_MINUTE } from 'services/constants/staleTime'; import '../styles/global.css'; @@ -32,7 +32,7 @@ const MyApp: React.FC = ({ Component, pageProps }) => { return ( - + @@ -41,7 +41,7 @@ const MyApp: React.FC = ({ Component, pageProps }) => { - + ); }; diff --git a/frontend/src/pages/event/[touristicEvent].tsx b/frontend/src/pages/event/[touristicEvent].tsx index 520a82348..510e50cf8 100644 --- a/frontend/src/pages/event/[touristicEvent].tsx +++ b/frontend/src/pages/event/[touristicEvent].tsx @@ -1,11 +1,13 @@ import { TouristicEventUI } from 'components/pages/touristicEvent/TouristicEventUI'; import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { getDefaultLanguage } from 'modules/header/utills'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { routes } from 'services/routes'; import { redirectIfWrongUrl } from 'modules/utils/url'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; +import { isRessourceMissing } from 'services/routeUtils'; +import { TouristicEventDetails } from 'modules/touristicEvent/interface'; import { getTouristicEventDetails } from '../../modules/touristicEvent/connector'; import { isUrlString } from '../../modules/utils/string'; import Custom404 from '../404'; @@ -16,24 +18,40 @@ export const getServerSideProps: GetServerSideProps = async context => { : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); try { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - const details = await getTouristicEventDetails(id, locale, commonDictionaries); - await queryClient.prefetchQuery(['touristicEventDetails', id, locale], () => details); + await queryClient.prefetchQuery({ + queryKey: ['touristicEventDetails', id, locale], + queryFn: () => getTouristicEventDetails(id, locale, commonDictionaries), + }); + + const details = queryClient.getQueryData(['touristicEventDetails', id, locale]); - const redirect = redirectIfWrongUrl( - id, - details.name, - { ...context, locale }, - routes.TOURISTIC_EVENT, - ); - if (redirect) - return { - redirect, - }; + if (details !== undefined) { + const redirect = redirectIfWrongUrl( + id, + details.name, + { ...context, locale }, + routes.TOURISTIC_EVENT, + ); + if (redirect) + return { + redirect, + }; + } return { props: { @@ -54,9 +72,9 @@ interface Props { } const TouristicEvent: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { touristicEvent } = router.query; - const language = router.locale ?? getDefaultLanguage(); + const { query, locale } = useRouter(); + const { touristicEvent } = query; + const language = locale ?? getDefaultLanguage(); if (errorCode === 404) return ; diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 07614ef64..95d70f988 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -15,23 +15,26 @@ export const getServerSideProps: GetServerSideProps = async context => { const suggestions = adaptSuggestions(homePageConfig.suggestions, locale); if (suggestions !== null) { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); const activitySuggestionIds = suggestions.flatMap(suggestion => 'ids' in suggestion ? suggestion.ids : [suggestion.type], ); - await queryClient.prefetchQuery( - ['activitySuggestions', `Suggestion-${activitySuggestionIds.join('-')}`, locale], - () => getActivitySuggestions(suggestions, locale, commonDictionaries), - ); + await queryClient.prefetchQuery({ + queryKey: ['activitySuggestions', activitySuggestionIds, locale], + queryFn: () => getActivitySuggestions(suggestions, locale, commonDictionaries), + }); } if (homePageConfig.activityBar.shouldDisplay === true) { - await queryClient.prefetchQuery(['homeActivities', locale], () => - getActivityBarContent(locale, homePageConfig.activityBar.links), - ); + await queryClient.prefetchQuery({ + queryKey: ['homeActivities', locale], + queryFn: () => getActivityBarContent(locale, homePageConfig.activityBar.links), + }); } return { diff --git a/frontend/src/pages/information/[flatPage].tsx b/frontend/src/pages/information/[flatPage].tsx index 047151d27..f95b93dab 100644 --- a/frontend/src/pages/information/[flatPage].tsx +++ b/frontend/src/pages/information/[flatPage].tsx @@ -1,7 +1,7 @@ import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { FlatPageUI } from 'components/pages/flatPage'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { routes } from 'services/routes'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; import { getActivitySuggestions } from 'modules/activitySuggestions/connector'; @@ -9,6 +9,8 @@ import { getFlatPageDetails } from 'modules/flatpage/connector'; import { getSuggestionsFromContent } from 'modules/flatpage/utils'; import { isUrlString } from 'modules/utils/string'; import { redirectIfWrongUrl } from 'modules/utils/url'; +import { isRessourceMissing } from 'services/routeUtils'; +import { FlatPageDetails } from 'modules/flatpage/interface'; import Custom404 from '../404'; export const getServerSideProps: GetServerSideProps = async context => { @@ -16,35 +18,51 @@ export const getServerSideProps: GetServerSideProps = async context => { const id = isUrlString(context.query.flatPage) ? context.query.flatPage.split('-')[0] : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - const details = await getFlatPageDetails(id, locale, commonDictionaries); - await queryClient.prefetchQuery(['flatPageDetails', id, locale], () => details); + await queryClient.prefetchQuery({ + queryKey: ['flatPageDetails', id, locale], + queryFn: () => getFlatPageDetails(id, locale, commonDictionaries), + }); - const suggestions = getSuggestionsFromContent(details.content); + const details = queryClient.getQueryData(['flatPageDetails', id, locale]); - const activitySuggestionIds = suggestions.flatMap(suggestion => - 'ids' in suggestion ? suggestion.ids : [suggestion.type], - ); + if (details !== undefined) { + const suggestions = getSuggestionsFromContent(details.content); - await queryClient.prefetchQuery( - ['activitySuggestions', ...activitySuggestionIds, id, locale], - () => getActivitySuggestions(suggestions, locale, commonDictionaries), - ); + const activitySuggestionIds = suggestions.flatMap(suggestion => + 'ids' in suggestion ? suggestion.ids : [suggestion.type], + ); - const redirect = redirectIfWrongUrl( - id, - details.title, - { ...context, locale }, - routes.FLAT_PAGE, - ); - if (redirect) - return { - redirect, - }; + await queryClient.prefetchQuery({ + queryKey: ['activitySuggestions', activitySuggestionIds, locale], + queryFn: () => getActivitySuggestions(suggestions, locale, commonDictionaries), + }); + + const redirect = redirectIfWrongUrl( + id, + details.title, + { ...context, locale }, + routes.FLAT_PAGE, + ); + if (redirect) + return { + redirect, + }; + } return { props: { @@ -65,8 +83,8 @@ interface Props { } const TouristicContent: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { flatPage } = router.query; + const { query } = useRouter(); + const { flatPage } = query; if (errorCode === 404 || flatPage === undefined) return ; diff --git a/frontend/src/pages/outdoor-course/[outdoorCourse].tsx b/frontend/src/pages/outdoor-course/[outdoorCourse].tsx index 0c4614c8a..f8b388248 100644 --- a/frontend/src/pages/outdoor-course/[outdoorCourse].tsx +++ b/frontend/src/pages/outdoor-course/[outdoorCourse].tsx @@ -1,13 +1,15 @@ import { OutdoorCourseUI } from 'components/pages/site/OutdoorCourseUI'; import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { getDefaultLanguage } from 'modules/header/utills'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { routes } from 'services/routes'; import { redirectIfWrongUrl } from 'modules/utils/url'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; -import { getOutdoorCourseDetails } from '../../modules/outdoorCourse/connector'; -import { isUrlString } from '../../modules/utils/string'; +import { isRessourceMissing } from 'services/routeUtils'; +import { OutdoorCourseDetails } from 'modules/outdoorCourse/interface'; +import { getOutdoorCourseDetails } from 'modules/outdoorCourse/connector'; +import { isUrlString } from 'modules/utils/string'; import Custom404 from '../404'; export const getServerSideProps: GetServerSideProps = async context => { @@ -16,25 +18,40 @@ export const getServerSideProps: GetServerSideProps = async context => { : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); try { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - const details = await getOutdoorCourseDetails(id, locale, commonDictionaries); - await queryClient.prefetchQuery(['outdoorCourseDetails', id, locale], () => details); + await queryClient.prefetchQuery({ + queryKey: ['outdoorCourseDetails', id, locale], + queryFn: () => getOutdoorCourseDetails(id, locale, commonDictionaries), + }); + const details = queryClient.getQueryData(['outdoorCourseDetails', id, locale]) ; - const redirect = redirectIfWrongUrl( - id, - details.name, - { ...context, locale }, - routes.OUTDOOR_COURSE, - ); - if (redirect) - return { + if (details !==undefined) { + const redirect = redirectIfWrongUrl( + id, + details.name, + { ...context, locale }, + routes.OUTDOOR_COURSE, + ); + if (redirect) + return { redirect, }; + } return { props: { @@ -55,9 +72,9 @@ interface Props { } const OutdoorCourse: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { outdoorCourse } = router.query; - const language = router.locale ?? getDefaultLanguage(); + const { query, locale } = useRouter(); + const { outdoorCourse } = query; + const language = locale ?? getDefaultLanguage(); if (errorCode === 404) return ; diff --git a/frontend/src/pages/outdoor-site/[outdoorSite].tsx b/frontend/src/pages/outdoor-site/[outdoorSite].tsx index 59f2abade..b1c48a1df 100644 --- a/frontend/src/pages/outdoor-site/[outdoorSite].tsx +++ b/frontend/src/pages/outdoor-site/[outdoorSite].tsx @@ -1,38 +1,56 @@ import { OutdoorSiteUI } from 'components/pages/site'; import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { getDefaultLanguage } from 'modules/header/utills'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { routes } from 'services/routes'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; -import { getOutdoorSiteDetails } from '../../modules/outdoorSite/connector'; -import { isUrlString } from '../../modules/utils/string'; -import { redirectIfWrongUrl } from '../../modules/utils/url'; +import { isRessourceMissing } from 'services/routeUtils'; +import { OutdoorSiteDetails } from 'modules/outdoorSite/interface'; +import { getOutdoorSiteDetails } from 'modules/outdoorSite/connector'; +import { isUrlString } from 'modules/utils/string'; +import { redirectIfWrongUrl } from 'modules/utils/url'; import Custom404 from '../404'; export const getServerSideProps: GetServerSideProps = async context => { const id = isUrlString(context.query.outdoorSite) ? context.query.outdoorSite.split('-')[0] : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); try { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - const details = await getOutdoorSiteDetails(id, locale, commonDictionaries); - await queryClient.prefetchQuery(['outdoorSiteDetails', id, locale], () => details); + await queryClient.prefetchQuery({ + queryKey: ['outdoorSiteDetails', id, locale], + queryFn: () => getOutdoorSiteDetails(id, locale, commonDictionaries), + }); + + const details = queryClient.getQueryData(['outdoorSiteDetails', id, locale]); - const redirect = redirectIfWrongUrl( - id, - details.name, - { ...context, locale }, - routes.OUTDOOR_SITE, - ); - if (redirect) - return { + if (details !== undefined) { + const redirect = redirectIfWrongUrl( + id, + details.name, + { ...context, locale }, + routes.OUTDOOR_SITE, + ); + if (redirect) + return { redirect, }; + } return { props: { @@ -53,9 +71,9 @@ interface Props { } const OutdoorSite: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { outdoorSite } = router.query; - const language = router.locale ?? getDefaultLanguage(); + const { query, locale } = useRouter(); + const { outdoorSite } = query; + const language = locale ?? getDefaultLanguage(); if (errorCode === 404) return ; diff --git a/frontend/src/pages/search.tsx b/frontend/src/pages/search.tsx index 43591f77f..e4c43b2e9 100644 --- a/frontend/src/pages/search.tsx +++ b/frontend/src/pages/search.tsx @@ -1,12 +1,15 @@ +import { GetServerSideProps, NextPage } from 'next'; +import { dehydrate, DehydratedState, QueryCache, QueryClient } from '@tanstack/react-query'; +import router from 'next/router'; import { SearchUI } from 'components/pages/search'; import { parseFilters } from 'components/pages/search/utils'; import { getInitialFilters } from 'modules/filters/connector'; import { getDefaultLanguage } from 'modules/header/utills'; import { getSearchResults } from 'modules/results/connector'; -import { GetServerSideProps, NextPage } from 'next'; -import { dehydrate, DehydratedState, QueryClient } from '@tanstack/react-query'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; -import { getGlobalConfig } from '../modules/utils/api.config'; +import { getGlobalConfig } from 'modules/utils/api.config'; +import { isRessourceMissing } from 'services/routeUtils'; +import { routes } from 'services/routes'; import Custom404 from './404'; const sanitizeState = (unsafeState: DehydratedState): DehydratedState => @@ -14,16 +17,25 @@ const sanitizeState = (unsafeState: DehydratedState): DehydratedState => export const getServerSideProps: GetServerSideProps = async context => { const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); const { initialFiltersStateWithSelectedOptions } = await getInitialFilters(locale, context.query); const parsedInitialFiltersState = parseFilters(initialFiltersStateWithSelectedOptions); const initialTextFilter = context.query.text?.toString() ?? ''; const page = Number(context.query.page ?? 1); try { - await queryClient.prefetchQuery(['initialFilterState', locale], () => - getInitialFilters(locale, context.query), - ); + await queryClient.prefetchQuery({ + queryKey: ['initialFilterState', locale], + queryFn: () => getInitialFilters(locale, context.query), + }); const bboxFilter = undefined; const dateFilter = { @@ -31,39 +43,43 @@ export const getServerSideProps: GetServerSideProps = async context => { endDate: context.query.endDate ?? '', }; - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - await queryClient.prefetchQuery(['counter'], () => - getSearchResults( - { - filtersState: [], - textFilterState: null, - bboxState: null, - dateFilter: { endDate: '', beginDate: '' }, - }, - { - treks: 1, - touristicContents: 1, - outdoorSites: getGlobalConfig().enableOutdoor ? 1 : null, - touristicEvents: getGlobalConfig().enableTouristicEvents ? 1 : null, - }, - locale, - commonDictionaries, - ), - ); + await queryClient.prefetchQuery({ + queryKey: ['counter'], + queryFn: () => + getSearchResults( + { + filtersState: [], + textFilterState: null, + bboxState: null, + dateFilter: { endDate: '', beginDate: '' }, + }, + { + treks: 1, + touristicContents: 1, + outdoorSites: getGlobalConfig().enableOutdoor ? 1 : null, + touristicEvents: getGlobalConfig().enableTouristicEvents ? 1 : null, + }, + locale, + commonDictionaries, + ), + }); - await queryClient.prefetchInfiniteQuery( - [ + await queryClient.prefetchInfiniteQuery({ + queryKey: [ 'trekResults', - JSON.stringify(parsedInitialFiltersState), + parsedInitialFiltersState, context.locale, initialTextFilter, bboxFilter, - JSON.stringify(dateFilter), + dateFilter, page, ], - () => + queryFn: () => getSearchResults( { filtersState: parsedInitialFiltersState, @@ -80,7 +96,13 @@ export const getServerSideProps: GetServerSideProps = async context => { locale, commonDictionaries, ), - ); + initialPageParam: { + treks: page, + touristicContents: page, + outdoorSites: getGlobalConfig().enableOutdoor ? page : null, + touristicEvents: getGlobalConfig().enableTouristicEvents ? page : null, + }, + }); } catch (error) { return { props: { diff --git a/frontend/src/pages/service/[touristicContent].tsx b/frontend/src/pages/service/[touristicContent].tsx index 8a218ed64..022b796d8 100644 --- a/frontend/src/pages/service/[touristicContent].tsx +++ b/frontend/src/pages/service/[touristicContent].tsx @@ -1,13 +1,15 @@ import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { TouristicContentUI } from 'components/pages/touristicContent'; import { getDefaultLanguage } from 'modules/header/utills'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { routes } from 'services/routes'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; -import { getTouristicContentDetails } from '../../modules/touristicContent/connector'; -import { isUrlString } from '../../modules/utils/string'; -import { redirectIfWrongUrl } from '../../modules/utils/url'; +import { isRessourceMissing } from 'services/routeUtils'; +import { TouristicContentDetails } from 'modules/touristicContent/interface'; +import { getTouristicContentDetails } from 'modules/touristicContent/connector'; +import { isUrlString } from 'modules/utils/string'; +import { redirectIfWrongUrl } from 'modules/utils/url'; import Custom404 from '../404'; export const getServerSideProps: GetServerSideProps = async context => { @@ -16,25 +18,44 @@ export const getServerSideProps: GetServerSideProps = async context => { : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); try { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); - - const details = await getTouristicContentDetails(id, locale, commonDictionaries); - await queryClient.prefetchQuery(['touristicContentDetails', id, locale], () => details); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); - const redirect = redirectIfWrongUrl( + await queryClient.prefetchQuery({ + queryKey: ['touristicContentDetails', id, locale], + queryFn: () => getTouristicContentDetails(id, locale, commonDictionaries), + }); + const details = queryClient.getQueryData([ + 'touristicContentDetails', id, - details.name, - { ...context, locale }, - routes.TOURISTIC_CONTENT, - ); - if (redirect) - return { - redirect, - }; + locale, + ]); + + if (details !== undefined) { + const redirect = redirectIfWrongUrl( + id, + details.name, + { ...context, locale }, + routes.TOURISTIC_CONTENT, + ); + if (redirect) + return { + redirect, + }; + } return { props: { @@ -55,9 +76,9 @@ interface Props { } const TouristicContent: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { touristicContent } = router.query; - const language = router.locale ?? getDefaultLanguage(); + const { query, locale } = useRouter(); + const { touristicContent } = query; + const language = locale ?? getDefaultLanguage(); if (errorCode === 404) return ; diff --git a/frontend/src/pages/trek/[slug].tsx b/frontend/src/pages/trek/[slug].tsx index 225df19a8..4ee37dc52 100644 --- a/frontend/src/pages/trek/[slug].tsx +++ b/frontend/src/pages/trek/[slug].tsx @@ -1,14 +1,16 @@ import { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; +import router, { useRouter } from 'next/router'; import { DetailsUI } from 'components/pages/details'; import { useEffect } from 'react'; -import { dehydrate, QueryClient } from '@tanstack/react-query'; +import { dehydrate, QueryCache, QueryClient } from '@tanstack/react-query'; import { getDetails, getTrekFamily } from 'modules/details/connector'; import { isUrlString } from 'modules/utils/string'; import { getDefaultLanguage } from 'modules/header/utills'; import { routes } from 'services/routes'; import { getCommonDictionaries } from 'modules/dictionaries/connector'; -import { redirectIfWrongUrl } from '../../modules/utils/url'; +import { isRessourceMissing } from 'services/routeUtils'; +import { Details } from 'modules/details/interface'; +import { redirectIfWrongUrl } from 'modules/utils/url'; import Custom404 from '../404'; export const getServerSideProps: GetServerSideProps = async context => { @@ -16,30 +18,45 @@ export const getServerSideProps: GetServerSideProps = async context => { const parentIdString = isUrlString(context.query.parentId) ? context.query.parentId : ''; const { locale = 'fr' } = context; - const queryClient = new QueryClient(); + const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: error => { + if (isRessourceMissing(error)) { + void router.push(routes.HOME); + } + }, + }), + }); try { - const commonDictionaries = await getCommonDictionaries(locale); - await queryClient.prefetchQuery(['commonDictionaries', locale], () => commonDictionaries); + const commonDictionaries = await queryClient.fetchQuery({ + queryKey: ['commonDictionaries', locale], + queryFn: () => getCommonDictionaries(locale), + }); + + await queryClient.prefetchQuery({ queryKey: ['details', id, locale], queryFn: () => getDetails(id, locale, commonDictionaries) }); + + const details = queryClient.getQueryData
(['details', id, locale]); - const details = await getDetails(id, locale, commonDictionaries); + await queryClient.prefetchQuery({ + queryKey: ['trekFamily', parentIdString, locale], + queryFn: () => getTrekFamily(parentIdString, locale), + }); - await queryClient.prefetchQuery(['details', id, locale], () => details); - await queryClient.prefetchQuery(['trekFamily', parentIdString, locale], () => - getTrekFamily(parentIdString, locale), - ); + if (details !== undefined) { - const redirect = redirectIfWrongUrl( - id, - details.title, - { ...context, locale }, - routes.TREK, - Number(parentIdString), - ); - if (redirect) - return { - redirect, - }; + const redirect = redirectIfWrongUrl( + id, + details.title, + { ...context, locale }, + routes.TREK, + Number(parentIdString), + ); + if (redirect) + return { + redirect, + }; + } return { props: { @@ -60,9 +77,9 @@ interface Props { } const Trek: NextPage = ({ errorCode }) => { - const router = useRouter(); - const { slug, parentId } = router.query; - const language = router.locale ?? getDefaultLanguage(); + const { query, locale } = useRouter(); + const { slug, parentId } = query; + const language = locale ?? getDefaultLanguage(); useEffect(() => { // Force to scroll top on page refresh diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 63d51c218..f943fdb2e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2668,34 +2668,29 @@ dependencies: tslib "^2.4.0" -"@tanstack/match-sorter-utils@^8.1.1": - version "8.5.14" - resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.5.14.tgz#12efcd536abe491d09521e0242bc4d51442f8a8a" - integrity sha512-lVNhzTcOJ2bZ4IU+PeCPQ36vowBHvviJb2ZfdRFX5uhy7G0jM8N34zAMbmS5ZmVH8D2B7oU82OWo0e/5ZFzQrw== - dependencies: - remove-accents "0.4.2" +"@tanstack/query-core@5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.51.1.tgz#55049ef0252c7d29de05e8219fd19b679509270e" + integrity sha512-fJBMQMpo8/KSsWW5ratJR5+IFr7YNJ3K2kfP9l5XObYHsgfVy1w3FJUWU4FT2fj7+JMaEg33zOcNDBo0LMwHnw== -"@tanstack/query-core@4.10.3": - version "4.10.3" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.10.3.tgz#a6477bab9ed1ae4561ca0a59ae06f8c615c692b7" - integrity sha512-+ME02sUmBfx3Pjd+0XtEthK8/4rVMD2jcxvnW9DSgFONpKtpjzfRzjY4ykzpDw1QEo2eoPvl7NS8J5mNI199aA== +"@tanstack/query-devtools@5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.51.1.tgz#80fb2b16cc11896b808d17b23487c43db38d22a8" + integrity sha512-rehG0WmL3EXER6MAI2uHQia/n0b5c3ZROohpYm7u3G7yg4q+HsfQy6nuAo6uy40NzHUe3FmnfWCZQ0Vb/3lE6g== -"@tanstack/react-query-devtools@^4.11.0": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.11.0.tgz#a2adf21fa644eae5b834ba4235b99818301ab702" - integrity sha512-g/414SruE0TEp4jeMYxVUSXGXCc+zMY9j0gB8Q6B91gmyPseNqs9WLkVrqxRXoQDyRvDcphVOaEANNygGx+zGg== +"@tanstack/react-query-devtools@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.51.1.tgz#08889d7a9db90e4ce7470805f61764d218e2ef79" + integrity sha512-bRShIVKGpUOHpwziGKT8Aq1Ty0lIlGmNI7E0KbGYtmyOaImErpdElTdxfES1bRaI7i/j+mf2hLy+E6q7SrCwPg== dependencies: - "@tanstack/match-sorter-utils" "^8.1.1" - superjson "^1.10.0" - use-sync-external-store "^1.2.0" + "@tanstack/query-devtools" "5.51.1" -"@tanstack/react-query@^4.10.3": - version "4.10.3" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.10.3.tgz#294deefa0fb6ada88bc4631d346ef5d5551c5443" - integrity sha512-4OEJjkcsCTmG3ui7RjsVzsXerWQvInTe95CBKFyOV/GchMUlNztoFnnYmlMhX7hLUqJMhbG9l7M507V7+xU8Hw== +"@tanstack/react-query@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.1.tgz#e483b9c9011e079b89cf73ce447b2e06340b3f41" + integrity sha512-s47HKFnQ4HOJAHoIiXcpna/roMMPZJPy6fJ6p4ZNVn8+/onlLBEDd1+xc8OnDuwgvecqkZD7Z2mnSRbcWefrKw== dependencies: - "@tanstack/query-core" "4.10.3" - use-sync-external-store "^1.2.0" + "@tanstack/query-core" "5.51.1" "@testing-library/dom@^8.5.0": version "8.19.0" @@ -4721,13 +4716,6 @@ cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -copy-anything@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.2.tgz#7189171ff5e1893b2287e8bf574b8cd448ed50b1" - integrity sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA== - dependencies: - is-what "^4.1.6" - copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -7593,11 +7581,6 @@ is-weakset@^2.0.1: call-bind "^1.0.2" get-intrinsic "^1.1.1" -is-what@^4.1.6: - version "4.1.7" - resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.7.tgz#c41dc1d2d2d6a9285c624c2505f61849c8b1f9cc" - integrity sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -10417,11 +10400,6 @@ remark@^13.0.0: remark-stringify "^9.0.0" unified "^9.1.0" -remove-accents@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" - integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -11622,13 +11600,6 @@ sugarss@^2.0.0: dependencies: postcss "^7.0.2" -superjson@^1.10.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/superjson/-/superjson-1.10.1.tgz#9c73e9393489dddab89d638694eadcbf4bda2f36" - integrity sha512-7fvPVDHmkTKg6641B9c6vr6Zz5CwPtF9j0XFExeLxJxrMaeLU2sqebY3/yrI3l0K5zJ+H9QA3H+lIYj5ooCOkg== - dependencies: - copy-anything "^3.0.2" - supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12352,11 +12323,6 @@ use-isomorphic-layout-effect@^1.1.2: resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== -use-sync-external-store@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== - use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"