diff --git a/TODO.md b/TODO.md index 8d4d65d..80d8d17 100644 --- a/TODO.md +++ b/TODO.md @@ -15,6 +15,7 @@ - [x] Basic UI layout - [x] Replace all tags by mui - [x] Add a scroll to top button + - [ ] Format the prices for currency - [x] Add float sidebar - [x] Enable button to enable and disable sidebar - [x] Add float sidebar with website socials and theme toggle @@ -30,6 +31,36 @@ - [x] Create section for popular games - [x] Create section for most liked games - [x] Create section for future games + - [x] Create a game details page + - [x] Create the main details of a game + - [x] Display name, cover and release date + - [x] Display a short description of the game + - [x] Show game details + - [x] Display the real game website + - [x] Display the age rating + - [x] Display the game protection + - [x] Display the crack status + - [x] Display the game hearts quantity + - [x] Display the game platforms with a link for all platform games + - [x] Display the game genres with a link for all genre games + - [x] Display the game tags with a link for all tag games + - [x] Display the game categories with a link for all category games + - [x] Display the game publishers + - [x] Display the game developers + - [x] Display the torrent links availables for that game + - [x] Display the game availability on platforms to be purchased + - [x] Display the game detailed description + - [x] Display the game gallery + - [x] Display some game reviews + - [x] Display the game critics + - [x] Display the available DLCs for the game + - [x] Display the DLC name + - [x] Display the DLC release date + - [x] Display the DLC platforms + - [x] Display the DLC gallery + - [x] Display the DLC availability on platforms to be purchased + - [x] Display the game requirements for each OS and type (minimum, recommended or maximum) + - [x] Display the game languages availability - [ ] Game news and reviews section - [ ] Fetch and display the latest gaming news - [ ] Add reviews section with filtering options diff --git a/src/Routes.tsx b/src/Routes.tsx index 546aafa..d54cb7b 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -6,6 +6,7 @@ import { DefaultLayout } from './layouts' export const Routes = () => { const Home = L(lazy(() => import('./pages/Home'))) + const GameDetails = L(lazy(() => import('./pages/Game/Details'))) return useRoutes([ { @@ -15,6 +16,13 @@ export const Routes = () => { path: '/', element: , }, + { + path: '/games', + children: [ + { index: true, element: <>Games }, + { path: ':slug', element: }, + ], + }, ], }, ]) diff --git a/src/components/Header/modules/HeaderCarousel.tsx b/src/components/Header/modules/HeaderCarousel.tsx index e3599cc..0d33cf2 100644 --- a/src/components/Header/modules/HeaderCarousel.tsx +++ b/src/components/Header/modules/HeaderCarousel.tsx @@ -22,10 +22,10 @@ function HeaderCarousel(props: HeaderCarouselProps) { emulateTouch preventMovementUntilSwipeScrollTolerance={true} swipeScrollTolerance={50}> - {banners.map((banner) => ( - + {banners.map(({ id, banner, related }) => ( + Game Banner @@ -34,27 +34,27 @@ function HeaderCarousel(props: HeaderCarouselProps) { - {banner.title} + {related.title} - Best Price: $59.99 + Best Price: {related.best_price} - {banner.description} + {related.short_description} Available on: - {banner.platforms.map(({ id, name }) => ( + {related.platforms.map(({ id, name }) => ( - {banner.tags.map(({ id, name }) => ( + {related.tags.map(({ id, name }) => (


+

• Explore a Land of Vast Wonders


"A world unseen, where wonders gleam,
And with each stride, a new scene streams."


Enter a fascinating realm filled with the wonders and discoveries of ancient Chinese mythology!
+ As the Destined One, you shall traverse an array of breathtaking and distinctive landscapes from the classic tale, composing an epic of adventure that is seen anew.


+

• Confront Mighty Foes, Old and New


"Heroic Monkey, might and fame,
Adversaries rise, to test his name."


One of the major highlights of Journey to the West is its diverse cast of adversaries, each with their unique strengths.
+ As the Destined One, you will encounter powerful foes and worthy rivals throughout your journey. Fearlessly engage them in epic battles where surrender is not an option.


+

• Temper Your Mastery of Varied Spells


"Spells unbound, talents in play,
Unleashed abilities seize the day."


Spells, transformations, and magic vessels of all manifestations, in which some counteract while others amplify, have long been iconic combat elements of Chinese mythology. +
As the Destined One, aside from mastering various staff techniques, you can also freely combine different spells, abilities, weapons, and equipment to find a winning strategy that best suits your combat style.


+

• Discover Heartfelt Tales Behind Every Facade


"Within all souls, both wild and tame,
There thrives a tale of life's fierce flame."


Beneath the ferocity and craftiness of your foes lies an engaging tapestry of their origins, personalities, and motivations waiting to be revealed.
+ As the Destined One, you will uncover the stories behind a variety of characters, delving beyond your battles with them to taste the love, hate, greed, and fury they once held and still carry within.`, + short_description: `Black Myth: Wukong is an action RPG that draws on Chinese + mythology. Fight through a richly detailed world inspired + by ancient Chinese literature.`, + website: 'https://www.heishenhua.com/', + hearts_count: 839123, + badge: 'hot', + crack: { + id: 1, + status: 'Uncracked', + torrent: null, + cracked_at: null, + by: null, + protection: { + id: 1, + name: 'Denuvo', + }, + }, + galleries: [ + { + id: 1, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2358720/ss_86c4b7462bba219a0d0b89931a35812b9f188976.1920x1080.jpg?t=1725007201', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2358720/ss_86c4b7462bba219a0d0b89931a35812b9f188976.600x338.jpg?t=1725007201', + media_type: { id: 1, name: 'photo' }, + }, + { + id: 2, + path: 'http://video.akamai.steamstatic.com/store_trailers/257048125/movie_max.mp4?t=1724238304', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/257048125/movie.293x165.jpg?t=1724238304', + media_type: { id: 2, name: 'video' }, + }, + ], + reviews: [ + { + id: 1, + played: true, + rate: 10, + comment: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores! + Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores! + Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores!`, + user: { + id: 1, + name: 'Player 1', + email: 'dev@dev.com', + nickname: 'theplayer1', + birthdate: '2000-03-25', + created_at: '2024-01-01T00:00:00.000Z', + updated_at: '2024-01-01T00:00:00.000Z', + profile: { + photo: + 'https://wac-cdn.atlassian.com/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=2199', + share: true, + }, + }, + }, + { + id: 2, + played: false, + rate: 5.8, + comment: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores! + Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores! + Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores!`, + user: { + id: 2, + name: 'Player 2', + email: 'dev2@dev.com', + nickname: 'theplayer2', + birthdate: '2000-03-25', + created_at: '2024-01-01T00:00:00.000Z', + updated_at: '2024-01-01T00:00:00.000Z', + profile: { + photo: + 'https://wac-cdn.atlassian.com/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=2199', + share: true, + }, + }, + }, + { + id: 3, + played: false, + rate: 5, + comment: + 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores!', + user: { + id: 3, + name: 'Player 3', + email: 'dev3@dev.com', + nickname: 'theplayer3', + birthdate: '2000-03-25', + created_at: '2024-01-01T00:00:00.000Z', + updated_at: '2024-01-01T00:00:00.000Z', + profile: { + photo: + 'https://wac-cdn.atlassian.com/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=2199', + share: true, + }, + }, + }, + { + id: 4, + played: true, + rate: 2.4, + comment: + 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Placeat laudantium praesentium architecto quis iusto aut et non facere, incidunt quo corrupti? Quibusdam ipsum voluptate quaerat distinctio ea. Quasi, debitis maiores!', + user: { + id: 4, + name: 'Player 4', + email: 'dev4@dev.com', + nickname: 'theplayer4', + birthdate: '2000-03-25', + created_at: '2024-01-01T00:00:00.000Z', + updated_at: '2024-01-01T00:00:00.000Z', + profile: { + photo: + 'https://wac-cdn.atlassian.com/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=2199', + share: true, + }, + }, + }, + ], + dlcs: [ + { + id: 1, + release: '2024-08-19', + name: 'Black Myth: Wukong Deluxe Edition Upgrade', + cover: + 'https://cdn1.epicgames.com/spt-assets/ca9ef1bcf2f54043baac351366aec677/black-myth-wukong-1t5ca.jpg', + galleries: [ + { + id: 1, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_fda62ee2e4ead7eac83ef0e825c6a644d6d38fa2.1920x1080.jpg?t=1725007043', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_fda62ee2e4ead7eac83ef0e825c6a644d6d38fa2.600x338.jpg?t=1725007043', + media_type: { id: 1, name: 'photo' }, + }, + { + id: 2, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.1920x1080.jpg?t=1725007043', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.600x338.jpg?t=1725007043', + media_type: { id: 1, name: 'photo' }, + }, + { + id: 3, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_fda62ee2e4ead7eac83ef0e825c6a644d6d38fa2.1920x1080.jpg?t=1725007043', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_fda62ee2e4ead7eac83ef0e825c6a644d6d38fa2.600x338.jpg?t=1725007043', + media_type: { id: 1, name: 'photo' }, + }, + { + id: 4, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.1920x1080.jpg?t=1725007043', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.600x338.jpg?t=1725007043', + media_type: { id: 1, name: 'photo' }, + }, + { + id: 5, + path: 'http://video.akamai.steamstatic.com/store_trailers/257048125/movie_max.mp4?t=1724238304', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/257048125/movie.293x165.jpg?t=1724238304', + media_type: { id: 2, name: 'video' }, + }, + { + id: 6, + path: 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.1920x1080.jpg?t=1725007043', + thumbnail: + 'https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/2672610/ss_86c4b7462bba219a0d0b89931a35812b9f188976.600x338.jpg?t=1725007043', + media_type: { id: 1, name: 'photo' }, + }, + ], + platforms: [ + { id: 1, slug: 'ps5', name: 'PS5' }, + { id: 3, slug: 'pc', name: 'PC' }, + { id: 4, slug: 'xbox-series-s', name: 'Xbox Series S' }, + { id: 5, slug: 'xbox-series-x', name: 'Xbox Series X' }, + { id: 6, slug: 'geforce-now', name: 'GeForce Now' }, + ], + categories: [ + { id: 1, slug: 'single-player', name: 'Single-player' }, + { + id: 2, + slug: 'downloadable-content', + name: 'Downloadable Content', + }, + ], + genres: [ + { id: 1, slug: 'action', name: 'Action' }, + { id: 2, slug: 'adventure', name: 'Adventure' }, + { id: 8, slug: 'rpg', name: 'RPG' }, + { id: 9, slug: 'souls', name: 'Souls' }, + ], + companies: [ + { + id: 1, + name: 'Steam', + slug: 'steam', + url: 'https://store.steampowered.com/app/2672610/Black_Myth_Wukong__Atualizacao_para_Edicao_Digital_Deluxe/', + country: 'Bellevue, Washington', + logo: 'https://upload.wikimedia.org/wikipedia/commons/c/c1/Steam_Logo.png', + company_game_id: '2672610', + price: 5000, + }, + ], + }, + ], + tags: [ + { id: 13, slug: 'souls', name: 'Souls' }, + { id: 12, slug: 'uncracked', name: 'Uncracked' }, + { id: 9, slug: 'denuvo', name: 'Denuvo' }, + { id: 8, slug: 'rpg', name: 'RPG' }, + { id: 1, slug: 'action', name: 'Action' }, + { id: 2, slug: 'adventure', name: 'Adventure' }, + { id: 3, slug: 'single-player', name: 'Single-player' }, + { id: 4, slug: 'fantasy', name: 'Fantasy' }, + { id: 5, slug: 'mythologic', name: 'Mythologic' }, + ], + publishers: [ + { + id: 1, + name: 'Game Science', + acting: true, + created_at: '2020-08-01T08:00:00.000Z', + }, + ], + developers: [ + { + id: 1, + name: 'Game Science', + acting: true, + created_at: '2020-08-01T08:00:00.000Z', + }, + ], + critics: [ + { + id: 1, + logo: 'https://w7.pngwing.com/pngs/41/567/png-transparent-metacritic-video-game-review-aggregator-app-miscellaneous-game-text.png', + name: 'Metacritic', + rate: 7.9, + url: 'https://www.metacritic.com/game/black-myth-wukong/', + created_at: '2024-08-19T00:00:00.000Z', + }, + ], + languages: [ + { + id: 1, + iso: 'zh-cn', + name: 'Simplified Chinese', + dubs: true, + menu: true, + subtitles: true, + }, + { + id: 2, + iso: 'en-us', + name: 'English', + dubs: true, + menu: true, + subtitles: true, + }, + { + id: 3, + iso: 'pt-br', + name: 'Brazilian Portuguese', + dubs: false, + menu: true, + subtitles: true, + }, + ], + companies: [ + { + id: 1, + name: 'Steam', + slug: 'steam', + country: 'Bellevue, Washington', + url: 'https://store.steampowered.com/app/2358720/Black_Myth_Wukong/', + company_game_id: '2358720', + logo: 'https://upload.wikimedia.org/wikipedia/commons/c/c1/Steam_Logo.png', + price: 22999, + }, + { + id: 2, + name: 'Epic Games', + slug: 'epic-games', + country: 'Cary, North Carolina', + url: 'https://store.epicgames.com/pt-BR/p/black-myth-wukong-87a72b/', + company_game_id: 'black-myth-wukong-87a72b', + logo: 'https://w7.pngwing.com/pngs/787/226/png-transparent-epic-games-logo-icons-logos-emojis-iconic-brands.png', + price: 22999, + }, + ], + categories: [], + platforms: [ + { id: 1, slug: 'ps5', name: 'PS5' }, + { id: 3, slug: 'pc', name: 'PC' }, + { id: 4, slug: 'xbox-series-s', name: 'Xbox Series S' }, + { id: 5, slug: 'xbox-series-x', name: 'Xbox Series X' }, + { id: 6, slug: 'geforce-now', name: 'GeForce Now' }, + ], + genres: [ + { id: 1, slug: 'action', name: 'Action' }, + { id: 2, slug: 'adventure', name: 'Adventure' }, + { id: 3, slug: 'single-player', name: 'Single-player' }, + { id: 4, slug: 'fantasy', name: 'Fantasy' }, + { id: 5, slug: 'mythologic', name: 'Mythologic' }, + { id: 9, slug: 'souls', name: 'Souls' }, + ], + requirements: [ + { + id: 1, + bits: 64, + cpu: 'Intel Core i5-8400 / AMD Ryzen 5 1600', + ram: '16 GB RAM', + gpu: 'NVIDIA GeForce GTX 1060 6GB / AMD Radeon RX 580 8GB', + dx: 'Version 11', + rom: '130 GB available space', + so: 'Windows 10 64-bit', + obs: 'HDD Supported, SSD Recommended. The above specifications were tested with DLSS/FSR/XeSS enabled.', + type: { id: 1, name: 'minimum', type: 'windows' }, + }, + { + id: 2, + bits: 64, + cpu: 'Intel Core i7-9700 / AMD Ryzen 5 5500', + ram: '16 GB RAM', + gpu: 'NVIDIA GeForce RTX 2060 / AMD Radeon RX 5700 XT / INTEL Arc A750', + dx: 'Version 12', + rom: '130 GB available space', + so: 'Windows 10 64-bit', + obs: 'SSD Required. The above specifications were tested with DLSS/FSR/XeSS enabled.', + type: { id: 2, name: 'recommended', type: 'windows' }, + }, + ], + torrents: [ + { + id: 1, + posted_in: '2024-02-09T00:00:00.000Z', + url: 'https://google.com', + provider: { + id: 1, + name: 'FitGirl Repacks', + url: 'https://google.com', + }, + }, + ], +} diff --git a/src/mocks/index.ts b/src/mocks/index.ts index b1a1433..2989b07 100644 --- a/src/mocks/index.ts +++ b/src/mocks/index.ts @@ -1,3 +1,4 @@ +export * from './game' export * from './tags' export * from './games' export * from './genres' diff --git a/src/pages/Game/Details.tsx b/src/pages/Game/Details.tsx new file mode 100644 index 0000000..1e3547f --- /dev/null +++ b/src/pages/Game/Details.tsx @@ -0,0 +1,126 @@ +import { Box, Container, List, Stack, Typography } from '@mui/material' + +import { MOCK_GAME_DETAILS } from '@/mocks' + +import { + Availables, + Critics, + DLCs, + Galleries, + Languages, + MainDetails, + Requirements, + ReviewCard, + Torrents, +} from './modules' + +function Details() { + const game = MOCK_GAME_DETAILS + + return ( + + + + + + + Available at + + {game.companies.map((company) => ( + + ))} + + + {game.torrents.length > 0 && ( + + + Torrents + + {game.torrents.map((torrent) => ( + + ))} + + )} + + + + + + + + + + Reviews + + + {game.reviews.map((review) => ( + + ))} + + + + + + Críticas + + + {game.critics.map((critic) => ( + + ))} + + + + + + Downloadable Content (DLC) + + {game.dlcs.length > 0 ? ( + + {game.dlcs.map((dlc) => ( + + ))} + + ) : ( + No one DLC available for this game. + )} + + + + + System Requirements + + + + + + + + Languages Supported + + + + + + + ) +} + +export default Details diff --git a/src/pages/Game/modules/Availables.tsx b/src/pages/Game/modules/Availables.tsx new file mode 100644 index 0000000..3a946c9 --- /dev/null +++ b/src/pages/Game/modules/Availables.tsx @@ -0,0 +1,56 @@ +import { Box, Link, Typography } from '@mui/material' + +import { Company } from '@/types' + +interface AvailablesProps { + company: Company +} + +function Availables(props: AvailablesProps) { + const { company } = props + + return ( + + {company.name} + + + + {company.name} + + + + Available in {company.country} + + + Price:{' '} + {(company.price / 100).toLocaleString('en-US', { + currency: 'USD', + style: 'currency', + })} + + + Buy Now + + + + ) +} + +export default Availables diff --git a/src/pages/Game/modules/Critics.tsx b/src/pages/Game/modules/Critics.tsx new file mode 100644 index 0000000..7e351a9 --- /dev/null +++ b/src/pages/Game/modules/Critics.tsx @@ -0,0 +1,48 @@ +import { Box, Link, Typography } from '@mui/material' +import { format } from 'date-fns' + +import { Critic } from '@/types' + +interface CriticsProps { + critic: Critic +} + +function Critics(props: CriticsProps) { + const { critic } = props + + return ( + + {critic.name} + + + {critic.name} + + + Rate: {critic.rate} + + + Date: {format(new Date(critic.created_at), 'LLL, dd yyyy')} + + + Read Critic + + + + ) +} + +export default Critics diff --git a/src/pages/Game/modules/DLCs.tsx b/src/pages/Game/modules/DLCs.tsx new file mode 100644 index 0000000..9f6ceae --- /dev/null +++ b/src/pages/Game/modules/DLCs.tsx @@ -0,0 +1,150 @@ +import { Box, Link, Typography } from '@mui/material' +import { useState } from 'react' +import { IoPlay } from 'react-icons/io5' + +import { DLC } from '@/types' + +import { DLCsModal } from '.' + +interface DLCsProps { + dlc: DLC +} + +function DLCs(props: DLCsProps) { + const { dlc } = props + const [modalPath, setModalPath] = useState<{ + path: string + type: 'photo' | 'video' + } | null>(null) + + const openModal = (path: string, type: 'photo' | 'video') => { + setModalPath({ path, type }) + } + + const closeModal = () => { + setModalPath(null) + } + + return ( + <> + + + + + {`${dlc.name} + + + {dlc.name} + + + Release Date: {dlc.release} + + + + {dlc.platforms.map((platform) => ( + + {platform.name} + + ))} + + + + + + + Gallery + + + {dlc.galleries.map(({ id, media_type, thumbnail, path }) => ( + + openModal(path, media_type.name as 'photo' | 'video') + }> + {media_type.name === 'photo' ? ( + {`Gallery + ) : ( + + {`Gallery + + + + + + + )} + + + + ))} + + + + + + Available At + + + {dlc.companies.map((company) => ( + + {`${company.name} + + + {company.name} + + + {(company.price / 100).toLocaleString('en-US', { + currency: 'USD', + style: 'currency', + })} + + + + ))} + + + + + + + ) +} + +export default DLCs diff --git a/src/pages/Game/modules/DLCsModal.tsx b/src/pages/Game/modules/DLCsModal.tsx new file mode 100644 index 0000000..cf22c61 --- /dev/null +++ b/src/pages/Game/modules/DLCsModal.tsx @@ -0,0 +1,78 @@ +import { Box } from '@mui/material' +import { useEffect, useState } from 'react' + +function DLCsModal({ + modalPath, + closeModal, +}: { + modalPath: { path: string; type: 'photo' | 'video' } | null + closeModal: () => void +}) { + const [showModal, setShowModal] = useState(false) + const [isVisible, setIsVisible] = useState(false) + + useEffect(() => { + if (modalPath) { + setShowModal(true) + setTimeout(() => setIsVisible(true), 80) + } else if (showModal) { + setIsVisible(false) + setTimeout(() => setShowModal(false), 500) + } + }, [modalPath, showModal]) + + if (!showModal || !modalPath) return null + + return ( + { + if (e.key === 'Escape') { + setIsVisible(false) + setTimeout(closeModal, 400) + } + }} + onClick={() => { + setIsVisible(false) + setTimeout(closeModal, 400) + }}> + e.stopPropagation()}> + + {modalPath.type === 'photo' ? ( + {`Gallery + ) : ( + + )} + + + ) +} + +export default DLCsModal diff --git a/src/pages/Game/modules/Galleries.tsx b/src/pages/Game/modules/Galleries.tsx new file mode 100644 index 0000000..d9a0edd --- /dev/null +++ b/src/pages/Game/modules/Galleries.tsx @@ -0,0 +1,41 @@ +import { Navigation } from 'swiper/modules' +import { Swiper, SwiperSlide } from 'swiper/react' + +import { Gallery } from '@/types' + +interface GalleriesProps { + galleries: Gallery[] +} + +function Galleries(props: GalleriesProps) { + const { galleries } = props + + return ( + + {galleries.map((gallery) => ( + + {gallery.media_type.name === 'photo' ? ( + {`Gallery + ) : ( + + )} + + ))} + + ) +} + +export default Galleries diff --git a/src/pages/Game/modules/Languages.tsx b/src/pages/Game/modules/Languages.tsx new file mode 100644 index 0000000..210d2bc --- /dev/null +++ b/src/pages/Game/modules/Languages.tsx @@ -0,0 +1,75 @@ +import { + Stack, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from '@mui/material' + +import { Language } from '@/types' + +interface LanguagesProps { + languages: Language[] +} + +function Languages(props: LanguagesProps) { + const { languages } = props + + return ( + + + + + + Language + + + Dubs + + + Menu + + + Subtitles + + + + + {languages.map((lang) => ( + + + {lang.name} + + + {lang.dubs ? '✓' : '✖'} + + + {lang.menu ? '✓' : '✖'} + + + {lang.subtitles ? '✓' : '✖'} + + + ))} + +
+
+ ) +} + +export default Languages diff --git a/src/pages/Game/modules/MainDetails.tsx b/src/pages/Game/modules/MainDetails.tsx new file mode 100644 index 0000000..00f282b --- /dev/null +++ b/src/pages/Game/modules/MainDetails.tsx @@ -0,0 +1,324 @@ +import { Box, Chip, Link, List, ListItem, Typography } from '@mui/material' + +import { GameDetails } from '@/types' + +interface MainDetailsProps { + game: GameDetails +} + +function MainDetails(props: MainDetailsProps) { + const { game } = props + + return ( + <> + + {`${game.title} + + + + + + {game.title} + + + Release Date: {game.release} + + + + + + + Short Description + + + {game.short_description} + + + + + + Game details + + + + + Website: + + + + {game.website} + + + + + + Age Rating: + + + {game.age}+ + + + + + Protection: + + + {game.crack.protection.name} + + + + + Status: + + + + {game.crack.torrent && ( + + + Torrent: + + + {game.crack.torrent} + + + )} + {game.crack.by && ( + + + Cracked By: + + + {game.crack.by.name} + + + )} + + + ❤️: + + + {game.hearts_count} + + + + + + + + + + Platforms + + + {game.platforms.map((platform) => ( + + + {platform.name} + + + ))} + + + + + + Genres + + + {game.genres.map((genre) => ( + + + {genre.name} + + + ))} + + + + + + + + Tags + + + {game.tags.map((tag) => ( + + + {tag.name} + + + ))} + + + + + + Categories + + + {game.categories.map((category) => ( + + + {category.name} + + + ))} + + + + + + + + Publishers + + + {game.publishers.map((publisher) => ( + + {publisher.name} + + ))} + + + + + + Developers + + + {game.developers.map((developer) => ( + + {developer.name} + + ))} + + + + + ) +} + +export default MainDetails diff --git a/src/pages/Game/modules/Requirements.tsx b/src/pages/Game/modules/Requirements.tsx new file mode 100644 index 0000000..e8081aa --- /dev/null +++ b/src/pages/Game/modules/Requirements.tsx @@ -0,0 +1,114 @@ +import { useState } from 'react' + +import { Requirement } from '@/types' + +interface RequirementsProps { + requirements: Requirement[] +} + +function Requirements(props: RequirementsProps) { + const { requirements } = props + + const uniqueTypes = Array.from( + new Set( + requirements.map((req) => `${req.type.name}-${req.type.type}`), + ), + ).map((key) => { + const [name, type] = key.split('-') + return { name, type } + }) + + const [activeTab, setActiveTab] = useState( + uniqueTypes.length > 0 + ? `${uniqueTypes[0].name}-${uniqueTypes[0].type}` + : '', + ) + + return ( + <> +
+ {uniqueTypes.map(({ name, type }, index) => ( + + ))} +
+ +
+ {requirements + .filter( + (req) => `${req.type.name}-${req.type.type}` === activeTab, + ) + .map((req) => ( +
+
+ +

+ {req.type.name.charAt(0).toUpperCase() + + req.type.name.slice(1)}{' '} + Requirements +

+ +
    +
  • + + OS: + {' '} + {req.so} +
  • +
  • + CPU:{' '} + {req.cpu} +
  • +
  • + RAM:{' '} + {req.ram} +
  • +
  • + GPU:{' '} + {req.gpu} +
  • +
  • + + DirectX: + {' '} + {req.dx} +
  • +
  • + + Storage: + {' '} + {req.rom} +
  • +
  • + + Bits: + {' '} + {req.bits}-bit +
  • + {req.obs && ( +
  • + + Notes: + {' '} + {req.obs} +
  • + )} +
+
+ ))} +
+ + ) +} + +export default Requirements diff --git a/src/pages/Game/modules/ReviewCard.tsx b/src/pages/Game/modules/ReviewCard.tsx new file mode 100644 index 0000000..b9ffb20 --- /dev/null +++ b/src/pages/Game/modules/ReviewCard.tsx @@ -0,0 +1,64 @@ +import { Box, Typography } from '@mui/material' + +import { Review } from '@/types' + +interface ReviewCardProps { + review: Review +} + +function ReviewCard(props: ReviewCardProps) { + const { review } = props + + const rateColor = () => { + if (review.rate < 5) return 'text-theme-red-900' + else if (review.rate === 5) return 'text-yellow-500' + + return 'text-green-500' + } + + return ( + + + {review.user.nickname} + + + {review.user.nickname} + + + {review.user.name} + + + + + + {review.comment} + + + + + {review.rate} + + + /10 + + + {review.played && ( + + Played + + )} + + ) +} + +export default ReviewCard diff --git a/src/pages/Game/modules/Torrents.tsx b/src/pages/Game/modules/Torrents.tsx new file mode 100644 index 0000000..0cffef0 --- /dev/null +++ b/src/pages/Game/modules/Torrents.tsx @@ -0,0 +1,43 @@ +import { Box, Link, Typography } from '@mui/material' +import { format } from 'date-fns' + +import { Torrent } from '@/types' + +interface TorrentsProps { + torrent: Torrent +} + +function Torrents(props: TorrentsProps) { + const { torrent } = props + + return ( + + + + + + {torrent.provider.name} + + + + Posted at:{' '} + {format(new Date(torrent.posted_in), 'LLL, dd yyyy')} + + + + Go to torrent + + + + ) +} + +export default Torrents diff --git a/src/pages/Game/modules/index.ts b/src/pages/Game/modules/index.ts new file mode 100644 index 0000000..393bd70 --- /dev/null +++ b/src/pages/Game/modules/index.ts @@ -0,0 +1,10 @@ +export { default as DLCs } from './DLCs' +export { default as Critics } from './Critics' +export { default as Torrents } from './Torrents' +export { default as DLCsModal } from './DLCsModal' +export { default as Galleries } from './Galleries' +export { default as Languages } from './Languages' +export { default as Availables } from './Availables' +export { default as ReviewCard } from './ReviewCard' +export { default as MainDetails } from './MainDetails' +export { default as Requirements } from './Requirements' diff --git a/src/pages/Home/modules/HotGames.tsx b/src/pages/Home/modules/HotGames.tsx index ca02b89..1f2057a 100644 --- a/src/pages/Home/modules/HotGames.tsx +++ b/src/pages/Home/modules/HotGames.tsx @@ -1,4 +1,10 @@ -import { Box, IconButton, Link, Stack, Typography } from '@mui/material' +import { + Box, + Container, + IconButton, + Link, + Typography, +} from '@mui/material' import { useState } from 'react' import { MdViewList, MdViewModule } from 'react-icons/md' @@ -28,10 +34,7 @@ function HotGames(props: HotGamesProps) { const filteres = filteredGamesFn(games, search) return ( - + @@ -91,7 +94,7 @@ function HotGames(props: HotGamesProps) { className="flex justify-center p-4 mt-6 border border-theme-red-900 sm:max-w-48 w-full mx-auto rounded-lg hover:bg-theme-red-900 transition duration-500 dark:text-white text-black hover:text-white"> See more... - + ) } diff --git a/src/pages/Home/modules/PopularGames.tsx b/src/pages/Home/modules/PopularGames.tsx index cd3f780..c87a6e2 100644 --- a/src/pages/Home/modules/PopularGames.tsx +++ b/src/pages/Home/modules/PopularGames.tsx @@ -1,4 +1,10 @@ -import { Box, IconButton, Link, Stack, Typography } from '@mui/material' +import { + Box, + Container, + IconButton, + Link, + Typography, +} from '@mui/material' import { useState } from 'react' import { MdViewList, MdViewModule } from 'react-icons/md' @@ -28,10 +34,7 @@ function PopularGames(props: PopularGamesProps) { const filteres = filteredGamesFn(games, search) return ( - + @@ -91,7 +94,7 @@ function PopularGames(props: PopularGamesProps) { className="flex justify-center p-4 mt-6 border border-theme-red-900 sm:max-w-48 w-full mx-auto rounded-lg hover:bg-theme-red-900 transition duration-500 dark:text-white text-black hover:text-white"> See more... - + ) } diff --git a/src/pages/Home/modules/UpcomingGames.tsx b/src/pages/Home/modules/UpcomingGames.tsx index 8356b69..f626db4 100644 --- a/src/pages/Home/modules/UpcomingGames.tsx +++ b/src/pages/Home/modules/UpcomingGames.tsx @@ -1,4 +1,4 @@ -import { Box, Typography } from '@mui/material' +import { Box, Container, Typography } from '@mui/material' import { Autoplay, EffectCoverflow } from 'swiper/modules' import { Swiper, SwiperSlide } from 'swiper/react' @@ -12,7 +12,7 @@ function UpcomingGames(props: UpcomingGamesProps) { const { games } = props return ( - + @@ -94,7 +94,7 @@ function UpcomingGames(props: UpcomingGamesProps) { ))} - + ) } diff --git a/src/styles/global.css b/src/styles/global.css index 85122d3..6ca795a 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -90,6 +90,10 @@ animation: slide-in 1s ease-out; } + .animate-flicker { + animation: flicker 1.5s infinite; + } + .swiper-pagination-bullet { background-color: #ff1b6b; opacity: 0.7; @@ -104,6 +108,26 @@ animation: glow 2s infinite; } + .neon-text { + text-shadow: + 0 0 5px #f40, + 0 0 10px #f40, + 0 0 20px #f40, + 0 0 30px #f40; + } +} + +@layer utilities { + @keyframes flicker { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.8; + } + } + @keyframes slide-in { from { transform: translateY(100%); diff --git a/src/types/banners.ts b/src/types/banners.ts index 02915be..753b25a 100644 --- a/src/types/banners.ts +++ b/src/types/banners.ts @@ -4,8 +4,13 @@ import { Tag } from './tags' export interface Banner { id: number banner: string - title: string - description: string - platforms: Platform[] - tags: Tag[] + related: { + id: number + slug: string + title: string + best_price: number + short_description: string + tags: Tag[] + platforms: Platform[] + } } diff --git a/src/types/categories.ts b/src/types/categories.ts index f86e0c3..74cb17d 100644 --- a/src/types/categories.ts +++ b/src/types/categories.ts @@ -1,4 +1,5 @@ export interface Category { id: number + slug: string name: string } diff --git a/src/types/companies.ts b/src/types/companies.ts index 4d2b983..6a67bcc 100644 --- a/src/types/companies.ts +++ b/src/types/companies.ts @@ -4,5 +4,7 @@ export interface Company { slug: string name: string logo: string + price: number country: string + company_game_id: string } diff --git a/src/types/galleries.ts b/src/types/galleries.ts index 549fa90..016e9f5 100644 --- a/src/types/galleries.ts +++ b/src/types/galleries.ts @@ -1,4 +1,11 @@ +export interface MediaType { + id: number + name: string +} + export interface Gallery { id: number path: string + thumbnail: string + media_type: MediaType } diff --git a/src/types/games.ts b/src/types/games.ts index 9b6b0aa..bbacf60 100644 --- a/src/types/games.ts +++ b/src/types/games.ts @@ -11,7 +11,6 @@ import { export interface Price { id: number price: number - company: Company } export interface Crackers { @@ -20,13 +19,18 @@ export interface Crackers { acting: boolean } +export interface Protection { + id: number + name: string +} + export interface Crack { id: number - by: Crackers status: string - torrent?: string - protection: string - created_at: string + torrent: string | null + cracked_at: string | null + by: Crackers | null + protection: Protection } export interface Developer { @@ -43,6 +47,12 @@ export interface Publisher { created_at: string } +export interface RequirementType { + id: number + name: 'minimum' | 'recommended' | 'maximum' + type: 'windows' | 'mac' | 'linux' +} + export interface Requirement { id: number so: string @@ -53,7 +63,8 @@ export interface Requirement { rom: string bits: number obs?: string - network: string + network?: string + type: RequirementType } export interface GameList { @@ -75,16 +86,23 @@ export interface Language { id: number iso: string name: string + menu: boolean + dubs: boolean + subtitles: boolean } export interface DLC { id: number name: string + cover: string + release: string game?: { id: number title: string } genres: Genre[] + galleries: Gallery[] + companies: Company[] platforms: Platform[] categories: Category[] } @@ -92,11 +110,25 @@ export interface DLC { export interface Critic { id: number url: string + logo: string name: string - since: string + rate: number created_at: string } +export interface TorrentProvider { + id: number + url: string + name: string +} + +export interface Torrent { + id: number + url: string + posted_in: string + provider: TorrentProvider +} + export interface GameDetails { id: number age: number @@ -107,22 +139,24 @@ export interface GameDetails { badge?: string release: string website?: string - description: string + description?: string hearts_count: number + short_description?: string tags: Tag[] dlcs: DLC[] genres: Genre[] - prices: Price[] reviews: Review[] companies: Company[] galleries: Gallery[] - language: Language[] + languages: Language[] platforms: Platform[] categories: Category[] developers: Developer[] publishers: Publisher[] requirements: Requirement[] - critics: Critic & { rate: number }[] + critics: Critic[] + torrents: Torrent[] + crack: Crack } export interface NextRelease { diff --git a/src/types/user.ts b/src/types/user.ts index 4605898..f32a0a7 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -4,28 +4,15 @@ export interface Profile { linkedin?: string facebook?: string share: boolean - share_avatar: boolean -} - -export interface Address { - city: string - state: string - street: string - number: string - district: string - postalcode: string - complement?: string } export interface User { id: number name: string email: string - coduser: string nickname?: string birthdate: string created_at: string updated_at: string profile?: Profile - address?: Address }