Spaces:
Sleeping
Sleeping
| "use client"; | |
| import React, { useState, useEffect } from "react"; | |
| import { useRouter } from "next/navigation"; | |
| import { useForm } from "react-hook-form"; | |
| import { zodResolver } from "@hookform/resolvers/zod"; | |
| import { z } from "zod"; | |
| import { Input } from "@/components/ui/Input"; | |
| import { Button } from "@/components/ui/Button"; | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; | |
| import { useAuthStore } from "@/store/authStore"; | |
| import { login } from "@/lib/api/endpoints"; | |
| import { Lock, User, Rocket, AlertCircle } from "lucide-react"; | |
| import { toast } from "react-hot-toast"; | |
| const loginSchema = z.object({ | |
| username: z.string().min(1, "Username is required"), | |
| password: z.string().min(1, "Password is required"), | |
| }); | |
| type LoginFormData = z.infer<typeof loginSchema>; | |
| export default function LoginPage() { | |
| const router = useRouter(); | |
| const { isAuthenticated, login: setAuth } = useAuthStore(); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const { | |
| register, | |
| handleSubmit, | |
| formState: { errors }, | |
| } = useForm<LoginFormData>({ | |
| resolver: zodResolver(loginSchema), | |
| }); | |
| // Redirect if already authenticated | |
| useEffect(() => { | |
| if (isAuthenticated) { | |
| router.push("/"); | |
| } | |
| }, [isAuthenticated, router]); | |
| const onSubmit = async (data: LoginFormData) => { | |
| setIsLoading(true); | |
| try { | |
| const response = await login(data.username, data.password); | |
| setAuth(response.token, response.username); | |
| toast.success("Login successful!"); | |
| router.push("/"); | |
| } catch (error: any) { | |
| const errorMessage = | |
| error.response?.data?.detail || error.message || "Login failed. Please check your credentials."; | |
| toast.error(errorMessage); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 via-cyan-50 to-pink-50 py-12 px-4 sm:px-6 lg:px-8"> | |
| <div className="max-w-md w-full space-y-8"> | |
| {/* Logo and Title */} | |
| <div className="text-center animate-fade-in"> | |
| <div className="flex justify-center mb-4"> | |
| <div className="relative"> | |
| <Rocket className="h-16 w-16 text-blue-500 animate-bounce" /> | |
| <div className="absolute inset-0 bg-blue-500/20 rounded-full blur-xl"></div> | |
| </div> | |
| </div> | |
| <h1 className="text-4xl font-extrabold mb-2"> | |
| <span className="gradient-text">PsyAdGenesis</span> | |
| </h1> | |
| <p className="text-gray-600 text-lg">Sign in to your account</p> | |
| </div> | |
| {/* Login Card */} | |
| <Card variant="glass" className="animate-scale-in" style={{ animationDelay: "0.2s" }}> | |
| <CardHeader> | |
| <CardTitle className="text-2xl text-center">Login</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> | |
| <div> | |
| <Input | |
| label="Username" | |
| type="text" | |
| placeholder="Enter your username" | |
| error={errors.username?.message} | |
| {...register("username")} | |
| /> | |
| </div> | |
| <div> | |
| <Input | |
| label="Password" | |
| type="password" | |
| placeholder="Enter your password" | |
| error={errors.password?.message} | |
| {...register("password")} | |
| /> | |
| </div> | |
| <Button | |
| type="submit" | |
| variant="primary" | |
| size="lg" | |
| className="w-full" | |
| isLoading={isLoading} | |
| > | |
| Sign In | |
| </Button> | |
| </form> | |
| <div className="mt-6 p-4 bg-blue-50/50 rounded-xl border border-blue-200/50"> | |
| <div className="flex items-start space-x-3"> | |
| <AlertCircle className="h-5 w-5 text-blue-600 mt-0.5 flex-shrink-0" /> | |
| <div className="text-sm text-blue-800"> | |
| <p className="font-semibold mb-1">Note:</p> | |
| <p>Credentials are managed manually. Please contact your administrator for access.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| </div> | |
| ); | |
| } | |