diff --git a/client/src/app/dashboard/page.tsx b/client/src/app/dashboard/page.tsx index 7f98966..1b2a53e 100644 --- a/client/src/app/dashboard/page.tsx +++ b/client/src/app/dashboard/page.tsx @@ -3,9 +3,9 @@ import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { useSocket } from '../../hooks/useSocket'; -import { Shield, Users, Gamepad2, XCircle, LogOut, Clock, History, AlertTriangle, UserMinus } from 'lucide-react'; +import { Shield, Users, Gamepad2, LogOut, Clock, History, UserMinus, Key } from 'lucide-react'; -const ADMIN_PASSWORD = "admin123"; // En producción esto debería estar en .env y validarse en servidor +const ADMIN_PASSWORD = "admin123"; export default function Dashboard() { const { socket, isConnected } = useSocket(); @@ -15,17 +15,29 @@ export default function Dashboard() { const [gameHistory, setGameHistory] = useState([]); const [error, setError] = useState(''); - // Cargar datos + // Comprobar sesión al cargar + useEffect(() => { + const savedSession = localStorage.getItem('resistencia_admin_session'); + if (savedSession === 'active') { + setIsAuthenticated(true); + } + }, []); + + // Cargar datos y escuchar actualizaciones en tiempo real useEffect(() => { if (isAuthenticated && socket) { + // Solicitar datos iniciales socket.emit('admin_get_data'); + // Escuchar actualizaciones (el servidor emite a admin-room) socket.on('admin_data', (data: any) => { + console.log('[ADMIN] Datos recibidos:', data); setActiveGames(data.activeGames); setGameHistory(data.history); }); socket.on('admin_action_success', () => { + console.log('[ADMIN] Acción realizada con éxito'); socket.emit('admin_get_data'); }); @@ -40,12 +52,19 @@ export default function Dashboard() { e.preventDefault(); if (password === ADMIN_PASSWORD) { setIsAuthenticated(true); + localStorage.setItem('resistencia_admin_session', 'active'); setError(''); } else { setError('Acceso Denegado: Contraseña incorrecta'); } }; + const handleLogout = () => { + localStorage.removeItem('resistencia_admin_session'); + setIsAuthenticated(false); + setPassword(''); + }; + const closeGame = (roomId: string) => { if (confirm('¿Seguro que quieres forzar el cierre de esta partida?')) { socket?.emit('admin_close_game', { roomId }); @@ -62,32 +81,49 @@ export default function Dashboard() { return (
-
- -
-

Acceso Restringido

-

Introduce la clave de acceso al Puesto de Mando

+
-
-
+
+
+ +
+ +

Acceso Admin

+

Panel de Control de La Resistencia

+ + +
+ setPassword(e.target.value)} - placeholder="Clave de Operaciones" - className="w-full bg-[#1a1a20] border border-white/5 rounded-xl px-5 py-4 text-white focus:outline-none focus:ring-2 focus:ring-red-500/50 transition-all placeholder:text-gray-600" + placeholder="Introduce la contraseña" + className="w-full bg-[#1a1a20] border border-white/5 rounded-2xl pl-12 pr-6 py-4 text-white focus:outline-none focus:ring-2 focus:ring-red-600/50 transition-all placeholder:text-gray-600 font-medium" />
- {error &&

{error}

} + + {error && ( + + + {error} + + )} + @@ -96,111 +132,133 @@ export default function Dashboard() { } return ( -
+
{/* Header */} -
-
-
- - S.S.O Dashboard +
+
+
+
+ +
+ Seguimiento de Operaciones
-

Centro de Operaciones

+

Comandante

-
+
- Servidor + Status Servidor
-
- {isConnected ? 'ONLINE' : 'OFFLINE'} +
+ {isConnected ? 'OPERATIVO' : 'DESCONECTADO'}
+ +
+
-
+
- {/* Panel Central: Partidas Activas */} -
+ {/* Panel Latino: Estadísticas Rápidas */} +
+ {[ + { label: 'Partidas Activas', value: activeGames.length, color: 'text-red-500', icon: Gamepad2 }, + { label: 'Agentes Online', value: activeGames.reduce((acc, g) => acc + g.currentPlayers, 0), color: 'text-blue-400', icon: Users }, + { label: 'Misiones Registradas', value: gameHistory.length, color: 'text-orange-400', icon: History }, + { label: 'Uso de CPU', value: '4%', color: 'text-green-400', icon: Clock } + ].map((stat, i) => ( +
+
+

{stat.label}

+

{stat.value}

+
+ +
+ ))} +
+ + {/* Columna Principal: Partidas Activas */} +
-
-

- - Operaciones en Curso -

- - {activeGames.length} Activas - +
+

Canales de Radio Activos

-
- +
+ {activeGames.length === 0 ? ( -
- -

No hay misiones ejecutándose en este momento

-
+ + +

Silencio Radioeléctrico

+

Buscando señales de misiones activas...

+
) : ( activeGames.map((game: any) => ( -
- -
-
-

{game.name}

-
- - - {game.currentPlayers}/{game.maxPlayers} Agentes +
+
+
+

{game.name}

+ {game.status} +
+
+ + + {game.currentPlayers} / {game.maxPlayers} Agentes Movilizados - - {game.status} + | + ID: {game.id.slice(0, 8)}
-
- -
+
- {/* Jugadores en esta partida */} -
-
+ {/* Subpanel: Jugadores */} +
+
{game.players.map((player: any) => ( -
+
-
- {player.name[0]} +
+ {player.name[0].toUpperCase()}
-

{player.name}

-

{player.id.slice(0, 8)}

+

{player.name}

+

AG-{player.id.slice(0, 4)}

))} @@ -214,47 +272,76 @@ export default function Dashboard() {
- {/* Lateral: Historial */} -
-
-
-

- - Archivo de Guerra -

+ {/* Columna Lateral: Archivo Histórico */} +
+
+
+

Informe Forense

-
- {gameHistory.length === 0 ? ( -
-

No hay registros históricos

-
- ) : ( - gameHistory.map((entry: any) => ( -
-
-

{entry.room_name}

- - {entry.winner || 'Abortada'} - -
-
-

{entry.players}

-

{new Date(entry.created_at).toLocaleString()}

-
+
+
+

Últimos Informes Desclasificados

+
+ +
+ {gameHistory.length === 0 ? ( +
+ +

Sin archivos registrados

- )) - )} + ) : ( + gameHistory.map((entry: any) => ( + +
+
+

{entry.room_name}

+
+ {entry.host_name} + + {new Date(entry.created_at).toLocaleDateString()} +
+
+
+ {entry.winner ? (entry.winner === 'resistance' ? 'RES' : 'SPIES') : 'LOGOUT'} +
+
+
+ {entry.players.split(',').length} Agtes +
+ {new Date(entry.created_at).toLocaleTimeString()} +
+
+
+ )) + )} +
+ +
+

Fin del Informe Forense

+