Spaces:
Sleeping
Sleeping
File size: 6,602 Bytes
5e870e6 | 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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | 'use client';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { getCurrentUser } from '@/lib/api';
import { ShieldCheck, Fingerprint, Globe, Cpu, Lock } from 'lucide-react';
interface ProtectedRouteProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}
export default function ProtectedRoute({ children, fallback }: ProtectedRouteProps) {
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
const router = useRouter();
useEffect(() => {
const checkAuthStatus = async () => {
try {
const user = await getCurrentUser();
if (user) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
setTimeout(() => {
try {
router.replace('/login');
} catch (error) {
window.location.href = '/login';
}
}, 300);
}
} catch (error) {
const storedUser = localStorage.getItem('user');
if (storedUser) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
setTimeout(() => {
try {
router.replace('/login');
} catch (error) {
window.location.href = '/login';
}
}, 300);
}
}
};
const timer = setTimeout(checkAuthStatus, 200);
return () => clearTimeout(timer);
}, [router]);
if (isAuthenticated === null) {
return (
<div className="min-h-screen bg-[#020617] flex items-center justify-center relative overflow-hidden">
{/* Dynamic Background Elements */}
<div className="absolute inset-0 pointer-events-none">
<motion.div
animate={{
scale: [1, 1.2, 1],
opacity: [0.1, 0.2, 0.1]
}}
transition={{ duration: 4, repeat: Infinity }}
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-600/10 blur-[120px] rounded-full"
/>
<div className="absolute top-0 left-0 w-full h-full bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-[0.02]" />
</div>
<div className="relative z-10 flex flex-col items-center">
{/* Central Logo/Icon Animation */}
<div className="relative mb-12">
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
className="w-32 h-32 rounded-[40px] bg-white/5 border border-white/10 flex items-center justify-center relative overflow-hidden backdrop-blur-2xl shadow-2xl"
>
<motion.div
animate={{
y: [-20, 20, -20],
opacity: [0.2, 0.5, 0.2]
}}
transition={{ duration: 3, repeat: Infinity }}
className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-indigo-500 to-transparent"
/>
<Fingerprint className="w-12 h-12 text-indigo-400" />
</motion.div>
{/* Spinning Rings */}
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 8, repeat: Infinity, ease: "linear" }}
className="absolute -inset-4 border border-indigo-500/20 rounded-full border-dashed"
/>
<motion.div
animate={{ rotate: -360 }}
transition={{ duration: 12, repeat: Infinity, ease: "linear" }}
className="absolute -inset-8 border border-purple-500/10 rounded-full border-dashed"
/>
</div>
{/* Status Text */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-center"
>
<h2 className="text-xl font-black text-white tracking-[0.2em] uppercase mb-4">Neural Handshake</h2>
<div className="flex items-center gap-3 justify-center">
<div className="flex gap-1.5">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
animate={{
scale: [1, 1.5, 1],
opacity: [0.3, 1, 0.3],
backgroundColor: ["#475569", "#6366f1", "#475569"]
}}
transition={{
duration: 1,
repeat: Infinity,
delay: i * 0.2
}}
className="w-1.5 h-1.5 rounded-full"
/>
))}
</div>
<span className="text-[10px] font-black text-indigo-400 uppercase tracking-widest">Verifying Identity Matrix</span>
</div>
</motion.div>
{/* Feature Grid (Subtle) */}
<div className="mt-16 grid grid-cols-3 gap-8">
{[
{ icon: ShieldCheck, label: "Secure" },
{ icon: Globe, label: "Global" },
{ icon: Lock, label: "Encrypted" }
].map((item, i) => (
<motion.div
key={i}
initial={{ opacity: 0 }}
animate={{ opacity: 0.3 }}
transition={{ delay: 0.5 + i * 0.1 }}
className="flex flex-col items-center gap-2"
>
<item.icon className="w-4 h-4 text-white" />
<span className="text-[8px] font-bold text-white uppercase tracking-widest">{item.label}</span>
</motion.div>
))}
</div>
</div>
{/* Binary Stream Decoration */}
<div className="absolute left-10 top-0 bottom-0 w-px bg-white/5 flex flex-col items-center justify-around py-20 pointer-events-none opacity-20 hidden md:flex">
{[0, 1, 0, 1, 1, 0].map((v, i) => (
<span key={i} className="text-[10px] font-mono text-indigo-500">{v}</span>
))}
</div>
<div className="absolute right-10 top-0 bottom-0 w-px bg-white/5 flex flex-col items-center justify-around py-20 pointer-events-none opacity-20 hidden md:flex">
{[1, 0, 1, 0, 0, 1].map((v, i) => (
<span key={i} className="text-[10px] font-mono text-purple-500">{v}</span>
))}
</div>
</div>
);
}
if (isAuthenticated) {
return <>{children}</>;
}
return null;
} |