Files

316 lines
15 KiB
TypeScript

"use client";
import { useEffect, useState, useRef } from "react";
import { useDictionary } from "@/lib/dictionary";
import { useOmni } from "@/components/OmniSyncProvider";
import Link from "next/link";
import { useRouter } from "next/navigation";
interface NeuralSubject {
id: string;
name: string;
attentionScore: number;
cognitiveLoad: number;
alphaWave: number;
status: "FOCUSED" | "DISTRACTED" | "ZONED_OUT";
}
export default function TelepathyMatrix() {
const { t } = useDictionary();
const { theme, locale } = useOmni();
const router = useRouter();
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
// ALL HOOKS MUST BE DECLARED BEFORE ANY CONDITIONAL RETURN (React Rules of Hooks)
const [subjects, setSubjects] = useState<NeuralSubject[]>([
{ id: "xcu_subj_01", name: "Commander Alpha", attentionScore: 95, cognitiveLoad: 80, alphaWave: 0.8, status: "FOCUSED" },
{ id: "xcu_subj_02", name: "Operative Bravo", attentionScore: 88, cognitiveLoad: 60, alphaWave: 0.6, status: "FOCUSED" },
{ id: "xcu_subj_03", name: "Spectre 7", attentionScore: 45, cognitiveLoad: 30, alphaWave: 0.4, status: "DISTRACTED" },
{ id: "xcu_subj_04", name: "Agent Echo", attentionScore: 12, cognitiveLoad: 10, alphaWave: 0.1, status: "ZONED_OUT" },
{ id: "xcu_subj_05", name: "Node Delta", attentionScore: 78, cognitiveLoad: 70, alphaWave: 0.7, status: "FOCUSED" },
{ id: "xcu_subj_06", name: "Vanguard 1", attentionScore: 25, cognitiveLoad: 15, alphaWave: 0.2, status: "ZONED_OUT" },
]);
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const checkAuth = async () => {
try {
const res = await fetch("/api/superadmin/supreme-dashboard");
const data = await res.json();
if (data.error) {
router.push("/");
} else {
setIsCheckingAuth(false);
}
} catch (err) {
router.push("/");
}
};
checkAuth();
}, [router]);
// Brainwave Visualization Effect - MUST be before conditional return (React Rules of Hooks)
useEffect(() => {
if (isCheckingAuth) return;
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
let animationFrameId: number;
let time = 0;
const resize = () => {
canvas.width = canvas.parentElement?.clientWidth || 800;
canvas.height = 150;
};
window.addEventListener("resize", resize);
resize();
const draw = () => {
time += 0.05;
ctx.clearRect(0, 0, canvas.width, canvas.height);
const width = canvas.width;
const height = canvas.height;
const centerY = height / 2;
// Draw 3 Brainwaves (Alpha, Beta, Gamma)
const waves = [
{ color: "rgba(11, 92, 255, 0.8)", freq: 0.02, amp: 20, speed: 1.5 }, // Alpha (Blue)
{ color: "rgba(37, 211, 102, 0.8)", freq: 0.05, amp: 10, speed: 2.5 }, // Beta (Green)
{ color: "rgba(255, 0, 128, 0.8)", freq: 0.1, amp: 5, speed: 4.0 } // Gamma (Magenta)
];
waves.forEach((wave, i) => {
ctx.beginPath();
ctx.moveTo(0, centerY);
for (let x = 0; x < width; x++) {
const y = Math.sin(x * wave.freq + time * wave.speed + i) * wave.amp
+ Math.sin(x * wave.freq * 0.5 + time) * (wave.amp * 0.5);
ctx.lineTo(x, centerY + y);
}
ctx.strokeStyle = wave.color;
ctx.lineWidth = 2;
ctx.stroke();
});
// Draw grid overlay
ctx.strokeStyle = "rgba(255,255,255,0.05)";
ctx.lineWidth = 1;
for (let x = 0; x < width; x += 30) {
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke();
}
for (let y = 0; y < height; y += 30) {
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke();
}
animationFrameId = requestAnimationFrame(draw);
};
draw();
return () => {
window.removeEventListener("resize", resize);
cancelAnimationFrame(animationFrameId);
};
}, [isCheckingAuth]);
// Simulate real-time neural data updates - MUST be before conditional return
useEffect(() => {
if (isCheckingAuth) return;
const interval = setInterval(() => {
setSubjects(prev => prev.map(s => {
let newScore = s.attentionScore + (Math.random() * 10 - 5);
newScore = Math.max(0, Math.min(100, newScore));
let newStatus: "FOCUSED" | "DISTRACTED" | "ZONED_OUT" = "FOCUSED";
if (newScore < 30) newStatus = "ZONED_OUT";
else if (newScore < 60) newStatus = "DISTRACTED";
return {
...s,
attentionScore: newScore,
cognitiveLoad: Math.max(0, Math.min(100, s.cognitiveLoad + (Math.random() * 6 - 3))),
status: newStatus
};
}));
}, 2000);
return () => clearInterval(interval);
}, [isCheckingAuth]);
if (isCheckingAuth) {
return <div className="min-h-screen bg-black flex items-center justify-center text-purple-500 font-mono text-sm">INITIALIZING SECURE CONNECTION...</div>;
}
const sendSensoryPing = (id: string, name: string) => {
// Flash the screen to simulate ping
const el = document.getElementById(`card-${id}`);
if (el) {
el.classList.add("ring-4", "ring-[#ff0080]", "ring-opacity-100", "scale-105");
setTimeout(() => el.classList.remove("ring-4", "ring-[#ff0080]", "ring-opacity-100", "scale-105"), 500);
}
alert(`Sensory Ping (Haptic & Audio) disuntikkan langsung ke korteks partisipan [${name}]`);
};
return (
<div className={`min-h-screen ${theme === 'dark' ? 'bg-[#03060a] text-gray-200' : 'bg-gray-50 text-gray-900'} font-sans selection:bg-purple-500/30 overflow-x-hidden relative`}>
<style dangerouslySetInnerHTML={{__html: `
@keyframes cyber-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.8; transform: scale(0.98); }
}
.animate-cyber-pulse {
animation: cyber-pulse 2s infinite ease-in-out;
}
@keyframes emergency-flash {
0%, 100% { background-color: rgba(220, 38, 38, 0.1); border-color: rgba(220, 38, 38, 0.3); }
50% { background-color: rgba(220, 38, 38, 0.4); border-color: rgba(220, 38, 38, 0.8); box-shadow: 0 0 20px rgba(220, 38, 38, 0.6); }
}
.emergency-flash {
animation: emergency-flash 1s infinite;
}
`}} />
{/* Decorative Grid */}
<div className="absolute inset-0 z-0 pointer-events-none" style={{
backgroundImage: theme === 'dark'
? 'linear-gradient(rgba(255, 0, 128, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 0, 128, 0.03) 1px, transparent 1px)'
: 'linear-gradient(rgba(0, 0, 0, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 0, 0, 0.03) 1px, transparent 1px)',
backgroundSize: '40px 40px'
}}></div>
<div className="relative z-10 p-6 md:p-10 max-w-7xl mx-auto flex flex-col gap-8">
{/* HEADER */}
<header className="flex flex-col md:flex-row justify-between items-start md:items-center border-b border-white/10 pb-6 gap-4">
<div>
<div className="flex items-center gap-3 mb-2">
<div className="w-8 h-8 bg-purple-500/20 rounded-lg flex items-center justify-center border border-purple-500/50 shadow-[0_0_15px_rgba(168,85,247,0.4)]">
<svg className="w-5 h-5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
</div>
<h1 className="text-3xl font-black tracking-tighter text-transparent bg-clip-text bg-gradient-to-r from-[#ff0080] to-[#7928ca]">
TELEPATHY MATRIX
</h1>
</div>
<p className="text-sm font-mono text-purple-400/80 uppercase tracking-widest">
Global Neural Attention & Cognitive Load Dashboard
</p>
</div>
<div className="flex gap-4 items-center">
<div className="px-4 py-2 rounded-lg bg-red-500/10 border border-red-500/30 flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-red-500 animate-pulse"></span>
<span className="font-mono text-red-400 text-xs font-bold uppercase">Surveillance Active</span>
</div>
<Link href="/supreme-admin" className="px-4 py-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 text-sm font-bold transition-all shadow-sm">
Kembali ke Supreme Admin
</Link>
</div>
</header>
{/* MASTER OSCILLATOR CANVAS */}
<div className={`rounded-2xl border ${theme === 'dark' ? 'bg-black/60 border-white/10' : 'bg-white border-gray-200'} backdrop-blur-xl p-6 relative overflow-hidden shadow-2xl`}>
<div className="absolute top-4 left-4 flex items-center gap-2 z-20">
<span className="text-[10px] font-black uppercase tracking-widest text-gray-400">Aggregated Brainwaves</span>
</div>
<div className="absolute top-4 right-4 flex gap-3 z-20 text-[9px] font-black uppercase tracking-widest">
<div className="flex items-center gap-1"><span className="w-2 h-2 bg-blue-500 rounded-full"></span> Alpha (Relaxed)</div>
<div className="flex items-center gap-1"><span className="w-2 h-2 bg-green-500 rounded-full"></span> Beta (Focused)</div>
<div className="flex items-center gap-1"><span className="w-2 h-2 bg-[#ff0080] rounded-full"></span> Gamma (High Load)</div>
</div>
<div className="w-full h-[150px] mt-4 relative z-10 opacity-80 mix-blend-screen">
<canvas ref={canvasRef} className="absolute inset-0 w-full h-full"></canvas>
</div>
</div>
{/* NEURAL GRID */}
<div>
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-black uppercase tracking-widest flex items-center gap-2">
<svg className="w-5 h-5 text-gray-500" fill="currentColor" viewBox="0 0 20 20"><path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path><path fillRule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z" clipRule="evenodd"></path></svg>
Active Subjects
</h2>
<div className="text-[10px] font-mono bg-white/5 border border-white/10 px-3 py-1 rounded-full text-gray-400">
{subjects.filter(s => s.status === 'ZONED_OUT').length} CRITICAL / {subjects.length} TOTAL
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{subjects.map(subject => {
const isCritical = subject.status === 'ZONED_OUT';
return (
<div
id={`card-${subject.id}`}
key={subject.id}
className={`glass-panel p-6 rounded-2xl border transition-all duration-300 relative overflow-hidden group ${
isCritical ? 'emergency-flash' : theme === 'dark' ? 'border-white/5 bg-white/2 hover:border-white/20' : 'border-gray-200 bg-white hover:border-purple-300 shadow-sm'
}`}
>
{/* Background Glow */}
<div className={`absolute -right-10 -top-10 w-32 h-32 rounded-full blur-3xl opacity-20 pointer-events-none transition-colors ${
isCritical ? 'bg-red-500' : subject.status === 'FOCUSED' ? 'bg-emerald-500' : 'bg-amber-500'
}`}></div>
<div className="flex justify-between items-start mb-6 relative z-10">
<div>
<div className="text-[10px] font-mono text-gray-500">{subject.id}</div>
<h3 className={`text-lg font-black tracking-tight mt-1 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>{subject.name}</h3>
</div>
<div className={`px-2 py-1 rounded-md text-[9px] font-black uppercase tracking-widest border ${
isCritical ? 'bg-red-500/20 text-red-500 border-red-500/30' :
subject.status === 'FOCUSED' ? 'bg-emerald-500/20 text-emerald-500 border-emerald-500/30' :
'bg-amber-500/20 text-amber-500 border-amber-500/30'
}`}>
{subject.status}
</div>
</div>
<div className="space-y-5 relative z-10">
<div>
<div className="flex justify-between text-xs mb-1 font-bold">
<span className="text-gray-500 uppercase">Attention Score</span>
<span className={isCritical ? 'text-red-500' : 'text-emerald-500'}>{subject.attentionScore.toFixed(1)}%</span>
</div>
<div className="w-full bg-black/40 rounded-full h-1.5 overflow-hidden border border-white/5">
<div className={`h-full transition-all duration-500 ${isCritical ? 'bg-red-500' : 'bg-emerald-500'}`} style={{ width: `${subject.attentionScore}%` }}></div>
</div>
</div>
<div>
<div className="flex justify-between text-xs mb-1 font-bold">
<span className="text-gray-500 uppercase">Cognitive Load</span>
<span className="text-purple-400">{subject.cognitiveLoad.toFixed(1)}%</span>
</div>
<div className="w-full bg-black/40 rounded-full h-1.5 overflow-hidden border border-white/5">
<div className="bg-purple-500 h-full transition-all duration-500" style={{ width: `${subject.cognitiveLoad}%` }}></div>
</div>
</div>
</div>
<div className="mt-6 pt-4 border-t border-white/10 relative z-10 flex justify-between items-center">
<div className="text-[9px] font-mono text-gray-500">
Alpha: {subject.alphaWave.toFixed(2)}Hz
</div>
<button
onClick={() => sendSensoryPing(subject.id, subject.name)}
className={`px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all shadow-lg hover:scale-105 active:scale-95 ${
isCritical
? 'bg-red-600 hover:bg-red-500 text-white shadow-red-500/30'
: 'bg-white/10 hover:bg-white/20 text-gray-300 border border-white/10'
}`}
>
Sensory Ping
</button>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
);
}