mirror of
https://gitlab.massbug.com/massbug/judge4c.git
synced 2025-07-04 15:32:09 +00:00
feat: implement authentication pages and forms for sign-in and sign-up
This commit is contained in:
parent
67565b1b18
commit
5a0be71f40
@ -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">
|
||||||
|
9
src/app/(auth)/sign-in/page.tsx
Normal file
9
src/app/(auth)/sign-in/page.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { SignInCard } from "@/features/auth/components/sign-in-card";
|
||||||
|
|
||||||
|
const SignInPage = () => {
|
||||||
|
return <SignInCard />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignInPage;
|
9
src/app/(auth)/sign-up/page.tsx
Normal file
9
src/app/(auth)/sign-up/page.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { SignUpCard } from "@/features/auth/components/sign-up-card";
|
||||||
|
|
||||||
|
const SignUpPage = () => {
|
||||||
|
return <SignUpCard />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignUpPage;
|
122
src/features/auth/components/sign-in-card.tsx
Normal file
122
src/features/auth/components/sign-in-card.tsx
Normal 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'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>
|
||||||
|
);
|
||||||
|
};
|
156
src/features/auth/components/sign-up-card.tsx
Normal file
156
src/features/auth/components/sign-up-card.tsx
Normal 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'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>
|
||||||
|
);
|
||||||
|
};
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user