diff options
| author | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
|---|---|---|
| committer | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
| commit | ef55d9bb24430610b60c1b77821307cd72c08c55 (patch) | |
| tree | e6935a810b34370b0a4c821e55f6397aac50c832 /web/src/pages/LoginPage.tsx | |
| download | helpdesk-master.tar.gz helpdesk-master.zip | |
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'web/src/pages/LoginPage.tsx')
| -rw-r--r-- | web/src/pages/LoginPage.tsx | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/web/src/pages/LoginPage.tsx b/web/src/pages/LoginPage.tsx new file mode 100644 index 0000000..73f355b --- /dev/null +++ b/web/src/pages/LoginPage.tsx @@ -0,0 +1,124 @@ +/** + * LoginPage.tsx + * Página de autenticação — ponto de entrada da aplicação. + */ + +import { useState, type FormEvent } from 'react'; +import { useNavigate, Link } from 'react-router-dom'; +import { useMutation } from '@tanstack/react-query'; +import { api } from '@/lib/api'; +import { useAuth } from '@/context/AuthContext'; +import { useToast } from '@/context/ToastContext'; +import styles from './LoginPage.module.css'; + +export default function LoginPage() { + const navigate = useNavigate(); + const { login } = useAuth(); + const { addToast } = useToast(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPass, setShowPass] = useState(false); + + const { mutate, isPending, isError, error } = useMutation({ + mutationFn: () => api.auth.login({ email, password }), + onSuccess: ({ token, user }) => { + login(token, user); + addToast('Login realizado com sucesso!', 'success'); + navigate('/dashboard', { replace: true }); + }, + onError: (err: any) => { + addToast(err.message || 'Erro ao realizar login', 'error'); + } + }); + + function handleSubmit(e: FormEvent) { + e.preventDefault(); + mutate(); + } + + const errorMsg = isError && error instanceof Error ? error.message : null; + + return ( + <div className={styles.page}> + <div className={styles.card}> + + {/* Logo */} + <div className={styles.logo}> + <svg width="40" height="40" viewBox="0 0 32 32" fill="none" aria-hidden="true"> + <rect width="32" height="32" rx="8" fill="var(--color-accent)" fillOpacity="0.2" /> + <path d="M8 11h16M8 16h10M8 21h12" stroke="var(--color-accent)" strokeWidth="2.2" strokeLinecap="round" /> + </svg> + <div> + <h1 className={styles.appName}>Helpdesk TI</h1> + <p className={styles.appSub}>Central de Chamados</p> + </div> + </div> + + <h2 className={styles.heading}>Entrar na sua conta</h2> + + <form className={styles.form} onSubmit={handleSubmit} noValidate> + <div className={styles.field}> + <label htmlFor="login-email" className={styles.label}>E-mail</label> + <input + id="login-email" + type="email" + className="input" + placeholder="seu@email.com" + value={email} + onChange={(e) => setEmail(e.target.value)} + required + autoComplete="email" + disabled={isPending} + /> + </div> + + <div className={styles.field}> + <label htmlFor="login-password" className={styles.label}>Senha</label> + <div className={styles.passwordWrapper}> + <input + id="login-password" + type={showPass ? 'text' : 'password'} + className="input" + placeholder="••••••••" + value={password} + onChange={(e) => setPassword(e.target.value)} + required + autoComplete="current-password" + disabled={isPending} + /> + <button + type="button" + className={styles.showPass} + onClick={() => setShowPass((v) => !v)} + aria-label={showPass ? 'Ocultar senha' : 'Mostrar senha'} + > + {showPass ? '🙈' : '👁️'} + </button> + </div> + </div> + + {errorMsg && ( + <div className={styles.error} role="alert">{errorMsg}</div> + )} + + <button + type="submit" + className={`btn btn--primary ${styles.submitBtn}`} + disabled={isPending || !email || !password} + aria-busy={isPending} + > + {isPending ? ( + <><span className="spinner" style={{ width: 16, height: 16, borderWidth: 2 }} /> Entrando...</> + ) : 'Entrar'} + </button> + </form> + + <p className={styles.registerLink}> + Não tem conta?{' '} + <Link to="/register">Solicite acesso ao administrador</Link> + </p> + </div> + </div> + ); +} |