Add src/app/signup/page.tsx

This commit is contained in:
2026-03-11 19:32:10 +00:00
parent aad94e4324
commit f7e85fec99

404
src/app/signup/page.tsx Normal file
View File

@@ -0,0 +1,404 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
import { useState } from 'react';
import { Mail, Lock, User, Eye, EyeOff, ArrowRight, CheckCircle } from 'lucide-react';
export default function SignupPage() {
const [formData, setFormData] = useState({
fullName: '',
email: '',
password: '',
confirmPassword: ''
});
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [passwordStrength, setPasswordStrength] = useState(0);
const [agreedToTerms, setAgreedToTerms] = useState(false);
const calculatePasswordStrength = (pwd: string) => {
let strength = 0;
if (pwd.length >= 8) strength++;
if (pwd.match(/[a-z]/) && pwd.match(/[A-Z]/)) strength++;
if (pwd.match(/[0-9]/)) strength++;
if (pwd.match(/[^a-zA-Z0-9]/)) strength++;
setPasswordStrength(strength);
};
const validateForm = () => {
const newErrors: Record<string, string> = {};
if (!formData.fullName.trim()) {
newErrors.fullName = 'Full name is required';
}
if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = 'Please enter a valid email';
}
if (!formData.password) {
newErrors.password = 'Password is required';
} else if (formData.password.length < 8) {
newErrors.password = 'Password must be at least 8 characters';
} else if (passwordStrength < 2) {
newErrors.password = 'Password is too weak';
}
if (!formData.confirmPassword) {
newErrors.confirmPassword = 'Please confirm your password';
} else if (formData.password !== formData.confirmPassword) {
newErrors.confirmPassword = 'Passwords do not match';
}
if (!agreedToTerms) {
newErrors.terms = 'You must agree to the terms';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
if (name === 'password') {
calculatePasswordStrength(value);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
// Simulated API call
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Signup attempt:', { ...formData, confirmPassword: undefined });
// In a real app, you would create account and redirect
} finally {
setIsSubmitting(false);
}
};
const getPasswordStrengthColor = () => {
if (passwordStrength <= 1) return '#ef4444';
if (passwordStrength <= 2) return '#eab308';
if (passwordStrength <= 3) return '#f97316';
return '#22c55e';
};
const getPasswordStrengthText = () => {
if (!formData.password) return '';
if (passwordStrength <= 1) return 'Weak';
if (passwordStrength <= 2) return 'Fair';
if (passwordStrength <= 3) return 'Good';
return 'Strong';
};
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: "/" },
{ name: "Treino", id: "#training" },
{ name: "Nutrição", id: "#nutrition" },
{ name: "Comunidade", id: "#community" },
{ name: "Perfil", id: "#profile" }
]}
button={{ text: "Entrar", href: "/login" }}
brandName="FitFlow Pro"
/>
</div>
<div id="signup" data-section="signup" className="min-h-screen flex items-center justify-center px-4 py-20">
<div className="w-full max-w-md">
<div className="rounded-2xl border border-opacity-20 p-8 backdrop-blur-sm" style={{
backgroundColor: 'var(--color-card)',
borderColor: 'var(--color-foreground)'
}}>
{/* Header */}
<div className="mb-8 text-center">
<h1 className="text-3xl font-bold mb-2" style={{ color: 'var(--color-foreground)' }}>
Create Account
</h1>
<p className="text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Join 150k+ athletes transforming their bodies
</p>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-5">
{/* Full Name Field */}
<div>
<label htmlFor="fullName" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Full Name
</label>
<div className="relative">
<User className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="fullName"
type="text"
name="fullName"
value={formData.fullName}
onChange={handleChange}
placeholder="Your name"
className="w-full pl-10 pr-4 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.fullName ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
</div>
{errors.fullName && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.fullName}
</p>
)}
</div>
{/* Email Field */}
<div>
<label htmlFor="email" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Email Address
</label>
<div className="relative">
<Mail className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="email"
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="you@example.com"
className="w-full pl-10 pr-4 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.email ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
</div>
{errors.email && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.email}
</p>
)}
</div>
{/* Password Field */}
<div>
<label htmlFor="password" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Password
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="password"
type={showPassword ? 'text' : 'password'}
name="password"
value={formData.password}
onChange={handleChange}
placeholder="••••••••"
className="w-full pl-10 pr-10 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.password ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-3.5 transition-opacity hover:opacity-75"
>
{showPassword ? (
<EyeOff className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
) : (
<Eye className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
)}
</button>
</div>
{/* Password Strength Indicator */}
{formData.password && (
<div className="mt-2 flex items-center gap-2">
<div className="flex-1 h-1.5 rounded-full bg-gray-300" style={{ background: 'var(--color-background)' }}>
<div
className="h-full rounded-full transition-all"
style={{
width: `${(passwordStrength / 4) * 100}%`,
backgroundColor: getPasswordStrengthColor(),
}}
/>
</div>
<span className="text-xs font-medium" style={{ color: getPasswordStrengthColor() }}>
{getPasswordStrengthText()}
</span>
</div>
)}
{errors.password && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.password}
</p>
)}
</div>
{/* Confirm Password Field */}
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium mb-2" style={{ color: 'var(--color-foreground)' }}>
Confirm Password
</label>
<div className="relative">
<Lock className="absolute left-3 top-3.5 w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
<input
id="confirmPassword"
type={showConfirmPassword ? 'text' : 'password'}
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
placeholder="••••••••"
className="w-full pl-10 pr-10 py-2.5 rounded-lg border transition-all"
style={{
backgroundColor: 'var(--color-background)',
borderColor: errors.confirmPassword ? '#ef4444' : 'var(--color-primary-cta)',
color: 'var(--color-foreground)',
}}
/>
<button
type="button"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className="absolute right-3 top-3.5 transition-opacity hover:opacity-75"
>
{showConfirmPassword ? (
<EyeOff className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
) : (
<Eye className="w-5 h-5" style={{ color: 'var(--color-foreground)', opacity: 0.5 }} />
)}
</button>
</div>
{errors.confirmPassword && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.confirmPassword}
</p>
)}
</div>
{/* Terms Agreement */}
<div className="pt-2">
<label className="flex items-start gap-3 cursor-pointer">
<div className="mt-0.5">
{agreedToTerms ? (
<CheckCircle className="w-5 h-5" style={{ color: 'var(--color-primary-cta)' }} />
) : (
<div className="w-5 h-5 rounded border" style={{ borderColor: 'var(--color-foreground)', opacity: 0.3 }} />
)}
</div>
<span className="text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
I agree to the{' '}
<a href="#terms" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Terms of Service
</a>
{' '}and{' '}
<a href="#privacy" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Privacy Policy
</a>
</span>
<input
type="checkbox"
checked={agreedToTerms}
onChange={(e) => setAgreedToTerms(e.target.checked)}
className="hidden"
/>
</label>
{errors.terms && (
<p className="mt-1 text-sm font-medium" style={{ color: '#ef4444' }}>
{errors.terms}
</p>
)}
</div>
{/* Submit Button */}
<button
type="submit"
disabled={isSubmitting}
className="w-full py-2.5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 disabled:opacity-50 mt-6"
style={{
backgroundColor: 'var(--color-primary-cta)',
color: 'white',
}}
>
{isSubmitting ? 'Creating Account...' : 'Create Account'}
{!isSubmitting && <ArrowRight className="w-4 h-4" />}
</button>
</form>
{/* Divider */}
<div className="my-6 flex items-center gap-4">
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
<span className="text-xs" style={{ color: 'var(--color-foreground)', opacity: 0.5 }}>OR</span>
<div className="flex-1 h-px" style={{ backgroundColor: 'var(--color-foreground)', opacity: 0.1 }} />
</div>
{/* Social Signup */}
<div className="grid grid-cols-2 gap-3">
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Google
</button>
<button
type="button"
className="py-2.5 rounded-lg border font-medium transition-all hover:opacity-80"
style={{
backgroundColor: 'var(--color-background)',
borderColor: 'var(--color-foreground)',
color: 'var(--color-foreground)',
}}
>
Apple
</button>
</div>
{/* Login Link */}
<p className="mt-6 text-center text-sm" style={{ color: 'var(--color-foreground)', opacity: 0.7 }}>
Already have an account?{' '}
<a href="/login" className="font-semibold hover:underline" style={{ color: 'var(--color-primary-cta)' }}>
Sign in
</a>
</p>
</div>
</div>
</div>
</ThemeProvider>
);
}