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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
import useData from "./useData";
import { Genre } from "./useGenres";
export interface Platform {
export interface Platform {
id: number;
name: string;
slug: string;
@ -14,6 +15,9 @@ export interface Game {
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;