Spaces:
Running
Running
| import { useState } from "react"; | |
| import { useNavigate } from "react-router"; | |
| import { LogIn, Loader2 } from "lucide-react"; | |
| import { login } from "../../services/api"; | |
| export default function Login() { | |
| const [email, setEmail] = useState(""); | |
| const [password, setPassword] = useState(""); | |
| const [error, setError] = useState(""); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const navigate = useNavigate(); | |
| const handleLogin = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| setError(""); | |
| if (!email || !password) { | |
| setError("Please enter both email and password"); | |
| return; | |
| } | |
| if (!/\S+@\S+\.\S+/.test(email)) { | |
| setError("Please enter a valid email address"); | |
| return; | |
| } | |
| setIsLoading(true); | |
| try { | |
| const res = await login(email, password); | |
| const user = { | |
| user_id: res.data.id, | |
| email: res.data.email, | |
| name: res.data.fullname, | |
| loginTime: new Date().toISOString(), | |
| }; | |
| localStorage.setItem("chatbot_user", JSON.stringify(user)); | |
| navigate("/"); | |
| } catch (err: unknown) { | |
| setError(err instanceof Error ? err.message : "Login failed"); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#BAE6FD] via-[#A7F3D0] to-[#FDE68A]"> | |
| <div className="w-full max-w-md px-4"> | |
| <div className="bg-white rounded-xl shadow-2xl p-6 border border-slate-200"> | |
| <div className="flex items-center justify-center mb-6"> | |
| <div className="bg-gradient-to-br from-[#059669] to-[#047857] p-2.5 rounded-lg"> | |
| <LogIn className="w-6 h-6 text-white" /> | |
| </div> | |
| </div> | |
| <h1 className="text-xl text-center mb-1 text-slate-900"> | |
| Welcome Back | |
| </h1> | |
| <p className="text-center text-slate-500 text-sm mb-6"> | |
| Sign in to continue to your chatbot | |
| </p> | |
| <form onSubmit={handleLogin} className="space-y-4"> | |
| <div> | |
| <label | |
| htmlFor="email" | |
| className="block text-xs mb-1.5 text-slate-700" | |
| > | |
| Email Address | |
| </label> | |
| <input | |
| id="email" | |
| type="email" | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| className="w-full px-3 py-2 text-sm rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-[#4FC3F7] focus:border-transparent transition" | |
| placeholder="you@example.com" | |
| disabled={isLoading} | |
| /> | |
| </div> | |
| <div> | |
| <label | |
| htmlFor="password" | |
| className="block text-xs mb-1.5 text-slate-700" | |
| > | |
| Password | |
| </label> | |
| <input | |
| id="password" | |
| type="password" | |
| value={password} | |
| onChange={(e) => setPassword(e.target.value)} | |
| className="w-full px-3 py-2 text-sm rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-[#4FC3F7] focus:border-transparent transition" | |
| placeholder="Enter your password" | |
| disabled={isLoading} | |
| /> | |
| </div> | |
| {error && ( | |
| <div className="bg-red-50 border border-red-200 text-red-700 px-3 py-2 rounded-lg text-xs"> | |
| {error} | |
| </div> | |
| )} | |
| <button | |
| type="submit" | |
| disabled={isLoading} | |
| className="w-full flex items-center justify-center gap-2 bg-gradient-to-r from-[#059669] to-[#047857] text-white py-2.5 text-sm rounded-lg hover:from-[#047857] hover:to-[#065F46] transition font-medium disabled:opacity-60 disabled:cursor-not-allowed" | |
| > | |
| {isLoading ? ( | |
| <Loader2 className="w-4 h-4 animate-spin" /> | |
| ) : ( | |
| <LogIn className="w-4 h-4" /> | |
| )} | |
| {isLoading ? "Signing in..." : "Sign In"} | |
| </button> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |