Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: search game by classifications #23

Merged
merged 2 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ export const Routes = () => {
const Reset = L(lazy(() => import('./pages/Password/Reset')))
const Forgot = L(lazy(() => import('./pages/Password/Forgot')))
const GameGenres = L(lazy(() => import('./pages/Game/Genres')))
const GameCracks = L(lazy(() => import('./pages/Game/Cracks')))
const BlogDetails = L(lazy(() => import('./pages/Blog/Details')))
const GameDetails = L(lazy(() => import('./pages/Game/Details')))
const GameCrackers = L(lazy(() => import('./pages/Game/Crackers')))
const GamePlatforms = L(lazy(() => import('./pages/Game/Platforms')))
const GamePublishers = L(lazy(() => import('./pages/Game/Publishers')))
const GameCategories = L(lazy(() => import('./pages/Game/Categories')))
const GameDevelopers = L(lazy(() => import('./pages/Game/Developers')))

return useRoutes([
{ path: '/login', element: <Login /> },
Expand Down Expand Up @@ -58,8 +62,18 @@ export const Routes = () => {
},
{ path: '/tags/:tag', element: <GameTags /> },
{ path: '/genres/:genre', element: <GameGenres /> },
{ path: '/cracks/:crack', element: <GameCracks /> },
{ path: '/crackers/:cracker', element: <GameCrackers /> },
{ path: '/platforms/:platform', element: <GamePlatforms /> },
{ path: '/categories/:category', element: <GameCategories /> },
{
path: '/developers/:developer',
element: <GameDevelopers />,
},
{
path: '/publishers/:publisher',
element: <GamePublishers />,
},
],
},
],
Expand Down
1 change: 1 addition & 0 deletions src/components/Comments/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
useDeleteCommentMutation,
} from '@/services/api'
import { Comment } from '@/types'

import ActionDialog from '../ActionDialog'

interface CommentsProps {
Expand Down
102 changes: 65 additions & 37 deletions src/hooks/useGames.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, useEffect, useState } from 'react'
import { ChangeEvent, useEffect, useMemo, useState } from 'react'

import { MOCK_SEARCH_GAMES } from '@/mocks'
import { GameList } from '@/types'
import { useLazyFindGamesByQuery } from '@/services/api'
import { FiltersClassifications, GameList } from '@/types'

export type SortField =
| 'title'
Expand All @@ -19,44 +19,36 @@ function useGames(params: {
category?: string
genre?: string
tag?: string
cracker?: string
developer?: string
publisher?: string
crack?: string
}) {
const [games, setGames] = useState<GameList[]>([])
const [trigger, { games, isLoading }] = useLazyFindGamesByQuery({
selectFromResult: ({ data = [], isLoading, isFetching }) => ({
games: data,
isLoading: isLoading || isFetching,
}),
})
const [initialRequestDone, setInitialRequestDone] =
useState<boolean>(false)
const [originalGames, setOriginalGames] = useState<GameList[]>([])
const [displayedGames, setDisplayedGames] = useState<GameList[]>([])
const [totalGames, setTotalGames] = useState<number>(0)
const [currentPage, setCurrentPage] = useState<number>(1)
const [pageSize, setPageSize] = useState<number>(5)
const [pageSize, setPageSize] = useState<number>(12)
const [sort, setSort] = useState<SortState>({
field: 'title',
order: 'asc',
})

const fetchGames = async () => {
let filteredGames = MOCK_SEARCH_GAMES

if (params.category) {
filteredGames = filteredGames.filter((game) =>
game.categories.some(({ slug }) => slug === params.category),
)
}

if (params.genre) {
filteredGames = filteredGames.filter((game) =>
game.genres.some(({ slug }) => slug === params.genre),
)
}

if (params.tag) {
filteredGames = filteredGames.filter((game) =>
game.tags.some(({ slug }) => slug === params.tag),
)
}

if (params.platform) {
filteredGames = filteredGames.filter((game) =>
game.platforms.some(({ slug }) => slug === params.platform),
)
}
const memoizedParams = useMemo(
() => params,
[params.category, params.platform, params.genre, params.tag],
)

const sortedGames = filteredGames.sort((a, b) => {
const fetchGames = async () => {
const sortedGames = displayedGames.sort((a, b) => {
const aValue = a[sort.field]
const bValue = b[sort.field]

Expand All @@ -74,19 +66,54 @@ function useGames(params: {
return 0
})

setGames(
setDisplayedGames(
sortedGames.slice(
(currentPage - 1) * pageSize,
currentPage * pageSize,
),
)

setTotalGames(filteredGames.length)
setTotalGames(displayedGames.length)
}

useEffect(() => {
fetchGames()
}, [currentPage, pageSize, sort])
}, [currentPage, pageSize, sort, originalGames])

useEffect(() => {
if (!isLoading && !initialRequestDone) {
const filters: {
by: FiltersClassifications
filterable?: string
}[] = [
{ by: 'tags', filterable: memoizedParams.tag },
{ by: 'genres', filterable: memoizedParams.genre },
{ by: 'cracks', filterable: memoizedParams.crack },
{ by: 'crackers', filterable: memoizedParams.cracker },
{ by: 'platforms', filterable: memoizedParams.platform },
{ by: 'categories', filterable: memoizedParams.category },
{ by: 'developers', filterable: memoizedParams.developer },
{ by: 'publishers', filterable: memoizedParams.publisher },
]

filters.forEach((filter) => {
if (filter.filterable && originalGames.length === 0) {
trigger({
by: filter.by,
filterable: filter.filterable,
})
}
})
setInitialRequestDone(true)
}
}, [memoizedParams, isLoading, initialRequestDone])

useEffect(() => {
if (games && games.length > 0 && originalGames.length === 0) {
setOriginalGames(games)
setDisplayedGames(games)
setTotalGames(games.length)
}
}, [games])

const handleSortChange = (field: SortField, order: 'asc' | 'desc') => {
setSort({ field, order })
Expand All @@ -104,14 +131,15 @@ function useGames(params: {

return {
sort,
games,
pageSize,
isLoading,
totalGames,
currentPage,
setCurrentPage,
handleSortChange,
handlePageChange,
handlePageSizeChange,
games: displayedGames,
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/mocks/data/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const MOCK_GAME_DETAILS: GameDetails = {
protection: {
id: 1,
name: 'Denuvo',
slug: 'denuvo',
},
},
support: {
Expand Down Expand Up @@ -239,13 +240,15 @@ export const MOCK_GAME_DETAILS: GameDetails = {
{
id: 1,
name: 'Game Science',
slug: 'game-science',
acting: true,
},
],
developers: [
{
id: 1,
name: 'Game Science',
slug: 'game-science',
acting: true,
},
],
Expand Down
8 changes: 6 additions & 2 deletions src/pages/Game/Categories.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { useState } from 'react'
import { useParams } from 'react-router-dom'

import { LoadingScreen } from '@/components'
import { useGames } from '@/hooks'

import ModuledFilters from './ModuledFilters'

function Categories() {
const { category = '' } = useParams()
const [view, setView] = useState<'grid' | 'list'>('grid')
const [isAnimating, setIsAnimating] = useState(false)
const [isAnimating, setIsAnimating] = useState<boolean>(false)

const {
games,
totalGames,
currentPage,
pageSize,
sort,
isLoading,
handlePageChange,
handleSortChange,
handlePageSizeChange,
Expand All @@ -31,7 +33,9 @@ function Categories() {
}, 500)
}

return (
return isLoading ? (
<LoadingScreen />
) : (
<ModuledFilters
games={games}
totalGames={totalGames}
Expand Down
51 changes: 51 additions & 0 deletions src/pages/Game/Crackers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useState } from 'react'
import { useParams } from 'react-router-dom'

import { useGames } from '@/hooks'

import ModuledFilters from './ModuledFilters'

function Crackers() {
const { cracker = '' } = useParams()
const [view, setView] = useState<'grid' | 'list'>('grid')
const [isAnimating, setIsAnimating] = useState<boolean>(false)

const {
games,
totalGames,
currentPage,
pageSize,
sort,
handlePageChange,
handleSortChange,
handlePageSizeChange,
} = useGames({
cracker,
})

const handleViewChange = (newView: 'grid' | 'list') => {
setIsAnimating(true)
setTimeout(() => {
setView(newView)
setIsAnimating(false)
}, 500)
}

return (
<ModuledFilters
games={games}
totalGames={totalGames}
currentPage={currentPage}
pageSize={pageSize}
sort={sort}
view={view}
isAnimating={isAnimating}
onViewChange={handleViewChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
onSortChange={handleSortChange}
/>
)
}

export default Crackers
51 changes: 51 additions & 0 deletions src/pages/Game/Cracks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useState } from 'react'
import { useParams } from 'react-router-dom'

import { useGames } from '@/hooks'

import ModuledFilters from './ModuledFilters'

function Cracks() {
const { crack = '' } = useParams()
const [view, setView] = useState<'grid' | 'list'>('grid')
const [isAnimating, setIsAnimating] = useState<boolean>(false)

const {
games,
totalGames,
currentPage,
pageSize,
sort,
handlePageChange,
handleSortChange,
handlePageSizeChange,
} = useGames({
crack,
})

const handleViewChange = (newView: 'grid' | 'list') => {
setIsAnimating(true)
setTimeout(() => {
setView(newView)
setIsAnimating(false)
}, 500)
}

return (
<ModuledFilters
games={games}
totalGames={totalGames}
currentPage={currentPage}
pageSize={pageSize}
sort={sort}
view={view}
isAnimating={isAnimating}
onViewChange={handleViewChange}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
onSortChange={handleSortChange}
/>
)
}

export default Cracks
Loading
Loading