Files
microdao-daarion/apps/web/src/app/(auth)/login/page.tsx
Apple e0d534ea87 fix: Remove HTML5 validation from auth forms
- Add noValidate to forms
- Move validation to JS
- Add proper autoComplete attributes
- Better error messages in Ukrainian
2025-11-26 13:50:13 -08:00

189 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { Mail, Lock, Loader2, Sparkles, AlertCircle } from 'lucide-react'
import { useAuth } from '@/context/AuthContext'
import { cn } from '@/lib/utils'
export default function LoginPage() {
const router = useRouter()
const { login, isAuthenticated } = useAuth()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
// Redirect if already authenticated
if (isAuthenticated) {
router.push('/')
return null
}
const validateForm = (): string | null => {
if (!email.trim()) {
return 'Введіть email адресу'
}
// Basic email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email)) {
return 'Введіть коректну email адресу'
}
if (!password) {
return 'Введіть пароль'
}
if (password.length < 8) {
return 'Пароль повинен містити мінімум 8 символів'
}
return null
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
// Client-side validation
const validationError = validateForm()
if (validationError) {
setError(validationError)
return
}
setLoading(true)
try {
await login(email, password)
router.push('/')
} catch (err) {
setError(err instanceof Error ? err.message : 'Помилка входу. Перевірте дані та спробуйте ще раз.')
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen flex items-center justify-center px-4 py-12">
<div className="w-full max-w-md">
{/* Logo */}
<div className="text-center mb-8">
<Link href="/" className="inline-flex items-center gap-2">
<Sparkles className="w-10 h-10 text-cyan-400" />
<span className="text-2xl font-bold bg-gradient-to-r from-cyan-400 to-blue-500 bg-clip-text text-transparent">
DAARION
</span>
</Link>
<h1 className="mt-6 text-2xl font-bold text-white">Вхід в акаунт</h1>
<p className="mt-2 text-slate-400">
Увійдіть, щоб продовжити у DAARION.city
</p>
</div>
{/* Form */}
<div className="glass-panel p-8">
<form onSubmit={handleSubmit} noValidate className="space-y-6">
{/* Error */}
{error && (
<div className="flex items-center gap-2 p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-sm">
<AlertCircle className="w-4 h-4 flex-shrink-0" />
{error}
</div>
)}
{/* Email */}
<div>
<label htmlFor="email" className="block text-sm font-medium text-slate-300 mb-2">
Email
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-500" />
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="your@email.com"
autoComplete="email"
className={cn(
'w-full pl-10 pr-4 py-3 bg-slate-800/50 border border-white/10 rounded-xl',
'text-white placeholder-slate-500',
'focus:outline-none focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20',
'transition-all'
)}
/>
</div>
</div>
{/* Password */}
<div>
<label htmlFor="password" className="block text-sm font-medium text-slate-300 mb-2">
Пароль
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-500" />
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
autoComplete="current-password"
className={cn(
'w-full pl-10 pr-4 py-3 bg-slate-800/50 border border-white/10 rounded-xl',
'text-white placeholder-slate-500',
'focus:outline-none focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20',
'transition-all'
)}
/>
</div>
</div>
{/* Submit */}
<button
type="submit"
disabled={loading}
className={cn(
'w-full py-3 rounded-xl font-medium transition-all',
'bg-gradient-to-r from-cyan-500 to-blue-600 text-white',
'hover:from-cyan-400 hover:to-blue-500',
'shadow-lg shadow-cyan-500/20 hover:shadow-cyan-500/40',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
>
{loading ? (
<Loader2 className="w-5 h-5 mx-auto animate-spin" />
) : (
'Увійти'
)}
</button>
</form>
{/* Divider */}
<div className="my-6 flex items-center gap-4">
<div className="flex-1 h-px bg-white/10" />
<span className="text-sm text-slate-500">або</span>
<div className="flex-1 h-px bg-white/10" />
</div>
{/* Register link */}
<p className="text-center text-slate-400">
Немає акаунту?{' '}
<Link href="/register" className="text-cyan-400 hover:text-cyan-300 font-medium">
Зареєструватися
</Link>
</p>
</div>
{/* Back to home */}
<p className="mt-6 text-center">
<Link href="/" className="text-slate-500 hover:text-slate-400 text-sm">
Повернутися на головну
</Link>
</p>
</div>
</div>
)
}