Files
multiverse/jumpa-vc/app/page.tsx
T

492 lines
31 KiB
TypeScript

// [TSM.ID].[11031972] — All Rights Reserved. Proprietary & Confidential.
"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
export default function DashboardPage() {
const router = useRouter();
const [isVerifying, setIsVerifying] = useState(true);
const [username, setUsername] = useState("");
const [engineStrategy, setEngineStrategy] = useState("XCU_DIRECTOR");
// States for Join Modal
const [showJoinModal, setShowJoinModal] = useState(false);
const [roomName, setRoomName] = useState("");
// State for Chronos Smart Scheduler
const [showChronosModal, setShowChronosModal] = useState(false);
const [roomMode, setRoomMode] = useState<"standard" | "webinar" | "podcast">("standard");
const [chronoDate, setChronoDate] = useState("");
const [chronoName, setChronoName] = useState("");
const [chronoResult, setChronoResult] = useState<{link: string, text: string} | null>(null);
const [schedules, setSchedules] = useState<{id: string, title: string, mode: string, date: string, link: string}[]>([]);
// State for The Vault
const [showVaultModal, setShowVaultModal] = useState(false);
const [vaultVideoUrl, setVaultVideoUrl] = useState<string | null>(null);
const [vaultVideoName, setVaultVideoName] = useState<string>("");
// States for Copy Alert
const [copySuccess, setCopySuccess] = useState(false);
useEffect(() => {
(async () => {
try {
const resp = await fetch("/vc/api/auth/me");
const data = await resp.json();
if (data.error) {
// Auth failed - show as guest instead of redirecting (prevents loop)
setUsername("Guest");
setIsVerifying(false);
} else {
setUsername(data.email); // Full email as display name
if (data.mediaEngineStrategy) {
setEngineStrategy(data.mediaEngineStrategy);
}
setIsVerifying(false);
}
} catch {
// Network error - show as guest instead of redirecting (prevents loop)
setUsername("Guest");
setIsVerifying(false);
}
})();
}, []);
const handleNewMeeting = async () => {
// TAHAP NANO: Generate Random Room ID dengan Kriptografi Murni
const uuid = crypto.randomUUID().toUpperCase().split('-');
const newRoomId = `JMP-${uuid[1]}-${uuid[2]}`;
const inviteLink = typeof window !== 'undefined' ? `${window.location.origin}/vc/room/${newRoomId}` : `/vc/room/${newRoomId}`;
// Copy to clipboard
try {
await navigator.clipboard.writeText(`Join JUMPA.ID Meeting:\n${inviteLink}`);
setCopySuccess(true);
setTimeout(() => {
router.push(`/room/${newRoomId}`);
}, 1500); // Tunggu 1.5 detik agar user melihat notif tercopy sebelum dipindah
} catch {
// Fallback jika clipboard API tidak diizinkan
router.push(`/room/${newRoomId}`);
}
};
const handleJoinSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (roomName) {
router.push(`/room/${roomName.toUpperCase()}`);
}
};
if (isVerifying) {
return (
<div className="min-h-screen flex items-center justify-center bg-[#0a101d]">
<div className="flex flex-col items-center gap-4">
<div className="w-12 h-12 border-4 border-brand border-t-transparent rounded-full animate-spin"></div>
<p className="text-brand font-medium">Securing Connection...</p>
</div>
</div>
);
}
return (
<div className="min-h-dvh bg-[#050b14] text-white selection:bg-red-500/30 relative overflow-x-hidden pb-24 md:pb-10">
{/* Abstract Background Decorators */}
<div className="fixed inset-0 z-0 pointer-events-none">
<div className="absolute top-[-10%] left-[-10%] w-[500px] h-[500px] bg-red-600/10 rounded-full blur-[150px] animate-pulse"></div>
<div className="absolute bottom-[-10%] right-[-10%] w-[400px] h-[400px] bg-blue-600/10 rounded-full blur-[120px]"></div>
</div>
{/* Top Navbar - Ultra Refined */}
<header className="h-16 border-b border-white/5 flex items-center justify-between px-4 md:px-8 backdrop-blur-xl bg-black/40 relative z-50">
<div className="flex items-center gap-4">
<button
onClick={() => window.location.href = '/dashboard'}
className="flex items-center gap-2 p-2 rounded-xl bg-white/5 hover:bg-white/10 text-gray-400 hover:text-white transition-all border border-white/5 group"
title="Back to Hub"
>
<svg className="w-5 h-5 group-hover:-translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
<span className="hidden md:inline font-bold text-xs uppercase tracking-widest">Master Hub</span>
</button>
<div className="h-6 w-[1px] bg-white/10 mx-2 hidden md:block"></div>
<div className="flex items-center gap-3">
<div className={`w-8 h-8 rounded-lg flex items-center justify-center shadow-lg ${engineStrategy.includes('XCU') ? 'bg-red-600 shadow-red-600/20' : 'bg-blue-600 shadow-blue-600/20'}`}>
<svg className="w-5 h-5 text-black" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2L2 22h20L12 2zm0 4.5l6.5 13h-13L12 6.5zM11 16h2v2h-2v-2zm0-5h2v4h-2v-4z"/></svg>
</div>
<span className={`font-black text-sm md:text-lg tracking-tighter uppercase ${engineStrategy.includes('XCU') ? 'text-red-500' : 'text-blue-500'}`}>
JUMPA <span className="text-white">MEET</span>
<span className="hidden lg:inline ml-2 text-[10px] text-gray-600 font-mono tracking-widest">XCU-DIRECT-LINK</span>
</span>
</div>
</div>
<div className="flex items-center gap-3">
<div className="hidden sm:flex flex-col items-end mr-2">
<span className="text-[10px] text-gray-500 uppercase font-bold">Authenticated as</span>
<span className="text-xs text-white font-mono">{username}</span>
</div>
<div className="w-10 h-10 rounded-full bg-linear-to-br from-gray-800 to-black border border-white/10 flex items-center justify-center font-black text-brand shadow-xl">
{username.substring(0,2).toUpperCase()}
</div>
</div>
</header>
{/* Main Content */}
<main className="max-w-6xl mx-auto px-4 md:px-6 py-10 md:py-20 relative z-10">
<div className="flex flex-col lg:flex-row gap-12 lg:gap-20 items-center lg:items-start">
{/* Left Side: Dynamic Status & Info */}
<div className="flex-1 text-center lg:text-left">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-red-500/10 border border-red-500/20 text-red-500 text-[10px] font-black uppercase tracking-widest mb-6 animate-pulse">
<div className="w-1.5 h-1.5 rounded-full bg-red-500"></div>
XCU Engine Status: Ultra Optimal
</div>
<h1 className="text-5xl md:text-8xl font-black tracking-tighter mb-4 leading-none">
{new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</h1>
<p className="text-lg md:text-xl text-gray-400 font-medium mb-10">
{new Date().toLocaleDateString([], { weekday: 'long', month: 'long', day: 'numeric' })}
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 max-w-xl">
<div className="p-5 rounded-2xl bg-black/40 border border-white/5 backdrop-blur-md">
<h4 className="text-[10px] font-black text-red-500 uppercase tracking-widest mb-2">Protocol</h4>
<p className="text-xs text-gray-400">WebTransport HTTP/3 QUIC (0ms Latency Kernel Bypass)</p>
</div>
<div className="p-5 rounded-2xl bg-black/40 border border-white/5 backdrop-blur-md">
<h4 className="text-[10px] font-black text-blue-500 uppercase tracking-widest mb-2">Encryption</h4>
<p className="text-xs text-gray-400">Quantum-Safe AES-GCM 256-bit (E2EE Active)</p>
</div>
</div>
</div>
{/* Right Side: High-Density Action Grid */}
<div className="w-full max-w-md">
<div className="grid grid-cols-2 gap-4">
{/* New Meeting - The Big Button */}
<button
onClick={handleNewMeeting}
className="col-span-2 group relative p-8 rounded-3xl border border-red-500/30 bg-red-950/20 hover:bg-red-900/30 transition-all duration-500 overflow-hidden shadow-2xl shadow-red-950/20"
>
<div className="absolute inset-0 bg-linear-to-br from-red-600/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div className="relative flex flex-col items-center gap-4">
<div className="w-20 h-20 rounded-2xl bg-red-600 flex items-center justify-center text-black shadow-[0_0_30px_rgba(220,38,38,0.5)] group-hover:shadow-[0_0_50px_rgba(220,38,38,0.8)] transition-all group-hover:scale-110">
<svg className="w-10 h-10" fill="currentColor" viewBox="0 0 24 24"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"></path></svg>
</div>
<div className="text-center">
<span className="block font-black text-lg tracking-tighter text-white uppercase">Mulai Rapat Baru</span>
<span className="text-[10px] text-red-500 font-mono font-bold uppercase tracking-widest">Direct Ultra Link</span>
</div>
</div>
</button>
{/* Join Button */}
<button onClick={() => setShowJoinModal(true)} className="group p-6 rounded-3xl border border-white/5 bg-white/5 hover:bg-white/10 transition-all flex flex-col items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-blue-600/20 flex items-center justify-center text-blue-500 group-hover:bg-blue-600 group-hover:text-white transition-all">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4"></path></svg>
</div>
<span className="font-bold text-xs uppercase tracking-widest">Join</span>
</button>
{/* Chronos Button */}
<button onClick={() => setShowChronosModal(true)} className="group p-6 rounded-3xl border border-white/5 bg-white/5 hover:bg-white/10 transition-all flex flex-col items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-pink-600/20 flex items-center justify-center text-pink-500 group-hover:bg-pink-600 group-hover:text-white transition-all">
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z"/></svg>
</div>
<span className="font-bold text-xs uppercase tracking-widest">Chronos</span>
</button>
{/* Vault Button */}
<button onClick={() => setShowVaultModal(true)} className="group p-6 rounded-3xl border border-white/5 bg-white/5 hover:bg-white/10 transition-all flex flex-col items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-teal-600/20 flex items-center justify-center text-teal-500 group-hover:bg-teal-600 group-hover:text-white transition-all">
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/></svg>
</div>
<span className="font-bold text-xs uppercase tracking-widest">The Vault</span>
</button>
{/* Tenant Master Button */}
<button
onClick={() => router.push(`/room/JMP-TENANT-MATRIX`)}
className="group p-6 rounded-3xl border border-amber-500/20 bg-amber-500/5 hover:bg-amber-500/10 transition-all flex flex-col items-center gap-3"
>
<div className="w-12 h-12 rounded-xl bg-amber-500/20 flex items-center justify-center text-amber-500 group-hover:bg-amber-500 group-hover:text-black transition-all">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path></svg>
</div>
<span className="font-bold text-[10px] uppercase tracking-tighter text-center">Master Room</span>
</button>
</div>
</div>
</div>
</main>
{/* Mobile Bottom Navigation - 100% Mobile Compatible */}
<nav className="fixed bottom-4 left-4 right-4 md:hidden glass-panel rounded-2xl p-2 z-50 border-white/10 shadow-[0_-10px_40px_rgba(0,0,0,0.5)] flex justify-around items-center">
<button onClick={() => window.location.href = '/dashboard'} className="flex flex-col items-center gap-1 p-2 text-gray-400">
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></svg>
<span className="text-[9px] font-bold uppercase">Home</span>
</button>
<button onClick={() => window.location.href = '/c'} className="flex flex-col items-center gap-1 p-2 text-gray-400">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path></svg>
<span className="text-[9px] font-bold uppercase">Chat</span>
</button>
<button onClick={() => window.location.href = '/vc'} className="flex flex-col items-center gap-1 p-2 text-red-500">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
<span className="text-[9px] font-bold uppercase">Meet</span>
</button>
</nav>
{/* Ultra Footer */}
<footer className="text-center py-10 opacity-30 mt-auto">
<p className="text-[10px] font-mono tracking-[0.2em] uppercase">Jumpa.ID Meet Absolute Zero Engine v2.4</p>
</footer>
{/* Copy Success Alert */}
{copySuccess && (
<div className="absolute top-20 left-1/2 -translate-x-1/2 bg-green-500/90 text-white px-6 py-3 rounded-full font-medium shadow-xl backdrop-blur-md flex items-center gap-2 animate-in fade-in slide-in-from-top-5 z-50">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path></svg>
Invite Link Copied! Joining meeting...
</div>
)}
{/* Join Modal */}
{showJoinModal && (
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md">
<div className="bg-[#111b21] p-8 rounded-3xl border border-white/10 shadow-2xl w-[400px] animate-in zoom-in-95 duration-200">
<h2 className="text-2xl font-bold mb-2 text-white">Join Meeting</h2>
<p className="text-gray-400 text-sm mb-6">Enter meeting ID or personal link name</p>
<form onSubmit={handleJoinSubmit}>
<input
type="text"
value={roomName}
onChange={(e) => setRoomName(e.target.value.toUpperCase())}
placeholder="Meeting ID (e.g. BOARD-ROOM)"
className="w-full bg-[#0a101d] text-white border border-white/10 rounded-xl px-4 py-3.5 mb-6 focus:outline-none focus:border-brand focus:ring-1 focus:ring-brand"
autoFocus
required
/>
<div className="flex gap-3">
<button type="button" onClick={() => setShowJoinModal(false)} className="flex-1 py-3 text-gray-300 font-medium bg-white/5 hover:bg-white/10 rounded-xl transition-colors">Cancel</button>
<button type="submit" className="flex-1 py-3 text-black font-bold bg-brand hover:bg-brand/90 rounded-xl transition-colors">Join</button>
</div>
</form>
</div>
</div>
)}
{/* CHRONOS SMART SCHEDULER MODAL */}
{showChronosModal && (
<div className="fixed inset-0 z-600 flex items-center justify-center bg-black/90 backdrop-blur-md p-4">
<div className="bg-[#111b21] w-full max-w-[700px] h-[80vh] rounded-2xl shadow-2xl border border-pink-500/50 flex flex-col overflow-hidden animate-in zoom-in duration-200">
<div className="bg-[#202c33] px-6 py-4 border-b border-gray-800 flex justify-between items-center">
<h3 className="text-pink-400 font-bold text-lg flex items-center gap-2">
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z"/></svg>
Chronos Smart Scheduler (Tenant Center)
</h3>
<button title="Aksi" onClick={() => setShowChronosModal(false)} className="text-gray-400 hover:text-white transition-colors">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<div className="p-6 overflow-y-auto flex-1 custom-scroll">
<div className="grid grid-cols-3 gap-4 mb-8">
<div onClick={() => setRoomMode('standard')} className={`cursor-pointer p-4 rounded-xl border flex flex-col items-center gap-2 transition-all ${roomMode === 'standard' ? 'bg-blue-600/20 border-blue-500 text-blue-400' : 'bg-[#202c33] border-gray-600 text-gray-400 hover:border-gray-400'}`}>
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>
<span className="font-bold text-xs md:text-sm text-center">Standard VC</span>
</div>
<div onClick={() => setRoomMode('webinar')} className={`cursor-pointer p-4 rounded-xl border flex flex-col items-center gap-2 transition-all ${roomMode === 'webinar' ? 'bg-pink-600/20 border-pink-500 text-pink-400' : 'bg-[#202c33] border-gray-600 text-gray-400 hover:border-gray-400'}`}>
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24"><path d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM8 15h8v-2H8v2zm0-4h8V9H8v2z"/></svg>
<span className="font-bold text-xs md:text-sm text-center">Holo-Stage Webinar</span>
</div>
<div onClick={() => setRoomMode('podcast')} className={`cursor-pointer p-4 rounded-xl border flex flex-col items-center gap-2 transition-all ${roomMode === 'podcast' ? 'bg-amber-600/20 border-amber-500 text-amber-500' : 'bg-[#202c33] border-gray-600 text-gray-400 hover:border-gray-400'}`}>
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/></svg>
<span className="font-bold text-xs md:text-sm text-center">Studio Podcast</span>
</div>
</div>
<div className="bg-[#202c33] p-4 rounded-xl border border-gray-600 mb-6">
<h4 className="text-white font-bold mb-3">Jadwalkan Ruangan (Smart Timeline)</h4>
{!chronoResult ? (
<div className="flex flex-col md:flex-row gap-2">
<input title="Masukan"
type="datetime-local"
value={chronoDate}
onChange={(e) => setChronoDate(e.target.value)}
className="flex-1 bg-[#111b21] text-white px-4 py-2 rounded-lg border border-gray-700 focus:outline-none focus:border-pink-500"
/>
<input
type="text"
placeholder="Nama Ruangan..."
value={chronoName}
onChange={(e) => setChronoName(e.target.value)}
className="flex-1 bg-[#111b21] text-white px-4 py-2 rounded-lg border border-gray-700 focus:outline-none focus:border-pink-500"
/>
<button
onClick={() => {
if (chronoDate && chronoName) {
const uuid = crypto.randomUUID().toUpperCase().split('-');
const newRoomId = `JMP-SCH-${uuid[1]}-${uuid[2]}`;
const inviteLink = `/vc/room/${newRoomId}`;
const inviteText = `Panggilan JUMPA.ID Terjadwal\nTopik: ${chronoName}\nWaktu: ${chronoDate}\n\nTautan Rapat:\n${inviteLink}\nMeeting ID: ${newRoomId}`;
setChronoResult({ link: inviteLink, text: inviteText });
// Tambahkan ke state schedules untuk ditampilkan di list bawah
setSchedules(prev => [...prev, {
id: newRoomId,
title: chronoName,
mode: roomMode,
date: chronoDate,
link: inviteLink
}]);
} else {
alert("Mohon isi tanggal dan nama ruangan.");
}
}}
className="bg-pink-600 hover:bg-pink-500 text-white px-6 py-2 rounded-lg font-bold"
>
JADWALKAN
</button>
</div>
) : (
<div className="bg-green-500/10 border border-green-500/30 p-4 rounded-lg mt-2">
<h5 className="text-green-400 font-bold mb-2 text-sm flex items-center gap-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7"></path></svg>
Berhasil Dijadwalkan!
</h5>
<textarea title="Teks"
readOnly
value={chronoResult.text}
className="w-full h-24 bg-black/50 text-gray-300 text-xs p-2 rounded-lg border border-white/5 resize-none outline-none custom-scroll mb-2"></textarea>
<div className="flex gap-2">
<button onClick={() => {
navigator.clipboard.writeText(chronoResult.text);
alert("Teks undangan berhasil disalin!");
}} className="flex-1 py-1.5 text-black font-bold bg-brand hover:bg-brand/90 rounded-lg text-xs">Copy Invitation</button>
<button onClick={() => { setChronoResult(null); setChronoDate(""); setChronoName(""); }} className="px-4 py-1.5 text-white font-medium bg-white/10 hover:bg-white/20 rounded-lg text-xs">Tutup</button>
</div>
</div>
)}
</div>
<div className="space-y-3">
<h4 className="text-gray-400 font-bold text-sm">3D TIMELINE SCHEDULES</h4>
{schedules.length === 0 ? (
<div className="text-center py-6 text-gray-500 text-sm">Belum ada ruangan yang dijadwalkan.</div>
) : (
schedules.map((sch) => (
<div key={sch.id} className="bg-linear-to-r from-[#202c33] to-[#111b21] p-4 rounded-xl border border-gray-700 flex justify-between items-center group">
<div>
<div className="flex items-center gap-2">
<span className="bg-pink-500 text-white text-[10px] px-2 py-0.5 rounded font-bold uppercase">{sch.mode}</span>
<span className="text-white font-bold">{sch.title}</span>
</div>
<div className="text-gray-400 text-sm mt-1">{new Date(sch.date).toLocaleString('id-ID')}</div>
</div>
<div className="flex gap-2">
<button onClick={() => { navigator.clipboard.writeText(`Gabung Rapat: ${sch.link}`); alert('Link disalin!'); }} className="bg-white/10 hover:bg-white/20 text-white px-3 py-2 rounded-lg text-xs font-bold md:opacity-0 group-hover:opacity-100 transition-opacity">COPY LINK</button>
<button
onClick={() => {
setShowChronosModal(false);
router.push(`/room/${sch.id}`);
}}
className="bg-brand/20 hover:bg-brand text-brand hover:text-black border border-brand/50 px-3 py-2 rounded-lg text-xs font-bold transition-all"
>
START
</button>
</div>
</div>
))
)}
</div>
</div>
</div>
</div>
)}
{/* THE VAULT (RECORDINGS) MODAL */}
{showVaultModal && (
<div className="fixed inset-0 z-600 flex items-center justify-center bg-black/90 backdrop-blur-md p-4">
<div className="bg-[#111b21] w-full max-w-[800px] h-[85vh] rounded-2xl shadow-2xl border border-teal-500/50 flex flex-col overflow-hidden animate-in zoom-in duration-200">
<div className="bg-[#202c33] px-6 py-4 border-b border-gray-800 flex justify-between items-center">
<h3 className="text-teal-400 font-bold text-lg flex items-center gap-2">
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/></svg>
The Vault (Quantum Recordings)
</h3>
<button title="Aksi" onClick={() => setShowVaultModal(false)} className="text-gray-400 hover:text-white transition-colors">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<div className="p-6 overflow-y-auto flex-1 custom-scroll flex flex-col gap-4">
<div className="bg-[#202c33] rounded-xl overflow-hidden border border-gray-700 hover:border-teal-500 transition-colors flex flex-col items-center justify-center text-center p-6 border-dashed relative">
<svg className="w-12 h-12 text-teal-500 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
<h4 className="text-white font-bold mb-2">Secure Local Player</h4>
<p className="text-gray-400 text-sm mb-4">Pilih file rekaman hasil <strong className="text-teal-400">Quantum Record</strong> (.webm / .mp4) dari perangkat Anda. Video diputar 100% lokal tanpa diunggah ke internet.</p>
<input
type="file"
accept="video/webm, video/mp4, video/x-matroska"
className="hidden"
id="vault-file-upload"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
if (vaultVideoUrl) URL.revokeObjectURL(vaultVideoUrl);
const url = URL.createObjectURL(file);
setVaultVideoUrl(url);
setVaultVideoName(file.name);
}
}}
/>
<label
htmlFor="vault-file-upload"
className="cursor-pointer bg-teal-600 hover:bg-teal-500 text-white px-6 py-2.5 rounded-lg font-bold shadow-lg shadow-teal-500/20 transition-all"
>
Buka Rekaman Lokal
</label>
</div>
{vaultVideoUrl && (
<div className="bg-[#111b21] rounded-xl overflow-hidden border border-teal-500 shadow-xl shadow-teal-500/10 flex flex-col animate-in fade-in zoom-in-95">
<div className="bg-teal-900/20 px-4 py-3 border-b border-teal-500/30 flex justify-between items-center">
<span className="text-teal-300 font-mono text-xs truncate max-w-[70%]">📄 {vaultVideoName}</span>
<button
onClick={() => {
URL.revokeObjectURL(vaultVideoUrl);
setVaultVideoUrl(null);
setVaultVideoName("");
}}
className="text-red-400 hover:text-red-300 text-xs font-bold uppercase"
>
Tutup Video
</button>
</div>
<div className="bg-black aspect-video flex items-center justify-center">
<video
src={vaultVideoUrl}
controls
autoPlay
className="w-full h-full object-contain outline-none"
></video>
</div>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
}