feat(frontend): melhorias no ConsultorCard
- Adicionar função corrigir_encoding para fix de caracteres Latin1 - Melhorias visuais no CSS do card de consultor
This commit is contained in:
@@ -205,6 +205,14 @@
|
|||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-premiado {
|
||||||
|
background: linear-gradient(120deg, #fbbf24, #f59e0b);
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 0.15rem 0.35rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.card-stats {
|
.card-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -580,36 +588,36 @@
|
|||||||
|
|
||||||
.selos-container.selos-compacto {
|
.selos-container.selos-compacto {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.5rem;
|
||||||
gap: 0.2rem;
|
gap: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selos-compacto .selo {
|
.selos-compacto .selo {
|
||||||
background: rgba(148, 163, 184, 0.12);
|
background: rgba(148, 163, 184, 0.15);
|
||||||
border-color: rgba(148, 163, 184, 0.25);
|
border-color: rgba(148, 163, 184, 0.3);
|
||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
padding: 0.1rem 0.25rem;
|
padding: 0.25rem 0.45rem;
|
||||||
gap: 0.15rem;
|
gap: 0.25rem;
|
||||||
font-size: 0.6rem;
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selos-compacto .selo:hover {
|
.selos-compacto .selo:hover {
|
||||||
background: rgba(148, 163, 184, 0.2);
|
background: rgba(148, 163, 184, 0.25);
|
||||||
border-color: rgba(148, 163, 184, 0.4);
|
border-color: rgba(148, 163, 184, 0.5);
|
||||||
color: #cbd5e1;
|
color: #cbd5e1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selos-compacto .selo-icone {
|
.selos-compacto .selo-icone {
|
||||||
filter: grayscale(100%) brightness(1.2);
|
filter: grayscale(100%) brightness(1.2);
|
||||||
opacity: 0.85;
|
opacity: 0.9;
|
||||||
font-size: 0.65rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selos-compacto .selo-qtd {
|
.selos-compacto .selo-qtd {
|
||||||
background: rgba(148, 163, 184, 0.25);
|
background: rgba(148, 163, 184, 0.3);
|
||||||
color: #cbd5e1;
|
color: #cbd5e1;
|
||||||
font-size: 0.5rem;
|
font-size: 0.6rem;
|
||||||
padding: 0.05rem 0.2rem;
|
padding: 0.1rem 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selo {
|
.selo {
|
||||||
@@ -927,3 +935,140 @@
|
|||||||
font-size: 0.55rem;
|
font-size: 0.55rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tipo-clicavel,
|
||||||
|
.selo-clicavel {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-clicavel:hover,
|
||||||
|
.selo-clicavel:hover {
|
||||||
|
transform: translateY(-2px) scale(1.05);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal {
|
||||||
|
background: linear-gradient(155deg, rgba(30, 41, 59, 0.98), rgba(15, 23, 42, 0.98));
|
||||||
|
border: 1px solid var(--stroke);
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
|
||||||
|
max-width: 600px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
border-bottom: 1px solid var(--stroke);
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-header .tipo-atuacao {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 0.35rem 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-header .tipo-icone {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-close {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border: 1px solid var(--stroke);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 1rem;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-close:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.2);
|
||||||
|
border-color: rgba(239, 68, 68, 0.4);
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-body {
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--stroke);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item-main {
|
||||||
|
flex: 1;
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-summary {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: rgba(79, 70, 229, 0.1);
|
||||||
|
border: 1px solid rgba(79, 70, 229, 0.25);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-summary strong {
|
||||||
|
color: var(--accent-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-empty {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--muted);
|
||||||
|
padding: 2rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipo-modal-body h5 {
|
||||||
|
color: var(--accent-2);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 1rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
import React, { useState, useRef, useEffect, useMemo, memo } from 'react';
|
import React, { useState, useRef, useEffect, useMemo, memo } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
import './ConsultorCard.css';
|
import './ConsultorCard.css';
|
||||||
import RawDataModal from './RawDataModal';
|
import RawDataModal from './RawDataModal';
|
||||||
import { rankingService } from '../services/api';
|
import { rankingService } from '../services/api';
|
||||||
|
|
||||||
const SELOS = {
|
const SELOS = {
|
||||||
PRESID_CAMARA: { label: 'Presidente Camara', cor: 'selo-camara', icone: '👑' },
|
PRESID_CAMARA: { codigo: 'PRESID_CAMARA', label: 'Presidente Camara', cor: 'selo-camara', icone: '👑' },
|
||||||
COORD_PPG: { label: 'Coord. PPG', cor: 'selo-coord', icone: '🎓' },
|
COORD_PPG: { codigo: 'COORD_PPG', label: 'Coord. PPG', cor: 'selo-coord', icone: '🎓' },
|
||||||
BPQ: { label: 'BPQ', cor: 'selo-bpq', icone: '🏅' },
|
BPQ: { codigo: 'BPQ', label: 'BPQ', cor: 'selo-bpq', icone: '🏅' },
|
||||||
AUTOR_GP: { label: 'Autor GP', cor: 'selo-gp', icone: '🏆' },
|
AUTOR_GP: { codigo: 'AUTOR_GP', label: 'Autor GP', cor: 'selo-gp', icone: '🏆' },
|
||||||
AUTOR_PREMIO: { label: 'Autor Premio', cor: 'selo-premio', icone: '🥇' },
|
AUTOR_PREMIO: { codigo: 'AUTOR_PREMIO', label: 'Autor Premio', cor: 'selo-premio', icone: '🥇' },
|
||||||
AUTOR_MENCAO: { label: 'Autor Mencao', cor: 'selo-mencao', icone: '🥈' },
|
AUTOR_MENCAO: { codigo: 'AUTOR_MENCAO', label: 'Autor Mencao', cor: 'selo-mencao', icone: '🥈' },
|
||||||
ORIENT_GP: { label: 'Orient. GP', cor: 'selo-gp', icone: '🏆' },
|
ORIENT_GP: { codigo: 'ORIENT_GP', label: 'Orient. GP', cor: 'selo-gp', icone: '🏆' },
|
||||||
ORIENT_PREMIO: { label: 'Orient. Premio', cor: 'selo-orient-premio', icone: '🎖️' },
|
ORIENT_PREMIO: { codigo: 'ORIENT_PREMIO', label: 'Orient. Premio', cor: 'selo-orient-premio', icone: '🎖️' },
|
||||||
ORIENT_MENCAO: { label: 'Orient. Mencao', cor: 'selo-orient-mencao', icone: '📜' },
|
ORIENT_MENCAO: { codigo: 'ORIENT_MENCAO', label: 'Orient. Mencao', cor: 'selo-orient-mencao', icone: '📜' },
|
||||||
COORIENT_GP: { label: 'Coorient. GP', cor: 'selo-gp', icone: '🏆' },
|
COORIENT_GP: { codigo: 'COORIENT_GP', label: 'Coorient. GP', cor: 'selo-gp', icone: '🏆' },
|
||||||
COORIENT_PREMIO: { label: 'Coorient. Premio', cor: 'selo-coorient-premio', icone: '🎖️' },
|
COORIENT_PREMIO: { codigo: 'COORIENT_PREMIO', label: 'Coorient. Premio', cor: 'selo-coorient-premio', icone: '🎖️' },
|
||||||
COORIENT_MENCAO: { label: 'Coorient. Mencao', cor: 'selo-coorient-mencao', icone: '📜' },
|
COORIENT_MENCAO: { codigo: 'COORIENT_MENCAO', label: 'Coorient. Mencao', cor: 'selo-coorient-mencao', icone: '📜' },
|
||||||
ORIENT_TESE: { label: 'Orient. Tese', cor: 'selo-orient', icone: '📚' },
|
ORIENT_TESE: { codigo: 'ORIENT_TESE', label: 'Orient. Tese', cor: 'selo-orient', icone: '📚' },
|
||||||
ORIENT_DISS: { label: 'Orient. Diss.', cor: 'selo-orient', icone: '📄' },
|
ORIENT_DISS: { codigo: 'ORIENT_DISS', label: 'Orient. Diss.', cor: 'selo-orient', icone: '📄' },
|
||||||
ORIENT_POS_DOC: { label: 'Orient. Pos-Doc', cor: 'selo-orient', icone: '🔬' },
|
ORIENT_POS_DOC: { codigo: 'ORIENT_POS_DOC', label: 'Orient. Pos-Doc', cor: 'selo-orient', icone: '🔬' },
|
||||||
CO_ORIENT_TESE: { label: 'Coorient. Tese', cor: 'selo-coorient', icone: '📚' },
|
CO_ORIENT_TESE: { codigo: 'CO_ORIENT_TESE', label: 'Coorient. Tese', cor: 'selo-coorient', icone: '📚' },
|
||||||
CO_ORIENT_DISS: { label: 'Coorient. Diss.', cor: 'selo-coorient', icone: '📄' },
|
CO_ORIENT_DISS: { codigo: 'CO_ORIENT_DISS', label: 'Coorient. Diss.', cor: 'selo-coorient', icone: '📄' },
|
||||||
CO_ORIENT_POS_DOC: { label: 'Coorient. Pos-Doc', cor: 'selo-coorient', icone: '🔬' },
|
CO_ORIENT_POS_DOC: { codigo: 'CO_ORIENT_POS_DOC', label: 'Coorient. Pos-Doc', cor: 'selo-coorient', icone: '🔬' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const TIPOS_ATUACAO_CONFIG = {
|
const TIPOS_ATUACAO_CONFIG = {
|
||||||
@@ -100,25 +101,47 @@ const gerarSelos = (consultor) => {
|
|||||||
return selos;
|
return selos;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelosBadges = ({ selos, compacto = false }) => {
|
const SELOS_COM_DADOS = [
|
||||||
|
'PRESID_CAMARA', 'COORD_PPG', 'BPQ',
|
||||||
|
'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'
|
||||||
|
];
|
||||||
|
|
||||||
|
const SelosBadges = ({ selos, compacto = false, onSeloClick }) => {
|
||||||
if (!selos || selos.length === 0) return null;
|
if (!selos || selos.length === 0) return null;
|
||||||
|
|
||||||
const selosExibidos = compacto ? selos.slice(0, 4) : selos;
|
const selosExibidos = compacto ? selos.slice(0, 4) : selos;
|
||||||
const selosOcultos = compacto && selos.length > 4 ? selos.length - 4 : 0;
|
const selosOcultos = compacto && selos.length > 4 ? selos.length - 4 : 0;
|
||||||
|
|
||||||
|
const handleClick = (e, selo) => {
|
||||||
|
if (onSeloClick && SELOS_COM_DADOS.includes(selo.codigo)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onSeloClick(selo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`selos-container ${compacto ? 'selos-compacto' : ''}`}>
|
<div className={`selos-container ${compacto ? 'selos-compacto' : ''}`}>
|
||||||
{selosExibidos.map((selo, idx) => (
|
{selosExibidos.map((selo, idx) => {
|
||||||
|
const temDados = SELOS_COM_DADOS.includes(selo.codigo);
|
||||||
|
return (
|
||||||
<span
|
<span
|
||||||
key={idx}
|
key={idx}
|
||||||
className={`selo ${selo.cor}`}
|
className={`selo ${selo.cor} ${onSeloClick && temDados ? 'selo-clicavel' : ''}`}
|
||||||
title={selo.hint || `${selo.label}${selo.qtd > 1 ? ` (${selo.qtd}x)` : ''}`}
|
title={temDados ? `Clique para ver detalhes` : (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>
|
<span className="selo-icone">{selo.icone}</span>
|
||||||
{!compacto && <span className="selo-label">{selo.label}</span>}
|
{!compacto && <span className="selo-label">{selo.label}</span>}
|
||||||
{!compacto && <span className="selo-qtd">{selo.qtd || 1}</span>}
|
{!compacto && <span className="selo-qtd">{selo.qtd || 1}</span>}
|
||||||
</span>
|
</span>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
{selosOcultos > 0 && (
|
{selosOcultos > 0 && (
|
||||||
<span className="selo selo-mais" title={`+${selosOcultos} selos`}>+{selosOcultos}</span>
|
<span className="selo selo-mais" title={`+${selosOcultos} selos`}>+{selosOcultos}</span>
|
||||||
)}
|
)}
|
||||||
@@ -126,18 +149,32 @@ const SelosBadges = ({ selos, compacto = false }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TiposAtuacaoBadges = ({ tipos, exibirTodos = false }) => {
|
const TiposAtuacaoBadges = ({ tipos, exibirTodos = false, onBadgeClick, consultor }) => {
|
||||||
if (!tipos || tipos.length === 0) return null;
|
if (!tipos || tipos.length === 0) return null;
|
||||||
|
|
||||||
const tiposExibidos = exibirTodos ? tipos : tipos.slice(0, 4);
|
const tiposExibidos = exibirTodos ? tipos : tipos.slice(0, 4);
|
||||||
const tiposOcultos = !exibirTodos && tipos.length > 4 ? tipos.length - 4 : 0;
|
const tiposOcultos = !exibirTodos && tipos.length > 4 ? tipos.length - 4 : 0;
|
||||||
|
|
||||||
|
const handleClick = (e, tipo) => {
|
||||||
|
if (onBadgeClick) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onBadgeClick(tipo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`tipos-atuacao-container ${exibirTodos ? 'tipos-expandido' : ''}`}>
|
<div className={`tipos-atuacao-container ${exibirTodos ? 'tipos-expandido' : ''}`}>
|
||||||
{tiposExibidos.map((tipo, idx) => {
|
{tiposExibidos.map((tipo, idx) => {
|
||||||
const config = TIPOS_ATUACAO_CONFIG[tipo] || { cor: 'tipo-default', icone: '📌' };
|
const config = TIPOS_ATUACAO_CONFIG[tipo] || { cor: 'tipo-default', icone: '📌' };
|
||||||
return (
|
return (
|
||||||
<span key={idx} className={`tipo-atuacao ${config.cor}`} title={tipo}>
|
<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-icone">{config.icone}</span>
|
||||||
<span className="tipo-label">{tipo}</span>
|
<span className="tipo-label">{tipo}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -152,6 +189,368 @@ const TiposAtuacaoBadges = ({ tipos, exibirTodos = false }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 FORMULAS = {
|
const FORMULAS = {
|
||||||
bloco_a: {
|
bloco_a: {
|
||||||
titulo: 'Coordenacao CAPES',
|
titulo: 'Coordenacao CAPES',
|
||||||
@@ -222,6 +621,8 @@ const ScoreItemWithTooltip = ({ value, label, formula, style }) => (
|
|||||||
const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecionado }) => {
|
const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecionado }) => {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [showRawModal, setShowRawModal] = useState(false);
|
const [showRawModal, setShowRawModal] = useState(false);
|
||||||
|
const [tipoAtuacaoModal, setTipoAtuacaoModal] = useState(null);
|
||||||
|
const [seloModal, setSeloModal] = useState(null);
|
||||||
const cardRef = useRef(null);
|
const cardRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -354,7 +755,7 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<div className="card-details">
|
<div className="card-details" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="details-grid">
|
<div className="details-grid">
|
||||||
<div className="detail-section">
|
<div className="detail-section">
|
||||||
<h4>Pontuacao Total</h4>
|
<h4>Pontuacao Total</h4>
|
||||||
@@ -413,14 +814,19 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
{consultor.tipos_atuacao?.length > 0 && (
|
{consultor.tipos_atuacao?.length > 0 && (
|
||||||
<div className="detail-section tipos-section">
|
<div className="detail-section tipos-section">
|
||||||
<h4>Tipos de Atuacao</h4>
|
<h4>Tipos de Atuacao</h4>
|
||||||
<TiposAtuacaoBadges tipos={consultor.tipos_atuacao} exibirTodos={true} />
|
<TiposAtuacaoBadges
|
||||||
|
tipos={consultor.tipos_atuacao}
|
||||||
|
exibirTodos={true}
|
||||||
|
onBadgeClick={setTipoAtuacaoModal}
|
||||||
|
consultor={consultor}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selos.length > 0 && (
|
{selos.length > 0 && (
|
||||||
<div className="detail-section selos-section">
|
<div className="detail-section selos-section">
|
||||||
<h4>Selos e Reconhecimentos</h4>
|
<h4>Selos e Reconhecimentos</h4>
|
||||||
<SelosBadges selos={selos} />
|
<SelosBadges selos={selos} onSeloClick={setSeloModal} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -550,6 +956,49 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
</div>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -560,6 +1009,22 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio
|
|||||||
onClose={() => setShowRawModal(false)}
|
onClose={() => setShowRawModal(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{tipoAtuacaoModal && (
|
||||||
|
<TipoAtuacaoModal
|
||||||
|
tipo={tipoAtuacaoModal}
|
||||||
|
consultor={consultor}
|
||||||
|
onClose={() => setTipoAtuacaoModal(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{seloModal && (
|
||||||
|
<SeloModal
|
||||||
|
selo={seloModal}
|
||||||
|
consultor={consultor}
|
||||||
|
onClose={() => setSeloModal(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user