[TSM.ID].[11031972] PXE : Platform X Ecosystem I [118 Module -LIVE-]
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable */
|
||||
// [TSM.ID].[11031972] -- All Rights Reserved. Proprietary & Confidential.
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { io, Socket } from "../../../lib/zero-socket";
|
||||
|
||||
export default function GuestPortal() {
|
||||
const params = useParams();
|
||||
const room = params.room as string;
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const [pin, setPin] = useState("");
|
||||
const [status, setStatus] = useState<"idle" | "knocking" | "approved" | "denied">("idle");
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const newSocket = io(window.location.origin, {
|
||||
path: '/c/socket.io',
|
||||
transports: ['websocket']
|
||||
});
|
||||
setSocket(newSocket);
|
||||
|
||||
newSocket.on("guest_approved", () => {
|
||||
setStatus("approved");
|
||||
// Set a temporary cookie or local storage to bypass auth in the Chat UI
|
||||
// In this specific implementation, we will just redirect to the room with a temporary token
|
||||
// For simplicity, we just change the state to 'approved' and render the chat UI here or redirect
|
||||
window.location.href = `/?guest_room=${encodeURIComponent(room)}&guest_name=${encodeURIComponent(name)}&guest_token=true`;
|
||||
});
|
||||
|
||||
newSocket.on("guest_denied", () => {
|
||||
setStatus("denied");
|
||||
newSocket.disconnect();
|
||||
});
|
||||
|
||||
return () => { newSocket.disconnect(); };
|
||||
}, [room, name]);
|
||||
|
||||
const handleKnock = async () => {
|
||||
if (!name.trim() || !pin.trim()) return alert("Nama dan PIN wajib diisi!");
|
||||
if (!socket) return;
|
||||
|
||||
// Validasi PIN ke API sebelum mengetuk
|
||||
try {
|
||||
setStatus("knocking");
|
||||
const resp = await fetch('/c/api/guest/knock', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ room, pin, name })
|
||||
});
|
||||
const data = await resp.json();
|
||||
|
||||
if (data.success) {
|
||||
// PIN Valid, sekarang server menembakkan notifikasi ke Host
|
||||
socket.emit('guest_knock', { room, name, pinId: data.pinId });
|
||||
} else {
|
||||
alert(data.error || "PIN tidak valid atau sudah kedaluwarsa.");
|
||||
setStatus("idle");
|
||||
}
|
||||
} catch (e) {
|
||||
alert("Terjadi kesalahan.");
|
||||
setStatus("idle");
|
||||
}
|
||||
};
|
||||
|
||||
if (status === "approved") {
|
||||
return <div className="min-h-screen bg-[#050b14] flex items-center justify-center text-white">Memasuki Ruangan...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#050b14] flex items-center justify-center p-4 relative overflow-hidden">
|
||||
{/* Background Decorators */}
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-brand rounded-full mix-blend-screen filter blur-[200px] opacity-10 animate-pulse"></div>
|
||||
|
||||
<div className="w-full max-w-md bg-[#111b21]/80 backdrop-blur-xl border border-gray-700/50 p-8 rounded-3xl shadow-2xl z-10">
|
||||
<div className="flex flex-col items-center mb-8">
|
||||
<div className="w-16 h-16 bg-brand/20 rounded-full flex items-center justify-center mb-4 border border-brand/30">
|
||||
<svg className="w-8 h-8 text-brand" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"></path></svg>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-white mb-2">Akses Tamu (Guest)</h1>
|
||||
<p className="text-gray-400 text-center text-sm">
|
||||
Anda diundang ke ruangan: <br/>
|
||||
<strong className="text-brand block mt-1 text-lg">{decodeURIComponent(room)}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{status === "idle" && (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-gray-400 text-xs font-bold mb-2 uppercase tracking-wider">Nama Lengkap / Instansi</label>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Contoh: Budi (Kemenkes)"
|
||||
className="w-full bg-[#202c33] text-white px-4 py-3 rounded-xl outline-none focus:ring-2 focus:ring-brand border border-transparent transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-400 text-xs font-bold mb-2 uppercase tracking-wider">PIN Masuk (6-Digit)</label>
|
||||
<input
|
||||
type="text"
|
||||
maxLength={6}
|
||||
value={pin}
|
||||
onChange={(e) => setPin(e.target.value.replace(/\D/g, ''))}
|
||||
placeholder="000000"
|
||||
className="w-full bg-[#202c33] text-white px-4 py-3 rounded-xl outline-none focus:ring-2 focus:ring-brand border border-transparent transition-all text-center tracking-[0.5em] font-mono text-xl"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleKnock}
|
||||
disabled={!name || pin.length !== 6}
|
||||
className="w-full mt-6 py-4 bg-brand hover:bg-opacity-90 text-black font-extrabold rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-[0_0_20px_rgba(0,255,136,0.2)] hover:shadow-[0_0_30px_rgba(0,255,136,0.4)]">
|
||||
Ketuk Pintu (Minta Akses)
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "knocking" && (
|
||||
<div className="flex flex-col items-center py-8 text-center">
|
||||
<div className="w-16 h-16 border-4 border-gray-700 border-t-[var(--color-brand)] rounded-full animate-spin mb-6"></div>
|
||||
<h3 className="text-xl font-bold text-white mb-2">Menunggu Tuan Rumah...</h3>
|
||||
<p className="text-gray-400">PIN Anda valid. Host sedang meninjau permintaan Anda untuk masuk ke ruangan.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "denied" && (
|
||||
<div className="flex flex-col items-center py-8 text-center">
|
||||
<div className="w-16 h-16 bg-red-500/20 rounded-full flex items-center justify-center mb-6 border border-red-500/50">
|
||||
<svg className="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12"></path></svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-red-500 mb-2">Akses Ditolak</h3>
|
||||
<p className="text-gray-400">Tuan rumah menolak permintaan Anda untuk masuk ke ruangan ini.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user