2728 lines
115 KiB
JavaScript
2728 lines
115 KiB
JavaScript
import React, { useState, useRef, useEffect, useMemo, memo } from 'react';
|
||
import { createPortal } from 'react-dom';
|
||
import './ConsultorCard.css';
|
||
import RawDataModal from './RawDataModal';
|
||
import { rankingService } from '../services/api';
|
||
|
||
const SELOS = {
|
||
CA: { codigo: 'CA', label: 'Coord. Área', cor: 'selo-ca', icone: '🎯' },
|
||
CAJ: { codigo: 'CAJ', label: 'Coord. Adjunto', cor: 'selo-caj', icone: '📋' },
|
||
CAJ_MP: { codigo: 'CAJ_MP', label: 'Coord. Adj. MP', cor: 'selo-caj-mp', icone: '📋' },
|
||
CAM: { codigo: 'CAM', label: 'Câmara Temática', cor: 'selo-cam', icone: '🏛️' },
|
||
PRESID_CAMARA: { codigo: 'PRESID_CAMARA', label: 'Presidente Câmara', cor: 'selo-camara', icone: '👑' },
|
||
CONS_ATIVO: { codigo: 'CONS_ATIVO', label: 'Consultor', cor: 'selo-cons-ativo', icone: '✅' },
|
||
AVAL_COMIS: { codigo: 'AVAL_COMIS', label: 'Avaliador', cor: 'selo-aval', icone: '⚖️' },
|
||
COORD_COMIS: { codigo: 'COORD_COMIS', label: 'Coord. Comissão', cor: 'selo-coord-comis', icone: '📊' },
|
||
AUTOR_GP: { codigo: 'AUTOR_GP', label: 'Autor GP', cor: 'selo-gp', icone: '🏆' },
|
||
AUTOR_PREMIO: { codigo: 'AUTOR_PREMIO', label: 'Autor Prêmio', cor: 'selo-premio', icone: '🥇' },
|
||
AUTOR_MENCAO: { codigo: 'AUTOR_MENCAO', label: 'Autor Menção', cor: 'selo-mencao', icone: '🥈' },
|
||
ORIENT_GP: { codigo: 'ORIENT_GP', label: 'Orient. GP', cor: 'selo-gp', icone: '🏆' },
|
||
ORIENT_PREMIO: { codigo: 'ORIENT_PREMIO', label: 'Orient. Prêmio', cor: 'selo-orient-premio', icone: '🎖️' },
|
||
ORIENT_MENCAO: { codigo: 'ORIENT_MENCAO', label: 'Orient. Menção', cor: 'selo-orient-mencao', icone: '📜' },
|
||
COORIENT_GP: { codigo: 'COORIENT_GP', label: 'Coorient. GP', cor: 'selo-gp', icone: '🏆' },
|
||
COORIENT_PREMIO: { codigo: 'COORIENT_PREMIO', label: 'Coorient. Prêmio', cor: 'selo-coorient-premio', icone: '🎖️' },
|
||
COORIENT_MENCAO: { codigo: 'COORIENT_MENCAO', label: 'Coorient. Menção', cor: 'selo-coorient-mencao', icone: '📜' },
|
||
ORIENT_TESE: { codigo: 'ORIENT_TESE', label: 'Orient. Tese', cor: 'selo-orient', icone: '📚' },
|
||
ORIENT_DISS: { codigo: 'ORIENT_DISS', label: 'Orient. Diss.', cor: 'selo-orient', icone: '📄' },
|
||
EVENTO: { codigo: 'EVENTO', label: 'Evento', cor: 'selo-evento', icone: '🎪' },
|
||
PROJ: { codigo: 'PROJ', label: 'Projeto', cor: 'selo-proj', icone: '🔧' },
|
||
IDIOMA_MULTILINGUE: { codigo: 'IDIOMA_MULTILINGUE', label: 'Multilíngue', cor: 'selo-idioma', icone: '🌐' },
|
||
};
|
||
|
||
const TIPOS_ATUACAO_CONFIG = {
|
||
'Coordenador': { cor: 'tipo-coordenador', icone: '🎯' },
|
||
'Consultor': { cor: 'tipo-consultor', icone: '💼' },
|
||
'Avaliador': { cor: 'tipo-avaliador', icone: '📋' },
|
||
'Premiado': { cor: 'tipo-premiado', icone: '🏆' },
|
||
'Orientador': { cor: 'tipo-orientador', icone: '🎓' },
|
||
'Bolsista CNPq': { cor: 'tipo-bolsista', icone: '🔬' },
|
||
'Inscrito Premio': { cor: 'tipo-inscrito', icone: '📝' },
|
||
'Projeto': { cor: 'tipo-projeto', icone: '📊' },
|
||
'Evento': { cor: 'tipo-evento', icone: '📅' },
|
||
};
|
||
|
||
const gerarSelos = (consultor) => {
|
||
const selos = [];
|
||
const normalizarIdioma = (valor) => (valor || '')
|
||
.toString()
|
||
.normalize('NFD')
|
||
.replace(/[\u0300-\u036f]/g, '')
|
||
.toLowerCase()
|
||
.trim();
|
||
|
||
const coordenacoes = Array.isArray(consultor.coordenacoes_capes) ? consultor.coordenacoes_capes : [];
|
||
const coordCA = coordenacoes.filter((c) => c.codigo === 'CA');
|
||
const coordCAJ = coordenacoes.filter((c) => c.codigo === 'CAJ');
|
||
const coordCAJ_MP = coordenacoes.filter((c) => c.codigo === 'CAJ_MP');
|
||
const coordCAM = coordenacoes.filter((c) => c.codigo === 'CAM');
|
||
|
||
if (coordCA.length > 0) {
|
||
selos.push({ ...SELOS.CA, qtd: coordCA.length, hint: `Coordenador de Área (${coordCA.length}x)` });
|
||
}
|
||
if (coordCAJ.length > 0) {
|
||
selos.push({ ...SELOS.CAJ, qtd: coordCAJ.length, hint: `Coordenador Adjunto (${coordCAJ.length}x)` });
|
||
}
|
||
if (coordCAJ_MP.length > 0) {
|
||
selos.push({ ...SELOS.CAJ_MP, qtd: coordCAJ_MP.length, hint: `Coord. Adjunto MP (${coordCAJ_MP.length}x)` });
|
||
}
|
||
if (coordCAM.length > 0) {
|
||
selos.push({ ...SELOS.CAM, qtd: coordCAM.length, hint: `Câmara Temática (${coordCAM.length}x)` });
|
||
}
|
||
|
||
const isPresidCamaraVigente = coordenacoes.some(
|
||
(c) => c.codigo === 'CAM' && c.presidente && (c.ativo ?? !c.fim)
|
||
);
|
||
if (isPresidCamaraVigente) {
|
||
selos.push({ ...SELOS.PRESID_CAMARA, qtd: 1, hint: 'Presidente Câmara Temática' });
|
||
}
|
||
|
||
const consultoria = consultor.consultoria;
|
||
if (consultoria && consultoria.codigo === 'CONS_ATIVO') {
|
||
selos.push({ ...SELOS.CONS_ATIVO, qtd: 1, hint: 'Consultor Ativo' });
|
||
}
|
||
|
||
const avaliacoes = Array.isArray(consultor.avaliacoes_comissao) ? consultor.avaliacoes_comissao : [];
|
||
const coordComissao = avaliacoes.filter((a) => (a.codigo || '').includes('COORD'));
|
||
const avalComissao = avaliacoes.filter((a) => (a.codigo || '').includes('AVAL'));
|
||
if (coordComissao.length > 0) {
|
||
selos.push({ ...SELOS.COORD_COMIS, qtd: coordComissao.length, hint: `Coord. Comissão (${coordComissao.length}x)` });
|
||
}
|
||
if (avalComissao.length > 0) {
|
||
selos.push({ ...SELOS.AVAL_COMIS, qtd: avalComissao.length, hint: `Avaliador Comissão (${avalComissao.length}x)` });
|
||
}
|
||
|
||
const premiacoes = Array.isArray(consultor.premiacoes) ? consultor.premiacoes : [];
|
||
|
||
const gerarSelosPorPapel = (papel, seloGP, seloPremio, seloMencao, hintPrefix) => {
|
||
const lista = premiacoes.filter((p) => (p.papel || '').toString().toLowerCase() === papel.toLowerCase());
|
||
const gp = lista.filter((p) => p.codigo === 'PREMIACAO_GP_AUTOR').length;
|
||
const premio = lista.filter((p) => p.codigo === 'PREMIACAO_AUTOR').length;
|
||
const mencao = lista.filter((p) => p.codigo === 'MENCAO_AUTOR').length;
|
||
|
||
if (gp > 0) selos.push({ ...seloGP, qtd: gp, hint: `${hintPrefix} - Grande Prêmio` });
|
||
if (premio > 0) selos.push({ ...seloPremio, qtd: premio, hint: `${hintPrefix} - Prêmio` });
|
||
if (mencao > 0) selos.push({ ...seloMencao, qtd: mencao, hint: `${hintPrefix} - Menção Honrosa` });
|
||
};
|
||
|
||
gerarSelosPorPapel('autor', SELOS.AUTOR_GP, SELOS.AUTOR_PREMIO, SELOS.AUTOR_MENCAO, 'Autor');
|
||
gerarSelosPorPapel('orientador', SELOS.ORIENT_GP, SELOS.ORIENT_PREMIO, SELOS.ORIENT_MENCAO, 'Orientador');
|
||
gerarSelosPorPapel('coorientador', SELOS.COORIENT_GP, SELOS.COORIENT_PREMIO, SELOS.COORIENT_MENCAO, 'Coorientador');
|
||
|
||
const orientacoes = Array.isArray(consultor.orientacoes) ? consultor.orientacoes : [];
|
||
const orientTese = orientacoes.filter((o) => o.codigo === 'ORIENT_TESE' || o.codigo === 'ORIENT_TESE_PREM');
|
||
const orientDiss = orientacoes.filter((o) => o.codigo === 'ORIENT_DISS' || o.codigo === 'ORIENT_DISS_PREM');
|
||
if (orientTese.length > 0) {
|
||
selos.push({ ...SELOS.ORIENT_TESE, qtd: orientTese.length, hint: `Orient. Tese (${orientTese.length}x)` });
|
||
}
|
||
if (orientDiss.length > 0) {
|
||
selos.push({ ...SELOS.ORIENT_DISS, qtd: orientDiss.length, hint: `Orient. Dissertação (${orientDiss.length}x)` });
|
||
}
|
||
|
||
const participacoes = Array.isArray(consultor.participacoes) ? consultor.participacoes : [];
|
||
const eventos = participacoes.filter((p) => p.codigo === 'EVENTO');
|
||
const projetos = participacoes.filter((p) => p.codigo === 'PROJ');
|
||
if (eventos.length > 0) {
|
||
selos.push({ ...SELOS.EVENTO, qtd: eventos.length, hint: `Eventos (${eventos.length}x)` });
|
||
}
|
||
if (projetos.length > 0) {
|
||
selos.push({ ...SELOS.PROJ, qtd: projetos.length, hint: `Projetos (${projetos.length}x)` });
|
||
}
|
||
|
||
const idiomas = Array.isArray(consultor.idiomas) ? consultor.idiomas : [];
|
||
const idiomasUnicosMap = new Map();
|
||
let temPortugues = false;
|
||
for (const idioma of idiomas) {
|
||
const nome = idioma?.idioma || idioma?.nome || idioma?.descricao || '';
|
||
const chave = normalizarIdioma(nome);
|
||
if (!chave) continue;
|
||
if (!idiomasUnicosMap.has(chave)) {
|
||
idiomasUnicosMap.set(chave, nome);
|
||
}
|
||
if (chave.includes('portugues') || chave.includes('portuguese')) {
|
||
temPortugues = true;
|
||
}
|
||
}
|
||
const idiomasUnicos = Array.from(idiomasUnicosMap.values());
|
||
const totalIdiomas = idiomasUnicos.length + (!temPortugues && idiomasUnicos.length > 0 ? 1 : 0);
|
||
if (totalIdiomas >= 3) {
|
||
selos.push({
|
||
...SELOS.IDIOMA_MULTILINGUE,
|
||
qtd: totalIdiomas,
|
||
hint: `Multilingue: ${idiomasUnicos.join(', ')}`,
|
||
});
|
||
}
|
||
|
||
return selos;
|
||
};
|
||
|
||
const SELOS_COM_DADOS = [
|
||
'CA', 'CAJ', 'CAJ_MP', 'CAM', 'PRESID_CAMARA',
|
||
'CONS_ATIVO',
|
||
'AVAL_COMIS', 'COORD_COMIS',
|
||
'AUTOR_GP', 'AUTOR_PREMIO', 'AUTOR_MENCAO',
|
||
'ORIENT_GP', 'ORIENT_PREMIO', 'ORIENT_MENCAO',
|
||
'COORIENT_GP', 'COORIENT_PREMIO', 'COORIENT_MENCAO',
|
||
'ORIENT_TESE', 'ORIENT_DISS',
|
||
'EVENTO', 'PROJ',
|
||
'IDIOMA_MULTILINGUE',
|
||
];
|
||
|
||
const SelosBadges = ({ selos, compacto = false, onSeloClick }) => {
|
||
if (!selos || selos.length === 0) return null;
|
||
|
||
const handleClick = (e, selo) => {
|
||
if (onSeloClick && SELOS_COM_DADOS.includes(selo.codigo)) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
onSeloClick(selo);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={`selos-container ${compacto ? 'selos-compacto' : ''}`}>
|
||
{selos.map((selo, idx) => {
|
||
const temDados = SELOS_COM_DADOS.includes(selo.codigo);
|
||
return (
|
||
<span
|
||
key={idx}
|
||
className={`selo ${selo.cor} ${onSeloClick && temDados ? 'selo-clicavel' : ''}`}
|
||
title={selo.hint || `${selo.label}${selo.qtd > 1 ? ` (${selo.qtd}x)` : ''}`}
|
||
onMouseDown={(e) => onSeloClick && temDados && e.stopPropagation()}
|
||
onClick={(e) => handleClick(e, selo)}
|
||
>
|
||
<span className="selo-icone">{selo.icone}</span>
|
||
{!compacto && <span className="selo-label">{selo.label}</span>}
|
||
{!compacto && <span className="selo-qtd">{selo.qtd || 1}</span>}
|
||
</span>
|
||
);
|
||
})}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const TiposAtuacaoBadges = ({ tipos, exibirTodos = false, onBadgeClick, consultor }) => {
|
||
if (!tipos || tipos.length === 0) return null;
|
||
|
||
const tiposExibidos = exibirTodos ? tipos : tipos.slice(0, 4);
|
||
const tiposOcultos = !exibirTodos && tipos.length > 4 ? tipos.length - 4 : 0;
|
||
|
||
const handleClick = (e, tipo) => {
|
||
if (onBadgeClick) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
onBadgeClick(tipo);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={`tipos-atuacao-container ${exibirTodos ? 'tipos-expandido' : ''}`}>
|
||
{tiposExibidos.map((tipo, idx) => {
|
||
const config = TIPOS_ATUACAO_CONFIG[tipo] || { cor: 'tipo-default', icone: '📌' };
|
||
return (
|
||
<span
|
||
key={idx}
|
||
className={`tipo-atuacao ${config.cor} ${onBadgeClick ? 'tipo-clicavel' : ''}`}
|
||
title={`Clique para ver detalhes de ${tipo}`}
|
||
onMouseDown={(e) => onBadgeClick && e.stopPropagation()}
|
||
onClick={(e) => handleClick(e, tipo)}
|
||
>
|
||
<span className="tipo-icone">{config.icone}</span>
|
||
<span className="tipo-label">{tipo}</span>
|
||
</span>
|
||
);
|
||
})}
|
||
{tiposOcultos > 0 && (
|
||
<span className="tipo-atuacao tipo-mais" title={tipos.slice(4).join(', ')}>
|
||
+{tiposOcultos}
|
||
</span>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const TipoAtuacaoModal = ({ tipo, consultor, onClose }) => {
|
||
if (!tipo || !consultor) return null;
|
||
|
||
const formatDate = (dateStr) => {
|
||
if (!dateStr) return 'Atual';
|
||
return new Date(dateStr).toLocaleDateString('pt-BR');
|
||
};
|
||
|
||
const renderContent = () => {
|
||
switch (tipo) {
|
||
case 'Coordenador': {
|
||
const coords = consultor.coordenacoes_capes || [];
|
||
if (coords.length === 0) return <p className="modal-empty">Sem dados de coordenação</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...coords].sort((a, b) => new Date(b.inicio || 0) - new Date(a.inicio || 0)).map((c, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{c.codigo}</span>
|
||
<span className="modal-item-main">{c.area_avaliacao}</span>
|
||
{c.presidente && <span className="badge badge-premiado">👑 Presidente</span>}
|
||
<span className="muted">{formatDate(c.inicio)} - {formatDate(c.fim)}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Consultor': {
|
||
const cons = consultor.consultoria;
|
||
if (!cons) return <p className="modal-empty">Sem dados de consultoria</p>;
|
||
const vinculos = cons.vinculos || [];
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">
|
||
<span>Anos consecutivos: <strong>{cons.anos_consecutivos || 0}</strong></span>
|
||
<span>Início: <strong>{formatDate(cons.inicio)}</strong></span>
|
||
</div>
|
||
{vinculos.length > 0 && (
|
||
<>
|
||
<h5>Vínculos ({vinculos.length})</h5>
|
||
{[...vinculos].sort((a, b) => new Date(b.periodo?.inicio || 0) - new Date(a.periodo?.inicio || 0)).map((v, i) => {
|
||
const isAtivo = v.periodo?.ativo ?? !v.periodo?.fim;
|
||
return (
|
||
<div key={i} className="modal-item">
|
||
<span className={`badge ${isAtivo ? 'badge-ativo' : 'badge-historico'}`}>
|
||
{isAtivo ? 'ATIVO' : 'ENCERRADO'}
|
||
</span>
|
||
<span className="modal-item-main">
|
||
{v.ies ? (v.ies.sigla ? `${v.ies.sigla} - ${v.ies.nome || ''}` : v.ies.nome) : 'IES não informada'}
|
||
</span>
|
||
<span className="muted">{formatDate(v.periodo?.inicio)} - {formatDate(v.periodo?.fim)}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Avaliador': {
|
||
const avals = consultor.avaliacoes_comissao || [];
|
||
if (avals.length === 0) return <p className="modal-empty">Sem avaliações de comissão</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...avals].sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((a, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{a.codigo}</span>
|
||
<span className="modal-item-main">{a.nome_comissao || a.premio || a.descricao || '-'}</span>
|
||
<span className="muted">{a.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Premiado': {
|
||
const prems = consultor.premiacoes || [];
|
||
if (prems.length === 0) return <p className="modal-empty">Sem premiações</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...prems].sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{p.codigo}</span>
|
||
<span className="modal-item-main">{p.nome_premio || p.premio || '-'}</span>
|
||
<span className="muted">{p.papel || ''}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Orientador': {
|
||
const orients = consultor.orientacoes || [];
|
||
if (orients.length === 0) return <p className="modal-empty">Sem orientações</p>;
|
||
const contagem = {};
|
||
orients.forEach(o => { contagem[o.codigo] = (contagem[o.codigo] || 0) + 1; });
|
||
const labels = {
|
||
ORIENT_POS_DOC: '🔬 Pós-Doutorado',
|
||
ORIENT_TESE: '📚 Tese (Doutorado)',
|
||
ORIENT_DISS: '📄 Dissertação (Mestrado)',
|
||
CO_ORIENT_POS_DOC: '🔬 Coorient. Pós-Doc',
|
||
CO_ORIENT_TESE: '📚 Coorient. Tese',
|
||
CO_ORIENT_DISS: '📄 Coorient. Diss.'
|
||
};
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">Total: <strong>{orients.length}</strong> orientações</div>
|
||
{Object.entries(contagem).map(([cod, qtd]) => (
|
||
<div key={cod} className="modal-item">
|
||
<span className="modal-item-main">{labels[cod] || cod}</span>
|
||
<span className="pontos">{qtd}x</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Bolsista CNPq': {
|
||
const bolsas = consultor.bolsas_cnpq || [];
|
||
if (bolsas.length === 0) return <p className="modal-empty">Sem bolsas CNPq</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{bolsas.map((b, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">BPQ</span>
|
||
<span className="modal-item-main">Nível {b.nivel || 'N/A'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Inscrito Premio': {
|
||
const inscs = consultor.inscricoes || [];
|
||
if (inscs.length === 0) return <p className="modal-empty">Sem inscrições em prêmios</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...inscs].sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((ins, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{ins.codigo}</span>
|
||
<span className="modal-item-main">{ins.evento || ins.premio || ins.descricao || '-'}</span>
|
||
<span className="muted">{ins.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Projeto': {
|
||
const parts = (consultor.participacoes || []).filter(p => p.codigo === 'PROJ');
|
||
if (parts.length === 0) return <p className="modal-empty">Sem projetos</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...parts].sort((a, b) => (b.ano || 0) - (a.ano || 0)).slice(0, 20).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">PROJ</span>
|
||
<span className="modal-item-main">{p.descricao || p.tipo || '-'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
{parts.length > 20 && <p className="muted">... e mais {parts.length - 20} projetos</p>}
|
||
</div>
|
||
);
|
||
}
|
||
case 'Evento': {
|
||
const parts = (consultor.participacoes || []).filter(p => p.codigo === 'EVENTO');
|
||
if (parts.length === 0) return <p className="modal-empty">Sem eventos</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{[...parts].sort((a, b) => (b.ano || 0) - (a.ano || 0)).slice(0, 20).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">EVENTO</span>
|
||
<span className="modal-item-main">{p.descricao || p.tipo || '-'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
{parts.length > 20 && <p className="muted">... e mais {parts.length - 20} eventos</p>}
|
||
</div>
|
||
);
|
||
}
|
||
default:
|
||
return <p className="modal-empty">Tipo não reconhecido</p>;
|
||
}
|
||
};
|
||
|
||
const config = TIPOS_ATUACAO_CONFIG[tipo] || { cor: 'tipo-default', icone: '📌' };
|
||
|
||
return createPortal(
|
||
<div className="tipo-modal-overlay" onClick={onClose}>
|
||
<div className="tipo-modal" onClick={(e) => e.stopPropagation()}>
|
||
<div className="tipo-modal-header">
|
||
<span className={`tipo-atuacao ${config.cor}`}>
|
||
<span className="tipo-icone">{config.icone}</span>
|
||
<span className="tipo-label">{tipo}</span>
|
||
</span>
|
||
<button className="tipo-modal-close" onClick={onClose}>✕</button>
|
||
</div>
|
||
<div className="tipo-modal-body">
|
||
{renderContent()}
|
||
</div>
|
||
</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
};
|
||
|
||
const SeloModal = ({ selo, consultor, onClose }) => {
|
||
if (!selo || !consultor) return null;
|
||
|
||
const formatDate = (dateStr) => {
|
||
if (!dateStr) return 'Atual';
|
||
return new Date(dateStr).toLocaleDateString('pt-BR');
|
||
};
|
||
|
||
const renderContent = () => {
|
||
switch (selo.codigo) {
|
||
case 'PRESID_CAMARA': {
|
||
const coords = (consultor.coordenacoes_capes || []).filter(c => c.presidente);
|
||
if (coords.length === 0) return <p className="modal-empty">Sem dados</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{coords.map((c, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{c.codigo}</span>
|
||
<span className="modal-item-main">{c.area_avaliacao}</span>
|
||
<span className="muted">{formatDate(c.inicio)} - {formatDate(c.fim)}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'COORD_PPG':
|
||
return <p className="modal-empty">Coordenador de Programa de Pós-Graduação</p>;
|
||
case 'BPQ': {
|
||
const bolsas = consultor.bolsas_cnpq || [];
|
||
if (bolsas.length === 0) return <p className="modal-empty">Sem bolsas</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">Total: <strong>{bolsas.length}</strong> bolsa(s) BPQ</div>
|
||
{bolsas.map((b, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">BPQ</span>
|
||
<span className="modal-item-main">Nível {b.nivel || 'N/A'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'AUTOR_GP':
|
||
case 'AUTOR_PREMIO':
|
||
case 'AUTOR_MENCAO': {
|
||
const codMap = { AUTOR_GP: 'PREMIACAO_GP_AUTOR', AUTOR_PREMIO: 'PREMIACAO_AUTOR', AUTOR_MENCAO: 'MENCAO_AUTOR' };
|
||
const prems = (consultor.premiacoes || []).filter(p => p.codigo === codMap[selo.codigo] && (p.papel || '').toLowerCase() === 'autor');
|
||
if (prems.length === 0) return <p className="modal-empty">Sem premiações</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{prems.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{p.codigo}</span>
|
||
<span className="modal-item-main">{p.nome_premio || p.premio || '-'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'ORIENT_GP':
|
||
case 'ORIENT_PREMIO':
|
||
case 'ORIENT_MENCAO': {
|
||
const codMap = { ORIENT_GP: 'PREMIACAO_GP_AUTOR', ORIENT_PREMIO: 'PREMIACAO_AUTOR', ORIENT_MENCAO: 'MENCAO_AUTOR' };
|
||
const prems = (consultor.premiacoes || []).filter(p => p.codigo === codMap[selo.codigo] && (p.papel || '').toLowerCase() === 'orientador');
|
||
if (prems.length === 0) return <p className="modal-empty">Sem premiações como orientador</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{prems.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{p.codigo}</span>
|
||
<span className="modal-item-main">{p.nome_premio || p.premio || '-'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'COORIENT_GP':
|
||
case 'COORIENT_PREMIO':
|
||
case 'COORIENT_MENCAO': {
|
||
const codMap = { COORIENT_GP: 'PREMIACAO_GP_AUTOR', COORIENT_PREMIO: 'PREMIACAO_AUTOR', COORIENT_MENCAO: 'MENCAO_AUTOR' };
|
||
const prems = (consultor.premiacoes || []).filter(p => p.codigo === codMap[selo.codigo] && (p.papel || '').toLowerCase() === 'coorientador');
|
||
if (prems.length === 0) return <p className="modal-empty">Sem premiações como coorientador</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
{prems.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{p.codigo}</span>
|
||
<span className="modal-item-main">{p.nome_premio || p.premio || '-'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'ORIENT_TESE':
|
||
case 'ORIENT_DISS':
|
||
case 'ORIENT_POS_DOC':
|
||
case 'CO_ORIENT_TESE':
|
||
case 'CO_ORIENT_DISS':
|
||
case 'CO_ORIENT_POS_DOC': {
|
||
const orients = consultor.orientacoes || [];
|
||
const lista = orients.filter(o => o.codigo === selo.codigo);
|
||
const isCoorient = selo.codigo.startsWith('CO_');
|
||
const tipoLabel = {
|
||
ORIENT_TESE: 'Teses de Doutorado',
|
||
ORIENT_DISS: 'Dissertações de Mestrado',
|
||
ORIENT_POS_DOC: 'Pós-Doutorados',
|
||
CO_ORIENT_TESE: 'Coorientações de Tese',
|
||
CO_ORIENT_DISS: 'Coorientações de Dissertação',
|
||
CO_ORIENT_POS_DOC: 'Coorientações de Pós-Doc'
|
||
};
|
||
const premiadas = lista.filter(o => o.premiada);
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">
|
||
<span>Total: <strong>{lista.length}</strong> {tipoLabel[selo.codigo]?.toLowerCase() || 'orientações'}</span>
|
||
{premiadas.length > 0 && <span>Premiadas: <strong>{premiadas.length}</strong> 🏆</span>}
|
||
</div>
|
||
<div className="modal-item" style={{ flexDirection: 'column', alignItems: 'flex-start', gap: '0.5rem' }}>
|
||
<p style={{ margin: 0, color: 'var(--muted)', fontSize: '0.8rem' }}>
|
||
{isCoorient ? 'Coorientações' : 'Orientações'} de {selo.codigo.includes('TESE') ? 'Doutorado' : selo.codigo.includes('DISS') ? 'Mestrado' : 'Pós-Doutorado'} concluídas.
|
||
</p>
|
||
{premiadas.length > 0 && (
|
||
<p style={{ margin: 0, color: 'var(--gold)', fontSize: '0.8rem' }}>
|
||
🏆 {premiadas.length} orientação(ões) premiada(s) no Prêmio CAPES de Tese
|
||
</p>
|
||
)}
|
||
<p style={{ margin: 0, color: 'var(--muted)', fontSize: '0.75rem', fontStyle: 'italic' }}>
|
||
Dados agregados do currículo Lattes via ATUACAPES.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
case 'MB_BANCA_POS_DOC':
|
||
case 'MB_BANCA_TESE':
|
||
case 'MB_BANCA_DISS': {
|
||
const bancas = consultor.membros_banca || [];
|
||
const lista = bancas.filter(b => b.codigo === selo.codigo);
|
||
const tipoLabel = {
|
||
MB_BANCA_POS_DOC: 'Bancas de Pós-Doutorado',
|
||
MB_BANCA_TESE: 'Bancas de Doutorado',
|
||
MB_BANCA_DISS: 'Bancas de Mestrado'
|
||
};
|
||
if (lista.length === 0) return <p className="modal-empty">Sem dados de bancas</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">
|
||
Total: <strong>{lista.length}</strong> {tipoLabel[selo.codigo]?.toLowerCase() || 'bancas'}
|
||
</div>
|
||
{lista.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((b, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">{b.nivel || b.tipo || selo.codigo}</span>
|
||
<span className="modal-item-main">{b.tipo || tipoLabel[selo.codigo]}</span>
|
||
<span className="muted">{b.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'EVENTO': {
|
||
const participacoes = consultor.participacoes || [];
|
||
const eventos = participacoes.filter(p => p.codigo === 'EVENTO');
|
||
if (eventos.length === 0) return <p className="modal-empty">Sem eventos</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">
|
||
Total: <strong>{eventos.length}</strong> participação(ões) em eventos
|
||
</div>
|
||
{eventos.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((e, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">EVENTO</span>
|
||
<span className="modal-item-main">{e.descricao || e.tipo || 'Evento'}</span>
|
||
<span className="muted">{e.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
case 'PROJ': {
|
||
const participacoes = consultor.participacoes || [];
|
||
const projetos = participacoes.filter(p => p.codigo === 'PROJ');
|
||
if (projetos.length === 0) return <p className="modal-empty">Sem projetos</p>;
|
||
return (
|
||
<div className="modal-list">
|
||
<div className="modal-summary">
|
||
Total: <strong>{projetos.length}</strong> participação(ões) em projetos
|
||
</div>
|
||
{projetos.sort((a, b) => (b.ano || 0) - (a.ano || 0)).map((p, i) => (
|
||
<div key={i} className="modal-item">
|
||
<span className="badge">PROJ</span>
|
||
<span className="modal-item-main">{p.descricao || p.tipo || 'Projeto'}</span>
|
||
<span className="muted">{p.ano || '-'}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
default:
|
||
return <p className="modal-empty">Sem dados detalhados para este selo</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={`selo ${selo.cor}`} style={{ fontSize: '0.85rem', padding: '0.35rem 0.7rem' }}>
|
||
<span className="selo-icone" style={{ fontSize: '1rem' }}>{selo.icone}</span>
|
||
<span className="selo-label">{selo.label}</span>
|
||
<span className="selo-qtd">{selo.qtd}</span>
|
||
</span>
|
||
<button className="tipo-modal-close" onClick={onClose}>✕</button>
|
||
</div>
|
||
<div className="tipo-modal-body">
|
||
{renderContent()}
|
||
</div>
|
||
</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
};
|
||
|
||
const ItemDetalheModal = ({ item, tipo, onClose }) => {
|
||
const [subDetalhe, setSubDetalhe] = useState(null);
|
||
|
||
if (!item || !tipo) return null;
|
||
|
||
const formatDate = (dateStr) => {
|
||
if (!dateStr) return 'N/A';
|
||
if (dateStr.includes('/')) {
|
||
const parts = dateStr.split(' ')[0].split('/');
|
||
if (parts.length === 3) {
|
||
return `${parts[0]}/${parts[1]}/${parts[2]}`;
|
||
}
|
||
}
|
||
return new Date(dateStr).toLocaleDateString('pt-BR');
|
||
};
|
||
|
||
const currentItem = subDetalhe?.item || item;
|
||
const currentTipo = subDetalhe?.tipo || tipo;
|
||
|
||
const getTitulo = () => {
|
||
switch (currentTipo) {
|
||
case 'titulacao': return 'Titulação';
|
||
case 'producoes_lattes': return 'Currículo Lattes';
|
||
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 currentItem.codigo === 'PROJ' ? 'Projeto' : 'Evento';
|
||
case 'orientacao': return 'Orientação';
|
||
case 'orientacao_lattes': return 'Orientação Concluída';
|
||
case 'idioma': return 'Idioma';
|
||
case 'docencia': return 'Docência em PPG';
|
||
case 'emprego': return 'Vínculo Empregatício';
|
||
case 'projeto': return 'Projeto de Pesquisa';
|
||
case 'premiacao_lattes': return 'Premiação';
|
||
default: return 'Detalhes';
|
||
}
|
||
};
|
||
|
||
const getIcone = () => {
|
||
switch (currentTipo) {
|
||
case 'titulacao': return '🎓';
|
||
case 'producoes_lattes': return '📚';
|
||
case 'vinculo': return '💼';
|
||
case 'coordenacao': return '🎯';
|
||
case 'premiacao': return '🏆';
|
||
case 'avaliacao': return '📋';
|
||
case 'inscricao': return '📝';
|
||
case 'participacao': return currentItem.codigo === 'PROJ' ? '📊' : '📅';
|
||
case 'orientacao': return '🎓';
|
||
case 'orientacao_lattes': return '👨🏫';
|
||
case 'idioma': return '🌐';
|
||
case 'docencia': return '👨🏫';
|
||
case 'emprego': return '🏢';
|
||
case 'projeto': return '📊';
|
||
case 'premiacao_lattes': return '🏆';
|
||
default: return '📄';
|
||
}
|
||
};
|
||
|
||
const handleVoltar = () => {
|
||
setSubDetalhe(null);
|
||
};
|
||
|
||
const renderContent = () => {
|
||
switch (currentTipo) {
|
||
case 'titulacao': {
|
||
const it = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Grau Acadêmico</span>
|
||
<span className="modal-detalhe-value">{it.grau || 'N/A'}</span>
|
||
</div>
|
||
{it.area && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área de Conhecimento</span>
|
||
<span className="modal-detalhe-value">{it.area}</span>
|
||
</div>
|
||
)}
|
||
{it.area_avaliacao && it.area_avaliacao !== it.area && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área de Avaliação</span>
|
||
<span className="modal-detalhe-value">{it.area_avaliacao}</span>
|
||
</div>
|
||
)}
|
||
{(it.ies_sigla || it.ies_nome) && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Instituição</span>
|
||
<span className="modal-detalhe-value">
|
||
{it.ies_sigla && it.ies_nome ? `${it.ies_sigla} - ${it.ies_nome}` : (it.ies_sigla || it.ies_nome)}
|
||
</span>
|
||
</div>
|
||
)}
|
||
{it.ies_status && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Natureza Jurídica</span>
|
||
<span className="modal-detalhe-value">{it.ies_status}</span>
|
||
</div>
|
||
)}
|
||
{it.programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Programa</span>
|
||
<span className="modal-detalhe-value">{it.programa}</span>
|
||
</div>
|
||
)}
|
||
{it.codigo_programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Código do Programa</span>
|
||
<span className="modal-detalhe-value">{it.codigo_programa}</span>
|
||
</div>
|
||
)}
|
||
{it.programa_modalidade && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Modalidade</span>
|
||
<span className="modal-detalhe-value">{it.programa_modalidade}</span>
|
||
</div>
|
||
)}
|
||
{it.programa_situacao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Situação Programa</span>
|
||
<span className="modal-detalhe-value">{it.programa_situacao}</span>
|
||
</div>
|
||
)}
|
||
{it.pais && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">País</span>
|
||
<span className="modal-detalhe-value">{it.pais}</span>
|
||
</div>
|
||
)}
|
||
{it.ano && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Ano de Conclusão</span>
|
||
<span className="modal-detalhe-value">{it.ano}</span>
|
||
</div>
|
||
)}
|
||
{it.inicio && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Início</span>
|
||
<span className="modal-detalhe-value">{formatDate(it.inicio)}</span>
|
||
</div>
|
||
)}
|
||
{it.fim && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Término</span>
|
||
<span className="modal-detalhe-value">{formatDate(it.fim)}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'orientacao_lattes': {
|
||
const o = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
{o.orientando && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Orientando</span>
|
||
<span className="modal-detalhe-value">{o.orientando}</span>
|
||
</div>
|
||
)}
|
||
{o.tipo && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Tipo</span>
|
||
<span className="modal-detalhe-value">{o.tipo}</span>
|
||
</div>
|
||
)}
|
||
{o.titulo && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Título</span>
|
||
<span className="modal-detalhe-value" style={{ fontSize: '0.85rem' }}>{o.titulo}</span>
|
||
</div>
|
||
)}
|
||
{o.ano && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Ano</span>
|
||
<span className="modal-detalhe-value">{o.ano}</span>
|
||
</div>
|
||
)}
|
||
{o.programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Programa</span>
|
||
<span className="modal-detalhe-value">{o.programa}</span>
|
||
</div>
|
||
)}
|
||
{o.ies && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Instituição</span>
|
||
<span className="modal-detalhe-value">{o.ies}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'idioma': {
|
||
const i = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Idioma</span>
|
||
<span className="modal-detalhe-value">{i.idioma || 'N/A'}</span>
|
||
</div>
|
||
{i.proficiencia_leitura && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Leitura</span>
|
||
<span className="modal-detalhe-value">{i.proficiencia_leitura}</span>
|
||
</div>
|
||
)}
|
||
{i.proficiencia_escrita && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Escrita</span>
|
||
<span className="modal-detalhe-value">{i.proficiencia_escrita}</span>
|
||
</div>
|
||
)}
|
||
{i.proficiencia_fala && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Fala</span>
|
||
<span className="modal-detalhe-value">{i.proficiencia_fala}</span>
|
||
</div>
|
||
)}
|
||
{i.proficiencia_compreensao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Compreensão</span>
|
||
<span className="modal-detalhe-value">{i.proficiencia_compreensao}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'docencia': {
|
||
const d = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Programa</span>
|
||
<span className="modal-detalhe-value">{d.programa || 'N/A'}</span>
|
||
</div>
|
||
{d.codigo_programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Código</span>
|
||
<span className="modal-detalhe-value">{d.codigo_programa}</span>
|
||
</div>
|
||
)}
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Instituição</span>
|
||
<span className="modal-detalhe-value">{d.ies_nome} ({d.ies_sigla})</span>
|
||
</div>
|
||
{d.modalidade && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Modalidade</span>
|
||
<span className="modal-detalhe-value">{d.modalidade}</span>
|
||
</div>
|
||
)}
|
||
{d.situacao_programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Situação</span>
|
||
<span className="modal-detalhe-value">{d.situacao_programa}</span>
|
||
</div>
|
||
)}
|
||
{d.area && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área</span>
|
||
<span className="modal-detalhe-value">{d.area}</span>
|
||
</div>
|
||
)}
|
||
{d.area_avaliacao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área de Avaliação</span>
|
||
<span className="modal-detalhe-value">{d.area_avaliacao}</span>
|
||
</div>
|
||
)}
|
||
{d.categoria && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Categoria</span>
|
||
<span className="modal-detalhe-value">{d.categoria}</span>
|
||
</div>
|
||
)}
|
||
{d.tipo_vinculo && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Tipo de Vínculo</span>
|
||
<span className="modal-detalhe-value">{d.tipo_vinculo}</span>
|
||
</div>
|
||
)}
|
||
{d.regime_trabalho && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Regime de Trabalho</span>
|
||
<span className="modal-detalhe-value">{d.regime_trabalho}</span>
|
||
</div>
|
||
)}
|
||
{d.carga_horaria && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Carga Horária</span>
|
||
<span className="modal-detalhe-value">{d.carga_horaria}h</span>
|
||
</div>
|
||
)}
|
||
{d.linhas_pesquisa_ativas?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Linhas de Pesquisa Ativas ({d.linhas_pesquisa_ativas.length} de {d.total_linhas_pesquisa})</h5>
|
||
<ul className="modal-list">
|
||
{d.linhas_pesquisa_ativas.map((linha, idx) => (
|
||
<li key={idx} className="modal-item">
|
||
<span className="modal-item-main">{linha}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'emprego': {
|
||
const e = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Empregador</span>
|
||
<span className="modal-detalhe-value">{e.empregador || 'N/A'}</span>
|
||
</div>
|
||
{e.cnpj && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">CNPJ</span>
|
||
<span className="modal-detalhe-value">{e.cnpj}</span>
|
||
</div>
|
||
)}
|
||
{e.tipo_emprego && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Tipo</span>
|
||
<span className="modal-detalhe-value">{e.tipo_emprego}</span>
|
||
</div>
|
||
)}
|
||
{e.atividade && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Atividade</span>
|
||
<span className="modal-detalhe-value">{e.atividade}</span>
|
||
</div>
|
||
)}
|
||
{e.vinculo && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Vínculo</span>
|
||
<span className="modal-detalhe-value">{e.vinculo}</span>
|
||
</div>
|
||
)}
|
||
{e.profissao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Profissão</span>
|
||
<span className="modal-detalhe-value">{e.profissao}</span>
|
||
</div>
|
||
)}
|
||
{e.periodos?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Períodos ({e.periodos.length})</h5>
|
||
<ul className="modal-list">
|
||
{e.periodos.map((p, idx) => (
|
||
<li key={idx} className="modal-item">
|
||
<span className="modal-item-main">
|
||
{p.inicio ? formatDate(p.inicio) : 'Sem data'} a {p.fim ? formatDate(p.fim) : 'Atual'}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'projeto': {
|
||
const p = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Nome</span>
|
||
<span className="modal-detalhe-value">{p.nome || 'N/A'}</span>
|
||
</div>
|
||
{p.situacao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Situação</span>
|
||
<span className={`badge ${p.situacao === 'EM ANDAMENTO' ? 'badge-ativo' : 'badge-historico'}`}>
|
||
{p.situacao}
|
||
</span>
|
||
</div>
|
||
)}
|
||
{p.ano_inicio && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Ano de Início</span>
|
||
<span className="modal-detalhe-value">{p.ano_inicio}</span>
|
||
</div>
|
||
)}
|
||
{p.ies_sigla && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Instituição</span>
|
||
<span className="modal-detalhe-value">{p.ies_sigla}</span>
|
||
</div>
|
||
)}
|
||
{p.programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Programa</span>
|
||
<span className="modal-detalhe-value">{p.programa}</span>
|
||
</div>
|
||
)}
|
||
{p.area && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área</span>
|
||
<span className="modal-detalhe-value">{p.area}</span>
|
||
</div>
|
||
)}
|
||
{p.linha_pesquisa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Linha de Pesquisa</span>
|
||
<span className="modal-detalhe-value">{p.linha_pesquisa}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'premiacao_lattes': {
|
||
const pr = currentItem;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Evento</span>
|
||
<span className="modal-detalhe-value">{pr.evento || pr.premio || 'N/A'}</span>
|
||
</div>
|
||
{pr.premiacao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Tipo</span>
|
||
<span className="modal-detalhe-value">{pr.premiacao}</span>
|
||
</div>
|
||
)}
|
||
{pr.ano && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Ano</span>
|
||
<span className="modal-detalhe-value">{pr.ano}</span>
|
||
</div>
|
||
)}
|
||
{pr.papel && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Papel</span>
|
||
<span className="modal-detalhe-value">{pr.papel}</span>
|
||
</div>
|
||
)}
|
||
{pr.situacao && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Situação</span>
|
||
<span className="modal-detalhe-value">{pr.situacao}</span>
|
||
</div>
|
||
)}
|
||
{pr.ies_sigla && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Instituição</span>
|
||
<span className="modal-detalhe-value">{pr.ies_sigla}</span>
|
||
</div>
|
||
)}
|
||
{pr.programa && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Programa</span>
|
||
<span className="modal-detalhe-value">{pr.programa}</span>
|
||
</div>
|
||
)}
|
||
{pr.area && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Área</span>
|
||
<span className="modal-detalhe-value">{pr.area}</span>
|
||
</div>
|
||
)}
|
||
{pr.produto_nome && (
|
||
<>
|
||
<h5 className="modal-section-title">Produto</h5>
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Título</span>
|
||
<span className="modal-detalhe-value">{pr.produto_nome}</span>
|
||
</div>
|
||
{pr.produto_tipo && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Tipo</span>
|
||
<span className="modal-detalhe-value">{pr.produto_tipo}</span>
|
||
</div>
|
||
)}
|
||
{pr.produto_autor && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Autor</span>
|
||
<span className="modal-detalhe-value">{pr.produto_autor}</span>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
case 'producoes_lattes': {
|
||
const titulacoes = item.titulacoes || [];
|
||
const idiomas = item.idiomas || [];
|
||
const orientacoes = item.orientacoes_concluidas || [];
|
||
const endereco = item.endereco_profissional;
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">ID Lattes</span>
|
||
<a
|
||
href={item.url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="modal-detalhe-value lattes-link-inline"
|
||
>
|
||
{item.id_lattes} ↗
|
||
</a>
|
||
</div>
|
||
{item.nacionalidade && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Nacionalidade</span>
|
||
<span className="modal-detalhe-value">{item.nacionalidade}</span>
|
||
</div>
|
||
)}
|
||
|
||
{titulacoes.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Formação Acadêmica ({titulacoes.length})</h5>
|
||
<ul className="modal-list">
|
||
{titulacoes.map((t, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: t, tipo: 'titulacao' })}
|
||
>
|
||
<span className="modal-item-main">
|
||
<strong>{t.grau}</strong>
|
||
{t.area && ` em ${t.area}`}
|
||
</span>
|
||
<span className="modal-item-detail">
|
||
{t.ies_sigla || t.ies_nome}
|
||
{t.ano && ` (${t.ano})`}
|
||
</span>
|
||
{t.programa && (
|
||
<span className="modal-item-sub muted">Programa: {t.programa}</span>
|
||
)}
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{idiomas.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Idiomas ({idiomas.length})</h5>
|
||
<ul className="modal-list">
|
||
{idiomas.map((i, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: i, tipo: 'idioma' })}
|
||
>
|
||
<span className="modal-item-main">{i.idioma}</span>
|
||
<span className="modal-item-detail muted">
|
||
{[
|
||
i.proficiencia_leitura && `Leitura: ${i.proficiencia_leitura}`,
|
||
i.proficiencia_escrita && `Escrita: ${i.proficiencia_escrita}`,
|
||
i.proficiencia_fala && `Fala: ${i.proficiencia_fala}`,
|
||
].filter(Boolean).join(' | ')}
|
||
</span>
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{orientacoes.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">
|
||
Orientações Concluídas ({item.total_orientacoes})
|
||
</h5>
|
||
<ul className="modal-list">
|
||
{orientacoes.slice(0, 15).map((o, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: o, tipo: 'orientacao_lattes' })}
|
||
>
|
||
<span className="modal-item-main">{o.orientando || 'Orientando não informado'}</span>
|
||
<span className="modal-item-detail">
|
||
{o.tipo?.replace('Orientação ', '').replace(' Concluída', '')}
|
||
{o.ano && ` (${o.ano})`}
|
||
{o.ies && ` - ${o.ies}`}
|
||
</span>
|
||
{o.titulo && (
|
||
<span className="modal-item-sub muted" style={{ fontSize: '0.75rem' }}>
|
||
{o.titulo.length > 60 ? o.titulo.substring(0, 60) + '...' : o.titulo}
|
||
</span>
|
||
)}
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
{item.total_orientacoes > 15 && (
|
||
<li className="modal-item muted">
|
||
... e mais {item.total_orientacoes - 15} orientações
|
||
</li>
|
||
)}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{item.estatisticas_orientacoes && (
|
||
<>
|
||
<h5 className="modal-section-title">Estatísticas de Orientações</h5>
|
||
<div className="modal-stats-grid">
|
||
{item.estatisticas_orientacoes.doutorado_finalizado > 0 && (
|
||
<div className="modal-stat-card">
|
||
<span className="stat-number">{item.estatisticas_orientacoes.doutorado_finalizado}</span>
|
||
<span className="stat-label">Doutorados Concluídos</span>
|
||
</div>
|
||
)}
|
||
{item.estatisticas_orientacoes.mestrado_finalizado > 0 && (
|
||
<div className="modal-stat-card">
|
||
<span className="stat-number">{item.estatisticas_orientacoes.mestrado_finalizado}</span>
|
||
<span className="stat-label">Mestrados Concluídos</span>
|
||
</div>
|
||
)}
|
||
{item.estatisticas_orientacoes.doutorado_andamento > 0 && (
|
||
<div className="modal-stat-card">
|
||
<span className="stat-number">{item.estatisticas_orientacoes.doutorado_andamento}</span>
|
||
<span className="stat-label">Doutorados em Andamento</span>
|
||
</div>
|
||
)}
|
||
{item.estatisticas_orientacoes.mestrado_andamento > 0 && (
|
||
<div className="modal-stat-card">
|
||
<span className="stat-number">{item.estatisticas_orientacoes.mestrado_andamento}</span>
|
||
<span className="stat-label">Mestrados em Andamento</span>
|
||
</div>
|
||
)}
|
||
{item.estatisticas_orientacoes.pos_doutorado > 0 && (
|
||
<div className="modal-stat-card">
|
||
<span className="stat-number">{item.estatisticas_orientacoes.pos_doutorado}</span>
|
||
<span className="stat-label">Pós-Doutorados</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{item.docencias?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Docência em PPG ({item.docencias.length})</h5>
|
||
<ul className="modal-list">
|
||
{item.docencias.map((d, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: d, tipo: 'docencia' })}
|
||
>
|
||
<span className="modal-item-main">
|
||
<strong>{d.programa || 'Programa não informado'}</strong>
|
||
</span>
|
||
<span className="modal-item-detail">
|
||
{d.ies_sigla} | {d.modalidade} | {d.categoria}
|
||
</span>
|
||
{d.linhas_pesquisa_ativas?.length > 0 && (
|
||
<span className="modal-item-sub muted">
|
||
{d.linhas_pesquisa_ativas.length} linhas de pesquisa ativas
|
||
</span>
|
||
)}
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{item.empregos?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Vínculos Empregatícios ({item.empregos.length})</h5>
|
||
<ul className="modal-list">
|
||
{item.empregos.map((e, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: e, tipo: 'emprego' })}
|
||
>
|
||
<span className="modal-item-main">{e.empregador || 'Empregador não informado'}</span>
|
||
<span className="modal-item-detail">
|
||
{e.tipo_emprego}
|
||
{e.atividade && ` | ${e.atividade}`}
|
||
</span>
|
||
{e.periodos?.length > 0 && (
|
||
<span className="modal-item-sub muted">
|
||
{e.periodos.length} período(s) registrado(s)
|
||
</span>
|
||
)}
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{item.projetos?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">
|
||
Projetos de Pesquisa ({item.total_projetos})
|
||
</h5>
|
||
<ul className="modal-list">
|
||
{item.projetos.slice(0, 10).map((p, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: p, tipo: 'projeto' })}
|
||
>
|
||
<span className="modal-item-main">
|
||
{p.nome?.length > 60 ? p.nome.substring(0, 60) + '...' : p.nome}
|
||
</span>
|
||
<span className="modal-item-detail">
|
||
{p.situacao}
|
||
{p.ano_inicio && ` | Início: ${p.ano_inicio}`}
|
||
{p.ies_sigla && ` | ${p.ies_sigla}`}
|
||
</span>
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
{item.total_projetos > 10 && (
|
||
<li className="modal-item muted">
|
||
... e mais {item.total_projetos - 10} projetos
|
||
</li>
|
||
)}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{item.premiacoes_detalhadas?.length > 0 && (
|
||
<>
|
||
<h5 className="modal-section-title">Premiações Detalhadas ({item.premiacoes_detalhadas.length})</h5>
|
||
<ul className="modal-list">
|
||
{item.premiacoes_detalhadas.map((pr, idx) => (
|
||
<li
|
||
key={idx}
|
||
className="modal-item modal-item-clicavel"
|
||
onClick={() => setSubDetalhe({ item: pr, tipo: 'premiacao_lattes' })}
|
||
>
|
||
<span className="modal-item-main">
|
||
<strong>{pr.evento || pr.premio}</strong>
|
||
{pr.premiacao && ` - ${pr.premiacao}`}
|
||
</span>
|
||
<span className="modal-item-detail">
|
||
{pr.papel && `${pr.papel} | `}
|
||
{pr.ano}
|
||
{pr.ies_sigla && ` | ${pr.ies_sigla}`}
|
||
</span>
|
||
{pr.produto_nome && (
|
||
<span className="modal-item-sub muted" style={{ fontSize: '0.75rem' }}>
|
||
{pr.produto_nome.length > 60 ? pr.produto_nome.substring(0, 60) + '...' : pr.produto_nome}
|
||
</span>
|
||
)}
|
||
<span className="modal-item-arrow">→</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</>
|
||
)}
|
||
|
||
{endereco && (
|
||
<>
|
||
<h5 className="modal-section-title">Endereço Profissional</h5>
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-value">
|
||
{[
|
||
endereco.logradouro,
|
||
endereco.numero,
|
||
endereco.bairro,
|
||
endereco.cidade,
|
||
endereco.pais
|
||
].filter(Boolean).join(', ')}
|
||
</span>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{titulacoes.length === 0 && idiomas.length === 0 && orientacoes.length === 0 &&
|
||
!item.docencias?.length && !item.projetos?.length && !item.premiacoes_detalhadas?.length && (
|
||
<p className="modal-empty">Dados detalhados não disponíveis no ATUACAPES.</p>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
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>
|
||
{item.evento && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Evento</span>
|
||
<span className="modal-detalhe-value">{item.evento}</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 Base</span>
|
||
<span className="modal-detalhe-value pontos">{PONTOS_BASE[item.codigo] || 0} pts</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">
|
||
{subDetalhe && (
|
||
<button className="tipo-modal-voltar" onClick={handleVoltar}>
|
||
← Voltar
|
||
</button>
|
||
)}
|
||
<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',
|
||
descricao: 'CA=200 | CAJ=150 | CAJ_MP=120 | CAM=100\nTempo: multiplicador por ano (anos completos)\nBônus atualidade (mandato vigente) + Retorno (mandato anterior)',
|
||
},
|
||
bloco_b: {
|
||
titulo: 'Consultoria',
|
||
descricao: 'CONS_ATIVO=150 | CONS_HIST=100 | CONS_FALECIDO=100\nTempo: 5 pts/ano (max 50)\nContinuidade: 8a+=+20 (escalonado)\nRetorno (reativação): +15 (uma vez)',
|
||
},
|
||
bloco_c: {
|
||
titulo: 'Avaliacoes/Premiacoes',
|
||
descricao: 'Premiações: GP=100 | Prêmio=50 | Menção=30\nAvaliações de Comissão e Coordenação\nInscrições em Prêmios\nOrientações e Bancas de trabalhos premiados',
|
||
},
|
||
bloco_d: {
|
||
titulo: 'Indicadores',
|
||
descricao: 'Bolsas CNPq: BPQ Nível Superior/Intermediário\nParticipações: Eventos e Projetos\nOrientações e Bancas (sem premiação)',
|
||
},
|
||
};
|
||
|
||
const PONTOS_BASE = {
|
||
CA: 200, CAJ: 150, CAJ_MP: 120, CAM: 100,
|
||
CONS_ATIVO: 150, CONS_HIST: 100, CONS_FALECIDO: 100,
|
||
INSC_AUTOR: 10, INSC_INST_AUTOR: 20,
|
||
AVAL_COMIS_PREMIO: 30, AVAL_COMIS_GP: 40,
|
||
COORD_COMIS_PREMIO: 40, COORD_COMIS_GP: 50,
|
||
PREMIACAO_GP_AUTOR: 100, PREMIACAO_AUTOR: 50, MENCAO_AUTOR: 30,
|
||
BOL_BPQ_NIVEL: 30,
|
||
EVENTO: 1, PROJ: 10,
|
||
};
|
||
|
||
const gerarInsights = (consultor, totalConsultores) => {
|
||
const insights = [];
|
||
const posicao = consultor.posicao || consultor.rank || 0;
|
||
const pontuacao = consultor.pontuacao_total || 0;
|
||
const blocoA = consultor.bloco_a || 0;
|
||
const blocoB = consultor.bloco_b || 0;
|
||
const blocoC = consultor.bloco_c || 0;
|
||
const blocoD = consultor.bloco_d || 0;
|
||
|
||
if (totalConsultores > 0 && posicao > 0) {
|
||
const percentil = ((totalConsultores - posicao) / totalConsultores * 100);
|
||
if (percentil >= 99) {
|
||
insights.push({ icone: '🏆', texto: `Top 1% - Elite dos ${totalConsultores.toLocaleString('pt-BR')} consultores` });
|
||
} else if (percentil >= 95) {
|
||
insights.push({ icone: '⭐', texto: `Top 5% entre ${totalConsultores.toLocaleString('pt-BR')} consultores` });
|
||
} else if (percentil >= 90) {
|
||
insights.push({ icone: '📈', texto: `Top 10% no ranking geral` });
|
||
} else if (percentil >= 75) {
|
||
insights.push({ icone: '✓', texto: `Acima de 75% dos consultores` });
|
||
} else if (percentil >= 50) {
|
||
insights.push({ icone: '→', texto: `Metade superior do ranking` });
|
||
}
|
||
}
|
||
|
||
const blocos = [
|
||
{ nome: 'Coordenação CAPES', letra: 'A', valor: blocoA },
|
||
{ nome: 'Consultoria', letra: 'B', valor: blocoB },
|
||
{ nome: 'Avaliações/Premiações', letra: 'C', valor: blocoC },
|
||
{ nome: 'Indicadores', letra: 'D', valor: blocoD },
|
||
];
|
||
|
||
const blocosAtivos = blocos.filter(b => b.valor > 0);
|
||
if (blocosAtivos.length > 0 && pontuacao > 0) {
|
||
const maiorBloco = blocosAtivos.reduce((a, b) => a.valor > b.valor ? a : b);
|
||
const pct = Math.round(maiorBloco.valor / pontuacao * 100);
|
||
if (pct >= 50) {
|
||
insights.push({ icone: '📊', texto: `${pct}% da pontuação vem de ${maiorBloco.nome}` });
|
||
}
|
||
}
|
||
|
||
const coords = consultor.coordenacoes_capes || [];
|
||
const coordAtiva = coords.find(c => c.ativo ?? !c.fim);
|
||
if (coordAtiva) {
|
||
const labels = { CA: 'Coordenador de Área', CAJ: 'Coordenador Adjunto', CAJ_MP: 'Coord. Adjunto MP', CAM: 'Câmara Temática' };
|
||
const bonusAtual = { CA: 30, CAJ: 20, CAJ_MP: 15, CAM: 20 };
|
||
insights.push({ icone: '🎯', texto: `${labels[coordAtiva.codigo] || coordAtiva.codigo} em exercício (+${bonusAtual[coordAtiva.codigo] || 0} bônus atualidade)` });
|
||
} else if (coords.length > 0) {
|
||
insights.push({ icone: '📜', texto: `Histórico de ${coords.length} coordenação(ões) CAPES` });
|
||
}
|
||
|
||
const consultoria = consultor.consultoria || {};
|
||
if (consultor.ativo && consultoria.anos_consecutivos >= 8) {
|
||
insights.push({ icone: '💎', texto: `${consultoria.anos_consecutivos} anos consecutivos (+20 bônus continuidade)` });
|
||
} else if (consultoria.anos_consecutivos >= 3) {
|
||
insights.push({ icone: '🔷', texto: `${consultoria.anos_consecutivos} anos consecutivos de consultoria` });
|
||
}
|
||
|
||
if (consultoria.retornos > 0) {
|
||
const bonusRetorno = consultor.ativo ? 15 : 20;
|
||
insights.push({ icone: '🔄', texto: `Retorno à consultoria (+${bonusRetorno} bônus reativação)` });
|
||
}
|
||
|
||
const premiacoes = consultor.premiacoes || [];
|
||
const gps = premiacoes.filter(p => p.codigo === 'PREMIACAO_GP_AUTOR');
|
||
const premios = premiacoes.filter(p => p.codigo === 'PREMIACAO_AUTOR');
|
||
const mencoes = premiacoes.filter(p => p.codigo === 'MENCAO_AUTOR');
|
||
if (gps.length > 0) {
|
||
insights.push({ icone: '🏆', texto: `${gps.length}x Grande Prêmio CAPES (base 100 pts cada)` });
|
||
}
|
||
if (premios.length > 0) {
|
||
insights.push({ icone: '🥇', texto: `${premios.length}x Prêmio CAPES (base 50 pts cada)` });
|
||
}
|
||
if (mencoes.length > 0) {
|
||
insights.push({ icone: '🎖️', texto: `${mencoes.length}x Menção Honrosa (base 30 pts cada)` });
|
||
}
|
||
|
||
const anos = consultor.anos_atuacao || 0;
|
||
if (anos >= 15) {
|
||
insights.push({ icone: '👑', texto: `${anos} anos de contribuição ao SNPG` });
|
||
} else if (anos >= 10) {
|
||
insights.push({ icone: '🏅', texto: `Veterano com ${anos} anos de atuação` });
|
||
}
|
||
|
||
if (blocoA > 0 && blocoB > 0 && blocoC > 0) {
|
||
insights.push({ icone: '🌟', texto: 'Perfil diversificado: coordenação + consultoria + avaliações' });
|
||
}
|
||
|
||
if (insights.length === 0) {
|
||
insights.push({ icone: '📋', texto: `Pontuação total: ${pontuacao} pontos` });
|
||
if (posicao > 0) {
|
||
insights.push({ icone: '📍', texto: `Posição #${posicao.toLocaleString('pt-BR')} no ranking` });
|
||
}
|
||
}
|
||
|
||
return insights;
|
||
};
|
||
|
||
const InsightsModal = ({ consultor, totalConsultores, onClose }) => {
|
||
const insights = useMemo(() => gerarInsights(consultor, totalConsultores), [consultor, totalConsultores]);
|
||
const posicao = consultor.posicao || consultor.rank || 0;
|
||
|
||
return createPortal(
|
||
<div className="tipo-modal-overlay" onClick={onClose}>
|
||
<div className="tipo-modal insights-modal" onClick={(e) => e.stopPropagation()}>
|
||
<div className="tipo-modal-header">
|
||
<span className="modal-titulo-item">
|
||
<span className="modal-titulo-icone">💡</span>
|
||
<span>Por que estou na posição #{posicao}?</span>
|
||
</span>
|
||
<button className="tipo-modal-close" onClick={onClose}>✕</button>
|
||
</div>
|
||
<div className="tipo-modal-body">
|
||
<div className="insights-list">
|
||
{insights.map((insight, idx) => (
|
||
<div key={idx} className="insight-item">
|
||
<span className="insight-icone">{insight.icone}</span>
|
||
<span className="insight-texto">{insight.texto}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div className="insights-footer">
|
||
<small>Análise baseada nos critérios oficiais do Ranking CAPES V1.0</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
};
|
||
|
||
const TETOS = {
|
||
CA: { teto: 450, doc: 'Bloco A - Coordenação CAPES', bonus: 'Tempo: 10pts/ano (max 100) | Atualidade: +30 | Retorno: +20' },
|
||
CAJ: { teto: 370, doc: 'Bloco A - Coordenação CAPES', bonus: 'Tempo: 8pts/ano (max 80) | Atualidade: +20 | Retorno: +15' },
|
||
CAJ_MP: { teto: 315, doc: 'Bloco A - Coordenação CAPES', bonus: 'Tempo: 6pts/ano (max 60) | Atualidade: +15 | Retorno: +10' },
|
||
CAM: { teto: 280, doc: 'Bloco A - Coordenação CAPES', bonus: 'Tempo: 5pts/ano (max 50) | Atualidade: +20 | Retorno: +10' },
|
||
CONS_ATIVO: { teto: 230, doc: 'Bloco B - Consultoria', bonus: 'Tempo: 5pts/ano (max 50) | Atualidade: +20 | Continuidade 8a+: +20 | Retorno: +15' },
|
||
CONS_HIST: { teto: 230, doc: 'Bloco B - Consultoria', bonus: 'Tempo: 5pts/ano (max 50) | Continuidade 8a+: +20 | Retorno: +20' },
|
||
CONS_FALECIDO: { teto: 230, doc: 'Bloco B - Consultoria', bonus: 'Tempo: 5pts/ano (max 50) | Continuidade 8a+: +20' },
|
||
INSC_AUTOR: { teto: 20, doc: 'Bloco C - Inscrições', bonus: '+2/participação (max 10)' },
|
||
INSC_INST_AUTOR: { teto: 50, doc: 'Bloco C - Inscrições', bonus: '+5/participação (max 10)' },
|
||
AVAL_COMIS_PREMIO: { teto: 60, doc: 'Bloco C - Avaliação/Comissão', bonus: '+2/ano (max 15)' },
|
||
AVAL_COMIS_GP: { teto: 80, doc: 'Bloco C - Avaliação/Comissão', bonus: '+3/ano (max 20)' },
|
||
COORD_COMIS_PREMIO: { teto: 100, doc: 'Bloco C - Avaliação/Comissão', bonus: '+4/ano (max 20)' },
|
||
COORD_COMIS_GP: { teto: 120, doc: 'Bloco C - Avaliação/Comissão', bonus: '+6/ano (max 20)' },
|
||
PREMIACAO_GP_AUTOR: { teto: 300, doc: 'Bloco C - Premiações' },
|
||
PREMIACAO_AUTOR: { teto: 150, doc: 'Bloco C - Premiações' },
|
||
MENCAO_AUTOR: { teto: 90, doc: 'Bloco C - Premiações' },
|
||
EVENTO: { teto: 5, doc: 'Bloco D - Participações', bonus: '+1/participação (max 10)' },
|
||
PROJ: { teto: 30, doc: 'Bloco D - Participações', bonus: '+2/participação (max 10)' },
|
||
BOL_BPQ_NIVEL: { teto: 60, doc: 'Bloco D - Bolsas CNPq' },
|
||
};
|
||
|
||
const PontuacaoModal = ({ dados, onClose }) => {
|
||
if (!dados) return null;
|
||
|
||
const { tipo, label, value, formula, atuacao, bloco } = dados;
|
||
|
||
const getTitulo = () => {
|
||
if (tipo === 'bloco') return `${label} - Detalhes`;
|
||
if (tipo === 'atuacao') return `${atuacao?.codigo || label} - Cálculo`;
|
||
if (tipo === 'total') return 'Pontuação Total';
|
||
return label;
|
||
};
|
||
|
||
const getIcone = () => {
|
||
if (label?.includes('BLOCO A') || label === 'A') return '🎯';
|
||
if (label?.includes('BLOCO B') || label === 'B') return '💼';
|
||
if (label?.includes('BLOCO C') || label === 'C') return '🏆';
|
||
if (label?.includes('BLOCO D') || label === 'D') return '📊';
|
||
if (tipo === 'total') return '📈';
|
||
return '📌';
|
||
};
|
||
|
||
const renderBlocoContent = () => {
|
||
const formulaLines = formula ? formula.split('\n') : [];
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Pontuação</span>
|
||
<span className="modal-detalhe-value pontos">{value} pts</span>
|
||
</div>
|
||
{formulaLines.length > 0 && (
|
||
<div className="modal-formula-section">
|
||
<span className="modal-detalhe-label">Fórmula de Cálculo</span>
|
||
<div className="modal-formula-box">
|
||
{formulaLines.map((line, idx) => (
|
||
<div key={idx} className="modal-formula-line">{line}</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
{bloco?.atuacoes?.length > 0 && (
|
||
<div className="modal-atuacoes-section">
|
||
<span className="modal-detalhe-label">Composição</span>
|
||
<div className="modal-atuacoes-list">
|
||
{bloco.atuacoes.map((at, idx) => (
|
||
<div key={idx} className="modal-atuacao-item">
|
||
<span className="badge">{at.codigo}</span>
|
||
<span className="modal-atuacao-valor">{at.total} pts</span>
|
||
<span className="modal-atuacao-detalhe">
|
||
{at.quantidade > 1 ? `(${at.quantidade}x)` : ''}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const renderAtuacaoContent = () => {
|
||
if (!atuacao) return null;
|
||
const base = atuacao.base || 0;
|
||
const tempo = atuacao.tempo || 0;
|
||
const bonus = atuacao.bonus || 0;
|
||
const bruto = base + tempo + bonus;
|
||
const meta = TETOS[atuacao.codigo];
|
||
const hasTeto = meta && meta.teto > 0;
|
||
const capped = hasTeto && bruto > meta.teto;
|
||
const unidade = atuacao.quantidade > 1 ? Math.round(base / atuacao.quantidade) : null;
|
||
|
||
return (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Código</span>
|
||
<span className="badge">{atuacao.codigo}</span>
|
||
</div>
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Pontuação Final</span>
|
||
<span className="modal-detalhe-value pontos">{atuacao.total} pts</span>
|
||
</div>
|
||
|
||
<div className="modal-formula-section">
|
||
<span className="modal-detalhe-label">Cálculo Detalhado</span>
|
||
<div className="modal-formula-box">
|
||
{unidade ? (
|
||
<div className="modal-formula-line">
|
||
<span className="formula-label">Base:</span>
|
||
<span className="formula-calc">{unidade} × {atuacao.quantidade} = {base}</span>
|
||
</div>
|
||
) : (
|
||
<div className="modal-formula-line">
|
||
<span className="formula-label">Base:</span>
|
||
<span className="formula-calc">{base}</span>
|
||
</div>
|
||
)}
|
||
{tempo > 0 && (
|
||
<div className="modal-formula-line">
|
||
<span className="formula-label">Tempo:</span>
|
||
<span className="formula-calc">+{tempo}</span>
|
||
</div>
|
||
)}
|
||
{bonus > 0 && (
|
||
<div className="modal-formula-line">
|
||
<span className="formula-label">Bônus:</span>
|
||
<span className="formula-calc">+{bonus}</span>
|
||
</div>
|
||
)}
|
||
<div className="modal-formula-line formula-subtotal">
|
||
<span className="formula-label">Subtotal:</span>
|
||
<span className="formula-calc">{bruto}</span>
|
||
</div>
|
||
{hasTeto && (
|
||
<div className="modal-formula-line">
|
||
<span className="formula-label">Teto:</span>
|
||
<span className="formula-calc">{meta.teto}</span>
|
||
</div>
|
||
)}
|
||
<div className="modal-formula-line formula-total">
|
||
<span className="formula-label">Total:</span>
|
||
<span className="formula-calc">{atuacao.total} {capped ? '(limitado pelo teto)' : ''}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{meta?.doc && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Referência</span>
|
||
<span className="modal-detalhe-value muted">{meta.doc}</span>
|
||
</div>
|
||
)}
|
||
{meta?.bonus && (
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Regra de Bônus</span>
|
||
<span className="modal-detalhe-value muted">{meta.bonus}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const renderTotalContent = () => (
|
||
<div className="modal-detalhe-content">
|
||
<div className="modal-detalhe-row">
|
||
<span className="modal-detalhe-label">Pontuação Total</span>
|
||
<span className="modal-detalhe-value pontos">{value} pts</span>
|
||
</div>
|
||
<div className="modal-formula-section">
|
||
<span className="modal-detalhe-label">Fórmula</span>
|
||
<div className="modal-formula-box">
|
||
<div className="modal-formula-line">Bloco A + Bloco B + Bloco C + Bloco D</div>
|
||
<div className="modal-formula-line muted" style={{ fontSize: '0.8rem', marginTop: '0.5rem' }}>
|
||
A: Coordenação CAPES | B: Consultoria | C: Avaliações/Premiações | D: Indicadores
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
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">
|
||
{tipo === 'bloco' && renderBlocoContent()}
|
||
{tipo === 'atuacao' && renderAtuacaoContent()}
|
||
{tipo === 'total' && renderTotalContent()}
|
||
</div>
|
||
</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
};
|
||
|
||
const ScoreItemClickable = ({ value, label, formula, style, onClick }) => (
|
||
<div className="score-item-wrapper score-item-clicavel" onClick={onClick}>
|
||
<div className="score-item" style={style}>
|
||
<div className="score-item-value" style={style}>{value}</div>
|
||
<div className="score-item-label">{label}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecionado, totalConsultores = 0 }) => {
|
||
const [expanded, setExpanded] = useState(false);
|
||
const [showRawModal, setShowRawModal] = useState(false);
|
||
const [tipoAtuacaoModal, setTipoAtuacaoModal] = useState(null);
|
||
const [seloModal, setSeloModal] = useState(null);
|
||
const [itemDetalhe, setItemDetalhe] = useState(null);
|
||
const [loadingLattes, setLoadingLattes] = useState(false);
|
||
const [pontuacaoModal, setPontuacaoModal] = useState(null);
|
||
const [showInsights, setShowInsights] = useState(false);
|
||
const cardRef = useRef(null);
|
||
|
||
useEffect(() => {
|
||
if (highlight && cardRef.current) {
|
||
const timeoutId = setTimeout(() => {
|
||
cardRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
}, 100);
|
||
return () => clearTimeout(timeoutId);
|
||
}
|
||
}, [highlight]);
|
||
|
||
const getRankClass = (rank) => {
|
||
if (rank === 1) return 'rank-1';
|
||
if (rank === 2) return 'rank-2';
|
||
if (rank === 3) return 'rank-3';
|
||
return '';
|
||
};
|
||
|
||
const formatDate = (dateStr) => {
|
||
if (!dateStr) return 'Atual';
|
||
return new Date(dateStr).toLocaleDateString('pt-BR');
|
||
};
|
||
|
||
const handleCheckboxClick = (e) => {
|
||
e.stopPropagation();
|
||
onToggleSelecionado(consultor);
|
||
};
|
||
|
||
const handleRawDataClick = (e) => {
|
||
e.stopPropagation();
|
||
setShowRawModal(true);
|
||
};
|
||
|
||
const handleInsightsClick = (e) => {
|
||
e.stopPropagation();
|
||
setShowInsights(true);
|
||
};
|
||
|
||
const { consultoria, pontuacao } = consultor;
|
||
const blocoA = pontuacao?.bloco_a || { total: consultor.bloco_a || 0 };
|
||
const blocoB = pontuacao?.bloco_b || { total: consultor.bloco_b || 0 };
|
||
const blocoC = pontuacao?.bloco_c || { total: consultor.bloco_c || 0 };
|
||
const blocoD = pontuacao?.bloco_d || { total: consultor.bloco_d || 0 };
|
||
const pontuacaoTotal = Number(consultor.pontuacao_total ?? 0);
|
||
|
||
const selos = useMemo(() => gerarSelos(consultor), [consultor]);
|
||
|
||
return (
|
||
<div ref={cardRef} className={`ranking-card ${expanded ? 'expanded' : ''} ${highlight ? 'highlight' : ''} ${selecionado ? 'selecionado' : ''}`} onClick={() => setExpanded(!expanded)}>
|
||
<div className="card-main">
|
||
<div className="selecao-checkbox" onClick={handleCheckboxClick} title="Selecionar para comparar">
|
||
<input
|
||
type="checkbox"
|
||
checked={selecionado}
|
||
onChange={() => {}}
|
||
/>
|
||
<span className="checkmark"></span>
|
||
</div>
|
||
<div className={`rank ${getRankClass(consultor.posicao || consultor.rank)}`}>
|
||
<span className={`rank-number rank-digits-${String(consultor.posicao || consultor.rank).length}`}>
|
||
#{consultor.posicao || consultor.rank}
|
||
</span>
|
||
<button
|
||
className="btn-insights"
|
||
onClick={handleInsightsClick}
|
||
title="Por que estou nesta posição?"
|
||
>
|
||
?
|
||
</button>
|
||
</div>
|
||
|
||
<div className="card-info">
|
||
<div className="consultant-name">
|
||
{import.meta.env.VITE_HOST_ATUACAPES && consultor.id_pessoa && (
|
||
<a
|
||
href={`${import.meta.env.VITE_HOST_ATUACAPES}/perfil/${consultor.id_pessoa}`}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="link-atuacapes"
|
||
onClick={(e) => e.stopPropagation()}
|
||
title="Ver perfil no ATUACAPES"
|
||
>
|
||
↗
|
||
</a>
|
||
)}
|
||
{consultor.nome}
|
||
{consultor.ativo && <span className="badge badge-ativo">ATIVO</span>}
|
||
{!consultor.ativo && <span className="badge badge-historico">HISTORICO</span>}
|
||
{consultor.veterano && <span className="badge badge-veterano">VETERANO</span>}
|
||
</div>
|
||
<div className="consultant-area">
|
||
{consultor.anos_atuacao} anos de atuacao
|
||
{consultoria?.inicio && ` | Desde ${formatDate(consultoria.inicio)}`}
|
||
</div>
|
||
{selos.length > 0 && (
|
||
<div className="consultant-selos-row">
|
||
<SelosBadges selos={selos} compacto={true} onSeloClick={setSeloModal} />
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="card-stats">
|
||
<div className="stat" title={(() => {
|
||
const coords = consultor.coordenacoes_capes || [];
|
||
if (coords.length === 0) return 'Sem coordenação CAPES';
|
||
const prioridade = { CA: 1, CAJ: 2, CAJ_MP: 3, CAM: 4 };
|
||
const sorted = [...coords].sort((a, b) => {
|
||
const aAtivo = a.ativo ?? !a.fim;
|
||
const bAtivo = b.ativo ?? !b.fim;
|
||
if (aAtivo !== bAtivo) return bAtivo ? 1 : -1;
|
||
return (prioridade[a.codigo] || 99) - (prioridade[b.codigo] || 99);
|
||
});
|
||
return sorted[0]?.area_avaliacao || 'Coordenação CAPES';
|
||
})()}>
|
||
<div className="stat-value">{(() => {
|
||
const coords = consultor.coordenacoes_capes || [];
|
||
if (coords.length === 0) return '-';
|
||
const prioridade = { CA: 1, CAJ: 2, CAJ_MP: 3, CAM: 4 };
|
||
const sorted = [...coords].sort((a, b) => {
|
||
const aAtivo = a.ativo ?? !a.fim;
|
||
const bAtivo = b.ativo ?? !b.fim;
|
||
if (aAtivo !== bAtivo) return bAtivo ? 1 : -1;
|
||
return (prioridade[a.codigo] || 99) - (prioridade[b.codigo] || 99);
|
||
});
|
||
return sorted[0]?.codigo || '-';
|
||
})()}</div>
|
||
<div className="stat-label">Coord.</div>
|
||
</div>
|
||
<div className="stat" title={`${consultoria?.anos_consecutivos || 0} anos consecutivos`}>
|
||
<div className="stat-value">{consultoria?.anos_consecutivos || 0}</div>
|
||
<div className="stat-label">Anos Consec.</div>
|
||
</div>
|
||
<div className="stat">
|
||
<div className="score-value">{pontuacaoTotal}</div>
|
||
<div className="stat-label">Score</div>
|
||
</div>
|
||
<button
|
||
className="btn-raw-data"
|
||
onClick={handleRawDataClick}
|
||
title="Ver dados completos do ATUACAPES"
|
||
>
|
||
📋
|
||
</button>
|
||
<div className="expand-icon">{expanded ? '▲' : '▼'}</div>
|
||
</div>
|
||
</div>
|
||
|
||
{expanded && (
|
||
<div className="card-details" onClick={(e) => e.stopPropagation()}>
|
||
<div className="details-grid">
|
||
<div className="detail-section">
|
||
<h4>Pontuacao Total</h4>
|
||
<div className="score-breakdown-total score-breakdown-5cols">
|
||
<ScoreItemClickable
|
||
value={blocoA.total}
|
||
label="A"
|
||
style={{ color: blocoA.total > 0 ? 'var(--accent-2)' : 'var(--muted)' }}
|
||
onClick={() => setPontuacaoModal({
|
||
tipo: 'bloco',
|
||
label: 'BLOCO A - Coordenação CAPES',
|
||
value: blocoA.total,
|
||
formula: FORMULAS.bloco_a.descricao,
|
||
bloco: blocoA
|
||
})}
|
||
/>
|
||
<ScoreItemClickable
|
||
value={blocoB.total}
|
||
label="B"
|
||
style={{ color: blocoB.total > 0 ? 'var(--gold)' : 'var(--muted)' }}
|
||
onClick={() => setPontuacaoModal({
|
||
tipo: 'bloco',
|
||
label: 'BLOCO B - Consultoria',
|
||
value: blocoB.total,
|
||
formula: FORMULAS.bloco_b.descricao,
|
||
bloco: blocoB
|
||
})}
|
||
/>
|
||
<ScoreItemClickable
|
||
value={blocoC.total}
|
||
label="C"
|
||
style={{ color: blocoC.total > 0 ? 'var(--bronze)' : 'var(--muted)' }}
|
||
onClick={() => setPontuacaoModal({
|
||
tipo: 'bloco',
|
||
label: 'BLOCO C - Avaliações/Premiações',
|
||
value: blocoC.total,
|
||
formula: FORMULAS.bloco_c.descricao,
|
||
bloco: blocoC
|
||
})}
|
||
/>
|
||
<ScoreItemClickable
|
||
value={blocoD.total}
|
||
label="D"
|
||
style={{ color: blocoD.total > 0 ? 'var(--silver)' : 'var(--muted)' }}
|
||
onClick={() => setPontuacaoModal({
|
||
tipo: 'bloco',
|
||
label: 'BLOCO D - Indicadores',
|
||
value: blocoD.total,
|
||
formula: FORMULAS.bloco_d.descricao,
|
||
bloco: blocoD
|
||
})}
|
||
/>
|
||
<ScoreItemClickable
|
||
value={pontuacaoTotal}
|
||
label="TOTAL"
|
||
style={{ background: 'var(--accent)', color: 'white' }}
|
||
onClick={() => setPontuacaoModal({
|
||
tipo: 'total',
|
||
label: 'TOTAL',
|
||
value: pontuacaoTotal
|
||
})}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{blocoA.atuacoes && blocoA.atuacoes.length > 0 && (
|
||
<BlocoDetalhes titulo="A - Coordenacao CAPES" bloco={blocoA} cor="var(--accent-2)" onItemClick={setPontuacaoModal} />
|
||
)}
|
||
|
||
{blocoB.atuacoes && blocoB.atuacoes.length > 0 && (
|
||
<BlocoDetalhes titulo="B - Consultoria" bloco={blocoB} cor="var(--gold)" onItemClick={setPontuacaoModal} />
|
||
)}
|
||
|
||
{blocoC.atuacoes && blocoC.atuacoes.length > 0 && (
|
||
<BlocoDetalhes titulo="C - Avaliacoes/Premiacoes" bloco={blocoC} cor="var(--bronze)" onItemClick={setPontuacaoModal} />
|
||
)}
|
||
|
||
{blocoD.atuacoes && blocoD.atuacoes.length > 0 && (
|
||
<BlocoDetalhes titulo="D - Indicadores" bloco={blocoD} cor="var(--silver)" onItemClick={setPontuacaoModal} />
|
||
)}
|
||
</div>
|
||
|
||
{consultor.tipos_atuacao?.length > 0 && (
|
||
<div className="detail-section tipos-section">
|
||
<h4>Tipos de Atuacao</h4>
|
||
<TiposAtuacaoBadges
|
||
tipos={consultor.tipos_atuacao}
|
||
exibirTodos={true}
|
||
onBadgeClick={setTipoAtuacaoModal}
|
||
consultor={consultor}
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{selos.length > 0 && (
|
||
<div className="detail-section selos-section">
|
||
<h4>Selos e Reconhecimentos</h4>
|
||
<SelosBadges selos={selos} onSeloClick={setSeloModal} />
|
||
</div>
|
||
)}
|
||
|
||
{consultor.lattes?.id_lattes && (
|
||
<div className="detail-section lattes-section">
|
||
<h4>Curriculo Lattes</h4>
|
||
<div className="lattes-content">
|
||
<a
|
||
href={consultor.lattes.url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="lattes-link"
|
||
>
|
||
<span className="lattes-icon">📄</span>
|
||
<span className="lattes-id">{consultor.lattes.id_lattes}</span>
|
||
<span className="lattes-external">↗</span>
|
||
</a>
|
||
<div className="titulacoes-resumo">
|
||
<span
|
||
className="titulacao-badge titulacao-clicavel lattes-producoes"
|
||
title="Ver producoes do Lattes"
|
||
onClick={async (e) => {
|
||
e.stopPropagation();
|
||
try {
|
||
const data = await rankingService.getLattes(consultor.id_pessoa);
|
||
if (data.encontrado) {
|
||
setItemDetalhe({
|
||
item: { ...data, filtro: null, titulo: 'Producoes Lattes' },
|
||
tipo: 'producoes_lattes'
|
||
});
|
||
} else if (data.motivo) {
|
||
alert(data.motivo);
|
||
} else {
|
||
alert('Nenhuma producao encontrada no Lattes');
|
||
}
|
||
} catch (err) {
|
||
alert('Erro ao buscar Lattes: ' + err.message);
|
||
}
|
||
}}
|
||
>
|
||
📚 Producoes
|
||
</span>
|
||
{consultor.lattes.titulacoes
|
||
?.sort((a, b) => {
|
||
const ordem = { 'Pos-Doutorado': 1, 'Doutorado': 2, 'Mestrado': 3, 'Graduacao': 4, 'Bacharelado': 5 };
|
||
return (ordem[a.grau] || 99) - (ordem[b.grau] || 99);
|
||
})
|
||
.map((t, idx) => (
|
||
<span
|
||
key={idx}
|
||
className="titulacao-badge titulacao-clicavel"
|
||
onClick={(e) => {
|
||
e.stopPropagation();
|
||
setItemDetalhe({
|
||
item: t,
|
||
tipo: 'titulacao'
|
||
});
|
||
}}
|
||
title={`Clique para detalhes: ${t.grau}${t.area ? ` em ${t.area}` : ''}${t.programa ? ` (${t.programa})` : ''}${t.ies_nome ? ` - ${t.ies_nome}` : ''}`}
|
||
>
|
||
{t.grau}{t.ies_sigla ? ` (${t.ies_sigla})` : ''}{t.ano ? ` - ${t.ano}` : ''}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultoria?.vinculos?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Vinculos de Consultoria</h4>
|
||
<div className="list-items">
|
||
{[...consultoria.vinculos]
|
||
.sort((a, b) => new Date(b.periodo?.inicio || 0) - new Date(a.periodo?.inicio || 0))
|
||
.map((vinculo, idx) => {
|
||
const periodo = vinculo.periodo || {};
|
||
const isAtivo = periodo.ativo ?? !periodo.fim;
|
||
return (
|
||
<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>
|
||
<span className="ies-nome">
|
||
{vinculo.ies
|
||
? vinculo.ies.sigla && vinculo.ies.nome
|
||
? `${vinculo.ies.sigla} - ${vinculo.ies.nome}`
|
||
: vinculo.ies.sigla || vinculo.ies.nome
|
||
: 'IES nao informada'}
|
||
</span>
|
||
<span className="muted">
|
||
{formatDate(periodo.inicio)} - {isAtivo ? 'Atual' : formatDate(periodo.fim)}
|
||
</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.coordenacoes_capes?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Coordenacoes CAPES</h4>
|
||
<div className="list-items">
|
||
{[...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 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>
|
||
<span className="muted">
|
||
{formatDate(coord.inicio || coord.periodo?.inicio)} - {formatDate(coord.fim || coord.periodo?.fim)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.premiacoes?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Premiacoes</h4>
|
||
<div className="list-items">
|
||
{[...consultor.premiacoes]
|
||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||
.map((prem, idx) => (
|
||
<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>
|
||
<span className="muted">{prem.ano}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.avaliacoes_comissao?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Avaliacoes de Comissao</h4>
|
||
<div className="list-items">
|
||
{[...consultor.avaliacoes_comissao]
|
||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||
.map((aval, idx) => (
|
||
<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>
|
||
<span className="muted">{aval.ano}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.inscricoes?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Inscricoes</h4>
|
||
<div className="list-items">
|
||
{[...consultor.inscricoes]
|
||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||
.map((insc, idx) => (
|
||
<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.evento || insc.premio}</span>
|
||
<span className="muted">{insc.ano}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.participacoes?.length > 0 && (
|
||
<div className="extra-details">
|
||
<h4>Participacoes (Eventos/Projetos)</h4>
|
||
<div className="list-items">
|
||
{[...consultor.participacoes]
|
||
.sort((a, b) => (b.ano || 0) - (a.ano || 0))
|
||
.slice(0, 10)
|
||
.map((part, idx) => (
|
||
<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>
|
||
<span className="muted">{part.ano}</span>
|
||
</div>
|
||
))}
|
||
{consultor.participacoes.length > 10 && (
|
||
<div className="list-item muted">... e mais {consultor.participacoes.length - 10} participacoes</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{consultor.orientacoes?.length > 0 && (() => {
|
||
const orientacoes = consultor.orientacoes;
|
||
const contagem = {};
|
||
const premiadas = {};
|
||
orientacoes.forEach(o => {
|
||
contagem[o.codigo] = (contagem[o.codigo] || 0) + 1;
|
||
if (o.premiada) premiadas[o.codigo] = (premiadas[o.codigo] || 0) + 1;
|
||
});
|
||
const config = {
|
||
ORIENT_POS_DOC: { label: 'Pós-Doutorado', icone: '🔬', cor: 'tipo-orientador' },
|
||
ORIENT_TESE: { label: 'Tese (Doutorado)', icone: '📚', cor: 'tipo-orientador' },
|
||
ORIENT_DISS: { label: 'Dissertação (Mestrado)', icone: '📄', cor: 'tipo-orientador' },
|
||
CO_ORIENT_POS_DOC: { label: 'Coorient. Pós-Doc', icone: '🔬', cor: 'tipo-avaliador' },
|
||
CO_ORIENT_TESE: { label: 'Coorient. Tese', icone: '📚', cor: 'tipo-avaliador' },
|
||
CO_ORIENT_DISS: { label: 'Coorient. Diss.', icone: '📄', cor: 'tipo-avaliador' },
|
||
};
|
||
const ordem = ['ORIENT_POS_DOC', 'ORIENT_TESE', 'ORIENT_DISS', 'CO_ORIENT_POS_DOC', 'CO_ORIENT_TESE', 'CO_ORIENT_DISS'];
|
||
return (
|
||
<div className="extra-details">
|
||
<h4>Orientacoes ({orientacoes.length} total)</h4>
|
||
<div className="list-items">
|
||
{ordem.filter(cod => contagem[cod] > 0).map(cod => {
|
||
const cfg = config[cod];
|
||
const qtd = contagem[cod];
|
||
const prem = premiadas[cod] || 0;
|
||
return (
|
||
<div key={cod} className="list-item">
|
||
<span className="tipo-icone">{cfg.icone}</span>
|
||
<span className={`badge ${cfg.cor}`}>{cfg.label}</span>
|
||
<span className="pontos" style={{ minWidth: '60px' }}>{qtd}x</span>
|
||
{prem > 0 && (
|
||
<span className="badge badge-premiado" title={`${prem} premiada(s)`}>
|
||
🏆 {prem}
|
||
</span>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
);
|
||
})()}
|
||
</div>
|
||
)}
|
||
|
||
{showRawModal && (
|
||
<RawDataModal
|
||
idPessoa={consultor.id_pessoa}
|
||
nome={consultor.nome}
|
||
onClose={() => setShowRawModal(false)}
|
||
/>
|
||
)}
|
||
|
||
{tipoAtuacaoModal && (
|
||
<TipoAtuacaoModal
|
||
tipo={tipoAtuacaoModal}
|
||
consultor={consultor}
|
||
onClose={() => setTipoAtuacaoModal(null)}
|
||
/>
|
||
)}
|
||
|
||
{seloModal && (
|
||
<SeloModal
|
||
selo={seloModal}
|
||
consultor={consultor}
|
||
onClose={() => setSeloModal(null)}
|
||
/>
|
||
)}
|
||
|
||
{itemDetalhe && (
|
||
<ItemDetalheModal
|
||
item={itemDetalhe.item}
|
||
tipo={itemDetalhe.tipo}
|
||
onClose={() => setItemDetalhe(null)}
|
||
/>
|
||
)}
|
||
|
||
{pontuacaoModal && (
|
||
<PontuacaoModal
|
||
dados={pontuacaoModal}
|
||
onClose={() => setPontuacaoModal(null)}
|
||
/>
|
||
)}
|
||
|
||
{showInsights && (
|
||
<InsightsModal
|
||
consultor={consultor}
|
||
totalConsultores={totalConsultores}
|
||
onClose={() => setShowInsights(false)}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
});
|
||
|
||
ConsultorCard.displayName = 'ConsultorCard';
|
||
|
||
const CODIGOS_APENAS_SELO = [
|
||
'ORIENT_POS_DOC', 'ORIENT_TESE', 'ORIENT_DISS',
|
||
'CO_ORIENT_POS_DOC', 'CO_ORIENT_TESE', 'CO_ORIENT_DISS',
|
||
'MB_BANCA_POS_DOC', 'MB_BANCA_TESE', 'MB_BANCA_DISS',
|
||
'ORIENT_POS_DOC_PREM', 'ORIENT_TESE_PREM', 'ORIENT_DISS_PREM',
|
||
'CO_ORIENT_POS_DOC_PREM', 'CO_ORIENT_TESE_PREM', 'CO_ORIENT_DISS_PREM',
|
||
'MB_BANCA_POS_DOC_PREM', 'MB_BANCA_TESE_PREM', 'MB_BANCA_DISS_PREM',
|
||
];
|
||
|
||
const BlocoDetalhes = memo(({ titulo, bloco, cor, onItemClick }) => {
|
||
const atuacoesFiltradas = bloco.atuacoes?.filter(at => !CODIGOS_APENAS_SELO.includes(at.codigo)) || [];
|
||
|
||
return (
|
||
<div className="detail-section">
|
||
<h4 style={{ color: cor }}>{titulo}</h4>
|
||
<div className="score-breakdown">
|
||
{atuacoesFiltradas.map((at, idx) => (
|
||
<div
|
||
key={idx}
|
||
className="score-item-wrapper score-item-clicavel"
|
||
onClick={() => onItemClick && onItemClick({
|
||
tipo: 'atuacao',
|
||
label: at.codigo,
|
||
value: at.total,
|
||
atuacao: at
|
||
})}
|
||
>
|
||
<div className="score-item">
|
||
<div className="score-item-value">{at.total}</div>
|
||
<div className="score-item-label">{at.codigo}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
<div
|
||
className="score-item-wrapper score-item-clicavel"
|
||
onClick={() => onItemClick && onItemClick({
|
||
tipo: 'bloco',
|
||
label: titulo,
|
||
value: bloco.total,
|
||
bloco: bloco
|
||
})}
|
||
>
|
||
<div className="score-item score-total">
|
||
<div className="score-item-value">{bloco.total}</div>
|
||
<div className="score-item-label">TOTAL</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
});
|
||
|
||
BlocoDetalhes.displayName = 'BlocoDetalhes';
|
||
|
||
export default ConsultorCard;
|