Spaces:
Running
Running
File size: 6,827 Bytes
b88ce1b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Loader2, ArrowRight, Command, ShieldCheck } from 'lucide-react';
import { useAuth } from '../context/AuthContext';
import { cn } from '../lib/utils';
export default function AuthOverlay() {
const { isAuthenticated, login } = useAuth();
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const handleLogin = async (e) => {
e.preventDefault();
if (!password) return;
setIsSubmitting(true);
setError('');
await new Promise(resolve => setTimeout(resolve, 600));
const result = await login(password);
if (!result.success) {
setError(result.error);
setPassword('');
}
setIsSubmitting(false);
};
if (isAuthenticated) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-white">
{/* 极简网格背景 */}
<div className="absolute inset-0 subtle-grid opacity-40" />
{/* 顶部装饰 */}
<div className="absolute top-0 inset-x-0 h-px bg-gradient-to-r from-transparent via-zinc-200 to-transparent opacity-50" />
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
className="relative w-full max-w-[360px] mx-auto z-10"
>
<div className="bg-white rounded-2xl border border-zinc-200 shadow-xl shadow-zinc-200/50 p-8">
<div className="text-center mb-8">
<div className="inline-flex items-center justify-center w-10 h-10 mb-4 rounded-lg bg-zinc-100 text-zinc-900 border border-zinc-200">
<Command className="w-5 h-5" />
</div>
<h1 className="text-xl font-semibold text-zinc-900 tracking-tight mb-1">
Antigravity
</h1>
<p className="text-zinc-500 text-sm">
请输入访问密码
</p>
</div>
<form onSubmit={handleLogin} className="space-y-4">
<div className="space-y-1.5">
<div className="relative">
<input
type="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
if (error) setError('');
}}
placeholder="Password"
className={cn(
"w-full px-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg text-zinc-900 placeholder:text-zinc-400",
"focus:outline-none focus:ring-2 focus:ring-zinc-900/5 focus:border-zinc-900 transition-all duration-200",
"text-sm font-medium tracking-widest placeholder:tracking-normal",
error && "border-red-500 focus:border-red-500 focus:ring-red-500/5 bg-red-50/50 text-red-900 placeholder:text-red-300"
)}
autoFocus
/>
<AnimatePresence>
{error && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
className="absolute right-3 top-1/2 -translate-y-1/2"
>
<div className="w-1.5 h-1.5 rounded-full bg-red-500" />
</motion.div>
)}
</AnimatePresence>
</div>
<AnimatePresence>
{error && (
<motion.p
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="text-[11px] font-medium text-red-600 pl-1"
>
{error}
</motion.p>
)}
</AnimatePresence>
</div>
<button
type="submit"
disabled={isSubmitting || !password}
className={cn(
"w-full flex items-center justify-center gap-2 py-2.5 px-4 rounded-lg",
"bg-zinc-900 text-white font-medium text-sm",
"hover:bg-zinc-800 active:scale-[0.98] transition-all duration-200",
"disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100",
"shadow-sm hover:shadow-md"
)}
>
{isSubmitting ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<>
<span>进入系统</span>
<ArrowRight className="w-4 h-4 opacity-50" />
</>
)}
</button>
</form>
</div>
<div className="mt-8 text-center">
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-zinc-100/50 border border-zinc-200/50 text-[10px] font-medium text-zinc-400">
<ShieldCheck className="w-3 h-3" />
<span>SECURE GATEWAY</span>
</div>
</div>
</motion.div>
</div>
);
}
|