Loto / src /App.tsx
Raí Santos
oi
4c1e4ec
import React, { useState, useEffect, useMemo, Suspense } from 'react';
import Sidebar from './components/Sidebar';
import ErrorBoundary from './components/ErrorBoundary';
import { useOptimizedAlgorithm } from './hooks/useOptimizedAlgorithm';
import { useLotomaniaAPI } from './hooks/useLotomaniaAPI';
import { setupSecurityDefaults } from './utils/SecurityUtils';
import { setupBundleOptimizations } from './utils/BundleOptimizer';
import { conditionalPreload } from './utils/lazyComponents';
import {
DualGameViewer,
Statistics,
ProbabilityCalculator,
EnhancedLotomaniaGrid,
LotomaniaFeatures,
ComponentLoader
} from './utils/lazyComponents';
import RealComparisonDemo from './components/RealComparisonDemo';
import GameByGameResults from './components/GameByGameResults';
import {
AlertCircle,
RefreshCw,
Settings,
PlayCircle,
TrendingUp,
Zap,
Target,
BarChart3,
Calculator
} from 'lucide-react';
const App: React.FC = () => {
const [activeSection, setActiveSection] = useState('dual-visualizer');
const [currentVerticalGameId, setCurrentVerticalGameId] = useState(1);
const [currentHorizontalGameId, setCurrentHorizontalGameId] = useState(253); // Primeiro jogo horizontal
// Configurar segurança e otimizações na inicialização
useEffect(() => {
// Configurar defaults de segurança
setupSecurityDefaults();
// Configurar otimizações de bundle
setupBundleOptimizations();
// Preload condicional baseado na performance do dispositivo
conditionalPreload();
console.log('🛡️ Segurança e otimizações configuradas');
}, []);
// Usar hook otimizado
const {
allGames,
verticalGames,
horizontalGames,
gameStatistics,
isLoading: gamesLoading,
error: gamesError,
algorithm,
getGameById,
getNumbersFromGame,
performanceMetrics
} = useOptimizedAlgorithm();
const {
latestResult,
loading: apiLoading,
error: apiError,
fetchLatestResult,
analyzeGameResult
} = useLotomaniaAPI();
// Encontrar jogos atuais baseados nas colunas/linhas fornecidas pelo usuário
useEffect(() => {
if (allGames.length === 0) return;
try {
const userCurrentVerticalGame = algorithm.findGameByColumns([2, 4, 5, 6, 10]);
const userCurrentHorizontalGame = algorithm.findGameByRows([2, 4, 5, 6, 10]);
if (userCurrentVerticalGame) {
setCurrentVerticalGameId(userCurrentVerticalGame.id);
}
if (userCurrentHorizontalGame) {
setCurrentHorizontalGameId(userCurrentHorizontalGame.id);
}
} catch (error) {
console.error('Erro ao encontrar jogos do usuário:', error);
}
}, [allGames, algorithm]);
// Estatísticas dos jogos usando o hook otimizado
const gameStats = useMemo(() => {
if (!gameStatistics) {
return {
currentGame: Math.min(currentVerticalGameId, currentHorizontalGameId),
totalGames: allGames.length,
verticalGames: verticalGames.length,
horizontalGames: horizontalGames.length,
remainingGames: 0,
phases: 1,
gamesPerPhase: {}
};
}
return {
currentGame: Math.min(currentVerticalGameId, currentHorizontalGameId),
totalGames: gameStatistics.totalGames,
verticalGames: gameStatistics.verticalGames,
horizontalGames: gameStatistics.horizontalGames,
remainingGames: algorithm.getRemainingGames(currentVerticalGameId),
phases: gameStatistics.phases,
gamesPerPhase: gameStatistics.gamesPerPhase
};
}, [gameStatistics, allGames, verticalGames, horizontalGames, currentVerticalGameId, currentHorizontalGameId, algorithm]);
// Análise dos jogos atuais contra o último resultado usando funções otimizadas
const currentGamesAnalysis = useMemo(() => {
if (!latestResult) return null;
try {
const currentVerticalGame = getGameById(currentVerticalGameId);
const currentHorizontalGame = getGameById(currentHorizontalGameId);
if (!currentVerticalGame || !currentHorizontalGame ||
currentVerticalGame.type !== 'vertical' || currentHorizontalGame.type !== 'horizontal') {
return null;
}
const verticalNumbers = getNumbersFromGame(currentVerticalGame);
const horizontalNumbers = getNumbersFromGame(currentHorizontalGame);
if (verticalNumbers.length === 0 || horizontalNumbers.length === 0) {
return null;
}
const verticalAnalysis = analyzeGameResult(verticalNumbers, latestResult);
const horizontalAnalysis = analyzeGameResult(horizontalNumbers, latestResult);
return {
vertical: verticalAnalysis,
horizontal: horizontalAnalysis,
matchedNumbers: {
vertical: verticalAnalysis.matchedNumbers,
horizontal: horizontalAnalysis.matchedNumbers
}
};
} catch (error) {
console.error('Erro na análise dos jogos atuais:', error);
return null;
}
}, [currentVerticalGameId, currentHorizontalGameId, latestResult, getGameById, getNumbersFromGame, analyzeGameResult]);
const handleSectionChange = (section: string) => {
setActiveSection(section);
};
const handleVerticalGameChange = (gameId: number) => {
setCurrentVerticalGameId(gameId);
};
const handleHorizontalGameChange = (gameId: number) => {
setCurrentHorizontalGameId(gameId);
};
// Renderizar seções não implementadas
const renderPendingSection = (title: string, description: string, icon: React.ReactNode) => (
<div className="space-y-6">
<div className="bg-gradient-to-r from-gray-600 to-gray-700 p-6 rounded-xl text-white">
<div className="flex items-center space-x-3">
{icon}
<div>
<h2 className="text-2xl font-bold mb-1">{title}</h2>
<p className="text-gray-100">{description}</p>
</div>
</div>
</div>
<div className="bg-white p-8 rounded-xl shadow-lg border border-gray-200 text-center">
<div className="max-w-md mx-auto">
<Zap className="w-16 h-16 text-blue-500 mx-auto mb-4" />
<h3 className="text-xl font-semibold text-gray-800 mb-2">
Funcionalidade Avançada
</h3>
<p className="text-gray-600 mb-4">
Esta seção está sendo implementada com recursos avançados de análise e simulação.
</p>
<div className="bg-blue-50 p-4 rounded-lg">
<p className="text-blue-800 text-sm">
🚀 Em breve: análises preditivas, simulações Monte Carlo,
otimização de estratégias e muito mais!
</p>
</div>
</div>
</div>
</div>
);
// Loading state enquanto jogos estão sendo gerados
if (gamesLoading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<ComponentLoader message="Gerando jogos da estratégia..." />
</div>
);
}
// Error state se houve erro na geração dos jogos
if (gamesError) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-xl shadow-lg border border-gray-200 p-6 text-center">
<AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
<h2 className="text-xl font-bold text-gray-900 mb-2">Erro na Aplicação</h2>
<p className="text-gray-600 mb-4">{gamesError}</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
Recarregar Aplicação
</button>
</div>
</div>
);
}
const renderContent = () => {
switch (activeSection) {
case 'real-comparison': {
const currentVerticalGame = verticalGames.find(g => g.id === currentVerticalGameId);
const currentHorizontalGame = horizontalGames.find(g => g.id === currentHorizontalGameId);
return (
<div className="space-y-6">
<div className="bg-gradient-to-r from-blue-600 to-green-600 text-white p-6 rounded-xl shadow-lg">
<h2 className="text-2xl font-bold mb-2">🔍 DEMONSTRAÇÃO DE COMPARAÇÃO REAL</h2>
<p className="text-blue-100">
Veja exatamente como seus jogos são comparados com os resultados oficiais da Lotomania -
100% dados reais, sem invenções!
</p>
</div>
{currentVerticalGame && (
<div>
<h3 className="text-xl font-bold text-gray-800 mb-3">
📊 Jogo Vertical Atual (#{currentVerticalGame.id})
</h3>
<RealComparisonDemo
game={currentVerticalGame}
result={latestResult}
algorithm={algorithm}
/>
</div>
)}
{currentHorizontalGame && (
<div>
<h3 className="text-xl font-bold text-gray-800 mb-3">
📊 Jogo Horizontal Atual (#{currentHorizontalGame.id})
</h3>
<RealComparisonDemo
game={currentHorizontalGame}
result={latestResult}
algorithm={algorithm}
/>
</div>
)}
<div className="bg-yellow-50 p-4 rounded-lg border border-yellow-200">
<div className="text-center">
<p className="text-yellow-800 font-semibold mb-2">
💡 Use os controles na barra lateral para navegar entre os jogos e ver comparações diferentes!
</p>
<p className="text-yellow-700 text-sm">
Cada jogo mostra exatamente quais números de 1 a 100 estão marcados e como eles se comparam com o resultado oficial.
</p>
</div>
</div>
</div>
);
}
case 'dual-visualizer':
return (
<Suspense fallback={<ComponentLoader message="Carregando visualizador..." />}>
<DualGameViewer
verticalGames={verticalGames}
horizontalGames={horizontalGames}
currentVerticalGameId={currentVerticalGameId}
currentHorizontalGameId={currentHorizontalGameId}
onVerticalGameChange={handleVerticalGameChange}
onHorizontalGameChange={handleHorizontalGameChange}
matchedNumbers={currentGamesAnalysis?.matchedNumbers}
/>
</Suspense>
);
case 'grid': {
const verticalGameForGrid = getGameById(currentVerticalGameId);
const horizontalGameForGrid = getGameById(currentHorizontalGameId);
return (
<Suspense fallback={<ComponentLoader message="Carregando grid interativo..." />}>
<div className="space-y-6">
<div className="bg-gradient-to-r from-blue-600 to-blue-700 p-6 rounded-xl text-white">
<h2 className="text-2xl font-bold mb-2">Grid Interativo Avançado</h2>
<p className="text-blue-100">
Visualização detalhada dos jogos vertical e horizontal com destaque dos acertos
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{verticalGameForGrid && verticalGameForGrid.type === 'vertical' && (
<div>
<h3 className="text-lg font-semibold mb-3 text-blue-800">
Jogo Vertical #{verticalGameForGrid.id}
</h3>
<EnhancedLotomaniaGrid
game={verticalGameForGrid}
showNumbers={false}
size="md"
className="shadow-lg"
highlightMatches={currentGamesAnalysis?.matchedNumbers?.vertical || []}
/>
</div>
)}
{horizontalGameForGrid && horizontalGameForGrid.type === 'horizontal' && (
<div>
<h3 className="text-lg font-semibold mb-3 text-green-800">
Jogo Horizontal #{horizontalGameForGrid.id}
</h3>
<EnhancedLotomaniaGrid
game={horizontalGameForGrid}
showNumbers={false}
size="md"
className="shadow-lg"
highlightMatches={currentGamesAnalysis?.matchedNumbers?.horizontal || []}
/>
</div>
)}
</div>
{/* Grid com números visíveis */}
<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">
Visualização com Números
</h3>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{verticalGameForGrid && verticalGameForGrid.type === 'vertical' && (
<EnhancedLotomaniaGrid
game={verticalGameForGrid}
showNumbers={true}
size="sm"
highlightMatches={currentGamesAnalysis?.matchedNumbers?.vertical || []}
/>
)}
{horizontalGameForGrid && horizontalGameForGrid.type === 'horizontal' && (
<EnhancedLotomaniaGrid
game={horizontalGameForGrid}
showNumbers={true}
size="sm"
highlightMatches={currentGamesAnalysis?.matchedNumbers?.horizontal || []}
/>
)}
</div>
</div>
</div>
</Suspense>
);
}
case 'statistics':
return (
<Suspense fallback={<ComponentLoader message="Carregando estatísticas..." />}>
<Statistics
totalGames={gameStats.totalGames}
currentGame={gameStats.currentGame}
phases={gameStats.phases}
gamesPerPhase={gameStats.gamesPerPhase}
/>
</Suspense>
);
case 'results-analysis':
return latestResult ? (
<Suspense fallback={<ComponentLoader message="Carregando análise de resultados..." />}>
<GameByGameResults
allGames={allGames}
algorithm={algorithm}
currentResult={latestResult}
/>
</Suspense>
) : (
<div className="flex items-center justify-center h-64">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4"></div>
<p className="text-gray-600">Carregando resultado da Lotomania...</p>
</div>
</div>
);
case 'probability':
return (
<Suspense fallback={<ComponentLoader message="Carregando calculadora de probabilidades..." />}>
<ProbabilityCalculator
verticalGames={verticalGames}
horizontalGames={horizontalGames}
latestResult={latestResult}
/>
</Suspense>
);
case 'lotomania-features':
return (
<Suspense fallback={<ComponentLoader message="Carregando funcionalidades da Lotomania..." />}>
<LotomaniaFeatures />
</Suspense>
);
case 'calculator':
return (
<div className="space-y-6">
<div className="bg-gradient-to-r from-purple-600 to-purple-700 p-6 rounded-xl text-white">
<h2 className="text-2xl font-bold mb-2">Calculadora da Estratégia</h2>
<p className="text-purple-100">
Cálculos e informações detalhadas da estratégia dual
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600 mb-1">Total de Jogos</p>
<p className="text-2xl font-bold text-blue-600">{gameStats.totalGames.toLocaleString()}</p>
<p className="text-xs text-gray-500 mt-1">Vertical + Horizontal</p>
</div>
<Calculator className="w-8 h-8 text-blue-500" />
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600 mb-1">Jogos Verticais</p>
<p className="text-2xl font-bold text-blue-600">{gameStats.verticalGames.toLocaleString()}</p>
<p className="text-xs text-gray-500 mt-1">Colunas marcadas</p>
</div>
<BarChart3 className="w-8 h-8 text-blue-500" />
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600 mb-1">Jogos Horizontais</p>
<p className="text-2xl font-bold text-green-600">{gameStats.horizontalGames.toLocaleString()}</p>
<p className="text-xs text-gray-500 mt-1">Linhas marcadas</p>
</div>
<BarChart3 className="w-8 h-8 text-green-500" />
</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600 mb-1">Investimento Total</p>
<p className="text-2xl font-bold text-purple-600">R$ {(gameStats.totalGames * 3.0).toLocaleString()}</p>
<p className="text-xs text-gray-500 mt-1">R$ 3,00 por jogo</p>
</div>
<Target className="w-8 h-8 text-purple-500" />
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<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">Análise por Tipo</h3>
<div className="space-y-4">
<div className="flex justify-between items-center p-3 bg-blue-50 rounded-lg">
<span className="text-blue-800 font-medium">Jogos Verticais:</span>
<span className="text-blue-900 font-bold">{gameStats.verticalGames}</span>
</div>
<div className="flex justify-between items-center p-3 bg-green-50 rounded-lg">
<span className="text-green-800 font-medium">Jogos Horizontais:</span>
<span className="text-green-900 font-bold">{gameStats.horizontalGames}</span>
</div>
<div className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span className="text-gray-800 font-medium">Fases Total:</span>
<span className="text-gray-900 font-bold">{gameStats.phases}</span>
</div>
</div>
</div>
<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">Progresso Atual</h3>
<div className="space-y-4">
<div>
<div className="flex justify-between text-sm text-gray-600 mb-1">
<span>Jogo Vertical Atual</span>
<span>{currentVerticalGameId} / {gameStats.verticalGames}</span>
</div>
<div className="w-full bg-blue-200 rounded-full h-2">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${(currentVerticalGameId / gameStats.verticalGames) * 100}%` }}
/>
</div>
</div>
<div>
<div className="flex justify-between text-sm text-gray-600 mb-1">
<span>Jogo Horizontal Atual</span>
<span>{currentHorizontalGameId} / {gameStats.horizontalGames}</span>
</div>
<div className="w-full bg-green-200 rounded-full h-2">
<div
className="bg-green-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${(currentHorizontalGameId / gameStats.horizontalGames) * 100}%` }}
/>
</div>
</div>
</div>
</div>
</div>
</div>
);
case 'patterns':
return renderPendingSection(
'Análise de Padrões',
'Identificação de padrões e tendências nos sorteios',
<TrendingUp className="w-6 h-6" />
);
case 'simulator':
return renderPendingSection(
'Simulador Avançado',
'Simulação Monte Carlo e cenários otimizados',
<PlayCircle className="w-6 h-6" />
);
case 'settings':
return renderPendingSection(
'Configurações',
'Personalização e preferências do sistema',
<Settings className="w-6 h-6" />
);
default:
return (
<div className="text-center p-8">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
Seção em Desenvolvimento
</h2>
<p className="text-gray-600">
Esta funcionalidade será implementada em breve.
</p>
</div>
);
}
};
return (
<ErrorBoundary>
<div className="min-h-screen bg-gray-50 flex">
<Sidebar
activeSection={activeSection}
onSectionChange={handleSectionChange}
gameStats={gameStats}
/>
<main className="flex-1 p-6 overflow-auto will-change-scroll">
<div className="max-w-7xl mx-auto">
{/* Indicador de performance em desenvolvimento */}
{process.env.NODE_ENV === 'development' && performanceMetrics && (
<div className="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg text-sm">
<div className="flex items-center justify-between">
<span className="font-medium text-yellow-800">
Performance: {performanceMetrics.totalGames} jogos •
Validação: {performanceMetrics.validationTime.toFixed(2)}ms •
Memória: ~{(performanceMetrics.memoryUsage.estimated / 1024).toFixed(1)}KB
</span>
<span className={`text-xs px-2 py-1 rounded ${
performanceMetrics.isValid ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{performanceMetrics.isValid ? '✓ Válido' : '✗ Erro'}
</span>
</div>
</div>
)}
{/* Indicador de análise de resultado atual */}
{currentGamesAnalysis && latestResult && (
<div className="mb-6 bg-white p-4 rounded-xl shadow-lg border border-gray-200 will-change-transform">
<div className="flex items-center justify-between">
<div>
<h4 className="font-semibold text-gray-800">
Resultado vs Jogos Atuais - Concurso #{latestResult.concurso}
</h4>
<div className="flex space-x-6 mt-2 text-sm">
<div className="flex items-center space-x-2">
<span className="w-3 h-3 bg-blue-500 rounded-full"></span>
<span>Vertical: {currentGamesAnalysis.vertical.matches}/50 acertos</span>
{currentGamesAnalysis.vertical.isWinning && (
<span className="text-green-600 font-bold">🏆 PREMIADO!</span>
)}
</div>
<div className="flex items-center space-x-2">
<span className="w-3 h-3 bg-green-500 rounded-full"></span>
<span>Horizontal: {currentGamesAnalysis.horizontal.matches}/50 acertos</span>
{currentGamesAnalysis.horizontal.isWinning && (
<span className="text-green-600 font-bold">🏆 PREMIADO!</span>
)}
</div>
</div>
</div>
<button
onClick={fetchLatestResult}
disabled={apiLoading}
className={`p-2 rounded-lg transition-colors duration-200 ${
apiLoading ? 'text-gray-300' : 'text-gray-400 hover:text-gray-600 hover:bg-gray-100'
}`}
title="Atualizar resultado"
>
<RefreshCw className={`w-5 h-5 ${apiLoading ? 'animate-spin' : ''}`} />
</button>
</div>
</div>
)}
{/* Erros da API */}
{(apiError || gamesError) && (
<div className="mb-6 bg-red-50 border border-red-200 p-4 rounded-xl">
<div className="flex items-center space-x-2">
<AlertCircle className="w-5 h-5 text-red-600" />
<div>
<h4 className="font-semibold text-red-800">Aviso do Sistema</h4>
<p className="text-red-700 text-sm mt-1">
{apiError && `API: ${apiError}`}
{gamesError && `Jogos: ${gamesError}`}
</p>
</div>
</div>
</div>
)}
{/* Conteúdo principal com ErrorBoundary interno */}
<ErrorBoundary>
{renderContent()}
</ErrorBoundary>
</div>
</main>
</div>
</ErrorBoundary>
);
};
export default App;