From 9176a227389e7ca85d9dbf832da6b32700ab2ba8 Mon Sep 17 00:00:00 2001 From: Aaditya Mahanta Date: Mon, 26 Feb 2024 05:31:30 +0530 Subject: [PATCH] missing auth flow --- .../src/app/forgot/forgot-pass-form.tsx | 105 +++++++++ devsoc24-portal-fe/src/app/forgot/page.tsx | 52 +++++ .../src/app/login/login-form.tsx | 2 +- devsoc24-portal-fe/src/app/reset/page.tsx | 52 +++++ .../src/app/reset/reset-form.tsx | 202 ++++++++++++++++++ .../src/app/signup/verify/page.tsx | 55 +++++ .../src/app/signup/verify/verify-form.tsx | 119 +++++++++++ devsoc24-portal-fe/src/lib/utils.ts | 20 +- devsoc24-portal-fe/src/schemas/password.ts | 39 ++++ devsoc24-portal-fe/src/schemas/signup.ts | 12 +- 10 files changed, 653 insertions(+), 5 deletions(-) create mode 100644 devsoc24-portal-fe/src/app/forgot/forgot-pass-form.tsx create mode 100644 devsoc24-portal-fe/src/app/forgot/page.tsx create mode 100644 devsoc24-portal-fe/src/app/reset/page.tsx create mode 100644 devsoc24-portal-fe/src/app/reset/reset-form.tsx create mode 100644 devsoc24-portal-fe/src/app/signup/verify/page.tsx create mode 100644 devsoc24-portal-fe/src/app/signup/verify/verify-form.tsx create mode 100644 devsoc24-portal-fe/src/schemas/password.ts diff --git a/devsoc24-portal-fe/src/app/forgot/forgot-pass-form.tsx b/devsoc24-portal-fe/src/app/forgot/forgot-pass-form.tsx new file mode 100644 index 0000000..5a806aa --- /dev/null +++ b/devsoc24-portal-fe/src/app/forgot/forgot-pass-form.tsx @@ -0,0 +1,105 @@ +import { forgotSchema } from "@/schemas/password"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { type z } from "zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +// import { toast } from "react-toastify"; +import { useRouter } from "next/navigation"; +import { MailIcon } from "lucide-react"; +import Link from "next/link"; + +type LoginFormValues = z.infer; + +export default function ForgotForm() { + const router = useRouter(); + + const form = useForm({ + resolver: zodResolver(forgotSchema), + defaultValues: { + email: "", + }, + mode: "onChange", + }); + + async function onSubmit(data: LoginFormValues) { + console.log(data); + // const toastId = toast.loading("Logging in...", { autoClose: false }); + // const res = await loginUser(data); + + // toast.update(toastId, { + // render: + // res === 200 ? "Login successful!" : res !== 500 ? res : , + // type: res === 200 ? "success" : "error", + // isLoading: false, + // autoClose: 2000, + // }); + + // if (res === 200) { + // setTimeout(() => { + // void router.push("/overview"); + // }, 2000); + // } + } + + return ( +
+ + ( + + {/* Username */} + +
+ + +
+
+ +
+ )} + /> + +

+ Don't have an account?{" "} + + Sign Up + +

+ + + + + ); +} diff --git a/devsoc24-portal-fe/src/app/forgot/page.tsx b/devsoc24-portal-fe/src/app/forgot/page.tsx new file mode 100644 index 0000000..a76b9ae --- /dev/null +++ b/devsoc24-portal-fe/src/app/forgot/page.tsx @@ -0,0 +1,52 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Logo from "@/components/logo"; +import { ModeToggle } from "@/components/theme-toggle"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import Image from "next/image"; +import title from "@/assets/images/title.svg"; +import title2 from "@/assets/images/glitchtitle1.svg"; +import title3 from "@/assets/images/glitchtitle2.svg"; +import title4 from "@/assets/images/glitchtitle3.svg"; +import ForgotForm from "./forgot-pass-form"; + +export default function Page() { + const [currentTitleIndex, setCurrentTitleIndex] = useState(0); + const titles = [title, title2, title3, title4]; + + useEffect(() => { + const intervals = [2000, 400, 600, 400]; + + const interval = setInterval(() => { + setCurrentTitleIndex((prevIndex) => (prevIndex + 1) % titles.length); + }, intervals[currentTitleIndex]); + + return () => { + clearInterval(interval); + }; + }, [currentTitleIndex, titles.length]); + + return ( +
+
+ + +
+
+ + + title + +
+

Welcome back!

+

Login to your account

+
+ + + +
+
+
+ ); +} diff --git a/devsoc24-portal-fe/src/app/login/login-form.tsx b/devsoc24-portal-fe/src/app/login/login-form.tsx index 63d2937..a39aa79 100644 --- a/devsoc24-portal-fe/src/app/login/login-form.tsx +++ b/devsoc24-portal-fe/src/app/login/login-form.tsx @@ -135,7 +135,7 @@ export default function LoginForm() { /> Recover password diff --git a/devsoc24-portal-fe/src/app/reset/page.tsx b/devsoc24-portal-fe/src/app/reset/page.tsx new file mode 100644 index 0000000..f008d80 --- /dev/null +++ b/devsoc24-portal-fe/src/app/reset/page.tsx @@ -0,0 +1,52 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Logo from "@/components/logo"; +import { ModeToggle } from "@/components/theme-toggle"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import Image from "next/image"; +import title from "@/assets/images/title.svg"; +import title2 from "@/assets/images/glitchtitle1.svg"; +import title3 from "@/assets/images/glitchtitle2.svg"; +import title4 from "@/assets/images/glitchtitle3.svg"; +import ResetForm from "./reset-form"; + +export default function Page() { + const [currentTitleIndex, setCurrentTitleIndex] = useState(0); + const titles = [title, title2, title3, title4]; + + useEffect(() => { + const intervals = [2000, 400, 600, 400]; + + const interval = setInterval(() => { + setCurrentTitleIndex((prevIndex) => (prevIndex + 1) % titles.length); + }, intervals[currentTitleIndex]); + + return () => { + clearInterval(interval); + }; + }, [currentTitleIndex, titles.length]); + + return ( +
+
+ + +
+
+ + + title + +
+

Welcome back!

+

Login to your account

+
+ + + +
+
+
+ ); +} diff --git a/devsoc24-portal-fe/src/app/reset/reset-form.tsx b/devsoc24-portal-fe/src/app/reset/reset-form.tsx new file mode 100644 index 0000000..e6129d0 --- /dev/null +++ b/devsoc24-portal-fe/src/app/reset/reset-form.tsx @@ -0,0 +1,202 @@ +import { resetSchema } from "@/schemas/password"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { type z } from "zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +// import { toast } from "react-toastify"; +import { useRouter } from "next/navigation"; +import { EyeIcon, EyeOffIcon, KeyRoundIcon, MailIcon } from "lucide-react"; +import Link from "next/link"; + +type LoginFormValues = z.infer; + +export default function ResetForm() { + const router = useRouter(); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + const [isCPasswordVisible, setIsCPasswordVisible] = useState(false); + + const form = useForm({ + resolver: zodResolver(resetSchema), + defaultValues: { + otp: "", + password: "", + confirmPassword: "", + }, + mode: "onChange", + }); + + async function onSubmit(data: LoginFormValues) { + console.log(data); + // const toastId = toast.loading("Logging in...", { autoClose: false }); + // const res = await loginUser(data); + + // toast.update(toastId, { + // render: + // res === 200 ? "Login successful!" : res !== 500 ? res : , + // type: res === 200 ? "success" : "error", + // isLoading: false, + // autoClose: 2000, + // }); + + // if (res === 200) { + // setTimeout(() => { + // void router.push("/overview"); + // }, 2000); + // } + } + + return ( +
+ + ( + + {/* Username */} + +
+ + +
+
+ +
+ )} + /> + ( + + {/* Password */} + +
+ + +
+ +
+
+
+ + +
+ )} + /> + + ( + + {/* Password */} + +
+ + +
+ +
+
+
+ + +
+ )} + /> + +

+ Already have an account?{" "} + + Login + +

+ + + + + ); +} diff --git a/devsoc24-portal-fe/src/app/signup/verify/page.tsx b/devsoc24-portal-fe/src/app/signup/verify/page.tsx new file mode 100644 index 0000000..29e28d6 --- /dev/null +++ b/devsoc24-portal-fe/src/app/signup/verify/page.tsx @@ -0,0 +1,55 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Logo from "@/components/logo"; +import { ModeToggle } from "@/components/theme-toggle"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import Image from "next/image"; +import title from "@/assets/images/title.svg"; +import title2 from "@/assets/images/glitchtitle1.svg"; +import title3 from "@/assets/images/glitchtitle2.svg"; +import title4 from "@/assets/images/glitchtitle3.svg"; +import VerifyForm from "./verify-form"; +// import SignupForm from "./signup-form"; + +export default function Page() { + const [currentTitleIndex, setCurrentTitleIndex] = useState(0); + const titles = [title, title2, title3, title4]; + + useEffect(() => { + const intervals = [2000, 400, 600, 400]; + + const interval = setInterval(() => { + setCurrentTitleIndex((prevIndex) => (prevIndex + 1) % titles.length); + }, intervals[currentTitleIndex]); + + return () => { + clearInterval(interval); + }; + }, [currentTitleIndex, titles.length]); + + return ( +
+
+ + +
+
+ + + title + +
+

+ DEVSOC'24 Time Baby! +

+

Start hack-a-lacking!

+
+ + + +
+
+
+ ); +} diff --git a/devsoc24-portal-fe/src/app/signup/verify/verify-form.tsx b/devsoc24-portal-fe/src/app/signup/verify/verify-form.tsx new file mode 100644 index 0000000..2f00b7e --- /dev/null +++ b/devsoc24-portal-fe/src/app/signup/verify/verify-form.tsx @@ -0,0 +1,119 @@ +import { verifySchema } from "@/schemas/signup"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { type z } from "zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +// import { toast } from "react-toastify"; +import { useRouter } from "next/navigation"; +import { KeyRoundIcon } from "lucide-react"; +import Link from "next/link"; +import { useEffect, useState } from "react"; +import { getTime } from "@/lib/utils"; + +type LoginFormValues = z.infer; + +export default function VerifyForm() { + const [timer, setTimer] = useState(11); + const router = useRouter(); + + useEffect(() => { + const timer = setInterval(() => { + setTimer((prev) => prev - 1); + }, 1000); + + return () => clearInterval(timer); + }, []); + + const form = useForm({ + resolver: zodResolver(verifySchema), + defaultValues: { + otp: "", + }, + mode: "onChange", + }); + + async function onSubmit(data: LoginFormValues) { + console.log(data); + // const toastId = toast.loading("Logging in...", { autoClose: false }); + // const res = await loginUser(data); + + // toast.update(toastId, { + // render: + // res === 200 ? "Login successful!" : res !== 500 ? res : , + // type: res === 200 ? "success" : "error", + // isLoading: false, + // autoClose: 2000, + // }); + + // if (res === 200) { + // setTimeout(() => { + // void router.push("/overview"); + // }, 2000); + // } + } + + return ( +
+ + ( + + {/* Username */} + +
+ + +
+
+ +
+ )} + /> + +

+ Haven't received OTP?{" "} + + {timer <= 0 ? "Resend" : `Resend in ${getTime(timer)}`} + +

+ + + + + ); +} diff --git a/devsoc24-portal-fe/src/lib/utils.ts b/devsoc24-portal-fe/src/lib/utils.ts index d084cca..8a2e861 100644 --- a/devsoc24-portal-fe/src/lib/utils.ts +++ b/devsoc24-portal-fe/src/lib/utils.ts @@ -1,6 +1,20 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); +} + +export function getTime(time: number) { + if (time < 60) { + if (time < 10) { + return `0:0${time}`; + } else { + return `0:${time}`; + } + } else { + const mins = Math.floor(time / 60); + if (mins === 0) return `${mins}:00`; + else return `${mins}:${time % 60}`; + } } diff --git a/devsoc24-portal-fe/src/schemas/password.ts b/devsoc24-portal-fe/src/schemas/password.ts new file mode 100644 index 0000000..1443c39 --- /dev/null +++ b/devsoc24-portal-fe/src/schemas/password.ts @@ -0,0 +1,39 @@ +"use client"; + +import * as z from "zod"; + +export const forgotSchema = z.object({ + email: z + .string({ + required_error: "Email is required", + invalid_type_error: "Email must be a string", + }) + .email({ + message: "Email must be a valid email address", + }), +}); + +export const resetSchema = z + .object({ + otp: z + .string({ + required_error: "OTP is required", + invalid_type_error: "OTP must be a string", + }) + .min(6, "OTP must be 6 characters long") + .max(6, "OTP must be 6 characters long"), + password: z + .string({ + required_error: "Password is required", + invalid_type_error: "Password must be a string", + }) + .min(6, "Password must be atleat 6 characters long"), + confirmPassword: z.string({ + required_error: "Password is required", + invalid_type_error: "Password must be a string", + }), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords do not match", + path: ["confirmPassword"], + }); diff --git a/devsoc24-portal-fe/src/schemas/signup.ts b/devsoc24-portal-fe/src/schemas/signup.ts index 84bff1e..4be5e64 100644 --- a/devsoc24-portal-fe/src/schemas/signup.ts +++ b/devsoc24-portal-fe/src/schemas/signup.ts @@ -28,6 +28,16 @@ export const signupSchema = z path: ["confirmPassword"], }); +export const verifySchema = z.object({ + otp: z + .string({ + required_error: "OTP is required", + invalid_type_error: "OTP must be a string", + }) + .min(6, "OTP must be 6 characters long") + .max(6, "OTP must be 6 characters long"), +}); + export const details1Schema = z.object({ firstName: z .string({ @@ -61,5 +71,5 @@ export const details1Schema = z.object({ .literal("Male") .or(z.literal("Female")) .or(z.literal("Others")) - .or(z.literal("Prefer Not to Say")) + .or(z.literal("Prefer Not to Say")), });