diff --git a/actions/auth.ts b/actions/auth.ts new file mode 100644 index 0000000000..6523d2f8b4 --- /dev/null +++ b/actions/auth.ts @@ -0,0 +1,27 @@ +"use server"; + +import { AuthError } from "next-auth"; + +import { signIn } from "@/auth.config"; + +// ... + +export async function authenticate( + prevState: string | undefined, + formData: FormData, +) { + try { + console.log(formData); + await signIn("credentials", formData); + } catch (error) { + if (error instanceof AuthError) { + switch (error.type) { + case "CredentialsSignin": + return "Invalid credentials."; + default: + return "Something went wrong."; + } + } + throw error; + } +} diff --git a/actions/index.ts b/actions/index.ts index 5532383f5f..0085735bb3 100644 --- a/actions/index.ts +++ b/actions/index.ts @@ -1 +1,2 @@ +export * from "./auth"; export * from "./providers"; diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx index 5ff842ef07..f1f003744e 100644 --- a/app/(auth)/layout.tsx +++ b/app/(auth)/layout.tsx @@ -1,6 +1,30 @@ -export const metadata = { - title: "Next.js", - description: "Generated by Next.js", +import "@/styles/globals.css"; + +import { Metadata, Viewport } from "next"; + +import { Toaster } from "@/components/ui"; +import { fontSans } from "@/config/fonts"; +import { siteConfig } from "@/config/site"; +import { cn } from "@/lib"; + +import { Providers } from "../providers"; + +export const metadata: Metadata = { + title: { + default: siteConfig.name, + template: `%s - ${siteConfig.name}`, + }, + description: siteConfig.description, + icons: { + icon: "/favicon.ico", + }, +}; + +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "white" }, + { media: "(prefers-color-scheme: dark)", color: "black" }, + ], }; export default function RootLayout({ @@ -9,8 +33,20 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - - {children} + + + + + {children} + + + ); } diff --git a/app/(auth)/sign-in/page.tsx b/app/(auth)/sign-in/page.tsx index e12af16f4c..f012c3f358 100644 --- a/app/(auth)/sign-in/page.tsx +++ b/app/(auth)/sign-in/page.tsx @@ -1,7 +1,9 @@ import React from "react"; +import { AuthForm } from "@/components/auth"; + const SignIn = () => { - return
SignIn
; + return ; }; export default SignIn; diff --git a/app/(auth)/sign-up/page.tsx b/app/(auth)/sign-up/page.tsx index b466e40354..dfe04cadeb 100644 --- a/app/(auth)/sign-up/page.tsx +++ b/app/(auth)/sign-up/page.tsx @@ -1,7 +1,9 @@ import React from "react"; +import { AuthForm } from "@/components/auth"; + const SignUp = () => { - return
SignUp
; + return ; }; export default SignUp; diff --git a/auth.config.ts b/auth.config.ts index 7baf25dd7d..58e112ef3c 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -1,9 +1,28 @@ -import type { NextAuthConfig } from "next-auth"; +import NextAuth, { type NextAuthConfig } from "next-auth"; +import Credentials from "next-auth/providers/credentials"; +import { z } from "zod"; export const authConfig = { pages: { signIn: "/sign-in", - // signUp: "/sign-up", + newUser: "/sign-up", }, - providers: [], + providers: [ + Credentials({ + async authorize(credentials) { + const parsedCredentials = z + .object({ email: z.string().email(), password: z.string().min(6) }) + .safeParse(credentials); + + if (!parsedCredentials.success) return null; + + const { email, password } = parsedCredentials.data; + console.log("AuthConfig.ts"); + console.log({ email, password }); + return null; + }, + }), + ], } satisfies NextAuthConfig; + +export const { signIn, signOut, auth } = NextAuth(authConfig); diff --git a/components/auth/AuthForm.tsx b/components/auth/AuthForm.tsx new file mode 100644 index 0000000000..d1d7403dd1 --- /dev/null +++ b/components/auth/AuthForm.tsx @@ -0,0 +1,141 @@ +"use client"; + +import { Icon } from "@iconify/react"; +import { Button, Checkbox, Divider, Input, Link } from "@nextui-org/react"; +import { useState } from "react"; +import { useFormState } from "react-dom"; + +import { authenticate } from "@/actions"; + +import { ProwlerExtended } from "../icons"; +import { ThemeSwitch } from "../ThemeSwitch"; + +export const AuthForm = ({ type }: { type: string }) => { + const [isVisible, setIsVisible] = useState(false); + + const [state, dispath] = useFormState(authenticate, undefined); + console.log(state); + + const toggleVisibility = () => setIsVisible(!isVisible); + + return ( +
+ {/* Brand Logo and ThemeSwitch */} +
+
+ + +
+
+ + {/* Testimonial */} +
+

+ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eget + augue nec massa volutpat aliquet. + +

+
+ + {/* Login Form */} +
+

+ {type === "sign-in" ? "Sign In" : "Sign Up"} +

+
+ + + {isVisible ? ( + + ) : ( + + )} + + } + label="Password" + name="password" + placeholder="Enter your password" + type={isVisible ? "text" : "password"} + variant="bordered" + /> +
+ + Remember me + + + Forgot password? + +
+ + + {type === "sign-in" && ( + <> +
+ +

OR

+ +
+
+ + +
+ + )} + {type === "sign-in" ? ( +

+ Need to create an account?  + Sign Up +

+ ) : ( +

+ Already have an account?  + Log In +

+ )} +
+
+ ); +}; diff --git a/components/auth/index.ts b/components/auth/index.ts new file mode 100644 index 0000000000..1279953728 --- /dev/null +++ b/components/auth/index.ts @@ -0,0 +1 @@ +export * from "./AuthForm"; diff --git a/package-lock.json b/package-lock.json index 0ce80ed402..6352893d57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,8 @@ "server-only": "^0.0.1", "shadcn-ui": "^0.2.3", "tailwind-merge": "^2.5.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@iconify/react": "^5.0.1", diff --git a/package.json b/package.json index 8f2d01c7c9..ab09acc019 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "server-only": "^0.0.1", "shadcn-ui": "^0.2.3", "tailwind-merge": "^2.5.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@iconify/react": "^5.0.1",