diff --git a/frontend/src/App.css b/frontend/src/App.css index abd08dd..6b4fe5f 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -49,7 +49,7 @@ .controls { margin: 1.5rem 0; display: flex; - gap: 1rem; + gap: 1.5rem; flex-wrap: wrap; align-items: center; } @@ -77,6 +77,37 @@ border-color: var(--accent-2); } +.pagination { + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; +} + +.pagination button { + padding: 0.6rem 0.9rem; + border-radius: 8px; + border: 1px solid var(--stroke); + background: rgba(255,255,255,0.06); + color: var(--text); + cursor: pointer; + transition: all 150ms ease; +} + +.pagination button:hover:not(:disabled) { + border-color: var(--accent-2); +} + +.pagination button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.page-info { + color: var(--muted); + font-weight: 500; +} + .ranking-list { display: flex; flex-direction: column; diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 4549c6e..418ac3f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -9,19 +9,23 @@ function App() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [total, setTotal] = useState(0); - const [limite, setLimite] = useState(100); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(50); + const [totalPages, setTotalPages] = useState(0); useEffect(() => { loadRanking(); - }, [limite]); + }, [page, pageSize]); const loadRanking = async () => { try { setLoading(true); setError(null); - const response = await rankingService.getRanking(limite); + const response = await rankingService.getRanking(page, pageSize); setConsultores(response.consultores); setTotal(response.total); + setTotalPages(response.total_pages || 0); + setPage(response.page || page); } catch (err) { console.error('Erro ao carregar ranking:', err); setError('Erro ao carregar ranking. Verifique se a API está rodando.'); @@ -57,7 +61,7 @@ function App() {
+ +
+ + + + Página {page} de {totalPages || '?'} + + + +
diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index d4c2821..183e916 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -9,13 +9,12 @@ const api = axios.create({ }); export const rankingService = { - async getRanking(limite = 100) { - // Usa ranking paginado (Oracle) para não depender do Elasticsearch - const params = { page: 1, size: limite }; + async getRanking(page = 1, size = 100) { + // Usa ranking paginado (Oracle) para percorrer os 350k + const params = { page, size }; const response = await api.get('/ranking/paginado', { params }); const data = response.data; - // Adapta para o formato esperado pelo frontend const hoje = new Date(); const consultores = (data.consultores || []).map((c) => { @@ -35,35 +34,38 @@ export const rankingService = { rank: c.posicao, posicao: c.posicao, pontuacao_total: c.pontuacao_total, - componente_a: c.componente_a, - componente_b: c.componente_b, - componente_c: c.componente_c, - componente_d: c.componente_d, - ativo: c.ativo, - anos_atuacao: c.anos_atuacao, - veterano: anos >= 10, - pontuacao: { - pontuacao_total: c.pontuacao_total, - componente_a: { base: c.componente_a, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_a }, - componente_b: { base: c.componente_b, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_b }, - componente_c: { base: c.componente_c, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_c }, - componente_d: { base: c.componente_d, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_d }, - }, - consultoria: { - total_eventos: consultoria.total_eventos ?? 0, - eventos_recentes: consultoria.eventos_recentes ?? 0, - vezes_responsavel: consultoria.vezes_responsavel ?? 0, - primeiro_evento: consultoria.primeiro_evento || primeiroEvento.toISOString(), - ultimo_evento: consultoria.ultimo_evento || null, - }, - coordenacoes_capes: [], - coordenacoes_programas: [], - premiacoes: [], - }}); + componente_a: c.componente_a, + componente_b: c.componente_b, + componente_c: c.componente_c, + componente_d: c.componente_d, + ativo: c.ativo, + anos_atuacao: anos, + veterano: anos >= 10, + pontuacao: { + pontuacao_total: c.pontuacao_total, + componente_a: { base: c.componente_a, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_a }, + componente_b: { base: c.componente_b, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_b }, + componente_c: { base: c.componente_c, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_c }, + componente_d: { base: c.componente_d, tempo: 0, extras: 0, bonus: 0, retorno: 0, total: c.componente_d }, + }, + consultoria: { + total_eventos: consultoria.total_eventos ?? 0, + eventos_recentes: consultoria.eventos_recentes ?? 0, + vezes_responsavel: consultoria.vezes_responsavel ?? 0, + primeiro_evento: consultoria.primeiro_evento || primeiroEvento.toISOString(), + ultimo_evento: consultoria.ultimo_evento || null, + }, + coordenacoes_capes: [], + coordenacoes_programas: [], + premiacoes: [], + }; + }); return { total: data.total, total_pages: data.total_pages, + page: data.page ?? page, + size: data.size ?? size, consultores, }; },