feat: sign in page
This commit is contained in:
parent
bb8bf01e33
commit
66b99099b3
10 changed files with 379 additions and 100 deletions
129
components/Forms/LoginForm.tsx
Normal file
129
components/Forms/LoginForm.tsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
"use client"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/router"
|
||||
import { useForm } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
import { loginSchema } from "zodSchema/login"
|
||||
|
||||
type FormData = z.infer<typeof loginSchema>
|
||||
|
||||
export default function LoginForm() {
|
||||
const router = useRouter()
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { errors, isSubmitting, isDirty, isValid },
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(loginSchema),
|
||||
})
|
||||
|
||||
async function onSubmit(data: FormData) {
|
||||
console.log(isSubmitting)
|
||||
console.log(data)
|
||||
// Replace this with a server action or fetch an API endpoint to authenticate
|
||||
await new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, 2000) // 2 seconds in milliseconds
|
||||
})
|
||||
router.push("/tweets")
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="selection:bg-rose-500 selection:text-white">
|
||||
<div className="flex min-h-screen items-center justify-center bg-rose-100">
|
||||
<div className="flex-1 p-8">
|
||||
<div className="mx-auto w-80 overflow-hidden rounded-3xl bg-white shadow-xl">
|
||||
{/* Form Header */}
|
||||
<div className="rounded-bl-4xl relative h-44 bg-rose-500">
|
||||
<svg className="absolute bottom-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
|
||||
<path
|
||||
fill="#ffffff"
|
||||
fillOpacity="1"
|
||||
d="M0,64L48,80C96,96,192,128,288,128C384,128,480,96,576,85.3C672,75,768,85,864,122.7C960,160,1056,224,1152,245.3C1248,267,1344,245,1392,234.7L1440,224L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Form Body */}
|
||||
<div className="rounded-tr-4xl bg-white px-10 pb-8 pt-4">
|
||||
<h1 className="text-2xl font-semibold text-gray-900">Welcome back!</h1>
|
||||
<form className="mt-12" action="" method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* Email Input */}
|
||||
<div className="relative">
|
||||
<input
|
||||
{...register("email", { required: true })}
|
||||
id="email"
|
||||
name="email"
|
||||
type="text"
|
||||
className="peer h-10 w-full border-b-2 border-gray-300 text-gray-900 placeholder-transparent focus:border-rose-600 focus:outline-none"
|
||||
placeholder="john@doe.com"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{errors?.email && <p className="text-sm text-red-600">{errors?.email?.message}</p>}
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="absolute -top-3.5 left-0 text-sm text-gray-600 transition-all peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:-top-3.5 peer-focus:text-sm peer-focus:text-gray-600"
|
||||
>
|
||||
Email address
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Password Input */}
|
||||
<div className="relative mt-10">
|
||||
<input
|
||||
{...register("password", { required: true })}
|
||||
id="password"
|
||||
type="password"
|
||||
name="password"
|
||||
className="peer h-10 w-full border-b-2 border-gray-300 text-gray-900 placeholder-transparent focus:border-rose-600 focus:outline-none"
|
||||
placeholder="Password"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{errors?.password && <p className="text-sm text-red-600">{errors?.password?.message}</p>}
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="absolute -top-3.5 left-0 text-sm text-gray-600 transition-all peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-400 peer-focus:-top-3.5 peer-focus:text-sm peer-focus:text-gray-600"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!isDirty || !isValid || isSubmitting}
|
||||
className="mt-20 block w-full cursor-pointer rounded bg-rose-500 px-4 py-2 text-center font-semibold text-white hover:bg-rose-400 focus:outline-none focus:ring focus:ring-rose-500 focus:ring-opacity-80 focus:ring-offset-2 disabled:opacity-70"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div role="status">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="mr-2 inline h-6 w-6 animate-spin fill-rose-600 text-white opacity-100"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
{/* SVG for Spinner Animation */}
|
||||
</svg>
|
||||
</div>
|
||||
) : (
|
||||
"Sign In"
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Forgot Password Link */}
|
||||
<a
|
||||
href="#"
|
||||
className="mt-4 block text-center text-sm font-medium text-rose-600 hover:underline focus:outline-none focus:ring-2 focus:ring-rose-500"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -101,11 +101,7 @@ const Navbar: React.FC<NavbarProps> = ({ logoSrc }) => {
|
|||
</a>
|
||||
<div className="flex space-x-3 rtl:space-x-reverse md:order-3 md:space-x-0">
|
||||
<li className="hidden md:block">
|
||||
<Button
|
||||
href="https://vercel.com/new/git/external?repository-url=https://github.com/Blazity/next-enterprise"
|
||||
intent="navSignIn"
|
||||
size="hero1"
|
||||
>
|
||||
<Button href="/auth/SignIn" intent="navSignIn" size="hero1">
|
||||
Sign In
|
||||
</Button>
|
||||
</li>
|
||||
|
|
@ -143,7 +139,7 @@ const Navbar: React.FC<NavbarProps> = ({ logoSrc }) => {
|
|||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
strokeLinecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export default function OurServices() {
|
|||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
strokeLinecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
className="ml-2 h-4 w-4"
|
||||
|
|
@ -90,7 +90,7 @@ export default function OurServices() {
|
|||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
strokeLinecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
className="ml-2 h-4 w-4"
|
||||
|
|
@ -130,7 +130,7 @@ export default function OurServices() {
|
|||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
strokeLinecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
className="ml-2 h-4 w-4"
|
||||
|
|
@ -171,7 +171,7 @@ export default function OurServices() {
|
|||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
strokeLinecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
className="ml-2 h-4 w-4"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue