|
import React from 'react'; |
|
import { LotomaniaGame } from '../types'; |
|
|
|
interface EnhancedLotomaniaGridProps { |
|
game: LotomaniaGame; |
|
showNumbers?: boolean; |
|
size?: 'sm' | 'md' | 'lg'; |
|
className?: string; |
|
highlightMatches?: number[]; |
|
} |
|
|
|
const EnhancedLotomaniaGrid: React.FC<EnhancedLotomaniaGridProps> = ({ |
|
game, |
|
showNumbers = true, |
|
size = 'md', |
|
className = '', |
|
highlightMatches = [] |
|
}) => { |
|
const sizeClasses = { |
|
sm: 'w-7 h-7 text-xs', |
|
md: 'w-9 h-9 text-sm', |
|
lg: 'w-11 h-11 text-base' |
|
}; |
|
|
|
const isVertical = game.type === 'vertical'; |
|
const markedPositions = isVertical ? game.markedColumns : game.markedRows!; |
|
|
|
|
|
const getCellNumber = (row: number, col: number): number => { |
|
|
|
|
|
|
|
|
|
const number = (row * 10) + col + 1; |
|
|
|
return number === 100 ? 0 : number; |
|
}; |
|
|
|
const formatCellNumber = (number: number): string => { |
|
return number.toString().padStart(2, '0'); |
|
}; |
|
|
|
const isCellMarked = (row: number, col: number): boolean => { |
|
if (isVertical) { |
|
|
|
return markedPositions.includes(col + 1); |
|
} else { |
|
|
|
return markedPositions.includes(row + 1); |
|
} |
|
}; |
|
|
|
const renderGrid = () => { |
|
const grid = []; |
|
|
|
|
|
for (let row = 0; row < 10; row++) { |
|
const rowCells = []; |
|
|
|
for (let col = 0; col < 10; col++) { |
|
const number = getCellNumber(row, col); |
|
const isMarked = isCellMarked(row, col); |
|
|
|
rowCells.push( |
|
<div |
|
key={`${row}-${col}`} |
|
className={` |
|
${sizeClasses[size]} |
|
relative group cursor-pointer |
|
flex items-center justify-center |
|
border-2 border-gray-300 rounded-md |
|
transition-all duration-300 hover:scale-105 |
|
font-bold |
|
${isMarked |
|
? `${isVertical ? 'bg-blue-600' : 'bg-green-600'} text-white shadow-lg border-transparent` |
|
: 'bg-white text-black border-gray-300 hover:border-gray-400' |
|
} |
|
`} |
|
title={`Número ${formatCellNumber(number)} - ${isVertical ? `Coluna ${col + 1}` : `Linha ${row + 1}`} ${isMarked ? '(MARCADO)' : '(Não marcado)'}`} |
|
> |
|
{/* Número sempre visível */} |
|
<span className={`select-none ${isMarked ? 'text-white' : 'text-black'} font-bold text-center`}> |
|
{formatCellNumber(number)} |
|
</span> |
|
|
|
{/* Tooltip informativo */} |
|
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-20 whitespace-nowrap shadow-xl"> |
|
<div className="text-center"> |
|
<div className="font-bold text-yellow-300">Número {formatCellNumber(number)}</div> |
|
<div>{isVertical ? `Coluna ${col + 1}` : `Linha ${row + 1}`}</div> |
|
<div className={`font-semibold ${isMarked ? 'text-green-300' : 'text-gray-300'}`}> |
|
{isMarked ? '✓ MARCADO' : '○ Não marcado'} |
|
</div> |
|
</div> |
|
{/* Seta do tooltip */} |
|
<div className="absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-gray-900"></div> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
grid.push( |
|
<div key={row} className="flex gap-1"> |
|
{rowCells} |
|
</div> |
|
); |
|
} |
|
|
|
return grid; |
|
}; |
|
|
|
const renderHeaders = () => { |
|
const columnHeaders = ( |
|
<div className="flex gap-1 mb-3"> |
|
<div className={`${sizeClasses[size]} mr-3`}></div> |
|
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(col => ( |
|
<div |
|
key={col} |
|
className={` |
|
${sizeClasses[size]} |
|
flex items-center justify-center |
|
text-sm font-bold transition-all duration-300 rounded-lg |
|
border-2 |
|
${isVertical && markedPositions.includes(col) |
|
? 'bg-blue-600 text-white border-blue-700 shadow-lg' |
|
: 'bg-gray-100 text-gray-700 border-gray-300 hover:bg-gray-200' |
|
} |
|
`} |
|
title={`Coluna ${col}${isVertical && markedPositions.includes(col) ? ' (MARCADA)' : ''}`} |
|
> |
|
{col} |
|
</div> |
|
))} |
|
</div> |
|
); |
|
|
|
const rowHeaders = ( |
|
<div className="flex flex-col gap-1 mr-3"> |
|
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(row => ( |
|
<div |
|
key={row} |
|
className={` |
|
${sizeClasses[size]} |
|
flex items-center justify-center |
|
text-sm font-bold transition-all duration-300 rounded-lg |
|
border-2 |
|
${!isVertical && markedPositions.includes(row) |
|
? 'bg-green-600 text-white border-green-700 shadow-lg' |
|
: 'bg-gray-100 text-gray-700 border-gray-300 hover:bg-gray-200' |
|
} |
|
`} |
|
title={`Linha ${row}${!isVertical && markedPositions.includes(row) ? ' (MARCADA)' : ''}`} |
|
> |
|
{row} |
|
</div> |
|
))} |
|
</div> |
|
); |
|
|
|
return { columnHeaders, rowHeaders }; |
|
}; |
|
|
|
const renderGameInfo = () => { |
|
const totalNumbers = markedPositions.length * 10; |
|
const gameTypeLabel = isVertical ? 'Vertical (Colunas)' : 'Horizontal (Linhas)'; |
|
const positionsLabel = isVertical ? 'colunas' : 'linhas'; |
|
|
|
|
|
const markedNumbers: number[] = []; |
|
for (let row = 0; row < 10; row++) { |
|
for (let col = 0; col < 10; col++) { |
|
if (isCellMarked(row, col)) { |
|
markedNumbers.push(getCellNumber(row, col)); |
|
} |
|
} |
|
} |
|
markedNumbers.sort((a, b) => a - b); |
|
|
|
|
|
const columnNumbers: { [key: number]: number[] } = {}; |
|
for (let col = 1; col <= 10; col++) { |
|
columnNumbers[col] = []; |
|
for (let row = 0; row < 10; row++) { |
|
const number = getCellNumber(row, col - 1); |
|
columnNumbers[col].push(number); |
|
} |
|
} |
|
|
|
return ( |
|
<div className={`mt-4 p-4 rounded-xl border-2 shadow-sm ${ |
|
isVertical |
|
? 'bg-blue-50 border-blue-300' |
|
: 'bg-green-50 border-green-300' |
|
}`}> |
|
<div className="flex items-center justify-between mb-3"> |
|
<h4 className={`text-base font-bold ${ |
|
isVertical ? 'text-blue-800' : 'text-green-800' |
|
}`}> |
|
🎯 Jogo {gameTypeLabel} |
|
</h4> |
|
<span className={`text-sm px-3 py-1 rounded-full font-bold ${ |
|
isVertical |
|
? 'bg-blue-600 text-white' |
|
: 'bg-green-600 text-white' |
|
}`}> |
|
#{game.id} |
|
</span> |
|
</div> |
|
|
|
{/* Mostrar disposição real da cartela */} |
|
<div className="mb-3"> |
|
<div className={`text-sm font-semibold mb-2 ${isVertical ? 'text-blue-700' : 'text-green-700'}`}> |
|
Disposição da cartela (como na Lotomania real): |
|
</div> |
|
<div className="grid grid-cols-10 gap-1 text-xs p-2 bg-white rounded border"> |
|
{Object.entries(columnNumbers).map(([colNum, numbers]) => ( |
|
<div key={colNum} className="text-center"> |
|
<div className={`font-bold mb-1 text-xs ${ |
|
isVertical && markedPositions.includes(parseInt(colNum)) |
|
? 'text-blue-600' |
|
: 'text-gray-600' |
|
}`}> |
|
Col{colNum} |
|
</div> |
|
{numbers.map(num => ( |
|
<div |
|
key={num} |
|
className={`text-xs py-0.5 ${ |
|
(isVertical && markedPositions.includes(parseInt(colNum))) || |
|
(!isVertical && markedPositions.includes(Math.floor((num === 0 ? 100 : num - 1) / 10) + 1)) |
|
? 'font-bold text-blue-600' |
|
: 'text-gray-500' |
|
}`} |
|
> |
|
{formatCellNumber(num)} |
|
</div> |
|
))} |
|
</div> |
|
))} |
|
</div> |
|
</div> |
|
|
|
{/* Posições marcadas (colunas/linhas) */} |
|
<div className="mb-3"> |
|
<div className={`text-sm font-semibold mb-2 ${isVertical ? 'text-blue-700' : 'text-green-700'}`}> |
|
{positionsLabel.charAt(0).toUpperCase() + positionsLabel.slice(1)} marcadas: |
|
</div> |
|
<div className="flex flex-wrap gap-2"> |
|
{markedPositions.map(pos => ( |
|
<span |
|
key={pos} |
|
className={`px-3 py-1 text-sm rounded-lg font-bold text-white shadow-sm ${ |
|
isVertical ? 'bg-blue-600' : 'bg-green-600' |
|
}`} |
|
> |
|
{pos} |
|
</span> |
|
))} |
|
</div> |
|
</div> |
|
|
|
{/* Números marcados específicos */} |
|
<div className="mb-3"> |
|
<div className={`text-sm font-semibold mb-2 ${isVertical ? 'text-blue-700' : 'text-green-700'}`}> |
|
Números marcados no jogo: ({markedNumbers.length} números) |
|
</div> |
|
<div className="bg-white p-3 rounded-lg border max-h-24 overflow-y-auto"> |
|
<div className="flex flex-wrap gap-1"> |
|
{markedNumbers.map(num => ( |
|
<span |
|
key={num} |
|
className={`px-2 py-1 text-xs rounded font-bold text-white ${ |
|
highlightMatches.includes(num) |
|
? 'bg-yellow-500 ring-2 ring-yellow-300' |
|
: isVertical |
|
? 'bg-blue-500' |
|
: 'bg-green-500' |
|
}`} |
|
> |
|
{formatCellNumber(num)} |
|
</span> |
|
))} |
|
</div> |
|
</div> |
|
</div> |
|
|
|
{/* Estatísticas e acertos */} |
|
<div className={`text-sm space-y-2 p-3 rounded-lg ${ |
|
highlightMatches.length > 0 |
|
? 'bg-yellow-100 border border-yellow-300' |
|
: 'bg-gray-100' |
|
}`}> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-700">Total de números jogados:</span> |
|
<span className="font-bold">{totalNumbers}</span> |
|
</div> |
|
<div className="flex justify-between"> |
|
<span className="text-gray-700">Fase • Ciclo:</span> |
|
<span className="font-bold">{game.phase} • {game.cycle}</span> |
|
</div> |
|
{highlightMatches.length > 0 && ( |
|
<div className="flex justify-between items-center pt-2 border-t border-yellow-300"> |
|
<span className="text-yellow-800 font-bold">🎉 Acertos encontrados:</span> |
|
<span className="bg-yellow-600 text-white px-3 py-1 rounded-full text-sm font-bold"> |
|
{highlightMatches.length} pontos |
|
</span> |
|
</div> |
|
)} |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
const { columnHeaders, rowHeaders } = renderHeaders(); |
|
|
|
return ( |
|
<div className={`bg-white rounded-xl p-6 shadow-lg border ${className}`}> |
|
<div className="flex flex-col items-center"> |
|
{/* Cabeçalhos das colunas */} |
|
{columnHeaders} |
|
|
|
<div className="flex mb-2"> |
|
{/* Cabeçalhos das linhas */} |
|
{rowHeaders} |
|
|
|
{/* Grid principal */} |
|
<div className="flex flex-col gap-1 p-2 bg-gray-50 rounded-lg border shadow-inner"> |
|
{renderGrid()} |
|
</div> |
|
</div> |
|
|
|
{/* Informações do jogo */} |
|
{renderGameInfo()} |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default EnhancedLotomaniaGrid; |