Files
ranking/index-teste.html
Frederico Castro 9e6ba459a8 feat: Sistema de Ranking de Consultores CAPES - versão inicial
Backend (FastAPI + DDD):
- Arquitetura DDD com camadas Domain, Application, Infrastructure, Interface
- Integração com Elasticsearch (ATUACAPES) para dados de consultores
- Integração com Oracle (SUCUPIRA_PAINEL) para coordenações PPG
- Cálculo dos 4 componentes de pontuação (A, B, C, D)
- Cache em memória para otimização de performance
- API REST com endpoints /ranking, /ranking/detalhado, /consultor/{id}

Frontend (React + Vite):
- Interface responsiva com cards expansíveis
- Visualização detalhada de pontuação por componente
- Filtro por quantidade de consultores (Top 10, 50, 100, etc)

Docker:
- docker-compose com shared_network externa
- Backend com Oracle Instant Client
- Frontend com Vite dev server
2025-12-09 01:24:35 -03:00

153 lines
4.7 KiB
HTML

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Ranking de Consultores CAPES - Teste</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg-base: #0b1220;
--stroke: rgba(255,255,255,0.09);
--accent: #4f46e5;
--accent-2: #16a9fa;
--success: #22c55e;
--gold: #fbbf24;
--text: #f8fafc;
--muted: #94a3b8;
--shadow: 0 25px 50px -12px rgba(0,0,0,0.45);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Space Grotesk', system-ui;
background: radial-gradient(circle at 15% 20%, rgba(79,70,229,0.18), transparent 25%),
radial-gradient(circle at 75% 0%, rgba(22,169,250,0.18), transparent 26%),
var(--bg-base);
color: var(--text);
min-height: 100vh;
padding: 2.25rem;
}
.container { max-width: 1280px; margin: 0 auto; }
header {
padding: 1.5rem;
background: linear-gradient(145deg, rgba(79,70,229,0.18), rgba(22,169,250,0.14));
border: 1px solid var(--stroke);
border-radius: 18px;
margin-bottom: 2rem;
}
h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
.loading { text-align: center; padding: 4rem; font-size: 1.5rem; color: var(--accent-2); }
.ranking-card {
background: linear-gradient(155deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02));
border: 1px solid var(--stroke);
border-radius: 18px;
padding: 1.5rem;
margin-bottom: 1rem;
display: grid;
grid-template-columns: 70px 1fr auto;
align-items: center;
gap: 1.5rem;
cursor: pointer;
transition: transform 200ms;
}
.ranking-card:hover { transform: translateY(-4px); }
.rank {
width: 62px;
height: 62px;
display: grid;
place-items: center;
font-size: 1.35rem;
font-weight: 700;
border-radius: 14px;
background: linear-gradient(145deg, rgba(255,255,255,0.08), rgba(255,255,255,0.03));
}
.rank-1 { background: linear-gradient(145deg, #fbbf24, #f59e0b); color: #0f172a; }
.rank-2 { background: linear-gradient(145deg, #cbd5e1, #94a3b8); color: #0f172a; }
.rank-3 { background: linear-gradient(145deg, #fb923c, #f97316); color: #0f172a; }
.name { font-size: 1.15rem; font-weight: 700; }
.badge {
display: inline-block;
font-size: 0.7rem;
padding: 0.3rem 0.6rem;
border-radius: 999px;
margin-left: 0.5rem;
text-transform: uppercase;
}
.badge-ativo { background: var(--success); color: white; }
.badge-veterano { background: var(--gold); color: #0f172a; }
.info { color: var(--muted); font-size: 0.9rem; }
.score { font-size: 2rem; font-weight: 800; color: var(--accent-2); }
.controls { margin: 1.5rem 0; }
.controls select {
padding: 0.5rem 1rem;
background: rgba(255,255,255,0.06);
border: 1px solid var(--stroke);
border-radius: 8px;
color: var(--text);
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Ranking de Consultores CAPES</h1>
<p>Sistema completo - 4 Componentes (Coordenação CAPES + PPG + Consultoria + Premiações)</p>
</header>
<div class="controls">
<label>
Limite:
<select id="limite" onchange="loadRanking()">
<option value="10">Top 10</option>
<option value="50">Top 50</option>
<option value="100" selected>Top 100</option>
</select>
</label>
</div>
<div id="ranking" class="ranking-list">
<div class="loading">Carregando ranking...</div>
</div>
</div>
<script>
async function loadRanking() {
const limite = document.getElementById('limite').value;
const container = document.getElementById('ranking');
container.innerHTML = '<div class="loading">Carregando ranking...</div>';
try {
const response = await fetch(`/api/v1/ranking?limite=${limite}`);
const data = await response.json();
container.innerHTML = '';
data.consultores.forEach(c => {
const card = document.createElement('div');
card.className = 'ranking-card';
card.innerHTML = `
<div class="rank rank-${c.rank <= 3 ? c.rank : ''}">#${c.rank}</div>
<div>
<div class="name">
${c.nome}
${c.ativo ? '<span class="badge badge-ativo">Ativo</span>' : ''}
${c.veterano ? '<span class="badge badge-veterano">Veterano</span>' : ''}
</div>
<div class="info">${c.anos_atuacao} anos de atuação</div>
</div>
<div class="score">${c.pontuacao_total}</div>
`;
container.appendChild(card);
});
} catch (err) {
container.innerHTML = `<div class="loading" style="color:red;">Erro: ${err.message}</div>`;
}
}
loadRanking();
</script>
</body>
</html>