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 { 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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
Loading…
Reference in a new issue