Loto / src /components /DualGameViewer.tsx
Raí Santos
oi
4c1e4ec
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);
// Auto-play functionality
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]);
// Sync mode - when one changes, change the other to same relative position
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;