210 lines
7.0 KiB
TypeScript
210 lines
7.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
|
|
export type AuthMode = 'login' | 'register' | 'forgot-password'
|
|
|
|
export interface AuthFormProps {
|
|
mode: AuthMode
|
|
onSubmit: (data: AuthFormData) => void | Promise<void>
|
|
isLoading?: boolean
|
|
error?: string
|
|
onModeChange?: (mode: AuthMode) => void
|
|
}
|
|
|
|
export interface AuthFormData {
|
|
email: string
|
|
password?: string
|
|
username?: string
|
|
confirmPassword?: string
|
|
}
|
|
|
|
export function AuthForm({
|
|
mode,
|
|
onSubmit,
|
|
isLoading = false,
|
|
error,
|
|
onModeChange
|
|
}: AuthFormProps) {
|
|
const [formData, setFormData] = useState<AuthFormData>({
|
|
email: '',
|
|
password: '',
|
|
username: '',
|
|
confirmPassword: ''
|
|
})
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
onSubmit(formData)
|
|
}
|
|
|
|
const titles = {
|
|
login: 'Welcome back',
|
|
register: 'Create your account',
|
|
'forgot-password': 'Reset your password'
|
|
}
|
|
|
|
const buttonTexts = {
|
|
login: 'Sign in',
|
|
register: 'Create account',
|
|
'forgot-password': 'Send reset link'
|
|
}
|
|
|
|
return (
|
|
<div className="w-full max-w-md mx-auto">
|
|
<div className="bg-zinc-900 rounded-2xl p-8 shadow-2xl border border-zinc-800">
|
|
{/* Logo/Title */}
|
|
<div className="text-center mb-8">
|
|
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-purple-500 flex items-center justify-center">
|
|
<svg className="w-8 h-8 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" />
|
|
</svg>
|
|
</div>
|
|
<h1 className="text-2xl font-bold text-white">{titles[mode]}</h1>
|
|
</div>
|
|
|
|
{/* Error Message */}
|
|
{error && (
|
|
<div className="mb-6 p-4 bg-red-500/10 border border-red-500/50 rounded-lg text-red-400 text-sm">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{/* Form */}
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
{/* Username (Register only) */}
|
|
{mode === 'register' && (
|
|
<div>
|
|
<label htmlFor="username" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Username
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="username"
|
|
value={formData.username}
|
|
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
placeholder="Choose a username"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Email */}
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Email
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
value={formData.email}
|
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
placeholder="your@email.com"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{/* Password (not in forgot-password) */}
|
|
{mode !== 'forgot-password' && (
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Password
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="password"
|
|
value={formData.password}
|
|
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
placeholder="••••••••"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Confirm Password (Register only) */}
|
|
{mode === 'register' && (
|
|
<div>
|
|
<label htmlFor="confirmPassword" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Confirm Password
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="confirmPassword"
|
|
value={formData.confirmPassword}
|
|
onChange={(e) => setFormData({ ...formData, confirmPassword: e.target.value })}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
placeholder="••••••••"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Forgot Password Link (Login only) */}
|
|
{mode === 'login' && onModeChange && (
|
|
<div className="text-right">
|
|
<button
|
|
type="button"
|
|
onClick={() => onModeChange('forgot-password')}
|
|
className="text-sm text-purple-400 hover:text-purple-300"
|
|
>
|
|
Forgot password?
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Submit Button */}
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading}
|
|
className="w-full py-3 bg-purple-500 hover:bg-purple-600 disabled:bg-zinc-700 disabled:text-zinc-500 text-white font-semibold rounded-lg transition"
|
|
>
|
|
{isLoading ? 'Loading...' : buttonTexts[mode]}
|
|
</button>
|
|
</form>
|
|
|
|
{/* Mode Switch Links */}
|
|
{onModeChange && (
|
|
<div className="mt-6 text-center text-sm text-zinc-400">
|
|
{mode === 'login' && (
|
|
<p>
|
|
Don't have an account?{' '}
|
|
<button
|
|
onClick={() => onModeChange('register')}
|
|
className="text-purple-400 hover:text-purple-300 font-medium"
|
|
>
|
|
Sign up
|
|
</button>
|
|
</p>
|
|
)}
|
|
{mode === 'register' && (
|
|
<p>
|
|
Already have an account?{' '}
|
|
<button
|
|
onClick={() => onModeChange('login')}
|
|
className="text-purple-400 hover:text-purple-300 font-medium"
|
|
>
|
|
Sign in
|
|
</button>
|
|
</p>
|
|
)}
|
|
{mode === 'forgot-password' && (
|
|
<p>
|
|
Remember your password?{' '}
|
|
<button
|
|
onClick={() => onModeChange('login')}
|
|
className="text-purple-400 hover:text-purple-300 font-medium"
|
|
>
|
|
Sign in
|
|
</button>
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|