feat: implement authentication pages and forms for sign-in and sign-up

This commit is contained in:
ngc2207 2025-01-29 17:20:56 +08:00
parent 67565b1b18
commit 5a0be71f40
6 changed files with 310 additions and 2 deletions

View File

@ -1,11 +1,18 @@
import { Button } from "@/components/ui/button"; "use client";
import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { usePathname } from "next/navigation";
import { Button } from "@/components/ui/button";
interface AuthLayoutProps { interface AuthLayoutProps {
children: React.ReactNode; children: React.ReactNode;
} }
const AuthLayout = ({ children }: AuthLayoutProps) => { const AuthLayout = ({ children }: AuthLayoutProps) => {
const pathname = usePathname();
const isSignIn = pathname === "/sign-in";
return ( return (
<main className="min-h-screen"> <main className="min-h-screen">
<div className="mx-auto max-w-screen-2xl p-4"> <div className="mx-auto max-w-screen-2xl p-4">
@ -18,7 +25,11 @@ const AuthLayout = ({ children }: AuthLayoutProps) => {
style={{ width: "auto", height: "auto" }} style={{ width: "auto", height: "auto" }}
/> />
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="secondary">Sign Up</Button> <Button asChild variant="secondary">
<Link href={isSignIn ? "/sign-up" : "/sign-in"}>
{isSignIn ? "Sign Up" : "Login"}
</Link>
</Button>
</div> </div>
</nav> </nav>
<div className="flex flex-col items-center justify-center pt-4 md:pt-14"> <div className="flex flex-col items-center justify-center pt-4 md:pt-14">

View File

@ -0,0 +1,9 @@
"use client";
import { SignInCard } from "@/features/auth/components/sign-in-card";
const SignInPage = () => {
return <SignInCard />;
};
export default SignInPage;

View File

@ -0,0 +1,9 @@
"use client";
import { SignUpCard } from "@/features/auth/components/sign-up-card";
const SignUpPage = () => {
return <SignUpCard />;
};
export default SignUpPage;

View File

@ -0,0 +1,122 @@
import { z } from "zod";
import Link from "next/link";
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/components/ui/form";
import { FcGoogle } from "react-icons/fc";
import { FaGithub } from "react-icons/fa";
import { useForm } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { zodResolver } from "@hookform/resolvers/zod";
import { Separator } from "@/components/ui/separator";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(1, "Required"),
});
export const SignInCard = () => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: "",
password: "",
},
});
const onSubmit = (values: z.infer<typeof formSchema>) => {
console.log({ values });
};
return (
<Card className="w-full h-full md:w-[487px]">
<CardHeader className="flex items-center justify-center text-center p-7">
<CardTitle className="text-2xl">Welcome back !</CardTitle>
</CardHeader>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
name="email"
control={form.control}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="email"
placeholder="Enter email address"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
name="password"
control={form.control}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="password"
placeholder="Enter password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button disabled={false} size="lg" className="w-full">
Login
</Button>
</form>
</Form>
</CardContent>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7 flex flex-col gap-y-4">
<Button
disabled={false}
variant="secondary"
size="lg"
className="w-full"
>
<FcGoogle className="mr-2 size-5" />
Login with Google
</Button>
<Button
disabled={false}
variant="secondary"
size="lg"
className="w-full"
>
<FaGithub className="mr-2 size-5" />
Login with Github
</Button>
</CardContent>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7 flex items-center justify-center">
<p>
Don&apos;t have an account?{" "}
<Link href="/sign-up">
<span className="underline underline-offset-4 hover:text-blue-700">Sign Up</span>
</Link>
</p>
</CardContent>
</Card>
);
};

View File

@ -0,0 +1,156 @@
import { z } from "zod";
import Link from "next/link";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/components/ui/form";
import { FcGoogle } from "react-icons/fc";
import { FaGithub } from "react-icons/fa";
import { useForm } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { zodResolver } from "@hookform/resolvers/zod";
const formSchema = z.object({
name: z.string().trim().min(1, "Required"),
email: z.string().email(),
password: z.string().min(8, "Minimum of 8 characters required"),
});
export const SignUpCard = () => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
password: "",
},
});
const onSubmit = (values: z.infer<typeof formSchema>) => {
console.log({ values });
};
return (
<Card className="w-full h-full md:w-[487px]">
<CardHeader className="flex items-center justify-center text-center p-7">
<CardTitle className="text-2xl">Sign Up</CardTitle>
<CardDescription>
By signing up, you&apos;re giving a nod to our{" "}
<Link href="/privacy">
<span className="hover:underline underline-offset-4 text-blue-700">Privacy Policy</span>
</Link>{" "}
and{" "}
<Link href="/terms">
<span className="hover:underline underline-offset-4 text-blue-700">Terms of service</span>
</Link>
</CardDescription>
</CardHeader>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
name="name"
control={form.control}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="text"
placeholder="Enter your name"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
name="email"
control={form.control}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="email"
placeholder="Enter email address"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
name="password"
control={form.control}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="password"
placeholder="Enter your password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button disabled={false} size="lg" className="w-full">
Login
</Button>
</form>
</Form>
</CardContent>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7 flex flex-col gap-y-4">
<Button
disabled={false}
variant="secondary"
size="lg"
className="w-full"
>
<FcGoogle className="mr-2 size-5" />
Login with Google
</Button>
<Button
disabled={false}
variant="secondary"
size="lg"
className="w-full"
>
<FaGithub className="mr-2 size-5" />
Login with Github
</Button>
</CardContent>
<div className="px-7">
<Separator />
</div>
<CardContent className="p-7 flex items-center justify-center">
<p>
Already have an account?{" "}
<Link href="/sign-in">
<span className="underline underline-offset-4 hover:text-blue-700">Sign In</span>
</Link>
</p>
</CardContent>
</Card>
);
};

View File

@ -6,6 +6,7 @@ export default {
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}", "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/features/**/*.{js,ts,jsx,tsx,mdx}",
], ],
theme: { theme: {
extend: { extend: {