299 lines
11 KiB
TypeScript
299 lines
11 KiB
TypeScript
import { useState } from 'react';
|
||
import { useNavigate, useLocation } from 'react-router-dom';
|
||
import { useAuth } from '@/contexts/AuthContext';
|
||
import { Button } from '@/components/ui/button';
|
||
import { Input } from '@/components/ui/input';
|
||
import { Label } from '@/components/ui/label';
|
||
import { Checkbox } from '@/components/ui/checkbox';
|
||
import { toast } from 'sonner';
|
||
import { Loader2, MapPin, Rocket, Map, Save, Users, Eye, EyeOff, Shield } from 'lucide-react';
|
||
|
||
export default function LoginPage() {
|
||
const [isLogin, setIsLogin] = useState(true);
|
||
const [username, setUsername] = useState('');
|
||
const [password, setPassword] = useState('');
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [rememberMe, setRememberMe] = useState(false);
|
||
const [loading, setLoading] = useState(false);
|
||
const { signInWithUsername, signUpWithUsername, user } = useAuth();
|
||
const navigate = useNavigate();
|
||
const location = useLocation();
|
||
const from = location.state?.from || '/explore';
|
||
|
||
// Eğer kullanıcı zaten giriş yapmışsa yönlendir
|
||
if (user) {
|
||
navigate(from, { replace: true });
|
||
}
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!username || !password) return;
|
||
|
||
// Kullanıcı adı validasyonu
|
||
if (!/^[a-z0-9_]+$/.test(username)) {
|
||
toast.error('Kullanıcı adı sadece harf, rakam ve alt çizgi içerebilir');
|
||
return;
|
||
}
|
||
|
||
if (password.length < 6) {
|
||
toast.error('Şifre en az 6 karakter olmalıdır');
|
||
return;
|
||
}
|
||
|
||
setLoading(true);
|
||
try {
|
||
if (isLogin) {
|
||
const { error } = await signInWithUsername(username, password);
|
||
if (error) {
|
||
if (error.message.includes('Invalid login credentials')) {
|
||
throw new Error('Kullanıcı adı veya şifre hatalı');
|
||
}
|
||
throw error;
|
||
}
|
||
toast.success(`Hoşgeldin, ${username}!`);
|
||
navigate(from, { replace: true });
|
||
} else {
|
||
const { error } = await signUpWithUsername(username, password);
|
||
if (error) {
|
||
if (error.message.includes('already registered')) {
|
||
throw new Error('Bu kullanıcı adı zaten kullanılıyor');
|
||
}
|
||
throw error;
|
||
}
|
||
toast.success('Hesap oluşturuldu! Giriş yapılıyor...');
|
||
// Otomatik giriş yap
|
||
const { error: signInError } = await signInWithUsername(username, password);
|
||
if (!signInError) {
|
||
navigate(from, { replace: true });
|
||
}
|
||
}
|
||
} catch (error: any) {
|
||
const errorMessage = error.message || 'Bir hata oluştu. Lütfen tekrar deneyin.';
|
||
toast.error(errorMessage);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen flex">
|
||
{/* Sol Taraf - Hero Section (Sadece Desktop) */}
|
||
<div className="hidden lg:flex lg:w-1/2 bg-gradient-to-br from-blue-500 via-blue-600 to-blue-800 p-12 flex-col justify-between text-white relative overflow-hidden">
|
||
{/* Dekoratif arka plan */}
|
||
<div className="absolute inset-0 opacity-10">
|
||
<div className="absolute top-20 left-20 w-72 h-72 bg-white rounded-full blur-3xl" />
|
||
<div className="absolute bottom-20 right-20 w-96 h-96 bg-white rounded-full blur-3xl" />
|
||
</div>
|
||
|
||
<div className="relative z-10">
|
||
{/* Logo */}
|
||
<div className="flex items-center space-x-3 mb-16">
|
||
<div className="p-3 bg-white/20 backdrop-blur-sm rounded-2xl">
|
||
<MapPin className="h-8 w-8" />
|
||
</div>
|
||
<span className="text-2xl font-bold">Cappadocia AI</span>
|
||
</div>
|
||
|
||
{/* Ana Başlık */}
|
||
<div className="space-y-6 max-w-md">
|
||
<h1 className="text-5xl font-bold leading-tight">
|
||
Kapadokya'ya Hoşgeldin
|
||
</h1>
|
||
<p className="text-xl text-blue-100">
|
||
Yapay zeka rehberliğinde unutulmaz deneyimler yarat
|
||
</p>
|
||
|
||
{/* Özellikler Listesi */}
|
||
<div className="space-y-4 pt-8">
|
||
<div className="flex items-center space-x-3">
|
||
<div className="p-2 bg-white/20 backdrop-blur-sm rounded-lg">
|
||
<Rocket className="h-5 w-5" />
|
||
</div>
|
||
<span className="text-lg">AI Destekli Planlama</span>
|
||
</div>
|
||
<div className="flex items-center space-x-3">
|
||
<div className="p-2 bg-white/20 backdrop-blur-sm rounded-lg">
|
||
<Map className="h-5 w-5" />
|
||
</div>
|
||
<span className="text-lg">Gerçek Zamanlı Haritalar</span>
|
||
</div>
|
||
<div className="flex items-center space-x-3">
|
||
<div className="p-2 bg-white/20 backdrop-blur-sm rounded-lg">
|
||
<Save className="h-5 w-5" />
|
||
</div>
|
||
<span className="text-lg">Planlarını Kaydet</span>
|
||
</div>
|
||
<div className="flex items-center space-x-3">
|
||
<div className="p-2 bg-white/20 backdrop-blur-sm rounded-lg">
|
||
<Users className="h-5 w-5" />
|
||
</div>
|
||
<span className="text-lg">Arkadaşlarınla Paylaş</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Alt Bilgi */}
|
||
<div className="relative z-10 flex items-center space-x-2 text-sm text-blue-100">
|
||
<Shield className="h-4 w-4" />
|
||
<span>Güvenlik: Verileriniz 256-bit SSL ile korunur</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Sağ Taraf - Login Form */}
|
||
<div className="w-full lg:w-1/2 flex items-center justify-center p-8 bg-background">
|
||
<div className="w-full max-w-md space-y-8">
|
||
{/* Mobile Logo */}
|
||
<div className="lg:hidden flex justify-center mb-8">
|
||
<div className="flex items-center space-x-2">
|
||
<MapPin className="h-8 w-8 text-primary" />
|
||
<span className="text-2xl font-bold">Cappadocia AI</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Form Header */}
|
||
<div className="space-y-2 text-center lg:text-left">
|
||
<h2 className="text-3xl font-bold tracking-tight">
|
||
{isLogin ? 'Giriş Yap' : 'Hesap Oluştur'}
|
||
</h2>
|
||
<p className="text-muted-foreground">
|
||
{isLogin ? 'Rotanızı yönetin ve seyahatinizi planlayın' : 'Kapadokya maceranıza başlayın'}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Form */}
|
||
<form onSubmit={handleSubmit} className="space-y-6">
|
||
{/* Kullanıcı Adı */}
|
||
<div className="space-y-2">
|
||
<Label htmlFor="username" className="text-sm font-semibold">
|
||
Kullanıcı Adı
|
||
</Label>
|
||
<Input
|
||
id="username"
|
||
placeholder="kullaniciadi"
|
||
required
|
||
value={username}
|
||
onChange={e => setUsername(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, ''))}
|
||
className="h-11 text-base"
|
||
autoComplete="username"
|
||
/>
|
||
<p className="text-xs text-muted-foreground">
|
||
Sadece harf, rakam ve alt çizgi kullanılabilir
|
||
</p>
|
||
</div>
|
||
|
||
{/* Şifre */}
|
||
<div className="space-y-2">
|
||
<div className="flex items-center justify-between">
|
||
<Label htmlFor="password" className="text-sm font-semibold">
|
||
Şifre
|
||
</Label>
|
||
{isLogin && (
|
||
<button
|
||
type="button"
|
||
className="text-xs text-primary hover:underline"
|
||
onClick={() => toast.info('Şifre sıfırlama özelliği yakında eklenecek')}
|
||
>
|
||
Şifremi unuttum
|
||
</button>
|
||
)}
|
||
</div>
|
||
<div className="relative">
|
||
<Input
|
||
id="password"
|
||
type={showPassword ? 'text' : 'password'}
|
||
placeholder="••••••••"
|
||
required
|
||
value={password}
|
||
onChange={e => setPassword(e.target.value)}
|
||
className="h-11 text-base pr-10"
|
||
autoComplete={isLogin ? 'current-password' : 'new-password'}
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowPassword(!showPassword)}
|
||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||
>
|
||
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||
</button>
|
||
</div>
|
||
{!isLogin && password && (
|
||
<p className="text-xs text-muted-foreground">
|
||
{password.length < 6 ? '⚠️ En az 6 karakter gerekli' : '✓ Şifre uygun'}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
{/* Beni Hatırla */}
|
||
{isLogin && (
|
||
<div className="flex items-center space-x-2">
|
||
<Checkbox
|
||
id="remember"
|
||
checked={rememberMe}
|
||
onCheckedChange={(checked) => setRememberMe(checked as boolean)}
|
||
/>
|
||
<Label
|
||
htmlFor="remember"
|
||
className="text-sm font-normal cursor-pointer"
|
||
>
|
||
Beni hatırla
|
||
</Label>
|
||
</div>
|
||
)}
|
||
|
||
{/* Submit Button */}
|
||
<Button
|
||
type="submit"
|
||
className="w-full h-11 text-base font-semibold"
|
||
disabled={loading}
|
||
>
|
||
{loading ? (
|
||
<>
|
||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||
{isLogin ? 'Giriş yapılıyor...' : 'Hesap oluşturuluyor...'}
|
||
</>
|
||
) : (
|
||
isLogin ? 'Giriş Yap' : 'Hesap Oluştur'
|
||
)}
|
||
</Button>
|
||
</form>
|
||
|
||
{/* Divider */}
|
||
<div className="relative">
|
||
<div className="absolute inset-0 flex items-center">
|
||
<span className="w-full border-t" />
|
||
</div>
|
||
<div className="relative flex justify-center text-xs uppercase">
|
||
<span className="bg-background px-2 text-muted-foreground">veya</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Toggle Login/Signup */}
|
||
<div className="text-center">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setIsLogin(!isLogin);
|
||
setPassword('');
|
||
}}
|
||
className="text-sm text-muted-foreground hover:text-primary transition-colors"
|
||
>
|
||
{isLogin ? (
|
||
<>
|
||
Hesabınız yok mu?{' '}
|
||
<span className="font-semibold text-primary">Kayıt ol</span>
|
||
</>
|
||
) : (
|
||
<>
|
||
Zaten hesabınız var mı?{' '}
|
||
<span className="font-semibold text-primary">Giriş yap</span>
|
||
</>
|
||
)}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|