← Назад
import { useState } from 'react'; import { Lock, Eye, EyeOff, Zap } from 'lucide-react'; const API_BASE = import.meta.env.VITE_API_BASE || 'https://dashboard.szhub.space/api'; interface LoginPageProps { onLogin: (token: string) => void; } export function LoginPage({ onLogin }: LoginPageProps) { const [password, setPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!password) return; setLoading(true); setError(''); try { const res = await fetch(`${API_BASE}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }), }); if (!res.ok) { setError('Неверный пароль'); setLoading(false); return; } const { token } = await res.json(); localStorage.setItem('dashboard_token', token); onLogin(token); } catch { setError('Ошибка соединения с сервером'); setLoading(false); } }; return ( <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center px-4"> {/* Background glow */} <div className="absolute inset-0 overflow-hidden pointer-events-none"> <div className="absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-indigo-500/5 blur-3xl" /> </div> <div className="relative w-full max-w-sm"> {/* Logo */} <div className="flex flex-col items-center mb-8"> <div className="p-4 rounded-2xl bg-indigo-500/20 border border-indigo-500/30 mb-4"> <Zap className="w-10 h-10 text-indigo-400" /> </div> <h1 className="text-2xl font-bold text-white tracking-tight">szhub dashboard</h1> <p className="text-sm text-gray-500 mt-1">Введи пароль для входа</p> </div> {/* Card */} <div className="bg-slate-800/60 backdrop-blur border border-white/10 rounded-2xl p-6 shadow-2xl"> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="block text-xs text-gray-400 uppercase tracking-wider mb-2"> Пароль </label> <div className="relative"> <div className="absolute inset-y-0 left-3 flex items-center pointer-events-none"> <Lock className="w-4 h-4 text-gray-500" /> </div> <input type={showPassword ? 'text' : 'password'} value={password} onChange={(e) => setPassword(e.target.value)} placeholder="••••••••••••••••" className="w-full bg-slate-900/60 border border-white/10 rounded-xl pl-10 pr-10 py-3 text-white placeholder-gray-600 focus:outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/30 transition-all" autoFocus /> <button type="button" onClick={() => setShowPassword(!showPassword)} className="absolute inset-y-0 right-3 flex items-center text-gray-500 hover:text-gray-300 transition-colors" > {showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />} </button> </div> </div> {error && ( <div className="flex items-center gap-2 text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-xl px-3 py-2"> <span>⚠</span> {error} </div> )} <button type="submit" disabled={loading || !password} className="w-full bg-indigo-600 hover:bg-indigo-500 disabled:opacity-40 disabled:cursor-not-allowed text-white font-semibold rounded-xl py-3 transition-all duration-200 flex items-center justify-center gap-2" > {loading ? ( <span className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" /> ) : ( 'Войти' )} </button> </form> </div> <p className="text-center text-xs text-gray-600 mt-6">szhub.space · {new Date().getFullYear()}</p> </div> </div> ); }