diff --git a/frontend/src/app/game/game.module.css b/frontend/src/app/game/game.module.css new file mode 100644 index 0000000..294d114 --- /dev/null +++ b/frontend/src/app/game/game.module.css @@ -0,0 +1,488 @@ + +.gameContainer { + min-height: 100vh; + background-color: var(--yellow-bg); + padding: 20px; + display: flex; + flex-direction: column; +} + + +.navbarGame { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; + background-color: var(--yellow-bg); + border-bottom: 3px solid var(--primary-green); + margin-bottom: 20px; +} + +.gameInfo { + display: flex; + flex-direction: column; +} + +.gameTitle { + color: var(--primary-green); + font-size: 32px; + margin: 0; + font-weight: bold; +} + +.gameSubtitle { + color: var(--secondary-green); + font-size: 18px; + margin: 5px 0 0 0; +} + +.userInfoGame { + display: flex; + align-items: center; + gap: 15px; +} + +.userAvatarGame { + width: 50px; + height: 50px; + border-radius: 50%; + background-color: var(--primary-green); + color: var(--lime-bright); + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + font-size: 20px; +} + +.userNameGame { + color: var(--primary-green); + font-weight: bold; + font-size: 18px; +} + + +.gameContent { + display: flex; + flex-direction: column; + gap: 30px; + flex: 1; + max-width: 1400px; + margin: 0 auto; + width: 100%; +} + +.drawnNumbersSection { + background-color: white; + border-radius: 15px; + border: 3px solid var(--light-green); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + padding: 25px; +} + +.sectionTitle { + color: var(--primary-green); + font-size: 28px; + margin: 0 0 20px 0; + text-align: center; + font-weight: bold; +} + +.drawnNumbersGrid { + display: flex; + flex-wrap: wrap; + gap: 12px; + justify-content: center; + min-height: 80px; + padding: 15px; + background-color: var(--lime-bg); + border-radius: 10px; + border: 2px dashed var(--light-green); +} + +.drawnNumber { + width: 60px; + height: 60px; + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + color: var(--lime-bright); + font-size: 24px; + font-weight: bold; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; +} + +.drawnNumber::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 50%; + background: rgba(255, 255, 255, 0.1); +} + +.drawnNumber.latest { + transform: scale(1.1); + border: 3px solid var(--lime-bright); + box-shadow: 0 0 15px var(--lime-light); + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + 0% { transform: scale(1.1); } + 50% { transform: scale(1.15); } + 100% { transform: scale(1.1); } +} + + +.cardsSection { + flex: 1; + display: flex; + flex-direction: column; +} + +.cardsGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 25px; + margin-bottom: 30px; +} + +.bingoCard { + background-color: white; + border-radius: 15px; + border: 3px solid var(--light-green); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); + overflow: hidden; + transition: transform 0.3s ease; +} + +.bingoCard:hover { + transform: translateY(-5px); +} + +.cardHeader { + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%); + padding: 15px; + text-align: center; + color: var(--lime-bright); +} + +.cardTitle { + margin: 0; + font-size: 22px; + font-weight: bold; +} + +.cardContent { + padding: 20px; +} + + +.bingoGrid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 8px; + margin: 0 auto; + width: fit-content; +} + +.gridHeader { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 8px; + margin-bottom: 10px; +} + +.letterCell { + width: 50px; + height: 50px; + background-color: var(--primary-green); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: var(--lime-bright); + font-size: 24px; + font-weight: bold; +} + +.gridRow { + display: contents; +} + +.numberCell { + width: 50px; + height: 50px; + border: 2px solid var(--light-green); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + font-weight: bold; + color: var(--primary-green); + background-color: white; + cursor: pointer; + transition: all 0.2s; +} + +.numberCell:hover { + background-color: var(--lime-bg); +} + +.numberCell.marked { + background-color: var(--lime-light); + color: var(--primary-green); + border-color: var(--lime-light); + position: relative; +} + +.numberCell.marked::after { + content: '✓'; + position: absolute; + top: -5px; + right: -5px; + background-color: var(--primary-green); + color: var(--lime-bright); + width: 20px; + height: 20px; + border-radius: 50%; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; +} + +.numberCell.free { + background-color: var(--lime-light); + color: var(--primary-green); + font-weight: bold; + font-style: italic; +} + +.actionsSection { + background-color: white; + border-radius: 15px; + border: 3px solid var(--light-green); + padding: 25px; + margin-top: auto; +} + +.actionsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 30px; +} + +.actionButton { + height: 80px; + border-radius: 15px; + border: none; + font-size: 28px; + font-weight: bold; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 15px; + transition: all 0.3s ease; + font-family: var(--font-baloo-bhaijaan); +} + +.actionButton:hover { + transform: translateY(-3px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); +} + +.actionButton:active { + transform: translateY(0); +} + +.bingoButton { + background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); + color: #B22222; + border: 3px solid #FF8C00; +} + +.bingoButton:hover { + background: linear-gradient(135deg, #FFA500 0%, #FF8C00 100%); +} + +.exitButton { + background: var(--primary-green); + color: white; + border: 3px solid var(--primary-green); +} + +.exitButton:hover { + background: var(--primary-green); +} + +.buttonIcon { + font-size: 32px; +} + + +.modalOverlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.winModal { + background: white; + border-radius: 20px; + padding: 40px; + text-align: center; + max-width: 500px; + width: 90%; + border: 5px solid var(--lime-bright); + box-shadow: 0 0 50px rgba(226, 246, 126, 0.5); + animation: modalAppear 0.5s ease-out; +} + +@keyframes modalAppear { + 0% { + transform: scale(0.5); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +.winTitle { + color: var(--primary-green); + font-size: 36px; + margin: 0 0 20px 0; + display: flex; + align-items: center; + justify-content: center; + gap: 15px; +} + +.winMessage { + color: var(--secondary-green); + font-size: 20px; + margin-bottom: 30px; + line-height: 1.5; +} + +.prizeAmount { + font-size: 32px; + color: #FFD700; + font-weight: bold; + background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + padding: 10px; + border-radius: 10px; + margin: 20px 0; +} + +.modalButtons { + display: flex; + gap: 20px; + justify-content: center; + margin-top: 30px; +} + +@media (max-width: 1024px) { + .cardsGrid { + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + } +} + +@media (max-width: 768px) { + .navbarGame { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .userInfoGame { + flex-direction: column; + } + + .cardsGrid { + grid-template-columns: 1fr; + } + + .actionsGrid { + grid-template-columns: 1fr; + gap: 15px; + } + + .actionButton { + height: 70px; + font-size: 24px; + } + + .drawnNumber { + width: 50px; + height: 50px; + font-size: 20px; + } +} + +@media (max-width: 480px) { + .gameContainer { + padding: 10px; + } + + .bingoGrid { + gap: 5px; + } + + .letterCell, + .numberCell { + width: 40px; + height: 40px; + font-size: 18px; + } + + .numberCell.marked::after { + width: 15px; + height: 15px; + font-size: 12px; + top: -3px; + right: -3px; + } + + .sectionTitle { + font-size: 24px; + } + + .gameTitle { + font-size: 28px; + } +} + +.cardsContainer { + max-height: 400px; + overflow-y: auto; + padding: 10px; + margin-bottom: 20px; + border: 2px solid var(--lime-bg); + border-radius: 10px; +} + + +.bingoCard.active { + border-color: var(--lime-bright); + box-shadow: 0 0 20px rgba(226, 246, 126, 0.5); +} \ No newline at end of file diff --git a/frontend/src/app/game/page.tsx b/frontend/src/app/game/page.tsx new file mode 100644 index 0000000..7c8bedf --- /dev/null +++ b/frontend/src/app/game/page.tsx @@ -0,0 +1,407 @@ +'use client' +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import styles from './game.module.css'; + +interface BingoCard { + id: string; + name: string; + numbers: number[][]; + markedNumbers: number[]; + price: number; +} + +interface GameUser { + name: string; + avatarInitials: string; + balance: number; +} + +export default function GamePage() { + const router = useRouter(); + + // Estado do usuário + const [user] = useState({ + name: "João Silva", + avatarInitials: "JS", + balance: 250.50 + }); + + // Estado dos números sorteados + const [drawnNumbers, setDrawnNumbers] = useState([7, 15, 32, 48, 61]); + const [lastNumber, setLastNumber] = useState(61); + + // Estado das cartelas + const [bingoCards, setBingoCards] = useState([ + { + id: "1", + name: "Cartela Premium", + numbers: [ + [7, 12, 0, 41, 55], + [19, 24, 0, 46, 62], + [28, 35, 0, 52, 68], + [33, 40, 0, 57, 73], + [44, 51, 0, 64, 75] + ], + markedNumbers: [7, 15, 32, 48, 61], + price: 25.00 + }, + { + id: "2", + name: "Cartela Rápida", + numbers: [ + [3, 16, 0, 42, 61], + [8, 22, 0, 47, 67], + [13, 30, 0, 53, 71], + [27, 38, 0, 58, 74], + [35, 45, 0, 63, 70] + ], + markedNumbers: [7, 61], + price: 15.00 + }, + { + id: "3", + name: "Cartela Social", + numbers: [ + [5, 18, 0, 39, 65], + [11, 25, 0, 44, 69], + [17, 32, 0, 50, 72], + [23, 37, 0, 56, 63], + [29, 43, 0, 60, 74] + ], + markedNumbers: [32, 48], + price: 10.00 + } + ]); + + // Estado do modal de vitória + const [showWinModal, setShowWinModal] = useState(false); + const [winningCard, setWinningCard] = useState(''); + const [winningPrize, setWinningPrize] = useState(0); + + // Estado do jogo + const [isPlaying, setIsPlaying] = useState(true); + const [gameTime, setGameTime] = useState(0); + + // Simular sorteio de números (apenas frontend por enquanto) + useEffect(() => { + if (!isPlaying) return; + + const gameTimer = setInterval(() => { + setGameTime(prev => prev + 1); + }, 1000); + + const numberTimer = setInterval(() => { + if (drawnNumbers.length >= 75) { + clearInterval(numberTimer); + return; + } + + // Gerar número aleatório que ainda não foi sorteado + let newNumber: number; + do { + newNumber = Math.floor(Math.random() * 75) + 1; + } while (drawnNumbers.includes(newNumber)); + + setDrawnNumbers(prev => [...prev, newNumber]); + setLastNumber(newNumber); + + // Marcar automaticamente nas cartelas + setBingoCards(prev => prev.map(card => ({ + ...card, + markedNumbers: card.markedNumbers.includes(newNumber) + ? card.markedNumbers + : [...card.markedNumbers, newNumber] + }))); + + }, 5000); // Sorteia um número a cada 5 segundos + + return () => { + clearInterval(gameTimer); + clearInterval(numberTimer); + }; + }, [isPlaying, drawnNumbers.length]); + + // Função para formatar tempo (mm:ss) + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + }; + + // Função para verificar se um número está marcado em uma cartela + const isNumberMarked = (card: BingoCard, number: number) => { + return card.markedNumbers.includes(number); + }; + + + const handleBingoClick = () => { + // Simular vitória aleatória + const randomCard = bingoCards[Math.floor(Math.random() * bingoCards.length)]; + setWinningCard(randomCard.name); + setWinningPrize(randomCard.price * 10); + setShowWinModal(true); + setIsPlaying(false); + }; + + + const handleExitClick = () => { + if (confirm('Tem certeza que deseja sair do jogo? Seu progresso será perdido.')) { + router.push('/user'); + } + }; + + + const handleContinuePlaying = () => { + setShowWinModal(false); + setIsPlaying(true); + setDrawnNumbers([]); + setLastNumber(0); + setGameTime(0); + + setBingoCards(prev => prev.map(card => ({ + ...card, + markedNumbers: [] + }))); + }; + + + const handleBackToMenu = () => { + setShowWinModal(false); + router.push('/user'); + }; + + const handleNumberClick = (cardId: string, number: number) => { + if (number === 0) return; + + setBingoCards(prev => prev.map(card => { + if (card.id !== cardId) return card; + + const isMarked = card.markedNumbers.includes(number); + + return { + ...card, + markedNumbers: isMarked + ? card.markedNumbers.filter(n => n !== number) + : [...card.markedNumbers, number] + }; + })); + }; + + const getColumnLetter = (index: number) => { + const letters = ['B', 'I', 'N', 'G', 'O']; + return letters[index]; + }; + + + const renderBingoCard = (card: BingoCard) => ( +
+
+

{card.name}

+
+
+
+ +
+ {Array.from({ length: 5 }).map((_, index) => ( +
+ {getColumnLetter(index)} +
+ ))} +
+ + {card.numbers.map((row, rowIndex) => ( +
+ {row.map((number, colIndex) => { + const isFreeSpace = rowIndex === 2 && colIndex === 2; + const isMarked = isNumberMarked(card, number); + + return ( +
handleNumberClick(card.id, number)} + > + {isFreeSpace ? 'FREE' : number > 0 ? number : ''} +
+ ); + })} +
+ ))} +
+
+
+ ); + + return ( +
+ +
+
+

BINGO ONLINE

+

+ Jogo em andamento • Tempo: {formatTime(gameTime)} • Números sorteados: {drawnNumbers.length}/75 +

+
+ +
+
+ {user.avatarInitials} +
+
+
{user.name}
+
+ Saldo: R$ {user.balance.toFixed(2)} +
+
+
+
+ + +
+ +
+

Números Sorteados

+
+ {drawnNumbers.map((number, index) => ( +
+ {number} +
+ ))} + + {drawnNumbers.length === 0 && ( +
+ Aguardando primeiro sorteio... +
+ )} +
+ + {lastNumber > 0 && ( +
+ Último número: {lastNumber} +
+ )} +
+ +
+

Suas Cartelas

+ +
+
+ {bingoCards.map(renderBingoCard)} +
+
+ +
+
+ Total de números marcados: {bingoCards.reduce((acc, card) => acc + card.markedNumbers.length, 0)} +
+
+
+ + +
+
+ + + +
+ +
+

Clique nos números para marcar/desmarcar manualmente.

+

Quando completar uma linha, coluna ou diagonal, clique em "BINGO!"

+
+
+
+ + {showWinModal && ( +
+
+

+ 🎉 BINGO! 🎉 +

+ +
+ Parabéns! Você completou uma linha na cartela:
+ {winningCard} +
+ +
+ 🏆 Prêmio: R$ {winningPrize.toFixed(2)} 🏆 +
+ +
+ Seu novo saldo será:
+ + R$ {(user.balance + winningPrize).toFixed(2)} + +
+ +
+ + + +
+
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/frontend/src/app/reports/page.tsx b/frontend/src/app/reports/page.tsx new file mode 100644 index 0000000..641fc0e --- /dev/null +++ b/frontend/src/app/reports/page.tsx @@ -0,0 +1,276 @@ +'use client' +import { useState } from 'react'; +import Button from "@/app/components/button"; +import { useRouter } from 'next/navigation'; +import styles from './reports.module.css'; + +interface Statistic { + id: string; + label: string; + value: number | string; + change?: string; +} + +interface NumberDistribution { + range: string; + count: number; + percentage: number; +} + +interface GameSession { + id: string; + date: string; + time: string; + players: number; + winner: string; + prize: number; + numbersCalled: number; + duration: string; +} + +export default function ReportsPage() { + const router = useRouter(); + const [showExportModal, setShowExportModal] = useState(false); + const [exportFormat, setExportFormat] = useState<'pdf' | 'csv' | 'json'>('pdf'); + + + const [statistics] = useState([ + { id: '1', label: 'Partidas Jogadas', value: 42, change: '+12%' }, + { id: '2', label: 'Total de Jogadores', value: 156, change: '+5%' }, + { id: '3', label: 'Prêmios Distribuídos', value: 'R$ 8.450', change: '+18%' }, + { id: '4', label: 'Média por Partida', value: 'R$ 201', change: '+3%' }, + { id: '5', label: 'Taxa de Vitória', value: '4.7%', change: '-0.2%' }, + { id: '6', label: 'Números Sorteados', value: '1.764', change: '+8%' }, + ]); + + const [numberDistribution] = useState([ + { range: '1-15 (B)', count: 42, percentage: 17.5 }, + { range: '16-30 (I)', count: 38, percentage: 15.8 }, + { range: '31-45 (N)', count: 36, percentage: 15.0 }, + { range: '46-60 (G)', count: 39, percentage: 16.3 }, + { range: '61-75 (O)', count: 45, percentage: 18.8 }, + ]); + + const [mostFrequentNumbers] = useState<{ number: number; count: number }[]>([ + { number: 7, count: 28 }, + { number: 15, count: 26 }, + { number: 32, count: 25 }, + { number: 48, count: 24 }, + { number: 67, count: 23 }, + ]); + + + const [leastFrequentNumbers] = useState<{ number: number; count: number }[]>([ + { number: 1, count: 12 }, + { number: 20, count: 13 }, + { number: 35, count: 14 }, + { number: 59, count: 15 }, + { number: 74, count: 16 }, + ]); + + + const [gameSessions] = useState([ + { id: '1', date: '25/03/2024', time: '20:00', players: 45, winner: 'João Silva', prize: 300.00, numbersCalled: 43, duration: '15 min' }, + { id: '2', date: '24/03/2024', time: '15:00', players: 32, winner: 'Maria Santos', prize: 150.00, numbersCalled: 38, duration: '12 min' }, + { id: '3', date: '23/03/2024', time: '21:30', players: 58, winner: 'Carlos Lima', prize: 500.00, numbersCalled: 41, duration: '18 min' }, + { id: '4', date: '22/03/2024', time: '19:00', players: 28, winner: 'Ana Costa', prize: 120.00, numbersCalled: 35, duration: '10 min' }, + ]); + + const handleLogout = () => { + localStorage.removeItem('bingoToken'); + window.location.href = '/login'; + }; + + const formatCurrency = (amount: number) => { + return new Intl.NumberFormat('pt-BR', { + style: 'currency', + currency: 'BRL' + }).format(amount); + }; + + const handleExport = () => { + console.log(`Exportando relatório no formato: ${exportFormat}`); + + setShowExportModal(false); + alert(`Relatório exportado como ${exportFormat.toUpperCase()}!`); + }; + + return ( +
+ +
+ +
+ + +
+
+ +
+

Relatórios e Estatísticas

+

Análise detalhada das partidas e desempenho do jogo

+
+ + +
+
+
+

Estatísticas Gerais

+
+
+
+ {statistics.map(stat => ( +
+
{stat.value}
+
{stat.label}
+ {stat.change && ( + + {stat.change} + + )} +
+ ))} +
+
+
+ +
+
+

Distribuição de Números

+
+
+
+ {numberDistribution.map(item => ( +
+
{item.range}
+
+
+
+
{item.count}
+
+ ))} +
+
+
+ + +
+
+

Números Frequentes

+
+
+

Mais Sorteados:

+ + + + + + + + + {mostFrequentNumbers.map(item => ( + + + + + ))} + +
NúmeroQuantidade
BINGO-{item.number}{item.count} vezes
+ +

Menos Sorteados:

+ + + + + + + + + {leastFrequentNumbers.map(item => ( + + + + + ))} + +
NúmeroQuantidade
BINGO-{item.number}{item.count} vezes
+
+
+ + +
+
+

Histórico Recente

+
+
+
+ {gameSessions.map(session => ( +
+
+
+
+ {session.date} • {session.time} • {session.duration} +
+
+ {session.winner} venceu {formatCurrency(session.prize)} +
+
+ {session.players} jogadores • {session.numbersCalled} números +
+
+
+ ))} +
+
+
+
+ +
+ +
+
+
+ + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/app/reports/reports.module.css b/frontend/src/app/reports/reports.module.css new file mode 100644 index 0000000..d2edc54 --- /dev/null +++ b/frontend/src/app/reports/reports.module.css @@ -0,0 +1,380 @@ +/* Estilos da Navbar específica para relatórios */ +.navbarReportsPage { + width: 100%; + max-width: 1400px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; +} + +.navbarLinksLeftReports { + display: flex; + gap: 20px; + flex: 1; + justify-content: flex-start; +} + +.navbarLogoCenterReports { + flex: 0 0 auto; + margin: 0 40px; +} + +.navbarLogoReports { + height: 150px; + width: 150px; + display: block; +} + +.navbarLinksRightReports { + display: flex; + gap: 20px; + flex: 1; + justify-content: flex-end; +} + +.navbarLogoutReports { + margin-left: 20px; +} + +/* Conteúdo principal */ +.reportsMainContent { + padding: 40px 20px; + background-color: var(--yellow-bg); + min-height: calc(100vh - 120px); +} + +.reportsContainer { + max-width: 1400px; + margin: 0 auto; +} + +.reportsHeader { + text-align: center; + margin-bottom: 40px; +} + +.reportsHeader h1 { + color: var(--primary-green); + font-size: 36px; + margin-bottom: 10px; +} + +.reportsHeader p { + color: var(--secondary-green); + font-size: 18px; +} + +/* Grid de relatórios */ +.reportsGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 30px; + margin-bottom: 40px; +} + +.reportCard { + background-color: white; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + border: 2px solid var(--light-green); + overflow: hidden; +} + +.reportCardHeader { + background-color: var(--primary-green); + padding: 15px 30px; + border-radius: 10px 10px 0 0; +} + +.reportCardTitle { + color: var(--lime-bright); + margin: 0; + font-size: 22px; + display: flex; + align-items: center; + gap: 10px; +} + +.reportCardContent { + padding: 20px 30px; +} + +/* Estatísticas gerais */ +.statsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; +} + +.statItem { + display: flex; + flex-direction: column; + align-items: center; + padding: 15px; + background-color: var(--lime-bg); + border-radius: 8px; + border: 1px solid var(--light-green); +} + +.statValue { + font-size: 32px; + font-weight: bold; + color: var(--primary-green); + margin-bottom: 5px; +} + +.statLabel { + color: var(--secondary-green); + font-size: 14px; + text-align: center; +} + +/* Gráfico de distribuição */ +.distributionChart { + margin: 20px 0; +} + +.chartBar { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.chartLabel { + width: 50px; + color: var(--primary-green); + font-weight: bold; +} + +.chartBarInner { + flex: 1; + height: 20px; + background-color: var(--lime-bg); + border-radius: 10px; + overflow: hidden; + border: 1px solid var(--light-green); +} + +.chartBarFill { + height: 100%; + background-color: var(--primary-green); + border-radius: 10px; + transition: width 0.5s ease; +} + +.chartCount { + width: 40px; + text-align: right; + color: var(--secondary-green); + font-weight: bold; +} + +/* Tabela de números */ +.numbersTable { + width: 100%; + border-collapse: collapse; + margin-top: 15px; +} + +.numbersTable th { + background-color: var(--lime-bg); + color: var(--primary-green); + padding: 10px; + text-align: left; + border: 1px solid var(--light-green); +} + +.numbersTable td { + padding: 10px; + border: 1px solid var(--light-green); + color: var(--secondary-green); +} + +.numbersTable .highlight { + background-color: var(--lime-light); + color: var(--primary-green); + font-weight: bold; +} + +/* Gráfico de linha do tempo */ +.timeline { + position: relative; + padding-left: 30px; + margin: 20px 0; +} + +.timeline::before { + content: ''; + position: absolute; + left: 15px; + top: 0; + bottom: 0; + width: 2px; + background-color: var(--light-green); +} + +.timelineItem { + position: relative; + margin-bottom: 20px; +} + +.timelineDot { + position: absolute; + left: -30px; + top: 5px; + width: 10px; + height: 10px; + border-radius: 50%; + background-color: var(--primary-green); + border: 2px solid white; + box-shadow: 0 0 0 2px var(--primary-green); +} + +.timelineContent { + background-color: var(--lime-bg); + padding: 15px; + border-radius: 8px; + border: 1px solid var(--light-green); +} + +.timelineTime { + color: var(--secondary-green); + font-size: 14px; + margin-bottom: 5px; +} + +.timelineEvent { + color: var(--primary-green); + font-weight: 500; +} + +/* Botões de ação */ +.reportsActions { + display: flex; + gap: 15px; + justify-content: center; + margin-top: 30px; + flex-wrap: wrap; +} + +.exportButton { + background-color: var(--primary-green); + color: var(--lime-bright); + border: none; + padding: 12px 24px; + border-radius: 50px; + font-weight: bold; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + font-family: var(--font-baloo-bhaijaan); + transition: background-color 0.2s; +} + +.exportButton:hover { + background-color: var(--secondary-green); +} + +.exportButtonSecondary { + background-color: white; + color: var(--primary-green); + border: 2px solid var(--light-green); +} + +.exportButtonSecondary:hover { + background-color: var(--lime-bg); +} + +/* Modal de exportação */ +.modalOverlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modalContent { + background: white; + border-radius: 12px; + padding: 32px; + width: 90%; + max-width: 500px; + border: 2px solid var(--light-green); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +.modalHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; + border-bottom: 2px solid var(--lime-bg); + padding-bottom: 12px; +} + +.modalHeader h3 { + margin: 0; + color: var(--primary-green); + font-size: 20px; +} + +.closeButton { + background: none; + border: none; + font-size: 32px; + color: var(--primary-green); + cursor: pointer; + line-height: 1; +} + +.closeButton:hover { + color: var(--secondary-green); +} + +.modalBody { + margin-bottom: 24px; +} + +.modalActions { + display: flex; + gap: 12px; + justify-content: flex-end; +} + +/* Responsividade */ +@media (max-width: 768px) { + .reportsGrid { + grid-template-columns: 1fr; + } + + .statsGrid { + grid-template-columns: 1fr; + } + + .reportsActions { + flex-direction: column; + } + + .exportButton { + width: 100%; + justify-content: center; + } +} + +@media (max-width: 600px) { + .navbarReportsPage { + padding: 0 20px; + } + + .reportCardContent { + padding: 15px 20px; + } +} \ No newline at end of file