feat(ui): Enhance responsive design and game flow

- Intro: Change title to 'Sombras en París'
- Roll Call: Make screen fully responsive with fixed header
- Team Building: Clean up leader UI and make player tokens responsive
- Mission History: Fix expand/collapse interaction (z-index issue)
This commit is contained in:
Resistencia Dev
2025-12-12 19:14:14 +01:00
parent 1a7b667c77
commit c67f97845a
2 changed files with 43 additions and 33 deletions

View File

@@ -120,7 +120,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
</div> </div>
<h1 className="z-10 text-5xl font-bold uppercase tracking-[0.3em] mb-8 text-yellow-500 drop-shadow-lg text-center"> <h1 className="z-10 text-5xl font-bold uppercase tracking-[0.3em] mb-8 text-yellow-500 drop-shadow-lg text-center">
Guerra Total Sombras en París
</h1> </h1>
{/* Audio Auto-Play - Solo para el host */} {/* Audio Auto-Play - Solo para el host */}
@@ -237,7 +237,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
// FASE ROLL CALL // FASE ROLL CALL
if (gameState.phase === 'roll_call' as any) { if (gameState.phase === 'roll_call' as any) {
return ( return (
<div className="relative w-full h-screen flex flex-col items-center justify-center bg-black overflow-hidden text-white font-mono"> <div className="relative w-full h-screen flex flex-col bg-black overflow-hidden text-white font-mono">
<div className="absolute inset-0 z-0"> <div className="absolute inset-0 z-0">
@@ -245,11 +245,15 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
<div className="absolute inset-0 bg-black/70" /> <div className="absolute inset-0 bg-black/70" />
</div> </div>
<div className="z-10 w-full max-w-5xl px-4"> {/* --- 1. SECCIÓN SUPERIOR: TÍTULO (20-25% altura) --- */}
<h2 className="text-3xl text-center mb-12 uppercase tracking-[0.2em] text-gray-300 border-b border-gray-600 pb-4"> <div className="relative z-10 w-full h-[20vh] flex items-center justify-center px-4 border-b border-gray-600/50 bg-black/20 backdrop-blur-sm">
<h2 className="text-2xl md:text-3xl lg:text-4xl text-center uppercase tracking-[0.2em] text-gray-300 drop-shadow-lg">
Pasando Lista... Pasando Lista...
</h2> </h2>
</div>
{/* --- 2. SECCIÓN INFERIOR: JUGADORES (Resto de altura) --- */}
<div className="relative z-10 w-full flex-1 overflow-y-auto p-4 flex flex-col items-center">
{isHost && ( {isHost && (
<audio <audio
src="/assets/audio/Rondas.ogg" src="/assets/audio/Rondas.ogg"
@@ -258,7 +262,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
/> />
)} )}
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8"> <div className="w-full max-w-6xl grid grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3 md:gap-8 justify-items-center content-center py-4">
{gameState.players.map((p, i) => { {gameState.players.map((p, i) => {
return ( return (
<motion.div <motion.div
@@ -266,9 +270,10 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ delay: i * 0.3 }} // Aparecen uno a uno transition={{ delay: i * 0.3 }} // Aparecen uno a uno
className="flex flex-col items-center gap-3" className="flex flex-col items-center gap-1 md:gap-3 w-full"
> >
<div className="w-32 h-32 rounded-full border-4 border-gray-400 overflow-hidden relative shadow-2xl bg-black"> {/* Avatar Responsive */}
<div className="w-20 h-20 md:w-32 md:h-32 rounded-full border-2 md:border-4 border-gray-400 overflow-hidden relative shadow-2xl bg-black">
<Image <Image
src={`/assets/images/characters/${p.avatar}`} src={`/assets/images/characters/${p.avatar}`}
alt="Avatar" alt="Avatar"
@@ -276,7 +281,8 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
className="object-cover grayscale contrast-125" className="object-cover grayscale contrast-125"
/> />
</div> </div>
<div className="bg-black/80 px-4 py-1 rounded border border-white/20 text-xl font-bold text-yellow-500 uppercase"> {/* Nombre Responsive */}
<div className="bg-black/80 px-2 py-0.5 md:px-4 md:py-1 rounded border border-white/20 text-xs md:text-xl font-bold text-yellow-500 uppercase text-center w-full truncate max-w-[120px] md:max-w-none">
{p.name} {p.name}
</div> </div>
</motion.div> </motion.div>
@@ -502,27 +508,31 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
className="flex flex-col items-center gap-6 w-full max-w-4xl" className="flex flex-col items-center gap-6 w-full max-w-4xl"
> >
{/* Información del líder */} {/* Información del líder */}
<div className="bg-yellow-600/90 text-black p-4 rounded-lg shadow-xl border-4 border-yellow-400 w-full text-center"> {/* Información del líder - SOLO para NO líderes */}
<div className="flex items-center justify-center gap-3"> {!isLeader && (
<Image src="/assets/images/tokens/token_leader.png" alt="Leader" width={40} height={40} /> <div className="bg-yellow-600/90 text-black p-2 rounded-lg shadow-xl border-4 border-yellow-400 w-full text-center mb-2">
<div> <div className="flex items-center justify-center gap-3">
<div className="text-sm uppercase tracking-wider font-bold">Líder Actual</div> <div>
<div className="text-2xl font-bold"> <div className="text-xs uppercase tracking-wider font-bold">Líder Actual</div>
{gameState.players.find(p => p.id === gameState.currentLeaderId)?.name || 'Desconocido'} <div className="text-xl font-bold">
{gameState.players.find(p => p.id === gameState.currentLeaderId)?.name || 'Desconocido'}
</div>
</div> </div>
</div> </div>
<Image src="/assets/images/tokens/token_leader.png" alt="Leader" width={40} height={40} />
</div> </div>
</div> )}
{/* Mensaje para el líder o para los demás */} {/* Mensaje para el líder o para los demás */}
<div className="bg-paper-bg text-black p-6 rounded shadow-2xl rotate-1 w-full text-center"> {/* Mensaje para el líder o para los demás */}
<h2 className="text-2xl font-bold font-mono mb-2 uppercase text-resistance-blue"> <div className="bg-paper-bg text-black p-4 md:p-6 rounded shadow-2xl w-full text-center">
<h2 className="text-xl md:text-2xl font-bold font-mono mb-2 uppercase text-resistance-blue">
{isLeader ? '🎯 TU TURNO: ELIGE TU EQUIPO' : '⏳ ESPERANDO AL LÍDER...'} {isLeader ? '🎯 TU TURNO: ELIGE TU EQUIPO' : '⏳ ESPERANDO AL LÍDER...'}
</h2> </h2>
<p className="mb-4 font-serif italic text-gray-700"> {isLeader && (
Se necesitan <span className="font-bold text-red-700 text-xl">{currentQuestSize} agentes</span> para la misión #{gameState.currentRound}. <p className="mb-4 font-serif italic text-gray-700">
</p> Se necesitan <span className="font-bold text-red-700 text-xl">{currentQuestSize} agentes</span> para la misión #{gameState.currentRound}.
</p>
)}
{/* Contador de seleccionados */} {/* Contador de seleccionados */}
{isLeader && ( {isLeader && (
@@ -790,9 +800,9 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
</AnimatePresence> </AnimatePresence>
</div> </div>
{/* JUGADORES (TIENDA DE CAMPAÑA) */} {/* JUGADORES (TIENDA DE CAMPAÑA - Adaptable) */}
<div className="z-10 w-full overflow-x-auto pb-4"> <div className="z-10 w-full py-2 bg-black/40 backdrop-blur-sm border-t border-white/10">
<div className="flex justify-center gap-4 min-w-max px-4"> <div className="flex flex-wrap justify-center items-center gap-2 md:gap-6 px-2 w-full">
{gameState.players.map((player) => { {gameState.players.map((player) => {
const isSelected = selectedTeam.includes(player.id); const isSelected = selectedTeam.includes(player.id);
const isMe = player.id === currentPlayerId; const isMe = player.id === currentPlayerId;
@@ -811,8 +821,8 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
> >
{/* Avatar */} {/* Avatar */}
<div className={` <div className={`
w-16 h-16 rounded-full border-2 overflow-hidden relative shadow-lg bg-black w-12 h-12 md:w-20 md:h-20 rounded-full border-2 overflow-hidden relative shadow-lg bg-black
${isSelected ? 'border-yellow-400 ring-4 ring-yellow-400/30' : 'border-gray-400'} ${isSelected ? 'border-yellow-400 ring-2 md:ring-4 ring-yellow-400/30' : 'border-gray-400'}
${gameState.currentLeaderId === player.id ? 'ring-2 ring-white' : ''} ${gameState.currentLeaderId === player.id ? 'ring-2 ring-white' : ''}
`}> `}>
<Image <Image
@@ -854,7 +864,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
{/* HISTÓRICO DE MISIONES (Esquina superior derecha) */} {/* HISTÓRICO DE MISIONES (Esquina superior derecha) */}
{gameState.missionHistory.length > 0 && ( {gameState.missionHistory.length > 0 && (
<div className="absolute top-4 right-4 bg-black/80 p-3 rounded-lg border border-white/20 backdrop-blur-sm"> <div className="absolute top-4 right-4 bg-black/80 p-3 rounded-lg border border-white/20 backdrop-blur-sm z-50">
<div className="text-[10px] text-gray-400 uppercase mb-2 text-center font-bold tracking-wider">Historial</div> <div className="text-[10px] text-gray-400 uppercase mb-2 text-center font-bold tracking-wider">Historial</div>
<div className="flex gap-2"> <div className="flex gap-2">
{gameState.missionHistory.map((mission, idx) => { {gameState.missionHistory.map((mission, idx) => {
@@ -864,14 +874,14 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
<div key={idx} className="relative"> <div key={idx} className="relative">
<div <div
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold border-2 cursor-pointer transition-all hover:scale-110 ${mission.isSuccess className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold border-2 cursor-pointer transition-all hover:scale-110 ${mission.isSuccess
? 'bg-blue-600 border-blue-400 text-white' ? 'bg-blue-600 border-blue-400 text-white'
: 'bg-red-600 border-red-400 text-white' : 'bg-red-600 border-red-400 text-white'
} ${isExpanded ? 'ring-2 ring-yellow-400' : ''}`} } ${isExpanded ? 'ring-2 ring-yellow-400 relative z-[60]' : ''}`}
title={`Misión ${mission.round}: ${mission.isSuccess ? 'Éxito' : 'Fracaso'} (${mission.successes} ${mission.fails})`} title={`Misión ${mission.round}: ${mission.isSuccess ? 'Éxito' : 'Fracaso'} (${mission.successes} ${mission.fails})`}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
console.log('Click en misión', idx, 'Estado actual:', expandedMission); console.log('Click en misión', idx);
setExpandedMission(isExpanded ? null : idx); setExpandedMission(prev => prev === idx ? null : idx);
}} }}
> >
{mission.round} {mission.round}