mirror of
https://github.com/mosh-hamedani/game-hub.git
synced 2026-05-21 12:14:32 +02:00
Filter games by genre
This commit is contained in:
parent
f2bb5d8381
commit
2b26f7d1f4
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue