Filter games by genre

This commit is contained in:
Mosh Hamedani 2023-03-02 09:23:53 -08:00
parent f2bb5d8381
commit 2b26f7d1f4
6 changed files with 37 additions and 20 deletions

View file

@ -1,9 +1,13 @@
import { Grid, GridItem, Show } from "@chakra-ui/react"; import { Grid, GridItem, Show } from "@chakra-ui/react";
import { useState } from "react";
import GameGrid from "./components/GameGrid"; import GameGrid from "./components/GameGrid";
import GenreList from "./components/GenreList"; import GenreList from "./components/GenreList";
import NavBar from "./components/NavBar"; import NavBar from "./components/NavBar";
import { Genre } from "./hooks/useGenres";
function App() { function App() {
const [selectedGenre, setSelectedGenre] = useState<Genre | null>(null);
return ( return (
<Grid <Grid
templateAreas={{ templateAreas={{
@ -20,11 +24,11 @@ function App() {
</GridItem> </GridItem>
<Show above="lg"> <Show above="lg">
<GridItem area="aside" paddingX={5}> <GridItem area="aside" paddingX={5}>
<GenreList /> <GenreList onSelectGenre={(genre) => setSelectedGenre(genre)} />
</GridItem> </GridItem>
</Show> </Show>
<GridItem area="main"> <GridItem area="main">
<GameGrid /> <GameGrid selectedGenre={selectedGenre} />
</GridItem> </GridItem>
</Grid> </Grid>
); );

View file

@ -1,11 +1,16 @@
import { SimpleGrid, Text } from "@chakra-ui/react"; import { SimpleGrid, Text } from "@chakra-ui/react";
import useGames from "../hooks/useGames"; import useGames from "../hooks/useGames";
import { Genre } from "../hooks/useGenres";
import GameCard from "./GameCard"; import GameCard from "./GameCard";
import GameCardContainer from "./GameCardContainer"; import GameCardContainer from "./GameCardContainer";
import GameCardSkeleton from "./GameCardSkeleton"; import GameCardSkeleton from "./GameCardSkeleton";
const GameGrid = () => { interface Props {
const { data, error, isLoading } = useGames(); selectedGenre: Genre | null
}
const GameGrid = ({ selectedGenre }: Props) => {
const { data, error, isLoading } = useGames(selectedGenre);
const skeletons = [1, 2, 3, 4, 5, 6]; const skeletons = [1, 2, 3, 4, 5, 6];
return ( return (
@ -18,13 +23,13 @@ const GameGrid = () => {
> >
{isLoading && {isLoading &&
skeletons.map((skeleton) => ( skeletons.map((skeleton) => (
<GameCardContainer> <GameCardContainer key={skeleton}>
<GameCardSkeleton key={skeleton} /> <GameCardSkeleton />
</GameCardContainer> </GameCardContainer>
))} ))}
{data.map((game) => ( {data.map((game) => (
<GameCardContainer> <GameCardContainer key={game.id}>
<GameCard key={game.id} game={game} /> <GameCard game={game} />
</GameCardContainer> </GameCardContainer>
))} ))}
</SimpleGrid> </SimpleGrid>

View file

@ -1,8 +1,12 @@
import { HStack, Image, List, ListItem, Spinner, Text } from "@chakra-ui/react"; import { Button, HStack, Image, List, ListItem, Spinner, Text } from "@chakra-ui/react";
import useGenres from "../hooks/useGenres"; import useGenres, { Genre } from "../hooks/useGenres";
import getCroppedImageUrl from "../services/image-url"; import getCroppedImageUrl from "../services/image-url";
const GenreList = () => { interface Props {
onSelectGenre: (genre: Genre) => void;
}
const GenreList = ({ onSelectGenre }: Props) => {
const { data, isLoading, error } = useGenres(); const { data, isLoading, error } = useGenres();
if (error) return null; if (error) return null;
@ -19,7 +23,7 @@ const GenreList = () => {
borderRadius={8} borderRadius={8}
src={getCroppedImageUrl(genre.image_background)} src={getCroppedImageUrl(genre.image_background)}
/> />
<Text fontSize='lg'>{genre.name}</Text> <Button onClick={() => onSelectGenre(genre)} fontSize='lg' variant='link'>{genre.name}</Button>
</HStack> </HStack>
</ListItem> </ListItem>
))} ))}

View file

@ -33,7 +33,7 @@ const PlatformIconList = ({ platforms }: Props) => {
return ( return (
<HStack marginY={1}> <HStack marginY={1}>
{platforms.map((platform) => ( {platforms.map((platform) => (
<Icon as={iconMap[platform.slug]} color='gray.500'/> <Icon key={platform.id} as={iconMap[platform.slug]} color='gray.500'/>
))} ))}
</HStack> </HStack>
); );

View file

@ -1,4 +1,4 @@
import { CanceledError } from "axios"; import { AxiosRequestConfig, CanceledError } from "axios";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import apiClient from "../services/api-client"; import apiClient from "../services/api-client";
@ -7,7 +7,7 @@ interface FetchResponse<T> {
results: T[]; results: T[];
} }
const useData = <T>(endpoint: string) => { const useData = <T>(endpoint: string, requestConfig?: AxiosRequestConfig, deps?: any[]) => {
const [data, setData] = useState<T[]>([]); const [data, setData] = useState<T[]>([]);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
@ -17,7 +17,7 @@ const useData = <T>(endpoint: string) => {
setLoading(true); setLoading(true);
apiClient apiClient
.get<FetchResponse<T>>(endpoint, { signal: controller.signal }) .get<FetchResponse<T>>(endpoint, { signal: controller.signal, ...requestConfig })
.then((res) => { .then((res) => {
setData(res.data.results); setData(res.data.results);
setLoading(false); setLoading(false);
@ -29,7 +29,7 @@ const useData = <T>(endpoint: string) => {
}); });
return () => controller.abort(); return () => controller.abort();
}, []); }, deps ? [...deps] : []);
return { data, error, isLoading }; return { data, error, isLoading };
}; };

View file

@ -1,4 +1,5 @@
import useData from "./useData"; import useData from "./useData";
import { Genre } from "./useGenres";
export interface Platform { export interface Platform {
id: number; id: number;
@ -14,6 +15,9 @@ export interface Game {
metacritic: number; metacritic: number;
} }
const useGames = () => useData<Game>('/games'); const useGames = (selectedGenre: Genre | null) =>
useData<Game>("/games", { params: { genres: selectedGenre?.id } }, [
selectedGenre?.id,
]);
export default useGames; export default useGames;