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

306 lines
12 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"
/**
* StepPredictionModal - Pozíció tippelés (Position Guessing)
*
* A dokumentáció szerint: A játékosnak meg kell tippelnie a VÉGLEGES POZÍCIÓT,
* nem a lépésszámot!
*
* Számítás: finalPosition = currentPosition + diceRoll + fieldStepValue + patternModifier
*
* @param {Object} props
* @param {boolean} props.isOpen - Modal megjelenítése
* @param {Function} props.onClose - Modal bezárása
* @param {Function} props.onSubmitPrediction - Tipp beküldése
* @param {number} props.currentPosition - Jelenlegi pozíció
* @param {number} props.diceRoll - Dobás értéke
* @param {number} props.fieldStepValue - Mező lépés értéke
* @param {number} props.patternModifier - Zóna alapú módosító
* @param {string} props.cardText - Kártya szövege
* @param {Array} props.hints - Segédletek tömbje
* @param {number} props.timeLimit - Időkorlát másodpercben (default: 30)
*/
const StepPredictionModal = ({
isOpen,
onClose,
onSubmitPrediction,
currentPosition = 0,
diceRoll = 0,
fieldStepValue = 0,
patternModifier = 0,
cardText = "Tippeld meg, melyik pozícióra fogsz lépni!",
hints = [],
timeLimit = 30
}) => {
const [prediction, setPrediction] = useState("")
const [showHints, setShowHints] = useState(false)
const [isProcessing, setIsProcessing] = useState(false)
const [timeLeft, setTimeLeft] = useState(timeLimit)
// Timer countdown
useEffect(() => {
if (!isOpen) 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])
const handleTimeout = () => {
if (onSubmitPrediction) {
onSubmitPrediction(null) // null = timeout
}
}
const handleSubmit = async () => {
if (!prediction || prediction === "" || isProcessing) return
const guessedPosition = parseInt(prediction)
if (isNaN(guessedPosition)) return
setIsProcessing(true)
try {
await onSubmitPrediction(guessedPosition)
} catch (error) {
console.error("Tipp küldési hiba:", error)
setIsProcessing(false)
}
}
// Reset amikor megnyílik
useEffect(() => {
if (isOpen) {
setPrediction("")
setShowHints(false)
setIsProcessing(false)
}
}, [isOpen])
// Számított végső pozíció (helyes válasz)
const calculatedPosition = currentPosition + diceRoll + fieldStepValue + patternModifier
const formatTime = (seconds) => {
return `0:${seconds.toString().padStart(2, '0')}`
}
const getTimeColor = () => {
if (timeLeft > 15) return "text-green-400"
if (timeLeft > 5) return "text-yellow-400"
return "text-red-400 animate-pulse"
}
if (!isOpen) 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-yellow-500/30 max-w-xl w-full overflow-hidden max-h-[90vh] overflow-y-auto"
>
{/* Header */}
<div className="bg-gradient-to-r from-yellow-600 via-orange-600 to-yellow-600 p-4 relative overflow-hidden sticky top-0 z-10">
<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-2">
<div className="text-3xl animate-bounce">🎯</div>
<div>
<h2 className="text-xl font-bold text-white">Pozíció Tippelés</h2>
<p className="text-white/80 text-xs">Melyik pozícióra lépsz?</p>
</div>
</div>
{/* Timer */}
<div className="bg-black/30 rounded-lg px-3 py-1">
<div className={`text-lg font-bold ${getTimeColor()}`}>
{formatTime(timeLeft)}
</div>
</div>
</div>
</div>
{/* Content */}
<div className="p-4 space-y-4">
{/* Card Text / Instructions */}
<div className="bg-gray-800/50 rounded-xl p-3 border border-gray-700">
<div className="flex items-start gap-2">
<div className="text-2xl">📝</div>
<div className="flex-1">
<p className="text-white text-sm leading-relaxed">
{cardText}
</p>
</div>
</div>
</div>
{/* Calculation Info */}
<div className="bg-gradient-to-br from-blue-900/30 to-purple-900/30 rounded-xl p-3 border border-blue-500/30">
<h3 className="text-blue-300 font-semibold mb-2 text-center text-sm">📊 Számítási Adatok</h3>
<div className="grid grid-cols-2 gap-2 text-sm">
<div className="bg-black/30 rounded-lg p-2">
<p className="text-gray-400 text-xs mb-1">Jelenlegi pozíció</p>
<p className="text-white font-bold text-lg">{currentPosition}</p>
</div>
<div className="bg-black/30 rounded-lg p-2">
<p className="text-gray-400 text-xs mb-1">Dobás (kocka)</p>
<p className="text-white font-bold text-lg">+{diceRoll}</p>
</div>
<div className="bg-black/30 rounded-lg p-2">
<p className="text-gray-400 text-xs mb-1">Mező lépés</p>
<p className="text-white font-bold text-lg">+{fieldStepValue}</p>
</div>
<div className="bg-black/30 rounded-lg p-2">
<p className="text-gray-400 text-xs mb-1">Zóna módosító</p>
<p className={`font-bold text-lg ${patternModifier >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{patternModifier >= 0 ? '+' : ''}{patternModifier}
</p>
</div>
</div>
<div className="mt-2 bg-yellow-900/30 rounded-lg p-2 border border-yellow-500/30">
<p className="text-yellow-300 text-center text-xs">
<span className="font-semibold">Számítsd ki:</span> {currentPosition} + {diceRoll} + {fieldStepValue} {patternModifier >= 0 ? '+' : ''} {patternModifier} = ?
</p>
</div>
</div>
{/* Position Input */}
<div className="space-y-2">
<h3 className="text-yellow-300 font-semibold text-center text-sm">
Írd be a tippelt pozíciót:
</h3>
<input
type="number"
value={prediction}
onChange={(e) => setPrediction(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSubmit()}
disabled={isProcessing}
placeholder="Pl: 28"
className="w-full bg-gray-800 border-2 border-yellow-600 rounded-xl px-4 py-3 text-white text-center text-2xl font-bold focus:border-yellow-400 focus:outline-none disabled:opacity-50"
min={currentPosition}
max={100}
/>
</div>
{/* Prediction Info */}
{prediction && prediction !== "" && (
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
className="bg-yellow-900/20 rounded-xl p-2 border border-yellow-500/30 text-center"
>
<p className="text-yellow-300 text-sm">
A tipped: <span className="font-bold text-xl text-white">{prediction}</span> pozíció
</p>
</motion.div>
)}
{/* Hints Section */}
{hints && hints.length > 0 && (
<div className="bg-blue-900/20 rounded-xl p-4 border border-blue-500/30">
<button
onClick={() => setShowHints(!showHints)}
className="w-full flex items-center justify-between text-blue-300 hover:text-blue-200 transition-colors"
>
<div className="flex items-center gap-2">
<span className="text-2xl">💡</span>
<span className="font-semibold">Segédlet</span>
</div>
<span className="text-xl">{showHints ? "▼" : "▶"}</span>
</button>
<AnimatePresence>
{showHints && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="mt-3 space-y-2"
>
{hints.map((hint, index) => (
<div key={index} className="bg-blue-900/30 rounded-lg p-3">
<p className="text-gray-300 text-sm">
<span className="font-bold text-blue-300">#{index + 1}:</span> {hint}
</p>
</div>
))}
</motion.div>
)}
</AnimatePresence>
</div>
)}
{/* Risk/Reward Info */}
<div className="bg-gradient-to-br from-green-900/20 to-red-900/20 rounded-xl p-4 border border-gray-600">
<div className="grid grid-cols-2 gap-4 text-center">
<div>
<div className="text-3xl mb-2"></div>
<p className="text-green-300 font-semibold text-sm">Ha eltalálod</p>
<p className="text-white text-xs">Lépsz az új pozícióra!</p>
</div>
<div>
<div className="text-3xl mb-2"></div>
<p className="text-red-300 font-semibold text-sm">Ha nem találod el</p>
<p className="text-white text-xs">-2 büntetés + nem lépsz!</p>
</div>
</div>
</div>
{/* Submit Button */}
<button
onClick={handleSubmit}
disabled={!prediction || prediction === "" || isProcessing}
className="w-full bg-gradient-to-r from-yellow-600 to-orange-600 hover:from-yellow-500 hover:to-orange-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-yellow-500/50"
>
<div className="flex items-center justify-center gap-2">
<span className="text-2xl">🎲</span>
<span className="text-lg">
{isProcessing ? "Feldolgozás..." : "Tipp beküldése"}
</span>
</div>
</button>
{/* Warning */}
{(!prediction || prediction === "") && (
<p className="text-center text-gray-400 text-sm">
Írd be a tippelt pozíciót a beküldéshez!
</p>
)}
</div>
</motion.div>
</div>
)}
</AnimatePresence>
)
}
export default StepPredictionModal