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:
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user