Skip to content
Snippets Groups Projects
Commit b30e9d0c authored by Yusuf Akgül's avatar Yusuf Akgül :hatching_chick:
Browse files

fix ui games list, start games detail view, skeleton on games loading

parent e8d6a04c
No related branches found
No related tags found
1 merge request!24fix ui games list, start games detail view, skeleton on games loading
Pipeline #36931 failed
import { Card } from "@/components/ui/card";
import { getGame } from "@/lib/igdb";
import { IGame } from "@/types/igdb-types";
import Image from "next/image";
......@@ -6,18 +7,62 @@ import Image from "next/image";
export default async function GameDetail({ params }: { params: { gameid: string } }) {
const data: IGame[] = await getGame(parseInt(params.gameid))
// onvert unix timestamp to date in this format: Feb 25, 2022
const date = new Date(data[0].first_release_date * 1000).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
})
const companies = data[0].involved_companies.map((company) => {
// put a comma between each company
if (company !== data[0].involved_companies[0]) {
return `, ${company.company.name}`
}
return company.company.name
})
return (
<div>
<h1>Game Detail</h1>
<h1>{data[0].name}</h1>
<Image
src={data[0].cover.url}
alt={data[0].name}
width={264}
height={374}
priority={true}
style={{ width: 'auto', height: 'auto' }} />
<p>{data[0].summary}</p>
<div className="main-content h-full">
<Card className="w-full h-full overflow-hidden">
<div className="h-64 overflow-hidden -top-1/2">
<div className="aspect-[889/500] relative block group">
<Image
src={data[0].screenshots[0].url}
alt={data[0].name}
fill
priority
className="object-center" />
</div>
</div>
<div className="p-6 md:p-12 ss:flex">
<div className="aspect-[264/374]">
<Card className="aspect-[264/374] relative block group -mt-36 w-52 flex-shrink-0">
<Image
src={data[0].cover.url}
alt={data[0].name}
fill
priority
className="object-cover rounded-lg" />
</Card>
</div>
<div className="ml-6 md:ml-12 grid items-start gap-2">
<h1>{data[0].name}</h1>
<h1>released on {date} by {companies}</h1>
<h1>{data[0].summary}</h1>
</div>
</div>
<div className="px-6 md:px-12">
<div className='mt-6 border-b border-gray-400 dark:border-gray-200 ' />
{/* comments */}
</div>
</Card>
<div>
<Card className="side-content">
a
</Card>
</div>
</div>
)
}
\ No newline at end of file
// root loading component, this renders when any loading happens
import { Card } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
export default function Loading() {
return (
<div>
<h1>Games Loading...</h1>
</div>
<main className="main-content">
<div className="flex justify-center">
<Card className="p-6 w-full">
<div className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8">
{Array.from({ length: 18 }, (_, i) => i + 1).map((i) => (
<Skeleton key={i} className="aspect-[264/374] bg-gray-300" />
))}
</div>
</Card>
</div>
<div className="side-content">
<Card className="p-6 grid items-start gap-2 bg-secondary">
<Skeleton className="h-6 w-1/4 bg-gray-400 dark:bg-gray-200" />
<Skeleton className="h-10 bg-background w-full" />
<Skeleton className="h-10 bg-background w-full" />
<Skeleton className="h-10 bg-background w-full" />
<Skeleton className="h-6 w-1/4 mt-6 bg-gray-400 dark:bg-gray-200" />
<Skeleton className="h-10 bg-background w-full" />
<Skeleton className="h-10 w-full bg-gray-300" />
</Card>
</div>
</main>
)
}
\ No newline at end of file
......@@ -5,18 +5,16 @@ import ScrollToTop from "@/components/scroll-to-top";
// renders a list of games infinitely
export default async function GamesPage() {
return (
<>
<main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_240px]">
<div className="grid">
<InfiniteScrollGames />
<main className="main-content">
<div className="flex justify-center">
<div className="fixed top-30 z-50">
<ScrollToTop />
</div>
<div className="hidden xl:block flex-col md:flex">
<Sort />
</div>
</main>
<div className="fixed top-6 left-1/2 -ml-7">
<ScrollToTop />
<InfiniteScrollGames />
</div>
<div className="side-content">
<Sort />
</div>
</>
</main>
)
}
\ No newline at end of file
......@@ -95,6 +95,15 @@
}
}
@layer components {
.main-content {
@apply relative md:gap-10 lg:grid lg:grid-cols-[1fr_240px];
}
.side-content {
@apply hidden lg:block flex-col;
}
}
body {
width: 100vw;
overflow-x: hidden;
......
// import { buttonVariants } from '@/components/ui/button'
// import { cn } from '@/lib/utils'
// import Link from 'next/link'
// export default function Home() {
// return (
// <>
// <section className="space-y-6 pb-8 pt-6 md:pb-12 md:pt-10 lg:py-32">
// <div className="container flex max-w-[64rem] flex-col items-center gap-4 text-center">
// <h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl">
// Welcome to GameUnity!
// </h1>
// <p className="max-w-[42rem] leading-normal text-muted-foreground sm:text-xl sm:leading-8">
// This will be our Home Page and is still WIP
// </p>
// <div className="space-x-4">
// <Link href="/login" className={cn(buttonVariants({ size: "lg" }))}>
// Log In
// </Link>
// <Link href="/signup" className={cn(buttonVariants({ size: "lg" }))}>
// Sign Up
// </Link>
// </div>
// <div className="flex flex-col space-y-4">
// <Link href="/games" className={cn(buttonVariants({ size: "lg" }))}>
// Games List Progress
// </Link>
// <Link href="/home" className={cn(buttonVariants({ size: "lg" }))}>
// Home List Progress
// </Link>
// </div>
// </div>
// </section>
// </>
// )
// }
import Link from "next/link"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { SiteFooter } from "@/components/site-footer"
import { GameUnityLogo } from "@/components/logo"
import { SiteFooter } from "@/components/site-footer"
import { buttonVariants } from "@/components/ui/button"
import { getCurrentUser } from "@/lib/session"
import { cn } from "@/lib/utils"
import Link from "next/link"
export default async function IndexPage() {
const user = await getCurrentUser()
return (
<>
<div className="flex flex-col h-screen justify-between">
<section className="space-y-6 pb-8 pt-6 md:pb-12 md:pt-10 lg:py-32">
<div className="container flex max-w-[64rem] flex-col items-center gap-4 text-center">
<div className="flex items-center">
......@@ -60,14 +23,15 @@ export default async function IndexPage() {
<p className="max-w-[42rem] leading-normal text-muted-foreground sm:text-xl sm:leading-8">
Step into a gaming world beyond imagination. Experience unparalleled features, connect with a vibrant community, and unlock your true gaming potential. Elevate your gameplay, discover new horizons, and make every gaming moment count. Join us and embark on an extraordinary gaming adventure like no other.
</p>
<div className="space-x-5">
<Link href="/login" className={cn(buttonVariants({ size: "lg" }))}>
{!user && <div className="align-middle mb-12">
<Link href="/login" className={cn(buttonVariants({ size: "lg" }), "mr-6")}>
Login
</Link>
<Link href="/signup" className={cn(buttonVariants({ size: "lg" }))}>
<span className="text-muted-foreground">or</span>
<Link href="/signup" className={cn(buttonVariants({ size: "lg" }), "ml-6")}>
Sign-Up
</Link>
</div>
</div>}
<Link href="/home" className={cn(buttonVariants({ size: "lg" }))}>
Home Feed
</Link>
......@@ -128,6 +92,6 @@ export default async function IndexPage() {
</div>
</section>
<SiteFooter className="border-t" />
</>
</div>
)
}
......@@ -5,19 +5,20 @@ import Link from "next/link";
// this is a single game helper-component, only for design purposes
export default function Game({ id, name, cover }: { id: number, name: string, cover: { url: string } }) {
return (
<Card>
<Link href={`/games/${id}`}>
<div className="rounded-lg flex items-center justify-center overflow-hidden">
<Image
src={cover.url}
alt={name}
width={264}
height={374}
priority={true}
style={{ width: '100%', height: '100%' }} />
<Link href={`/games/${id}`}>
<Card className="aspect-[264/374] relative block group items-center justify-center overflow-hidden">
<Image
src={cover.url}
alt={name}
fill
priority
className="object-cover rounded-lg" />
<div className="absolute bottom-0 p-3 group-hover:bg-background w-full">
<div className="transition-all transform translate-y-6 opacity-0 group-hover:opacity-100 group-hover:translate-y-0">
<p className="truncate">{name}</p>
</div>
</div>
</Link>
<p className="truncate">{name}</p>
</Card>
</Card>
</Link>
)
}
\ No newline at end of file
......@@ -2,11 +2,13 @@
import Game from "@/components/game-item";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { IGame } from "@/types/igdb-types";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useSearchParams } from "next/navigation";
import { Fragment } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import { Skeleton } from "./ui/skeleton";
export function InfiniteScrollGames() {
const searchParams = useSearchParams()
......@@ -46,7 +48,7 @@ export function InfiniteScrollGames() {
)
return (
<Card className="p-6">
<Card className="p-6 w-full">
{status === 'error'
?
(<span className="text-center">
......@@ -58,26 +60,31 @@ export function InfiniteScrollGames() {
dataLength={data?.pages.length * 20}
next={fetchNextPage}
hasMore={hasNextPage ? true : false}
className="grid grid-cols-1 ss:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8 items-center"
loader={
<h1 className="text-center pt-6">
<b>Trying to load more...</b>
</h1>
<>
{Array.from({ length: 8 }, (_, i) => i + 1).map((i) => (
<Skeleton key={i} className="aspect-[264/374] bg-gray-300 hidden lg:block" />
))}
{Array.from({ length: 4 }, (_, i) => i + 1).map((i) => (
<Skeleton key={i} className="aspect-[264/374] bg-gray-300 lg:hidden" />
))}
</>
}
endMessage={
<h1 className="text-center pt-6">
<b>Yay! You have seen it all!</b>
</h1>
<div className="text-center">
<h1 className="font-bold text-2xl">Yay!<br />You have seen it all!</h1>
</div>
}
>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8">
{data.pages.map((page, i) => (
<Fragment key={i}>
{page.map((game: IGame) => (
<Game id={game.id} name={game.name} cover={game.cover} key={game.id} />
))}
</Fragment>
))}
</div>
{data.pages.map((page, i) => (
<Fragment key={i}>
{page.map((game: IGame) => (
<Game id={game.id} name={game.name} cover={game.cover} key={game.id} />
))}
</Fragment>
))}
</InfiniteScroll>
))}
</Card>
......
......@@ -8,7 +8,7 @@ export function SiteFooter({ className }: React.HTMLAttributes<HTMLElement>) {
<div className="container flex flex-col items-center justify-between gap-4 py-10 md:h-24 md:flex-row md:py-0">
<div className="flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0">
<Icons.logoWhite className="h-7 w-7" />
<span className="pr-6">
<span className="md:pr-6">
<Icons.logoWhiteName className="" />
</span>
<p className="text-white text-center text-sm leading-loose md:text-left">
......
......@@ -77,7 +77,11 @@ export async function getGame(id: number): Promise<IGame[]> {
'Client-ID': CLIENT_ID,
'Authorization': `Bearer ${auth.access_token}`
},
body: `fields name, cover.*, summary; where cover != null; where id = ${id};`
body:
`fields *, cover.image_id, screenshots.image_id, involved_companies.company.name;
where total_rating_count > 3
& cover != null & total_rating != null & rating != null & age_ratings != null
& id = ${id};`
})
if (!response.ok) {
......@@ -87,8 +91,12 @@ export async function getGame(id: number): Promise<IGame[]> {
const games = await response.json() as IGame[]
games.forEach(game => {
game.cover.url = getImageURL(game.cover.image_id, 'cover_big')
})
game.cover.url = getImageURL(game.cover.image_id, 'cover_big');
game.screenshots.forEach(screenshot => {
screenshot.url = getImageURL(screenshot.image_id, 'screenshot_big');
});
});
return games
}
\ No newline at end of file
......@@ -5,13 +5,21 @@ module.exports = {
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
],
],
theme: {
screens: {
"ss": "380px",
"sm": '640px',
"md": '768px',
"lg": '1024px',
"xl": '1280px',
"2xl": '1536px',
},
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
"2xl": '1536px',
},
},
extend: {
......
......@@ -30,7 +30,7 @@ export interface IGame {
game_modes: number[];
genres: number[];
hypes: number;
involved_companies: number[];
involved_companies: IInvolvedCompany[];
keywords: number[];
language_supports: number[];
multiplayer_modes: number[];
......@@ -44,7 +44,7 @@ export interface IGame {
release_dates: number[];
remakes: number[];
remasters: number[];
screenshots: number[];
screenshots: IScreenshots[];
similar_games: number[];
slug: string;
standalone_expansions: number[];
......@@ -75,6 +75,17 @@ export interface ICover {
width: number;
}
export interface IScreenshots {
id: number;
alpha_channel: boolean;
animated: boolean;
game: number;
height: number;
image_id: string;
url: string;
width: number;
}
export interface IGenre {
id: number;
created_at: number;
......@@ -84,6 +95,22 @@ export interface IGenre {
url: string;
}
interface IInvolvedCompany {
id: number;
company: {
id: number;
name: string;
};
created_at: number;
developer: boolean;
game: number;
porting: boolean;
publisher: boolean;
supporting: boolean;
updated_at: number;
checksum: string;
}
export interface IPlatform {
id: number;
abbreviation: string;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment