104 lines
4.1 KiB
TypeScript
104 lines
4.1 KiB
TypeScript
import { useState } from "react";
|
|
import { Layout } from "../components/Layout";
|
|
import type { LoginProps } from "../types/Login";
|
|
import { Eye, EyeOff } from "lucide-icons-react";
|
|
import api from "../Api";
|
|
import { useNavigate } from "react-router-dom";
|
|
import toast from "react-hot-toast";
|
|
|
|
export const Login = () => {
|
|
const [form, setForm] = useState<LoginProps>({
|
|
email: "",
|
|
password: ""
|
|
});
|
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const navigate = useNavigate();
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setForm({
|
|
...form,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
};
|
|
|
|
const handleShowPassword = () => {
|
|
setShowPassword(!showPassword);
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError(null);
|
|
setLoading(true);
|
|
|
|
try {
|
|
await api.post("/api/auth/login", {
|
|
username: form.email,
|
|
password: form.password
|
|
});
|
|
navigate("/");
|
|
toast.success("Login realizado com sucesso!");
|
|
} catch (err: any) {
|
|
const message = err?.response?.data?.message || "Falha ao autenticar. Verifique as credenciais.";
|
|
setError(message);
|
|
toast.error(message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Layout className={Styles.layout}>
|
|
<img src="/logo.webp " alt="Logo" className="w-36 h-36 mx-auto mb-4" />
|
|
<div className={Styles.card}>
|
|
<form onSubmit={handleSubmit}>
|
|
<div>
|
|
<label htmlFor="email" className="block mb-2 text-md font-medium text-text">Email:</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
placeholder="john.doe@hittelco.com"
|
|
required
|
|
className={Styles.input}
|
|
onChange={handleChange}/>
|
|
</div>
|
|
<div className="relative">
|
|
<label htmlFor="password" className="block mb-2 text-md font-medium text-text">Password:</label>
|
|
<input
|
|
type={showPassword ? "text" : "password"}
|
|
id="password"
|
|
name="password"
|
|
placeholder="********"
|
|
required
|
|
className={Styles.input}
|
|
onChange={handleChange}
|
|
/>
|
|
<button
|
|
type="button"
|
|
aria-label={showPassword ? "Hide password" : "Show password"}
|
|
className={Styles.iconButton}
|
|
onClick={handleShowPassword}
|
|
>
|
|
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
|
|
</button>
|
|
</div>
|
|
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
|
|
<button type="submit" disabled={loading} className={Styles.button}>
|
|
{loading ? "Autenticando..." : "Login"}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</Layout>
|
|
);
|
|
};
|
|
|
|
const Styles = {
|
|
layout: "h-screen flex flex-col gap-4 justify-center animate-fade-up",
|
|
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
|
|
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
|
|
iconButton: "absolute right-3 top-1/2 text-text p-1 focus:outline-none",
|
|
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
|
|
};
|