From b975b5b2ebf3dbbb9e1873b8ab81872dcf2e9f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de> Date: Sat, 1 Jul 2023 01:26:45 +0200 Subject: [PATCH] reformat entire code and make ui layout better, preparing for mobile additions --- .../(gaming)/games/[gameid]/loading.tsx | 8 - .../(gaming)/games/[gameid]/page.tsx | 144 +++--- app/(content)/(gaming)/games/loading.tsx | 34 -- app/(content)/(gaming)/games/page.tsx | 30 +- app/(content)/(home)/home/page.tsx | 35 +- .../(user)/[userid]/followers/page.tsx | 56 ++- app/(content)/(user)/[userid]/loading.tsx | 38 -- app/(content)/(user)/[userid]/page.tsx | 259 +++++----- .../(user)/[userid]/status/[id]/page.tsx | 27 +- app/(content)/layout.tsx | 16 +- app/api/auth/[...nextauth]/route.ts | 1 - app/api/followers/route.ts | 33 +- app/api/gamelists/route.ts | 22 +- app/api/games/route.ts | 24 +- app/api/gweets/[id]/route.ts | 168 +++---- app/api/gweets/likes/route.ts | 170 +++---- app/api/gweets/regweets/route.ts | 104 ++-- app/api/gweets/route.ts | 384 +++++++-------- app/api/hashtags/route.ts | 122 ++--- app/api/signup/route.ts | 6 +- app/api/uploadthing/core.ts | 16 +- app/api/uploadthing/route.ts | 6 +- app/api/users/favgameslist/route.ts | 112 ++--- app/globals.css | 9 - app/layout.tsx | 34 +- app/loading.tsx | 18 +- app/page.tsx | 174 +++---- components/add-game-dropdown.tsx | 20 +- components/add-game-to-finished-list.tsx | 47 +- components/add-game-to-plan-list.tsx | 47 +- components/add-game-to-playing-list.tsx | 47 +- components/addGameToFavList.tsx | 29 +- components/back-button.tsx | 14 +- components/back-header.tsx | 14 +- components/create-gweet/api/post-gweet.ts | 69 +-- .../components/create-gweet-wrapper.tsx | 46 +- .../create-gweet/components/create-gweet.tsx | 458 +++++++++--------- .../create-gweet/hooks/use-create-gweet.ts | 72 +-- components/create-gweet/types/index.ts | 6 +- components/filter-sort-games.tsx | 260 +++++----- components/following-button.tsx | 100 ++-- components/game-item.tsx | 8 +- components/global-layout.tsx | 12 + components/gweets/api/delete-gweet.ts | 18 +- components/gweets/api/get-gweet.ts | 12 +- components/gweets/api/get-gweets.ts | 32 +- components/gweets/api/handle-regweet.ts | 26 +- components/gweets/api/toggle-like.ts | 40 +- .../components/actions/comment-button.tsx | 28 +- .../gweets/components/actions/like-button.tsx | 70 +-- .../components/actions/regweet-button.tsx | 70 +-- components/gweets/components/comments.tsx | 66 +-- .../gweets/components/delete-gweet-modal.tsx | 118 ++--- .../gweets/components/gweet-actions.tsx | 28 +- components/gweets/components/gweet-author.tsx | 12 +- .../gweets/components/gweet-creation-date.tsx | 18 +- .../gweets/components/gweet-details.tsx | 42 +- .../gweets/components/gweet-options.tsx | 54 +-- components/gweets/components/gweet.tsx | 148 +++--- components/gweets/components/gweets.tsx | 66 +-- .../gweets/components/infinite-gweets.tsx | 84 ++-- components/gweets/hooks/use-delete-gweet.ts | 28 +- components/gweets/hooks/use-gweet.ts | 34 +- components/gweets/hooks/use-gweets.ts | 52 +- components/gweets/hooks/use-like.ts | 48 +- components/gweets/hooks/use-regweet.ts | 38 +- components/gweets/types/index.ts | 32 +- components/icons.tsx | 6 +- components/infinity-scroll.tsx | 48 +- components/logo.tsx | 2 +- components/nav.tsx | 30 +- components/profile/types/index.ts | 44 +- components/scroll-to-top.tsx | 28 +- components/search-input.tsx | 22 +- components/trends/api/get-hashtags.ts | 14 +- components/trends/api/post-hashtags.ts | 20 +- .../api/retrieve-hashtags-from-gweet.ts | 6 +- components/trends/components/trend.tsx | 42 +- components/trends/components/trends.tsx | 78 +-- components/trends/hooks/use-hashtags.ts | 34 +- components/trends/index.ts | 7 - components/trends/types/index.ts | 14 +- components/try-again.tsx | 8 +- components/ui/alert.tsx | 74 +-- components/ui/avatar.tsx | 58 +-- components/ui/button.tsx | 82 ++-- components/ui/card.tsx | 91 ++-- components/ui/dialog.tsx | 154 +++--- components/ui/dropdown-menu.tsx | 257 +++++----- components/ui/form.tsx | 11 +- components/ui/hover-card.tsx | 28 +- components/ui/input.tsx | 28 +- components/ui/label.tsx | 18 +- components/ui/scroll-area.tsx | 62 +-- components/ui/select.tsx | 151 +++--- components/ui/separator.tsx | 38 +- components/ui/sheet.tsx | 170 ++++--- components/ui/skeleton.tsx | 16 +- components/ui/textarea.tsx | 26 +- components/ui/toast.tsx | 142 +++--- components/ui/toaster.tsx | 52 +- components/ui/use-toast.ts | 258 +++++----- components/user-auth-form.tsx | 10 +- lib/auth.ts | 18 +- lib/config/dashboard.ts | 2 +- lib/config/platform.ts | 6 +- lib/db.ts | 14 +- lib/igdb.ts | 44 +- lib/react-query/getQueryClient.ts | 8 +- lib/react-query/hydrate.client.tsx | 6 +- lib/react-query/provider.tsx | 10 +- lib/uploadthing.ts | 8 +- lib/utils.ts | 60 +-- lib/validations/auth.ts | 4 +- types/igdb-types.d.ts | 220 ++++----- types/prisma-item.d.ts | 10 +- 116 files changed, 3445 insertions(+), 3557 deletions(-) delete mode 100644 app/(content)/(gaming)/games/[gameid]/loading.tsx delete mode 100644 app/(content)/(gaming)/games/loading.tsx delete mode 100644 app/(content)/(user)/[userid]/loading.tsx create mode 100644 components/global-layout.tsx delete mode 100644 components/trends/index.ts diff --git a/app/(content)/(gaming)/games/[gameid]/loading.tsx b/app/(content)/(gaming)/games/[gameid]/loading.tsx deleted file mode 100644 index 794f6fd..0000000 --- a/app/(content)/(gaming)/games/[gameid]/loading.tsx +++ /dev/null @@ -1,8 +0,0 @@ -// loading component, this renders when loading in /games happens -export default function Loading() { - return ( - <div> - <h1>Game Loading...</h1> - </div> - ) -} \ No newline at end of file diff --git a/app/(content)/(gaming)/games/[gameid]/page.tsx b/app/(content)/(gaming)/games/[gameid]/page.tsx index dacb61f..f344c53 100644 --- a/app/(content)/(gaming)/games/[gameid]/page.tsx +++ b/app/(content)/(gaming)/games/[gameid]/page.tsx @@ -1,20 +1,21 @@ -import AddGameDropdown from "@/components/add-game-dropdown"; -import AddGameToFavList from "@/components/addGameToFavList"; -import { BackHeader } from "@/components/back-header"; -import { AspectRatio } from "@/components/ui/aspect-ratio"; -import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { db } from "@/lib/db"; -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 AddGameDropdown from "@/components/add-game-dropdown" +import AddGameToFavList from "@/components/addGameToFavList" +import { BackHeader } from "@/components/back-header" +import { GlobalLayout } from "@/components/global-layout" +import { AspectRatio } from "@/components/ui/aspect-ratio" +import { Button } from "@/components/ui/button" +import { Card } from "@/components/ui/card" +import { db } from "@/lib/db" +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" // renders a single game detail page export default async function GameDetail({ params }: { params: { gameid: string } }) { const data: IGame[] = await getGame(parseInt(params.gameid)) - + // TODO put to backend const date = formatDate(data[0].first_release_date * 1000) const user = await getCurrentUser() @@ -32,75 +33,78 @@ 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="p-3"> - <BackHeader> - <h1 className="font-bold">Game</h1> - </BackHeader> - </div> + <GlobalLayout + mainContent={ + <Card className="w-full overflow-hidden"> + <div className="p-3"> + <BackHeader> + <h1 className="font-bold">Game</h1> + </BackHeader> + </div> - <div className="h-64 overflow-hidden"> - <AspectRatio ratio={889 / 500}> - <Image - src={data[0].screenshots[0].url} - alt={data[0].name} - fill - priority - className="object-center" /> - </AspectRatio> - </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"> + <div className="h-64 overflow-hidden"> + <AspectRatio ratio={889 / 500}> <Image - src={data[0].cover.url} + src={data[0].screenshots[0].url} alt={data[0].name} fill priority - className="object-cover rounded-lg" /> - </Card> - <div className="flex justify-start p-6"> - <AddGameDropdown fullUser={fullUser!} gameid={params.gameid} /> - </div> + className="object-center" /> + </AspectRatio> </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 className="flex justify-start p-6"> + <AddGameDropdown fullUser={fullUser!} gameid={params.gameid} /> + </div> + </div> - <div className="ml-6 md:ml-12 space-y-3"> - <h1 className="text-2xl font-bold">{data[0].name}</h1> - <h1>released on{' '} - <span className="font-semibold">{date}</span> by{' '} - <span className="font-semibold">{companies}</span> - </h1> - <h1 className="pt-3">{data[0].summary}</h1> + <div className="ml-6 md:ml-12 space-y-3"> + <h1 className="text-2xl font-bold">{data[0].name}</h1> + <h1>released on{' '} + <span className="font-semibold">{date}</span> by{' '} + <span className="font-semibold">{companies}</span> + </h1> + <h1 className="pt-3">{data[0].summary}</h1> - <div className="pt-6"> + <div className="pt-6"> - <h1 className="mb-2">Genres</h1> - <div className="flex flex-wrap gap-2"> - {data[0].genres.map((genre, i) => { - return <Button key={i} variant="outline" size="lg" className="px-6 py-3">{genre.name}</Button> - })} + <h1 className="mb-2">Genres</h1> + <div className="flex flex-wrap gap-2"> + {data[0].genres.map((genre, i) => { + return <Button key={i} variant="outline" size="lg" className="px-6 py-3">{genre.name}</Button> + })} + </div> </div> - </div> - <div className="pt-6"> - <h1 className="mb-2">Platforms</h1> - <div className="flex flex-wrap gap-2"> - {data[0].platforms.map((platform, i) => { - return <Button key={i} variant="outline" size="lg" className="px-6 py-3">{platform.name}</Button> - })} + <div className="pt-6"> + <h1 className="mb-2">Platforms</h1> + <div className="flex flex-wrap gap-2"> + {data[0].platforms.map((platform, i) => { + return <Button key={i} variant="outline" size="lg" className="px-6 py-3">{platform.name}</Button> + })} + </div> </div> </div> </div> - </div> - <div className="px-6 md:px-12"> - <div className="border-b border-gray-400 dark:border-gray-200" /> - <div className="p-6 w-full flex justify-center"> - {user && <AddGameToFavList userGameList={fullUser?.favGameList!} gameId={params.gameid} />} + <div className="px-6 md:px-12"> + <div className="border-b border-gray-400 dark:border-gray-200" /> + <div className="p-6 w-full flex justify-center"> + {user && <AddGameToFavList userGameList={fullUser?.favGameList!} gameId={params.gameid} />} + </div> + {/* comments */} </div> - {/* comments */} - </div> - </Card > - <div className="side-content"> + </Card > + } + + sideContent={ <Card className="p-6 grid items-start gap-2 bg-secondary"> <h1>Screenshots</h1> <div className="grid grid-cols-1 gap-4"> @@ -118,7 +122,7 @@ export default async function GameDetail({ params }: { params: { gameid: string })} </div> </Card> - </div> - </div > + } + /> ) } \ No newline at end of file diff --git a/app/(content)/(gaming)/games/loading.tsx b/app/(content)/(gaming)/games/loading.tsx deleted file mode 100644 index 08b23ae..0000000 --- a/app/(content)/(gaming)/games/loading.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Card } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; - -export default function Loading() { - return ( - <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 diff --git a/app/(content)/(gaming)/games/page.tsx b/app/(content)/(gaming)/games/page.tsx index 17586db..b4aee50 100644 --- a/app/(content)/(gaming)/games/page.tsx +++ b/app/(content)/(gaming)/games/page.tsx @@ -1,20 +1,24 @@ -import Sort from "@/components/filter-sort-games"; -import { InfiniteScrollGames } from "@/components/infinity-scroll"; -import ScrollToTop from "@/components/scroll-to-top"; +import Sort from "@/components/filter-sort-games" +import { GlobalLayout } from "@/components/global-layout" +import { InfiniteScrollGames } from "@/components/infinity-scroll" +import ScrollToTop from "@/components/scroll-to-top" // renders a list of games infinitely export default async function GamesPage() { return ( - <main className="main-content"> - <div className="flex justify-center"> - <div className="fixed top-30 z-40"> - <ScrollToTop /> - </div> - <InfiniteScrollGames /> - </div> - <div className="side-content"> + <GlobalLayout + mainContent={ + <> + <div className="fixed top-30 z-40"> + <ScrollToTop /> + </div> + <InfiniteScrollGames /> + </> + } + + sideContent={ <Sort /> - </div> - </main> + } + /> ) } \ No newline at end of file diff --git a/app/(content)/(home)/home/page.tsx b/app/(content)/(home)/home/page.tsx index 3152776..12366ad 100644 --- a/app/(content)/(home)/home/page.tsx +++ b/app/(content)/(home)/home/page.tsx @@ -1,19 +1,22 @@ -import { CreateGweet } from "@/components/create-gweet/components/create-gweet"; -import { Gweets } from "@/components/gweets/components/gweets"; +import { CreateGweet } from "@/components/create-gweet/components/create-gweet" +import { GlobalLayout } from "@/components/global-layout" +import { Gweets } from "@/components/gweets/components/gweets" +import ScrollToTop from "@/components/scroll-to-top" export default async function HomePage() { - return ( - <div className="main-content px-3 pb-3"> - <div className="lg:col-span-1"> - <CreateGweet /> - <Gweets /> - </div> - - <div className="side-content"> - <div className="flex-col"> - - </div> - </div> - </div> - ) + return ( + <GlobalLayout + mainContent={ + <> + <div className="fixed top-30 z-40"> + <ScrollToTop /> + </div> + <div className="w-full space-y-6 flex flex-col"> + <CreateGweet /> + <Gweets /> + </div> + </> + } + /> + ) } \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/followers/page.tsx b/app/(content)/(user)/[userid]/followers/page.tsx index 4f7528f..4c4cc45 100644 --- a/app/(content)/(user)/[userid]/followers/page.tsx +++ b/app/(content)/(user)/[userid]/followers/page.tsx @@ -1,15 +1,11 @@ -import { UserAvatar } from "@/components/user-avatar"; -import { db } from "@/lib/db"; -import { User } from "@prisma/client"; -import { getServerSession } from "next-auth"; +import { GlobalLayout } from "@/components/global-layout" +import { UserAvatar } from "@/components/user-avatar" +import { db } from "@/lib/db" +import { User } from "@prisma/client" +import Link from "next/link" export default async function UserFollowers({ params }: { params: { userid: string } }) { - // const session = await getServerSession(authOptions); - - // if (!session) { - // return <div>Loading...</div>; - // } const fullUser = await db.user.findFirst({ where: { @@ -21,7 +17,6 @@ export default async function UserFollowers({ params }: { params: { userid: stri } }) - const followers = await db.user.findMany({ where: { following: { @@ -38,21 +33,30 @@ export default async function UserFollowers({ params }: { params: { userid: stri }) return ( - <div> - <h1>Followers Page WIP</h1> - {followers?.map((follower: User) => ( - <div key={follower.id}> {follower.id} <UserAvatar - user={{ username: follower.name || null, image: follower.image || null }} - className="h-20 w-20 -mt-50" /> - - <div className="ml-6 md:ml-12 space-y-3"> - <h1 className="text-2xl font-bold">{follower.name}</h1> - <h1 className="text-md text-sky-500">@{follower.username}</h1> - </div> + <GlobalLayout + mainContent={ + <div className="flex flex-col w-full"> + {followers?.map((follower: User) => ( + <div className="flex items-center space-y-6" key={follower.id}> + <Link href={`/${follower.username}`} className="flex flex-row"> + <UserAvatar + user={{ username: follower.username, image: follower.image }} + className="h-10 w-10" + /> + + <div className="flex flex-col ml-3"> + <span className="font-bold">{follower.name}</span> + <span className="text-sky-500 text-sm">@{follower.username}</span> + </div> + </Link> + + <div className="ml-auto"> + {/* Followbutton */} + </div> + </div> + ))} </div> - ))} - </div> + } + /> ) -} - - +} \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/loading.tsx b/app/(content)/(user)/[userid]/loading.tsx deleted file mode 100644 index 0cc1e86..0000000 --- a/app/(content)/(user)/[userid]/loading.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Card } from "@/components/ui/card" -import { Skeleton } from "@/components/ui/skeleton" - -// loading component, this renders when loading in /games happens -export default function Loading() { - return ( - <div className="main-content h-full"> - <Card className="w-full h-full overflow-hidden"> - <div className="h-64 overflow-hidden"> - <Skeleton className="bg-slate-600 dark:bg-slate-400" /> - </div> - <div className="p-6 md:p-12 ss:flex"> - <Skeleton className="h-52 w-52 -mt-36 rounded-full" /> - <div className="ml-6 md:ml-12 space-y-3"> - <Skeleton className="h-6 w-1/4 bg-gray-400 dark:bg-gray-200" /> - <Skeleton className="h-6 w-1/4 bg-sky-500" /> - </div> - </div> - <div className="px-6 md:px-12"> - {/* <div className="border-b border-gray-400 dark:border-gray-200" /> */} - {/* gweets */} - </div> - </Card > - <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" /> - <div className="grid grid-cols-1 gap-4"> - {Array.from({ length: 2 }, (_, i) => i + 1).map((i) => { - return ( - <Skeleton key={i} className="aspect-[264/374] bg-gray-300" /> - ) - })} - </div> - </Card> - </div> - </div > - ) -} \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/page.tsx b/app/(content)/(user)/[userid]/page.tsx index 9d88a68..d60b12f 100644 --- a/app/(content)/(user)/[userid]/page.tsx +++ b/app/(content)/(user)/[userid]/page.tsx @@ -1,5 +1,6 @@ import FollowButton from "@/components/following-button" import GameItem from "@/components/game-item" +import { GlobalLayout } from "@/components/global-layout" import { AspectRatio } from "@/components/ui/aspect-ratio" import { Card } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" @@ -12,145 +13,145 @@ import { redirect } from "next/navigation" export default async function User({ params }: { params: { userid: string } }) { - const user = await getCurrentUser() + const user = await getCurrentUser() - const sessionUser = await db.user.findFirst({ - where: { - id: user?.id - }, - include: { - following: true, - followers: true - } - }) + const sessionUser = await db.user.findFirst({ + where: { + id: user?.id + }, + include: { + following: true, + followers: true + } + }) - const fullUser = await db.user.findFirst({ - where: { - username: params.userid - }, - include: { - following: true, - followers: true - } - }) + const fullUser = await db.user.findFirst({ + where: { + username: params.userid + }, + include: { + following: true, + followers: true + } + }) - if (!fullUser) { - redirect('/home') - } + if (!fullUser) { + redirect('/home') + } - let favoritegames = undefined - let playingGames = undefined - let finishedGames = undefined - let planningGames = undefined + let favoritegames = undefined + let playingGames = undefined + let finishedGames = undefined + let planningGames = undefined - if (fullUser?.favGameList?.length !== 0 && fullUser?.favGameList?.length != undefined) { - favoritegames = await getFavoriteGames(fullUser?.favGameList!) - } - if (fullUser?.playingGameList?.length !== 0) { - playingGames = await getFavoriteGames(fullUser?.playingGameList!) - } - if (fullUser?.finishedGameList?.length !== 0) { - finishedGames = await getFavoriteGames(fullUser?.finishedGameList!) - } - if (fullUser?.planningGameList?.length !== 0) { - planningGames = await getFavoriteGames(fullUser?.planningGameList!) - } + if (fullUser?.favGameList?.length !== 0 && fullUser?.favGameList?.length != undefined) { + favoritegames = await getFavoriteGames(fullUser?.favGameList!) + } + if (fullUser?.playingGameList?.length !== 0) { + playingGames = await getFavoriteGames(fullUser?.playingGameList!) + } + if (fullUser?.finishedGameList?.length !== 0) { + finishedGames = await getFavoriteGames(fullUser?.finishedGameList!) + } + if (fullUser?.planningGameList?.length !== 0) { + planningGames = await getFavoriteGames(fullUser?.planningGameList!) + } - 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} className="bg-slate-600 dark:bg-slate-400"> - {/* profile banner */} - {/* <Image - src={ } - alt={ } - fill - priority - className="object-center" /> */} - </AspectRatio> - </div> - <div className="p-6 md:p-12 ss:flex"> - <UserAvatar - user={{ username: fullUser.username, image: fullUser.image || null }} - className="h-52 w-52 -mt-36" - /> - <div className="ml-6 md:ml-12 space-y-3"> - <h1 className="text-2xl font-bold">{fullUser.name}</h1> - <h1 className="text-md text-sky-500">@{fullUser.username}</h1> - {/* <h1 className="pt-3">{fullUser.bio}</h1> */} - </div> - <div className="flex justify-start ml-6 md:ml-12 space-y-3"> - <FollowButton user={sessionUser!} followingId={fullUser?.id!} /> - </div> - </div> + return ( + <GlobalLayout + mainContent={ + <div className="space-y-6 w-full"> + <Card className="overflow-hidden"> + <div className="h-64 overflow-hidden"> + <AspectRatio ratio={889 / 500} className="bg-slate-600 dark:bg-slate-400"> + {/* profile banner */} + {/* <Image + src={ } + alt={ } + fill + priority + className="object-center" /> */} + </AspectRatio> + </div> + <div className="p-6 md:p-12 ss:flex"> + <UserAvatar + user={{ username: fullUser.username, image: fullUser.image || null }} + className="h-52 w-52 -mt-36" + /> + <div className="ml-6 md:ml-12 space-y-3"> + <h1 className="text-2xl font-bold">{fullUser.name}</h1> + <h1 className="text-md text-sky-500">@{fullUser.username}</h1> + {/* <h1 className="pt-3">{fullUser.bio}</h1> */} + </div> + <div className="flex justify-start ml-6 md:ml-12 space-y-3"> + <FollowButton user={sessionUser!} followingId={fullUser?.id!} /> + </div> + </div> - <div className="px-6 md:px-12"> - {/* <div className="border-b border-gray-400 dark:border-gray-200" /> */} - {/* gweets */} - </div> - </Card > - <div className="side-content"> - <Card className="p-6 grid items-start gap-2 bg-secondary"> - <h1>Media</h1> - <div className="grid grid-cols-1 gap-4"> - {Array.from({ length: 2 }, (_, i) => i + 1).map((i) => { - return ( - <Skeleton key={i} className="aspect-[264/374] bg-gray-300" /> - ) - })} - </div> - </Card> - </div> + <div className="px-6 md:px-12"> + {/* <div className="border-b border-gray-400 dark:border-gray-200" /> */} + {/* gweets */} + </div> + </Card > + <Card className="overflow-hidden p-6 md:p-12" > + <h1 className="text-2xl font-bold pb-3">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>No favorites currently...</p>} + </div> + </Card> - </div > - <div className="main-content"> - <Card className="w-full h-full overflow-hidden p-6 md:p-12" > - <h1 className="text-2xl font-bold pb-3">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>No favorites currently...</p>} - </div> - </Card> + <Card className="overflow-hidden p-6 md:p-12" > + <h1 className="text-2xl font-bold pb-3">Currently playing</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"> + {playingGames ? playingGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + )) + : + <p>Currently not playing any games...</p>} + </div> + </Card> - <Card className="w-full h-full overflow-hidden p-6 md:p-12" > - <h1 className="text-2xl font-bold pb-3">Currently playing</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"> - {playingGames ? playingGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>Currently not playing any games...</p>} - </div> - </Card> + <Card className="overflow-hidden p-6 md:p-12" > + <h1 className="text-2xl font-bold pb-3">Planning to play</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"> + {planningGames ? planningGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + )) + : + <p>Currently not planning to play any games...</p>} + </div> + </Card> - <Card className="w-full h-full overflow-hidden p-6 md:p-12" > - <h1 className="text-2xl font-bold pb-3">Planning to play</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"> - {planningGames ? planningGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>Currently not planning to play any games...</p>} - </div> - </Card> + <Card className="overflow-hidden p-6 md:p-12" > + <h1 className="text-2xl font-bold pb-3">Finished 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"> + {finishedGames ? finishedGames.map((game: IGame) => ( + <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> + )) + : + <p>No finished games...</p>} + </div> + </Card> + </div> + } - <Card className="w-full h-full overflow-hidden p-6 md:p-12" > - <h1 className="text-2xl font-bold pb-3">Finished 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"> - {finishedGames ? finishedGames.map((game: IGame) => ( - <GameItem id={game.id} name={game.name} cover={game.cover} key={game.id} /> - )) - : - <p>No finished games...</p>} - </div> - </Card> - </div> - </> - ) + sideContent={ + <Card className="p-6 grid items-start gap-2 bg-secondary"> + <h1>Media</h1> + <div className="grid grid-cols-1 gap-4"> + {Array.from({ length: 2 }, (_, i) => i + 1).map((i) => { + return ( + <Skeleton key={i} className="aspect-[264/374] bg-gray-300" /> + ) + })} + </div> + </Card> + } + /> + ) } \ No newline at end of file diff --git a/app/(content)/(user)/[userid]/status/[id]/page.tsx b/app/(content)/(user)/[userid]/status/[id]/page.tsx index cae286f..90586d8 100644 --- a/app/(content)/(user)/[userid]/status/[id]/page.tsx +++ b/app/(content)/(user)/[userid]/status/[id]/page.tsx @@ -1,15 +1,20 @@ -import { BackHeader } from "@/components/back-header"; -import { GweetDetails } from "@/components/gweets/components/gweet-details"; -import { Card } from "@/components/ui/card"; +import { BackHeader } from "@/components/back-header" +import { GlobalLayout } from "@/components/global-layout" +import { GweetDetails } from "@/components/gweets/components/gweet-details" +import { Card } from "@/components/ui/card" export default async function GweetDetailPage() { return ( - <Card className="w-full h-full p-3 xl:p-6 "> - <BackHeader> - <h1 className="font-bold">Gweet</h1> - </BackHeader> + <GlobalLayout + mainContent={ + <Card className="w-full h-full p-3 xl:p-6 "> + <BackHeader> + <h1 className="font-bold">Gweet</h1> + </BackHeader> - <GweetDetails /> - </Card> - ); -}; \ No newline at end of file + <GweetDetails /> + </Card> + } + /> + ) +} \ No newline at end of file diff --git a/app/(content)/layout.tsx b/app/(content)/layout.tsx index 6c0fcad..f8a28f7 100644 --- a/app/(content)/layout.tsx +++ b/app/(content)/layout.tsx @@ -15,11 +15,11 @@ export default async function ContentLayout({ const user = await getCurrentUser() return ( - <div className="flex min-h-screen flex-col space-y-6"> + <main className="flex min-h-screen flex-col space-y-6"> <header className="sticky top-0 z-50 border-b bg-background"> <div className="container flex h-16 items-center justify-between py-4"> <MainNav /> - <SearchInput className="p-3 md:w-2/3 2xl:w-1/3" /> + <SearchInput className="p-3 w-3/6 2xl:w-2/6" /> {user && <UserAccountNav user={{ name: user?.name, @@ -30,15 +30,15 @@ export default async function ContentLayout({ {!user && <p className="w-8"></p>} </div> </header> - <div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr]"> - <aside className="hidden w-[200px] flex-col md:flex"> + + <div className="container grid flex-1 gap-12 grid-cols-5"> + <aside className="self-start sticky top-[89px] col-span-1"> <DashboardNav items={dashboardConfig.sidebarNav} /> </aside> - <main className="flex w-full flex-1 flex-col overflow-hidden"> + <div className="flex flex-1 flex-col w-full h-full col-span-4 pb-6"> {children} - </main> + </div> </div> - <SiteFooter className="border-t" /> - </div> + </main> ) } \ No newline at end of file diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index 17d6a45..dbc44a8 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -4,4 +4,3 @@ import NextAuth from 'next-auth' const handler = NextAuth(authOptions) export { handler as GET, handler as POST } - diff --git a/app/api/followers/route.ts b/app/api/followers/route.ts index f2c8c58..cc32720 100644 --- a/app/api/followers/route.ts +++ b/app/api/followers/route.ts @@ -1,23 +1,22 @@ -import { db } from "@/lib/db"; -import { getCurrentUser } from "@/lib/session"; -import { User } from "@prisma/client"; -import { revalidatePath } from "next/cache"; -import { NextRequest, NextResponse } from "next/server"; +import { db } from "@/lib/db" +import { getCurrentUser } from "@/lib/session" +import { revalidatePath } from "next/cache" +import { NextRequest, NextResponse } from "next/server" export async function GET(req: NextRequest) { - const sessionUser = await getCurrentUser(); + const sessionUser = await getCurrentUser() if (!sessionUser) { - return NextResponse.json({ status: 401, message: 'Unauthorized' }); + return NextResponse.json({ status: 401, message: 'Unauthorized' }) } - let follows = undefined; + let follows = undefined try { follows = await db.follows.findMany({ where: { followerId: sessionUser.id } - }); + }) } catch (error) { console.log("error", error) } @@ -26,12 +25,12 @@ export async function GET(req: NextRequest) { } export async function PUT(req: NextRequest) { - const sessionUser = await getCurrentUser(); + const sessionUser = await getCurrentUser() const data = await req.json() if (!sessionUser) { - return NextResponse.json({ status: 401, message: 'Unauthorized' }); + return NextResponse.json({ status: 401, message: 'Unauthorized' }) } try { @@ -39,14 +38,14 @@ export async function PUT(req: NextRequest) { where: { id: sessionUser.id } - }); + }) const follow = await db.follows.findFirst({ where: { followerId: sessionUser.id, followingId: data.followingId } - }); + }) console.log("follow", follow) if (follow) { @@ -70,16 +69,14 @@ export async function PUT(req: NextRequest) { followerId: sessionUser.id, followingId: data.followingId }, - }); + }) } } catch (error) { console.log("err", error) } - const path = req.nextUrl.searchParams.get('path') || '/'; - revalidatePath(path); + const path = req.nextUrl.searchParams.get('path') || '/' + revalidatePath(path) return NextResponse.json({ status: 200, message: 'Follow handled' }) - - } \ No newline at end of file diff --git a/app/api/gamelists/route.ts b/app/api/gamelists/route.ts index a107c73..7c578b3 100644 --- a/app/api/gamelists/route.ts +++ b/app/api/gamelists/route.ts @@ -1,16 +1,16 @@ -import { db } from "@/lib/db"; -import { getCurrentUser } from "@/lib/session"; -import { User } from "@prisma/client"; -import { revalidatePath } from "next/cache"; -import { NextRequest, NextResponse } from "next/server"; +import { db } from "@/lib/db" +import { getCurrentUser } from "@/lib/session" +import { User } from "@prisma/client" +import { revalidatePath } from "next/cache" +import { NextRequest, NextResponse } from "next/server" export async function PUT(req: NextRequest) { - const sessionUser = await getCurrentUser(); + const sessionUser = await getCurrentUser() const data: User = await req.json() console.log("userid", sessionUser!.id, "formdataid", data.id) if (!sessionUser || sessionUser.id != data.id) { - return NextResponse.json({ status: 401, message: 'Unauthorized' }); + return NextResponse.json({ status: 401, message: 'Unauthorized' }) } console.log("put list") try { @@ -23,7 +23,7 @@ export async function PUT(req: NextRequest) { playingGameList: true, finishedGameList: true }, - }); + }) if (dbUser) { if (!data.finishedGameList) data.finishedGameList = dbUser?.finishedGameList @@ -44,10 +44,8 @@ export async function PUT(req: NextRequest) { } catch (error) { } - const path = req.nextUrl.searchParams.get('path') || '/'; - revalidatePath(path); + const path = req.nextUrl.searchParams.get('path') || '/' + revalidatePath(path) return NextResponse.json({ status: 201, message: 'Game Hinzugefügt' }) - - } \ No newline at end of file diff --git a/app/api/games/route.ts b/app/api/games/route.ts index 7ad46c1..04d0ee9 100644 --- a/app/api/games/route.ts +++ b/app/api/games/route.ts @@ -1,12 +1,12 @@ -import { platforms } from "@/lib/config/platform"; -import { getGames } from "@/lib/igdb"; -import { EGameCategory, EGameGenres, EGamePlatform } from "@/types/constants"; -import { IPlatformCategrory } from "@/types/igdb-types"; +import { platforms } from "@/lib/config/platform" +import { getGames } from "@/lib/igdb" +import { EGameCategory, EGameGenres, EGamePlatform } from "@/types/constants" +import { IPlatformCategrory } from "@/types/igdb-types" -import { NextRequest, NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server" export async function GET(req: NextRequest) { - const p = req.nextUrl.searchParams; + const p = req.nextUrl.searchParams try { const page = parseInt(p.get('page') as string) @@ -17,16 +17,16 @@ export async function GET(req: NextRequest) { const sortby = p.get('sortby') const order = p.get('order') - let filteredPlatforms: EGamePlatform[] | undefined; + let filteredPlatforms: EGamePlatform[] | undefined if (platform) { const selectedCategory = platforms.find( (platformCategory: IPlatformCategrory) => platformCategory.category === platform - ); + ) filteredPlatforms = selectedCategory ? selectedCategory.platforms - : undefined; + : undefined } const games = await getGames(page, @@ -36,9 +36,9 @@ export async function GET(req: NextRequest) { filteredPlatforms, sortby ? sortby : undefined, order ? order : undefined - ); - return NextResponse.json(games); + ) + return NextResponse.json(games) } catch (error) { - return NextResponse.json(error, { status: 500 }); + return NextResponse.json(error, { status: 500 }) } } \ No newline at end of file diff --git a/app/api/gweets/[id]/route.ts b/app/api/gweets/[id]/route.ts index be74cbb..9f04b89 100644 --- a/app/api/gweets/[id]/route.ts +++ b/app/api/gweets/[id]/route.ts @@ -1,98 +1,98 @@ -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { NextResponse } from "next/server" +import { z } from "zod" -import { db } from "@/lib/db"; +import { db } from "@/lib/db" // get a single gweet export async function GET(request: Request, { params }: { params: { id: string } }) { - const { id } = params; + const { id } = params - const gweetIdSchema = z.string().cuid(); + const gweetIdSchema = z.string().cuid() - const zod = gweetIdSchema.safeParse(id); + const zod = gweetIdSchema.safeParse(id) - if (!zod.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zod.error.formErrors, - }, { status: 400 }, - ); - } + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ) + } - try { - const gweet = await db.gweet.findUnique({ - where: { - id, - }, - include: { - author: true, - likes: { - include: { - user: { - include: { - followers: true, - }, - }, - }, - orderBy: { - createdAt: "desc", - }, - }, - media: true, - regweets: { - include: { - user: { - include: { - followers: true, - }, + try { + const gweet = await db.gweet.findUnique({ + where: { + id, }, - }, - orderBy: { - createdAt: "desc", - }, - }, - quote: { - include: { - author: true, - media: true, - }, - }, - allQuotes: { - include: { - likes: true, - regweets: true, - author: true, - quote: { - include: { + include: { author: true, - }, + likes: { + include: { + user: { + include: { + followers: true, + }, + }, + }, + orderBy: { + createdAt: "desc", + }, + }, + media: true, + regweets: { + include: { + user: { + include: { + followers: true, + }, + }, + }, + orderBy: { + createdAt: "desc", + }, + }, + quote: { + include: { + author: true, + media: true, + }, + }, + allQuotes: { + include: { + likes: true, + regweets: true, + author: true, + quote: { + include: { + author: true, + }, + }, + }, + + orderBy: { + createdAt: "desc", + }, + }, + allComments: true, }, - }, + }) - orderBy: { - createdAt: "desc", - }, - }, - allComments: true, - }, - }); + if (!gweet) { + return NextResponse.json( + { + message: "Gweet not found", + }, { status: 404 }, + ) + } - if (!gweet) { - return NextResponse.json( - { - message: "Gweet not found", - }, { status: 404 }, - ); + return NextResponse.json(gweet, { status: 200 }) + } catch (error) { + return NextResponse.json( + { + message: "Something went wrong", + error, + }, { status: 500 }, + ) } - - return NextResponse.json(gweet, { status: 200 }); - } catch (error) { - return NextResponse.json( - { - message: "Something went wrong", - error, - }, { status: 500 }, - ); - } -} +} \ No newline at end of file diff --git a/app/api/gweets/likes/route.ts b/app/api/gweets/likes/route.ts index 029782d..6c298fe 100644 --- a/app/api/gweets/likes/route.ts +++ b/app/api/gweets/likes/route.ts @@ -1,107 +1,107 @@ -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { NextResponse } from "next/server" +import { z } from "zod" -import { db } from "@/lib/db"; +import { db } from "@/lib/db" // get likes from user export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const user_id = searchParams.get("user_id") || undefined; + const { searchParams } = new URL(request.url) + const user_id = searchParams.get("user_id") || undefined - const userIdSchema = z.string().cuid(); - const zod = userIdSchema.safeParse(user_id); + const userIdSchema = z.string().cuid() + const zod = userIdSchema.safeParse(user_id) - if (!zod.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zod.error.formErrors, - }, { status: 400 }, - ); - } + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ) + } - try { - const gweets = await db.gweet.findMany({ - where: { - likes: { - some: { - userId: user_id, - }, - }, - }, + try { + const gweets = await db.gweet.findMany({ + where: { + likes: { + some: { + userId: user_id, + }, + }, + }, - include: { - author: true, - media: true, - likes: true, - regweets: true, - allComments: true, - }, - }); + include: { + author: true, + media: true, + likes: true, + regweets: true, + allComments: true, + }, + }) - return NextResponse.json(gweets, { status: 200 }); - } catch (error: any) { - return NextResponse.json( - { - message: "Something went wrong", - error: error.message, - }, { status: error.errorCode || 500 }, - ); - } + return NextResponse.json(gweets, { status: 200 }) + } catch (error: any) { + return NextResponse.json( + { + message: "Something went wrong", + error: error.message, + }, { status: error.errorCode || 500 }, + ) + } } // like and dislike export async function POST(request: Request) { - const { gweet_id, user_id } = await request.json(); + const { gweet_id, user_id } = await request.json() - const likeSchema = z - .object({ - gweet_id: z.string().cuid(), - user_id: z.string().cuid(), - }) - .strict(); + const likeSchema = z + .object({ + gweet_id: z.string().cuid(), + user_id: z.string().cuid(), + }) + .strict() - const zod = likeSchema.safeParse({ gweet_id, user_id }); + const zod = likeSchema.safeParse({ gweet_id, user_id }) - if (!zod.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zod.error.formErrors, - }, { status: 400 }, - ); - } + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ) + } - try { - const like = await db.like.findFirst({ - where: { - gweetId: gweet_id, - userId: user_id, - }, - }); + try { + const like = await db.like.findFirst({ + where: { + gweetId: gweet_id, + userId: user_id, + }, + }) - if (like) { - await db.like.delete({ - where: { - id: like.id, - }, - }); + if (like) { + await db.like.delete({ + where: { + id: like.id, + }, + }) - return NextResponse.json({ message: "Gweet unliked" }); - } else { - await db.like.create({ - data: { - gweetId: gweet_id, - userId: user_id, - }, - }); + return NextResponse.json({ message: "Gweet unliked" }) + } else { + await db.like.create({ + data: { + gweetId: gweet_id, + userId: user_id, + }, + }) - return NextResponse.json({ message: "Gweet liked" }); + return NextResponse.json({ message: "Gweet liked" }) + } + } catch (error: any) { + return NextResponse.json({ + message: "Something went wrong", + error: error.message, + }) } - } catch (error: any) { - return NextResponse.json({ - message: "Something went wrong", - error: error.message, - }); - } } \ No newline at end of file diff --git a/app/api/gweets/regweets/route.ts b/app/api/gweets/regweets/route.ts index a7672c1..1cc8241 100644 --- a/app/api/gweets/regweets/route.ts +++ b/app/api/gweets/regweets/route.ts @@ -1,56 +1,56 @@ -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { NextResponse } from "next/server" +import { z } from "zod" -import { db } from "@/lib/db"; +import { db } from "@/lib/db" export async function POST(request: Request) { - const { gweet_id, user_id } = await request.json(); - - const regweetSchema = z - .object({ - gweet_id: z.string().cuid(), - user_id: z.string().cuid(), - }) - .strict(); - - const zod = regweetSchema.safeParse({ gweet_id, user_id }); - - if (!zod.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zod.error.formErrors, - }, { status: 400 }, - ); - } - - try { - const regweet = await db.regweet.findFirst({ - where: { - gweetId: gweet_id, - userId: user_id, - }, - }); - - if (regweet) { - await db.regweet.delete({ - where: { - id: regweet.id, - }, - }); - - return NextResponse.json({ message: "Deleted gweet regweet" }); - } else { - await db.regweet.create({ - data: { - gweetId: gweet_id, - userId: user_id, - }, - }); - - return NextResponse.json({ message: "Gweet regweeted" }); + const { gweet_id, user_id } = await request.json() + + const regweetSchema = z + .object({ + gweet_id: z.string().cuid(), + user_id: z.string().cuid(), + }) + .strict() + + const zod = regweetSchema.safeParse({ gweet_id, user_id }) + + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ) } - } catch (error: any) { - return NextResponse.json({ error: error.message }, { status: 500 }); - } -} + + try { + const regweet = await db.regweet.findFirst({ + where: { + gweetId: gweet_id, + userId: user_id, + }, + }) + + if (regweet) { + await db.regweet.delete({ + where: { + id: regweet.id, + }, + }) + + return NextResponse.json({ message: "Deleted gweet regweet" }) + } else { + await db.regweet.create({ + data: { + gweetId: gweet_id, + userId: user_id, + }, + }) + + return NextResponse.json({ message: "Gweet regweeted" }) + } + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }) + } +} \ No newline at end of file diff --git a/app/api/gweets/route.ts b/app/api/gweets/route.ts index 8178a2d..c81cb02 100644 --- a/app/api/gweets/route.ts +++ b/app/api/gweets/route.ts @@ -1,211 +1,211 @@ -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { NextResponse } from "next/server" +import { z } from "zod" -import { db } from "@/lib/db"; -import { utapi } from "uploadthing/server"; +import { db } from "@/lib/db" +import { utapi } from "uploadthing/server" // get gweets export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - - const type = searchParams.get("type") || undefined; - const id = searchParams.get("id") || undefined; - - const cursorQuery = searchParams.get("cursor") || undefined; - const take = Number(searchParams.get("limit")) || 20; - - const skip = cursorQuery ? 1 : 0; - const cursor = cursorQuery ? { id: cursorQuery } : undefined; - - try { - const gweets = await db.gweet.findMany({ - skip, - take, - cursor, - - where: { - ...(type === "comments" && { - replyToGweetId: id, - }), - - ...(type === "search" && { - text: { - contains: id, - mode: "insensitive", - }, - }), - - ...(type === "user_gweets" && { - authorId: id, - }), - - ...(type === "user_replies" && { - authorId: id, - NOT: { - replyToGweetId: null, - }, - }), - - ...(type === "user_likes" && { - likes: { - some: { - userId: id, + const { searchParams } = new URL(request.url) + + const type = searchParams.get("type") || undefined + const id = searchParams.get("id") || undefined + + const cursorQuery = searchParams.get("cursor") || undefined + const take = Number(searchParams.get("limit")) || 20 + + const skip = cursorQuery ? 1 : 0 + const cursor = cursorQuery ? { id: cursorQuery } : undefined + + try { + const gweets = await db.gweet.findMany({ + skip, + take, + cursor, + + where: { + ...(type === "comments" && { + replyToGweetId: id, + }), + + ...(type === "search" && { + text: { + contains: id, + mode: "insensitive", + }, + }), + + ...(type === "user_gweets" && { + authorId: id, + }), + + ...(type === "user_replies" && { + authorId: id, + NOT: { + replyToGweetId: null, + }, + }), + + ...(type === "user_likes" && { + likes: { + some: { + userId: id, + }, + }, + }), }, - }, - }), - }, - - include: { - author: true, - likes: true, - media: true, - regweets: true, - - quote: { - include: { - author: true, - media: true, - }, - }, - - allComments: true, - allQuotes: true, - }, - - orderBy: { - createdAt: "desc", - }, - }); - - const nextId = gweets.length < take ? undefined : gweets[gweets.length - 1].id; - - return NextResponse.json({ gweets, nextId }); - } catch (error) { - return NextResponse.error(); - } + + include: { + author: true, + likes: true, + media: true, + regweets: true, + + quote: { + include: { + author: true, + media: true, + }, + }, + + allComments: true, + allQuotes: true, + }, + + orderBy: { + createdAt: "desc", + }, + }) + + const nextId = gweets.length < take ? undefined : gweets[gweets.length - 1].id + + return NextResponse.json({ gweets, nextId }) + } catch (error) { + return NextResponse.error() + } } // create gweet export async function POST(request: Request) { - const { gweet, fileprops } = await request.json(); - - const gweetSchema = z - .object({ - content: z.string().min(1).max(280), - authorId: z.string().cuid(), - replyToGweetId: z.string().cuid().optional(), - quoteGweetId: z.string().cuid().optional(), - }) - .strict(); - - const zodGweet = gweetSchema.safeParse(gweet); - - const mediaSchema = z.array( - z.object({ - gweetId: z.string().nullable().optional(), - url: z.string(), - key: z.string(), - type: z.string(), - }).strict() - ); - - if (!zodGweet.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zodGweet.error.formErrors, - }, { status: 400 }, - ); - } - - try { - const created_gweet = await db.gweet.create({ - data: { - ...gweet, - }, - }); - - if (fileprops.length > 0) { - const mediaArray = fileprops.map((fileprop: { fileUrl: string; fileKey: string; }) => { - const media = { - gweetId: created_gweet.id, - url: fileprop.fileUrl, - key: fileprop.fileKey, - type: "IMAGE", - } - - return media; - }); + const { gweet, fileprops } = await request.json() + + const gweetSchema = z + .object({ + content: z.string().min(1).max(280), + authorId: z.string().cuid(), + replyToGweetId: z.string().cuid().optional(), + quoteGweetId: z.string().cuid().optional(), + }) + .strict() + + const zodGweet = gweetSchema.safeParse(gweet) + + const mediaSchema = z.array( + z.object({ + gweetId: z.string().nullable().optional(), + url: z.string(), + key: z.string(), + type: z.string(), + }).strict() + ) + + if (!zodGweet.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zodGweet.error.formErrors, + }, { status: 400 }, + ) + } - const zodMedia = mediaSchema.safeParse(mediaArray); + try { + const created_gweet = await db.gweet.create({ + data: { + ...gweet, + }, + }) + + if (fileprops.length > 0) { + const mediaArray = fileprops.map((fileprop: { fileUrl: string; fileKey: string }) => { + const media = { + gweetId: created_gweet.id, + url: fileprop.fileUrl, + key: fileprop.fileKey, + type: "IMAGE", + } + + return media + }) + + const zodMedia = mediaSchema.safeParse(mediaArray) + + if (!zodMedia.success) { + return NextResponse.json( + { + message: "Invalid media body", + error: zodMedia.error.formErrors, + }, { status: 400 }, + ) + } + + await db.media.createMany({ + data: mediaArray, + }) + } - if (!zodMedia.success) { + return NextResponse.json(created_gweet, { status: 200 }) + } catch (error: any) { + console.log(error) return NextResponse.json( - { - message: "Invalid media body", - error: zodMedia.error.formErrors, - }, { status: 400 }, - ); - } - - await db.media.createMany({ - data: mediaArray, - }); + { + message: "Something went wrong", + error: error.message, + }, { status: error.errorCode || 500 }, + ) } - - return NextResponse.json(created_gweet, { status: 200 }); - } catch (error: any) { - console.log(error); - return NextResponse.json( - { - message: "Something went wrong", - error: error.message, - }, { status: error.errorCode || 500 }, - ); - } } // delete gweet export async function DELETE(request: Request) { - const { searchParams } = new URL(request.url); - const id = searchParams.get("id") as string; - - const idSchema = z.string().cuid(); - const zod = idSchema.safeParse(id); - - if (!zod.success) { - return NextResponse.json( - { - message: "Invalid request body", - error: zod.error.formErrors, - }, { status: 400 }, - ); - } - - try { - const checkMedia = await db.media.findMany({ - where: { - gweetId: id, - }, - }); - - if (checkMedia.length > 0) { - await utapi.deleteFiles(checkMedia.map((media) => media.key)); + const { searchParams } = new URL(request.url) + const id = searchParams.get("id") as string + + const idSchema = z.string().cuid() + const zod = idSchema.safeParse(id) + + if (!zod.success) { + return NextResponse.json( + { + message: "Invalid request body", + error: zod.error.formErrors, + }, { status: 400 }, + ) } - await db.gweet.delete({ - where: { - id, - }, - }); - - return NextResponse.json({ message: "Gweet deleted successfully", }); - } catch (error: any) { - return NextResponse.json( - { - message: "Something went wrong", - error: error.message, - }, { status: error.errorCode || 500 }, - ); - } -} + try { + const checkMedia = await db.media.findMany({ + where: { + gweetId: id, + }, + }) + + if (checkMedia.length > 0) { + await utapi.deleteFiles(checkMedia.map((media) => media.key)) + } + + await db.gweet.delete({ + where: { + id, + }, + }) + + return NextResponse.json({ message: "Gweet deleted successfully", }) + } catch (error: any) { + return NextResponse.json( + { + message: "Something went wrong", + error: error.message, + }, { status: error.errorCode || 500 }, + ) + } +} \ No newline at end of file diff --git a/app/api/hashtags/route.ts b/app/api/hashtags/route.ts index e2ab437..f909da1 100644 --- a/app/api/hashtags/route.ts +++ b/app/api/hashtags/route.ts @@ -1,74 +1,74 @@ -import { NextResponse } from "next/server"; -import { z } from "zod"; +import { NextResponse } from "next/server" +import { z } from "zod" -import { db } from "@/lib/db"; +import { db } from "@/lib/db" export async function GET() { - try { - const hashtags = await db.hashtag.findMany({ - take: 10, - orderBy: { - score: "desc", - }, - }); - return NextResponse.json(hashtags, { status: 200 }); - } catch (error: any) { - return NextResponse.json({ error: error.message }, { status: 500 }); - } + try { + const hashtags = await db.hashtag.findMany({ + take: 10, + orderBy: { + score: "desc", + }, + }) + return NextResponse.json(hashtags, { status: 200 }) + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }) + } } export async function POST(request: Request) { - const { hashtags } = await request.json(); + const { hashtags } = await request.json() - const hashtagsSchema = z.array(z.string()); + const hashtagsSchema = z.array(z.string()) - const zod = hashtagsSchema.safeParse(hashtags); + const zod = hashtagsSchema.safeParse(hashtags) + + if (!zod.success) { + return NextResponse.json({ error: zod.error }, { status: 400 }) + } - if (!zod.success) { - return NextResponse.json({ error: zod.error }, { status: 400 }); - } + try { + for (const hashtag of hashtags) { + const hashtagExists = await db.hashtag.findUnique({ + where: { + hashtag: hashtag.toLowerCase(), + }, + }) - try { - for (const hashtag of hashtags) { - const hashtagExists = await db.hashtag.findUnique({ - where: { - hashtag: hashtag.toLowerCase(), - }, - }); + if (hashtagExists) { + await db.hashtag.update({ + where: { + hashtag: hashtag.toLowerCase(), + }, + data: { + score: { + increment: 1, + }, + }, + }) + } else { + await db.hashtag.create({ + data: { + text: hashtag, + hashtag: hashtag.toLowerCase(), + }, + }) + } + } - if (hashtagExists) { - await db.hashtag.update({ - where: { - hashtag: hashtag.toLowerCase(), - }, - data: { - score: { - increment: 1, + return NextResponse.json( + { + message: "Hashtag(s) created", }, - }, - }); - } else { - await db.hashtag.create({ - data: { - text: hashtag, - hashtag: hashtag.toLowerCase(), - }, - }); - } + { status: 200 }, + ) + } catch (error: any) { + return NextResponse.json( + { + error: error.message, + }, + { status: 500 }, + ) } - - return NextResponse.json( - { - message: "Hashtag(s) created", - }, - { status: 200 }, - ); - } catch (error: any) { - return NextResponse.json( - { - error: error.message, - }, - { status: 500 }, - ); - } -} +} \ No newline at end of file diff --git a/app/api/signup/route.ts b/app/api/signup/route.ts index 6d2b390..ffb9dba 100644 --- a/app/api/signup/route.ts +++ b/app/api/signup/route.ts @@ -8,7 +8,7 @@ export async function POST(req: Request) { const { username, email, password } = await req.json() const hashed = await hash(password, 12) - const normalizedName = await normalize(username.toLowerCase()); + const normalizedName = await normalize(username.toLowerCase()) let usernameCheck = normalizedName const emailCheck = email.toLowerCase() @@ -22,7 +22,7 @@ export async function POST(req: Request) { throw new Error('email already exists') } - let isUnique = false; + let isUnique = false while (!isUnique) { const existingUserName = await db.user.findUnique({ where: { @@ -33,7 +33,7 @@ export async function POST(req: Request) { if (existingUserName) { usernameCheck = `${normalizedName}${Math.floor(Math.random() * 1000)}` } else { - isUnique = true; + isUnique = true } } diff --git a/app/api/uploadthing/core.ts b/app/api/uploadthing/core.ts index 3100106..3652b13 100644 --- a/app/api/uploadthing/core.ts +++ b/app/api/uploadthing/core.ts @@ -1,18 +1,18 @@ -import { getCurrentUser } from "@/lib/session"; -import { createUploadthing, type FileRouter } from "uploadthing/next"; +import { getCurrentUser } from "@/lib/session" +import { createUploadthing, type FileRouter } from "uploadthing/next" -const f = createUploadthing(); +const f = createUploadthing() export const ourFileRouter = { imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 4 } }) .middleware(async ({ req }) => { - const user = await getCurrentUser(); + const user = await getCurrentUser() - if (!user) throw new Error("Unauthorized"); + if (!user) throw new Error("Unauthorized") - return { userId: user.id }; + return { userId: user.id } }) .onUploadComplete(async ({ metadata, file }) => { }), -} satisfies FileRouter; +} satisfies FileRouter -export type OurFileRouter = typeof ourFileRouter; \ No newline at end of file +export type OurFileRouter = typeof ourFileRouter \ No newline at end of file diff --git a/app/api/uploadthing/route.ts b/app/api/uploadthing/route.ts index d092e55..abf9a41 100644 --- a/app/api/uploadthing/route.ts +++ b/app/api/uploadthing/route.ts @@ -1,7 +1,7 @@ -import { createNextRouteHandler } from "uploadthing/next"; +import { createNextRouteHandler } from "uploadthing/next" -import { ourFileRouter } from "./core"; +import { ourFileRouter } from "./core" export const { GET, POST } = createNextRouteHandler({ router: ourFileRouter, -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/app/api/users/favgameslist/route.ts b/app/api/users/favgameslist/route.ts index 852db6b..7b06bea 100644 --- a/app/api/users/favgameslist/route.ts +++ b/app/api/users/favgameslist/route.ts @@ -1,59 +1,59 @@ -import { db } from "@/lib/db"; -import { getCurrentUser } from "@/lib/session"; -import { revalidatePath } from "next/cache"; -import { NextRequest, NextResponse } from "next/server"; +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) - - try { - if (data.add) { - await db.user.update({ - where: { - id: userId - }, - data: { - favGameList: { - push: data.gameId - } - } - }) - } 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 }) - } + 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) + + try { + if (data.add) { + await db.user.update({ + where: { + id: userId + }, + data: { + favGameList: { + push: data.gameId + } + } + }) + } 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/app/globals.css b/app/globals.css index 41fd62b..cb7efa4 100644 --- a/app/globals.css +++ b/app/globals.css @@ -95,15 +95,6 @@ } } -@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; diff --git a/app/layout.tsx b/app/layout.tsx index ffdae6c..79aaabb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -8,26 +8,26 @@ import Providers from '@/lib/react-query/provider' const inter = Inter({ subsets: ['latin'] }) export const metadata = { - title: 'Create Next App', - description: 'Generated by create next app', + title: 'Create Next App', + description: 'Generated by create next app', } export default function RootLayout({ - children, + children, }: { - children: React.ReactNode + children: React.ReactNode }) { - return ( - <html lang="en" suppressHydrationWarning> - <head /> - <body className={inter.className}> - <ThemeProvider attribute="class" defaultTheme="system" enableSystem> - <Providers> - {children} - <Toaster /> - </Providers> - </ThemeProvider> - </body> - </html> - ) + return ( + <html lang="en" suppressHydrationWarning> + <head /> + <body className={inter.className}> + <ThemeProvider attribute="class" defaultTheme="system" enableSystem> + <Providers> + {children} + <Toaster /> + </Providers> + </ThemeProvider> + </body> + </html> + ) } \ No newline at end of file diff --git a/app/loading.tsx b/app/loading.tsx index 6d2f459..ec5ceb3 100644 --- a/app/loading.tsx +++ b/app/loading.tsx @@ -1,18 +1,12 @@ -import { Card } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; +import { GameUnityLogo } from "@/components/logo" +// fallback loading component export default function Loading() { return ( - <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 className="flex h-screen justify-center items-center"> + <div className="text-center"> + <GameUnityLogo className="h-10 w-10 animate-bounce" /> </div> - </main> + </div> ) } \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 783ecce..bcd1f81 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,92 +6,92 @@ import { cn } from "@/lib/utils" import Link from "next/link" export default async function IndexPage() { - const user = await getCurrentUser() + 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"> - <Link href="/home" className={cn("rounded-full p-3 hover:bg-accent")}> - <GameUnityLogo className="h-8 w-8" /> - </Link> - </div> - <h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl"> - The ultimate gaming hub - </h1> - <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> - {!user && <div className="align-middle mb-12"> - <Link href="/login" className={cn(buttonVariants({ size: "lg" }), "mr-6")}> - Login - </Link> - <span className="text-muted-foreground">or</span> - <Link href="/signup" className={cn(buttonVariants({ size: "lg" }), "ml-6")}> - Sign-Up - </Link> - </div>} - <Link href="/home" className={cn(buttonVariants({ size: "lg" }))}> - Home Feed - </Link> - <Link href="/games" className={cn(buttonVariants({ size: "lg" }))}> - Games List - </Link> + 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"> + <Link href="/home" className={cn("rounded-full p-3 hover:bg-accent")}> + <GameUnityLogo className="h-8 w-8" /> + </Link> + </div> + <h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl"> + The ultimate gaming hub + </h1> + <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> + {!user && <div className="align-middle mb-12"> + <Link href="/login" className={cn(buttonVariants({ size: "lg" }), "mr-6")}> + Login + </Link> + <span className="text-muted-foreground">or</span> + <Link href="/signup" className={cn(buttonVariants({ size: "lg" }), "ml-6")}> + Sign-Up + </Link> + </div>} + <Link href="/home" className={cn(buttonVariants({ size: "lg" }))}> + Home Feed + </Link> + <Link href="/games" className={cn(buttonVariants({ size: "lg" }))}> + Games List + </Link> + </div> + </section> + <section + id="features" + className="container space-y-6 bg-slate-50 py-8 dark:bg-transparent md:py-12 lg:py-24" + > + <div className="mx-auto flex max-w-[58rem] flex-col items-center space-y-4 text-center"> + <h2 className="font-heading text-3xl leading-[1.1] sm:text-3xl md:text-6xl"> + Features + </h2> + <p className="max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7"> + Create your Profile, find New Games, Rate & Review them, Add your Favorite Games to your List, Help and socialize with other Players, + show of your skills with hightscores and clips + </p> + </div> + <div className="mx-auto grid justify-center gap-4 sm:grid-cols-2 md:max-w-[64rem] md:grid-cols-3"> + <div className="relative overflow-hidden rounded-lg border bg-background p-2"> + <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> + <div className="space-y-2"> + <h3 className="font-bold">Discover new games </h3> + <p className="text-sm text-muted-foreground"> + The platform offers a way to discover and try new games. Through recommendations and ratings from other users, gamers gain inspiration for new games and can broaden their horizons. + </p> + </div> + </div> + </div> + <div className="relative overflow-hidden rounded-lg border bg-background p-2"> + <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> + <div className="space-y-2"> + <h3 className="font-bold">Community-Interaction</h3> + <p className="text-sm text-muted-foreground"> + Users can connect with other players, ask and answer questions, chat with other gamers, and make friends. The platform promotes interaction among gamers and creates a sense of community and belonging. + </p> + </div> + </div> + </div> + <div className="relative overflow-hidden rounded-lg border bg-background p-2"> + <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> + <div className="space-y-2"> + <h3 className="font-bold">Manage your Game collection</h3> + <p className="text-sm text-muted-foreground"> + The platform provides an easy way to search, collect, and organize games. With various filtering and sorting options, users always have an overview of their game collection. + </p> + </div> + </div> + </div> + </div> + <div className="mx-auto text-center md:max-w-[58rem]"> + <p className="leading-normal sm:text-lg sm:leading-7"> + Level up your gaming experience with us + </p> + </div> + </section> + <SiteFooter className="border-t" /> </div> - </section> - <section - id="features" - className="container space-y-6 bg-slate-50 py-8 dark:bg-transparent md:py-12 lg:py-24" - > - <div className="mx-auto flex max-w-[58rem] flex-col items-center space-y-4 text-center"> - <h2 className="font-heading text-3xl leading-[1.1] sm:text-3xl md:text-6xl"> - Features - </h2> - <p className="max-w-[85%] leading-normal text-muted-foreground sm:text-lg sm:leading-7"> - Create your Profile, find New Games, Rate & Review them, Add your Favorite Games to your List, Help and socialize with other Players, - show of your skills with hightscores and clips - </p> - </div> - <div className="mx-auto grid justify-center gap-4 sm:grid-cols-2 md:max-w-[64rem] md:grid-cols-3"> - <div className="relative overflow-hidden rounded-lg border bg-background p-2"> - <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> - <div className="space-y-2"> - <h3 className="font-bold">Discover new games </h3> - <p className="text-sm text-muted-foreground"> - The platform offers a way to discover and try new games. Through recommendations and ratings from other users, gamers gain inspiration for new games and can broaden their horizons. - </p> - </div> - </div> - </div> - <div className="relative overflow-hidden rounded-lg border bg-background p-2"> - <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> - <div className="space-y-2"> - <h3 className="font-bold">Community-Interaction</h3> - <p className="text-sm text-muted-foreground"> - Users can connect with other players, ask and answer questions, chat with other gamers, and make friends. The platform promotes interaction among gamers and creates a sense of community and belonging. - </p> - </div> - </div> - </div> - <div className="relative overflow-hidden rounded-lg border bg-background p-2"> - <div className="flex h-[180px] flex-col justify-between rounded-md p-6"> - <div className="space-y-2"> - <h3 className="font-bold">Manage your Game collection</h3> - <p className="text-sm text-muted-foreground"> - The platform provides an easy way to search, collect, and organize games. With various filtering and sorting options, users always have an overview of their game collection. - </p> - </div> - </div> - </div> - </div> - <div className="mx-auto text-center md:max-w-[58rem]"> - <p className="leading-normal sm:text-lg sm:leading-7"> - Level up your gaming experience with us - </p> - </div> - </section> - <SiteFooter className="border-t" /> - </div> - ) -} + ) +} \ No newline at end of file diff --git a/components/add-game-dropdown.tsx b/components/add-game-dropdown.tsx index e8fb0d4..6a9fa57 100644 --- a/components/add-game-dropdown.tsx +++ b/components/add-game-dropdown.tsx @@ -1,14 +1,14 @@ "use client" -import { useState } from "react"; -import { Card } from "./ui/card"; -import GameItem from "./game-item"; -import { IGame } from "@/types/igdb-types"; -import { User } from "@prisma/client"; -import AddGameToPlanList from "./add-game-to-plan-list"; -import AddGameToPlayingList from "./add-game-to-playing-list"; -import AddGameToFinishedList from "./add-game-to-finished-list"; -import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "./ui/select"; +import { IGame } from "@/types/igdb-types" +import { User } from "@prisma/client" +import { useState } from "react" +import AddGameToFinishedList from "./add-game-to-finished-list" +import AddGameToPlanList from "./add-game-to-plan-list" +import AddGameToPlayingList from "./add-game-to-playing-list" +import GameItem from "./game-item" +import { Card } from "./ui/card" +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "./ui/select" export default function AddGameDropdown(props: { gameid: string, fullUser: User }) { return ( @@ -30,4 +30,4 @@ export default function AddGameDropdown(props: { gameid: string, fullUser: User </> ) -} +} \ No newline at end of file diff --git a/components/add-game-to-finished-list.tsx b/components/add-game-to-finished-list.tsx index 40b4e1d..bc37572 100644 --- a/components/add-game-to-finished-list.tsx +++ b/components/add-game-to-finished-list.tsx @@ -1,23 +1,23 @@ "use client" -import { User } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Button } from "./ui/button"; +import { User } from "@prisma/client" +import { useRouter } from "next/navigation" +import { startTransition } from "react" +import { Button } from "./ui/button" export default function AddGameToFinishedList(props: { gameId: string, user: User }) { - const router = useRouter(); - const gameId = parseFloat(props.gameId); - const user = props.user; + const router = useRouter() + const gameId = parseFloat(props.gameId) + const user = props.user let formData: { - id: String; - gameId: Number; - add: boolean; - planningGameList: number[] | undefined; - playingGameList: number[] | undefined; - finishedGameList: number[] | undefined; + id: String + gameId: Number + add: boolean + planningGameList: number[] | undefined + playingGameList: number[] | undefined + finishedGameList: number[] | undefined } = { id: "", gameId: -1, @@ -25,12 +25,12 @@ export default function AddGameToFinishedList(props: { gameId: string, user: Use planningGameList: undefined, playingGameList: undefined, finishedGameList: undefined - }; + } async function removeGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id formData.finishedGameList = props.user.finishedGameList.filter((id) => id !== gameId) console.log(formData.finishedGameList) const response = await fetch('/api/gamelists', { @@ -41,17 +41,17 @@ export default function AddGameToFinishedList(props: { gameId: string, user: Use startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } async function addGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id props.user.finishedGameList.push(gameId) - formData.finishedGameList = props.user.finishedGameList; + formData.finishedGameList = props.user.finishedGameList const response = await fetch('/api/gamelists', { method: 'PUT', body: JSON.stringify(formData) @@ -60,13 +60,13 @@ export default function AddGameToFinishedList(props: { gameId: string, user: Use startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } - let button = <div></div>; + let button = <div></div> try { if (!props.user.finishedGameList.includes(parseFloat(props.gameId))) { button = ( @@ -92,5 +92,4 @@ export default function AddGameToFinishedList(props: { gameId: string, user: Use return ( button ) -} - +} \ No newline at end of file diff --git a/components/add-game-to-plan-list.tsx b/components/add-game-to-plan-list.tsx index b2c1e45..61f62e8 100644 --- a/components/add-game-to-plan-list.tsx +++ b/components/add-game-to-plan-list.tsx @@ -1,23 +1,23 @@ "use client" -import { User } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Button } from "./ui/button"; +import { User } from "@prisma/client" +import { useRouter } from "next/navigation" +import { startTransition } from "react" +import { Button } from "./ui/button" export default function AddGameToPlanList(props: { gameId: string, user: User }) { - const router = useRouter(); - const gameId = parseFloat(props.gameId); - const user = props.user; + const router = useRouter() + const gameId = parseFloat(props.gameId) + const user = props.user let formData: { - id: String; - gameId: Number; - add: boolean; - planningGameList: number[] | undefined; - playingGameList: number[] | undefined; - finishedGameList: number[] | undefined; + id: String + gameId: Number + add: boolean + planningGameList: number[] | undefined + playingGameList: number[] | undefined + finishedGameList: number[] | undefined } = { id: "", gameId: -1, @@ -25,12 +25,12 @@ export default function AddGameToPlanList(props: { gameId: string, user: User }) planningGameList: undefined, playingGameList: undefined, finishedGameList: undefined - }; + } async function removeGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id formData.planningGameList = props.user.planningGameList.filter((id) => id !== gameId) console.log(formData.planningGameList) const response = await fetch('/api/gamelists', { @@ -41,17 +41,17 @@ export default function AddGameToPlanList(props: { gameId: string, user: User }) startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } async function addGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id props.user.planningGameList.push(gameId) - formData.planningGameList = props.user.planningGameList; + formData.planningGameList = props.user.planningGameList const response = await fetch('/api/gamelists', { method: 'PUT', body: JSON.stringify(formData) @@ -60,13 +60,13 @@ export default function AddGameToPlanList(props: { gameId: string, user: User }) startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } - let button = <div></div>; + let button = <div></div> try { if (!props.user.planningGameList.includes(parseFloat(props.gameId))) { button = ( @@ -92,5 +92,4 @@ export default function AddGameToPlanList(props: { gameId: string, user: User }) return ( button ) -} - +} \ No newline at end of file diff --git a/components/add-game-to-playing-list.tsx b/components/add-game-to-playing-list.tsx index befa763..73e8067 100644 --- a/components/add-game-to-playing-list.tsx +++ b/components/add-game-to-playing-list.tsx @@ -1,23 +1,23 @@ "use client" -import { User } from "@prisma/client"; -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Button } from "./ui/button"; +import { User } from "@prisma/client" +import { useRouter } from "next/navigation" +import { startTransition } from "react" +import { Button } from "./ui/button" export default function AddGameToPlayingList(props: { gameId: string, user: User }) { - const router = useRouter(); - const gameId = parseFloat(props.gameId); - const user = props.user; + const router = useRouter() + const gameId = parseFloat(props.gameId) + const user = props.user let formData: { - id: String; - gameId: Number; - add: boolean; - planningGameList: number[] | undefined; - playingGameList: number[] | undefined; - finishedGameList: number[] | undefined; + id: String + gameId: Number + add: boolean + planningGameList: number[] | undefined + playingGameList: number[] | undefined + finishedGameList: number[] | undefined } = { id: "", gameId: -1, @@ -25,12 +25,12 @@ export default function AddGameToPlayingList(props: { gameId: string, user: User planningGameList: undefined, playingGameList: undefined, finishedGameList: undefined - }; + } async function removeGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id formData.playingGameList = props.user.playingGameList.filter((id) => id !== gameId) console.log(formData.playingGameList) const response = await fetch('/api/gamelists', { @@ -41,17 +41,17 @@ export default function AddGameToPlayingList(props: { gameId: string, user: User startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } async function addGame(e: any) { e.preventDefault() - formData.id = user.id; + formData.id = user.id props.user.playingGameList.push(gameId) - formData.playingGameList = props.user.playingGameList; + formData.playingGameList = props.user.playingGameList const response = await fetch('/api/gamelists', { method: 'PUT', body: JSON.stringify(formData) @@ -60,13 +60,13 @@ export default function AddGameToPlayingList(props: { gameId: string, user: User startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } - let button = <div></div>; + let button = <div></div> try { if (!props.user.playingGameList.includes(parseFloat(props.gameId))) { button = ( @@ -92,5 +92,4 @@ export default function AddGameToPlayingList(props: { gameId: string, user: User return ( button ) -} - +} \ No newline at end of file diff --git a/components/addGameToFavList.tsx b/components/addGameToFavList.tsx index 9500d4d..a2d8cf0 100644 --- a/components/addGameToFavList.tsx +++ b/components/addGameToFavList.tsx @@ -1,20 +1,20 @@ "use client" -import { useRouter } from "next/navigation"; -import { startTransition } from "react"; -import { Button } from "./ui/button"; +import { useRouter } from "next/navigation" +import { startTransition } from "react" +import { Button } from "./ui/button" export default function AddGameToFavList(props: { userGameList: Number[], gameId: string }) { - const router = useRouter(); + 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; + formData.gameId = gameId + formData.add = false const response = await fetch('/api/users/favgameslist', { method: 'PUT', body: JSON.stringify(formData) @@ -23,16 +23,16 @@ export default function AddGameToFavList(props: { userGameList: Number[], gameId startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } async function addGame(e: any) { e.preventDefault() - formData.gameId = gameId; - formData.add = true; + formData.gameId = gameId + formData.add = true const response = await fetch('/api/users/favgameslist', { method: 'PUT', body: JSON.stringify(formData) @@ -41,13 +41,13 @@ export default function AddGameToFavList(props: { userGameList: Number[], gameId startTransition(() => { // Refresh the current route and fetch new data from the server without // losing client-side browser or React state. - router.refresh(); - }); + router.refresh() + }) return await response.json() } - let button = <div></div>; + let button = <div></div> try { if (!props.userGameList.includes(parseFloat(props.gameId))) { button = ( @@ -73,5 +73,4 @@ export default function AddGameToFavList(props: { userGameList: Number[], gameId return ( button ) -} - +} \ No newline at end of file diff --git a/components/back-button.tsx b/components/back-button.tsx index a0be855..6450208 100644 --- a/components/back-button.tsx +++ b/components/back-button.tsx @@ -1,11 +1,11 @@ -"use client"; +"use client" -import { useRouter } from "next/navigation"; -import { Icons } from "./icons"; -import { Button } from "./ui/button"; +import { useRouter } from "next/navigation" +import { Icons } from "./icons" +import { Button } from "./ui/button" export const BackButton = () => { - const router = useRouter(); + const router = useRouter() return ( <Button @@ -16,5 +16,5 @@ export const BackButton = () => { > <Icons.chevronLeft /> </Button> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/back-header.tsx b/components/back-header.tsx index ac66b0e..beedd64 100644 --- a/components/back-header.tsx +++ b/components/back-header.tsx @@ -1,10 +1,10 @@ -import { BackButton } from "@/components/back-button"; +import { BackButton } from "@/components/back-button" export const BackHeader = ({ children }: { children: React.ReactNode }) => { - return ( - <div className="flex items-center space-x-3"> - <BackButton /> - {children} - </div> - ); + return ( + <div className="flex items-center space-x-3"> + <BackButton /> + {children} + </div> + ) } \ No newline at end of file diff --git a/components/create-gweet/api/post-gweet.ts b/components/create-gweet/api/post-gweet.ts index 32d7f2e..76c02d3 100644 --- a/components/create-gweet/api/post-gweet.ts +++ b/components/create-gweet/api/post-gweet.ts @@ -1,42 +1,43 @@ -import { postHashtags, retrieveHashtagsFromGweet } from "@/components/trends"; -import { uploadFiles } from "@/lib/uploadthing"; +import { postHashtags } from "@/components/trends/api/post-hashtags" +import { retrieveHashtagsFromGweet } from "@/components/trends/api/retrieve-hashtags-from-gweet" +import { uploadFiles } from "@/lib/uploadthing" export const postGweet = async ({ - content, - files, - authorId, - replyToGweetId, - quoteGweetId, -}: { - content: string; - files: File[]; - authorId: string; - replyToGweetId?: string | null; - quoteGweetId?: string | null; -}) => { - const gweet = { content, + files, authorId, - ...(replyToGweetId && { replyToGweetId }), - ...(quoteGweetId && { quoteGweetId }), - }; - - try { - let fileprops: { fileUrl: string; fileKey: string; }[] = []; - if (files.length > 0) { - fileprops = await uploadFiles({ files, endpoint: 'imageUploader' }) + replyToGweetId, + quoteGweetId, +}: { + content: string + files: File[] + authorId: string + replyToGweetId?: string | null + quoteGweetId?: string | null +}) => { + const gweet = { + content, + authorId, + ...(replyToGweetId && { replyToGweetId }), + ...(quoteGweetId && { quoteGweetId }), } - const data = await fetch('/api/gweets', { - method: 'POST', - body: JSON.stringify({ gweet, fileprops }) - }).then((result) => result.json()) + try { + let fileprops: { fileUrl: string; fileKey: string }[] = [] + if (files.length > 0) { + fileprops = await uploadFiles({ files, endpoint: 'imageUploader' }) + } + + const data = await fetch('/api/gweets', { + method: 'POST', + body: JSON.stringify({ gweet, fileprops }) + }).then((result) => result.json()) - const hashtags = retrieveHashtagsFromGweet(content); - if (hashtags) await postHashtags(hashtags); + const hashtags = retrieveHashtagsFromGweet(content) + if (hashtags) await postHashtags(hashtags) - return data; - } catch (error: any) { - return error.response.data; - } -}; \ No newline at end of file + return data + } catch (error: any) { + return error.response.data + } +} \ No newline at end of file diff --git a/components/create-gweet/components/create-gweet-wrapper.tsx b/components/create-gweet/components/create-gweet-wrapper.tsx index b057227..2cc2a57 100644 --- a/components/create-gweet/components/create-gweet-wrapper.tsx +++ b/components/create-gweet/components/create-gweet-wrapper.tsx @@ -1,30 +1,30 @@ -"use client"; +"use client" -import { useState } from "react"; +import { useState } from "react" -import { CreateGweet } from "./create-gweet"; +import { CreateGweet } from "./create-gweet" export const CreateGweetWrapper = ({ - replyToGweetId, + replyToGweetId, }: { - replyToGweetId: string | null; + replyToGweetId: string | null }) => { - const [isComment, setIsComment] = useState(true); + const [isComment, setIsComment] = useState(true) - return ( - <div className="px-6"> - <CreateGweet - replyToGweetId={replyToGweetId} - placeholder="Gweet your reply..." - isComment={isComment} - /> - {isComment && ( - <button - onClick={() => { - setIsComment(false); - }} - /> - )} - </div> - ); -}; \ No newline at end of file + return ( + <div className="px-6"> + <CreateGweet + replyToGweetId={replyToGweetId} + placeholder="Gweet your reply..." + isComment={isComment} + /> + {isComment && ( + <button + onClick={() => { + setIsComment(false) + }} + /> + )} + </div> + ) +} \ No newline at end of file diff --git a/components/create-gweet/components/create-gweet.tsx b/components/create-gweet/components/create-gweet.tsx index 71bd6bd..134413b 100644 --- a/components/create-gweet/components/create-gweet.tsx +++ b/components/create-gweet/components/create-gweet.tsx @@ -1,267 +1,267 @@ -"use client"; +"use client" -import { zodResolver } from "@hookform/resolvers/zod"; -import { useSession } from "next-auth/react"; -import Image from "next/image"; -import { useRef, useState } from "react"; -import { useForm } from "react-hook-form"; -import * as z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod" +import { useSession } from "next-auth/react" +import Image from "next/image" +import { useRef, useState } from "react" +import { useForm } from "react-hook-form" +import * as z from "zod" -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/ui/button" import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormMessage, -} from "@/components/ui/form"; + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form" -import { Textarea } from "@/components/ui/textarea"; -import { toast } from "@/components/ui/use-toast"; +import { Textarea } from "@/components/ui/textarea" +import { toast } from "@/components/ui/use-toast" -import { IGweet } from "@/components/gweets/types"; -import { UserAvatar } from "@/components/user-avatar"; +import { IGweet } from "@/components/gweets/types" +import { UserAvatar } from "@/components/user-avatar" -import { Icons } from "@/components/icons"; -import { Card } from "@/components/ui/card"; -import { useCreateGweet } from "../hooks/use-create-gweet"; -import { IChosenImages } from "../types"; +import { Icons } from "@/components/icons" +import { Card } from "@/components/ui/card" +import { useCreateGweet } from "../hooks/use-create-gweet" +import { IChosenImages } from "../types" const FormSchema = z.object({ - gweet: z - .string() - .min(1, { message: "Come on post something..." }) - .max(240, { message: "Gweets cannot be more that 240 characters." }), + gweet: z + .string() + .min(1, { message: "Come on post something..." }) + .max(240, { message: "Gweets cannot be more that 240 characters." }), }) export const CreateGweet = ({ - parent_gweet, - quoted_gweet, - replyToGweetId, - placeholder, - isComment = false, + parent_gweet, + quoted_gweet, + replyToGweetId, + placeholder, + isComment = false, }: { - parent_gweet?: IGweet | null; - quoted_gweet?: IGweet | null; - replyToGweetId?: string | null; - placeholder?: string | null; - isComment?: boolean; + parent_gweet?: IGweet | null + quoted_gweet?: IGweet | null + replyToGweetId?: string | null + placeholder?: string | null + isComment?: boolean }) => { - const [chosenImages, setChosenImages] = useState<IChosenImages[]>([]); - const imageUploadRef = useRef<HTMLInputElement>(null); + const [chosenImages, setChosenImages] = useState<IChosenImages[]>([]) + const imageUploadRef = useRef<HTMLInputElement>(null) - const { data: session } = useSession(); - const { isLoading, mutate, data } = useCreateGweet(); + const { data: session } = useSession() + const { isLoading, mutate, data } = useCreateGweet() - const form = useForm<z.infer<typeof FormSchema>>({ - resolver: zodResolver(FormSchema), - }) + const form = useForm<z.infer<typeof FormSchema>>({ + resolver: zodResolver(FormSchema), + }) - async function onGweet(formData: z.infer<typeof FormSchema>) { - if (!session) return null; + async function onGweet(formData: z.infer<typeof FormSchema>) { + if (!session) return null - mutate({ - content: formData.gweet, - authorId: session?.user?.id, - replyToGweetId, - files: chosenImages.map((image) => image.file), - quoteGweetId: quoted_gweet?.id || null, - }) + mutate({ + content: formData.gweet, + authorId: session?.user?.id, + replyToGweetId, + files: chosenImages.map((image) => image.file), + quoteGweetId: quoted_gweet?.id || null, + }) - toast({ - description: "Your gweet was send.", - }) + toast({ + description: "Your gweet was send.", + }) - form.setValue('gweet', ''); - setChosenImages([]); - } + form.setValue('gweet', '') + setChosenImages([]) + } - const chooseImages = async ( - event: React.ChangeEvent<HTMLInputElement>, - setChosenImages: (images: IChosenImages[]) => void, - ) => { - const files = event.target.files; + const chooseImages = async ( + event: React.ChangeEvent<HTMLInputElement>, + setChosenImages: (images: IChosenImages[]) => void, + ) => { + const files = event.target.files - if (files && files.length > 0) { - const newImages: IChosenImages[] = []; - const totalSelectedImages = chosenImages.length + files.length; + if (files && files.length > 0) { + const newImages: IChosenImages[] = [] + const totalSelectedImages = chosenImages.length + files.length - if (totalSelectedImages > 4) { - return toast({ - variant: "destructive", - description: "You can only upload 4 images per gweet.", - }) - } + if (totalSelectedImages > 4) { + return toast({ + variant: "destructive", + description: "You can only upload 4 images per gweet.", + }) + } - for (let i = 0; i < files.length; i++) { - const filePath = files[i]; + for (let i = 0; i < files.length; i++) { + const filePath = files[i] - const reader = new FileReader(); - reader.readAsDataURL(filePath); + const reader = new FileReader() + reader.readAsDataURL(filePath) - reader.onload = () => { - newImages.push({ - url: reader.result, - file: filePath, - }); + reader.onload = () => { + newImages.push({ + url: reader.result, + file: filePath, + }) - if (newImages.length === files.length) { - setChosenImages([...chosenImages, ...newImages]); - } - }; - } + if (newImages.length === files.length) { + setChosenImages([...chosenImages, ...newImages]) + } + } + } + } } - }; - if (!session) return null; + if (!session) return null - return ( - <> - {/* TODO showing if is replying */} - {parent_gweet && ( - <div className="grid grid-cols-2 gap-11 p-4"> + return ( + <> + {/* TODO showing if is replying */} + {parent_gweet && ( + <div className="grid grid-cols-2 gap-11 p-4"> - <div className="grid place-items-center grid-rows-2 gap-1"> - <UserAvatar - user={{ username: parent_gweet?.author?.username, image: parent_gweet?.author?.image || null }} - /> - <div className="bg-gray-300 h-full w-px"></div> - </div> + <div className="grid place-items-center grid-rows-2 gap-1"> + <UserAvatar + user={{ username: parent_gweet?.author?.username, image: parent_gweet?.author?.image || null }} + /> + <div className="bg-gray-300 h-full w-px"></div> + </div> - <div className="flex flex-col"> - <> - <div className="flex gap-1"> - <span className="text-secondary text-sm font-medium truncate hover:underline"> - {parent_gweet?.author?.name} - </span> - <span className="text-tertiary text-sm truncate"> - @{parent_gweet?.author?.email?.split("@")[0]} - </span> - <span className="text-tertiary">·</span> - </div> - <div> - {parent_gweet?.content && ( - <div className="text">{parent_gweet?.content}</div> - )} - </div> - </> + <div className="flex flex-col"> + <> + <div className="flex gap-1"> + <span className="text-secondary text-sm font-medium truncate hover:underline"> + {parent_gweet?.author?.name} + </span> + <span className="text-tertiary text-sm truncate"> + @{parent_gweet?.author?.email?.split("@")[0]} + </span> + <span className="text-tertiary">·</span> + </div> + <div> + {parent_gweet?.content && ( + <div className="text">{parent_gweet?.content}</div> + )} + </div> + </> - {!isComment && ( - <div className={`${!parent_gweet ? 'ml-16' : ''} flex items-center gap-1 cursor-pointer`}> - <span className="text-tertiary truncate">Replying to</span> - <span className="text-primary truncate"> - @{parent_gweet?.authorId} - </span> - </div> + {!isComment && ( + <div className={`${!parent_gweet ? 'ml-16' : ''} flex items-center gap-1 cursor-pointer`}> + <span className="text-tertiary truncate">Replying to</span> + <span className="text-primary truncate"> + @{parent_gweet?.authorId} + </span> + </div> + )} + </div> + </div> )} - </div> - </div> - )} - <div className="relative"> - <Form {...form}> - <form onSubmit={form.handleSubmit(onGweet)} className="space-y-6"> - <Card className="p-3 flex items-start relative"> - <UserAvatar - className="mr-3" - user={{ username: session?.user.username || null, image: session?.user?.image || null }} - /> - <Button - type="button" - variant="ghost" - size="icon" - className="absolute bottom-0 left-0 mb-3 ml-3" - onClick={() => imageUploadRef.current?.click()} - disabled={isLoading || !session.user} - > - <Icons.media className="text-muted-foreground" /> - </Button> - <div className="flex-grow"> - <FormField - control={form.control} - name="gweet" - render={({ field }) => ( - <FormItem> - <FormControl> - <Textarea - placeholder={placeholder || "What's on your mind?"} - className="resize-none min-h-[100px]" - disabled={isLoading || !session.user} - {...field} - /> - </FormControl> - {!isComment ? - <FormDescription> - Your gweets will be public, and everyone can see them. - </FormDescription> - : null - } - <FormMessage /> - </FormItem> - )} - /> + <div className="relative"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onGweet)} className="space-y-6"> + <Card className="p-3 flex items-start relative"> + <UserAvatar + className="mr-3" + user={{ username: session?.user.username || null, image: session?.user?.image || null }} + /> + <Button + type="button" + variant="ghost" + size="icon" + className="absolute bottom-0 left-0 mb-3 ml-3" + onClick={() => imageUploadRef.current?.click()} + disabled={isLoading || !session.user} + > + <Icons.media className="text-muted-foreground" /> + </Button> + <div className="flex-grow"> + <FormField + control={form.control} + name="gweet" + render={({ field }) => ( + <FormItem> + <FormControl> + <Textarea + placeholder={placeholder || "What's on your mind?"} + className="resize-none min-h-[100px]" + disabled={isLoading || !session.user} + {...field} + /> + </FormControl> + {!isComment ? + <FormDescription> + Your gweets will be public, and everyone can see them. + </FormDescription> + : null + } + <FormMessage /> + </FormItem> + )} + /> - <input - className="hidden w-full resize-none" - type="file" - multiple - onChange={(e) => chooseImages(e, setChosenImages)} - ref={imageUploadRef} - disabled={isLoading || !session.user} - /> - {chosenImages.length > 0 && ( - <div className={`grid object-cover h-[600px] pt-2 - ${chosenImages.length === 1 ? "grid-cols-1" - : chosenImages.length === 2 ? "grid-cols-2 gap-3" - : chosenImages.length === 3 || 4 ? "grid-cols-2 grid-rows-2 gap-3" - : "" - }`} - > - {chosenImages.map((image, i) => { - const isFirstImage = chosenImages.length === 3 && i === 0; - return ( - <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> - <Button - type="button" - size="icon" - variant="secondary" - className="rounded-full absolute top-1 right-1 z-40" - onClick={() => { - setChosenImages( - chosenImages.filter((img, j) => j !== i), - ); - }} - > - <Icons.close className="w-6 h-6" /> - </Button> - <Image - src={image.url as string} - alt="gweet image" - fill - className="object-cover rounded-lg" - /> + <input + className="hidden w-full resize-none" + type="file" + accept="image/*" + multiple + onChange={(e) => chooseImages(e, setChosenImages)} + ref={imageUploadRef} + disabled={isLoading || !session.user} + /> + {chosenImages.length > 0 && ( + <div className={`grid object-cover h-[600px] pt-2 ${chosenImages.length === 1 ? "grid-cols-1" + : chosenImages.length === 2 ? "grid-cols-2 gap-3" + : chosenImages.length === 3 || 4 ? "grid-cols-2 grid-rows-2 gap-3" + : "" + }`} + > + {chosenImages.map((image, i) => { + const isFirstImage = chosenImages.length === 3 && i === 0 + return ( + <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> + <Button + type="button" + size="icon" + variant="secondary" + className="rounded-full absolute top-1 right-1 z-40" + onClick={() => { + setChosenImages( + chosenImages.filter((img, j) => j !== i), + ) + }} + > + <Icons.close className="w-6 h-6" /> + </Button> + <Image + src={image.url as string} + alt="gweet image" + fill + className="object-cover rounded-lg" + /> + </Card> + ) + })} + </div> + )} + {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} + </div> </Card> - ); - })} - </div> - )} - {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} - </div> - </Card> - <div className="flex justify-end"> - <Button type="submit" size="lg" className="w-20" disabled={isLoading || !session.user}> - {isLoading ? ( - <Icons.spinner className="h-4 w-4 animate-spin" /> - ) : ( - isComment ? 'Reply' : 'Gweet' - )} - </Button> + <div className="flex justify-end"> + <Button type="submit" size="lg" className="w-20" disabled={isLoading || !session.user}> + {isLoading ? ( + <Icons.spinner className="h-4 w-4 animate-spin" /> + ) : ( + isComment ? 'Reply' : 'Gweet' + )} + </Button> + </div> + </form> + </Form> </div> - </form> - </Form> - </div> - </> - ); -}; + </> + ) +} \ No newline at end of file diff --git a/components/create-gweet/hooks/use-create-gweet.ts b/components/create-gweet/hooks/use-create-gweet.ts index 686d29a..545aeeb 100644 --- a/components/create-gweet/hooks/use-create-gweet.ts +++ b/components/create-gweet/hooks/use-create-gweet.ts @@ -1,40 +1,40 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query" -import { postGweet } from "../api/post-gweet"; +import { postGweet } from "../api/post-gweet" export const useCreateGweet = () => { - const queryClient = useQueryClient(); + const queryClient = useQueryClient() - return useMutation( - ({ - content, - files, - authorId, - replyToGweetId, - quoteGweetId, - }: { - content: string; - files: File[]; - authorId: string; - replyToGweetId?: string | null; - quoteGweetId?: string | null; - }) => { - return postGweet({ - content, - files, - authorId, - replyToGweetId, - quoteGweetId, - }); - }, - { - onSuccess: () => { - queryClient.invalidateQueries(["gweets"]); - queryClient.invalidateQueries(["hashtags"]); - }, - onError: (error) => { - console.log("error", error); - }, - }, - ); -}; \ No newline at end of file + return useMutation( + ({ + content, + files, + authorId, + replyToGweetId, + quoteGweetId, + }: { + content: string + files: File[] + authorId: string + replyToGweetId?: string | null + quoteGweetId?: string | null + }) => { + return postGweet({ + content, + files, + authorId, + replyToGweetId, + quoteGweetId, + }) + }, + { + onSuccess: () => { + queryClient.invalidateQueries(["gweets"]) + queryClient.invalidateQueries(["hashtags"]) + }, + onError: (error) => { + console.log("error", error) + }, + }, + ) +} \ No newline at end of file diff --git a/components/create-gweet/types/index.ts b/components/create-gweet/types/index.ts index 0e7b1e4..01e3118 100644 --- a/components/create-gweet/types/index.ts +++ b/components/create-gweet/types/index.ts @@ -1,8 +1,8 @@ export interface Post { - id: string; + id: string } export interface IChosenImages { - url: string | ArrayBuffer | null; - file: File; + url: string | ArrayBuffer | null + file: File } \ No newline at end of file diff --git a/components/filter-sort-games.tsx b/components/filter-sort-games.tsx index 9b37f76..12ef0c9 100644 --- a/components/filter-sort-games.tsx +++ b/components/filter-sort-games.tsx @@ -1,148 +1,148 @@ "use client" -import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; -import { useEffect, useState } from "react"; -import { Icons } from "./icons"; -import { Button } from "./ui/button"; -import { Card } from "./ui/card"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select" +import { usePathname, useRouter, useSearchParams } from "next/navigation" +import { useEffect, useState } from "react" +import { Icons } from "./icons" +import { Button } from "./ui/button" +import { Card } from "./ui/card" export default function Sort() { - const [selectedCategory, setSelectedCategory] = useState(''); - const [selectedGenre, setSelectedGenre] = useState(''); - const [selectedPlatform, setSelectedPlatform] = useState(''); - const [selectedSortMethod, setSelectedSortMethod] = useState('total_rating_count'); - const [selectedSortOrder, setSelectedSortOrder] = useState('desc'); + const [selectedCategory, setSelectedCategory] = useState('') + const [selectedGenre, setSelectedGenre] = useState('') + const [selectedPlatform, setSelectedPlatform] = useState('') + const [selectedSortMethod, setSelectedSortMethod] = useState('total_rating_count') + const [selectedSortOrder, setSelectedSortOrder] = useState('desc') - const router = useRouter(); - const pathname = usePathname(); - const searchParams = useSearchParams(); + const router = useRouter() + const pathname = usePathname() + const searchParams = useSearchParams() - const search = searchParams.get('search'); + const search = searchParams.get('search') - const urlParams = { - search: search ? `search=${search}` : '', - category: selectedCategory ? `category=${selectedCategory}` : '', - genre: selectedGenre ? `genre=${selectedGenre}` : '', - platform: selectedPlatform ? `platform=${selectedPlatform}` : '', - sortMethod: selectedSortMethod ? `sortby=${selectedSortMethod}` : '', - sortOrder: selectedSortOrder ? `order=${selectedSortOrder}` : '', - }; + const urlParams = { + search: search ? `search=${search}` : '', + category: selectedCategory ? `category=${selectedCategory}` : '', + genre: selectedGenre ? `genre=${selectedGenre}` : '', + platform: selectedPlatform ? `platform=${selectedPlatform}` : '', + sortMethod: selectedSortMethod ? `sortby=${selectedSortMethod}` : '', + sortOrder: selectedSortOrder ? `order=${selectedSortOrder}` : '', + } - const queryParamString = Object.values(urlParams) - .filter(Boolean) - .map((param, index) => (index === 0 ? `?${param}` : `&${param}`)) - .join(''); + const queryParamString = Object.values(urlParams) + .filter(Boolean) + .map((param, index) => (index === 0 ? `?${param}` : `&${param}`)) + .join('') - const url = `${pathname}${queryParamString ? `${queryParamString}` : ''}`; + const url = `${pathname}${queryParamString ? `${queryParamString}` : ''}` - // useEffect(() => { - // if (queryParamString) { - router.replace(url); - // } - // }, [queryParamString, router, url]); + // useEffect(() => { + // if (queryParamString) { + router.replace(url) + // } + // }, [queryParamString, router, url]); - function toggleSortOrder() { - const newSortOrder = selectedSortOrder === 'desc' ? 'asc' : 'desc'; - setSelectedSortOrder(newSortOrder); - } + function toggleSortOrder() { + const newSortOrder = selectedSortOrder === 'desc' ? 'asc' : 'desc' + setSelectedSortOrder(newSortOrder) + } - function onReset() { - router.replace(pathname + (search ? `?search=${search}` : '')); - setSelectedCategory(''); - setSelectedGenre(''); - setSelectedPlatform(''); - setSelectedSortMethod('total_rating_count'); - setSelectedSortOrder('desc'); - } + function onReset() { + router.replace(pathname + (search ? `?search=${search}` : '')) + setSelectedCategory('') + setSelectedGenre('') + setSelectedPlatform('') + setSelectedSortMethod('total_rating_count') + setSelectedSortOrder('desc') + } - return ( - <Card className="p-6 grid items-start gap-2 bg-secondary"> - <h1>Filter</h1> - <Select value={selectedCategory ? selectedCategory : undefined} key={selectedCategory[0]} onValueChange={(value) => setSelectedCategory(value)}> - <SelectTrigger className={`bg-background border-none w-full ${selectedCategory[0] ? 'font-extrabold' : ''}`}> - <SelectValue placeholder="By category..." /> - </SelectTrigger> - <SelectContent> - <SelectGroup> - <SelectLabel>Category</SelectLabel> - <SelectItem value="main_game">Main Game</SelectItem> - <SelectItem value="dlc_addon">DLC Addon</SelectItem> - <SelectItem value="expansion">Expansion</SelectItem> - <SelectItem value="remake">Remake</SelectItem> - <SelectItem value="remaster">Remaster</SelectItem> - <SelectItem value="port">Port</SelectItem> - </SelectGroup> - </SelectContent> - </Select> + return ( + <Card className="p-6 grid items-start gap-2 bg-secondary"> + <h1>Filter</h1> + <Select value={selectedCategory ? selectedCategory : undefined} key={selectedCategory[0]} onValueChange={(value) => setSelectedCategory(value)}> + <SelectTrigger className={`bg-background border-none w-full ${selectedCategory[0] ? 'font-extrabold' : ''}`}> + <SelectValue placeholder="By category..." /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectLabel>Category</SelectLabel> + <SelectItem value="main_game">Main Game</SelectItem> + <SelectItem value="dlc_addon">DLC Addon</SelectItem> + <SelectItem value="expansion">Expansion</SelectItem> + <SelectItem value="remake">Remake</SelectItem> + <SelectItem value="remaster">Remaster</SelectItem> + <SelectItem value="port">Port</SelectItem> + </SelectGroup> + </SelectContent> + </Select> - <Select value={selectedGenre ? selectedGenre : undefined} key={selectedGenre[0]} onValueChange={(value) => setSelectedGenre(value)}> - <SelectTrigger className={`bg-background border-none w-full ${selectedGenre[0] ? 'font-extrabold' : ''}`}> - <SelectValue placeholder="By genre..." /> - </SelectTrigger> - <SelectContent> - <SelectGroup> - <SelectLabel>Genre</SelectLabel> - <SelectItem value="fighting">Fighting</SelectItem> - <SelectItem value="shooter">Shooter</SelectItem> - <SelectItem value="music">Music</SelectItem> - <SelectItem value="platform">Platformer</SelectItem> - <SelectItem value="puzzle">Puzzle</SelectItem> - <SelectItem value="real-time-strategy-rts">Real Time Strategy</SelectItem> - <SelectItem value="role-playing-rpg">Role-playing</SelectItem> - <SelectItem value="simulator">Simulation</SelectItem> - <SelectItem value="sport">Sport</SelectItem> - <SelectItem value="turn-based-strategy-tbs">Turn-based strategy</SelectItem> - <SelectItem value="tactical">Tactical</SelectItem> - <SelectItem value="quiz-trivia">Quiz/Trivia</SelectItem> - <SelectItem value="hack-and-slash-beat-em-up">Hack and slash</SelectItem> - <SelectItem value="pinball">Pinball</SelectItem> - <SelectItem value="adventure">Adventure</SelectItem> - <SelectItem value="arcade">Arcade</SelectItem> - <SelectItem value="visual-novel">Visual Novel</SelectItem> - <SelectItem value="card-and-board-game">Card & Board Game</SelectItem> - <SelectItem value="adventure">Adventure</SelectItem> - <SelectItem value="moba">Moba</SelectItem> - <SelectItem value="point-and-click">Point-and-click</SelectItem> - </SelectGroup> - </SelectContent> - </Select> + <Select value={selectedGenre ? selectedGenre : undefined} key={selectedGenre[0]} onValueChange={(value) => setSelectedGenre(value)}> + <SelectTrigger className={`bg-background border-none w-full ${selectedGenre[0] ? 'font-extrabold' : ''}`}> + <SelectValue placeholder="By genre..." /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectLabel>Genre</SelectLabel> + <SelectItem value="fighting">Fighting</SelectItem> + <SelectItem value="shooter">Shooter</SelectItem> + <SelectItem value="music">Music</SelectItem> + <SelectItem value="platform">Platformer</SelectItem> + <SelectItem value="puzzle">Puzzle</SelectItem> + <SelectItem value="real-time-strategy-rts">Real Time Strategy</SelectItem> + <SelectItem value="role-playing-rpg">Role-playing</SelectItem> + <SelectItem value="simulator">Simulation</SelectItem> + <SelectItem value="sport">Sport</SelectItem> + <SelectItem value="turn-based-strategy-tbs">Turn-based strategy</SelectItem> + <SelectItem value="tactical">Tactical</SelectItem> + <SelectItem value="quiz-trivia">Quiz/Trivia</SelectItem> + <SelectItem value="hack-and-slash-beat-em-up">Hack and slash</SelectItem> + <SelectItem value="pinball">Pinball</SelectItem> + <SelectItem value="adventure">Adventure</SelectItem> + <SelectItem value="arcade">Arcade</SelectItem> + <SelectItem value="visual-novel">Visual Novel</SelectItem> + <SelectItem value="card-and-board-game">Card & Board Game</SelectItem> + <SelectItem value="adventure">Adventure</SelectItem> + <SelectItem value="moba">Moba</SelectItem> + <SelectItem value="point-and-click">Point-and-click</SelectItem> + </SelectGroup> + </SelectContent> + </Select> - <Select value={selectedPlatform ? selectedPlatform : undefined} key={selectedPlatform[0]} onValueChange={(value) => setSelectedPlatform(value)}> - <SelectTrigger className={`bg-background border-none w-full ${selectedPlatform[0] ? 'font-extrabold' : ''}`}> - <SelectValue placeholder="By Platform..." /> - </SelectTrigger> - <SelectContent> - <SelectGroup> - <SelectLabel>Platform</SelectLabel> - <SelectItem value="pc">PC</SelectItem> - <SelectItem value="playstation">Playstation</SelectItem> - <SelectItem value="xbox">Xbox</SelectItem> - <SelectItem value="nintendo">Nintendo</SelectItem> - </SelectGroup> - </SelectContent> - </Select> + <Select value={selectedPlatform ? selectedPlatform : undefined} key={selectedPlatform[0]} onValueChange={(value) => setSelectedPlatform(value)}> + <SelectTrigger className={`bg-background border-none w-full ${selectedPlatform[0] ? 'font-extrabold' : ''}`}> + <SelectValue placeholder="By Platform..." /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectLabel>Platform</SelectLabel> + <SelectItem value="pc">PC</SelectItem> + <SelectItem value="playstation">Playstation</SelectItem> + <SelectItem value="xbox">Xbox</SelectItem> + <SelectItem value="nintendo">Nintendo</SelectItem> + </SelectGroup> + </SelectContent> + </Select> - <h1 className="pt-6">Sort by</h1> - <div className="flex space-x-2 pb-1"> - <Select value={selectedSortMethod} onValueChange={(value) => setSelectedSortMethod(value)}> - <SelectTrigger className="bg-background border-none w-full"> - <SelectValue placeholder="Rating" /> - </SelectTrigger> - <SelectContent> - <SelectGroup> - <SelectLabel>Sorting</SelectLabel> - <SelectItem value="name">Name</SelectItem> - <SelectItem value="total_rating_count">Rating</SelectItem> - </SelectGroup> - </SelectContent> - </Select> - <Button variant="ghost" onClick={() => toggleSortOrder()} className="bg-background border-none"> - <Icons.arrowdown className={`h-4 w-4 transition-all transform ${selectedSortOrder === 'asc' ? 'rotate-180' : ''}`} /> - </Button> - </div> + <h1 className="pt-6">Sort by</h1> + <div className="flex space-x-2 pb-1"> + <Select value={selectedSortMethod} onValueChange={(value) => setSelectedSortMethod(value)}> + <SelectTrigger className="bg-background border-none w-full"> + <SelectValue placeholder="Rating" /> + </SelectTrigger> + <SelectContent> + <SelectGroup> + <SelectLabel>Sorting</SelectLabel> + <SelectItem value="name">Name</SelectItem> + <SelectItem value="total_rating_count">Rating</SelectItem> + </SelectGroup> + </SelectContent> + </Select> + <Button variant="ghost" onClick={() => toggleSortOrder()} className="bg-background border-none"> + <Icons.arrowdown className={`h-4 w-4 transition-all transform ${selectedSortOrder === 'asc' ? 'rotate-180' : ''}`} /> + </Button> + </div> - <Button variant="outline" onClick={() => onReset()}>Reset to Default</Button> - </Card> - ) + <Button variant="outline" onClick={() => onReset()}>Reset to Default</Button> + </Card> + ) } \ No newline at end of file diff --git a/components/following-button.tsx b/components/following-button.tsx index 68df591..b5eb3f4 100644 --- a/components/following-button.tsx +++ b/components/following-button.tsx @@ -1,19 +1,19 @@ "use client" -import { Follows, Prisma } from '@prisma/client'; -import { useSession } from 'next-auth/react'; -import { usePathname } from "next/navigation"; -import { useEffect, useState } from 'react'; -import { Button } from './ui/button'; +import { Follows, Prisma } from '@prisma/client' +import { useSession } from 'next-auth/react' +import { usePathname } from "next/navigation" +import { useEffect, useState } from 'react' +import { Button } from './ui/button' // 1: Define a type that includes the relation to `Post` const userWithFollows = Prisma.validator<Prisma.UserArgs>()({ - include: { followers: true, following: true }, + include: { followers: true, following: true }, }) // 2: Define a type that only contains a subset of the scalar fields const userPersonalData = Prisma.validator<Prisma.UserArgs>()({ - select: { email: true, name: true }, + select: { email: true, name: true }, }) // 3: This type will include a user and all their posts @@ -21,55 +21,55 @@ type UserWithFollows = Prisma.UserGetPayload<typeof userWithFollows> export default function FollowButton({ user, followingId }: { user: UserWithFollows; followingId: string }) { - const [isFollowing, setIsFollowing] = useState(false); - const [follows, setFollows] = useState([]); - const [buttonPressed, setButtonPress] = useState(false); + const [isFollowing, setIsFollowing] = useState(false) + const [follows, setFollows] = useState([]) + const [buttonPressed, setButtonPress] = useState(false) - const pathname = usePathname() - const { data: session } = useSession(); + const pathname = usePathname() + const { data: session } = useSession() - async function fetchFollows() { + async function fetchFollows() { - try { - const response = await fetch('/api/followers') + try { + const response = await fetch('/api/followers') - const data = await response.json() - setFollows(data.follows) - setIsFollowing(false) - data.follows?.forEach((f: Follows) => { + const data = await response.json() + setFollows(data.follows) + setIsFollowing(false) + data.follows?.forEach((f: Follows) => { + + if (f.followerId == user.id && f.followingId == followingId) { + setIsFollowing(true) + return + } + }) + } catch (error) { - if (f.followerId == user.id && f.followingId == followingId) { - setIsFollowing(true) - return; } - }) - } catch (error) { + } + useEffect(() => { + fetchFollows() + // TODO: fix this warning + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [buttonPressed]) + + async function handleFollow(e: any) { + e.preventDefault() + const response = await fetch('/api/followers', { + method: 'PUT', + body: JSON.stringify({ user, followingId }) + }) + + setButtonPress(!buttonPressed) } - } - useEffect(() => { - fetchFollows() - // TODO: fix this warning - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [buttonPressed]) - - async function handleFollow(e: any) { - e.preventDefault() - - const response = await fetch('/api/followers', { - method: 'PUT', - body: JSON.stringify({ user, followingId }) - }) - - setButtonPress(!buttonPressed) - } - - return ( - <> - {pathname !== `/${session?.user.username}` && - <Button onClick={handleFollow}> - {isFollowing ? 'Unfollow' : 'Follow'} - </Button>} - </> - ); + + return ( + <> + {pathname !== `/${session?.user.username}` && + <Button onClick={handleFollow}> + {isFollowing ? 'Unfollow' : 'Follow'} + </Button>} + </> + ) } \ No newline at end of file diff --git a/components/game-item.tsx b/components/game-item.tsx index 32655f8..b508152 100644 --- a/components/game-item.tsx +++ b/components/game-item.tsx @@ -1,11 +1,11 @@ -import { Card } from "@/components/ui/card"; -import Image from "next/image"; -import Link from "next/link"; +import { Card } from "@/components/ui/card" +import Image from "next/image" +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 ( - <Link href={`/games/${id}`}> + <Link href={`/games/${id}`} prefetch={false}> <Card className="aspect-[264/374] relative block group items-center justify-center overflow-hidden"> <Image src={cover.url} diff --git a/components/global-layout.tsx b/components/global-layout.tsx new file mode 100644 index 0000000..8d669c2 --- /dev/null +++ b/components/global-layout.tsx @@ -0,0 +1,12 @@ +export const GlobalLayout = ({ mainContent, sideContent }: { mainContent: JSX.Element, sideContent?: JSX.Element }) => { + return ( + <div className="grid grid-cols-4 gap-12 h-full w-full"> + <div className="col-span-3 flex justify-center"> + {mainContent} + </div> + <aside className="self-start sticky top-[89px] col-span-1"> + {sideContent} + </aside> + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/api/delete-gweet.ts b/components/gweets/api/delete-gweet.ts index 0d9f6bd..3ad7a6c 100644 --- a/components/gweets/api/delete-gweet.ts +++ b/components/gweets/api/delete-gweet.ts @@ -1,11 +1,11 @@ export const deleteGweet = async (gweetId: string) => { - try { - const data = await fetch(`/api/gweets?id=${gweetId}`, { - method: 'DELETE' - }).then((result) => result.json()); + try { + const data = await fetch(`/api/gweets?id=${gweetId}`, { + method: 'DELETE' + }).then((result) => result.json()) - return data; - } catch (error: any) { - console.log(error); - } -}; \ No newline at end of file + return data + } catch (error: any) { + console.log(error) + } +} \ No newline at end of file diff --git a/components/gweets/api/get-gweet.ts b/components/gweets/api/get-gweet.ts index 342687e..8b36271 100644 --- a/components/gweets/api/get-gweet.ts +++ b/components/gweets/api/get-gweet.ts @@ -1,9 +1,9 @@ export default async function getGweet(id: string | undefined) { - try { - const data = await fetch(`/api/gweets/${id}`).then((result) => result.json()); + try { + const data = await fetch(`/api/gweets/${id}`).then((result) => result.json()) - return data; - } catch (error: any) { - return error.response.data; - } + return data + } catch (error: any) { + return error.response.data + } } \ No newline at end of file diff --git a/components/gweets/api/get-gweets.ts b/components/gweets/api/get-gweets.ts index 56fa0eb..18bb683 100644 --- a/components/gweets/api/get-gweets.ts +++ b/components/gweets/api/get-gweets.ts @@ -1,20 +1,20 @@ export const getGweets = async ({ - pageParam = "", - limit = 20, - type, - id, + pageParam = "", + limit = 20, + type, + id, }: { - pageParam?: string; - limit?: number; - type?: string; - id?: string; + pageParam?: string + limit?: number + type?: string + id?: string }) => { - try { - const url = `/api/gweets?cursor=${pageParam}&limit=${limit}${type ? `&type=${type}` : ""}${id ? `&id=${id}` : ""}`; - const data = await fetch(url).then((result) => result.json()); + try { + const url = `/api/gweets?cursor=${pageParam}&limit=${limit}${type ? `&type=${type}` : ""}${id ? `&id=${id}` : ""}` + const data = await fetch(url).then((result) => result.json()) - return data; - } catch (error: any) { - return error.response.data; - } -}; \ No newline at end of file + return data + } catch (error: any) { + return error.response.data + } +} \ No newline at end of file diff --git a/components/gweets/api/handle-regweet.ts b/components/gweets/api/handle-regweet.ts index 0607ca8..b66376a 100644 --- a/components/gweets/api/handle-regweet.ts +++ b/components/gweets/api/handle-regweet.ts @@ -1,15 +1,15 @@ export const handleRegweet = async (gweetId: string, userId: string) => { - try { - const data = await fetch("/api/gweets/regweets", { - method: "POST", - body: JSON.stringify({ - gweet_id: gweetId, - user_id: userId, - }), - }).then((result) => result.json()); + try { + const data = await fetch("/api/gweets/regweets", { + method: "POST", + body: JSON.stringify({ + gweet_id: gweetId, + user_id: userId, + }), + }).then((result) => result.json()) - return data; - } catch (error: any) { - return error.response.data; - } -}; \ No newline at end of file + return data + } catch (error: any) { + return error.response.data + } +} \ No newline at end of file diff --git a/components/gweets/api/toggle-like.ts b/components/gweets/api/toggle-like.ts index 08b0ab7..d3924ca 100644 --- a/components/gweets/api/toggle-like.ts +++ b/components/gweets/api/toggle-like.ts @@ -1,24 +1,24 @@ export const toggleLike = async ({ - gweetId, - userId, + gweetId, + userId, }: { - gweetId: string | undefined; - userId: string | undefined; + gweetId: string | undefined + userId: string | undefined }) => { - try { - const data = await fetch("/api/gweets/likes", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - gweet_id: gweetId, - user_id: userId, - }), - }).then((result) => result.json()); + try { + const data = await fetch("/api/gweets/likes", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + gweet_id: gweetId, + user_id: userId, + }), + }).then((result) => result.json()) - return data; - } catch (error: any) { - return error.message; - } -}; \ No newline at end of file + return data + } catch (error: any) { + return error.message + } +} \ No newline at end of file diff --git a/components/gweets/components/actions/comment-button.tsx b/components/gweets/components/actions/comment-button.tsx index d5c53c4..b93be12 100644 --- a/components/gweets/components/actions/comment-button.tsx +++ b/components/gweets/components/actions/comment-button.tsx @@ -1,18 +1,18 @@ -import { Button } from "@/components/ui/button"; -import { IGweet } from "../../types"; +import { Button } from "@/components/ui/button" +import { IGweet } from "../../types" -import { Icons } from "@/components/icons"; +import { Icons } from "@/components/icons" export const CommentButton = ({ gweet }: { gweet: IGweet }) => { - return ( - <div className="relative inline-flex items-center"> - <Button variant="ghost" size="icon" className="hover:bg-sky-800 z-40"> - <Icons.messagecircle className="w-6 h-6" /> - </Button> + return ( + <div className="relative inline-flex items-center"> + <Button variant="ghost" size="icon" className="hover:bg-sky-800 z-40"> + <Icons.messagecircle className="w-6 h-6" /> + </Button> - {gweet.allComments.length > 0 && ( - <span className="absolute pl-12">{gweet.allComments.length}</span> - )} - </div> - ); -}; \ No newline at end of file + {gweet.allComments.length > 0 && ( + <span className="absolute pl-12">{gweet.allComments.length}</span> + )} + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/actions/like-button.tsx b/components/gweets/components/actions/like-button.tsx index 46c1779..925583a 100644 --- a/components/gweets/components/actions/like-button.tsx +++ b/components/gweets/components/actions/like-button.tsx @@ -1,41 +1,41 @@ -import { useSession } from "next-auth/react"; +import { useSession } from "next-auth/react" -import { Icons } from "@/components/icons"; -import { Button } from "@/components/ui/button"; -import { useLike } from "../../hooks/use-like"; -import { IGweet } from "../../types"; +import { Icons } from "@/components/icons" +import { Button } from "@/components/ui/button" +import { useLike } from "../../hooks/use-like" +import { IGweet } from "../../types" export const LikeButton = ({ gweet }: { gweet: IGweet }) => { - const { data: session } = useSession(); - const hasLiked = gweet.likes.some( - (like) => like.userId === session?.user.id, - ); + const { data: session } = useSession() + const hasLiked = gweet.likes.some( + (like) => like.userId === session?.user.id, + ) - const mutation = useLike({ - gweetAuthorId: gweet.author.id, - sessionOwnerId: session?.user.id, - }); + const mutation = useLike({ + gweetAuthorId: gweet.author.id, + sessionOwnerId: session?.user.id, + }) - return ( - <div className="inline-flex items-center"> - <Button - onClick={(e) => { - e.stopPropagation(); - if (session) { - mutation.mutate({ gweetId: gweet.id, userId: session.user.id }); - } - }} - variant="ghost" size="icon" className="hover:bg-red-800" - > - {hasLiked ? - <Icons.heart className="w-6 h-6 fill-red-600 text-red-600" /> - : <Icons.heart className="w-6 h-6" /> - } - </Button> + return ( + <div className="inline-flex items-center"> + <Button + onClick={(e) => { + e.stopPropagation() + if (session) { + mutation.mutate({ gweetId: gweet.id, userId: session.user.id }) + } + }} + variant="ghost" size="icon" className="hover:bg-red-800" + > + {hasLiked ? + <Icons.heart className="w-6 h-6 fill-red-600 text-red-600" /> + : <Icons.heart className="w-6 h-6" /> + } + </Button> - {gweet.likes.length > 0 && ( - <span className="px-2">{gweet?.likes?.length}</span> - )} - </div> - ); -}; \ No newline at end of file + {gweet.likes.length > 0 && ( + <span className="px-2">{gweet?.likes?.length}</span> + )} + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/actions/regweet-button.tsx b/components/gweets/components/actions/regweet-button.tsx index f02a894..f327bb3 100644 --- a/components/gweets/components/actions/regweet-button.tsx +++ b/components/gweets/components/actions/regweet-button.tsx @@ -1,40 +1,40 @@ -import { Icons } from "@/components/icons"; -import { Button } from "@/components/ui/button"; -import { useSession } from "next-auth/react"; -import { useRegweet } from "../../hooks/use-regweet"; -import { IGweet } from "../../types"; +import { Icons } from "@/components/icons" +import { Button } from "@/components/ui/button" +import { useSession } from "next-auth/react" +import { useRegweet } from "../../hooks/use-regweet" +import { IGweet } from "../../types" export const RegweetButton = ({ gweet }: { gweet: IGweet }) => { - const { data: session } = useSession(); - const hasRegweeted = gweet.regweets.some( - (regweet) => regweet.userId === session?.user?.id, - ); + const { data: session } = useSession() + const hasRegweeted = gweet.regweets.some( + (regweet) => regweet.userId === session?.user?.id, + ) - const mutation = useRegweet(); + const mutation = useRegweet() - return ( - <div className="relative inline-flex items-center"> - <Button - onClick={(e) => { - e.stopPropagation(); - if (session) { - mutation.mutate({ - gweetId: gweet.id, - userId: session.user.id, - }); - } - }} - variant="ghost" size="icon" className="hover:bg-green-800 z-40" - > - {hasRegweeted ? - <Icons.regweet className="w-6 h-6 text-green-600" /> - : <Icons.regweet className="w-6 h-6" /> - } - </Button> + return ( + <div className="relative inline-flex items-center"> + <Button + onClick={(e) => { + e.stopPropagation() + if (session) { + mutation.mutate({ + gweetId: gweet.id, + userId: session.user.id, + }) + } + }} + variant="ghost" size="icon" className="hover:bg-green-800 z-40" + > + {hasRegweeted ? + <Icons.regweet className="w-6 h-6 text-green-600" /> + : <Icons.regweet className="w-6 h-6" /> + } + </Button> - {gweet.regweets.length > 0 && ( - <span className="absolute pl-12">{gweet.regweets.length}</span> - )} - </div> - ); -}; \ No newline at end of file + {gweet.regweets.length > 0 && ( + <span className="absolute pl-12">{gweet.regweets.length}</span> + )} + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/comments.tsx b/components/gweets/components/comments.tsx index f1f5742..d7d3f28 100644 --- a/components/gweets/components/comments.tsx +++ b/components/gweets/components/comments.tsx @@ -1,39 +1,39 @@ -import { TryAgain } from "@/components/try-again"; +import { TryAgain } from "@/components/try-again" -import LoadingItem from "@/components/loading-item"; -import { useGweets } from "../hooks/use-gweets"; -import { InfiniteGweets } from "./infinite-gweets"; +import LoadingItem from "@/components/loading-item" +import { useGweets } from "../hooks/use-gweets" +import { InfiniteGweets } from "./infinite-gweets" export const Comments = ({ gweetId }: { gweetId: string }) => { - const { - data: comments, - isLoading, - isError, - fetchNextPage, - hasNextPage, - isFetchingNextPage, - isSuccess, - } = useGweets({ - queryKey: ["gweets", gweetId, "comments"], - type: "comments", - id: gweetId, - }); + const { + data: comments, + isLoading, + isError, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isSuccess, + } = useGweets({ + queryKey: ["gweets", gweetId, "comments"], + type: "comments", + id: gweetId, + }) - if (isLoading) { - return <LoadingItem />; - } + if (isLoading) { + return <LoadingItem /> + } - if (isError) { - return <TryAgain />; - } + if (isError) { + return <TryAgain /> + } - return ( - <InfiniteGweets - gweets={comments} - fetchNextPage={fetchNextPage} - hasNextPage={hasNextPage} - isFetchingNextPage={isFetchingNextPage} - isSuccess={isSuccess} - /> - ); -}; \ No newline at end of file + return ( + <InfiniteGweets + gweets={comments} + fetchNextPage={fetchNextPage} + hasNextPage={hasNextPage} + isFetchingNextPage={isFetchingNextPage} + isSuccess={isSuccess} + /> + ) +} \ No newline at end of file diff --git a/components/gweets/components/delete-gweet-modal.tsx b/components/gweets/components/delete-gweet-modal.tsx index 83ae165..6799634 100644 --- a/components/gweets/components/delete-gweet-modal.tsx +++ b/components/gweets/components/delete-gweet-modal.tsx @@ -1,67 +1,67 @@ -"use client"; +"use client" -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { useDeleteGweet } from "../hooks/use-delete-gweet"; -import { IGweet } from "../types"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu" +import { useDeleteGweet } from "../hooks/use-delete-gweet" +import { IGweet } from "../types" -import { Icons } from "@/components/icons"; -import { Button } from "@/components/ui/button"; +import { Icons } from "@/components/icons" +import { Button } from "@/components/ui/button" import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { useRouter } from "next/navigation"; + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { useRouter } from "next/navigation" export const DeleteGweetModal = ({ gweet, props, forwardedRef }: { gweet: IGweet, props: any, forwardedRef: any }) => { - const { triggerChildren, onSelect, onOpenChange, ...itemProps } = props; - const { isLoading, mutate, isSuccess } = useDeleteGweet(); + const { triggerChildren, onSelect, onOpenChange, ...itemProps } = props + const { isLoading, mutate, isSuccess } = useDeleteGweet() - const router = useRouter(); - if (isSuccess) router.push("/home"); + const router = useRouter() + if (isSuccess) router.push("/home") - return ( - <Dialog onOpenChange={onOpenChange}> - <DialogTrigger asChild> - <DropdownMenuItem - {...itemProps} - ref={forwardedRef} - onSelect={(event) => { - event.preventDefault(); - onSelect && onSelect(); - }}> - {triggerChildren} - </DropdownMenuItem> - </DialogTrigger> - <DialogContent className="sm:max-w-[425px]"> - <DialogHeader> - <DialogTitle>Delete gweet?</DialogTitle> - <DialogDescription> - You are about to delete this gweet. This can't be undone and will be removed from your profile, - the timeline of any accounts that follow you, and from the search results. - </DialogDescription> - </DialogHeader> - <DialogFooter> - <Button - variant="destructive" - type="submit" - disabled={isLoading} - onClick={() => { - mutate({ - gweetId: gweet?.id, - }); - }}> - {isLoading && ( - <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> - )} - Delete - </Button> - </DialogFooter> - </DialogContent> - </Dialog> - ) + return ( + <Dialog onOpenChange={onOpenChange}> + <DialogTrigger asChild> + <DropdownMenuItem + {...itemProps} + ref={forwardedRef} + onSelect={(event) => { + event.preventDefault() + onSelect && onSelect() + }}> + {triggerChildren} + </DropdownMenuItem> + </DialogTrigger> + <DialogContent className="sm:max-w-[425px]"> + <DialogHeader> + <DialogTitle>Delete gweet?</DialogTitle> + <DialogDescription> + You are about to delete this gweet. This can't be undone and will be removed from your profile, + the timeline of any accounts that follow you, and from the search results. + </DialogDescription> + </DialogHeader> + <DialogFooter> + <Button + variant="destructive" + type="submit" + disabled={isLoading} + onClick={() => { + mutate({ + gweetId: gweet?.id, + }) + }}> + {isLoading && ( + <Icons.spinner className="mr-2 h-4 w-4 animate-spin" /> + )} + Delete + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) } \ No newline at end of file diff --git a/components/gweets/components/gweet-actions.tsx b/components/gweets/components/gweet-actions.tsx index 2fe4fd6..3debbfe 100644 --- a/components/gweets/components/gweet-actions.tsx +++ b/components/gweets/components/gweet-actions.tsx @@ -1,19 +1,19 @@ -import { IGweet } from "../types"; +import { IGweet } from "../types" -import { CommentButton } from "./actions/comment-button"; -import { LikeButton } from "./actions/like-button"; -import { RegweetButton } from "./actions/regweet-button"; +import { CommentButton } from "./actions/comment-button" +import { LikeButton } from "./actions/like-button" +import { RegweetButton } from "./actions/regweet-button" export const GweetActions = ({ - gweet, + gweet, }: { - gweet: IGweet; + gweet: IGweet }) => { - return ( - <div className="space-x-12 w-64"> - <CommentButton gweet={gweet} /> - <RegweetButton gweet={gweet} /> - <LikeButton gweet={gweet} /> - </div> - ); -}; \ No newline at end of file + return ( + <div className="space-x-12 w-64"> + <CommentButton gweet={gweet} /> + <RegweetButton gweet={gweet} /> + <LikeButton gweet={gweet} /> + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweet-author.tsx b/components/gweets/components/gweet-author.tsx index 524f4a2..1b1c66a 100644 --- a/components/gweets/components/gweet-author.tsx +++ b/components/gweets/components/gweet-author.tsx @@ -1,7 +1,7 @@ -import { UserAvatar } from "@/components/user-avatar"; -import Link from "next/link"; -import { IGweet } from "../types"; -import { GweetOptions } from "./gweet-options"; +import { UserAvatar } from "@/components/user-avatar" +import Link from "next/link" +import { IGweet } from "../types" +import { GweetOptions } from "./gweet-options" export const GweetAuthor = ({ gweet }: { gweet: IGweet }) => { return ( @@ -21,5 +21,5 @@ export const GweetAuthor = ({ gweet }: { gweet: IGweet }) => { <GweetOptions gweet={gweet} /> </div> </div> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweet-creation-date.tsx b/components/gweets/components/gweet-creation-date.tsx index e787feb..38f354c 100644 --- a/components/gweets/components/gweet-creation-date.tsx +++ b/components/gweets/components/gweet-creation-date.tsx @@ -1,11 +1,11 @@ -import dayjs from "dayjs"; +import dayjs from "dayjs" export const GweetCreationDate = ({ date }: { date: Date }) => { - return ( - <div className="space-x-2"> - <span>{dayjs(date).format(`h:mm A`)}</span> - <span>·</span> - <span>{dayjs(date).format(`MMM D, YYYY`)}</span> - </div> - ); -}; \ No newline at end of file + return ( + <div className="space-x-2"> + <span>{dayjs(date).format(`h:mm A`)}</span> + <span>·</span> + <span>{dayjs(date).format(`MMM D, YYYY`)}</span> + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweet-details.tsx b/components/gweets/components/gweet-details.tsx index 20fd88c..87a3c8b 100644 --- a/components/gweets/components/gweet-details.tsx +++ b/components/gweets/components/gweet-details.tsx @@ -1,31 +1,31 @@ "use client" -import { CreateGweetWrapper } from "@/components/create-gweet/components/create-gweet-wrapper"; -import LoadingItem from "@/components/loading-item"; -import { TryAgain } from "@/components/try-again"; -import { Card } from "@/components/ui/card"; -import { Separator } from "@/components/ui/separator"; -import Image from "next/image"; -import { usePathname } from "next/navigation"; -import { useGweet } from "../hooks/use-gweet"; -import { Comments } from "./comments"; -import { GweetActions } from "./gweet-actions"; -import { GweetAuthor } from "./gweet-author"; -import { GweetCreationDate } from "./gweet-creation-date"; +import { CreateGweetWrapper } from "@/components/create-gweet/components/create-gweet-wrapper" +import LoadingItem from "@/components/loading-item" +import { TryAgain } from "@/components/try-again" +import { Card } from "@/components/ui/card" +import { Separator } from "@/components/ui/separator" +import Image from "next/image" +import { usePathname } from "next/navigation" +import { useGweet } from "../hooks/use-gweet" +import { Comments } from "./comments" +import { GweetActions } from "./gweet-actions" +import { GweetAuthor } from "./gweet-author" +import { GweetCreationDate } from "./gweet-creation-date" export const GweetDetails = () => { // TODO use params - const pathname = usePathname(); - const id = pathname?.split(`/`)[3] || ``; + const pathname = usePathname() + const id = pathname?.split(`/`)[3] || `` - const { data: gweet, isLoading, isError } = useGweet(id); + const { data: gweet, isLoading, isError } = useGweet(id) if (isLoading) { - return <LoadingItem />; + return <LoadingItem /> } if (isError) { - return <TryAgain />; + return <TryAgain /> } if (!isLoading && !isError && !gweet) { @@ -51,7 +51,7 @@ export const GweetDetails = () => { }`} > {gweet.media.map((image, i) => { - const isFirstImage = gweet.media.length === 3 && i === 0; + const isFirstImage = gweet.media.length === 3 && i === 0 return ( <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> <Image @@ -61,7 +61,7 @@ export const GweetDetails = () => { className="object-cover rounded-lg" /> </Card> - ); + ) })} </div> )} @@ -84,5 +84,5 @@ export const GweetDetails = () => { <Separator className="h-1 mb-3" /> <Comments gweetId={gweet?.id} /> </> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweet-options.tsx b/components/gweets/components/gweet-options.tsx index 2876d53..da121bd 100644 --- a/components/gweets/components/gweet-options.tsx +++ b/components/gweets/components/gweet-options.tsx @@ -1,42 +1,42 @@ -"use client"; +"use client" -import { Icons } from "@/components/icons"; -import { Button } from "@/components/ui/button"; +import { Icons } from "@/components/icons" +import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { toast } from "@/components/ui/use-toast"; -import { env } from "@/env.mjs"; -import getURL from "@/lib/utils"; -import { useSession } from "next-auth/react"; -import { useRef, useState } from "react"; -import { IGweet } from "../types"; -import { DeleteGweetModal } from "./delete-gweet-modal"; +} from "@/components/ui/dropdown-menu" +import { toast } from "@/components/ui/use-toast" +import { env } from "@/env.mjs" +import getURL from "@/lib/utils" +import { useSession } from "next-auth/react" +import { useRef, useState } from "react" +import { IGweet } from "../types" +import { DeleteGweetModal } from "./delete-gweet-modal" export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { - const { data: session } = useSession(); + const { data: session } = useSession() - const [dropdownOpen, setDropdownOpen] = useState<boolean>(false); - const [hasOpenDialog, setHasOpenDialog] = useState<boolean>(false); - const dropdownTriggerRef = useRef<HTMLButtonElement | null>(null); - const focusRef = useRef<HTMLButtonElement | null>(null); + const [dropdownOpen, setDropdownOpen] = useState<boolean>(false) + const [hasOpenDialog, setHasOpenDialog] = useState<boolean>(false) + const dropdownTriggerRef = useRef<HTMLButtonElement | null>(null) + const focusRef = useRef<HTMLButtonElement | null>(null) function handleDialogItemSelect() { - focusRef.current = dropdownTriggerRef.current; + focusRef.current = dropdownTriggerRef.current } function handleDialogItemOpenChange(open: boolean) { - setHasOpenDialog(open); + setHasOpenDialog(open) if (open === false) { - setDropdownOpen(false); + setDropdownOpen(false) } } - const url = getURL(`/${gweet?.author.username}/status/${gweet?.id}`); + const url = getURL(`/${gweet?.author.username}/status/${gweet?.id}`) return ( <DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}> @@ -52,13 +52,13 @@ export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { className="font-bold cursor-pointer" hidden={hasOpenDialog} onClick={(event) => { - event.stopPropagation(); + event.stopPropagation() }} onCloseAutoFocus={(event) => { if (focusRef.current) { - focusRef.current.focus(); - focusRef.current = null; - event.preventDefault(); + focusRef.current.focus() + focusRef.current = null + event.preventDefault() } }}> <DropdownMenuGroup> @@ -80,7 +80,7 @@ export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { )} <DropdownMenuItem onClick={() => { - navigator.clipboard.writeText(url); + navigator.clipboard.writeText(url) return toast({ description: "Copied link to gweet.", }) @@ -91,5 +91,5 @@ export const GweetOptions = ({ gweet }: { gweet: IGweet }) => { </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweet.tsx b/components/gweets/components/gweet.tsx index ff0b42f..63e119d 100644 --- a/components/gweets/components/gweet.tsx +++ b/components/gweets/components/gweet.tsx @@ -1,24 +1,24 @@ -import { buttonVariants } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; -import { UserAvatar } from "@/components/user-avatar"; -import { cn, formatTimeElapsed } from "@/lib/utils"; -import Image from "next/image"; -import { useRouter } from "next/navigation"; -import { IGweet } from "../types"; -import { GweetActions } from "./gweet-actions"; -import { GweetOptions } from "./gweet-options"; +import { buttonVariants } from "@/components/ui/button" +import { Card } from "@/components/ui/card" +import { UserAvatar } from "@/components/user-avatar" +import { cn, formatTimeElapsed } from "@/lib/utils" +import Image from "next/image" +import { useRouter } from "next/navigation" +import { IGweet } from "../types" +import { GweetActions } from "./gweet-actions" +import { GweetOptions } from "./gweet-options" export const Gweet = ({ gweet }: { gweet: IGweet }) => { - const router = useRouter(); + const router = useRouter() - return ( - <div - tabIndex={0} - onClick={() => router.push(`/${gweet.author.username}/status/${gweet.id}`)} - className={cn(buttonVariants({ variant: "ghost" }), "flex flex-col flex-grow h-auto w-full text-left cursor-pointer items-start p-3 space-y-3")} - > - {/* TODO replyto */} - {/* <div className=""> + return ( + <div + tabIndex={0} + onClick={() => router.push(`/${gweet.author.username}/status/${gweet.id}`)} + className={cn(buttonVariants({ variant: "ghost" }), "flex flex-col flex-grow h-auto w-full text-left cursor-pointer items-start p-3 space-y-3")} + > + {/* TODO replyto */} + {/* <div className=""> {gweet?.replyToGweetId && ( <div className=""> <span className="">Replying to</span> @@ -34,66 +34,66 @@ export const Gweet = ({ gweet }: { gweet: IGweet }) => { </div> )} </div> */} - <div className="flex flex-row h-auto w-full"> - <UserAvatar - user={{ username: gweet.author.username, image: gweet.author.image }} - className="h-10 w-10" - /> + <div className="flex flex-row h-auto w-full"> + <UserAvatar + user={{ username: gweet.author.username, image: gweet.author.image }} + className="h-10 w-10" + /> - <div className="flex flex-col flex-grow space-y-3 ml-3 w-1"> - <div className="flex items-start"> + <div className="flex flex-col flex-grow space-y-3 ml-3 w-1"> + <div className="flex items-start"> - <div className="flex space-x-2 flex-grow"> - <h1 className="font-bold">{gweet.author.name}</h1> - <h1 className="text-sky-500 text-sm"> - @{gweet.author.username} - </h1> - <span>·</span> - <h1 className="text-gray-500 text-sm"> - {formatTimeElapsed(gweet.createdAt)} - </h1> - </div> + <div className="flex space-x-2 flex-grow"> + <h1 className="font-bold">{gweet.author.name}</h1> + <h1 className="text-sky-500 text-sm"> + @{gweet.author.username} + </h1> + <span>·</span> + <h1 className="text-gray-500 text-sm"> + {formatTimeElapsed(gweet.createdAt)} + </h1> + </div> - <div className="ml-auto"> - <GweetOptions gweet={gweet} /> - </div> - </div> + <div className="ml-auto"> + <GweetOptions gweet={gweet} /> + </div> + </div> - <div className="flex flex-col flex-grow space-y-3 w-full"> - {gweet.content && <h1 className="break-words">{gweet.content}</h1>} + <div className="flex flex-col flex-grow space-y-3 w-full"> + {gweet.content && <h1 className="break-words">{gweet.content}</h1>} - {gweet.media.length > 0 && ( - <div className={`grid object-cover h-[600px] ${gweet.media.length === 1 ? "grid-cols-1" - : gweet.media.length === 2 ? "grid-cols-2 gap-3" - : gweet.media.length === 3 || 4 ? "grid-cols-2 grid-rows-2 gap-3" - : "" - }`} - > - {gweet.media.map((image, i) => { - const isFirstImage = gweet.media.length === 3 && i === 0; - return ( - <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> - <Image - src={image.url as string} - alt="gweet image" - fill - className="object-cover rounded-lg" - /> - </Card> - ); - })} - </div> - )} + {gweet.media.length > 0 && ( + <div className={`grid object-cover h-[600px] ${gweet.media.length === 1 ? "grid-cols-1" + : gweet.media.length === 2 ? "grid-cols-2 gap-3" + : gweet.media.length === 3 || 4 ? "grid-cols-2 grid-rows-2 gap-3" + : "" + }`} + > + {gweet.media.map((image, i) => { + const isFirstImage = gweet.media.length === 3 && i === 0 + return ( + <Card key={i} className={`relative max-h-[600px] overflow-hidden ${isFirstImage ? "row-span-2" : ""}`}> + <Image + src={image.url as string} + alt="gweet image" + fill + className="object-cover rounded-lg" + /> + </Card> + ) + })} + </div> + )} - {/* TODO */} - {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} - </div> - </div> - </div> + {/* TODO */} + {/* {quoted_gweet && <QuotedGweet gweet={quoted_gweet} />} */} + </div> + </div> + </div> - <div className="flex justify-end flex-grow w-full" > - <GweetActions gweet={gweet} /> - </div> - </div> - ); -}; \ No newline at end of file + <div className="flex justify-end flex-grow w-full" > + <GweetActions gweet={gweet} /> + </div> + </div> + ) +} \ No newline at end of file diff --git a/components/gweets/components/gweets.tsx b/components/gweets/components/gweets.tsx index 81fa2ee..893919b 100644 --- a/components/gweets/components/gweets.tsx +++ b/components/gweets/components/gweets.tsx @@ -1,39 +1,39 @@ -"use client"; +"use client" -import LoadingItem from "@/components/loading-item"; -import { TryAgain } from "@/components/try-again"; -import { Card } from "@/components/ui/card"; -import { useGweets } from "../hooks/use-gweets"; -import { InfiniteGweets } from "./infinite-gweets"; +import LoadingItem from "@/components/loading-item" +import { TryAgain } from "@/components/try-again" +import { Card } from "@/components/ui/card" +import { useGweets } from "../hooks/use-gweets" +import { InfiniteGweets } from "./infinite-gweets" export const Gweets = () => { - const { - data: gweets, - isLoading, - isError, - isSuccess, - isFetchingNextPage, - fetchNextPage, - hasNextPage, - } = useGweets({}); + const { + data: gweets, + isLoading, + isError, + isSuccess, + isFetchingNextPage, + fetchNextPage, + hasNextPage, + } = useGweets({}) - if (isLoading) { - return <LoadingItem />; - } + if (isLoading) { + return <LoadingItem /> + } - if (isError) { - return <TryAgain />; - } + if (isError) { + return <TryAgain /> + } - return ( - <Card className="w-full h-full mt-6 p-2 xl:p-4 "> - <InfiniteGweets - gweets={gweets} - isSuccess={isSuccess} - isFetchingNextPage={isFetchingNextPage} - fetchNextPage={fetchNextPage} - hasNextPage={hasNextPage} - /> - </Card> - ); -}; + return ( + <Card className="w-full h-full p-2 xl:p-4"> + <InfiniteGweets + gweets={gweets} + isSuccess={isSuccess} + isFetchingNextPage={isFetchingNextPage} + fetchNextPage={fetchNextPage} + hasNextPage={hasNextPage} + /> + </Card> + ) +} diff --git a/components/gweets/components/infinite-gweets.tsx b/components/gweets/components/infinite-gweets.tsx index c86d6ae..7631f82 100644 --- a/components/gweets/components/infinite-gweets.tsx +++ b/components/gweets/components/infinite-gweets.tsx @@ -1,51 +1,51 @@ "use client" -import LoadingItem from "@/components/loading-item"; -import { Separator } from "@/components/ui/separator"; -import { useEffect } from "react"; -import { useInView } from "react-intersection-observer"; -import { IInfiniteGweets } from "../types"; -import { Gweet } from "./gweet"; +import LoadingItem from "@/components/loading-item" +import { Separator } from "@/components/ui/separator" +import { useEffect } from "react" +import { useInView } from "react-intersection-observer" +import { IInfiniteGweets } from "../types" +import { Gweet } from "./gweet" export const InfiniteGweets = ({ - gweets, - isSuccess, - isFetchingNextPage, - fetchNextPage, - hasNextPage, + gweets, + isSuccess, + isFetchingNextPage, + fetchNextPage, + hasNextPage, }: { - gweets: IInfiniteGweets; - isSuccess: boolean | undefined; - isFetchingNextPage: boolean | undefined; - fetchNextPage: () => Promise<any> | void; - hasNextPage: boolean | undefined; + gweets: IInfiniteGweets + isSuccess: boolean | undefined + isFetchingNextPage: boolean | undefined + fetchNextPage: () => Promise<any> | void + hasNextPage: boolean | undefined }) => { - const { ref, inView } = useInView(); + const { ref, inView } = useInView() - useEffect(() => { - if (inView && hasNextPage) { - fetchNextPage(); - } - }, [inView, hasNextPage, fetchNextPage]); + useEffect(() => { + if (inView && hasNextPage) { + fetchNextPage() + } + }, [inView, hasNextPage, fetchNextPage]) - return ( - <div> - {isSuccess && - gweets?.pages?.map((page) => { - return page?.gweets?.map((gweet, index) => ( - <div - ref={index === page.gweets.length - 4 ? ref : undefined} - key={gweet.id} - > - <Gweet gweet={gweet} /> - <div className="px-6"> - <Separator className="my-3" /> - </div> - </div> - )); - })} + return ( + <> + {isSuccess && + gweets?.pages?.map((page) => { + return page?.gweets?.map((gweet, index) => ( + <div + ref={index === page.gweets.length - 4 ? ref : undefined} + key={gweet.id} + > + <Gweet gweet={gweet} /> + <div className="px-6"> + <Separator className="my-3" /> + </div> + </div> + )) + })} - {isFetchingNextPage && <LoadingItem />} - </div> - ); -}; \ No newline at end of file + {isFetchingNextPage && <LoadingItem />} + </> + ) +} \ No newline at end of file diff --git a/components/gweets/hooks/use-delete-gweet.ts b/components/gweets/hooks/use-delete-gweet.ts index 9fbfabc..198d992 100644 --- a/components/gweets/hooks/use-delete-gweet.ts +++ b/components/gweets/hooks/use-delete-gweet.ts @@ -1,18 +1,18 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query" -import { deleteGweet } from "../api/delete-gweet"; +import { deleteGweet } from "../api/delete-gweet" export const useDeleteGweet = () => { - const queryClient = useQueryClient(); + const queryClient = useQueryClient() - return useMutation(({ gweetId }: { gweetId: string }) => { - return deleteGweet(gweetId); - }, { - onSuccess: () => { - queryClient.invalidateQueries(["gweets"]); - }, - onError: (error) => { - console.log(error); - }, - }); -}; \ No newline at end of file + return useMutation(({ gweetId }: { gweetId: string }) => { + return deleteGweet(gweetId) + }, { + onSuccess: () => { + queryClient.invalidateQueries(["gweets"]) + }, + onError: (error) => { + console.log(error) + }, + }) +} \ No newline at end of file diff --git a/components/gweets/hooks/use-gweet.ts b/components/gweets/hooks/use-gweet.ts index 6ff302e..d19d087 100644 --- a/components/gweets/hooks/use-gweet.ts +++ b/components/gweets/hooks/use-gweet.ts @@ -1,20 +1,20 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query" -import getGweet from "../api/get-gweet"; -import { IGweet } from "../types"; +import getGweet from "../api/get-gweet" +import { IGweet } from "../types" export const useGweet = (id: string | undefined) => { - const queryClient = useQueryClient(); - return useQuery<IGweet>( - ["gweets", id], - async () => { - return getGweet(id); - }, - { - refetchOnWindowFocus: false, - onSuccess: (data) => { - queryClient.setQueryData(["gweets", id], data); - }, - }, - ); -}; + const queryClient = useQueryClient() + return useQuery<IGweet>( + ["gweets", id], + async () => { + return getGweet(id) + }, + { + refetchOnWindowFocus: false, + onSuccess: (data) => { + queryClient.setQueryData(["gweets", id], data) + }, + }, + ) +} diff --git a/components/gweets/hooks/use-gweets.ts b/components/gweets/hooks/use-gweets.ts index ea23e8e..56ec73f 100644 --- a/components/gweets/hooks/use-gweets.ts +++ b/components/gweets/hooks/use-gweets.ts @@ -1,33 +1,33 @@ -import { useInfiniteQuery } from "@tanstack/react-query"; +import { useInfiniteQuery } from "@tanstack/react-query" -import { getGweets } from "../api/get-gweets"; +import { getGweets } from "../api/get-gweets" export const useGweets = ({ - queryKey, - type, - id, + queryKey, + type, + id, }: { - queryKey?: string[]; - type?: string; - id?: string; + queryKey?: string[] + type?: string + id?: string }) => { - const data = useInfiniteQuery( - queryKey ?? ["gweets"], - ({ pageParam = "" }) => - getGweets({ - pageParam, - limit: 20, - type, - id, - }), + const data = useInfiniteQuery( + queryKey ?? ["gweets"], + ({ pageParam = "" }) => + getGweets({ + pageParam, + limit: 20, + type, + id, + }), - { - getNextPageParam: (lastPage) => { - return lastPage?.nextId ?? false; - }, - refetchOnWindowFocus: false, - }, - ); + { + getNextPageParam: (lastPage) => { + return lastPage?.nextId ?? false + }, + refetchOnWindowFocus: false, + }, + ) - return data; -}; + return data +} diff --git a/components/gweets/hooks/use-like.ts b/components/gweets/hooks/use-like.ts index 7f6366c..f0ab559 100644 --- a/components/gweets/hooks/use-like.ts +++ b/components/gweets/hooks/use-like.ts @@ -1,30 +1,30 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query" -import { toggleLike } from "../api/toggle-like"; +import { toggleLike } from "../api/toggle-like" export const useLike = ({ - gweetAuthorId, - sessionOwnerId, + gweetAuthorId, + sessionOwnerId, }: { - gweetAuthorId: string | undefined; - sessionOwnerId: string | undefined; + gweetAuthorId: string | undefined + sessionOwnerId: string | undefined }) => { - const queryClient = useQueryClient(); + const queryClient = useQueryClient() - return useMutation( - ({ gweetId, userId }: { gweetId: string | undefined; userId: string }) => { - return toggleLike({ gweetId, userId }); - }, - { - onSuccess: () => { - queryClient.invalidateQueries(["gweets"]); - queryClient.invalidateQueries(["users", sessionOwnerId]); - if (sessionOwnerId !== gweetAuthorId) - queryClient.invalidateQueries(["users", gweetAuthorId]); - }, - onError: () => { - console.log("error"); - }, - }, - ); -}; + return useMutation( + ({ gweetId, userId }: { gweetId: string | undefined; userId: string }) => { + return toggleLike({ gweetId, userId }) + }, + { + onSuccess: () => { + queryClient.invalidateQueries(["gweets"]) + queryClient.invalidateQueries(["users", sessionOwnerId]) + if (sessionOwnerId !== gweetAuthorId) + queryClient.invalidateQueries(["users", gweetAuthorId]) + }, + onError: () => { + console.log("error") + }, + }, + ) +} diff --git a/components/gweets/hooks/use-regweet.ts b/components/gweets/hooks/use-regweet.ts index f46cb4d..82b5ef8 100644 --- a/components/gweets/hooks/use-regweet.ts +++ b/components/gweets/hooks/use-regweet.ts @@ -1,26 +1,26 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query" -import { handleRegweet } from "../api/handle-regweet"; +import { handleRegweet } from "../api/handle-regweet" export const useRegweet = () => { - const QueryClient = useQueryClient(); + const QueryClient = useQueryClient() - return useMutation( - ({ gweetId, userId }: { gweetId: string; userId: string }) => { - return handleRegweet(gweetId, userId); - }, + return useMutation( + ({ gweetId, userId }: { gweetId: string; userId: string }) => { + return handleRegweet(gweetId, userId) + }, - { - onSuccess: () => { - QueryClient.invalidateQueries(["gweets"]); - QueryClient.invalidateQueries(["users"]); - }, + { + onSuccess: () => { + QueryClient.invalidateQueries(["gweets"]) + QueryClient.invalidateQueries(["users"]) + }, - onError: (error: any) => { - console.log(error); - }, + onError: (error: any) => { + console.log(error) + }, - onSettled: () => { }, - }, - ); -}; \ No newline at end of file + onSettled: () => { }, + }, + ) +} \ No newline at end of file diff --git a/components/gweets/types/index.ts b/components/gweets/types/index.ts index 29e8cd6..21ae5a0 100644 --- a/components/gweets/types/index.ts +++ b/components/gweets/types/index.ts @@ -1,35 +1,35 @@ -import { Gweet, Like, Media, Regweet } from "@prisma/client"; +import { Gweet, Like, Media, Regweet } from "@prisma/client" -import { IUser } from "@/components/profile/types"; +import { IUser } from "@/components/profile/types" export interface IFeed { - id: number; + id: number } export interface IGweet extends Gweet { - author: IUser; - quotedGweet: IGweet; - likes: ILike[]; - media: IMedia[]; - regweets: IRegweet[]; - allQuotes: IGweet[]; - allComments: IGweet[]; + author: IUser + quotedGweet: IGweet + likes: ILike[] + media: IMedia[] + regweets: IRegweet[] + allQuotes: IGweet[] + allComments: IGweet[] } export interface ILike extends Like { - user: IUser; - gweet: IGweet; + user: IUser + gweet: IGweet } export interface IMedia extends Media { - gweet: IGweet; + gweet: IGweet } export interface IRegweet extends Regweet { - user: IUser; + user: IUser } export interface IInfiniteGweets { - pages: { gweets: IGweet[]; nextId?: string | undefined }[]; - pageParams: any; + pages: { gweets: IGweet[]; nextId?: string | undefined }[] + pageParams: any } diff --git a/components/icons.tsx b/components/icons.tsx index 35f194b..52f4a7e 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -34,11 +34,11 @@ import { Users, X, type Icon as LucideIcon, -} from "lucide-react"; +} from "lucide-react" export type IconsType = { - [key: string]: LucideIcon; -}; + [key: string]: LucideIcon +} export type Icon = LucideIcon diff --git a/components/infinity-scroll.tsx b/components/infinity-scroll.tsx index c8623c4..3a55dda 100644 --- a/components/infinity-scroll.tsx +++ b/components/infinity-scroll.tsx @@ -1,33 +1,33 @@ "use client" -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"; +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() - const searchQuery = searchParams.get('search'); - const categoryQuery = searchParams.get('category'); - const genreQuery = searchParams.get('genre'); - const platformQuery = searchParams.get('platform'); - const sortbyQuery = searchParams.get('sortby'); - const orderQuery = searchParams.get('order'); + const searchQuery = searchParams.get('search') + const categoryQuery = searchParams.get('category') + const genreQuery = searchParams.get('genre') + const platformQuery = searchParams.get('platform') + const sortbyQuery = searchParams.get('sortby') + const orderQuery = searchParams.get('order') - const searchURL = searchQuery ? `&search=${searchQuery}` : ""; - const categoryURL = categoryQuery ? `&category=${categoryQuery}` : ""; - const genreURL = genreQuery ? `&genre=${genreQuery}` : ""; - const platformURL = platformQuery ? `&platform=${platformQuery}` : ""; - const sortbyURL = sortbyQuery ? `&sortby=${sortbyQuery}` : ""; - const orderURL = orderQuery ? `&order=${orderQuery}` : ""; + const searchURL = searchQuery ? `&search=${searchQuery}` : "" + const categoryURL = categoryQuery ? `&category=${categoryQuery}` : "" + const genreURL = genreQuery ? `&genre=${genreQuery}` : "" + const platformURL = platformQuery ? `&platform=${platformQuery}` : "" + const sortbyURL = sortbyQuery ? `&sortby=${sortbyQuery}` : "" + const orderURL = orderQuery ? `&order=${orderQuery}` : "" - const params = searchURL + categoryURL + genreURL + platformURL + sortbyURL + orderURL; + const params = searchURL + categoryURL + genreURL + platformURL + sortbyURL + orderURL const { status, @@ -42,13 +42,13 @@ export function InfiniteScrollGames() { ).then((result) => result.json() as Promise<IGame[]>), { getNextPageParam: (lastPage, pages) => { - return lastPage.length > 0 ? pages.length + 1 : undefined; + return lastPage.length > 0 ? pages.length + 1 : undefined }, }, ) return ( - <Card className="p-6 w-full"> + <Card className="p-6 w-full h-full"> {status === 'error' ? (<span className="text-center"> diff --git a/components/logo.tsx b/components/logo.tsx index 145e3c7..7f4e540 100644 --- a/components/logo.tsx +++ b/components/logo.tsx @@ -1,4 +1,4 @@ -import { Icons } from "./icons"; +import { Icons } from "./icons" export function GameUnityLogo({ className }: { className?: string }) { return ( diff --git a/components/nav.tsx b/components/nav.tsx index a6d6e24..726ae9d 100644 --- a/components/nav.tsx +++ b/components/nav.tsx @@ -1,32 +1,32 @@ "use client" -import { Icons, IconsType } from "@/components/icons"; -import { buttonVariants } from "@/components/ui/button"; -import { cn } from "@/lib/utils"; -import { SidebarNavItem } from "@/types"; -import { useSession } from "next-auth/react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import { ModeToggle } from "./mode-toggle"; +import { Icons, IconsType } from "@/components/icons" +import { buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils" +import { SidebarNavItem } from "@/types" +import { useSession } from "next-auth/react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { ModeToggle } from "./mode-toggle" interface DashboardNavProps { - items: SidebarNavItem[]; + items: SidebarNavItem[] } export default function DashboardNav({ items }: DashboardNavProps) { - const path = usePathname(); - const { data: session } = useSession(); + const path = usePathname() + const { data: session } = useSession() if (!items?.length) { - return null; + return null } - const visibleItems = session ? items : items.slice(0, 2); + const visibleItems = session ? items : items.slice(0, 2) return ( <nav className="grid items-start gap-2"> {visibleItems.map((item, index) => { - const Icon = Icons[item.icon as keyof IconsType || "arrowRight"]; + const Icon = Icons[item.icon as keyof IconsType || "arrowRight"] if (item.title === "My Profile") { item.href = `/${session?.user?.username}` } @@ -63,5 +63,5 @@ export default function DashboardNav({ items }: DashboardNavProps) { </div> )} </nav> - ); + ) } \ No newline at end of file diff --git a/components/profile/types/index.ts b/components/profile/types/index.ts index 0e493c9..fb812e3 100644 --- a/components/profile/types/index.ts +++ b/components/profile/types/index.ts @@ -1,35 +1,35 @@ -import { Follows, Like, User } from "@prisma/client"; +import { Follows, Like, User } from "@prisma/client" -import { IGweet } from "@/components/gweets/types"; +import { IGweet } from "@/components/gweets/types" export interface IUser extends User { - gweets: IGweet[]; - followers: IFollow[]; - following: IFollow[]; - likes: ILike[]; - _count?: { - followers?: number; - following?: number; - }; + gweets: IGweet[] + followers: IFollow[] + following: IFollow[] + likes: ILike[] + _count?: { + followers?: number + following?: number + } } export interface IProfile { - name: string; - bio: string | undefined; - banner: { - url: string | undefined; - }; - avatar: { - url: string | undefined; - }; + name: string + bio: string | undefined + banner: { + url: string | undefined + } + avatar: { + url: string | undefined + } } export interface IFollow extends Follows { - follower: IUser; - following: IUser; + follower: IUser + following: IUser } export interface ILike extends Like { - user: IUser; - gweet: IGweet; + user: IUser + gweet: IGweet } diff --git a/components/scroll-to-top.tsx b/components/scroll-to-top.tsx index bd045be..685811c 100644 --- a/components/scroll-to-top.tsx +++ b/components/scroll-to-top.tsx @@ -1,37 +1,37 @@ "use client" -import { cn } from "@/lib/utils"; -import { useEffect, useState } from "react"; -import { Icons } from "./icons"; -import { Button } from "./ui/button"; +import { cn } from "@/lib/utils" +import { useEffect, useState } from "react" +import { Icons } from "./icons" +import { Button } from "./ui/button" export default function ScrollToTop() { - const [isVisible, setIsVisible] = useState(false); + const [isVisible, setIsVisible] = useState(false) const toggleVisibility = () => { if (window.pageYOffset > 300) { - setIsVisible(true); + setIsVisible(true) } else { - setIsVisible(false); + setIsVisible(false) } - }; + } const scrollToTop = () => { window.scrollTo({ top: 0, behavior: "smooth" - }); - }; + }) + } useEffect(() => { - window.addEventListener("scroll", toggleVisibility); - return () => window.removeEventListener("scroll", toggleVisibility); - }, []); + window.addEventListener("scroll", toggleVisibility) + return () => window.removeEventListener("scroll", toggleVisibility) + }, []) return ( <Button size="lg" onClick={scrollToTop} className={cn(isVisible ? "block" : "hidden", "")}> <Icons.arrowupline className="h-3 w-3" aria-hidden="true" /> </Button> - ); + ) } \ No newline at end of file diff --git a/components/search-input.tsx b/components/search-input.tsx index 1786c3c..5232ea1 100644 --- a/components/search-input.tsx +++ b/components/search-input.tsx @@ -1,19 +1,19 @@ "use client" -import { cn } from '@/lib/utils'; -import { usePathname, useRouter } from 'next/navigation'; -import { useState } from 'react'; -import { Icons } from './icons'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { toast } from './ui/use-toast'; +import { cn } from '@/lib/utils' +import { usePathname, useRouter } from 'next/navigation' +import { useState } from 'react' +import { Icons } from './icons' +import { Button } from './ui/button' +import { Input } from './ui/input' +import { toast } from './ui/use-toast' interface DocsSearchProps extends React.HTMLAttributes<HTMLFormElement> { } export default function SearchInput({ className, ...props }: DocsSearchProps) { - const [searchQuery, setSearchQuery] = useState(""); - const router = useRouter(); - const pathname = usePathname(); + const [searchQuery, setSearchQuery] = useState("") + const router = useRouter() + const pathname = usePathname() function onSearch(event: React.FormEvent) { event.preventDefault() @@ -51,5 +51,5 @@ export default function SearchInput({ className, ...props }: DocsSearchProps) { <Icons.arrowRight className="h-3 w-3" /> </Button> </form> - ); + ) } \ No newline at end of file diff --git a/components/trends/api/get-hashtags.ts b/components/trends/api/get-hashtags.ts index ddf2644..a669cee 100644 --- a/components/trends/api/get-hashtags.ts +++ b/components/trends/api/get-hashtags.ts @@ -1,8 +1,8 @@ export const getHashtags = async () => { - try { - const data = await fetch(`/api/hashtags`).then((result) => result.json()); - return data; - } catch (error: any) { - return error.response.data; - } -}; + try { + const data = await fetch(`/api/hashtags`).then((result) => result.json()) + return data + } catch (error: any) { + return error.response.data + } +} diff --git a/components/trends/api/post-hashtags.ts b/components/trends/api/post-hashtags.ts index 9a281b9..e372faa 100644 --- a/components/trends/api/post-hashtags.ts +++ b/components/trends/api/post-hashtags.ts @@ -1,12 +1,12 @@ export const postHashtags = async (hashtags: string[]) => { - try { - const data = await fetch(`/api/hashtags`, { - method: 'POST', - body: JSON.stringify(hashtags) - }).then((result) => result.json()) + try { + const data = await fetch(`/api/hashtags`, { + method: 'POST', + body: JSON.stringify(hashtags) + }).then((result) => result.json()) - return data; - } catch (error: any) { - return error.response.data; - } -}; + return data + } catch (error: any) { + return error.response.data + } +} diff --git a/components/trends/api/retrieve-hashtags-from-gweet.ts b/components/trends/api/retrieve-hashtags-from-gweet.ts index f73af88..f8e5475 100644 --- a/components/trends/api/retrieve-hashtags-from-gweet.ts +++ b/components/trends/api/retrieve-hashtags-from-gweet.ts @@ -1,4 +1,4 @@ export const retrieveHashtagsFromGweet = (text: string): string[] | null => { - const hashtags = text.match(/#\w+/gi); - return hashtags ? hashtags.map((hashtag) => hashtag.slice(1)) : null; -}; + const hashtags = text.match(/#\w+/gi) + return hashtags ? hashtags.map((hashtag) => hashtag.slice(1)) : null +} diff --git a/components/trends/components/trend.tsx b/components/trends/components/trend.tsx index 30d3567..0e3f734 100644 --- a/components/trends/components/trend.tsx +++ b/components/trends/components/trend.tsx @@ -1,26 +1,26 @@ -"use client"; +"use client" -import { useRouter } from "next/navigation"; -import { iTrendProps } from "../types"; +import { useRouter } from "next/navigation" +import { iTrendProps } from "../types" export const Trend = ({ ranking = 1, title, gweets = 1 }: iTrendProps) => { - const router = useRouter(); + const router = useRouter() - return ( - <div - onClick={() => { router.push(`/search?query=${title.toLowerCase()}`); }} - className="flex justify-between items-center p-1 cursor-pointer hover:bg-trends-hover active:bg-trends-active"> - <div className="p-2"> - <div className="flex items-center gap-2 text-tertiary"> - <span>{ranking}</span> - <span className="w-2 h-2 bg-tertiary rounded-full"></span> - <span>Trending</span> + return ( + <div + onClick={() => { router.push(`/search?query=${title.toLowerCase()}`) }} + className="flex justify-between items-center p-1 cursor-pointer hover:bg-trends-hover active:bg-trends-active"> + <div className="p-2"> + <div className="flex items-center gap-2 text-tertiary"> + <span>{ranking}</span> + <span className="w-2 h-2 bg-tertiary rounded-full"></span> + <span>Trending</span> + </div> + <div className="text-secondary font-medium">{title}</div> + <div className="text-tertiary"> + {gweets} {gweets === 1 ? "gweet" : "gweets"} + </div> + </div> </div> - <div className="text-secondary font-medium">{title}</div> - <div className="text-tertiary"> - {gweets} {gweets === 1 ? "gweet" : "gweets"} - </div> - </div> - </div> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/trends/components/trends.tsx b/components/trends/components/trends.tsx index de4f29d..9ea7fe5 100644 --- a/components/trends/components/trends.tsx +++ b/components/trends/components/trends.tsx @@ -1,46 +1,46 @@ -import Link from "next/link"; +import Link from "next/link" -import { Icons } from "@/components/icons"; -import { TryAgain } from "@/components/try-again"; -import { useHashtags } from "../hooks/use-hashtags"; -import { Trend } from "./trend"; +import { Icons } from "@/components/icons" +import { TryAgain } from "@/components/try-again" +import { useHashtags } from "../hooks/use-hashtags" +import { Trend } from "./trend" export const Trends = ({ title = "Trends" }: { title?: string }) => { - const { data: hashtags, isLoading, isError, isSuccess } = useHashtags(); + const { data: hashtags, isLoading, isError, isSuccess } = useHashtags() - if (hashtags && hashtags?.length <= 0) return null; + if (hashtags && hashtags?.length <= 0) return null - return ( - <div className=""> - {isLoading ? ( + return ( <div className=""> - <Icons.spinner className="h-4 w-4 animate-spin" /> + {isLoading ? ( + <div className=""> + <Icons.spinner className="h-4 w-4 animate-spin" /> + </div> + ) : isError ? ( + <div className=""> + <TryAgain /> + </div> + ) : ( + <> + <div className=""> + <h1 className="">{title}</h1> + {isSuccess && + hashtags?.map((hashtag, index) => { + return ( + <Trend + key={hashtag.id} + ranking={index + 1} + title={hashtag.text} + gweets={hashtag.score} + /> + ) + })} + </div> + <button className=""> + <Link href={`trends`}>Show more</Link> + </button> + </> + )} </div> - ) : isError ? ( - <div className=""> - <TryAgain /> - </div> - ) : ( - <> - <div className=""> - <h1 className="">{title}</h1> - {isSuccess && - hashtags?.map((hashtag, index) => { - return ( - <Trend - key={hashtag.id} - ranking={index + 1} - title={hashtag.text} - gweets={hashtag.score} - /> - ); - })} - </div> - <button className=""> - <Link href={`trends`}>Show more</Link> - </button> - </> - )} - </div> - ); -}; + ) +} diff --git a/components/trends/hooks/use-hashtags.ts b/components/trends/hooks/use-hashtags.ts index fff14be..18ea7d3 100644 --- a/components/trends/hooks/use-hashtags.ts +++ b/components/trends/hooks/use-hashtags.ts @@ -1,20 +1,20 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query" -import { getHashtags } from "../api/get-hashtags"; -import { IHashtag } from "../types"; +import { getHashtags } from "../api/get-hashtags" +import { IHashtag } from "../types" export const useHashtags = () => { - const queryClient = useQueryClient(); - return useQuery<IHashtag[]>( - ["hashtags"], - async () => { - return getHashtags(); - }, - { - refetchOnWindowFocus: false, - onSuccess: (data) => { - queryClient.setQueryData(["hashtags"], data); - }, - }, - ); -}; + const queryClient = useQueryClient() + return useQuery<IHashtag[]>( + ["hashtags"], + async () => { + return getHashtags() + }, + { + refetchOnWindowFocus: false, + onSuccess: (data) => { + queryClient.setQueryData(["hashtags"], data) + }, + }, + ) +} diff --git a/components/trends/index.ts b/components/trends/index.ts deleted file mode 100644 index 07693e1..0000000 --- a/components/trends/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./api/get-hashtags"; -export * from "./api/post-hashtags"; -export * from "./api/retrieve-hashtags-from-gweet"; -export * from "./components/trend"; -export * from "./components/trends"; -export * from "./hooks/use-hashtags"; -export * from "./types"; diff --git a/components/trends/types/index.ts b/components/trends/types/index.ts index ddfc805..8086784 100644 --- a/components/trends/types/index.ts +++ b/components/trends/types/index.ts @@ -1,11 +1,11 @@ export interface IHashtag { - id: string; - text: string; - score: number; + id: string + text: string + score: number } export interface iTrendProps { - ranking: number; - title: string; - gweets: number; -} + ranking: number + title: string + gweets: number +} \ No newline at end of file diff --git a/components/try-again.tsx b/components/try-again.tsx index 00cea11..218a1b6 100644 --- a/components/try-again.tsx +++ b/components/try-again.tsx @@ -1,7 +1,7 @@ -import { useRouter } from "next/navigation"; +import { useRouter } from "next/navigation" export const TryAgain = () => { - const router = useRouter(); + const router = useRouter() return ( <div className="flex flex-col items-center gap-1.2 p-4"> @@ -10,5 +10,5 @@ export const TryAgain = () => { <span>Retry</span> </button> </div> - ); -}; \ No newline at end of file + ) +} \ No newline at end of file diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx index 9ddbc46..f427b40 100644 --- a/components/ui/alert.tsx +++ b/components/ui/alert.tsx @@ -1,59 +1,59 @@ -import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react" import { cn } from "@/lib/utils" const alertVariants = cva( - "relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11", - { - variants: { - variant: { - default: "bg-background text-foreground", - destructive: - "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive", - }, - }, - defaultVariants: { - variant: "default", - }, - } + "relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } ) const Alert = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> >(({ className, variant, ...props }, ref) => ( - <div - ref={ref} - role="alert" - className={cn(alertVariants({ variant }), className)} - {...props} - /> + <div + ref={ref} + role="alert" + className={cn(alertVariants({ variant }), className)} + {...props} + /> )) Alert.displayName = "Alert" const AlertTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes<HTMLHeadingElement> + HTMLParagraphElement, + React.HTMLAttributes<HTMLHeadingElement> >(({ className, ...props }, ref) => ( - <h5 - ref={ref} - className={cn("mb-1 font-medium leading-none tracking-tight", className)} - {...props} - /> + <h5 + ref={ref} + className={cn("mb-1 font-medium leading-none tracking-tight", className)} + {...props} + /> )) AlertTitle.displayName = "AlertTitle" const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes<HTMLParagraphElement> + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> >(({ className, ...props }, ref) => ( - <div - ref={ref} - className={cn("text-sm [&_p]:leading-relaxed", className)} - {...props} - /> + <div + ref={ref} + className={cn("text-sm [&_p]:leading-relaxed", className)} + {...props} + /> )) AlertDescription.displayName = "AlertDescription" -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertDescription, AlertTitle } diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx index 51e507b..8195569 100644 --- a/components/ui/avatar.tsx +++ b/components/ui/avatar.tsx @@ -1,50 +1,50 @@ "use client" -import * as React from "react" import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react" import { cn } from "@/lib/utils" const Avatar = React.forwardRef< - React.ElementRef<typeof AvatarPrimitive.Root>, - React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> + React.ElementRef<typeof AvatarPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> >(({ className, ...props }, ref) => ( - <AvatarPrimitive.Root - ref={ref} - className={cn( - "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", - className - )} - {...props} - /> + <AvatarPrimitive.Root + ref={ref} + className={cn( + "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", + className + )} + {...props} + /> )) Avatar.displayName = AvatarPrimitive.Root.displayName const AvatarImage = React.forwardRef< - React.ElementRef<typeof AvatarPrimitive.Image>, - React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> + React.ElementRef<typeof AvatarPrimitive.Image>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image> >(({ className, ...props }, ref) => ( - <AvatarPrimitive.Image - ref={ref} - className={cn("aspect-square h-full w-full", className)} - {...props} - /> + <AvatarPrimitive.Image + ref={ref} + className={cn("aspect-square h-full w-full", className)} + {...props} + /> )) AvatarImage.displayName = AvatarPrimitive.Image.displayName const AvatarFallback = React.forwardRef< - React.ElementRef<typeof AvatarPrimitive.Fallback>, - React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> + React.ElementRef<typeof AvatarPrimitive.Fallback>, + React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback> >(({ className, ...props }, ref) => ( - <AvatarPrimitive.Fallback - ref={ref} - className={cn( - "flex h-full w-full items-center justify-center rounded-full bg-muted", - className - )} - {...props} - /> + <AvatarPrimitive.Fallback + ref={ref} + className={cn( + "flex h-full w-full items-center justify-center rounded-full bg-muted", + className + )} + {...props} + /> )) AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarFallback, AvatarImage } diff --git a/components/ui/button.tsx b/components/ui/button.tsx index 6c84d7d..d4adf71 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -5,53 +5,53 @@ import * as React from "react" import { cn } from "@/lib/utils" const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", - outline: - "text-primary border-primary border-2 input hover:bg-accent", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", - hover: "hover:bg-accent hover:text-accent-foreground hover:shadow-md hover:ring-2 hover:ring-ring hover:ring-offset-2", - link: "underline-offset-4 hover:underline text-primary", - }, - size: { - default: "h-10 py-2 px-4", - sm: "h-9 px-3 rounded-md", - lg: "h-11 px-8 rounded-full", - icon: "h-10 w-10 rounded-full", - option: "h-8 w-8 rounded-full", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "text-primary border-primary border-2 input hover:bg-accent", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + hover: "hover:bg-accent hover:text-accent-foreground hover:shadow-md hover:ring-2 hover:ring-ring hover:ring-offset-2", + link: "underline-offset-4 hover:underline text-primary", + }, + size: { + default: "h-10 py-2 px-4", + sm: "h-9 px-3 rounded-md", + lg: "h-11 px-8 rounded-full", + icon: "h-10 w-10 rounded-full", + option: "h-8 w-8 rounded-full", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } ) export interface ButtonProps - extends React.ButtonHTMLAttributes<HTMLButtonElement>, - VariantProps<typeof buttonVariants> { - asChild?: boolean + extends React.ButtonHTMLAttributes<HTMLButtonElement>, + VariantProps<typeof buttonVariants> { + asChild?: boolean } const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" - return ( - <Comp - className={cn(buttonVariants({ variant, size, className }))} - ref={ref} - {...props} - /> - ) - } + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + <Comp + className={cn(buttonVariants({ variant, size, className }))} + ref={ref} + {...props} + /> + ) + } ) Button.displayName = "Button" diff --git a/components/ui/card.tsx b/components/ui/card.tsx index dff04b6..e2f67a5 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -3,77 +3,78 @@ import * as React from "react" import { cn } from "@/lib/utils" const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( - <div - ref={ref} - className={cn( - "rounded-lg border bg-card text-card-foreground shadow-sm", - className - )} - {...props} - /> + <div + ref={ref} + className={cn( + "rounded-lg border bg-card text-card-foreground shadow-sm", + className + )} + {...props} + /> )) Card.displayName = "Card" const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( - <div - ref={ref} - className={cn("flex flex-col space-y-1.5 p-6", className)} - {...props} - /> + <div + ref={ref} + className={cn("flex flex-col space-y-1.5 p-6", className)} + {...props} + /> )) CardHeader.displayName = "CardHeader" const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes<HTMLHeadingElement> + HTMLParagraphElement, + React.HTMLAttributes<HTMLHeadingElement> >(({ className, ...props }, ref) => ( - <h3 - ref={ref} - className={cn( - "text-lg font-semibold leading-none tracking-tight", - className - )} - {...props} - /> + <h3 + ref={ref} + className={cn( + "text-lg font-semibold leading-none tracking-tight", + className + )} + {...props} + /> )) CardTitle.displayName = "CardTitle" const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes<HTMLParagraphElement> + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> >(({ className, ...props }, ref) => ( - <p - ref={ref} - className={cn("text-sm text-muted-foreground", className)} - {...props} - /> + <p + ref={ref} + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> )) CardDescription.displayName = "CardDescription" const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( - <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> + <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> )) CardContent.displayName = "CardContent" const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes<HTMLDivElement> + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( - <div - ref={ref} - className={cn(" flex items-center p-6 pt-0", className)} - {...props} - /> + <div + ref={ref} + className={cn("flex items-center p-6 pt-0", className)} + {...props} + /> )) CardFooter.displayName = "CardFooter" -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } +export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } + diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 7ac0d44..093b282 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -1,8 +1,8 @@ "use client" -import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { X } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" @@ -11,118 +11,112 @@ const Dialog = DialogPrimitive.Root const DialogTrigger = DialogPrimitive.Trigger const DialogPortal = ({ - className, - children, - ...props + className, + children, + ...props }: DialogPrimitive.DialogPortalProps) => ( - <DialogPrimitive.Portal className={cn(className)} {...props}> - <div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> - {children} - </div> - </DialogPrimitive.Portal> + <DialogPrimitive.Portal className={cn(className)} {...props}> + <div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> + {children} + </div> + </DialogPrimitive.Portal> ) DialogPortal.displayName = DialogPrimitive.Portal.displayName const DialogOverlay = React.forwardRef< - React.ElementRef<typeof DialogPrimitive.Overlay>, - React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> + React.ElementRef<typeof DialogPrimitive.Overlay>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> >(({ className, ...props }, ref) => ( - <DialogPrimitive.Overlay - ref={ref} - className={cn( - "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in", - className - )} - {...props} - /> + <DialogPrimitive.Overlay + ref={ref} + className={cn( + "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in", + className + )} + {...props} + /> )) DialogOverlay.displayName = DialogPrimitive.Overlay.displayName const DialogContent = React.forwardRef< - React.ElementRef<typeof DialogPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> + React.ElementRef<typeof DialogPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> >(({ className, children, ...props }, ref) => ( - <DialogPortal> - <DialogOverlay /> - <DialogPrimitive.Content - ref={ref} - className={cn( - "fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0", - className - )} - {...props} - > - {children} - <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> - <X className="h-4 w-4" /> - <span className="sr-only">Close</span> - </DialogPrimitive.Close> - </DialogPrimitive.Content> - </DialogPortal> + <DialogPortal> + <DialogOverlay /> + <DialogPrimitive.Content + ref={ref} + className={cn( + "fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0", + className + )} + {...props} + > + {children} + <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> + <X className="h-4 w-4" /> + <span className="sr-only">Close</span> + </DialogPrimitive.Close> + </DialogPrimitive.Content> + </DialogPortal> )) DialogContent.displayName = DialogPrimitive.Content.displayName const DialogHeader = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLDivElement>) => ( - <div - className={cn( - "flex flex-col space-y-1.5 text-center sm:text-left", - className - )} - {...props} - /> + <div + className={cn( + "flex flex-col space-y-1.5 text-center sm:text-left", + className + )} + {...props} + /> ) DialogHeader.displayName = "DialogHeader" const DialogFooter = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLDivElement>) => ( - <div - className={cn( - "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", - className - )} - {...props} - /> + <div + className={cn( + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", + className + )} + {...props} + /> ) DialogFooter.displayName = "DialogFooter" const DialogTitle = React.forwardRef< - React.ElementRef<typeof DialogPrimitive.Title>, - React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> + React.ElementRef<typeof DialogPrimitive.Title>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> >(({ className, ...props }, ref) => ( - <DialogPrimitive.Title - ref={ref} - className={cn( - "text-lg font-semibold leading-none tracking-tight", - className - )} - {...props} - /> + <DialogPrimitive.Title + ref={ref} + className={cn( + "text-lg font-semibold leading-none tracking-tight", + className + )} + {...props} + /> )) DialogTitle.displayName = DialogPrimitive.Title.displayName const DialogDescription = React.forwardRef< - React.ElementRef<typeof DialogPrimitive.Description>, - React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> + React.ElementRef<typeof DialogPrimitive.Description>, + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> >(({ className, ...props }, ref) => ( - <DialogPrimitive.Description - ref={ref} - className={cn("text-sm text-muted-foreground", className)} - {...props} - /> + <DialogPrimitive.Description + ref={ref} + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> )) DialogDescription.displayName = DialogPrimitive.Description.displayName export { - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, + Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx index 93dbd16..05d426c 100644 --- a/components/ui/dropdown-menu.tsx +++ b/components/ui/dropdown-menu.tsx @@ -1,8 +1,8 @@ "use client" -import * as React from "react" import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" import { Check, ChevronRight, Circle } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" @@ -19,182 +19,171 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { - inset?: boolean - } + React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & { + inset?: boolean + } >(({ className, inset, children, ...props }, ref) => ( - <DropdownMenuPrimitive.SubTrigger - ref={ref} - className={cn( - "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent", - inset && "pl-8", - className - )} - {...props} - > - {children} - <ChevronRight className="ml-auto h-4 w-4" /> - </DropdownMenuPrimitive.SubTrigger> + <DropdownMenuPrimitive.SubTrigger + ref={ref} + className={cn( + "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent", + inset && "pl-8", + className + )} + {...props} + > + {children} + <ChevronRight className="ml-auto h-4 w-4" /> + </DropdownMenuPrimitive.SubTrigger> )) DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName + DropdownMenuPrimitive.SubTrigger.displayName const DropdownMenuSubContent = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> + React.ElementRef<typeof DropdownMenuPrimitive.SubContent>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent> >(({ className, ...props }, ref) => ( - <DropdownMenuPrimitive.SubContent - ref={ref} - className={cn( - "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", - className - )} - {...props} - /> + <DropdownMenuPrimitive.SubContent + ref={ref} + className={cn( + "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", + className + )} + {...props} + /> )) DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName + DropdownMenuPrimitive.SubContent.displayName const DropdownMenuContent = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> + React.ElementRef<typeof DropdownMenuPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> >(({ className, sideOffset = 4, ...props }, ref) => ( - <DropdownMenuPrimitive.Portal> - <DropdownMenuPrimitive.Content - ref={ref} - sideOffset={sideOffset} - className={cn( - "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", - className - )} - {...props} - /> - </DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Portal> + <DropdownMenuPrimitive.Content + ref={ref} + sideOffset={sideOffset} + className={cn( + "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", + className + )} + {...props} + /> + </DropdownMenuPrimitive.Portal> )) DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName const DropdownMenuItem = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.Item>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { - inset?: boolean - } + React.ElementRef<typeof DropdownMenuPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { + inset?: boolean + } >(({ className, inset, ...props }, ref) => ( - <DropdownMenuPrimitive.Item - ref={ref} - className={cn( - "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - inset && "pl-8", - className - )} - {...props} - /> + <DropdownMenuPrimitive.Item + ref={ref} + className={cn( + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + inset && "pl-8", + className + )} + {...props} + /> )) DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> + React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> >(({ className, children, checked, ...props }, ref) => ( - <DropdownMenuPrimitive.CheckboxItem - ref={ref} - className={cn( - "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className - )} - checked={checked} - {...props} - > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> - <DropdownMenuPrimitive.ItemIndicator> - <Check className="h-4 w-4" /> - </DropdownMenuPrimitive.ItemIndicator> - </span> - {children} - </DropdownMenuPrimitive.CheckboxItem> + <DropdownMenuPrimitive.CheckboxItem + ref={ref} + className={cn( + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + className + )} + checked={checked} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Check className="h-4 w-4" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.CheckboxItem> )) DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName + DropdownMenuPrimitive.CheckboxItem.displayName const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> + React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> >(({ className, children, ...props }, ref) => ( - <DropdownMenuPrimitive.RadioItem - ref={ref} - className={cn( - "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className - )} - {...props} - > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> - <DropdownMenuPrimitive.ItemIndicator> - <Circle className="h-2 w-2 fill-current" /> - </DropdownMenuPrimitive.ItemIndicator> - </span> - {children} - </DropdownMenuPrimitive.RadioItem> + <DropdownMenuPrimitive.RadioItem + ref={ref} + className={cn( + "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + className + )} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <DropdownMenuPrimitive.ItemIndicator> + <Circle className="h-2 w-2 fill-current" /> + </DropdownMenuPrimitive.ItemIndicator> + </span> + {children} + </DropdownMenuPrimitive.RadioItem> )) DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName const DropdownMenuLabel = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.Label>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { - inset?: boolean - } + React.ElementRef<typeof DropdownMenuPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { + inset?: boolean + } >(({ className, inset, ...props }, ref) => ( - <DropdownMenuPrimitive.Label - ref={ref} - className={cn( - "px-2 py-1.5 text-sm font-semibold", - inset && "pl-8", - className - )} - {...props} - /> + <DropdownMenuPrimitive.Label + ref={ref} + className={cn( + "px-2 py-1.5 text-sm font-semibold", + inset && "pl-8", + className + )} + {...props} + /> )) DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName const DropdownMenuSeparator = React.forwardRef< - React.ElementRef<typeof DropdownMenuPrimitive.Separator>, - React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> + React.ElementRef<typeof DropdownMenuPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator> >(({ className, ...props }, ref) => ( - <DropdownMenuPrimitive.Separator - ref={ref} - className={cn("-mx-1 my-1 h-px bg-muted", className)} - {...props} - /> + <DropdownMenuPrimitive.Separator + ref={ref} + className={cn("-mx-1 my-1 h-px bg-muted", className)} + {...props} + /> )) DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName const DropdownMenuShortcut = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLSpanElement>) => { - return ( - <span - className={cn("ml-auto text-xs tracking-widest opacity-60", className)} - {...props} - /> - ) + return ( + <span + className={cn("ml-auto text-xs tracking-widest opacity-60", className)} + {...props} + /> + ) } DropdownMenuShortcut.displayName = "DropdownMenuShortcut" export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, + DropdownMenuShortcut, DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger } diff --git a/components/ui/form.tsx b/components/ui/form.tsx index 63b6a61..feafc5c 100644 --- a/components/ui/form.tsx +++ b/components/ui/form.tsx @@ -165,13 +165,8 @@ const FormMessage = React.forwardRef< FormMessage.displayName = "FormMessage" export { - useFormField, - Form, - FormItem, - FormLabel, - FormControl, - FormDescription, - FormMessage, - FormField, + Form, FormControl, + FormDescription, FormField, FormItem, + FormLabel, FormMessage, useFormField } diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx index 8abff5a..2c1f5ff 100644 --- a/components/ui/hover-card.tsx +++ b/components/ui/hover-card.tsx @@ -1,7 +1,7 @@ "use client" -import * as React from "react" import * as HoverCardPrimitive from "@radix-ui/react-hover-card" +import * as React from "react" import { cn } from "@/lib/utils" @@ -10,20 +10,20 @@ const HoverCard = HoverCardPrimitive.Root const HoverCardTrigger = HoverCardPrimitive.Trigger const HoverCardContent = React.forwardRef< - React.ElementRef<typeof HoverCardPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> + React.ElementRef<typeof HoverCardPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - <HoverCardPrimitive.Content - ref={ref} - align={align} - sideOffset={sideOffset} - className={cn( - "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in zoom-in-90", - className - )} - {...props} - /> + <HoverCardPrimitive.Content + ref={ref} + align={align} + sideOffset={sideOffset} + className={cn( + "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in zoom-in-90", + className + )} + {...props} + /> )) HoverCardContent.displayName = HoverCardPrimitive.Content.displayName -export { HoverCard, HoverCardTrigger, HoverCardContent } +export { HoverCard, HoverCardContent, HoverCardTrigger } diff --git a/components/ui/input.tsx b/components/ui/input.tsx index 929e05f..25aed52 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -3,22 +3,22 @@ import * as React from "react" import { cn } from "@/lib/utils" export interface InputProps - extends React.InputHTMLAttributes<HTMLInputElement> {} + extends React.InputHTMLAttributes<HTMLInputElement> { } const Input = React.forwardRef<HTMLInputElement, InputProps>( - ({ className, type, ...props }, ref) => { - return ( - <input - type={type} - className={cn( - "flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", - className - )} - ref={ref} - {...props} - /> - ) - } + ({ className, type, ...props }, ref) => { + return ( + <input + type={type} + className={cn( + "flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", + className + )} + ref={ref} + {...props} + /> + ) + } ) Input.displayName = "Input" diff --git a/components/ui/label.tsx b/components/ui/label.tsx index 5341821..5ca447b 100644 --- a/components/ui/label.tsx +++ b/components/ui/label.tsx @@ -1,25 +1,25 @@ "use client" -import * as React from "react" import * as LabelPrimitive from "@radix-ui/react-label" import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react" import { cn } from "@/lib/utils" const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" ) const Label = React.forwardRef< - React.ElementRef<typeof LabelPrimitive.Root>, - React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & + React.ElementRef<typeof LabelPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants> >(({ className, ...props }, ref) => ( - <LabelPrimitive.Root - ref={ref} - className={cn(labelVariants(), className)} - {...props} - /> + <LabelPrimitive.Root + ref={ref} + className={cn(labelVariants(), className)} + {...props} + /> )) Label.displayName = LabelPrimitive.Root.displayName diff --git a/components/ui/scroll-area.tsx b/components/ui/scroll-area.tsx index 54b87cd..6775919 100644 --- a/components/ui/scroll-area.tsx +++ b/components/ui/scroll-area.tsx @@ -1,47 +1,47 @@ "use client" -import * as React from "react" import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" +import * as React from "react" import { cn } from "@/lib/utils" const ScrollArea = React.forwardRef< - React.ElementRef<typeof ScrollAreaPrimitive.Root>, - React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> + React.ElementRef<typeof ScrollAreaPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> >(({ className, children, ...props }, ref) => ( - <ScrollAreaPrimitive.Root - ref={ref} - className={cn("relative overflow-hidden", className)} - {...props} - > - <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> - {children} - </ScrollAreaPrimitive.Viewport> - <ScrollBar /> - <ScrollAreaPrimitive.Corner /> - </ScrollAreaPrimitive.Root> + <ScrollAreaPrimitive.Root + ref={ref} + className={cn("relative overflow-hidden", className)} + {...props} + > + <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]"> + {children} + </ScrollAreaPrimitive.Viewport> + <ScrollBar /> + <ScrollAreaPrimitive.Corner /> + </ScrollAreaPrimitive.Root> )) ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName const ScrollBar = React.forwardRef< - React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, - React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> + React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>, + React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> >(({ className, orientation = "vertical", ...props }, ref) => ( - <ScrollAreaPrimitive.ScrollAreaScrollbar - ref={ref} - orientation={orientation} - className={cn( - "flex touch-none select-none transition-colors", - orientation === "vertical" && - "h-full w-2.5 border-l border-l-transparent p-[1px]", - orientation === "horizontal" && - "h-2.5 border-t border-t-transparent p-[1px]", - className - )} - {...props} - > - <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> - </ScrollAreaPrimitive.ScrollAreaScrollbar> + <ScrollAreaPrimitive.ScrollAreaScrollbar + ref={ref} + orientation={orientation} + className={cn( + "flex touch-none select-none transition-colors", + orientation === "vertical" && + "h-full w-2.5 border-l border-l-transparent p-[1px]", + orientation === "horizontal" && + "h-2.5 border-t border-t-transparent p-[1px]", + className + )} + {...props} + > + <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" /> + </ScrollAreaPrimitive.ScrollAreaScrollbar> )) ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName diff --git a/components/ui/select.tsx b/components/ui/select.tsx index 0d23fc4..cde381c 100644 --- a/components/ui/select.tsx +++ b/components/ui/select.tsx @@ -1,8 +1,8 @@ "use client" -import * as React from "react" import * as SelectPrimitive from "@radix-ui/react-select" import { Check, ChevronDown } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" @@ -13,108 +13,101 @@ const SelectGroup = SelectPrimitive.Group const SelectValue = SelectPrimitive.Value const SelectTrigger = React.forwardRef< - React.ElementRef<typeof SelectPrimitive.Trigger>, - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> + React.ElementRef<typeof SelectPrimitive.Trigger>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> >(({ className, children, ...props }, ref) => ( - <SelectPrimitive.Trigger - ref={ref} - className={cn( - "flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", - className - )} - {...props} - > - {children} - <SelectPrimitive.Icon asChild> - <ChevronDown className="h-4 w-4 opacity-50" /> - </SelectPrimitive.Icon> - </SelectPrimitive.Trigger> + <SelectPrimitive.Trigger + ref={ref} + className={cn( + "flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", + className + )} + {...props} + > + {children} + <SelectPrimitive.Icon asChild> + <ChevronDown className="h-4 w-4 opacity-50" /> + </SelectPrimitive.Icon> + </SelectPrimitive.Trigger> )) SelectTrigger.displayName = SelectPrimitive.Trigger.displayName const SelectContent = React.forwardRef< - React.ElementRef<typeof SelectPrimitive.Content>, - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> + React.ElementRef<typeof SelectPrimitive.Content>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> >(({ className, children, position = "popper", ...props }, ref) => ( - <SelectPrimitive.Portal> - <SelectPrimitive.Content - ref={ref} - className={cn( - "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-80", - position === "popper" && "translate-y-1", - className - )} - position={position} - {...props} - > - <SelectPrimitive.Viewport - className={cn( - "p-1", - position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" - )} - > - {children} - </SelectPrimitive.Viewport> - </SelectPrimitive.Content> - </SelectPrimitive.Portal> + <SelectPrimitive.Portal> + <SelectPrimitive.Content + ref={ref} + className={cn( + "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-80", + position === "popper" && "translate-y-1", + className + )} + position={position} + {...props} + > + <SelectPrimitive.Viewport + className={cn( + "p-1", + position === "popper" && + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" + )} + > + {children} + </SelectPrimitive.Viewport> + </SelectPrimitive.Content> + </SelectPrimitive.Portal> )) SelectContent.displayName = SelectPrimitive.Content.displayName const SelectLabel = React.forwardRef< - React.ElementRef<typeof SelectPrimitive.Label>, - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> + React.ElementRef<typeof SelectPrimitive.Label>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> >(({ className, ...props }, ref) => ( - <SelectPrimitive.Label - ref={ref} - className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)} - {...props} - /> + <SelectPrimitive.Label + ref={ref} + className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)} + {...props} + /> )) SelectLabel.displayName = SelectPrimitive.Label.displayName const SelectItem = React.forwardRef< - React.ElementRef<typeof SelectPrimitive.Item>, - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> + React.ElementRef<typeof SelectPrimitive.Item>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> >(({ className, children, ...props }, ref) => ( - <SelectPrimitive.Item - ref={ref} - className={cn( - "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className - )} - {...props} - > - <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> - <SelectPrimitive.ItemIndicator> - <Check className="h-4 w-4" /> - </SelectPrimitive.ItemIndicator> - </span> + <SelectPrimitive.Item + ref={ref} + className={cn( + "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", + className + )} + {...props} + > + <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> + <SelectPrimitive.ItemIndicator> + <Check className="h-4 w-4" /> + </SelectPrimitive.ItemIndicator> + </span> - <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> - </SelectPrimitive.Item> + <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> + </SelectPrimitive.Item> )) SelectItem.displayName = SelectPrimitive.Item.displayName const SelectSeparator = React.forwardRef< - React.ElementRef<typeof SelectPrimitive.Separator>, - React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> + React.ElementRef<typeof SelectPrimitive.Separator>, + React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> >(({ className, ...props }, ref) => ( - <SelectPrimitive.Separator - ref={ref} - className={cn("-mx-1 my-1 h-px bg-muted", className)} - {...props} - /> + <SelectPrimitive.Separator + ref={ref} + className={cn("-mx-1 my-1 h-px bg-muted", className)} + {...props} + /> )) SelectSeparator.displayName = SelectPrimitive.Separator.displayName export { - Select, - SelectGroup, - SelectValue, - SelectTrigger, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, + Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx index 12d81c4..8507825 100644 --- a/components/ui/separator.tsx +++ b/components/ui/separator.tsx @@ -1,30 +1,30 @@ "use client" -import * as React from "react" import * as SeparatorPrimitive from "@radix-ui/react-separator" +import * as React from "react" import { cn } from "@/lib/utils" const Separator = React.forwardRef< - React.ElementRef<typeof SeparatorPrimitive.Root>, - React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> + React.ElementRef<typeof SeparatorPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> >( - ( - { className, orientation = "horizontal", decorative = true, ...props }, - ref - ) => ( - <SeparatorPrimitive.Root - ref={ref} - decorative={decorative} - orientation={orientation} - className={cn( - "shrink-0 bg-border", - orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", - className - )} - {...props} - /> - ) + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + <SeparatorPrimitive.Root + ref={ref} + decorative={decorative} + orientation={orientation} + className={cn( + "shrink-0 bg-border", + orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", + className + )} + {...props} + /> + ) ) Separator.displayName = SeparatorPrimitive.Root.displayName diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx index 98cdd40..13cd645 100644 --- a/components/ui/sheet.tsx +++ b/components/ui/sheet.tsx @@ -1,9 +1,9 @@ "use client" -import * as React from "react" import * as SheetPrimitive from "@radix-ui/react-dialog" import { cva, type VariantProps } from "class-variance-authority" import { X } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" @@ -14,131 +14,125 @@ const SheetTrigger = SheetPrimitive.Trigger const SheetClose = SheetPrimitive.Close const SheetPortal = ({ - className, - ...props + className, + ...props }: SheetPrimitive.DialogPortalProps) => ( - <SheetPrimitive.Portal className={cn(className)} {...props} /> + <SheetPrimitive.Portal className={cn(className)} {...props} /> ) SheetPortal.displayName = SheetPrimitive.Portal.displayName const SheetOverlay = React.forwardRef< - React.ElementRef<typeof SheetPrimitive.Overlay>, - React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> + React.ElementRef<typeof SheetPrimitive.Overlay>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> >(({ className, ...props }, ref) => ( - <SheetPrimitive.Overlay - className={cn( - "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", - className - )} - {...props} - ref={ref} - /> + <SheetPrimitive.Overlay + className={cn( + "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", + className + )} + {...props} + ref={ref} + /> )) SheetOverlay.displayName = SheetPrimitive.Overlay.displayName const sheetVariants = cva( - "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", - { - variants: { - side: { - top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", - bottom: - "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", - left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", - right: - "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", - }, - }, - defaultVariants: { - side: "right", - }, - } + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } ) interface SheetContentProps - extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, - VariantProps<typeof sheetVariants> {} + extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, + VariantProps<typeof sheetVariants> { } const SheetContent = React.forwardRef< - React.ElementRef<typeof SheetPrimitive.Content>, - SheetContentProps + React.ElementRef<typeof SheetPrimitive.Content>, + SheetContentProps >(({ side = "right", className, children, ...props }, ref) => ( - <SheetPortal> - <SheetOverlay /> - <SheetPrimitive.Content - ref={ref} - className={cn(sheetVariants({ side }), className)} - {...props} - > - {children} - <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> - <X className="h-4 w-4" /> - <span className="sr-only">Close</span> - </SheetPrimitive.Close> - </SheetPrimitive.Content> - </SheetPortal> + <SheetPortal> + <SheetOverlay /> + <SheetPrimitive.Content + ref={ref} + className={cn(sheetVariants({ side }), className)} + {...props} + > + {children} + <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> + <X className="h-4 w-4" /> + <span className="sr-only">Close</span> + </SheetPrimitive.Close> + </SheetPrimitive.Content> + </SheetPortal> )) SheetContent.displayName = SheetPrimitive.Content.displayName const SheetHeader = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLDivElement>) => ( - <div - className={cn( - "flex flex-col space-y-2 text-center sm:text-left", - className - )} - {...props} - /> + <div + className={cn( + "flex flex-col space-y-2 text-center sm:text-left", + className + )} + {...props} + /> ) SheetHeader.displayName = "SheetHeader" const SheetFooter = ({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLDivElement>) => ( - <div - className={cn( - "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", - className - )} - {...props} - /> + <div + className={cn( + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", + className + )} + {...props} + /> ) SheetFooter.displayName = "SheetFooter" const SheetTitle = React.forwardRef< - React.ElementRef<typeof SheetPrimitive.Title>, - React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> + React.ElementRef<typeof SheetPrimitive.Title>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> >(({ className, ...props }, ref) => ( - <SheetPrimitive.Title - ref={ref} - className={cn("text-lg font-semibold text-foreground", className)} - {...props} - /> + <SheetPrimitive.Title + ref={ref} + className={cn("text-lg font-semibold text-foreground", className)} + {...props} + /> )) SheetTitle.displayName = SheetPrimitive.Title.displayName const SheetDescription = React.forwardRef< - React.ElementRef<typeof SheetPrimitive.Description>, - React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> + React.ElementRef<typeof SheetPrimitive.Description>, + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> >(({ className, ...props }, ref) => ( - <SheetPrimitive.Description - ref={ref} - className={cn("text-sm text-muted-foreground", className)} - {...props} - /> + <SheetPrimitive.Description + ref={ref} + className={cn("text-sm text-muted-foreground", className)} + {...props} + /> )) SheetDescription.displayName = SheetPrimitive.Description.displayName export { - Sheet, - SheetTrigger, - SheetClose, - SheetContent, - SheetHeader, - SheetFooter, - SheetTitle, - SheetDescription, + Sheet, SheetClose, + SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx index 01b8b6d..6864060 100644 --- a/components/ui/skeleton.tsx +++ b/components/ui/skeleton.tsx @@ -1,15 +1,15 @@ import { cn } from "@/lib/utils" function Skeleton({ - className, - ...props + className, + ...props }: React.HTMLAttributes<HTMLDivElement>) { - return ( - <div - className={cn("animate-pulse rounded-md bg-muted", className)} - {...props} - /> - ) + return ( + <div + className={cn("animate-pulse rounded-md bg-muted", className)} + {...props} + /> + ) } export { Skeleton } diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx index dc548f3..1c2631b 100644 --- a/components/ui/textarea.tsx +++ b/components/ui/textarea.tsx @@ -3,21 +3,21 @@ import * as React from "react" import { cn } from "@/lib/utils" export interface TextareaProps - extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { } + extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { } const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( - ({ className, ...props }, ref) => { - return ( - <textarea - className={cn( - "flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", - className - )} - ref={ref} - {...props} - /> - ) - } + ({ className, ...props }, ref) => { + return ( + <textarea + className={cn( + "flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", + className + )} + ref={ref} + {...props} + /> + ) + } ) Textarea.displayName = "Textarea" diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx index 32ffed0..354805e 100644 --- a/components/ui/toast.tsx +++ b/components/ui/toast.tsx @@ -8,105 +8,105 @@ import { cn } from "@/lib/utils" const ToastProvider = ToastPrimitives.Provider const ToastViewport = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Viewport>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> + React.ElementRef<typeof ToastPrimitives.Viewport>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Viewport - ref={ref} - className={cn( - "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", - className - )} - {...props} - /> + <ToastPrimitives.Viewport + ref={ref} + className={cn( + "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", + className + )} + {...props} + /> )) ToastViewport.displayName = ToastPrimitives.Viewport.displayName const toastVariants = cva( - "data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full", - { - variants: { - variant: { - default: "bg-background border", - destructive: - "group destructive border-destructive bg-destructive text-destructive-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } + "data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full", + { + variants: { + variant: { + default: "bg-background border", + destructive: + "group destructive border-destructive bg-destructive text-destructive-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } ) const Toast = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Root>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & - VariantProps<typeof toastVariants> + React.ElementRef<typeof ToastPrimitives.Root>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & + VariantProps<typeof toastVariants> >(({ className, variant, ...props }, ref) => { - return ( - <ToastPrimitives.Root - ref={ref} - className={cn(toastVariants({ variant }), className)} - {...props} - /> - ) + return ( + <ToastPrimitives.Root + ref={ref} + className={cn(toastVariants({ variant }), className)} + {...props} + /> + ) }) Toast.displayName = ToastPrimitives.Root.displayName const ToastAction = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Action>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> + React.ElementRef<typeof ToastPrimitives.Action>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Action - ref={ref} - className={cn( - "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", - className - )} - {...props} - /> + <ToastPrimitives.Action + ref={ref} + className={cn( + "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-destructive/30 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", + className + )} + {...props} + /> )) ToastAction.displayName = ToastPrimitives.Action.displayName const ToastClose = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Close>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> + React.ElementRef<typeof ToastPrimitives.Close>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Close - ref={ref} - className={cn( - "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", - className - )} - toast-close="" - {...props} - > - <X className="h-4 w-4" /> - </ToastPrimitives.Close> + <ToastPrimitives.Close + ref={ref} + className={cn( + "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600", + className + )} + toast-close="" + {...props} + > + <X className="h-4 w-4" /> + </ToastPrimitives.Close> )) ToastClose.displayName = ToastPrimitives.Close.displayName const ToastTitle = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Title>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> + React.ElementRef<typeof ToastPrimitives.Title>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Title - ref={ref} - className={cn("text-sm font-semibold", className)} - {...props} - /> + <ToastPrimitives.Title + ref={ref} + className={cn("text-sm font-semibold", className)} + {...props} + /> )) ToastTitle.displayName = ToastPrimitives.Title.displayName const ToastDescription = React.forwardRef< - React.ElementRef<typeof ToastPrimitives.Description>, - React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> + React.ElementRef<typeof ToastPrimitives.Description>, + React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description> >(({ className, ...props }, ref) => ( - <ToastPrimitives.Description - ref={ref} - className={cn("text-sm opacity-90", className)} - {...props} - /> + <ToastPrimitives.Description + ref={ref} + className={cn("text-sm opacity-90", className)} + {...props} + /> )) ToastDescription.displayName = ToastPrimitives.Description.displayName @@ -115,5 +115,5 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast> type ToastActionElement = React.ReactElement<typeof ToastAction> export { - Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, type ToastActionElement, type ToastProps + Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, type ToastActionElement, type ToastProps } diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx index 4414b1c..099c7f9 100644 --- a/components/ui/toaster.tsx +++ b/components/ui/toaster.tsx @@ -1,35 +1,35 @@ "use client" import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, } from "@/components/ui/toast" import { useToast } from "@/components/ui/use-toast" export function Toaster() { - const { toasts } = useToast() + const { toasts } = useToast() - return ( - <ToastProvider> - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - <Toast key={id} {...props}> - <div className="grid gap-1"> - {title && <ToastTitle>{title}</ToastTitle>} - {description && ( - <ToastDescription>{description}</ToastDescription> - )} - </div> - {action} - <ToastClose /> - </Toast> - ) - })} - <ToastViewport /> - </ToastProvider> - ) + return ( + <ToastProvider> + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + <Toast key={id} {...props}> + <div className="grid gap-1"> + {title && <ToastTitle>{title}</ToastTitle>} + {description && ( + <ToastDescription>{description}</ToastDescription> + )} + </div> + {action} + <ToastClose /> + </Toast> + ) + })} + <ToastViewport /> + </ToastProvider> + ) } \ No newline at end of file diff --git a/components/ui/use-toast.ts b/components/ui/use-toast.ts index 2c94c2d..52930fa 100644 --- a/components/ui/use-toast.ts +++ b/components/ui/use-toast.ts @@ -7,121 +7,121 @@ const TOAST_LIMIT = 1 const TOAST_REMOVE_DELAY = 1000000 type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement + id: string + title?: React.ReactNode + description?: React.ReactNode + action?: ToastActionElement } const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", } as const let count = 0 function genId() { - count = (count + 1) % Number.MAX_VALUE - return count.toString() + count = (count + 1) % Number.MAX_VALUE + return count.toString() } type ActionType = typeof actionTypes type Action = - | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast + | { + type: ActionType["ADD_TOAST"] + toast: ToasterToast } - | { - type: ActionType["UPDATE_TOAST"] - toast: Partial<ToasterToast> + | { + type: ActionType["UPDATE_TOAST"] + toast: Partial<ToasterToast> } - | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] + | { + type: ActionType["DISMISS_TOAST"] + toastId?: ToasterToast["id"] } - | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] + | { + type: ActionType["REMOVE_TOAST"] + toastId?: ToasterToast["id"] } interface State { - toasts: ToasterToast[] + toasts: ToasterToast[] } const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>() const addToRemoveQueue = (toastId: string) => { - if (toastTimeouts.has(toastId)) { - return - } + if (toastTimeouts.has(toastId)) { + return + } - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) - dispatch({ - type: "REMOVE_TOAST", - toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId) + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }) + }, TOAST_REMOVE_DELAY) - toastTimeouts.set(toastId, timeout) + toastTimeouts.set(toastId, timeout) } export const reducer = (state: State, action: Action): State => { - switch (action.type) { - case "ADD_TOAST": - return { - ...state, - toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } - - case "UPDATE_TOAST": - return { - ...state, - toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t - ), - } - - case "DISMISS_TOAST": { - const { toastId } = action - - // ! Side effects ! - This could be extracted into a dismissToast() action, - // but I'll keep it here for simplicity - if (toastId) { - addToRemoveQueue(toastId) - } else { - state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) - } - - return { - ...state, - toasts: state.toasts.map((t) => - t.id === toastId || toastId === undefined - ? { - ...t, - open: false, - } - : t - ), - } - } - case "REMOVE_TOAST": - if (action.toastId === undefined) { - return { - ...state, - toasts: [], + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + } + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t + ), + } + + case "DISMISS_TOAST": { + const { toastId } = action + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId) + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id) + }) + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t + ), + } } - } - return { - ...state, - toasts: state.toasts.filter((t) => t.id !== action.toastId), - } - } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + } + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + } + } } const listeners: Array<(state: State) => void> = [] @@ -129,61 +129,61 @@ const listeners: Array<(state: State) => void> = [] let memoryState: State = { toasts: [] } function dispatch(action: Action) { - memoryState = reducer(memoryState, action) - listeners.forEach((listener) => { - listener(memoryState) - }) + memoryState = reducer(memoryState, action) + listeners.forEach((listener) => { + listener(memoryState) + }) } type Toast = Omit<ToasterToast, "id"> function toast({ ...props }: Toast) { - const id = genId() + const id = genId() + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }) + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) - const update = (props: ToasterToast) => dispatch({ - type: "UPDATE_TOAST", - toast: { ...props, id }, + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss() + }, + }, }) - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) - - dispatch({ - type: "ADD_TOAST", - toast: { - ...props, - id, - open: true, - onOpenChange: (open) => { - if (!open) dismiss() - }, - }, - }) - - return { - id: id, - dismiss, - update, - } + + return { + id: id, + dismiss, + update, + } } function useToast() { - const [state, setState] = React.useState<State>(memoryState) - - React.useEffect(() => { - listeners.push(setState) - return () => { - const index = listeners.indexOf(setState) - if (index > -1) { - listeners.splice(index, 1) - } - } - }, [state]) + const [state, setState] = React.useState<State>(memoryState) + + React.useEffect(() => { + listeners.push(setState) + return () => { + const index = listeners.indexOf(setState) + if (index > -1) { + listeners.splice(index, 1) + } + } + }, [state]) - return { - ...state, - toast, - dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - } + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + } } -export { useToast, toast } +export { toast, useToast } diff --git a/components/user-auth-form.tsx b/components/user-auth-form.tsx index f956d48..cf72baf 100644 --- a/components/user-auth-form.tsx +++ b/components/user-auth-form.tsx @@ -33,7 +33,7 @@ export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { }) const [isLoading, setIsLoading] = useState<boolean>(false) const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false) - const router = useRouter(); + const router = useRouter() const searchParams = useSearchParams() async function onSubmit(data: FormData) { @@ -54,7 +54,7 @@ export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { if (!res.ok) { if (res.status === 422) { - setError('email', { type: 'manual', message: 'This email is already in use. Please choose another one.' }); + setError('email', { type: 'manual', message: 'This email is already in use. Please choose another one.' }) } setIsLoading(false) @@ -71,7 +71,7 @@ export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { password: data.password, redirect: false, callbackUrl: searchParams?.get("from") || "/home", - }); + }) setIsLoading(false) @@ -80,13 +80,13 @@ export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { setError('usernameOrEmail', { type: 'manual', message: 'Sorry, we couldn\'t find an account with the provided email / username. Please double-check your input or create a new account.' - }); + }) } if (signInResult.error === "invalid password") { setError('password', { type: 'manual', message: 'Sorry, but it seems like the password you entered is invalid. Please try again.' - }); + }) } return toast({ variant: "destructive", diff --git a/lib/auth.ts b/lib/auth.ts index b1696bb..382e702 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -40,7 +40,7 @@ export const authOptions: NextAuthOptions = { { email: credentials.usernameOrEmail.toLowerCase() }, ], }, - }); + }) if (!user || !user.password) { throw new Error('user not found') @@ -91,31 +91,31 @@ export const authOptions: NextAuthOptions = { } if (!dbUser.username) { - let username = await normalize(dbUser.name?.toLowerCase().replace(/\s/g, '')); - const email = dbUser.email?.toLowerCase(); + let username = await normalize(dbUser.name?.toLowerCase().replace(/\s/g, '')) + const email = dbUser.email?.toLowerCase() - let isUnique = false; + let isUnique = false while (!isUnique) { const existingUserName = await db.user.findFirst({ where: { username, NOT: { email }, }, - }); + }) if (existingUserName) { - username = `${username}${Math.floor(Math.random() * 1000)}`; + username = `${username}${Math.floor(Math.random() * 1000)}` } else { - isUnique = true; + isUnique = true } } - dbUser.username = username; + dbUser.username = username await db.user.update({ where: { email }, data: { username }, - }); + }) } return { diff --git a/lib/config/dashboard.ts b/lib/config/dashboard.ts index aaf550c..0770e27 100644 --- a/lib/config/dashboard.ts +++ b/lib/config/dashboard.ts @@ -1,4 +1,4 @@ -import { DashboardConfig } from "@/types"; +import { DashboardConfig } from "@/types" export const dashboardConfig: DashboardConfig = { sidebarNav: [ diff --git a/lib/config/platform.ts b/lib/config/platform.ts index f6fda65..7ac4ea6 100644 --- a/lib/config/platform.ts +++ b/lib/config/platform.ts @@ -1,5 +1,5 @@ -import { EGamePlatform } from "@/types/constants"; -import { IPlatformCategrory } from "@/types/igdb-types"; +import { EGamePlatform } from "@/types/constants" +import { IPlatformCategrory } from "@/types/igdb-types" export const platforms: IPlatformCategrory[] = [ { @@ -50,4 +50,4 @@ export const platforms: IPlatformCategrory[] = [ EGamePlatform.gb ] } -]; \ No newline at end of file +] \ No newline at end of file diff --git a/lib/db.ts b/lib/db.ts index 8a33a38..34aa9c2 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,18 +1,18 @@ import { PrismaClient } from "@prisma/client" declare global { - // eslint-disable-next-line no-var - var cachedPrisma: PrismaClient + // eslint-disable-next-line no-var + var cachedPrisma: PrismaClient } let prisma: PrismaClient if (process.env.NODE_ENV === "production") { - prisma = new PrismaClient() + prisma = new PrismaClient() } else { - if (!global.cachedPrisma) { - global.cachedPrisma = new PrismaClient() - } - prisma = global.cachedPrisma + if (!global.cachedPrisma) { + global.cachedPrisma = new PrismaClient() + } + prisma = global.cachedPrisma } export const db = prisma \ No newline at end of file diff --git a/lib/igdb.ts b/lib/igdb.ts index e2354d6..a901843 100644 --- a/lib/igdb.ts +++ b/lib/igdb.ts @@ -30,10 +30,10 @@ async function getToken(): Promise<IAuth> { // fetches the top 200 games with a rating of 96 or higher export async function getGames(page = 1, search?: string, category?: number, genre?: number, platform?: number[], sortby = "total_rating_count", order = "desc"): Promise<IGame[]> { - const auth = await getToken(); - const url = new URL(`${IGDB_BASE_URL}/games`); + const auth = await getToken() + const url = new URL(`${IGDB_BASE_URL}/games`) - let offset = calculateOffset(page, limit); + let offset = calculateOffset(page, limit) const response = await fetch(url, { method: 'POST', @@ -51,19 +51,19 @@ export async function getGames(page = 1, search?: string, category?: number, gen ${genre ? `& genres = [${genre}]` : ""} ${platform ? `& platforms = (${platform.join(',')})` : ""} ;` - }); + }) if (!response.ok) { - throw new Error(`Error fetching games: ${response.statusText}`); + throw new Error(`Error fetching games: ${response.statusText}`) } - const games: IGame[] = await response.json() as IGame[]; + const games: IGame[] = 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') + }) - return games; + return games } // fetches a single game by id @@ -85,25 +85,25 @@ export async function getGame(id: number): Promise<IGame[]> { }) if (!response.ok) { - throw new Error(`Error fetching game: ${response.statusText}`); + throw new Error(`Error fetching game: ${response.statusText}`) } 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'); - }); - }); + screenshot.url = getImageURL(screenshot.image_id, 'screenshot_big') + }) + }) return games } export async function getFavoriteGames(gamelist: Number[]): Promise<IGame[]> { - const auth = await getToken(); - const url = new URL(`${IGDB_BASE_URL}/games`); + const auth = await getToken() + const url = new URL(`${IGDB_BASE_URL}/games`) const response = await fetch(url, { method: 'POST', @@ -115,17 +115,17 @@ export async function getFavoriteGames(gamelist: Number[]): Promise<IGame[]> { `fields name, cover.image_id; limit ${limit}; where id = (${gamelist.toString()}); ` - }); + }) if (!response.ok) { - throw new Error(`Error fetching games: ${response.statusText}`); + throw new Error(`Error fetching games: ${response.statusText}`) } - const games: IGame[] = await response.json() as IGame[]; + const games: IGame[] = 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') + }) - return games; + return games } \ No newline at end of file diff --git a/lib/react-query/getQueryClient.ts b/lib/react-query/getQueryClient.ts index a16c759..a173928 100644 --- a/lib/react-query/getQueryClient.ts +++ b/lib/react-query/getQueryClient.ts @@ -1,5 +1,5 @@ -import { QueryClient } from "@tanstack/query-core"; -import { cache } from "react"; +import { QueryClient } from "@tanstack/query-core" +import { cache } from "react" -const getQueryClient = cache(() => new QueryClient()); -export default getQueryClient; \ No newline at end of file +const getQueryClient = cache(() => new QueryClient()) +export default getQueryClient \ No newline at end of file diff --git a/lib/react-query/hydrate.client.tsx b/lib/react-query/hydrate.client.tsx index 588c452..35da035 100644 --- a/lib/react-query/hydrate.client.tsx +++ b/lib/react-query/hydrate.client.tsx @@ -1,7 +1,7 @@ -"use client"; +"use client" -import { HydrateProps, Hydrate as RQHydrate } from "@tanstack/react-query"; +import { HydrateProps, Hydrate as RQHydrate } from "@tanstack/react-query" export default function Hydrate(props: HydrateProps) { - return <RQHydrate {...props} />; + return <RQHydrate {...props} /> } \ No newline at end of file diff --git a/lib/react-query/provider.tsx b/lib/react-query/provider.tsx index c60f36a..d7754a0 100644 --- a/lib/react-query/provider.tsx +++ b/lib/react-query/provider.tsx @@ -1,13 +1,13 @@ 'use client' -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { SessionProvider } from 'next-auth/react'; -import React from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { SessionProvider } from 'next-auth/react' +import React from "react" export default function Providers({ children }: React.PropsWithChildren) { const [client] = React.useState( new QueryClient({ defaultOptions: { queries: { staleTime: 5000 } } }) - ); + ) return ( <QueryClientProvider client={client}> @@ -15,5 +15,5 @@ export default function Providers({ children }: React.PropsWithChildren) { {children} </SessionProvider> </QueryClientProvider> - ); + ) } \ No newline at end of file diff --git a/lib/uploadthing.ts b/lib/uploadthing.ts index 24fa466..d116367 100644 --- a/lib/uploadthing.ts +++ b/lib/uploadthing.ts @@ -1,10 +1,10 @@ -import { generateComponents } from "@uploadthing/react"; +import { generateComponents } from "@uploadthing/react" -import { generateReactHelpers } from '@uploadthing/react/hooks'; +import { generateReactHelpers } from '@uploadthing/react/hooks' -import type { OurFileRouter } from "@/app/api/uploadthing/core"; +import type { OurFileRouter } from "@/app/api/uploadthing/core" export const { UploadButton, UploadDropzone, Uploader } = - generateComponents<OurFileRouter>(); + generateComponents<OurFileRouter>() export const { uploadFiles } = generateReactHelpers<OurFileRouter>() \ No newline at end of file diff --git a/lib/utils.ts b/lib/utils.ts index 90a0b61..47c519a 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -5,54 +5,54 @@ import { twMerge } from "tailwind-merge" // tailwindcss classnames generator from shadcn export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)) } // changes the default size of the image to be fetched export function getImageURL(hashId: string, size: string): string { - const IGDB_IMG_BASE_URL = env.IGDB_IMG_BASE_URL ?? '' - return `${IGDB_IMG_BASE_URL}/t_${size}/${hashId}.jpg` + const IGDB_IMG_BASE_URL = env.IGDB_IMG_BASE_URL ?? '' + return `${IGDB_IMG_BASE_URL}/t_${size}/${hashId}.jpg` } // calculates the offset for the query export function calculateOffset(page: number, limit: number): number { - return (page - 1) * limit + return (page - 1) * limit } export function formatDate(data: number) { - const date = new Date(data) - return date.toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric' - }) + const date = new Date(data) + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + }) } // formats the time elapsed since creation export function formatTimeElapsed(createdAt: Date) { - const now = dayjs(); - const timeDiff = Math.abs(now.diff(dayjs(createdAt))); // Difference in milliseconds - const seconds = Math.floor(timeDiff / 1000); // Convert to seconds - const minutes = Math.floor(seconds / 60); // Convert to minutes - const hours = Math.floor(minutes / 60); // Convert to hours - const days = Math.floor(hours / 24); // Convert to days + const now = dayjs() + const timeDiff = Math.abs(now.diff(dayjs(createdAt))) // Difference in milliseconds + const seconds = Math.floor(timeDiff / 1000) // Convert to seconds + const minutes = Math.floor(seconds / 60) // Convert to minutes + const hours = Math.floor(minutes / 60) // Convert to hours + const days = Math.floor(hours / 24) // Convert to days - if (days > 0) { - return dayjs(createdAt).format('L'); // Show the date if days have passed - } else if (hours > 0) { - return hours + 'h'; // Show hours if hours have passed - } else if (minutes > 0) { - return minutes + 'm'; // Show minutes if minutes have passed - } else { - return seconds + 's'; // Show seconds if seconds have passed - } + if (days > 0) { + return dayjs(createdAt).format('MMM D') // Show the date if days have passed + } else if (hours > 0) { + return hours + 'h' // Show hours if hours have passed + } else if (minutes > 0) { + return minutes + 'm' // Show minutes if minutes have passed + } else { + return seconds + 's' // Show seconds if seconds have passed + } } // gets the current url for server or client -const IS_SERVER = typeof window === "undefined"; +const IS_SERVER = typeof window === "undefined" export default function getURL(path: string) { - const baseURL = IS_SERVER - ? env.NEXT_PUBLIC_APP_URL! - : window.location.origin; - return new URL(path, baseURL).toString(); + const baseURL = IS_SERVER + ? env.NEXT_PUBLIC_APP_URL! + : window.location.origin + return new URL(path, baseURL).toString() } \ No newline at end of file diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts index e749cbd..cc3f635 100644 --- a/lib/validations/auth.ts +++ b/lib/validations/auth.ts @@ -1,4 +1,4 @@ -import * as z from "zod"; +import * as z from "zod" export const userAuthSchema = z.object({ usernameOrEmail: z @@ -10,4 +10,4 @@ export const userAuthSchema = z.object({ username: z.string().min(3, "Username must be at least 3 characters").max(15, "Username must be at most 15 characters").optional(), email: z.string().email("Invalid email format").optional(), password: z.string().min(6, "Password must be at least 6 characters").max(18, "Password must be at most 18 characters"), -}); \ No newline at end of file +}) \ No newline at end of file diff --git a/types/igdb-types.d.ts b/types/igdb-types.d.ts index 9f2d9db..369be29 100644 --- a/types/igdb-types.d.ts +++ b/types/igdb-types.d.ts @@ -1,134 +1,134 @@ export interface IAuth { - access_token: string; - expires_in: number; - token_type: 'bearer'; + access_token: string + expires_in: number + token_type: 'bearer' } export interface IGame { - id: number; - age_ratings: number[]; - aggregrated_rating: number; - aggregrated_rating_count: number; - alternative_names: number[]; - artworks: number[]; - bundles: number[]; - category: number; - collection: number; - cover: ICover; - created_at: number; - dlcs: number[]; - expanded_games: number[]; - expansions: number[]; - external_games: number[]; - first_release_date: number; - follows: number; - forks: number[]; - franchise: number; - franchises: number[]; - game_engines: number[]; - game_localizations: number[]; - game_modes: number[]; - genres: IGenre[]; - hypes: number; - involved_companies: IInvolvedCompany[]; - keywords: number[]; - language_supports: number[]; - multiplayer_modes: number[]; - name: string; - parent_game: string; - platforms: IPlatform[]; - player_perspectives: number[]; - ports: number[]; - rating: number; - rating_count: number; - release_dates: number[]; - remakes: number[]; - remasters: number[]; - screenshots: IScreenshots[]; - similar_games: number[]; - slug: string; - standalone_expansions: number[]; - status: number; - storyline: string; - summary: string; - tags: number[]; - themes: number[]; - total_rating: number; - total_rating_count: number; - updated_at: number; - url: string; - version_parent: number; - version_title: string; - videos: number[]; - websites: number[]; + id: number + age_ratings: number[] + aggregrated_rating: number + aggregrated_rating_count: number + alternative_names: number[] + artworks: number[] + bundles: number[] + category: number + collection: number + cover: ICover + created_at: number + dlcs: number[] + expanded_games: number[] + expansions: number[] + external_games: number[] + first_release_date: number + follows: number + forks: number[] + franchise: number + franchises: number[] + game_engines: number[] + game_localizations: number[] + game_modes: number[] + genres: IGenre[] + hypes: number + involved_companies: IInvolvedCompany[] + keywords: number[] + language_supports: number[] + multiplayer_modes: number[] + name: string + parent_game: string + platforms: IPlatform[] + player_perspectives: number[] + ports: number[] + rating: number + rating_count: number + release_dates: number[] + remakes: number[] + remasters: number[] + screenshots: IScreenshots[] + similar_games: number[] + slug: string + standalone_expansions: number[] + status: number + storyline: string + summary: string + tags: number[] + themes: number[] + total_rating: number + total_rating_count: number + updated_at: number + url: string + version_parent: number + version_title: string + videos: number[] + websites: number[] } export interface ICover { - id: number; - alpha_channel: boolean; - animated: boolean; - game: number; - game_localization: number; - height: number; - image_id: string; - url: string; - width: number; + id: number + alpha_channel: boolean + animated: boolean + game: number + game_localization: number + height: number + image_id: string + url: string + width: number } export interface IScreenshots { - id: number; - alpha_channel: boolean; - animated: boolean; - game: number; - height: number; - image_id: string; - url: string; - width: number; + 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; - name: string; - slug: string; - updated_at: number; - url: string; + id: number + created_at: number + name: string + slug: string + updated_at: number + url: string } interface IInvolvedCompany { - id: number; + 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; + 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; - alternative_name: string; - category: number; - created_at: number; - generation: number; - name: string; - platform_logo: number; - platform_family: number; - slug: string; - updated_at: number; - url: string; - versions: number[]; - websites: number[]; + id: number + abbreviation: string + alternative_name: string + category: number + created_at: number + generation: number + name: string + platform_logo: number + platform_family: number + slug: string + updated_at: number + url: string + versions: number[] + websites: number[] } export interface IPlatformCategrory { - category: 'pc' | 'playstation' | 'xbox' | 'nintendo'; - platforms: EGamePlatform[]; + category: 'pc' | 'playstation' | 'xbox' | 'nintendo' + platforms: EGamePlatform[] } \ No newline at end of file diff --git a/types/prisma-item.d.ts b/types/prisma-item.d.ts index c2feefe..532ed3c 100644 --- a/types/prisma-item.d.ts +++ b/types/prisma-item.d.ts @@ -1,11 +1,11 @@ -import { Comment, Like, Post, User } from "@prisma/client"; +import { Comment, Like, Post, User } from "@prisma/client" export interface IPost extends Post { - user: User; - comments: Comment[]; - likes: Like[]; + user: User + comments: Comment[] + likes: Like[] } export interface IComment extends Comment { - user: User; + user: User } \ No newline at end of file -- GitLab