Initial commit from remix

This commit is contained in:
gpt-engineer-app[bot]
2025-09-25 16:01:00 +00:00
commit 5ddc52658d
149 changed files with 32798 additions and 0 deletions

280
src/pages/SignUp.tsx Normal file
View 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;