feat(selos): implementar sistema completo de selos para consultores

- Adicionar coluna SELOS na tabela TB_RANKING_CONSULTOR (migration v1.2)
- Criar script popular_selos.py para popular/atualizar selos em batch
- Simplificar para 22 selos com dados reais (remover selos sem dados)
- Adicionar selo IDIOMA_MULTILINGUE para consultores com 3+ idiomas
- Corrigir filtro de selos com query LIKE exata (evitar matches parciais)
- Alinhar ícones entre FiltroSelos e ConsultorCard
- Reorganizar filtro em 7 categorias: Coordenação, Consultoria, Avaliações,
  Premiações, Orientações, Participações, Características

Selos disponíveis: 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
This commit is contained in:
Frederico Castro
2025-12-27 21:08:22 -03:00
parent edb4e00880
commit 13ccfb02d3
8 changed files with 361 additions and 130 deletions

View File

@@ -5,33 +5,28 @@ import RawDataModal from './RawDataModal';
import { rankingService } from '../services/api';
const SELOS = {
PRESID_CAMARA: { codigo: 'PRESID_CAMARA', label: 'Presidente Camara', cor: 'selo-camara', icone: '👑' },
COORD_PPG: { codigo: 'COORD_PPG', label: 'Coord. PPG', cor: 'selo-coord', icone: '🎓' },
BPQ: { codigo: 'BPQ', label: 'BPQ', cor: 'selo-bpq', icone: '🏅' },
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 Premio', cor: 'selo-premio', icone: '🥇' },
AUTOR_MENCAO: { codigo: 'AUTOR_MENCAO', label: 'Autor Mencao', cor: 'selo-mencao', 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. Premio', cor: 'selo-orient-premio', icone: '🎖️' },
ORIENT_MENCAO: { codigo: 'ORIENT_MENCAO', label: 'Orient. Mencao', cor: 'selo-orient-mencao', 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. Premio', cor: 'selo-coorient-premio', icone: '🎖️' },
COORIENT_MENCAO: { codigo: 'COORIENT_MENCAO', label: 'Coorient. Mencao', cor: 'selo-coorient-mencao', 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: '📄' },
ORIENT_POS_DOC: { codigo: 'ORIENT_POS_DOC', label: 'Orient. Pos-Doc', cor: 'selo-orient', icone: '🔬' },
CO_ORIENT_TESE: { codigo: 'CO_ORIENT_TESE', label: 'Coorient. Tese', cor: 'selo-coorient', icone: '📚' },
CO_ORIENT_DISS: { codigo: 'CO_ORIENT_DISS', label: 'Coorient. Diss.', cor: 'selo-coorient', icone: '📄' },
CO_ORIENT_POS_DOC: { codigo: 'CO_ORIENT_POS_DOC', label: 'Coorient. Pos-Doc', cor: 'selo-coorient', icone: '🔬' },
MB_BANCA_POS_DOC: { codigo: 'MB_BANCA_POS_DOC', label: 'Banca Pos-Doc', cor: 'selo-banca', icone: '🔬' },
MB_BANCA_TESE: { codigo: 'MB_BANCA_TESE', label: 'Banca Tese', cor: 'selo-banca', icone: '📚' },
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_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: '🎓' },
TITULACAO_POS_DOUTOR: { codigo: 'TITULACAO_POS_DOUTOR', label: 'Pos-Doutor', cor: 'selo-titulacao', 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 = {
@@ -54,30 +49,46 @@ const gerarSelos = (consultor) => {
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.trim();
const normalizarCodigoIdioma = (valor) => normalizarIdioma(valor).replace(/[^a-z0-9]/g, '');
const isPresidCamaraVigente = consultor.coordenacoes_capes?.some(
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' });
}
if (consultor.coordenador_ppg) {
selos.push({ ...SELOS.COORD_PPG, qtd: 1, hint: 'Coordenador de PPG' });
const consultoria = consultor.consultoria;
if (consultoria && consultoria.codigo === 'CONS_ATIVO') {
selos.push({ ...SELOS.CONS_ATIVO, qtd: 1, hint: 'Consultor Ativo' });
}
const bolsas = Array.isArray(consultor.bolsas_cnpq) ? consultor.bolsas_cnpq : [];
if (bolsas.length > 0) {
const porNivel = {};
for (const b of bolsas) {
const nivel = (b.nivel || 'N/A').toString().trim();
porNivel[nivel] = (porNivel[nivel] || 0) + 1;
}
const niveis = Object.keys(porNivel).sort();
const label = niveis.length === 1 ? `BPQ ${niveis[0]}` : 'BPQ';
const niveisStr = niveis.join(', ');
selos.push({ ...SELOS.BPQ, label, qtd: bolsas.length, hint: `BPQ NIVEL ${niveisStr}` });
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 : [];
@@ -98,34 +109,13 @@ const gerarSelos = (consultor) => {
gerarSelosPorPapel('coorientador', SELOS.COORIENT_GP, SELOS.COORIENT_PREMIO, SELOS.COORIENT_MENCAO, 'Coorientador');
const orientacoes = Array.isArray(consultor.orientacoes) ? consultor.orientacoes : [];
const gerarSelosOrientacaoContagem = (codigo, isCoorientacao, seloBase) => {
const lista = orientacoes.filter((o) => o.codigo === codigo && (isCoorientacao ? o.coorientacao : !o.coorientacao));
if (lista.length > 0) {
selos.push({ ...seloBase, qtd: lista.length, hint: `${seloBase.label} (${lista.length}x)` });
}
};
gerarSelosOrientacaoContagem('ORIENT_POS_DOC', false, SELOS.ORIENT_POS_DOC);
gerarSelosOrientacaoContagem('ORIENT_TESE', false, SELOS.ORIENT_TESE);
gerarSelosOrientacaoContagem('ORIENT_DISS', false, SELOS.ORIENT_DISS);
gerarSelosOrientacaoContagem('CO_ORIENT_POS_DOC', true, SELOS.CO_ORIENT_POS_DOC);
gerarSelosOrientacaoContagem('CO_ORIENT_TESE', true, SELOS.CO_ORIENT_TESE);
gerarSelosOrientacaoContagem('CO_ORIENT_DISS', true, SELOS.CO_ORIENT_DISS);
const membrosBanca = Array.isArray(consultor.membros_banca) ? consultor.membros_banca : [];
const bancaPosDoc = membrosBanca.filter((m) => m.codigo === 'MB_BANCA_POS_DOC');
const bancaTese = membrosBanca.filter((m) => m.codigo === 'MB_BANCA_TESE');
const bancaDiss = membrosBanca.filter((m) => m.codigo === 'MB_BANCA_DISS');
if (bancaPosDoc.length > 0) {
selos.push({ ...SELOS.MB_BANCA_POS_DOC, qtd: bancaPosDoc.length, hint: `Banca Pos-Doc (${bancaPosDoc.length}x)` });
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 (bancaTese.length > 0) {
selos.push({ ...SELOS.MB_BANCA_TESE, qtd: bancaTese.length, hint: `Banca Tese (${bancaTese.length}x)` });
}
if (bancaDiss.length > 0) {
selos.push({ ...SELOS.MB_BANCA_DISS, qtd: bancaDiss.length, hint: `Banca Dissertacao (${bancaDiss.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 : [];
@@ -162,30 +152,19 @@ const gerarSelos = (consultor) => {
});
}
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;
};
const SELOS_COM_DADOS = [
'PRESID_CAMARA', 'COORD_PPG', 'BPQ',
'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', 'ORIENT_POS_DOC',
'CO_ORIENT_TESE', 'CO_ORIENT_DISS', 'CO_ORIENT_POS_DOC',
'MB_BANCA_POS_DOC', 'MB_BANCA_TESE', 'MB_BANCA_DISS',
'ORIENT_TESE', 'ORIENT_DISS',
'EVENTO', 'PROJ',
'IDIOMA_MULTILINGUE',
'TITULACAO_MESTRE', 'TITULACAO_DOUTOR', 'TITULACAO_POS_DOUTOR',
];
const SelosBadges = ({ selos, compacto = false, onSeloClick }) => {