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:
@@ -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 = {
|
||||
bloco_a: {
|
||||
titulo: 'Coordenacao CAPES',
|
||||
@@ -623,6 +920,7 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
const [showRawModal, setShowRawModal] = useState(false);
|
||||
const [tipoAtuacaoModal, setTipoAtuacaoModal] = useState(null);
|
||||
const [seloModal, setSeloModal] = useState(null);
|
||||
const [itemDetalhe, setItemDetalhe] = useState(null);
|
||||
const cardRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -840,7 +1138,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
const periodo = vinculo.periodo || {};
|
||||
const isAtivo = periodo.ativo ?? !periodo.fim;
|
||||
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'}`}>
|
||||
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
||||
</span>
|
||||
@@ -868,7 +1170,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
{[...consultor.coordenacoes_capes]
|
||||
.sort((a, b) => new Date(b.inicio || b.periodo?.inicio || 0) - new Date(a.inicio || a.periodo?.inicio || 0))
|
||||
.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="pontos">{PONTOS_BASE[coord.codigo] || 0} pts</span>
|
||||
<span>{coord.area_avaliacao}</span>
|
||||
@@ -888,7 +1194,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
{[...consultor.premiacoes]
|
||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||
.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="pontos">{PONTOS_BASE[prem.codigo] || 0} pts</span>
|
||||
<span>{prem.nome_premio}</span>
|
||||
@@ -906,7 +1216,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
{[...consultor.avaliacoes_comissao]
|
||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||
.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="pontos">{PONTOS_BASE[aval.codigo] || 0} pts</span>
|
||||
<span>{aval.nome_comissao || aval.premio}</span>
|
||||
@@ -924,7 +1238,11 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
{[...consultor.inscricoes]
|
||||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||||
.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="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</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))
|
||||
.slice(0, 10)
|
||||
.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="pontos">{PONTOS_BASE[part.codigo] || 0} pts</span>
|
||||
<span>{part.descricao || part.tipo}</span>
|
||||
@@ -1025,6 +1347,14 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
onClose={() => setSeloModal(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{itemDetalhe && (
|
||||
<ItemDetalheModal
|
||||
item={itemDetalhe.item}
|
||||
tipo={itemDetalhe.tipo}
|
||||
onClose={() => setItemDetalhe(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user