Loto / src /components /EnhancedLotomaniaGrid.tsx
Raí Santos
oi
4c1e4ec
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!;
// Corrigido: Agora segue a disposição real da Lotomania
const getCellNumber = (row: number, col: number): number => {
// Linha 0 (primeira): 01, 02, 03, 04, 05, 06, 07, 08, 09, 10
// Linha 1 (segunda): 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
// etc...
// Última linha: 91, 92, 93, 94, 95, 96, 97, 98, 99, 00
const number = (row * 10) + col + 1;
// Número 100 vira 00
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) {
// Para jogos verticais, verificar se a coluna está marcada
return markedPositions.includes(col + 1);
} else {
// Para jogos horizontais, verificar se a linha está marcada
return markedPositions.includes(row + 1);
}
};
const renderGrid = () => {
const grid = [];
// Gerar grid 10x10 (100 números) - disposição correta da Lotomania
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';
// Calcular os números específicos que estão marcados
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);
// Mostrar a disposição dos números por coluna (como na cartela real)
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;