diff --git a/app/(content)/(gaming)/games/[gameid]/page.tsx b/app/(content)/(gaming)/games/[gameid]/page.tsx index 20d751b75a932a20817380767d01bd898f1dcb6b..65e4ba58f2a688ffccd15b19c7f588fe9e6d15f4 100644 --- a/app/(content)/(gaming)/games/[gameid]/page.tsx +++ b/app/(content)/(gaming)/games/[gameid]/page.tsx @@ -1,10 +1,13 @@ +import AddGameToList from "@/components/addGameToList"; import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { getGame } from "@/lib/igdb"; +import { getCurrentUser } from "@/lib/session"; import { formatDate } from "@/lib/utils"; import { IGame } from "@/types/igdb-types"; import Image from "next/image"; +import { db } from "@/lib/db"; // renders a single game detail page export default async function GameDetail({ params }: { params: { gameid: string } }) { @@ -12,6 +15,16 @@ export default async function GameDetail({ params }: { params: { gameid: string const date = formatDate(data[0].first_release_date * 1000) + const user = await getCurrentUser() + const fullUser = await db.user.findFirst({ + where: { + id: user?.id + } + }) + + + /* console.log(user) */ + const companies = data[0].involved_companies.map((company) => { if (company !== data[0].involved_companies[0]) { return `, ${company.company.name}` @@ -20,7 +33,9 @@ export default async function GameDetail({ params }: { params: { gameid: string }) return ( + <div className="main-content h-full"> + <Card className="w-full h-full overflow-hidden"> <div className="h-64 overflow-hidden"> <AspectRatio ratio={889 / 500}> @@ -93,6 +108,7 @@ export default async function GameDetail({ params }: { params: { gameid: string </div> </Card> </div> + <AddGameToList userGameList={fullUser?.favGameList!} gameId={params.gameid} /> </div > ) } \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/page.tsx b/app/(content)/(user)/[userid]/page.tsx index d4b50066daf5a9f30224779a5ee4d38a42c9bbd4..e59fb23e2e2758881bf78a2d596074c269f577c3 100644 --- a/app/(content)/(user)/[userid]/page.tsx +++ b/app/(content)/(user)/[userid]/page.tsx @@ -1,8 +1,12 @@ +import GameItem from "@/components/game-item" import { AspectRatio } from "@/components/ui/aspect-ratio" import { Card } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" import { UserAvatar } from "@/components/user-avatar" +import { db } from "@/lib/db" +import { getFavoriteGames } from "@/lib/igdb" import { getCurrentUser } from "@/lib/session" +import { IGame } from "@/types/igdb-types" import { redirect } from "next/navigation" export default async function User({ params }: { params: { userid: string } }) { @@ -11,6 +15,13 @@ export default async function User({ params }: { params: { userid: string } }) { if (user?.username !== params.userid) { redirect('/') } + const fullUser = await db.user.findFirst({ + where: { + id: user?.id + } + }) + + const favoritegames = await getFavoriteGames(fullUser?.favGameList!) return ( <div className="main-content h-full"> @@ -54,6 +65,19 @@ export default async function User({ params }: { params: { userid: string } }) { </div> </Card> </div> + + <Card className="w-full h-full overflow-hidden p-6 md:p-12" > + <h1 className="text-2xl font-bold pb-3">Your Favorite Games</h1> + <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 items-center"> + {favoritegames ? favoritegames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + )) + : + <p>You have no favorites currently</p>} + </div> + </Card> </div > + + ) } \ No newline at end of file diff --git a/app/api/gameList/route.ts b/app/api/gameList/route.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f7489ede768fe669546f0bfa207aee40da2daba --- /dev/null +++ b/app/api/gameList/route.ts @@ -0,0 +1,62 @@ +import { db } from "@/lib/db"; +import { getCurrentUser } from "@/lib/session"; +import { revalidatePath } from "next/cache"; +import { NextRequest, NextResponse } from "next/server"; + +export async function PUT(req: NextRequest) { + const user = await getCurrentUser(); + + if (!user) { + return NextResponse.json({ status: 401, message: 'Unauthorized' }); + } + + const userId = user.id; + const data = await req.json() + data.gameId = parseInt(data.gameId) + console.log(data); + console.log(userId); + try { + if (data.add) { + console.log("add true") + await db.user.update({ + where: { + id: userId + }, + data: { + favGameList: { + push: data.gameId + } + } + }) + console.log("gameList updated") + } else { + const user = await db.user.findFirst({ + where: { + id: userId + }, + select: { + favGameList: true + }, + }); + + await db.user.update({ + where: { + id: userId + }, + data: { + favGameList: { + set: user?.favGameList.filter((id) => id !== data.gameId), + } + } + }) + } + + const path = req.nextUrl.searchParams.get('path') || '/'; + revalidatePath(path); + + return NextResponse.json({ status: 201, message: 'Game Hinzugefügt' }) + + } catch (error: any) { + return NextResponse.json({ status: 500, message: error.message }) + } +} \ No newline at end of file diff --git a/components/addGameToList.tsx b/components/addGameToList.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c40c4b9abdbe8f995f85a4fee5e60211024947ba --- /dev/null +++ b/components/addGameToList.tsx @@ -0,0 +1,78 @@ +"use client" + +import { Post, Prisma } from "@prisma/client"; +import { useRouter } from "next/navigation"; +import { startTransition, useEffect, useState } from "react"; + +export default function AddGameToList(props: { userGameList: Number[], gameId: string }) { + + const router = useRouter(); + const gameId = props.gameId + let formData = { gameId: "", add: true } + + async function removeGame(e: any) { + e.preventDefault() + + formData.gameId = gameId; + formData.add = false; + const response = await fetch('http://localhost:3000/api/gameList', { + method: 'PUT', + body: JSON.stringify(formData) + }) + + startTransition(() => { + // Refresh the current route and fetch new data from the server without + // losing client-side browser or React state. + router.refresh(); + }); + return await response.json() + } + + async function addGame(e: any) { + e.preventDefault() + + formData.gameId = gameId; + formData.add = true; + const response = await fetch('http://localhost:3000/api/gameList', { + method: 'PUT', + body: JSON.stringify(formData) + }) + + startTransition(() => { + // Refresh the current route and fetch new data from the server without + // losing client-side browser or React state. + router.refresh(); + }); + return await response.json() + } + + + let button = <div></div>; + try { + if (!props.userGameList.includes(parseFloat(props.gameId))) { + + button = ( + <form onSubmit={addGame}> + <button type="submit" className="mt-2 bg-gray-300 text-gray-800 px-4 py-2 rounded float-right"> + AddGame + </button> + </form> + ) + } else { + button = ( + <form onSubmit={removeGame}> + <button type="submit" className="mt-2 bg-gray-300 text-gray-800 px-4 py-2 rounded float-right"> + Remove Game + </button> + </form> + ) + } + } catch (error) { + + } + + return ( + button + ) +} + diff --git a/lib/igdb.ts b/lib/igdb.ts index a3f26bf32d53791fe2751bf12773973b19714016..d2b94af8234be79d80a1805e727e193856524575 100644 --- a/lib/igdb.ts +++ b/lib/igdb.ts @@ -99,4 +99,37 @@ export async function getGame(id: number): Promise<IGame[]> { }); return games +} + +export async function getFavoriteGames(gamelist: Number[]): Promise<IGame[]> { + const auth = await getToken(); + const url = new URL(`${IGDB_BASE_URL}/games`); + let gamelistString = gamelist.toString() + + console.log("ID STRING:", gamelistString) + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Client-ID': CLIENT_ID, + 'Authorization': `Bearer ${auth.access_token}` + }, + body: + `fields name, cover.image_id; limit ${limit}; + where id = (${gamelistString}); + ` + }); + console.log(response) + if (!response.ok) { + + throw new Error(`Error fetching games: ${response.statusText}`); + } + + const games: IGame[] = await response.json() as IGame[]; + + games.forEach(game => { + game.cover.url = getImageURL(game.cover.image_id, 'cover_big'); + }); + + return games; } \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d48c1ca1166fb604bff5e30cd79ecc23094c6df5..acecf74d5a9e6ce236203459582077b1a6ce1d9d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - generator client { provider = "prisma-client-js" } @@ -16,21 +13,20 @@ model Account { type String provider String providerAccountId String - refresh_token String? @db.Text - access_token String? @db.Text + refresh_token String? + access_token String? expires_at Int? token_type String? scope String? - id_token String? @db.Text + id_token String? session_state String? - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @map("updated_at") refresh_token_expires_in Int? - - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) - @@map(name: "accounts") + @@map("accounts") } model Session { @@ -38,10 +34,9 @@ model Session { sessionToken String @unique userId String @unique expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@map(name: "sessions") + @@map("sessions") } model User { @@ -52,20 +47,18 @@ model User { emailVerified DateTime? password String? image String? - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @map(name: "updated_at") - - accounts Account[] - sessions Session[] - - Post Post[] - Comment Comment[] - Like Like[] - - followers Follows[] @relation("follower") - following Follows[] @relation("following") - - @@map(name: "users") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @map("updated_at") + favGameList Int[] + accounts Account? + Comment Comment[] + following Follows[] @relation("following") + followers Follows[] @relation("follower") + Like Like[] + Post Post[] + sessions Session? + + @@map("users") } model VerificationToken { @@ -74,61 +67,58 @@ model VerificationToken { expires DateTime @@unique([identifier, token]) - @@map(name: "verification_tokens") + @@map("verification_tokens") } model Follows { - follower User @relation("following", fields: [followerId], references: [id]) followerId String - following User @relation("follower", fields: [followingId], references: [id]) followingId String createdAt DateTime @default(now()) + follower User @relation("following", fields: [followerId], references: [id]) + following User @relation("follower", fields: [followingId], references: [id]) @@id([followerId, followingId]) - @@map(name: "follows") + @@map("follows") } model Post { - id String @id @default(cuid()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @map(name: "updated_at") + id String @id @default(cuid()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @map("updated_at") userId String content String - likeCount Int? @default(0) - published Boolean @default(false) + likeCount Int? @default(0) + published Boolean @default(false) + Comment Comment[] + Like Like[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - Comment Comment[] - Like Like[] - - @@map(name: "posts") + @@map("posts") } model Like { - id String @id @default(cuid()) + id String @id @default(cuid()) postId String commentId String? userId String + comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade) + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) - post Post @relation(fields: [postId], references: [id], onDelete: Cascade) - comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@map(name: "likes") + @@map("likes") } model Comment { id String @id @default(cuid()) - createdAt DateTime @default(now()) @map(name: "created_at") - updatedAt DateTime @default(now()) @map(name: "updated_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @map("updated_at") message String likeCount Int? @default(0) postId String userId String + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + Like Like[] - post Post @relation(fields: [postId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - Like Like[] - - @@map(name: "comments") + @@map("comments") }