fix(backend): corrigir exibicao de idiomas e selos multilingue
- Adicionar idiomas e formacoes ao _source das queries ES (client.py) - Corrigir type mismatch int/str no endpoint paginado (routes.py) - Adicionar campo evento nas inscricoes para nome do premio - Implementar extracao de idiomas do ES no repository - Ajustar frontend para exibir selo multilingue corretamente
This commit is contained in:
@@ -87,7 +87,6 @@ const DADOS_BLOCOS = {
|
||||
],
|
||||
selos: [
|
||||
{ cod: 'PPG_COORD', nome: 'Coordenador PPG', obs: 'Indicador (sem pontuação V1)' },
|
||||
{ cod: 'IDIOMA_BILINGUE', nome: 'Bilíngue', obs: '2+ idiomas' },
|
||||
{ cod: 'IDIOMA_MULTILINGUE', nome: 'Multilíngue', obs: '3+ idiomas' },
|
||||
{ cod: 'TITULACAO_MESTRE', nome: 'Mestre', obs: 'Maior titulação' },
|
||||
{ cod: 'TITULACAO_DOUTOR', nome: 'Doutor', obs: 'Maior titulação' },
|
||||
|
||||
@@ -28,7 +28,6 @@ const SELOS = {
|
||||
MB_BANCA_DISS: { codigo: 'MB_BANCA_DISS', label: 'Banca Diss.', cor: 'selo-banca', icone: '📄' },
|
||||
EVENTO: { codigo: 'EVENTO', label: 'Evento', cor: 'selo-evento', icone: '📅' },
|
||||
PROJ: { codigo: 'PROJ', label: 'Projeto', cor: 'selo-proj', icone: '📁' },
|
||||
IDIOMA_BILINGUE: { codigo: 'IDIOMA_BILINGUE', label: 'Bilingue', cor: 'selo-idioma', icone: '🌍' },
|
||||
IDIOMA_MULTILINGUE: { codigo: 'IDIOMA_MULTILINGUE', label: 'Multilingue', cor: 'selo-idioma', icone: '🌐' },
|
||||
TITULACAO_MESTRE: { codigo: 'TITULACAO_MESTRE', label: 'Mestre', cor: 'selo-titulacao', icone: '🎓' },
|
||||
TITULACAO_DOUTOR: { codigo: 'TITULACAO_DOUTOR', label: 'Doutor', cor: 'selo-titulacao', icone: '🎓' },
|
||||
@@ -49,6 +48,13 @@ const TIPOS_ATUACAO_CONFIG = {
|
||||
|
||||
const gerarSelos = (consultor) => {
|
||||
const selos = [];
|
||||
const normalizarIdioma = (valor) => (valor || '')
|
||||
.toString()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.trim();
|
||||
const normalizarCodigoIdioma = (valor) => normalizarIdioma(valor).replace(/[^a-z0-9]/g, '');
|
||||
|
||||
const isPresidCamaraVigente = consultor.coordenacoes_capes?.some(
|
||||
(c) => c.codigo === 'CAM' && c.presidente && (c.ativo ?? !c.fim)
|
||||
@@ -132,6 +138,40 @@ const gerarSelos = (consultor) => {
|
||||
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(', ')}`,
|
||||
});
|
||||
}
|
||||
|
||||
const titulacao = consultor.titulacao || '';
|
||||
const titulacaoLower = titulacao.toLowerCase();
|
||||
if (titulacaoLower.includes('pós-doutorado') || titulacaoLower.includes('pos-doutorado') || titulacaoLower.includes('posdoc') || titulacaoLower.includes('pós-doc')) {
|
||||
selos.push({ ...SELOS.TITULACAO_POS_DOUTOR, qtd: 1, hint: 'Pós-Doutorado' });
|
||||
} else if (titulacaoLower.includes('doutorado') || titulacaoLower.includes('doutor')) {
|
||||
selos.push({ ...SELOS.TITULACAO_DOUTOR, qtd: 1, hint: 'Doutorado' });
|
||||
} else if (titulacaoLower.includes('mestrado') || titulacaoLower.includes('mestre')) {
|
||||
selos.push({ ...SELOS.TITULACAO_MESTRE, qtd: 1, hint: 'Mestrado' });
|
||||
}
|
||||
|
||||
return selos;
|
||||
};
|
||||
|
||||
@@ -144,6 +184,8 @@ const SELOS_COM_DADOS = [
|
||||
'CO_ORIENT_TESE', 'CO_ORIENT_DISS', 'CO_ORIENT_POS_DOC',
|
||||
'MB_BANCA_POS_DOC', 'MB_BANCA_TESE', 'MB_BANCA_DISS',
|
||||
'EVENTO', 'PROJ',
|
||||
'IDIOMA_MULTILINGUE',
|
||||
'TITULACAO_MESTRE', 'TITULACAO_DOUTOR', 'TITULACAO_POS_DOUTOR',
|
||||
];
|
||||
|
||||
const SelosBadges = ({ selos, compacto = false, onSeloClick }) => {
|
||||
@@ -355,7 +397,7 @@ const TipoAtuacaoModal = ({ tipo, consultor, onClose }) => {
|
||||
{[...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.premio || ins.descricao || '-'}</span>
|
||||
<span className="modal-item-main">{ins.evento || ins.premio || ins.descricao || '-'}</span>
|
||||
<span className="muted">{ins.ano || '-'}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -837,6 +879,12 @@ const ItemDetalheModal = ({ item, tipo, onClose }) => {
|
||||
<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>
|
||||
@@ -1461,7 +1509,7 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
onClick={handleRawDataClick}
|
||||
title="Ver dados completos do ATUACAPES"
|
||||
>
|
||||
⋮
|
||||
📋
|
||||
</button>
|
||||
<div className="expand-icon">{expanded ? '▲' : '▼'}</div>
|
||||
</div>
|
||||
@@ -1687,7 +1735,7 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
||||
>
|
||||
<span className="badge">{insc.codigo}</span>
|
||||
<span className="pontos">{PONTOS_BASE[insc.codigo] || 0} pts</span>
|
||||
<span>{insc.premio}</span>
|
||||
<span>{insc.evento || insc.premio}</span>
|
||||
<span className="muted">{insc.ano}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -19,6 +19,9 @@ const SELOS_CONFIG = {
|
||||
{ codigo: 'ORIENT_GP', label: 'Orient. GP', icone: '🏆' },
|
||||
{ codigo: 'ORIENT_PREMIO', label: 'Orient. Prêmio', icone: '🎖️' },
|
||||
{ codigo: 'ORIENT_MENCAO', label: 'Orient. Menção', icone: '📜' },
|
||||
{ codigo: 'COORIENT_GP', label: 'Coorient. GP', icone: '🏆' },
|
||||
{ codigo: 'COORIENT_PREMIO', label: 'Coorient. Prêmio', icone: '🎖️' },
|
||||
{ codigo: 'COORIENT_MENCAO', label: 'Coorient. Menção', icone: '📜' },
|
||||
],
|
||||
},
|
||||
orientacoes: {
|
||||
|
||||
@@ -493,6 +493,9 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.equipe-count {
|
||||
@@ -518,6 +521,7 @@
|
||||
.equipe-acoes {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-limpar {
|
||||
@@ -530,6 +534,7 @@
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-limpar:hover {
|
||||
@@ -547,6 +552,7 @@
|
||||
font-weight: 700;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-gerar-pdf:hover:not(:disabled) {
|
||||
|
||||
@@ -90,6 +90,8 @@ export const rankingService = {
|
||||
participacoes: c.participacoes || [],
|
||||
orientacoes: c.orientacoes || [],
|
||||
membros_banca: c.membros_banca || [],
|
||||
idiomas: c.idiomas || [],
|
||||
titulacao: c.titulacao || '',
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user