From fcbb8ac8042afd32a02a20cb2042ba7730da3ebb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de>
Date: Tue, 30 May 2023 17:23:09 +0200
Subject: [PATCH] nextauth mid

---
 app/(auth)/layout.tsx              |  7 +++
 app/(auth)/login/page.tsx          | 20 +++++++--
 app/(auth)/signup/page.tsx         | 20 +++++++--
 app/(content)/(home)/home/page.tsx | 12 ++---
 app/api/likes/likeService.ts       |  8 ++--
 app/api/messages/route.ts          |  6 +--
 app/api/signup/route.ts            | 32 ++++++++++++++
 components/auth-login-form.tsx     | 69 +++++++++++++++++++++++++++++
 components/auth-signup-form.tsx    | 71 ++++++++++++++++++++++++++++++
 components/like-button.tsx         |  2 +-
 components/nav.tsx                 |  2 +-
 components/post-messages.tsx       | 10 ++---
 components/ui/alert.tsx            | 59 +++++++++++++++++++++++++
 components/ui/label.tsx            | 26 +++++++++++
 lib/validations/auth.ts            |  5 +++
 package-lock.json                  | 24 ++++++++++
 package.json                       |  1 +
 prisma/schema.prisma               |  2 +-
 18 files changed, 347 insertions(+), 29 deletions(-)
 create mode 100644 app/(auth)/layout.tsx
 create mode 100644 app/api/signup/route.ts
 create mode 100644 components/auth-login-form.tsx
 create mode 100644 components/auth-signup-form.tsx
 create mode 100644 components/ui/alert.tsx
 create mode 100644 components/ui/label.tsx
 create mode 100644 lib/validations/auth.ts

diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx
new file mode 100644
index 0000000..0b6cf1f
--- /dev/null
+++ b/app/(auth)/layout.tsx
@@ -0,0 +1,7 @@
+interface AuthLayoutProps {
+    children: React.ReactNode
+}
+
+export default function AuthLayout({ children }: AuthLayoutProps) {
+    return <div className="min-h-screen">{children}</div>
+}
\ No newline at end of file
diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
index 126f637..f3ceae7 100644
--- a/app/(auth)/login/page.tsx
+++ b/app/(auth)/login/page.tsx
@@ -1,7 +1,19 @@
-export default function Login() {
+import { LoginForm } from '@/components/auth-login-form'
+import Link from 'next/link'
+
+export default function LoginPage() {
     return (
-        <div>
-            <h1>Login Page WIP</h1>
+        <div className="h-screen w-screen flex justify-center items-center bg-slate-100">
+            <div className="sm:shadow-xl px-8 pb-8 pt-12 sm:bg-white rounded-xl space-y-12">
+                <h1 className="font-semibold text-2xl">Login</h1>
+                <LoginForm />
+                <p className="text-center">
+                    Need to create an account?{' '}
+                    <Link className="text-indigo-500 hover:underline" href="/signup">
+                        Create Account
+                    </Link>{' '}
+                </p>
+            </div>
         </div>
-    );
+    )
 }
\ No newline at end of file
diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx
index 8477ded..8f2d6d9 100644
--- a/app/(auth)/signup/page.tsx
+++ b/app/(auth)/signup/page.tsx
@@ -1,7 +1,19 @@
-export default function Signup() {
+import { SignupForm } from '@/components/auth-signup-form'
+import Link from 'next/link'
+
+export default function SignupPage() {
     return (
-        <div>
-            <h1>Signup Page WIP</h1>
+        <div className="h-screen w-screen flex justify-center items-center bg-slate-100">
+            <div className="sm:shadow-xl px-8 pb-8 pt-12 sm:bg-white rounded-xl space-y-12">
+                <h1 className="font-semibold text-2xl">Create your Account</h1>
+                <SignupForm />
+                <p className="text-center">
+                    Have an account?{' '}
+                    <Link className="text-indigo-500 hover:underline" href="/login">
+                        Sign in
+                    </Link>{' '}
+                </p>
+            </div>
         </div>
-    );
+    )
 }
\ No newline at end of file
diff --git a/app/(content)/(home)/home/page.tsx b/app/(content)/(home)/home/page.tsx
index 9a41b73..836569d 100644
--- a/app/(content)/(home)/home/page.tsx
+++ b/app/(content)/(home)/home/page.tsx
@@ -3,7 +3,7 @@ import PostMessageForm from "@/components/post-messages";
 import { prisma } from "@/lib/db";
 import { Prisma } from "@prisma/client";
 
-type messageType = Prisma.MessageUncheckedCreateInput
+type messageType = Prisma.PostUncheckedCreateInput
 type messageItemProps = {
   msg: messageType;
 };
@@ -11,9 +11,9 @@ type messageItemProps = {
 export default async function HomePage() {
   let messages = null
   try {
-    messages = await prisma.message.findMany({
+    messages = await prisma.post.findMany({
       orderBy: {
-        sentAt: "desc"
+        createdAt: "desc"
       }
     })
 
@@ -52,9 +52,9 @@ const MessageItem = ({ msg }: messageItemProps) => {
       <div className="ml-4 flex flex-col">
         <div>
           <div className="flex items-center">
-            <span className="font-bold mr-2">{msg.author}</span>
+            <span className="font-bold mr-2">{msg.userId}</span>
             <span className="text-gray-500 text-sm">
-              {formatDate(new Date(msg.sentAt!))}
+              {formatDate(new Date(msg.createdAt!))}
             </span>
           </div>
           <div className="text-gray-800">{msg.content}</div>
@@ -68,7 +68,7 @@ const MessageItem = ({ msg }: messageItemProps) => {
         </div>
         <LikeButton data={{
           postId: msg.id,
-          author: msg.author
+          userId: msg.userId
         }} />
         <span className="text-gray-600">Like Count: {msg.likeCount} | <span className="text-gray-600">ReplyButton (Number of Replies)</span></span>
       </div>
diff --git a/app/api/likes/likeService.ts b/app/api/likes/likeService.ts
index 80fe5f3..0dd748e 100644
--- a/app/api/likes/likeService.ts
+++ b/app/api/likes/likeService.ts
@@ -16,7 +16,7 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
             where: {
                 id: like.id,
                 postId: like.postId,
-                author: like.author
+                userId: like.userId
             }
         })
 
@@ -31,7 +31,7 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
             }
         })
 
-        const msg = await prisma.message.update({
+        const msg = await prisma.post.update({
             where: {
                 id: like.postId
             },
@@ -46,11 +46,11 @@ export async function putLike(like: likeType): Promise<likeType | undefined> {
         const createdLike = await prisma.like.create({
             data: {
                 postId: like.postId,
-                author: like.author
+                userId: like.userId
             }
         })
 
-        const updatedMessage = await prisma.message.update({
+        const updatedMessage = await prisma.post.update({
             where: {
                 id: like.postId
             },
diff --git a/app/api/messages/route.ts b/app/api/messages/route.ts
index 20a1b72..f2799ef 100644
--- a/app/api/messages/route.ts
+++ b/app/api/messages/route.ts
@@ -7,7 +7,7 @@ export async function POST(req: NextRequest) {
 
 	console.log(data)
 	try {
-		await prisma.message.create({
+		await prisma.post.create({
 			data: data
 		})
 
@@ -27,9 +27,9 @@ export async function GET(req: NextRequest, res: NextResponse) {
 
 	console.log(data)
 	try {
-		const messages = await prisma.message.findMany({
+		const messages = await prisma.post.findMany({
 			orderBy: {
-				sentAt: "desc"
+				createdAt: "desc"
 			}
 		})
 
diff --git a/app/api/signup/route.ts b/app/api/signup/route.ts
new file mode 100644
index 0000000..79937ac
--- /dev/null
+++ b/app/api/signup/route.ts
@@ -0,0 +1,32 @@
+import { prisma } from '@/lib/db'
+import { hash } from 'bcrypt'
+import { NextResponse } from 'next/server'
+
+export async function POST(req: Request) {
+    try {
+        const { email, password } = await req.json()
+        const hashed = await hash(password, 12)
+
+        const user = await prisma.user.create({
+            data: {
+                email,
+                password: hashed
+            }
+        })
+
+        return NextResponse.json({
+            user: {
+                email: user.email
+            }
+        })
+    } catch (err: any) {
+        return new NextResponse(
+            JSON.stringify({
+                error: err.message
+            }),
+            {
+                status: 500
+            }
+        )
+    }
+}
\ No newline at end of file
diff --git a/components/auth-login-form.tsx b/components/auth-login-form.tsx
new file mode 100644
index 0000000..f932027
--- /dev/null
+++ b/components/auth-login-form.tsx
@@ -0,0 +1,69 @@
+'use client'
+
+import { Alert } from '@/components/ui/alert'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { signIn } from 'next-auth/react'
+import { useRouter, useSearchParams } from 'next/navigation'
+import { useState } from 'react'
+
+export const LoginForm = () => {
+    const router = useRouter()
+    const searchParams = useSearchParams()
+    const callbackUrl = searchParams.get('callbackUrl') || '/dashboard'
+    const [email, setEmail] = useState('')
+    const [password, setPassword] = useState('')
+    const [error, setError] = useState('')
+
+    const onSubmit = async (e: React.FormEvent) => {
+        e.preventDefault()
+        try {
+            const res = await signIn('credentials', {
+                redirect: false,
+                email,
+                password,
+                callbackUrl
+            })
+            console.log('Res', res)
+            if (!res?.error) {
+                router.push(callbackUrl)
+            } else {
+                setError('Invalid email or password')
+            }
+        } catch (err: any) { }
+    }
+
+    return (
+        <form onSubmit={onSubmit} className="space-y-12 w-full sm:w-[400px]">
+            <div className="grid w-full items-center gap-1.5">
+                <Label htmlFor="email">Email</Label>
+                <Input
+                    className="w-full"
+                    required
+                    value={email}
+                    onChange={(e) => setEmail(e.target.value)}
+                    id="email"
+                    type="email"
+                />
+            </div>
+            <div className="grid w-full items-center gap-1.5">
+                <Label htmlFor="password">Password</Label>
+                <Input
+                    className="w-full"
+                    required
+                    value={password}
+                    onChange={(e) => setPassword(e.target.value)}
+                    id="password"
+                    type="password"
+                />
+            </div>
+            {error && <Alert>{error}</Alert>}
+            <div className="w-full">
+                <Button className="w-full" size="lg">
+                    Login
+                </Button>
+            </div>
+        </form>
+    )
+}
\ No newline at end of file
diff --git a/components/auth-signup-form.tsx b/components/auth-signup-form.tsx
new file mode 100644
index 0000000..4eb7e63
--- /dev/null
+++ b/components/auth-signup-form.tsx
@@ -0,0 +1,71 @@
+'use client'
+
+import { Alert } from '@/components/ui/alert'
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { signIn } from 'next-auth/react'
+import { useState } from 'react'
+
+export const SignupForm = () => {
+    const [email, setEmail] = useState('')
+    const [password, setPassword] = useState('')
+    const [error, setError] = useState<string | null>(null)
+
+    const onSubmit = async (e: React.FormEvent) => {
+        e.preventDefault()
+
+        try {
+            const res = await fetch('/api/signup', {
+                method: 'POST',
+                body: JSON.stringify({
+                    email,
+                    password
+                }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            if (res.ok) {
+                signIn()
+            } else {
+                setError((await res.json()).error)
+            }
+        } catch (error: any) {
+            setError(error?.message)
+        }
+    }
+
+    return (
+        <form onSubmit={onSubmit} className="space-y-12 w-full sm:w-[400px]">
+            <div className="grid w-full items-center gap-1.5">
+                <Label htmlFor="email">Email</Label>
+                <Input
+                    className="w-full"
+                    required
+                    value={email}
+                    onChange={(e) => setEmail(e.target.value)}
+                    id="email"
+                    type="email"
+                />
+            </div>
+            <div className="grid w-full items-center gap-1.5">
+                <Label htmlFor="password">Password</Label>
+                <Input
+                    className="w-full"
+                    required
+                    value={password}
+                    onChange={(e) => setPassword(e.target.value)}
+                    id="password"
+                    type="password"
+                />
+            </div>
+            {error && <Alert>{error}</Alert>}
+            <div className="w-full">
+                <Button className="w-full" size="lg">
+                    Sign up
+                </Button>
+            </div>
+        </form>
+    )
+}
\ No newline at end of file
diff --git a/components/like-button.tsx b/components/like-button.tsx
index 1cbe755..6a08934 100644
--- a/components/like-button.tsx
+++ b/components/like-button.tsx
@@ -15,7 +15,7 @@ export default function LikeButton(props: { data: likeType }) {
     e.preventDefault()
     const msgLikeData = props.data;
     const likeData = {} as likeType
-    likeData.author = msgLikeData.author
+    likeData.userId = msgLikeData.userId
     likeData.postId = msgLikeData.postId
 
     const response = await fetch('http://localhost:3000/api/likes', {
diff --git a/components/nav.tsx b/components/nav.tsx
index 7be55e1..fbf7334 100644
--- a/components/nav.tsx
+++ b/components/nav.tsx
@@ -58,7 +58,7 @@ export default function DashboardNav({ items }: DashboardNavProps) {
                     <Link href="/login" className={cn(buttonVariants({ size: "lg" }))}>Log In</Link>
                     <Link href="/signup" className={cn(buttonVariants({ size: "lg", variant: "outline" }))}>Sign Up</Link>
                     <p>
-                        Unlock endless possibilities - register or log in to unleash the full potential of our website.
+                        Unlock endless possibilities - sign up or log in to unleash the full potential of our website.
                     </p>
                 </div>
             }
diff --git a/components/post-messages.tsx b/components/post-messages.tsx
index e17f95f..a6d9d14 100644
--- a/components/post-messages.tsx
+++ b/components/post-messages.tsx
@@ -1,12 +1,12 @@
 "use client"
 
-import { Message, Prisma } from "@prisma/client";
+import { Post, Prisma } from "@prisma/client";
 import { useRouter } from "next/navigation";
 import { startTransition, useState } from "react";
 
-type messageType = Prisma.MessageUncheckedCreateInput
+type messageType = Prisma.PostUncheckedCreateInput
 
-export default function PostMessageForm(props: { data: Message[] | null }) {
+export default function PostMessageForm(props: { data: Post[] | null }) {
 
   const [formData, setFormData] = useState<messageType>({ content: "" } as messageType);
   // const [messagesState, setMessages] = useState(props.data)
@@ -16,7 +16,7 @@ export default function PostMessageForm(props: { data: Message[] | null }) {
     e.preventDefault()
     // setMessages([...messagesState, formData])
     console.log(formData)
-    formData.author = "Default Author"
+    formData.userId = "Default Author"
     const response = await fetch('http://localhost:3000/api/messages', {
       method: 'POST',
       body: JSON.stringify(formData)
@@ -34,7 +34,7 @@ export default function PostMessageForm(props: { data: Message[] | null }) {
 
   const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
     const { value } = e.target;
-    setFormData({ content: value });
+    setFormData({ ...formData, content: value });
   };
 
   return (
diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx
new file mode 100644
index 0000000..9ddbc46
--- /dev/null
+++ b/components/ui/alert.tsx
@@ -0,0 +1,59 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+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",
+    },
+  }
+)
+
+const Alert = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
+>(({ className, variant, ...props }, ref) => (
+  <div
+    ref={ref}
+    role="alert"
+    className={cn(alertVariants({ variant }), className)}
+    {...props}
+  />
+))
+Alert.displayName = "Alert"
+
+const AlertTitle = React.forwardRef<
+  HTMLParagraphElement,
+  React.HTMLAttributes<HTMLHeadingElement>
+>(({ className, ...props }, ref) => (
+  <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>
+>(({ className, ...props }, ref) => (
+  <div
+    ref={ref}
+    className={cn("text-sm [&_p]:leading-relaxed", className)}
+    {...props}
+  />
+))
+AlertDescription.displayName = "AlertDescription"
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/components/ui/label.tsx b/components/ui/label.tsx
new file mode 100644
index 0000000..5341821
--- /dev/null
+++ b/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+  "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> &
+    VariantProps<typeof labelVariants>
+>(({ className, ...props }, ref) => (
+  <LabelPrimitive.Root
+    ref={ref}
+    className={cn(labelVariants(), className)}
+    {...props}
+  />
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/lib/validations/auth.ts b/lib/validations/auth.ts
new file mode 100644
index 0000000..e65b5cb
--- /dev/null
+++ b/lib/validations/auth.ts
@@ -0,0 +1,5 @@
+import * as z from "zod"
+
+export const userAuthSchema = z.object({
+    email: z.string().email(),
+})
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bfb1b94..6b5a068 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
       "dependencies": {
         "@prisma/client": "^4.14.1",
         "@radix-ui/react-dropdown-menu": "^2.0.5",
+        "@radix-ui/react-label": "^2.0.2",
         "@radix-ui/react-scroll-area": "^1.0.4",
         "@radix-ui/react-select": "^1.2.2",
         "@radix-ui/react-slot": "^1.0.2",
@@ -719,6 +720,29 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-label": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
+      "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@radix-ui/react-primitive": "1.0.3"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0",
+        "react-dom": "^16.8 || ^17.0 || ^18.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-menu": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.5.tgz",
diff --git a/package.json b/package.json
index 0a8e66a..f269b67 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
   "dependencies": {
     "@prisma/client": "^4.14.1",
     "@radix-ui/react-dropdown-menu": "^2.0.5",
+    "@radix-ui/react-label": "^2.0.2",
     "@radix-ui/react-scroll-area": "^1.0.4",
     "@radix-ui/react-select": "^1.2.2",
     "@radix-ui/react-slot": "^1.0.2",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 6dc5c1e..19feee8 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -12,7 +12,7 @@ datasource db {
 
 model User {
   id            String    @id @default(dbgenerated()) @db.Uuid
-  userName      String    @unique
+  userName      String?   @unique
   name          String?
   email         String?   @unique
   password      String
-- 
GitLab