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
This commit is contained in:
152
index-teste.html
Normal file
152
index-teste.html
Normal file
@@ -0,0 +1,152 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user