Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { zodResolver } from "@hookform/resolvers/zod"; | |
| import { SubmitHandler, useForm } from "react-hook-form"; | |
| import { z } from "zod"; | |
| import { Input } from "./ui/input"; | |
| import { Button } from "./ui/button"; | |
| import { login } from "@/utils/signIn"; | |
| import { Label } from "./ui/label"; | |
| import { useState } from "react"; | |
| import { Eye, EyeOff, Loader2 } from "lucide-react"; | |
| import Link from "next/link"; | |
| const schema = z.object({ | |
| email: z.string().email(), | |
| password: z.string().min(8), | |
| }); | |
| type FormFields = z.infer<typeof schema>; | |
| const SignInForm = () => { | |
| const [showPassword, setShowPassword] = useState(false); | |
| const { | |
| register, | |
| handleSubmit, | |
| setError, | |
| formState: { errors, isSubmitting }, | |
| } = useForm<FormFields>({ | |
| defaultValues: {}, | |
| resolver: zodResolver(schema), | |
| }); | |
| const onSubmit: SubmitHandler<FormFields> = async (data) => { | |
| try { | |
| const res = await login(data.email, data.password); | |
| if (res && "ok" in res && !res.ok) { | |
| setError("root", { message: res.message || "Gagal login" }); | |
| } | |
| // On success, server action will redirect to "/". | |
| } catch (error) { | |
| setError("root", { message: "Terjadi kesalahan. Coba lagi." }); | |
| } | |
| }; | |
| const togglePasswordVisibility = () => { | |
| setShowPassword(!showPassword); | |
| }; | |
| return ( | |
| <> | |
| <form className="flex flex-col gap-2" onSubmit={handleSubmit(onSubmit)}> | |
| <Label htmlFor="email" className="text-left"> | |
| </Label> | |
| <Input | |
| {...register("email")} | |
| placeholder="Email" | |
| id="email" | |
| type="email" | |
| /> | |
| {errors.email && ( | |
| <div className="text-left text-xs text-red-500"> | |
| {errors.email.message} | |
| </div> | |
| )} | |
| <Label htmlFor="password" className="text-left"> | |
| Password | |
| </Label> | |
| <div className="relative"> | |
| <Input | |
| {...register("password")} | |
| placeholder="Password" | |
| type={showPassword ? "text" : "password"} | |
| id="password" | |
| className="pr-10" | |
| /> | |
| <button | |
| type="button" | |
| onClick={togglePasswordVisibility} | |
| className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 hover:text-gray-700" | |
| tabIndex={-1} | |
| > | |
| {showPassword ? ( | |
| <EyeOff className="h-4 w-4" /> | |
| ) : ( | |
| <Eye className="h-4 w-4" /> | |
| )} | |
| </button> | |
| </div> | |
| {errors.password && ( | |
| <div className="text-left text-xs text-red-500"> | |
| {errors.password.message} | |
| </div> | |
| )} | |
| <Button | |
| disabled={isSubmitting} | |
| type="submit" | |
| className="mt-4 bg-orange-600 hover:bg-orange-800" | |
| > | |
| {isSubmitting ? "Loading..." : "Login"} | |
| </Button> | |
| {errors.root && ( | |
| <div className="text-xs text-red-500">{errors.root.message}</div> | |
| )} | |
| <div className="mt-3 text-center text-xs"> | |
| <Link | |
| href="/forgot-password" | |
| className="text-blue-600 hover:underline" | |
| > | |
| Lupa password? | |
| </Link> | |
| </div> | |
| </form> | |
| </> | |
| ); | |
| }; | |
| export default SignInForm; | |