Download frontend/src/components/PlatformLaunchModal.tsx from yusufcalisir/Collaborative-Fraud-Intelligence-Simulator: direct link, hf CLI and curl.
- Browser
- Download file 17.7 kB
-
https://huggingface.co/spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/resolve/main/frontend/src/components/PlatformLaunchModal.tsx
- Command line
-
hf download hf://spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/frontend/src/components/PlatformLaunchModal.tsx
-
curl -L -o PlatformLaunchModal.tsx https://huggingface.co/spaces/yusufcalisir/Collaborative-Fraud-Intelligence-Simulator/resolve/main/frontend/src/components/PlatformLaunchModal.tsx
17.7 kB
| import { useState, useEffect } from 'react'; | |
| import { motion, AnimatePresence } from 'framer-motion'; | |
| import { | |
| ShieldCheck, | |
| Cpu, | |
| Layers, | |
| Lock, | |
| ArrowRight, | |
| Sparkles, | |
| Binary, | |
| Radio, | |
| CheckCircle2, | |
| } from 'lucide-react'; | |
| import { useModalA11y } from '../hooks/useModalA11y'; | |
| interface PlatformLaunchModalProps { | |
| isOpen: boolean; | |
| onClose?: () => void; | |
| onComplete: () => void; | |
| } | |
| interface StageInfo { | |
| id: number; | |
| label: string; | |
| subtext: string; | |
| tag: string; | |
| icon: typeof Lock; | |
| color: string; | |
| glow: string; | |
| } | |
| const STAGES: StageInfo[] = [ | |
| { | |
| id: 1, | |
| label: 'mTLS 1.3 & Vault PKI Handshake', | |
| subtext: 'Authenticating consortium bank node on-premises certificates', | |
| tag: 'FIPS 140-3 Β· 1.2ms', | |
| icon: Lock, | |
| color: '#6366f1', | |
| glow: 'rgba(99, 102, 241, 0.4)', | |
| }, | |
| { | |
| id: 2, | |
| label: 'Post-Quantum Kyber-768 Exchange', | |
| subtext: 'Negotiating NIST FIPS 203 KEM & Dilithium-3 quantum-safe keys', | |
| tag: 'PQC Lattice Β· Bound', | |
| icon: Binary, | |
| color: '#8b5cf6', | |
| glow: 'rgba(139, 92, 246, 0.4)', | |
| }, | |
| { | |
| id: 3, | |
| label: 'PyTorch GAT & RΓ©nyi DP Noise Calibration', | |
| subtext: 'Computing 8-head structural embeddings with Opacus (Ξ΅=1.0, Ξ΄=1e-5)', | |
| tag: '12.8k Graphs Β· Active', | |
| icon: Layers, | |
| color: '#06b6d4', | |
| glow: 'rgba(6, 182, 212, 0.4)', | |
| }, | |
| { | |
| id: 4, | |
| label: 'Intel SGX Enclave & Paillier HE Sum', | |
| subtext: 'Hardware TEE blind homomorphic summation & zk-SNARK attestation', | |
| tag: 'Groth16 BN254 Β· <5ms', | |
| icon: Cpu, | |
| color: '#10b981', | |
| glow: 'rgba(16, 185, 129, 0.4)', | |
| }, | |
| { | |
| id: 5, | |
| label: 'Byzantine Krum Defense & Global Weights', | |
| subtext: 'Adversarial poisoning filtered. Dispatching consortium model', | |
| tag: '94.2% Acc Β· Consensus', | |
| icon: ShieldCheck, | |
| color: '#38bdf8', | |
| glow: 'rgba(56, 189, 248, 0.4)', | |
| }, | |
| { | |
| id: 6, | |
| label: 'Inter-Bank FININT & UNODC goAML Active', | |
| subtext: 'Curve25519 ECDH bridge Β· UNODC goAML 4.0 XML & EU AMLA Single Rulebook ready', | |
| tag: 'UNODC Β· EU AMLA', | |
| icon: ShieldCheck, | |
| color: '#f59e0b', | |
| glow: 'rgba(245, 158, 11, 0.4)', | |
| }, | |
| { | |
| id: 7, | |
| label: 'Asset Recovery & FININT Operational Hub', | |
| subtext: 'EUR recall KPIs Β· MTTR analytics Β· mule chain containment live', | |
| tag: 'ISO 20022 Β· EPC SCT', | |
| icon: Radio, | |
| color: '#10b981', | |
| glow: 'rgba(16, 185, 129, 0.4)', | |
| }, | |
| ]; | |
| const BANK_NODES = [ | |
| { id: 'JPM', name: 'JPMorgan', role: 'Bank Alpha', x: 20, y: 75, color: '#6366f1' }, | |
| { id: 'HSB', name: 'HSBC', role: 'Bank Beta', x: 80, y: 75, color: '#a855f7' }, | |
| { id: 'DBK', name: 'Deutsche', role: 'Bank Gamma', x: 50, y: 18, color: '#06b6d4' }, | |
| ]; | |
| export default function PlatformLaunchModal({ isOpen, onClose, onComplete }: PlatformLaunchModalProps) { | |
| const [currentStageIdx, setCurrentStageIdx] = useState(0); | |
| const [progress, setProgress] = useState(0); | |
| const { containerRef } = useModalA11y<HTMLDivElement>({ | |
| isOpen, | |
| onClose: onClose || onComplete, | |
| closeOnEscape: true, | |
| trapFocus: true, | |
| restoreFocus: true, | |
| }); | |
| useEffect(() => { | |
| if (!isOpen) { | |
| setCurrentStageIdx(0); | |
| setProgress(0); | |
| return; | |
| } | |
| const TOTAL_DURATION_MS = 950; | |
| const UPDATE_INTERVAL_MS = 15; | |
| const startTime = Date.now(); | |
| let completionTimeout: ReturnType<typeof setTimeout> | null = null; | |
| const timer = setInterval(() => { | |
| const elapsed = Date.now() - startTime; | |
| const rawPct = Math.min(100, (elapsed / TOTAL_DURATION_MS) * 100); | |
| setProgress(rawPct); | |
| const computedIdx = Math.min( | |
| STAGES.length - 1, | |
| Math.floor((rawPct / 100) * STAGES.length) | |
| ); | |
| setCurrentStageIdx(computedIdx); | |
| if (rawPct >= 100) { | |
| clearInterval(timer); | |
| completionTimeout = setTimeout(() => { | |
| onComplete(); | |
| }, 100); | |
| } | |
| }, UPDATE_INTERVAL_MS); | |
| return () => { | |
| clearInterval(timer); | |
| if (completionTimeout) { | |
| clearTimeout(completionTimeout); | |
| } | |
| }; | |
| }, [isOpen, onComplete]); | |
| if (!isOpen) return null; | |
| const currentStage = STAGES[Math.min(currentStageIdx, STAGES.length - 1)]!; | |
| const StageIcon = currentStage.icon; | |
| return ( | |
| <AnimatePresence> | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| exit={{ opacity: 0 }} | |
| className="fixed inset-0 z-[100] flex items-center justify-center p-3 sm:p-4 bg-[#020208]/92 backdrop-blur-2xl overflow-hidden select-none" | |
| > | |
| {/* Ambient Holographic Radial Background */} | |
| <div className="absolute inset-0 bg-[radial-gradient(#6366f1_1px,transparent_1px)] [background-size:24px_24px] opacity-20 pointer-events-none" /> | |
| <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[340px] sm:w-[500px] h-[340px] sm:h-[500px] bg-indigo-600/15 rounded-full blur-[120px] pointer-events-none" /> | |
| <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[220px] sm:w-[320px] h-[220px] sm:h-[320px] bg-cyan-500/15 rounded-full blur-[90px] pointer-events-none" /> | |
| {/* Modal Window Container - Zero Scroll, 100% Viewport-Friendly */} | |
| <motion.div | |
| ref={containerRef} | |
| role="dialog" | |
| aria-modal="true" | |
| aria-labelledby="launch-modal-title" | |
| aria-describedby="launch-modal-desc" | |
| tabIndex={-1} | |
| initial={{ scale: 0.94, opacity: 0, y: 10 }} | |
| animate={{ scale: 1, opacity: 1, y: 0 }} | |
| exit={{ scale: 0.94, opacity: 0, y: 10 }} | |
| transition={{ type: 'spring', damping: 26, stiffness: 300 }} | |
| className="relative w-full max-w-[480px] bg-[#050614]/95 border border-indigo-500/30 rounded-2xl sm:rounded-3xl shadow-[0_0_80px_rgba(79,70,229,0.3)] overflow-hidden flex flex-col focus:outline-none" | |
| > | |
| {/* Top Neon Laser Glow Bar */} | |
| <div className="h-1 w-full bg-gradient-to-r from-indigo-500 via-cyan-400 to-purple-500 shadow-[0_0_15px_rgba(6,182,212,0.8)]" /> | |
| {/* Header: Minimal, High-Tech, Crisp */} | |
| <div className="px-4 py-3 sm:px-5 sm:py-3.5 border-b border-white/8 bg-[#03030d]/80 flex items-center justify-between gap-2"> | |
| <div className="flex items-center gap-2.5 min-w-0"> | |
| <div className="relative flex items-center justify-center w-8 h-8 rounded-xl bg-indigo-500/15 border border-indigo-400/40 shrink-0 shadow-[0_0_12px_rgba(99,102,241,0.3)]"> | |
| <ShieldCheck className="w-4 h-4 text-cyan-400" /> | |
| <span className="absolute -top-0.5 -right-0.5 flex h-2 w-2"> | |
| <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" /> | |
| <span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-500" /> | |
| </span> | |
| </div> | |
| <div className="min-w-0"> | |
| <h3 id="launch-modal-title" className="text-xs sm:text-sm font-bold text-slate-100 tracking-tight flex items-center gap-1.5 truncate"> | |
| Consortium Handshake | |
| <span className="inline-flex items-center gap-1 px-1.5 py-0.2 rounded text-[9px] font-mono text-cyan-400 bg-cyan-500/10 border border-cyan-500/25"> | |
| <Radio className="w-2 h-2 animate-pulse" /> DEMO SEQUENCE | |
| </span> | |
| </h3> | |
| <p id="launch-modal-desc" className="text-[10px] font-mono text-slate-400 truncate"> | |
| CF-Intelligence Β· Initialization Animation Preview | |
| </p> | |
| </div> | |
| </div> | |
| <button | |
| onClick={onComplete || onClose} | |
| aria-label="Skip handshake and proceed to dashboard" | |
| className="px-2.5 py-1 rounded-lg text-[10px] sm:text-[11px] font-mono text-slate-400 hover:text-white bg-white/5 hover:bg-white/10 border border-white/10 transition-all cursor-pointer flex items-center gap-1 shrink-0 focus:outline-none focus:ring-2 focus:ring-cyan-400" | |
| title="Direct jump to dashboard" | |
| > | |
| <span>Skip</span> | |
| <ArrowRight className="w-3 h-3" /> | |
| </button> | |
| </div> | |
| {/* Body Content */} | |
| <div className="p-4 sm:p-5 space-y-3.5 sm:space-y-4"> | |
| {/* ββ 1. CINEMATIC SVG TOPOLOGY RADAR (Zero-scroll, responsive) ββ */} | |
| <div className="relative h-32 sm:h-36 rounded-2xl bg-[#03030c] border border-white/8 p-2 flex items-center justify-center overflow-hidden"> | |
| {/* Radar Grid Circles */} | |
| <div className="absolute inset-0 flex items-center justify-center pointer-events-none"> | |
| <div className="w-28 h-28 rounded-full border border-indigo-500/15 animate-ping opacity-25" /> | |
| <div className="w-24 h-24 rounded-full border border-indigo-500/20" /> | |
| <div className="w-16 h-16 rounded-full border border-cyan-500/25" /> | |
| </div> | |
| {/* Dynamic Connection Beams */} | |
| <svg className="absolute inset-0 w-full h-full pointer-events-none" viewBox="0 0 100 100" preserveAspectRatio="none"> | |
| {BANK_NODES.map((b) => ( | |
| <line | |
| key={b.id} | |
| x1={b.x} | |
| y1={b.y} | |
| x2={50} | |
| y2={50} | |
| stroke="rgba(99, 102, 241, 0.4)" | |
| strokeWidth="0.8" | |
| strokeDasharray="2 2" | |
| className="animate-pulse" | |
| /> | |
| ))} | |
| </svg> | |
| {/* Central Intel SGX Enclave Hub */} | |
| <div className="relative z-10 flex flex-col items-center"> | |
| <div className="relative w-12 h-12 rounded-xl bg-gradient-to-br from-indigo-600 via-purple-600 to-cyan-500 p-0.5 shadow-[0_0_25px_rgba(6,182,212,0.6)]"> | |
| <div className="w-full h-full rounded-[10px] bg-[#050616] flex flex-col items-center justify-center"> | |
| <Cpu className="w-4 h-4 text-cyan-300" /> | |
| <span className="text-[7.5px] font-mono font-bold text-cyan-300">SGX TEE</span> | |
| </div> | |
| </div> | |
| <span className="text-[8.5px] font-mono text-cyan-400 font-semibold mt-1">FL Coordinator</span> | |
| </div> | |
| {/* 3 Bank Nodes Around Enclave */} | |
| {BANK_NODES.map((node) => ( | |
| <div | |
| key={node.id} | |
| className="absolute flex flex-col items-center" | |
| style={{ left: `${node.x}%`, top: `${node.y}%`, transform: 'translate(-50%, -50%)' }} | |
| > | |
| <div | |
| className="w-8 h-8 rounded-lg flex items-center justify-center text-[10px] font-mono font-bold text-white shadow-md border" | |
| style={{ | |
| backgroundColor: `${node.color}20`, | |
| borderColor: `${node.color}70`, | |
| boxShadow: `0 0 12px ${node.color}40`, | |
| }} | |
| > | |
| {node.id} | |
| </div> | |
| <span className="text-[7.5px] font-mono text-slate-400 mt-0.5">{node.role}</span> | |
| </div> | |
| ))} | |
| </div> | |
| {/* ββ 2. DYNAMIC FOCUSED ACTIVE STAGE CARD (Framer Motion) ββ */} | |
| <div className="relative min-h-[72px] sm:min-h-[76px] rounded-xl sm:rounded-2xl bg-indigo-500/10 border border-indigo-500/30 p-3 sm:p-3.5 flex items-center justify-between gap-3 shadow-[0_0_20px_rgba(99,102,241,0.15)] overflow-hidden"> | |
| <div className="flex items-center gap-3 min-w-0"> | |
| <div | |
| className="w-9 h-9 sm:w-10 sm:h-10 rounded-xl flex items-center justify-center shrink-0 border" | |
| style={{ | |
| backgroundColor: `${currentStage.color}25`, | |
| borderColor: `${currentStage.color}60`, | |
| boxShadow: `0 0 15px ${currentStage.glow}`, | |
| }} | |
| > | |
| <StageIcon className="w-4 h-4 sm:w-5 sm:h-5 text-white" /> | |
| </div> | |
| <div className="min-w-0"> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-[9px] font-mono font-bold px-1.5 py-0.2 rounded bg-indigo-500/20 text-indigo-300 border border-indigo-500/30 shrink-0"> | |
| STAGE 0{currentStage.id}/0{STAGES.length} | |
| </span> | |
| </div> | |
| <h4 className="text-xs sm:text-[13px] font-bold text-slate-100 tracking-tight truncate mt-0.5"> | |
| {currentStage.label} | |
| </h4> | |
| <p className="text-[10px] text-slate-400 truncate mt-0.2"> | |
| {currentStage.subtext} | |
| </p> | |
| </div> | |
| </div> | |
| <span className="text-[9.5px] font-mono font-semibold px-2 py-1 rounded-md bg-white/5 border border-white/10 text-cyan-300 shrink-0"> | |
| {currentStage.tag} | |
| </span> | |
| </div> | |
| {/* ββ 3. SYNCHRONIZED MULTI-STAGE STEP TRACKER (Dynamic Visual Breadcrumb) ββ */} | |
| <div | |
| className="grid gap-1.5" | |
| style={{ gridTemplateColumns: `repeat(${STAGES.length}, minmax(0, 1fr))` }} | |
| > | |
| {STAGES.map((s, idx) => { | |
| const stageStartPct = (idx / STAGES.length) * 100; | |
| const stageEndPct = ((idx + 1) / STAGES.length) * 100; | |
| const isDone = progress >= stageEndPct; | |
| const isCurrent = progress >= stageStartPct && progress < stageEndPct; | |
| let segmentFill = 0; | |
| if (isDone) { | |
| segmentFill = 100; | |
| } else if (isCurrent) { | |
| segmentFill = Math.min( | |
| 100, | |
| Math.max(0, ((progress - stageStartPct) / (stageEndPct - stageStartPct)) * 100) | |
| ); | |
| } | |
| return ( | |
| <div | |
| key={s.id} | |
| className="h-1.5 rounded-full bg-white/10 overflow-hidden relative" | |
| > | |
| <div | |
| className="h-full rounded-full transition-[width] duration-75 ease-linear" | |
| style={{ | |
| width: `${segmentFill}%`, | |
| backgroundColor: isDone ? '#34d399' : s.color, | |
| boxShadow: isDone | |
| ? '0 0 8px rgba(52, 211, 153, 0.8)' | |
| : isCurrent | |
| ? `0 0 8px ${s.glow}` | |
| : 'none', | |
| }} | |
| /> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| {/* ββ 4. REAL-TIME TELEMETRY MICRO-PILLS ββ */} | |
| <div className="grid grid-cols-3 gap-2 text-center font-mono text-[9px] sm:text-[10px]"> | |
| <div className="p-1.5 rounded-lg bg-white/[0.02] border border-white/5 flex items-center justify-center gap-1 text-slate-300"> | |
| <CheckCircle2 className="w-2.5 h-2.5 text-emerald-400 shrink-0" /> | |
| <span className="truncate">3 Nodes Linked</span> | |
| </div> | |
| <div className="p-1.5 rounded-lg bg-white/[0.02] border border-white/5 flex items-center justify-center gap-1 text-slate-300"> | |
| <span className="w-1.5 h-1.5 rounded-full bg-cyan-400 shrink-0" /> | |
| <span className="truncate">Ξ΅=1.0 DP Bound</span> | |
| </div> | |
| <div className="p-1.5 rounded-lg bg-white/[0.02] border border-white/5 flex items-center justify-center gap-1 text-slate-300"> | |
| <span className="w-1.5 h-1.5 rounded-full bg-purple-400 shrink-0" /> | |
| <span className="truncate">SGX Attested</span> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Footer: Progress Bar */} | |
| <div className="px-4 py-3 sm:px-5 sm:py-3.5 bg-[#03030d] border-t border-white/8 space-y-1.5"> | |
| <div className="flex items-center justify-between text-[10.5px] sm:text-xs font-mono"> | |
| <span className="text-slate-300 flex items-center gap-1.5 truncate"> | |
| <Sparkles className="w-3 h-3 text-indigo-400 shrink-0" /> | |
| <span className="truncate"> | |
| {progress >= 100 | |
| ? 'Consortium Handshake Complete Β· Launching...' | |
| : 'Synchronizing Consortium Data Plane...'} | |
| </span> | |
| </span> | |
| <span className="text-cyan-400 font-bold ml-2 shrink-0">{Math.min(100, Math.round(progress))}%</span> | |
| </div> | |
| <div className="relative w-full h-1.5 sm:h-2 bg-slate-900 rounded-full overflow-hidden border border-white/10 p-0.5"> | |
| <div | |
| className="h-full bg-gradient-to-r from-indigo-500 via-cyan-400 to-purple-500 rounded-full shadow-[0_0_12px_rgba(6,182,212,0.8)] transition-[width] duration-75 ease-linear" | |
| style={{ width: `${Math.min(100, progress)}%` }} | |
| /> | |
| </div> | |
| <div className="flex items-center justify-between text-[9px] font-mono text-slate-400 pt-0.5"> | |
| <span>Interactive launch preview animation</span> | |
| <span>Verifying runtime services concurrently</span> | |
| </div> | |
| </div> | |
| </motion.div> | |
| </motion.div> | |
| </AnimatePresence> | |
| ); | |
| } | |