From 7deb7c274d2fed079e6903ff3090e25952f99a4c Mon Sep 17 00:00:00 2001 From: franco sanchez Date: Mon, 29 Jul 2024 19:42:10 -0300 Subject: [PATCH] feat: paging was applied to the filtering --- src/components/AllBooks.tsx | 2 +- src/hooks/queries.ts | 17 +- src/pages/Search.tsx | 427 +++++++++++++++++++++--------------- src/routes.tsx | 4 +- src/services/api.ts | 21 +- 5 files changed, 276 insertions(+), 195 deletions(-) diff --git a/src/components/AllBooks.tsx b/src/components/AllBooks.tsx index 4d12474..d4651b5 100644 --- a/src/components/AllBooks.tsx +++ b/src/components/AllBooks.tsx @@ -27,7 +27,7 @@ export function AllBooks() { useBooksPaginate(); let fetchingNextPageUI; - useScrollRestoration(isPending); // Restablecer la posición del scroll al volver de la vista del libro + useScrollRestoration(isPending); // Restablece la posición del scroll al volver de la vista del libro useEffect(() => { let isMounted = true; diff --git a/src/hooks/queries.ts b/src/hooks/queries.ts index 96151dd..2e4336b 100644 --- a/src/hooks/queries.ts +++ b/src/hooks/queries.ts @@ -109,9 +109,22 @@ function useBooksPaginate() { } function useFilter(query: string | undefined, param: string | undefined) { - return useSuspenseQuery({ + // return useSuspenseQuery({ + // queryKey: [keys.filter, query, param], + // queryFn: () => getBooksFilter(query, param, page), + // gcTime: 3000, + // retry: 1, + // }); + + return useInfiniteQuery({ queryKey: [keys.filter, query, param], - queryFn: () => getBooksFilter(query, param), + queryFn: ({ pageParam }) => getBooksFilter(query, param, pageParam), + initialPageParam: 0, + getNextPageParam: (lastPage) => { + if (lastPage.info.nextPage === null) return; + + return lastPage.info.nextPage; + }, gcTime: 3000, retry: 1, }); diff --git a/src/pages/Search.tsx b/src/pages/Search.tsx index 9b79a96..cbeb872 100644 --- a/src/pages/Search.tsx +++ b/src/pages/Search.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useParams, useLocation } from 'react-router-dom'; import { CgOptions } from 'react-icons/cg'; +import { useInView } from 'react-intersection-observer'; import { Button, Flex, @@ -11,6 +12,10 @@ import { RadioGroup, Image, useColorModeValue, + Spinner, + Alert, + AlertIcon, + AlertTitle, } from '@chakra-ui/react'; import { Card } from '@components/cards/Card'; @@ -26,151 +31,171 @@ import { ResultLength } from '@components/ResultLength'; import { AboutCategories } from '@components/AboutCategories'; import { Lost } from '@assets/assets'; import { aboutCategories } from '../data/links'; +import { SkeletonAllBooks } from '@components/skeletons/SkeletonABooks'; export default function Search() { - const location = useLocation(); - const { isOpen, onToggle, onClose } = useDisclosure(); + const { ref, inView } = useInView(); + // const location = useLocation(); + // const { isOpen, onToggle, onClose } = useDisclosure(); const grayColor = useColorModeValue('#E2E8F0', '#2D3748'); - const [languages, setLanguages] = useState([]); - const [selectedLanguage, setSelectedLanguage] = useState(''); - const [years, setYears] = useState([]); - const [selectedYear, setSelectedYear] = useState(''); + // const [languages, setLanguages] = useState([]); + // const [selectedLanguage, setSelectedLanguage] = useState(''); + // const [years, setYears] = useState([]); + // const [selectedYear, setSelectedYear] = useState(''); const { query, param } = useParams(); let asideFilter; let aboutCategoriesUI; let buttonFilter; + let fetchingNextPageUI; - const { data } = useFilter(query, param); + const { data, isPending, error, fetchNextPage, isFetchingNextPage } = + useFilter(query, param); - function getLanguagesAndYears( - data: Array, - ): LanguageAndYearType | null { - const languagesMap = data.reduce((acc, book) => { - const language = book.language; + useEffect(() => { + let isMounted = true; - if (language) { - acc[language] = (acc[language] || 0) + 1; - } + if (inView && isMounted) { + fetchNextPage(); + } - return acc; - }, {}); + return () => { + isMounted = false; + }; + }, [inView, fetchNextPage]); - const yearsMap = data.reduce((acc, book) => { - const year = book.year; + // function getLanguagesAndYears( + // data: Array | undefined, + // ): LanguageAndYearType | null { + // if (!data) { + // return null; + // } - if (year) { - acc[year] = (acc[year] || 0) + 1; - } + // const languagesMap = data.reduce((acc, book) => { + // const language = book.language; - return acc; - }, {}); + // if (language) { + // acc[language] = (acc[language] || 0) + 1; + // } - const language = Object.keys(languagesMap); - const year = Object.keys(yearsMap); + // return acc; + // }, {}); - if (language.length === 1 && year.length === 1) return null; + // const yearsMap = data.reduce((acc, book) => { + // const year = book.year; - return { language, languagesMap, year, yearsMap }; - } + // if (year) { + // acc[year] = (acc[year] || 0) + 1; + // } - const languagesAndYearData = getLanguagesAndYears(data); + // return acc; + // }, {}); - const filteredBooks = data.filter(({ language, year }) => { - return ( - (languages.length === 0 || languages.includes(language)) && - (years.length === 0 || years.includes(year)) - ); - }); + // const language = Object.keys(languagesMap); + // const year = Object.keys(yearsMap); - function handleLanguageChange(languages) { - setLanguages(languages); - setSelectedLanguage(languages); - } + // if (language.length === 1 && year.length === 1) return null; - function handleYearChange(year) { - setYears(year); - setSelectedYear(year); - } + // return { language, languagesMap, year, yearsMap }; + // } - // Restablecer los valores de los radios(filtros) cuando cambie la ruta - useEffect(() => { - setSelectedLanguage(''); - setLanguages([]); - setSelectedYear(''); - setYears([]); - }, [location.pathname]); + // const languagesAndYearData = getLanguagesAndYears(data?.pages[0]?.results); - if (languagesAndYearData) { - const { language, languagesMap, year, yearsMap } = languagesAndYearData; + // const filteredBooks = data?.pages.filter(({ language, year }) => { + // return ( + // (languages.length === 0 || languages.includes(language)) && + // (years.length === 0 || years.includes(year)) + // ); + // }); - asideFilter = ( - - - - Filtrar por: - - - - {language && - language.map((language) => ( - - {language} - - ({languagesMap && languagesMap[language]}) - - - ))} - Todos los Idiomas - - Idioma - - - - - - {year && - year.map((year) => ( - - {year} - - ({yearsMap && yearsMap[year]}) - - - ))} - Todos los Años - - Año - - - - - ); + // function handleLanguageChange(languages) { + // setLanguages(languages); + // setSelectedLanguage(languages); + // } - buttonFilter = ( - - ); - } + // function handleYearChange(year) { + // setYears(year); + // setSelectedYear(year); + // } + + // // Restablecer los valores de los radios(filtros) cuando cambie la ruta + // useEffect(() => { + // setSelectedLanguage(''); + // setLanguages([]); + // setSelectedYear(''); + // setYears([]); + // }, [location.pathname]); + + // if (languagesAndYearData) { + // const { language, languagesMap, year, yearsMap } = languagesAndYearData; + + // asideFilter = ( + // + // + // + // Filtrar por: + // + // + // + // {language && + // language.map((language) => ( + // + // {language} + // + // ({languagesMap && languagesMap[language]}) + // + // + // ))} + // Todos los Idiomas + // + // Idioma + // + // + // + // + // + // {year && + // year.map((year) => ( + // + // {year} + // + // ({yearsMap && yearsMap[year]}) + // + // + // ))} + // Todos los Años + // + // Año + // + // + // + // + // ); + + // buttonFilter = ( + // + // ); + // } // Verifica si los 3 campos de aboutCategories esten con info o no const categoryCheck = aboutCategories.find((item) => { @@ -187,6 +212,41 @@ export default function Search() { } } + if (isPending) { + return ; + } + + if (error) { + return ( + + + + No se pudieron obtener los datos + + + ); + } + + if (isFetchingNextPage) { + fetchingNextPageUI = ( + + + + ); + } + return ( <> @@ -202,10 +262,10 @@ export default function Search() { align='stretch' borderY={`1px solid ${grayColor}`} > - + {buttonFilter} - + /> */} - {filteredBooks.length > 0 ? ( - - {filteredBooks.map( - ({ - id, - title, - synopsis, - authors, - category, - language, - sourceLink, - image, - pathUrl, - }: CardType) => ( - - - - ), - )} - - ) : ( - - - ¡Ups! - - - - No se encontraron libros que cumplan con los filtros seleccionados - - - )} + + {data?.pages.map((page, index) => ( + + {page?.results.map( + ({ + id, + title, + synopsis, + authors, + category, + language, + sourceLink, + image, + pathUrl, + }: CardType) => ( + + + + ), + )} + {/* + + ¡Ups! + + + + No se encontraron libros que cumplan con los filtros + seleccionados + + */} + + ))} + + {fetchingNextPageUI} ); } diff --git a/src/routes.tsx b/src/routes.tsx index 4d9edd4..f6fa1f6 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -74,7 +74,9 @@ export const routes = createBrowserRouter([ { path: ':query/:param', element: ( - }> + } + > ), diff --git a/src/services/api.ts b/src/services/api.ts index 3853bf2..f43250d 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -17,19 +17,14 @@ async function getBook(pathUrl: string | undefined) { return await fetchData(`${API_URL}/books/path/${pathUrl}`); } -async function postBook(books: any) { - return await fetchData(`${API_URL}/books`, { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify(books), - }); -} - async function getBooksFilter( query: string | undefined, param: string | undefined, + page: number | undefined, ) { - return await fetchData(`${API_URL}/books?${query}=${param}`); + return await fetchData( + `${API_URL}/books?${query}=${param}&limit=10&page=${page}`, + ); } async function getMoreBooks() { @@ -52,6 +47,14 @@ async function getAllFilterOptions() { return await fetchData(`${API_URL}/books/options`); } +async function postBook(books: any) { + return await fetchData(`${API_URL}/books`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify(books), + }); +} + async function updateBook(id: string | undefined, books: any) { return await fetchData(`${API_URL}/books/${id}`, { method: 'PATCH',