|
import React, { useState, useEffect } from 'react'; |
|
import { ChevronLeft, ChevronRight, SkipBack, SkipForward, Play, Pause, RotateCw, Columns, Rows } from 'lucide-react'; |
|
import { LotomaniaGame } from '../types'; |
|
import EnhancedLotomaniaGrid from './EnhancedLotomaniaGrid'; |
|
|
|
interface DualGameViewerProps { |
|
verticalGames: LotomaniaGame[]; |
|
horizontalGames: LotomaniaGame[]; |
|
currentVerticalGameId: number; |
|
currentHorizontalGameId: number; |
|
onVerticalGameChange: (gameId: number) => void; |
|
onHorizontalGameChange: (gameId: number) => void; |
|
matchedNumbers?: { |
|
vertical: number[]; |
|
horizontal: number[]; |
|
}; |
|
} |
|
|
|
const DualGameViewer: React.FC<DualGameViewerProps> = ({ |
|
verticalGames, |
|
horizontalGames, |
|
currentVerticalGameId, |
|
currentHorizontalGameId, |
|
onVerticalGameChange, |
|
onHorizontalGameChange, |
|
matchedNumbers |
|
}) => { |
|
const [activeTab, setActiveTab] = useState<'vertical' | 'horizontal'>('vertical'); |
|
const [isAutoPlay, setIsAutoPlay] = useState(false); |
|
const [autoPlaySpeed, setAutoPlaySpeed] = useState(1000); |
|
const [syncMode, setSyncMode] = useState(false); |
|
|
|
const currentVerticalGame = verticalGames.find(game => game.id === currentVerticalGameId); |
|
const currentHorizontalGame = horizontalGames.find(game => game.id === currentHorizontalGameId); |
|
|
|
const verticalIndex = verticalGames.findIndex(game => game.id === currentVerticalGameId); |
|
const horizontalIndex = horizontalGames.findIndex(game => game.id === currentHorizontalGameId); |
|
|
|
|
|
useEffect(() => { |
|
if (!isAutoPlay) return; |
|
|
|
const interval = setInterval(() => { |
|
if (activeTab === 'vertical' && verticalIndex < verticalGames.length - 1) { |
|
onVerticalGameChange(verticalGames[verticalIndex + 1].id); |
|
} else if (activeTab === 'horizontal' && horizontalIndex < horizontalGames.length - 1) { |
|
onHorizontalGameChange(horizontalGames[horizontalIndex + 1].id); |
|
} else { |
|
setIsAutoPlay(false); |
|
} |
|
}, autoPlaySpeed); |
|
|
|
return () => clearInterval(interval); |
|
}, [isAutoPlay, activeTab, verticalIndex, horizontalIndex, verticalGames, horizontalGames, onVerticalGameChange, onHorizontalGameChange, autoPlaySpeed]); |
|
|
|
|
|
useEffect(() => { |
|
if (!syncMode) return; |
|
|
|
const verticalProgress = verticalIndex / (verticalGames.length - 1); |
|
const horizontalTargetIndex = Math.round(verticalProgress * (horizontalGames.length - 1)); |
|
|
|
if (horizontalTargetIndex >= 0 && horizontalTargetIndex < horizontalGames.length) { |
|
onHorizontalGameChange(horizontalGames[horizontalTargetIndex].id); |
|
} |
|
}, [syncMode, verticalIndex, verticalGames.length, horizontalGames, onHorizontalGameChange]); |
|
|
|
const currentGames = activeTab === 'vertical' ? verticalGames : horizontalGames; |
|
const currentGame = activeTab === 'vertical' ? currentVerticalGame : currentHorizontalGame; |
|
const currentIndex = activeTab === 'vertical' ? verticalIndex : horizontalIndex; |
|
const onGameChange = activeTab === 'vertical' ? onVerticalGameChange : onHorizontalGameChange; |
|
|
|
const goToFirstGame = () => { |
|
if (currentGames.length > 0) { |
|
onGameChange(currentGames[0].id); |
|
} |
|
}; |
|
|
|
const goToLastGame = () => { |
|
if (currentGames.length > 0) { |
|
onGameChange(currentGames[currentGames.length - 1].id); |
|
} |
|
}; |
|
|
|
const goToPreviousGame = () => { |
|
if (currentIndex > 0) { |
|
onGameChange(currentGames[currentIndex - 1].id); |
|
} |
|
}; |
|
|
|
const goToNextGame = () => { |
|
if (currentIndex < currentGames.length - 1) { |
|
onGameChange(currentGames[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 {activeTab === 'vertical' ? 'vertical' : 'horizontal'} |
|
</button> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
const TabButton: React.FC<{ |
|
type: 'vertical' | 'horizontal'; |
|
icon: React.ReactNode; |
|
label: string; |
|
count: number; |
|
}> = ({ type, icon, label, count }) => ( |
|
<button |
|
onClick={() => setActiveTab(type)} |
|
className={` |
|
flex items-center space-x-2 px-4 py-3 rounded-lg font-medium transition-all duration-200 |
|
${activeTab === type |
|
? 'bg-blue-600 text-white shadow-md' |
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200' |
|
} |
|
`} |
|
> |
|
{icon} |
|
<span>{label}</span> |
|
<span className={` |
|
px-2 py-1 rounded-full text-xs font-bold |
|
${activeTab === type ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-600'} |
|
`}> |
|
{count} |
|
</span> |
|
</button> |
|
); |
|
|
|
return ( |
|
<div className="space-y-6"> |
|
{/* Header com tabs */} |
|
<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 mb-4"> |
|
<div> |
|
<h2 className="text-2xl font-bold mb-1"> |
|
Visualizador de Jogos |
|
</h2> |
|
<p className="text-blue-100"> |
|
Navegue pelos jogos verticais e horizontais da sua estratégia |
|
</p> |
|
</div> |
|
|
|
{/* Controle de sincronização */} |
|
<button |
|
onClick={() => setSyncMode(!syncMode)} |
|
className={` |
|
p-3 rounded-lg transition-all duration-200 |
|
${syncMode ? 'bg-blue-500 text-white' : 'bg-blue-400 text-blue-100 hover:bg-blue-300'} |
|
`} |
|
title={syncMode ? 'Desativar sincronização' : 'Ativar sincronização'} |
|
> |
|
<RotateCw className={`w-5 h-5 ${syncMode ? 'animate-spin' : ''}`} /> |
|
</button> |
|
</div> |
|
|
|
{/* Tabs */} |
|
<div className="flex space-x-4"> |
|
<TabButton |
|
type="vertical" |
|
icon={<Columns className="w-4 h-4" />} |
|
label="Jogos Verticais" |
|
count={verticalGames.length} |
|
/> |
|
<TabButton |
|
type="horizontal" |
|
icon={<Rows className="w-4 h-4" />} |
|
label="Jogos Horizontais" |
|
count={horizontalGames.length} |
|
/> |
|
</div> |
|
</div> |
|
|
|
{/* Informações do jogo atual */} |
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200"> |
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> |
|
<div> |
|
<h3 className={`text-lg font-semibold mb-2 ${ |
|
activeTab === 'vertical' ? 'text-blue-800' : 'text-green-800' |
|
}`}> |
|
Jogo {activeTab === 'vertical' ? 'Vertical' : 'Horizontal'} #{currentGame.id} |
|
</h3> |
|
<div className="space-y-2 text-sm text-gray-600"> |
|
<div>Fase {currentGame.phase} • Ciclo {currentGame.cycle}</div> |
|
<div>Posição: {currentIndex + 1} de {currentGames.length} jogos</div> |
|
<div> |
|
Progresso: {Math.round(((currentIndex + 1) / currentGames.length) * 100)}% |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h4 className="text-lg font-semibold mb-2 text-gray-800"> |
|
{activeTab === 'vertical' ? 'Colunas' : 'Linhas'} Marcadas |
|
</h4> |
|
<div className="flex flex-wrap gap-2"> |
|
{(activeTab === 'vertical' ? currentGame.markedColumns : currentGame.markedRows!).map(pos => ( |
|
<span |
|
key={pos} |
|
className={`px-3 py-1 rounded-full font-medium text-white text-sm ${ |
|
activeTab === 'vertical' ? 'bg-blue-600' : 'bg-green-600' |
|
}`} |
|
> |
|
{pos} |
|
</span> |
|
))} |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Controles de navegação */} |
|
<div className="flex items-center justify-center space-x-4 mb-6"> |
|
<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 === currentGames.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 === currentGames.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 {activeTab === 'vertical' ? 'Verticais' : 'Horizontais'}</span> |
|
<span>{currentIndex + 1} / {currentGames.length}</span> |
|
</div> |
|
<div className="w-full bg-gray-200 rounded-full h-3"> |
|
<div |
|
className={`h-3 rounded-full transition-all duration-300 ${ |
|
activeTab === 'vertical' ? 'bg-blue-600' : 'bg-green-600' |
|
}`} |
|
style={{ width: `${((currentIndex + 1) / currentGames.length) * 100}%` }} |
|
/> |
|
</div> |
|
</div> |
|
|
|
{/* Controles avançados */} |
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> |
|
<div> |
|
<label className="block text-sm font-medium text-gray-700 mb-2"> |
|
Ir para o jogo: |
|
</label> |
|
<input |
|
type="number" |
|
min="1" |
|
max={currentGames.length} |
|
value={currentIndex + 1} |
|
onChange={(e) => { |
|
const position = parseInt(e.target.value); |
|
if (position >= 1 && position <= currentGames.length) { |
|
const targetGame = currentGames[position - 1]; |
|
if (targetGame) { |
|
jumpToGame(targetGame.id); |
|
} |
|
} |
|
}} |
|
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" |
|
placeholder={`1 a ${currentGames.length}`} |
|
/> |
|
</div> |
|
|
|
<div> |
|
<label className="block text-sm font-medium text-gray-700 mb-2"> |
|
Velocidade do auto-play: |
|
</label> |
|
<select |
|
value={autoPlaySpeed} |
|
onChange={(e) => setAutoPlaySpeed(parseInt(e.target.value))} |
|
className="w-full px-3 py-2 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> |
|
<label className="block text-sm font-medium text-gray-700 mb-2"> |
|
Modo de visualização: |
|
</label> |
|
<div className="space-y-2"> |
|
<label className="flex items-center space-x-2 text-sm"> |
|
<input |
|
type="checkbox" |
|
checked={syncMode} |
|
onChange={(e) => setSyncMode(e.target.checked)} |
|
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500" |
|
/> |
|
<span>Sincronizar jogos</span> |
|
</label> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Grid do jogo atual */} |
|
<EnhancedLotomaniaGrid |
|
game={currentGame} |
|
showNumbers={false} |
|
size="md" |
|
className="shadow-lg border border-gray-200" |
|
highlightMatches={ |
|
matchedNumbers |
|
? (activeTab === 'vertical' ? matchedNumbers.vertical : matchedNumbers.horizontal) |
|
: [] |
|
} |
|
/> |
|
|
|
{/* Comparação lado a lado (quando sync mode ativo) */} |
|
{syncMode && currentVerticalGame && currentHorizontalGame && ( |
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200"> |
|
<h3 className="text-lg font-semibold mb-4 text-gray-800"> |
|
Comparação: Vertical vs Horizontal |
|
</h3> |
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> |
|
<div> |
|
<h4 className="text-md font-medium mb-3 text-blue-800"> |
|
Jogo Vertical #{currentVerticalGame.id} |
|
</h4> |
|
<EnhancedLotomaniaGrid |
|
game={currentVerticalGame} |
|
showNumbers={false} |
|
size="sm" |
|
highlightMatches={matchedNumbers?.vertical || []} |
|
/> |
|
</div> |
|
<div> |
|
<h4 className="text-md font-medium mb-3 text-green-800"> |
|
Jogo Horizontal #{currentHorizontalGame.id} |
|
</h4> |
|
<EnhancedLotomaniaGrid |
|
game={currentHorizontalGame} |
|
showNumbers={false} |
|
size="sm" |
|
highlightMatches={matchedNumbers?.horizontal || []} |
|
/> |
|
</div> |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
); |
|
}; |
|
|
|
export default DualGameViewer; |
|
|