- MANUAL-PRODUTO.md: Manual do usuário final - MANUAL-VENDAS.md: Estratégia comercial e vendas - MANUAL-TECNICO.md: Infraestrutura e deploy - README.md: Visão geral do projeto
86 lines
3.7 KiB
TypeScript
86 lines
3.7 KiB
TypeScript
'use client';
|
|
import { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Link from 'next/link';
|
|
import { useAuthStore } from '@/stores/authStore';
|
|
|
|
export default function LoginPage() {
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const router = useRouter();
|
|
const { setAuth } = useAuthStore();
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setError('');
|
|
try {
|
|
const API = process.env.NEXT_PUBLIC_API_URL || '';
|
|
const res = await fetch(`${API}/api/auth/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email, password }),
|
|
});
|
|
const data = await res.json();
|
|
if (!res.ok) throw new Error(data.detail || 'Credenciais inválidas');
|
|
setAuth(data.access_token, data.user);
|
|
router.push('/scan');
|
|
} catch (err: any) {
|
|
setError(err.message || 'Credenciais inválidas');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-dark flex items-center justify-center px-4 relative overflow-hidden">
|
|
{/* Orbs */}
|
|
<div className="orb orb-primary w-[400px] h-[400px] -top-[150px] -right-[100px]" />
|
|
<div className="orb orb-accent w-[300px] h-[300px] bottom-[10%] -left-[100px]" />
|
|
|
|
<div className="relative z-10 w-full max-w-md animate-fade-up">
|
|
{/* Logo */}
|
|
<div className="text-center mb-10">
|
|
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary to-accent flex items-center justify-center mx-auto mb-4">
|
|
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2.5" strokeLinecap="round">
|
|
<circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="4" />
|
|
</svg>
|
|
</div>
|
|
<h1 className="text-3xl font-black gradient-text">ALETHEIA</h1>
|
|
<p className="text-gray-500 text-sm mt-1">A verdade sobre o que você come</p>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="glass rounded-3xl p-10 space-y-5">
|
|
<h2 className="text-xl font-bold">Entrar</h2>
|
|
|
|
<div>
|
|
<label className="block text-sm text-gray-400 mb-2">Email</label>
|
|
<input type="email" value={email} onChange={e => setEmail(e.target.value)} required
|
|
className="w-full px-4 py-3.5 rounded-xl bg-dark border border-dark-border text-white placeholder-gray-600 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 transition"
|
|
placeholder="seu@email.com" />
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm text-gray-400 mb-2">Senha</label>
|
|
<input type="password" value={password} onChange={e => setPassword(e.target.value)} required
|
|
className="w-full px-4 py-3.5 rounded-xl bg-dark border border-dark-border text-white placeholder-gray-600 focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/30 transition"
|
|
placeholder="••••••••" />
|
|
</div>
|
|
|
|
{error && <p className="text-danger text-sm">{error}</p>}
|
|
|
|
<button type="submit" disabled={loading} className="btn-glow w-full !text-center disabled:opacity-50">
|
|
{loading ? 'Entrando...' : 'Entrar →'}
|
|
</button>
|
|
|
|
<p className="text-center text-gray-500 text-sm">
|
|
Não tem conta? <Link href="/register" className="text-primary hover:underline">Criar conta grátis</Link>
|
|
</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|