Files
marketplace/client/src/pages/SignUpPage.tsx
delta-lynx-89e8 b37b734c82 Initial marketplace implementation
Full-stack marketplace for buying/selling second-hand items.
React 19 + TypeScript + Tailwind CSS v4 frontend with 17 screens,
Express + Prisma + Socket.io backend, Stripe payments, JWT auth.

Deployed at https://marketplace.173.212.212.157.sslip.io/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:00:44 -08:00

98 lines
5.0 KiB
TypeScript

import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Mail, Lock, Eye, EyeOff, User } from 'lucide-react';
import { Input } from '../components/ui/Input';
import { GradientButton } from '../components/ui/GradientButton';
import { Button } from '../components/ui/Button';
import { useAuth } from '../context/AuthContext';
export function SignUpPage() {
const navigate = useNavigate();
const { signup } = useAuth();
const [fullName, setFullName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (password !== confirmPassword) {
setError('Passwords do not match');
return;
}
setError('');
setIsLoading(true);
try {
await signup({ fullName, email, password });
navigate('/profile/create');
} catch (err) {
setError(err instanceof Error ? err.message : 'Sign up failed');
} finally {
setIsLoading(false);
}
};
return (
<div className="min-h-[80vh] flex items-center justify-center px-4 py-12">
<div className="w-full max-w-md">
<div className="bg-white rounded-3xl shadow-xl p-8">
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-gray-900">Sign Up</h1>
<p className="text-sm text-gray-500 mt-2">Create your account to start buying and selling</p>
</div>
<div className="space-y-3 mb-6">
<Button variant="outline" className="w-full justify-center gap-2" onClick={() => {}}>
<img src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg" alt="Google" className="w-5 h-5" />
Sign up with Google
</Button>
<Button variant="outline" className="w-full justify-center gap-2 !border-blue-200 !text-blue-600 hover:!bg-blue-50" onClick={() => {}}>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
Sign up with Facebook
</Button>
</div>
<div className="relative mb-6">
<div className="absolute inset-0 flex items-center"><div className="w-full border-t border-gray-200" /></div>
<div className="relative flex justify-center text-sm"><span className="px-3 bg-white text-gray-400">or</span></div>
</div>
{error && <div className="mb-4 p-3 rounded-xl bg-red-50 text-red-600 text-sm">{error}</div>}
<form onSubmit={handleSubmit} className="space-y-4">
<Input label="Full Name" placeholder="Enter your full name" value={fullName} onChange={(e) => setFullName(e.target.value)}
icon={<User className="w-4 h-4" />} required />
<Input label="Email" type="email" placeholder="Enter your email" value={email} onChange={(e) => setEmail(e.target.value)}
icon={<Mail className="w-4 h-4" />} required />
<div className="relative">
<Input label="Password" type={showPassword ? 'text' : 'password'} placeholder="Create a password" value={password} onChange={(e) => setPassword(e.target.value)}
icon={<Lock className="w-4 h-4" />} required />
<button type="button" onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-[38px] text-gray-400 hover:text-gray-600 cursor-pointer">
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
<Input label="Confirm Password" type={showPassword ? 'text' : 'password'} placeholder="Confirm your password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)}
icon={<Lock className="w-4 h-4" />} required />
<label className="flex items-start gap-2 text-xs text-gray-500">
<input type="checkbox" required className="mt-0.5 accent-primary-600" />
By signing up, you agree to our Terms of Service and Privacy Policy
</label>
<GradientButton type="submit" className="w-full" size="lg" isLoading={isLoading}>
Sign Up
</GradientButton>
</form>
<p className="text-center text-sm text-gray-500 mt-6">
Already have an account?{' '}
<Link to="/login" className="text-primary-600 font-semibold hover:text-primary-700">Login &gt;</Link>
</p>
</div>
</div>
</div>
);
}