|
import React, { useState, useEffect } from 'react'; |
|
import { ChevronLeft, ChevronRight, SkipBack, SkipForward, Play, Pause, Info } from 'lucide-react'; |
|
import { LotomaniaGame } from '../types'; |
|
import LotomaniaGrid from './LotomaniaGrid'; |
|
|
|
interface GameViewerProps { |
|
games: LotomaniaGame[]; |
|
currentGameId: number; |
|
onGameChange: (gameId: number) => void; |
|
} |
|
|
|
const GameViewer: React.FC<GameViewerProps> = ({ |
|
games, |
|
currentGameId, |
|
onGameChange |
|
}) => { |
|
const [isAutoPlay, setIsAutoPlay] = useState(false); |
|
const [autoPlaySpeed, setAutoPlaySpeed] = useState(1000); |
|
|
|
const currentGame = games.find(game => game.id === currentGameId); |
|
const currentIndex = games.findIndex(game => game.id === currentGameId); |
|
|
|
|
|
useEffect(() => { |
|
if (!isAutoPlay) return; |
|
|
|
const interval = setInterval(() => { |
|
if (currentIndex < games.length - 1) { |
|
onGameChange(games[currentIndex + 1].id); |
|
} else { |
|
setIsAutoPlay(false); |
|
} |
|
}, autoPlaySpeed); |
|
|
|
return () => clearInterval(interval); |
|
}, [isAutoPlay, currentIndex, games, onGameChange, autoPlaySpeed]); |
|
|
|
const goToFirstGame = () => { |
|
if (games.length > 0) { |
|
onGameChange(games[0].id); |
|
} |
|
}; |
|
|
|
const goToLastGame = () => { |
|
if (games.length > 0) { |
|
onGameChange(games[games.length - 1].id); |
|
} |
|
}; |
|
|
|
const goToPreviousGame = () => { |
|
if (currentIndex > 0) { |
|
onGameChange(games[currentIndex - 1].id); |
|
} |
|
}; |
|
|
|
const goToNextGame = () => { |
|
if (currentIndex < games.length - 1) { |
|
onGameChange(games[currentIndex + 1].id); |
|
} |
|
}; |
|
|
|
const jumpToGame = (gameId: number) => { |
|
onGameChange(gameId); |
|
}; |
|
|
|
if (!currentGame) { |
|
return ( |
|
<div className="flex items-center justify-center h-64"> |
|
<div className="text-center"> |
|
<div className="text-gray-500 mb-2">Jogo não encontrado</div> |
|
<button onClick={goToFirstGame} className="btn-primary"> |
|
Ir para o primeiro jogo |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
return ( |
|
<div className="space-y-6"> |
|
{/* Header com informações do jogo */} |
|
<div className="bg-gradient-to-r from-blue-600 to-blue-700 p-6 rounded-xl text-white"> |
|
<div className="flex items-center justify-between"> |
|
<div> |
|
<h2 className="text-2xl font-bold mb-1"> |
|
Jogo #{currentGame.id} |
|
</h2> |
|
<p className="text-blue-100"> |
|
Fase {currentGame.phase} • Ciclo {currentGame.cycle} • |
|
{currentIndex + 1} de {games.length} jogos |
|
</p> |
|
</div> |
|
<div className="text-right"> |
|
<div className="text-3xl font-bold"> |
|
{Math.round(((currentIndex + 1) / games.length) * 100)}% |
|
</div> |
|
<div className="text-blue-100 text-sm">Concluído</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Controles de Navegação */} |
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200"> |
|
<div className="flex items-center justify-center space-x-4 mb-6"> |
|
{/* Controles principais */} |
|
<button |
|
onClick={goToFirstGame} |
|
disabled={currentIndex === 0} |
|
className="p-2 rounded-lg bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200" |
|
title="Primeiro jogo" |
|
> |
|
<SkipBack className="w-5 h-5" /> |
|
</button> |
|
|
|
<button |
|
onClick={goToPreviousGame} |
|
disabled={currentIndex === 0} |
|
className="p-2 rounded-lg bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200" |
|
title="Jogo anterior" |
|
> |
|
<ChevronLeft className="w-5 h-5" /> |
|
</button> |
|
|
|
<button |
|
onClick={() => setIsAutoPlay(!isAutoPlay)} |
|
className={`p-3 rounded-lg transition-all duration-200 ${ |
|
isAutoPlay |
|
? 'bg-red-100 text-red-700 hover:bg-red-200' |
|
: 'bg-green-100 text-green-700 hover:bg-green-200' |
|
}`} |
|
title={isAutoPlay ? 'Pausar reprodução' : 'Reprodução automática'} |
|
> |
|
{isAutoPlay ? <Pause className="w-5 h-5" /> : <Play className="w-5 h-5" />} |
|
</button> |
|
|
|
<button |
|
onClick={goToNextGame} |
|
disabled={currentIndex === games.length - 1} |
|
className="p-2 rounded-lg bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200" |
|
title="Próximo jogo" |
|
> |
|
<ChevronRight className="w-5 h-5" /> |
|
</button> |
|
|
|
<button |
|
onClick={goToLastGame} |
|
disabled={currentIndex === games.length - 1} |
|
className="p-2 rounded-lg bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200" |
|
title="Último jogo" |
|
> |
|
<SkipForward className="w-5 h-5" /> |
|
</button> |
|
</div> |
|
|
|
{/* Barra de progresso */} |
|
<div className="mb-6"> |
|
<div className="flex justify-between text-sm text-gray-600 mb-2"> |
|
<span>Progresso dos Jogos</span> |
|
<span>{currentIndex + 1} / {games.length}</span> |
|
</div> |
|
<div className="w-full bg-gray-200 rounded-full h-3 cursor-pointer"> |
|
<div |
|
className="bg-blue-600 h-3 rounded-full transition-all duration-300" |
|
style={{ width: `${((currentIndex + 1) / games.length) * 100}%` }} |
|
/> |
|
</div> |
|
</div> |
|
|
|
{/* Input para saltar para jogo específico */} |
|
<div className="flex items-center space-x-4"> |
|
<label className="text-sm font-medium text-gray-700">Ir para o jogo:</label> |
|
<input |
|
type="number" |
|
min="1" |
|
max={games.length} |
|
value={currentGame.id} |
|
onChange={(e) => { |
|
const gameId = parseInt(e.target.value); |
|
if (gameId >= 1 && gameId <= games.length) { |
|
jumpToGame(gameId); |
|
} |
|
}} |
|
className="w-20 px-3 py-1 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" |
|
/> |
|
|
|
{/* Velocidade do auto-play */} |
|
<div className="flex items-center space-x-2 ml-8"> |
|
<label className="text-sm font-medium text-gray-700">Velocidade:</label> |
|
<select |
|
value={autoPlaySpeed} |
|
onChange={(e) => setAutoPlaySpeed(parseInt(e.target.value))} |
|
className="px-2 py-1 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" |
|
> |
|
<option value={3000}>Lenta (3s)</option> |
|
<option value={1000}>Normal (1s)</option> |
|
<option value={500}>Rápida (0.5s)</option> |
|
<option value={200}>Muito Rápida (0.2s)</option> |
|
</select> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Grid do jogo atual */} |
|
<div className="bg-white rounded-xl shadow-lg border border-gray-200 p-6"> |
|
<LotomaniaGrid |
|
markedColumns={currentGame.markedColumns} |
|
showNumbers={false} |
|
size="md" |
|
className="bg-transparent" |
|
/> |
|
</div> |
|
|
|
{/* Informações detalhadas do jogo */} |
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> |
|
<div className="card"> |
|
<div className="flex items-center space-x-2 mb-4"> |
|
<Info className="w-5 h-5 text-blue-600" /> |
|
<h3 className="text-lg font-semibold text-gray-800">Informações do Jogo</h3> |
|
</div> |
|
<div className="space-y-3"> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">ID do Jogo:</span> |
|
<span className="font-semibold">#{currentGame.id}</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Fase:</span> |
|
<span className="font-semibold">{currentGame.phase}</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Ciclo:</span> |
|
<span className="font-semibold">{currentGame.cycle}</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Posição:</span> |
|
<span className="font-semibold">{currentIndex + 1} / {games.length}</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div className="card"> |
|
<h3 className="text-lg font-semibold text-gray-800 mb-4">Colunas Marcadas</h3> |
|
<div className="flex flex-wrap gap-2"> |
|
{currentGame.markedColumns.map(col => ( |
|
<span |
|
key={col} |
|
className="px-3 py-1 bg-blue-600 text-white rounded-full font-medium text-sm" |
|
> |
|
{col} |
|
</span> |
|
))} |
|
</div> |
|
<p className="text-sm text-gray-600 mt-3"> |
|
Total: {currentGame.markedColumns.length * 10} números |
|
</p> |
|
</div> |
|
|
|
<div className="card"> |
|
<h3 className="text-lg font-semibold text-gray-800 mb-4">Estatísticas</h3> |
|
<div className="space-y-3"> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Números marcados:</span> |
|
<span className="font-semibold">{currentGame.markedColumns.length * 10}</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Cobertura:</span> |
|
<span className="font-semibold">{currentGame.markedColumns.length * 10}%</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-600">Progresso:</span> |
|
<span className="font-semibold"> |
|
{Math.round(((currentIndex + 1) / games.length) * 100)}% |
|
</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Navegação rápida */} |
|
<div className="card"> |
|
<h3 className="text-lg font-semibold text-gray-800 mb-4">Navegação Rápida</h3> |
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3"> |
|
{[1, 10, 50, 100, 500, 1000].filter(jump => jump <= games.length).map(jump => ( |
|
<button |
|
key={jump} |
|
onClick={() => jumpToGame(jump)} |
|
className={`p-3 rounded-lg text-sm font-medium transition-all duration-200 ${ |
|
currentGame.id === jump |
|
? 'bg-blue-600 text-white' |
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200' |
|
}`} |
|
> |
|
Jogo #{jump} |
|
</button> |
|
))} |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default GameViewer; |
|
|