feat(frontend): adicionar modais de detalhes para itens das listas
- Criar componente ItemDetalheModal para exibir detalhes de atuações - Adicionar modais clicáveis em: vínculos, coordenações, premiações, avaliações, inscrições e participações - Melhorar healthcheck do Oracle no docker-compose - Adicionar retry com backoff na conexão Oracle - Padronizar tamanho dos badges de tipos de atuação
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
import oracledb
|
import oracledb
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
@@ -15,32 +16,40 @@ class OracleClient:
|
|||||||
self._pool: Optional[oracledb.ConnectionPool] = None
|
self._pool: Optional[oracledb.ConnectionPool] = None
|
||||||
self._connected = False
|
self._connected = False
|
||||||
|
|
||||||
def connect(self) -> None:
|
def connect(self, max_retries: int = 10, initial_delay: float = 2.0) -> None:
|
||||||
try:
|
for attempt in range(max_retries):
|
||||||
test_conn = oracledb.connect(
|
try:
|
||||||
user=self.user,
|
test_conn = oracledb.connect(
|
||||||
password=self.password,
|
user=self.user,
|
||||||
dsn=self.dsn,
|
password=self.password,
|
||||||
)
|
dsn=self.dsn,
|
||||||
test_conn.ping()
|
)
|
||||||
test_conn.close()
|
test_conn.ping()
|
||||||
|
test_conn.close()
|
||||||
|
|
||||||
self._pool = oracledb.create_pool(
|
self._pool = oracledb.create_pool(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
password=self.password,
|
password=self.password,
|
||||||
dsn=self.dsn,
|
dsn=self.dsn,
|
||||||
min=1,
|
min=1,
|
||||||
max=10,
|
max=10,
|
||||||
increment=1,
|
increment=1,
|
||||||
)
|
)
|
||||||
self._connected = True
|
self._connected = True
|
||||||
logger.info(f"Pool Oracle conectado: {self.dsn}")
|
logger.info(f"Pool Oracle conectado: {self.dsn}")
|
||||||
except oracledb.Error as e:
|
return
|
||||||
logger.error(f"Oracle database error: {e}")
|
except oracledb.Error as e:
|
||||||
self._connected = False
|
delay = initial_delay * (2 ** attempt)
|
||||||
except Exception as e:
|
if attempt < max_retries - 1:
|
||||||
logger.error(f"Oracle connection error: {e}")
|
logger.warning(f"Oracle tentativa {attempt + 1}/{max_retries} falhou: {e}. Retry em {delay:.1f}s...")
|
||||||
self._connected = False
|
time.sleep(delay)
|
||||||
|
else:
|
||||||
|
logger.error(f"Oracle database error: {e}")
|
||||||
|
self._connected = False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Oracle connection error: {e}")
|
||||||
|
self._connected = False
|
||||||
|
return
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
if self._pool:
|
if self._pool:
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- oracle_data:/opt/oracle/oradata
|
- oracle_data:/opt/oracle/oradata
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "bash", "-c", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s SYSTEM/local123@localhost:1521/XEPDB1 | grep -q 1"]
|
test: ["CMD", "bash", "-c", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s local123/local123@localhost:1521/XEPDB1 | grep -q 1"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 30
|
retries: 30
|
||||||
|
|||||||
@@ -503,6 +503,83 @@
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-item-clicavel {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item-clicavel:hover {
|
||||||
|
background: rgba(6, 182, 212, 0.1);
|
||||||
|
border-left: 3px solid var(--accent-2);
|
||||||
|
padding-left: calc(0.5rem - 3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding: 0.7rem 0.5rem;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-row:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-label {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
min-width: 120px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-value {
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-align: right;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-value.pontos {
|
||||||
|
color: var(--accent-2);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-detalhe-value.muted {
|
||||||
|
color: var(--muted);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-titulo-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-titulo-icone {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-empty {
|
||||||
|
color: var(--muted);
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.details-grid {
|
.details-grid {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@@ -749,21 +826,21 @@
|
|||||||
color: var(--accent-2);
|
color: var(--accent-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipos-expandido {
|
.tipos-section .tipos-atuacao-container {
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipos-expandido .tipo-atuacao {
|
.tipos-expandido .tipo-atuacao {
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.35rem 0.6rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipos-expandido .tipo-icone {
|
.tipos-expandido .tipo-icone {
|
||||||
font-size: 0.8rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipos-expandido .tipo-label {
|
.tipos-expandido .tipo-label {
|
||||||
font-size: 0.65rem;
|
font-size: 0.72rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selos-section {
|
.selos-section {
|
||||||
|
|||||||
@@ -551,6 +551,303 @@ const SeloModal = ({ selo, consultor, onClose }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ItemDetalheModal = ({ item, tipo, onClose }) => {
|
||||||
|
if (!item || !tipo) return null;
|
||||||
|
|
||||||
|
const formatDate = (dateStr) => {
|
||||||
|
if (!dateStr) return 'N/A';
|
||||||
|
return new Date(dateStr).toLocaleDateString('pt-BR');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTitulo = () => {
|
||||||
|
switch (tipo) {
|
||||||
|
case 'vinculo': return 'Vínculo de Consultoria';
|
||||||
|
case 'coordenacao': return 'Coordenação CAPES';
|
||||||
|
case 'premiacao': return 'Premiação';
|
||||||
|
case 'avaliacao': return 'Avaliação de Comissão';
|
||||||
|
case 'inscricao': return 'Inscrição em Prêmio';
|
||||||
|
case 'participacao': return item.codigo === 'PROJ' ? 'Projeto' : 'Evento';
|
||||||
|
case 'orientacao': return 'Orientação';
|
||||||
|
default: return 'Detalhes';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIcone = () => {
|
||||||
|
switch (tipo) {
|
||||||
|
case 'vinculo': return '💼';
|
||||||
|
case 'coordenacao': return '🎯';
|
||||||
|
case 'premiacao': return '🏆';
|
||||||
|
case 'avaliacao': return '📋';
|
||||||
|
case 'inscricao': return '📝';
|
||||||
|
case 'participacao': return item.codigo === 'PROJ' ? '📊' : '📅';
|
||||||
|
case 'orientacao': return '🎓';
|
||||||
|
default: return '📄';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderContent = () => {
|
||||||
|
switch (tipo) {
|
||||||
|
case 'vinculo': {
|
||||||
|
const periodo = item.periodo || {};
|
||||||
|
const isAtivo = periodo.ativo ?? !periodo.fim;
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Status</span>
|
||||||
|
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
||||||
|
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Instituição</span>
|
||||||
|
<span className="modal-detalhe-value">
|
||||||
|
{item.ies ? (item.ies.sigla ? `${item.ies.sigla} - ${item.ies.nome || ''}` : item.ies.nome) : 'Não informada'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Início</span>
|
||||||
|
<span className="modal-detalhe-value">{formatDate(periodo.inicio)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Fim</span>
|
||||||
|
<span className="modal-detalhe-value">{isAtivo ? 'Em andamento' : formatDate(periodo.fim)}</span>
|
||||||
|
</div>
|
||||||
|
{item.situacao && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Situação</span>
|
||||||
|
<span className="modal-detalhe-value">{item.situacao}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'coordenacao': {
|
||||||
|
const isAtivo = item.ativo ?? !item.fim;
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo</span>
|
||||||
|
<span className="badge">{item.codigo || item.tipo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Área de Avaliação</span>
|
||||||
|
<span className="modal-detalhe-value">{item.area_avaliacao || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Status</span>
|
||||||
|
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
||||||
|
{isAtivo ? 'VIGENTE' : 'ENCERRADO'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Início</span>
|
||||||
|
<span className="modal-detalhe-value">{formatDate(item.inicio || item.periodo?.inicio)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Fim</span>
|
||||||
|
<span className="modal-detalhe-value">{isAtivo ? 'Em andamento' : formatDate(item.fim || item.periodo?.fim)}</span>
|
||||||
|
</div>
|
||||||
|
{item.presidente && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Função</span>
|
||||||
|
<span className="badge badge-premiado">👑 Presidente de Câmara</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'premiacao':
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Código</span>
|
||||||
|
<span className="badge">{item.codigo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Prêmio</span>
|
||||||
|
<span className="modal-detalhe-value">{item.nome_premio || item.premio || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Ano</span>
|
||||||
|
<span className="modal-detalhe-value">{item.ano || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
{item.papel && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Papel</span>
|
||||||
|
<span className="modal-detalhe-value">{item.papel}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.tipo && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo</span>
|
||||||
|
<span className="modal-detalhe-value">{item.tipo}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'avaliacao':
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Código</span>
|
||||||
|
<span className="badge">{item.codigo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Prêmio</span>
|
||||||
|
<span className="modal-detalhe-value">{item.premio || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
{item.nome_comissao && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Comissão</span>
|
||||||
|
<span className="modal-detalhe-value">{item.nome_comissao}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.comissao_tipo && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo Comissão</span>
|
||||||
|
<span className="modal-detalhe-value">{item.comissao_tipo}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.tipo && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Função</span>
|
||||||
|
<span className="modal-detalhe-value">{item.tipo}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Ano</span>
|
||||||
|
<span className="modal-detalhe-value">{item.ano || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'inscricao':
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Código</span>
|
||||||
|
<span className="badge">{item.codigo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Prêmio</span>
|
||||||
|
<span className="modal-detalhe-value">{item.premio || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
{item.tipo && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo Inscrição</span>
|
||||||
|
<span className="modal-detalhe-value">{item.tipo}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.situacao && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Situação</span>
|
||||||
|
<span className="modal-detalhe-value">{item.situacao}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Ano</span>
|
||||||
|
<span className="modal-detalhe-value">{item.ano || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'participacao':
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo</span>
|
||||||
|
<span className="badge">{item.codigo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Descrição</span>
|
||||||
|
<span className="modal-detalhe-value">{item.descricao || item.tipo || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Ano</span>
|
||||||
|
<span className="modal-detalhe-value">{item.ano || 'N/A'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação Base</span>
|
||||||
|
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'orientacao':
|
||||||
|
return (
|
||||||
|
<div className="modal-detalhe-content">
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Tipo</span>
|
||||||
|
<span className="badge">{item.codigo}</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Categoria</span>
|
||||||
|
<span className="modal-detalhe-value">
|
||||||
|
{item.codigo?.includes('TESE') ? 'Doutorado' : item.codigo?.includes('DISS') ? 'Mestrado' : 'Pós-Doutorado'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Função</span>
|
||||||
|
<span className="modal-detalhe-value">
|
||||||
|
{item.coorientacao || item.codigo?.startsWith('CO_') ? 'Coorientador' : 'Orientador'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{item.premiada && (
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Destaque</span>
|
||||||
|
<span className="badge badge-premiado">🏆 Premiada</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="modal-detalhe-row">
|
||||||
|
<span className="modal-detalhe-label">Pontuação</span>
|
||||||
|
<span className="modal-detalhe-value muted">Apenas selo (sem pontuação)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return <p className="modal-empty">Sem detalhes disponíveis</p>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="tipo-modal-overlay" onClick={onClose}>
|
||||||
|
<div className="tipo-modal" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<div className="tipo-modal-header">
|
||||||
|
<span className="modal-titulo-item">
|
||||||
|
<span className="modal-titulo-icone">{getIcone()}</span>
|
||||||
|
<span>{getTitulo()}</span>
|
||||||
|
</span>
|
||||||
|
<button className="tipo-modal-close" onClick={onClose}>✕</button>
|
||||||
|
</div>
|
||||||
|
<div className="tipo-modal-body">
|
||||||
|
{renderContent()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const FORMULAS = {
|
const FORMULAS = {
|
||||||
bloco_a: {
|
bloco_a: {
|
||||||
titulo: 'Coordenacao CAPES',
|
titulo: 'Coordenacao CAPES',
|
||||||
@@ -623,6 +920,7 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
const [showRawModal, setShowRawModal] = useState(false);
|
const [showRawModal, setShowRawModal] = useState(false);
|
||||||
const [tipoAtuacaoModal, setTipoAtuacaoModal] = useState(null);
|
const [tipoAtuacaoModal, setTipoAtuacaoModal] = useState(null);
|
||||||
const [seloModal, setSeloModal] = useState(null);
|
const [seloModal, setSeloModal] = useState(null);
|
||||||
|
const [itemDetalhe, setItemDetalhe] = useState(null);
|
||||||
const cardRef = useRef(null);
|
const cardRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -840,7 +1138,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
const periodo = vinculo.periodo || {};
|
const periodo = vinculo.periodo || {};
|
||||||
const isAtivo = periodo.ativo ?? !periodo.fim;
|
const isAtivo = periodo.ativo ?? !periodo.fim;
|
||||||
return (
|
return (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: vinculo, tipo: 'vinculo' })}
|
||||||
|
>
|
||||||
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
||||||
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
||||||
</span>
|
</span>
|
||||||
@@ -868,7 +1170,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
{[...consultor.coordenacoes_capes]
|
{[...consultor.coordenacoes_capes]
|
||||||
.sort((a, b) => new Date(b.inicio || b.periodo?.inicio || 0) - new Date(a.inicio || a.periodo?.inicio || 0))
|
.sort((a, b) => new Date(b.inicio || b.periodo?.inicio || 0) - new Date(a.inicio || a.periodo?.inicio || 0))
|
||||||
.map((coord, idx) => (
|
.map((coord, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: coord, tipo: 'coordenacao' })}
|
||||||
|
>
|
||||||
<span className="badge">{coord.codigo || coord.tipo}</span>
|
<span className="badge">{coord.codigo || coord.tipo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[coord.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[coord.codigo] || 0} pts</span>
|
||||||
<span>{coord.area_avaliacao}</span>
|
<span>{coord.area_avaliacao}</span>
|
||||||
@@ -888,7 +1194,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
{[...consultor.premiacoes]
|
{[...consultor.premiacoes]
|
||||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
.map((prem, idx) => (
|
.map((prem, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: prem, tipo: 'premiacao' })}
|
||||||
|
>
|
||||||
<span className="badge">{prem.codigo}</span>
|
<span className="badge">{prem.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[prem.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[prem.codigo] || 0} pts</span>
|
||||||
<span>{prem.nome_premio}</span>
|
<span>{prem.nome_premio}</span>
|
||||||
@@ -906,7 +1216,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
{[...consultor.avaliacoes_comissao]
|
{[...consultor.avaliacoes_comissao]
|
||||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
.map((aval, idx) => (
|
.map((aval, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: aval, tipo: 'avaliacao' })}
|
||||||
|
>
|
||||||
<span className="badge">{aval.codigo}</span>
|
<span className="badge">{aval.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[aval.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[aval.codigo] || 0} pts</span>
|
||||||
<span>{aval.nome_comissao || aval.premio}</span>
|
<span>{aval.nome_comissao || aval.premio}</span>
|
||||||
@@ -924,7 +1238,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
{[...consultor.inscricoes]
|
{[...consultor.inscricoes]
|
||||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
.map((insc, idx) => (
|
.map((insc, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: insc, tipo: 'inscricao' })}
|
||||||
|
>
|
||||||
<span className="badge">{insc.codigo}</span>
|
<span className="badge">{insc.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</span>
|
||||||
<span>{insc.premio}</span>
|
<span>{insc.premio}</span>
|
||||||
@@ -943,7 +1261,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||||
.slice(0, 10)
|
.slice(0, 10)
|
||||||
.map((part, idx) => (
|
.map((part, idx) => (
|
||||||
<div key={idx} className="list-item">
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="list-item list-item-clicavel"
|
||||||
|
onClick={() => setItemDetalhe({ item: part, tipo: 'participacao' })}
|
||||||
|
>
|
||||||
<span className="badge">{part.codigo}</span>
|
<span className="badge">{part.codigo}</span>
|
||||||
<span className="pontos">{PONTOS_BASE[part.codigo] || 0} pts</span>
|
<span className="pontos">{PONTOS_BASE[part.codigo] || 0} pts</span>
|
||||||
<span>{part.descricao || part.tipo}</span>
|
<span>{part.descricao || part.tipo}</span>
|
||||||
@@ -1025,6 +1347,14 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
onClose={() => setSeloModal(null)}
|
onClose={() => setSeloModal(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{itemDetalhe && (
|
||||||
|
<ItemDetalheModal
|
||||||
|
item={itemDetalhe.item}
|
||||||
|
tipo={itemDetalhe.tipo}
|
||||||
|
onClose={() => setItemDetalhe(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user