Skip to content

Commit

Permalink
feat: adding blog list and details
Browse files Browse the repository at this point in the history
  • Loading branch information
felipebrsk committed Sep 10, 2024
1 parent 21201b0 commit 2e87082
Show file tree
Hide file tree
Showing 28 changed files with 1,348 additions and 186 deletions.
5 changes: 5 additions & 0 deletions DONE.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
- [x] Genres
- [x] Platforms
- [x] Categories
- [x] Make error pages
- [x] Add ambience music
- [x] Add error code
- [x] Add flickering effect for background image
- [x] Add flickering effect on text

### Post MVP

Expand Down
9 changes: 5 additions & 4 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- [x] Create page to forgot password and reset
- [ ] Set up user registration and login
- [ ] Implement OAuth for Google and Facebook
- [ ] Create a user profile page with editable settings
- [x] Create a user profile page with editable settings
- [ ] Community forum
- [ ] Create a basic forum structure with categories and threads
- [ ] Allow users to create, edit, and delete posts
Expand All @@ -32,9 +32,7 @@
### Post-MVP

- [ ] Advanced features
- [ ] Add dark mode toggle
- [x] Add theme switcher
- [ ] Adjust all site with light mode
- [x] Add dark mode toggle
- [ ] Implement game recommendations based on user activity
- [ ] Create a personalized dashboard for logged-in users
- [ ] Testing & QA
Expand All @@ -49,6 +47,9 @@
- [x] Write detailed README.md with project overview
- [ ] Document API endpoints
- [x] Create a contribution guide for open source contributors
- [ ] Adjust all keyframes to tailwind
- [ ] Review entire responsivity
- [ ] Review entire dark and light mode

### Future Ideas

Expand Down
12 changes: 12 additions & 0 deletions src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ export const Routes = () => {
const Home = L(lazy(() => import('./pages/Home')))
const Login = L(lazy(() => import('./pages/Login')))
const Search = L(lazy(() => import('./pages/Search')))
const Blog = L(lazy(() => import('./pages/Blog/List')))
const Profile = L(lazy(() => import('./pages/Profile')))
const Register = L(lazy(() => import('./pages/Register')))
const Calendar = L(lazy(() => import('./pages/Calendar')))
const GameTags = L(lazy(() => import('./pages/Game/Tags')))
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 BlogDetails = L(lazy(() => import('./pages/Blog/Details')))
const GameDetails = L(lazy(() => import('./pages/Game/Details')))
const GamePlatforms = L(lazy(() => import('./pages/Game/Platforms')))
const GameCategories = L(lazy(() => import('./pages/Game/Categories')))
Expand Down Expand Up @@ -51,6 +53,16 @@ export const Routes = () => {
{ path: ':slug', element: <GameDetails /> },
],
},
{
path: 'blogs',
children: [
{ index: true, element: <Blog /> },
{
path: ':slug',
element: <BlogDetails />,
},
],
},
{ path: '/tags/:tag', element: <GameTags /> },
{ path: '/genres/:genre', element: <GameGenres /> },
{ path: '/platforms/:platform', element: <GamePlatforms /> },
Expand Down
Binary file added src/assets/RE4_Serenity_Theme.mp3
Binary file not shown.
285 changes: 160 additions & 125 deletions src/components/GameCard/GameCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Stack,
Typography,
} from '@mui/material'
import { useState } from 'react'
import {
IoEyeOutline,
IoHeartOutline,
Expand All @@ -14,6 +15,8 @@ import {

import { GameList } from '@/types'

import { HeartsUp } from '..'

interface GameCardProps {
game: GameList
view: 'list' | 'grid'
Expand All @@ -22,145 +25,177 @@ interface GameCardProps {
function GameCard(props: GameCardProps) {
const { game, view } = props

const [hearts, setHearts] = useState<number>(game.hearts_count)
const [isHearted, setIsHearted] = useState<boolean>(false)
const [heartPops, setHeartPops] = useState<number[]>([])

const handleHeartClick = () => {
setIsHearted((prev) => !prev)

setHearts(hearts + (isHearted ? -1 : 1))

if (!isHearted) {
const newHearts = Array.from({ length: 10 }, (_, i) => i * 10)

setHeartPops((prev) => [...prev, ...newHearts])
}
}

return (
<Stack
className={`${
view === 'grid' ? 'w-full' : 'w-full flex flex-col sm:flex-row'
} dark:bg-theme-dark-900 bg-gray-50 text-white rounded-lg shadow-lg overflow-hidden transform hover:scale-[1.02] transition-transform duration-300 relative group`}
style={{
border: '2px solid #333',
boxShadow: '0 0 20px rgba(0,0,0,0.8)',
}}>
<Box
position="relative"
className={view === 'list' ? 'sm:w-1/3' : ''}>
{game.badge && (
<Chip
label={game.badge}
className="absolute top-2 left-2 z-10 bg-gradient-to-r from-theme-red-900 to-yellow-500 text-sm font-bold text-white"
style={{
boxShadow: '0 0 10px rgba(255, 255, 255, 0.7)',
}}
/>
)}
<Box position="relative" className="overflow-hidden">
<img
src={game.cover}
alt={game.title}
width={view === 'grid' ? 500 : 300}
height={view === 'grid' ? 300 : 200}
className={`object-cover w-full h-full transform group-hover:scale-110 transition-transform duration-500 ${view === 'grid' ? 'max-h-64' : 'max-h-80'}`}
<>
<Box className="fixed inset-0 pointer-events-none z-50">
{heartPops.map((delay, index) => (
<HeartsUp
key={index}
delay={delay}
setHeartPops={setHeartPops}
/>
<Box className="absolute bottom-0 left-0 w-full px-4 py-2 flex sm:flex-row flex-col justify-between items-center backdrop-blur-sm bg-gradient-to-r from-black via-black to-red-400 opacity-85">
<Link href={`/games/${game.slug}`}>
<Typography
variant="h6"
className="text-lg font-bold text-white hover:text-theme-red-900 transition duration-500">
{game.title}
</Typography>
</Link>
<Box className="flex items-center sm:flex-row flex-col sm:gap-2 gap-0">
<Box display="flex" alignItems="center">
<IconButton
aria-label="heart"
color="primary"
size="small">
<IoHeartOutline className="text-white" />
</IconButton>
<Typography variant="caption">
{game.hearts_count}
</Typography>
</Box>
<Box className="flex items-center gap-1">
<IoEyeOutline className="text-white" />
<Typography variant="caption">
{game.views_count}
</Typography>
</Box>
</Box>
</Box>
<IconButton
aria-label="notifications"
className="absolute top-1 right-1 z-10">
<IoNotificationsOutline
className="animate-pulse text-white"
size={24}
/>
</IconButton>
</Box>
))}
</Box>

<Box
padding={2}
className={`flex flex-col justify-between ${view === 'list' ? 'sm:w-2/3' : ''} h-full gap-4`}>
<Box className="flex flex-col flex-grow">
<Typography variant="body2" className="text-gray-400">
Release Date: {game.release}
</Typography>
<Box className="grid xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-2 grid-cols-1 items-center my-2 sm:gap-1 gap-2 w-full">
{game.platforms.map((platform) => (
<Link
href={`/platforms/${platform.slug}`}
className="dark:bg-gray-800 bg-gray-300 dark:text-white text-black px-3 py-1 rounded-full sm:w-auto w-full text-center"
key={platform.id}>
<Stack
className={`${
view === 'grid' ? 'w-full' : 'w-full flex flex-col sm:flex-row'
} dark:bg-theme-dark-900 bg-gray-50 text-white rounded-lg shadow-lg overflow-hidden transform hover:scale-[1.02] transition-transform duration-300 relative group`}
style={{
border: '2px solid #333',
boxShadow: '0 0 20px rgba(0,0,0,0.8)',
}}>
<Box
position="relative"
className={view === 'list' ? 'sm:w-1/3' : ''}>
{game.badge && (
<Chip
label={game.badge}
className="absolute top-2 left-2 z-10 bg-gradient-to-r from-theme-red-900 to-yellow-500 text-sm font-bold text-white"
style={{
boxShadow: '0 0 10px rgba(255, 255, 255, 0.7)',
}}
/>
)}
<Box position="relative" className="overflow-hidden">
<img
src={game.cover}
alt={game.title}
width={view === 'grid' ? 500 : 300}
height={view === 'grid' ? 300 : 200}
className={`object-cover w-full h-full transform group-hover:scale-110 transition-transform duration-500 ${view === 'grid' ? 'max-h-64' : 'max-h-80'}`}
/>
<Box className="absolute bottom-0 left-0 w-full px-4 py-2 flex sm:flex-row flex-col justify-between items-center backdrop-blur-sm bg-gradient-to-r from-black via-black to-red-400 opacity-85">
<Link href={`/games/${game.slug}`}>
<Typography
component="span"
fontSize={13}
fontWeight="bold">
{platform.name}
variant="h6"
className="text-lg font-bold text-white hover:text-theme-red-900 transition duration-500">
{game.title}
</Typography>
</Link>
))}
<Box className="flex items-center sm:flex-row flex-col sm:gap-2 gap-0">
<Box display="flex" alignItems="center">
<IconButton
aria-label="heart"
color="primary"
size="small"
onClick={handleHeartClick}>
<IoHeartOutline
className={
isHearted ? 'text-theme-red-900' : 'text-white'
}
/>
</IconButton>
<Typography variant="caption">{hearts}</Typography>
</Box>
<Box className="flex items-center gap-1">
<IoEyeOutline className="text-white" />
<Typography variant="caption">
{game.views_count}
</Typography>
</Box>
</Box>
</Box>
<IconButton
aria-label="notifications"
className="absolute top-1 right-1 z-10">
<IoNotificationsOutline
className="animate-pulse text-white"
size={24}
/>
</IconButton>
</Box>
</Box>

<Box className="flex justify-between items-center">
<Typography variant="h6" className="font-bold">
{game.sale ? (
<div className="flex flex-col">
<span className="text-red-500 line-through">
{(game.commom_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})}
</span>
<span className="text-white">
{(game.best_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})}
</span>
</div>
) : (
(game.best_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})
)}
<Box
padding={2}
className={`flex flex-col justify-between ${view === 'list' ? 'sm:w-2/3' : ''} h-full gap-4`}>
<Box className="flex flex-col flex-grow">
<Typography variant="body2" className="text-gray-400">
Release Date: {game.release}
</Typography>
<Box className="grid xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-2 grid-cols-1 items-center my-2 sm:gap-1 gap-2 w-full">
{game.platforms.map((platform) => (
<Link
href={`/platforms/${platform.slug}`}
className="dark:bg-gray-800 bg-gray-300 dark:text-white text-black px-3 py-1 rounded-full sm:w-auto w-full text-center"
key={platform.id}>
<Typography
component="span"
fontSize={13}
fontWeight="bold">
{platform.name}
</Typography>
</Link>
))}
</Box>

<Box className="flex justify-between items-center">
<Typography variant="h6" className="font-bold">
{game.sale ? (
<div className="flex flex-col">
<span className="text-red-500 line-through">
{(game.commom_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})}
</span>
<span className="text-white">
{(game.best_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})}
</span>
</div>
) : (
(game.best_price / 100).toLocaleString('en-US', {
currency: 'USD',
style: 'currency',
})
)}
</Typography>
</Box>
</Box>
</Box>

<Box className="flex flex-wrap gap-2 my-2 w-full mt-auto">
{game.genres.map((genre) => (
<Link href={`/genres/${genre.slug}`} key={genre.id}>
<Chip
className="font-bold border border-theme-red-900 text-theme-red-900 sm:w-auto w-full"
label={genre.name}
variant="outlined"
/>
</Link>
))}
<Box className="flex flex-wrap gap-2 my-2 w-full mt-auto">
{game.genres.map((genre) => (
<Link href={`/genres/${genre.slug}`} key={genre.id}>
<Chip
className="font-bold border border-theme-red-900 text-theme-red-900 sm:w-auto w-full"
label={genre.name}
variant="outlined"
/>
</Link>
))}
</Box>
</Box>
</Box>

<Box
className="absolute inset-0 rounded-lg pointer-events-none"
style={{
background: 'linear-gradient(45deg, #ff1b6b, #45caff)',
opacity: 0.1,
transition: 'opacity 0.5s',
}}></Box>
</Stack>
<Box
className="absolute inset-0 rounded-lg pointer-events-none"
style={{
background: 'linear-gradient(45deg, #ff1b6b, #45caff)',
opacity: 0.1,
transition: 'opacity 0.5s',
}}
/>
</Stack>
</>
)
}

Expand Down
Loading

0 comments on commit 2e87082

Please sign in to comment.