Files
SerpentRace/SerpentRace_Frontend/src/pages/Game/CardDisplayModal.jsx
T

281 lines
10 KiB
React
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from "react"
import { motion, AnimatePresence } from "framer-motion"
/**
* CardDisplayModal - Kártya megjelenítése a játékos számára
*
* @param {Object} props
* @param {boolean} props.isOpen - Modal megjelenítése
* @param {Function} props.onClose - Modal bezárása
* @param {Object} props.card - Kártya adatok
* @param {string} props.cardType - Kártya típusa (QUESTION, LUCK, JOKER)
* @param {Function} props.onSubmitAnswer - Válasz beküldése (csak QUESTION típusnál)
* @param {number} props.timeLimit - Időkorlát másodpercben (default: 60)
*/
const CardDisplayModal = ({
isOpen,
onClose,
card,
cardType = "QUESTION",
onSubmitAnswer,
timeLimit = 60
}) => {
const [playerAnswer, setPlayerAnswer] = useState("")
const [selectedOption, setSelectedOption] = useState(null)
const [timeLeft, setTimeLeft] = useState(timeLimit)
const [isProcessing, setIsProcessing] = useState(false)
// Timer countdown
useEffect(() => {
if (!isOpen || cardType !== "QUESTION") return
setTimeLeft(timeLimit)
const timer = setInterval(() => {
setTimeLeft(prev => {
if (prev <= 1) {
clearInterval(timer)
handleTimeout()
return 0
}
return prev - 1
})
}, 1000)
return () => clearInterval(timer)
}, [isOpen, timeLimit])
// Reset state when modal opens
useEffect(() => {
if (isOpen) {
setPlayerAnswer("")
setSelectedOption(null)
setIsProcessing(false)
}
}, [isOpen])
const handleTimeout = () => {
if (onSubmitAnswer) {
onSubmitAnswer(null) // null = timeout
}
}
const handleSubmit = async () => {
if (isProcessing) return
let answer = null
// Quiz típus - A, B, C, D
if (card?.type === 0 || card?.answerOptions) {
answer = selectedOption
}
// Szöveges válasz
else {
answer = playerAnswer.trim()
}
if (!answer) return
setIsProcessing(true)
try {
await onSubmitAnswer(answer)
} catch (error) {
console.error("Válasz küldési hiba:", error)
}
}
const getCardIcon = () => {
switch (cardType) {
case "QUESTION": return "❓"
case "LUCK": return "🍀"
case "JOKER": return "🃏"
default: return "📝"
}
}
const getCardTitle = () => {
switch (cardType) {
case "QUESTION": return "Feladat Kártya"
case "LUCK": return "Szerencse Kártya"
case "JOKER": return "Joker Kártya"
default: return "Kártya"
}
}
const getCardBgGradient = () => {
switch (cardType) {
case "QUESTION": return "from-blue-600 via-purple-600 to-blue-600"
case "LUCK": return "from-green-600 via-teal-600 to-green-600"
case "JOKER": return "from-purple-600 via-pink-600 to-purple-600"
default: return "from-gray-600 via-gray-700 to-gray-600"
}
}
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins}:${secs.toString().padStart(2, '0')}`
}
const getTimeColor = () => {
if (timeLeft > 30) return "text-green-400"
if (timeLeft > 10) return "text-yellow-400"
return "text-red-400 animate-pulse"
}
if (!isOpen || !card) return null
return (
<AnimatePresence>
{isOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute inset-0 bg-black/80 backdrop-blur-sm"
/>
{/* Modal Content */}
<motion.div
initial={{ scale: 0.9, opacity: 0, y: 20 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
exit={{ scale: 0.9, opacity: 0, y: 20 }}
transition={{ type: "spring", duration: 0.5 }}
className="relative bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 rounded-2xl shadow-2xl border-2 border-purple-500/30 max-w-2xl w-full overflow-hidden"
>
{/* Header */}
<div className={`bg-gradient-to-r ${getCardBgGradient()} p-6 relative overflow-hidden`}>
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-pulse" />
<div className="relative flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="text-5xl animate-bounce">{getCardIcon()}</div>
<div>
<h2 className="text-2xl font-bold text-white">{getCardTitle()}</h2>
{cardType === "QUESTION" && (
<p className="text-white/80 text-sm">Válaszolj a kérdésre!</p>
)}
</div>
</div>
{/* Timer - csak QUESTION típusnál */}
{cardType === "QUESTION" && (
<div className="bg-black/30 rounded-lg px-4 py-2">
<div className={`text-2xl font-bold ${getTimeColor()}`}>
{formatTime(timeLeft)}
</div>
</div>
)}
</div>
</div>
{/* Content */}
<div className="p-6 space-y-6">
{/* Question/Text */}
<div className="bg-gray-800/50 rounded-xl p-5 border border-gray-700">
<div className="flex items-start gap-3">
<div className="text-3xl">📝</div>
<div className="flex-1">
<p className="text-white text-lg leading-relaxed">
{card.question || card.text || card.statement}
</p>
</div>
</div>
</div>
{/* Answer Options - Quiz típus (type: 0) */}
{cardType === "QUESTION" && (card.type === 0 || card.answerOptions) && (
<div className="space-y-3">
<h3 className="text-purple-300 font-semibold">Válaszd ki a helyes választ:</h3>
{card.answerOptions?.map((option, index) => (
<button
key={index}
onClick={() => setSelectedOption(option.answer)}
disabled={isProcessing}
className={`w-full text-left p-4 rounded-lg border-2 transition-all ${
selectedOption === option.answer
? "bg-purple-600 border-purple-400 text-white"
: "bg-gray-800 border-gray-600 text-gray-300 hover:border-purple-500"
} ${isProcessing ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`}
>
<span className="font-bold">{option.answer})</span> {option.text}
</button>
))}
</div>
)}
{/* Text Input - egyéb kérdés típusok */}
{cardType === "QUESTION" && card.type !== 0 && !card.answerOptions && (
<div className="space-y-3">
<h3 className="text-purple-300 font-semibold">Írd be a választ:</h3>
<input
type="text"
value={playerAnswer}
onChange={(e) => setPlayerAnswer(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSubmit()}
disabled={isProcessing}
placeholder="Válaszod..."
className="w-full bg-gray-800 border-2 border-gray-600 rounded-lg px-4 py-3 text-white focus:border-purple-500 focus:outline-none disabled:opacity-50"
/>
</div>
)}
{/* Hint (if available) */}
{card.hint && (
<div className="bg-yellow-900/20 rounded-xl p-4 border border-yellow-500/30">
<div className="flex items-start gap-3">
<div className="text-2xl">💡</div>
<div className="flex-1">
<h3 className="text-yellow-300 font-semibold mb-2">Segítség</h3>
<p className="text-gray-300 text-sm">{card.hint}</p>
</div>
</div>
</div>
)}
{/* Submit Button - csak QUESTION típusnál */}
{cardType === "QUESTION" && (
<button
onClick={handleSubmit}
disabled={isProcessing || (!playerAnswer && !selectedOption)}
className="w-full bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-500 hover:to-blue-500
text-white font-bold py-4 px-6 rounded-xl shadow-lg
transform transition-all duration-200 hover:scale-105 active:scale-95
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100
border border-purple-500/50"
>
<div className="flex items-center justify-center gap-2">
<span className="text-2xl"></span>
<span className="text-lg">
{isProcessing ? "Feldolgozás..." : "Válasz beküldése"}
</span>
</div>
</button>
)}
{/* Close Button - LUCK és JOKER típusnál */}
{(cardType === "LUCK" || cardType === "JOKER") && (
<button
onClick={onClose}
className="w-full bg-gradient-to-r from-green-600 to-teal-600 hover:from-green-500 hover:to-teal-500
text-white font-bold py-4 px-6 rounded-xl shadow-lg
transform transition-all duration-200 hover:scale-105 active:scale-95
border border-green-500/50"
>
<div className="flex items-center justify-center gap-2">
<span className="text-2xl">👍</span>
<span className="text-lg">Rendben</span>
</div>
</button>
)}
</div>
</motion.div>
</div>
)}
</AnimatePresence>
)
}
export default CardDisplayModal