Initial commit from remix
This commit is contained in:
280
src/pages/SignUp.tsx
Normal file
280
src/pages/SignUp.tsx
Normal file
@@ -0,0 +1,280 @@
|
||||
import { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
import { Apple, Eye, EyeOff } from 'lucide-react';
|
||||
import { FaGoogle } from 'react-icons/fa';
|
||||
|
||||
const SignUp = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
fullName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
userType: 'tourist' as 'tourist' | 'business'
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [agreeToTerms, setAgreeToTerms] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const { register } = useAuth();
|
||||
const { t } = useLanguage();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
setError('Las contraseñas no coinciden');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!agreeToTerms) {
|
||||
setError('Debes aceptar los términos de servicio');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await register({
|
||||
name: formData.fullName,
|
||||
email: formData.email,
|
||||
password: formData.password,
|
||||
type: formData.userType,
|
||||
location: { lat: 18.4861, lng: -69.9312 }, // Default to Santo Domingo
|
||||
preferences: { language: 'es' }
|
||||
});
|
||||
navigate('/dashboard');
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSocialLogin = (provider: string) => {
|
||||
console.log(`Register with ${provider}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex">
|
||||
{/* Left Side - Form */}
|
||||
<div className="flex-1 flex items-center justify-center p-8 bg-white">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
{/* Header */}
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
{t('welcomeSignUp')} <span className="text-primary italic">{t('signUp')}</span> {t('toContinue')}
|
||||
</h1>
|
||||
<p className="text-gray-600 text-sm leading-relaxed">
|
||||
{t('unlockContent')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Social Login Buttons */}
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleSocialLogin('apple')}
|
||||
className="w-full h-12 bg-gray-900 text-white hover:bg-gray-800 border-gray-900"
|
||||
>
|
||||
<Apple className="w-5 h-5 mr-3" />
|
||||
{t('signUpWithApple')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => handleSocialLogin('google')}
|
||||
className="w-full h-12 bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
>
|
||||
<FaGoogle className="w-5 h-5 mr-3" />
|
||||
{t('signUpWithGoogle')}
|
||||
</Button>
|
||||
|
||||
<p className="text-xs text-gray-500 text-center">
|
||||
{t('privacyNotice')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="px-4 bg-white text-gray-500">Or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Registration Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{error && (
|
||||
<div className="bg-red-50 text-red-500 p-3 rounded-lg text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('fullName')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
name="fullName"
|
||||
value={formData.fullName}
|
||||
onChange={handleChange}
|
||||
placeholder="Ingresa tu nombre completo"
|
||||
className="h-12"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Tipo de Usuario <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
name="userType"
|
||||
value={formData.userType}
|
||||
onChange={handleChange}
|
||||
className="w-full h-12 px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary"
|
||||
required
|
||||
>
|
||||
<option value="tourist">Turista</option>
|
||||
<option value="business">Comercio</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('enterEmail')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
placeholder={t('enterValidEmail')}
|
||||
className="h-12"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('password')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
name="password"
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
placeholder={t('enterPassword')}
|
||||
className="h-12 pr-12"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
{t('confirmPassword')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
name="confirmPassword"
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleChange}
|
||||
placeholder="Confirma tu contraseña"
|
||||
className="h-12 pr-12"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
{showConfirmPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="terms"
|
||||
checked={agreeToTerms}
|
||||
onChange={(e) => setAgreeToTerms(e.target.checked)}
|
||||
className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary mt-1"
|
||||
/>
|
||||
<label htmlFor="terms" className="ml-2 text-sm text-gray-700">
|
||||
By signing up, you agree to the{' '}
|
||||
<Link to="/terms" className="text-primary hover:underline">
|
||||
terms of service
|
||||
</Link>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full h-12 bg-primary hover:bg-primary-dark text-white font-semibold"
|
||||
>
|
||||
{isLoading ? t('loading') : t('signUp')}
|
||||
</Button>
|
||||
|
||||
<div className="text-center">
|
||||
<p className="text-sm text-gray-600">
|
||||
Already have an account?{' '}
|
||||
<Link to="/sign-in" className="text-primary hover:underline font-medium">
|
||||
{t('signIn')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Image & Content */}
|
||||
<div className="hidden lg:flex flex-1 bg-gray-100 items-center justify-center p-8">
|
||||
<div className="text-center max-w-md">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-4 leading-tight">
|
||||
Effortlessly organize your workspace with ease.
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-8">
|
||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
||||
</p>
|
||||
<div className="bg-gray-300 rounded-lg h-64 flex items-center justify-center">
|
||||
<span className="text-6xl font-bold text-gray-500">698x609</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
||||
Reference in New Issue
Block a user