import React, { useState, useEffect, useCallback, useRef } from 'react';
import ReactDOM from 'react-dom';
import { rankingService } from '../services/api';
import './RawDataModal.css';
const decodeHtmlEntities = (str) => {
if (typeof str !== 'string') return str;
const textarea = document.createElement('textarea');
textarea.innerHTML = str;
return textarea.value;
};
const formatDate = (dateStr) => {
if (!dateStr) return null;
try {
if (dateStr.includes('/')) {
return dateStr.split(' ')[0];
}
const date = new Date(dateStr);
return date.toLocaleDateString('pt-BR');
} catch {
return dateStr;
}
};
const formatValue = (value) => {
if (value === null || value === undefined || value === '') return null;
if (typeof value === 'boolean') return value ? 'Sim' : 'Não';
if (typeof value === 'number') return String(value);
if (typeof value === 'string') return decodeHtmlEntities(value);
if (Array.isArray(value)) {
if (value.length === 0) return null;
return value.map(item => {
if (typeof item === 'object' && item !== null) {
const val = item.nome || item.descricao || item.sigla || item.tipo || JSON.stringify(item);
return decodeHtmlEntities(val);
}
return decodeHtmlEntities(String(item));
}).join(', ');
}
if (typeof value === 'object') {
if (value.nome) return decodeHtmlEntities(value.nome);
if (value.descricao) return decodeHtmlEntities(value.descricao);
if (value.sigla) return decodeHtmlEntities(value.sigla);
return null;
}
return decodeHtmlEntities(String(value));
};
const LABEL_MAP = {
tipo: 'Tipo',
nome: 'Nome',
sigla: 'Sigla',
codigo: 'Código',
situacao: 'Situação',
situacaoConsultoria: 'Situação',
areaAvaliacao: 'Área de Avaliação',
areaConhecimento: 'Área de Conhecimento',
areaConhecimentoPos: 'Área de Conhecimento Pós',
areaPesquisa: 'Área de Pesquisa',
colegio: 'Colégio',
ies: 'IES',
programa: 'Programa',
nomeEmpregador: 'Empregador',
emprego: 'Vínculo',
atividade: 'Atividade',
dtAdmissao: 'Admissão',
dtDesligamento: 'Desligamento',
cnpjEmpregador: 'CNPJ Empregador',
profissao: 'Profissão',
matricula: 'Matrícula',
vinculo: 'Vínculo',
historico: 'Histórico',
inicioRelacionamento: 'Início Relacionamento',
fimRelacionamento: 'Fim Relacionamento',
categoria: 'Categoria',
tipoVinculo: 'Tipo de Vínculo',
vinculoTrabalho: 'Vínculo de Trabalho',
regimeTrabalho: 'Regime de Trabalho',
cargaHoraria: 'Carga Horária',
linhaPesquisa: 'Linha de Pesquisa',
areaConcentracao: 'Área de Concentração',
areaPesquisa: 'Área de Pesquisa',
consultorResponsavel: 'Consultor Responsável',
unidadeOrganizacional: 'Unidade Organizacional',
localEvento: 'Local do Evento',
nrProcesso: 'Número do Processo',
idProcesso: 'ID do Processo',
anoInicio: 'Ano de Início',
tipoTrabalho: 'Tipo de Trabalho',
inicioVinculacao: 'Início Vinculação',
fimVinculacao: 'Fim Vinculação',
inicioSituacao: 'Início Situação',
inativacaoSituacao: 'Inativação',
portaria: 'Portaria',
dataPortaria: 'Data Portaria',
premio: 'Prêmio',
evento: 'Evento',
premiacao: 'Premiação',
ano: 'Ano',
edicao: 'Edição',
papelPessoa: 'Papel',
comissao: 'Comissão',
produto: 'Produto',
nivel: 'Nível',
modalidade: 'Modalidade',
camaraTematica: 'Câmara Temática',
totalOrientacaoFinalizadaMestrado: 'Orientações Finalizadas (Mestrado)',
totalOrientacaoFinalizadaDoutorado: 'Orientações Finalizadas (Doutorado)',
totalOrientacaoAndamentoMestrado: 'Orientações em Andamento (Mestrado)',
totalOrientacaoAndamentoDoutorado: 'Orientações em Andamento (Doutorado)',
totalAcompanhamentoPosDoutorado: 'Acompanhamentos Pós-Doutorado',
};
const formatLabel = (key) => LABEL_MAP[key] || key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
const parseDate = (dateStr) => {
if (!dateStr) return null;
try {
if (dateStr.includes('/')) {
const parts = dateStr.split('/');
if (parts.length === 3) {
const [day, month, year] = parts;
return new Date(year, month - 1, day);
}
}
const date = new Date(dateStr);
return isNaN(date.getTime()) ? null : date;
} catch {
return null;
}
};
const getAtuacaoDate = (atuacao) => {
const candidateDates = [
atuacao.inicio,
atuacao.fim,
atuacao.dadosEmprego?.dtAdmissao,
atuacao.dadosEmprego?.dtDesligamento,
atuacao.dadosDocencia?.inicioVinculacao,
atuacao.dadosDocencia?.fimVinculacao,
atuacao.dadosCoordenacaoArea?.inicioVinculacao,
atuacao.dadosCoordenacaoArea?.fimVinculacao,
atuacao.dadosHistoricoCoordenacaoArea?.inicioVinculacao,
atuacao.dadosHistoricoCoordenacaoArea?.fimVinculacao,
atuacao.dadosConsultoria?.inicioVinculacao,
atuacao.dadosConsultoria?.fimVinculacao,
atuacao.dadosConsultoria?.inicioSituacao,
atuacao.dadosConsultoria?.inativacaoSituacao,
atuacao.dadosEvento?.inicio,
atuacao.dadosEvento?.fim,
atuacao.dadosParticipacaoEvento?.inicio,
atuacao.dadosParticipacaoEvento?.fim,
atuacao.dadosPremiacaoPremio?.ano ? `01/01/${atuacao.dadosPremiacaoPremio.ano}` : null,
atuacao.dadosParticipacaoPremio?.ano ? `01/01/${atuacao.dadosParticipacaoPremio.ano}` : null,
atuacao.dadosParticipacaoInscricaoPremio?.ano ? `01/01/${atuacao.dadosParticipacaoInscricaoPremio.ano}` : null,
atuacao.dadosProjeto?.anoInicio ? `01/01/${atuacao.dadosProjeto.anoInicio}` : null,
];
for (const dateStr of candidateDates) {
const date = parseDate(dateStr);
if (date) return date;
}
return null;
};
const sortAtuacoesByDate = (atuacoes) => {
return [...atuacoes].sort((a, b) => {
const dateA = getAtuacaoDate(a);
const dateB = getAtuacaoDate(b);
if (!dateA && !dateB) return 0;
if (!dateA) return 1;
if (!dateB) return -1;
return dateB.getTime() - dateA.getTime();
});
};
const DataField = ({ label, value, className = '' }) => {
const formattedValue = formatValue(value);
if (formattedValue === null) return null;
return (
{formatLabel(label)}
{formattedValue}
);
};
const NestedObjectDisplay = ({ data, depth = 0 }) => {
if (!data || typeof data !== 'object') return null;
const entries = Object.entries(data).filter(([key, value]) => {
if (value === null || value === undefined || value === '') return false;
if (Array.isArray(value) && value.length === 0) return false;
return true;
});
if (entries.length === 0) return null;
return (
{entries.map(([key, value]) => {
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
const simpleValue = value.nome || value.descricao || value.sigla;
if (simpleValue) {
return
;
}
return (
{formatLabel(key)}
);
}
if (Array.isArray(value)) {
const formatted = value.map(item => {
if (typeof item === 'object' && item !== null) {
return item.nome || item.descricao || item.sigla || item.tipo || Object.values(item).filter(v => typeof v === 'string')[0] || '';
}
return String(item);
}).filter(Boolean).join(', ');
if (!formatted) return null;
return
;
}
return
;
})}
);
};
const Section = ({ title, icon, children, defaultOpen = true, count }) => {
const [isOpen, setIsOpen] = useState(defaultOpen);
return (
setIsOpen(!isOpen)}>
{icon}
{title}
{count !== undefined && {count}}
{isOpen ? '▼' : '▶'}
{isOpen &&
{children}
}
);
};
const AtuacaoCard = ({ atuacao, index }) => {
const [expanded, setExpanded] = useState(false);
const tipo = atuacao.tipo || 'Tipo não informado';
const getNestedValue = (obj, path) => {
if (!obj || !path) return undefined;
return path.split('.').reduce((acc, key) => (acc ? acc[key] : undefined), obj);
};
const joinParts = (parts) => parts.filter(Boolean).join(' · ');
const buildSummary = () => {
const tipoLower = tipo.toLowerCase();
const dadosDocencia = atuacao.dadosDocencia || {};
const dadosEmprego = atuacao.dadosEmprego || {};
const dadosEvento = atuacao.dadosEvento || atuacao.dadosParticipacaoEvento || {};
const dadosProjeto = atuacao.dadosProjeto || {};
const dadosProcesso = atuacao.dadosProcesso || {};
const dadosOrientacaoDiscente = atuacao.dadosOrientacaoDiscente || {};
if (tipoLower.includes('docência') || tipoLower.includes('docencia')) {
const ies = dadosDocencia.ies?.sigla
? `${dadosDocencia.ies.sigla} — ${dadosDocencia.ies.nome}`
: dadosDocencia.ies?.nome;
const primary = joinParts([
dadosDocencia.programa?.nome || dadosDocencia.areaConhecimento?.nome || atuacao.descricao,
ies,
dadosDocencia.categoria,
]);
const secondary = joinParts([
dadosDocencia.tipoVinculo,
dadosDocencia.regimeTrabalho,
dadosDocencia.cargaHoraria ? `${dadosDocencia.cargaHoraria}h` : null,
]);
return { primary, secondary };
}
if (tipoLower.includes('emprego')) {
const primary = joinParts([
dadosEmprego.nomeEmpregador || atuacao.descricao,
dadosEmprego.emprego || dadosEmprego.vinculo,
dadosEmprego.atividade,
]);
const secondary = joinParts([
dadosEmprego.dtAdmissao,
dadosEmprego.dtDesligamento ? `até ${dadosEmprego.dtDesligamento}` : null,
dadosEmprego.profissao,
]);
return { primary, secondary };
}
if (tipoLower.includes('evento')) {
const primary = joinParts([
dadosEvento.nome || atuacao.descricao,
dadosEvento.unidadeOrganizacional,
dadosEvento.localEvento,
]);
const secondary = joinParts([
dadosEvento.atividade,
dadosEvento.tipo,
dadosEvento.codigo ? `Código ${dadosEvento.codigo}` : null,
]);
return { primary, secondary };
}
if (tipoLower.includes('projeto')) {
const primary = joinParts([
dadosProjeto.nome || atuacao.descricao,
dadosProjeto.programa?.nome,
dadosProjeto.ies?.sigla || dadosProjeto.ies?.nome,
]);
const secondary = joinParts([
dadosProjeto.situacao,
dadosProjeto.linhaPesquisa,
dadosProjeto.anoInicio ? `Início ${dadosProjeto.anoInicio}` : null,
]);
return { primary, secondary };
}
if (tipoLower.includes('orientação de discentes') || tipoLower.includes('orientacao de discentes')) {
const mestradoParts = [];
if (dadosOrientacaoDiscente.totalOrientacaoFinalizadaMestrado) {
mestradoParts.push(`Finalizadas ${dadosOrientacaoDiscente.totalOrientacaoFinalizadaMestrado}`);
}
if (dadosOrientacaoDiscente.totalOrientacaoAndamentoMestrado) {
mestradoParts.push(`Andamento ${dadosOrientacaoDiscente.totalOrientacaoAndamentoMestrado}`);
}
const doutoradoParts = [];
if (dadosOrientacaoDiscente.totalOrientacaoFinalizadaDoutorado) {
doutoradoParts.push(`Finalizadas ${dadosOrientacaoDiscente.totalOrientacaoFinalizadaDoutorado}`);
}
if (dadosOrientacaoDiscente.totalOrientacaoAndamentoDoutorado) {
doutoradoParts.push(`Andamento ${dadosOrientacaoDiscente.totalOrientacaoAndamentoDoutorado}`);
}
const primary = joinParts([
mestradoParts.length ? `Mestrado ${mestradoParts.join(' / ')}` : null,
doutoradoParts.length ? `Doutorado ${doutoradoParts.join(' / ')}` : null,
]);
const secondary = dadosOrientacaoDiscente.totalAcompanhamentoPosDoutorado
? `Pós-doc ${dadosOrientacaoDiscente.totalAcompanhamentoPosDoutorado}`
: null;
return { primary, secondary };
}
if (tipoLower.includes('processo')) {
const iesLabel = dadosProcesso.ies?.sigla || dadosProcesso.ies?.nome;
const primary = joinParts([dadosProcesso.nrProcesso || atuacao.descricao, iesLabel]);
const secondary = dadosProcesso.ies?.statusJuridico
? `IES ${dadosProcesso.ies.statusJuridico.trim()}`
: null;
return { primary, secondary };
}
const fallbackPrimary = joinParts([
atuacao.descricao,
getNestedValue(atuacao, 'procedencia.origem'),
]);
return { primary: fallbackPrimary, secondary: null };
};
const getAtuacaoColor = (tipo) => {
if (tipo.includes('Coordenação')) return 'atuacao-coordenacao';
if (tipo.includes('Consultor')) return 'atuacao-consultoria';
if (tipo.includes('Premiação')) return 'atuacao-premiacao';
if (tipo.includes('Avaliação')) return 'atuacao-avaliacao';
if (tipo.includes('Inscrição')) return 'atuacao-inscricao';
if (tipo.includes('Bolsista')) return 'atuacao-bolsa';
if (tipo.includes('Orientação')) return 'atuacao-orientacao';
return 'atuacao-outros';
};
const getAllDados = () => {
const allData = {};
const dataKeys = [
'dadosCoordenacaoArea',
'dadosHistoricoCoordenacaoArea',
'dadosConsultoria',
'dadosPremiacaoPremio',
'dadosParticipacaoPremio',
'dadosParticipacaoInscricaoPremio',
'dadosBolsistaCNPq',
'dadosOrientacao',
'dadosOrientacaoDiscente',
'dadosParticipacao',
'dadosParticipacaoEvento',
'dadosGestaoPrograma',
'dadosDocencia',
'dadosEmprego',
'dadosEvento',
'dadosProjeto',
'dadosProcesso',
'dadosAnaliseProcesso',
'dadosPapelPessoa',
];
const dateKeys = ['inicio', 'fim', 'inicioVinculacao', 'fimVinculacao', 'inicioSituacao', 'inativacaoSituacao'];
const dateData = {};
dataKeys.forEach(key => {
if (atuacao[key]) {
Object.entries(atuacao[key]).forEach(([k, v]) => {
if (dateKeys.includes(k)) {
dateData[k] = v;
} else {
allData[k] = v;
}
});
}
});
const baseKeys = ['descricao', 'quantidade', 'id', 'procedencia'];
baseKeys.forEach((key) => {
if (atuacao[key] !== null && atuacao[key] !== undefined && allData[key] === undefined) {
allData[key] = atuacao[key];
}
});
if (Object.keys(allData).length === 0) {
if (atuacao.inicio) allData.inicio = atuacao.inicio;
if (atuacao.fim) allData.fim = atuacao.fim;
Object.assign(allData, dateData);
}
return allData;
};
const dados = getAllDados();
const hasData = Object.keys(dados).length > 0;
const summary = buildSummary();
return (
setExpanded(!expanded)}>
#{index + 1}
{tipo}
{summary.primary && {summary.primary}}
{atuacao.inicio && {formatDate(atuacao.inicio)}}
{atuacao.inicio && atuacao.fim && - }
{atuacao.fim ? {formatDate(atuacao.fim)} : atuacao.inicio && Atual}
{summary.secondary &&
{summary.secondary}
}
{expanded ? '−' : '+'}
{expanded && (
{hasData ? (
) : (
Sem dados adicionais
)}
)}
);
};
const RawDataModal = ({ idPessoa, nome, onClose }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [viewMode, setViewMode] = useState('formatted');
const [filterType, setFilterType] = useState('all');
const [copyFeedback, setCopyFeedback] = useState(false);
const [downloadingPDF, setDownloadingPDF] = useState(false);
const fetchedRef = useRef(null);
useEffect(() => {
if (fetchedRef.current === idPessoa) {
return;
}
fetchedRef.current = idPessoa;
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await rankingService.getConsultorRaw(idPessoa);
setData(result);
} catch (err) {
setError(err.response?.data?.detail || err.message || 'Erro ao carregar dados');
} finally {
setLoading(false);
}
};
fetchData();
}, [idPessoa]);
const handleKeyDown = useCallback((e) => {
if (e.key === 'Escape') onClose();
}, [onClose]);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = '';
};
}, [handleKeyDown]);
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
setCopyFeedback(true);
setTimeout(() => setCopyFeedback(false), 2000);
} catch (err) {
console.error('Erro ao copiar:', err);
}
};
const handleDownloadPDF = async () => {
if (downloadingPDF) return;
setDownloadingPDF(true);
try {
await rankingService.downloadFichaPDF(idPessoa, nome);
} catch (err) {
console.error('Erro ao baixar PDF:', err);
alert('Erro ao gerar PDF. Tente novamente.');
} finally {
setDownloadingPDF(false);
}
};
const source = data?._source || {};
const dadosPessoais = source.dadosPessoais || {};
const atuacoes = source.atuacoes || [];
const tiposAtuacao = [...new Set(atuacoes.map(a => a.tipo))].sort();
const atuacoesFiltradas = sortAtuacoesByDate(
filterType === 'all'
? atuacoes
: atuacoes.filter(a => a.tipo === filterType)
);
const atuacoesPorTipo = tiposAtuacao.reduce((acc, tipo) => {
acc[tipo] = atuacoes.filter(a => a.tipo === tipo).length;
return acc;
}, {});
const modalContent = (
e.target.classList.contains('raw-modal-overlay') && onClose()}>
Dados Completos ATUACAPES
{nome} (ID: {idPessoa})
{loading && (
Carregando dados do Elasticsearch...
)}
{error && (
⚠
{error}
)}
{!loading && !error && data && viewMode === 'formatted' && (
{atuacoes.length > 0 && (
<>
{tiposAtuacao.map(tipo => (
setFilterType(filterType === tipo ? 'all' : tipo)}
>
{tipo.replace('Histórico de ', '').substring(0, 25)}
{atuacoesPorTipo[tipo]}
))}
{atuacoesFiltradas.map((atuacao, idx) => (
))}
>
)}
{atuacoes.length === 0 && (
Nenhuma atuação registrada
)}
)}
{!loading && !error && data && viewMode === 'json' && (
{JSON.stringify(data, null, 2)}
)}
Fonte: Elasticsearch ATUACAPES | Index: {data?._index || 'atuacapes'} | {atuacoes.length} atuações
);
return ReactDOM.createPortal(modalContent, document.body);
};
export default RawDataModal;