Feature: Completar sistema de votación de misiones
- Cambiar onClick para usar handleMissionVote - Crear componentes MissionReveal y MissionResult - Importar componentes en GameBoard - Tracking de votos con feedback visual - Usar imágenes vote_approve y vote_reject Resuelve error: MissionReveal is not defined
This commit is contained in:
@@ -2,6 +2,8 @@ import { useState, useEffect } from 'react';
|
|||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { GameState, GamePhase, Player, GAME_CONFIG } from '../../../shared/types';
|
import { GameState, GamePhase, Player, GAME_CONFIG } from '../../../shared/types';
|
||||||
|
import MissionReveal from './MissionReveal';
|
||||||
|
import MissionResult from './MissionResult';
|
||||||
|
|
||||||
interface GameBoardProps {
|
interface GameBoardProps {
|
||||||
gameState: GameState;
|
gameState: GameState;
|
||||||
@@ -517,7 +519,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
|||||||
{cardOrder ? (
|
{cardOrder ? (
|
||||||
<>
|
<>
|
||||||
{/* Carta de Éxito primero */}
|
{/* Carta de Éxito primero */}
|
||||||
<button onClick={() => actions.voteMission(true)} className="group">
|
<button onClick={() => handleMissionVote(true)} className="group">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-64 h-96 bg-gradient-to-br from-blue-600 to-blue-900 rounded-2xl shadow-2xl border-4 border-blue-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:rotate-3 hover:shadow-blue-500/50"
|
className="w-64 h-96 bg-gradient-to-br from-blue-600 to-blue-900 rounded-2xl shadow-2xl border-4 border-blue-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:rotate-3 hover:shadow-blue-500/50"
|
||||||
whileHover={{ scale: 1.1, rotate: 3 }}
|
whileHover={{ scale: 1.1, rotate: 3 }}
|
||||||
@@ -530,7 +532,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
|||||||
|
|
||||||
{/* Carta de Sabotaje segundo (solo para espías) */}
|
{/* Carta de Sabotaje segundo (solo para espías) */}
|
||||||
{currentPlayer?.faction === 'spies' && (
|
{currentPlayer?.faction === 'spies' && (
|
||||||
<button onClick={() => actions.voteMission(false)} className="group">
|
<button onClick={() => handleMissionVote(false)} className="group">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
||||||
whileHover={{ scale: 1.1, rotate: -3 }}
|
whileHover={{ scale: 1.1, rotate: -3 }}
|
||||||
@@ -546,7 +548,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
|||||||
<>
|
<>
|
||||||
{/* Carta de Sabotaje primero (solo para espías) */}
|
{/* Carta de Sabotaje primero (solo para espías) */}
|
||||||
{currentPlayer?.faction === 'spies' && (
|
{currentPlayer?.faction === 'spies' && (
|
||||||
<button onClick={() => actions.voteMission(false)} className="group">
|
<button onClick={() => handleMissionVote(false)} className="group">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
className="w-64 h-96 bg-gradient-to-br from-red-600 to-red-900 rounded-2xl shadow-2xl border-4 border-red-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:-rotate-3 hover:shadow-red-500/50"
|
||||||
whileHover={{ scale: 1.1, rotate: -3 }}
|
whileHover={{ scale: 1.1, rotate: -3 }}
|
||||||
@@ -559,7 +561,7 @@ export default function GameBoard({ gameState, currentPlayerId, actions }: GameB
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Carta de Éxito segundo */}
|
{/* Carta de Éxito segundo */}
|
||||||
<button onClick={() => actions.voteMission(true)} className="group">
|
<button onClick={() => handleMissionVote(true)} className="group">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="w-64 h-96 bg-gradient-to-br from-blue-600 to-blue-900 rounded-2xl shadow-2xl border-4 border-blue-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:rotate-3 hover:shadow-blue-500/50"
|
className="w-64 h-96 bg-gradient-to-br from-blue-600 to-blue-900 rounded-2xl shadow-2xl border-4 border-blue-400 flex flex-col items-center justify-center p-6 transform transition-all hover:scale-110 hover:rotate-3 hover:shadow-blue-500/50"
|
||||||
whileHover={{ scale: 1.1, rotate: 3 }}
|
whileHover={{ scale: 1.1, rotate: 3 }}
|
||||||
|
|||||||
32
client/src/components/MissionResult.tsx
Normal file
32
client/src/components/MissionResult.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
interface MissionResultProps {
|
||||||
|
success: boolean;
|
||||||
|
successes: number;
|
||||||
|
fails: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MissionResult({ success, successes, fails }: MissionResultProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="fixed inset-0 flex items-center justify-center bg-black/90 z-50"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
>
|
||||||
|
<div className="text-center">
|
||||||
|
<motion.h2
|
||||||
|
className={`text-6xl font-bold mb-8 ${success ? 'text-blue-500' : 'text-red-500'}`}
|
||||||
|
initial={{ scale: 0 }}
|
||||||
|
animate={{ scale: 1 }}
|
||||||
|
transition={{ type: 'spring', stiffness: 200 }}
|
||||||
|
>
|
||||||
|
{success ? '¡MISIÓN EXITOSA!' : 'MISIÓN FALLIDA'}
|
||||||
|
</motion.h2>
|
||||||
|
<div className="text-white text-2xl">
|
||||||
|
<p>Éxitos: {successes}</p>
|
||||||
|
<p>Sabotajes: {fails}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
client/src/components/MissionReveal.tsx
Normal file
33
client/src/components/MissionReveal.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
interface MissionRevealProps {
|
||||||
|
votes: boolean[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MissionReveal({ votes }: MissionRevealProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="fixed inset-0 flex items-center justify-center bg-black/90 z-50"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
>
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-8">Resultados de la Misión</h2>
|
||||||
|
<div className="flex gap-4 justify-center">
|
||||||
|
{votes.map((vote, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={idx}
|
||||||
|
className={`w-24 h-32 rounded-lg flex items-center justify-center text-4xl ${vote ? 'bg-blue-600' : 'bg-red-600'
|
||||||
|
}`}
|
||||||
|
initial={{ scale: 0, rotate: -180 }}
|
||||||
|
animate={{ scale: 1, rotate: 0 }}
|
||||||
|
transition={{ delay: idx * 0.3 }}
|
||||||
|
>
|
||||||
|
{vote ? '✓' : '✗'}
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user