Files
vfxreview/app/(auth)/login/page.tsx
T
twotalesanimation 0fbe856dce Initial commit
2026-05-19 22:20:29 +02:00

125 lines
3.6 KiB
TypeScript

"use client";
import { useState } from "react";
import { signIn } from "next-auth/react";
import { useRouter, useSearchParams } from "next/navigation";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Eye, EyeOff, LogIn } from "lucide-react";
const loginSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(1, "Password is required"),
});
type LoginFormValues = z.infer<typeof loginSchema>;
export default function LoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") ?? "/dashboard";
const [showPassword, setShowPassword] = useState(false);
const [authError, setAuthError] = useState<string | null>(null);
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
});
const onSubmit = async (data: LoginFormValues) => {
setAuthError(null);
const result = await signIn("credentials", {
email: data.email,
password: data.password,
redirect: false,
});
if (result?.error) {
setAuthError("Invalid email or password");
return;
}
router.push(callbackUrl);
router.refresh();
};
return (
<Card>
<CardHeader className="pb-4">
<CardTitle className="text-xl align-middle text-center">Sign in</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-1.5">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@studio.com"
autoComplete="email"
autoFocus
{...register("email")}
/>
{errors.email && (
<p className="text-xs text-red-400">{errors.email.message}</p>
)}
</div>
<div className="space-y-1.5">
<Label htmlFor="password">Password</Label>
<div className="relative">
<Input
id="password"
type={showPassword ? "text" : "password"}
placeholder="••••••••"
autoComplete="current-password"
className="pr-10"
{...register("password")}
/>
<button
type="button"
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
onClick={() => setShowPassword((v) => !v)}
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
{errors.password && (
<p className="text-xs text-red-400">{errors.password.message}</p>
)}
</div>
{authError && (
<p className="text-sm text-red-400 text-center">{authError}</p>
)}
<Button type="submit" className="w-full gap-2" disabled={isSubmitting}>
<LogIn className="h-4 w-4" />
{isSubmitting ? "Signing in..." : "Sign in"}
</Button>
</form>
</CardContent>
</Card>
);
}