Add src/app/auth/page.tsx

This commit is contained in:
2026-03-11 20:34:05 +00:00
parent c4d8f371b4
commit ca94a50c72

317
src/app/auth/page.tsx Normal file
View File

@@ -0,0 +1,317 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
import FooterBase from '@/components/sections/footer/FooterBase';
import { Mail, Lock, Eye, EyeOff, LogIn, UserPlus } from 'lucide-react';
export default function AuthPage() {
const router = useRouter();
const [isLogin, setIsLogin] = useState(true);
const [showPassword, setShowPassword] = useState(false);
const [formData, setFormData] = useState({
email: '',
password: '',
confirmPassword: '',
name: ''
});
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
setError('');
};
const validateForm = () => {
if (!formData.email) {
setError('Email é obrigatório');
return false;
}
if (!formData.password) {
setError('Senha é obrigatória');
return false;
}
if (!isLogin) {
if (!formData.name) {
setError('Nome é obrigatório');
return false;
}
if (formData.password !== formData.confirmPassword) {
setError('As senhas não coincidem');
return false;
}
if (formData.password.length < 6) {
setError('Senha deve ter no mínimo 6 caracteres');
return false;
}
}
return true;
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setLoading(true);
try {
const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register';
const payload = isLogin
? { email: formData.email, password: formData.password }
: { name: formData.name, email: formData.email, password: formData.password };
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok) {
setError(data.message || 'Erro ao processar requisição');
setLoading(false);
return;
}
// Store token and user data
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
// Redirect to dashboard
router.push('/dashboard');
} catch (err) {
setError('Erro de conexão. Tente novamente.');
setLoading(false);
}
};
return (
<ThemeProvider
defaultButtonVariant="elastic-effect"
defaultTextAnimation="entrance-slide"
borderRadius="pill"
contentWidth="smallMedium"
sizing="mediumSizeLargeTitles"
background="blurBottom"
cardStyle="gradient-bordered"
primaryButtonStyle="flat"
secondaryButtonStyle="glass"
headingFontWeight="extrabold"
>
<div id="nav" data-section="nav">
<NavbarStyleCentered
navItems={[
{ name: "Dashboard", id: "/dashboard" },
{ name: "Treino", id: "training" },
{ name: "Nutrição", id: "nutrition" },
{ name: "Comunidade", id: "community" },
{ name: "Perfil", id: "profile" }
]}
button={{ text: "Começar Agora", href: "/auth" }}
brandName="FitFlow Pro"
/>
</div>
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-md space-y-8">
{/* Header */}
<div className="text-center">
<h1 className="text-4xl font-extrabold text-foreground mb-2">
{isLogin ? 'Bem-vindo de Volta' : 'Crie sua Conta'}
</h1>
<p className="text-foreground/60">
{isLogin ? 'Acesse sua jornada de fitness' : 'Comece sua transformação hoje'}
</p>
</div>
{/* Form Card */}
<div className="bg-card rounded-2xl p-8 border border-background-accent shadow-lg">
{error && (
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<p className="text-red-700 text-sm font-medium">{error}</p>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Name field (Registration only) */}
{!isLogin && (
<div>
<label htmlFor="name" className="block text-sm font-medium text-foreground mb-2">
Nome Completo
</label>
<input
id="name"
name="name"
type="text"
value={formData.name}
onChange={handleInputChange}
className="w-full px-4 py-3 border border-background-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta bg-background text-foreground placeholder-foreground/40"
placeholder="Seu nome completo"
/>
</div>
)}
{/* Email field */}
<div>
<label htmlFor="email" className="block text-sm font-medium text-foreground mb-2">
Email
</label>
<div className="relative">
<Mail className="absolute left-4 top-3.5 h-5 w-5 text-foreground/40" />
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleInputChange}
className="w-full pl-12 pr-4 py-3 border border-background-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta bg-background text-foreground placeholder-foreground/40"
placeholder="seu.email@exemplo.com"
/>
</div>
</div>
{/* Password field */}
<div>
<label htmlFor="password" className="block text-sm font-medium text-foreground mb-2">
Senha
</label>
<div className="relative">
<Lock className="absolute left-4 top-3.5 h-5 w-5 text-foreground/40" />
<input
id="password"
name="password"
type={showPassword ? 'text' : 'password'}
value={formData.password}
onChange={handleInputChange}
className="w-full pl-12 pr-12 py-3 border border-background-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta bg-background text-foreground placeholder-foreground/40"
placeholder="Sua senha segura"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-4 top-3.5 text-foreground/40 hover:text-foreground"
>
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
</button>
</div>
</div>
{/* Confirm Password field (Registration only) */}
{!isLogin && (
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-foreground mb-2">
Confirmar Senha
</label>
<div className="relative">
<Lock className="absolute left-4 top-3.5 h-5 w-5 text-foreground/40" />
<input
id="confirmPassword"
name="confirmPassword"
type={showPassword ? 'text' : 'password'}
value={formData.confirmPassword}
onChange={handleInputChange}
className="w-full pl-12 pr-12 py-3 border border-background-accent rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta bg-background text-foreground placeholder-foreground/40"
placeholder="Confirme sua senha"
/>
</div>
</div>
)}
{/* Submit Button */}
<button
type="submit"
disabled={loading}
className="w-full py-3 px-4 bg-primary-cta text-background font-semibold rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50 flex items-center justify-center gap-2"
>
{loading ? (
<>
<div className="w-4 h-4 border-2 border-background border-t-transparent rounded-full animate-spin" />
Processando...
</>
) : isLogin ? (
<>
<LogIn className="h-5 w-5" />
Entrar
</>
) : (
<>
<UserPlus className="h-5 w-5" />
Criar Conta
</>
)}
</button>
</form>
{/* Toggle Login/Register */}
<div className="mt-6 text-center">
<p className="text-foreground/60 text-sm">
{isLogin ? 'Não tem conta? ' : 'Já tem conta? '}
<button
onClick={() => {
setIsLogin(!isLogin);
setFormData({ email: '', password: '', confirmPassword: '', name: '' });
setError('');
}}
className="text-primary-cta font-semibold hover:underline"
>
{isLogin ? 'Criar conta' : 'Entrar'}
</button>
</p>
</div>
</div>
{/* Security Note */}
<p className="text-center text-xs text-foreground/40">
Seu email e senha estão protegidos com criptografia de ponta a ponta.
</p>
</div>
</div>
<div id="footer" data-section="footer">
<FooterBase
columns={[
{
title: "Produto", items: [
{ label: "Dashboard", href: "dashboard" },
{ label: "Treino", href: "training" },
{ label: "Nutrição", href: "nutrition" },
{ label: "Cardio Hub", href: "cardio" }
]
},
{
title: "Comunidade", items: [
{ label: "Comunidade", href: "community" },
{ label: "Perfil", href: "profile" },
{ label: "Rankings", href: "rankings" },
{ label: "Blog", href: "blog" }
]
},
{
title: "Empresa", items: [
{ label: "Sobre", href: "about" },
{ label: "Contato", href: "contact" },
{ label: "Privacidade", href: "privacy" },
{ label: "Termos", href: "terms" }
]
}
]}
logoText="FitFlow Pro"
copyrightText="© 2025 FitFlow Pro. Todos os direitos reservados."
/>
</div>
</ThemeProvider>
);
}