diff --git a/backend/src/application/dtos/consultor_dto.py b/backend/src/application/dtos/consultor_dto.py index 3f9d664..e2e759c 100644 --- a/backend/src/application/dtos/consultor_dto.py +++ b/backend/src/application/dtos/consultor_dto.py @@ -21,6 +21,20 @@ class CoordenacaoCapesDTO: presidente: bool +@dataclass +class IESDTO: + id: str + nome: str + sigla: Optional[str] = None + + +@dataclass +class VinculoConsultoriaDTO: + periodo: PeriodoDTO + ies: Optional[IESDTO] = None + situacao: str = "" + + @dataclass class ConsultoriaDTO: codigo: str @@ -29,6 +43,11 @@ class ConsultoriaDTO: areas: List[str] anos_consecutivos: int retornos: int + vinculos: List[VinculoConsultoriaDTO] = None + + def __post_init__(self): + if self.vinculos is None: + self.vinculos = [] @dataclass diff --git a/backend/src/application/jobs/processar_ranking.py b/backend/src/application/jobs/processar_ranking.py index cd29a7b..913173c 100644 --- a/backend/src/application/jobs/processar_ranking.py +++ b/backend/src/application/jobs/processar_ranking.py @@ -131,7 +131,21 @@ class ProcessarRankingJob: "fim": consultor.consultoria.periodo.fim.isoformat() if consultor.consultoria.periodo.fim else None, "areas": consultor.consultoria.areas, "anos_consecutivos": consultor.consultoria.anos_consecutivos, - "retornos": consultor.consultoria.retornos + "retornos": consultor.consultoria.retornos, + "vinculos": [ + { + "inicio": v.periodo.inicio.isoformat() if v.periodo.inicio else None, + "fim": v.periodo.fim.isoformat() if v.periodo.fim else None, + "ativo": v.periodo.ativo, + "situacao": v.situacao, + "ies": { + "id": v.ies.id, + "nome": v.ies.nome, + "sigla": v.ies.sigla, + } if v.ies else None, + } + for v in consultor.consultoria.vinculos + ], } if consultor.consultoria else None, "inscricoes": [ { diff --git a/backend/src/application/use_cases/obter_ranking.py b/backend/src/application/use_cases/obter_ranking.py index 2856686..20a2d86 100644 --- a/backend/src/application/use_cases/obter_ranking.py +++ b/backend/src/application/use_cases/obter_ranking.py @@ -8,6 +8,8 @@ from ..dtos.consultor_dto import ( PeriodoDTO, CoordenacaoCapesDTO, ConsultoriaDTO, + IESDTO, + VinculoConsultoriaDTO, InscricaoDTO, AvaliacaoComissaoDTO, PremiacaoDTO, @@ -91,6 +93,23 @@ class ObterRankingUseCase: areas=consultor.consultoria.areas, anos_consecutivos=consultor.consultoria.anos_consecutivos, retornos=consultor.consultoria.retornos, + vinculos=[ + VinculoConsultoriaDTO( + periodo=PeriodoDTO( + inicio=v.periodo.inicio.isoformat() if v.periodo.inicio else "", + fim=v.periodo.fim.isoformat() if v.periodo.fim else None, + ativo=v.periodo.ativo, + anos_decorridos=v.periodo.anos_decorridos, + ), + ies=IESDTO( + id=v.ies.id, + nome=v.ies.nome, + sigla=v.ies.sigla, + ) if v.ies else None, + situacao=v.situacao, + ) + for v in consultor.consultoria.vinculos + ], ) if consultor.consultoria else None, inscricoes=[ InscricaoDTO( diff --git a/backend/src/domain/entities/consultor.py b/backend/src/domain/entities/consultor.py index 9afc453..bc770e5 100644 --- a/backend/src/domain/entities/consultor.py +++ b/backend/src/domain/entities/consultor.py @@ -17,12 +17,27 @@ class CoordenacaoCapes: presidente: bool = False +@dataclass +class IES: + id: str + nome: str + sigla: Optional[str] = None + + +@dataclass +class VinculoConsultoria: + periodo: Periodo + ies: Optional[IES] = None + situacao: str = "" + + @dataclass class Consultoria: codigo: str situacao: str periodo: Periodo periodos: List[Periodo] = field(default_factory=list) + vinculos: List[VinculoConsultoria] = field(default_factory=list) areas: List[str] = field(default_factory=list) anos_consecutivos: int = 0 retornos: int = 0 diff --git a/backend/src/infrastructure/repositories/consultor_repository_impl.py b/backend/src/infrastructure/repositories/consultor_repository_impl.py index 0287a83..4766793 100644 --- a/backend/src/infrastructure/repositories/consultor_repository_impl.py +++ b/backend/src/infrastructure/repositories/consultor_repository_impl.py @@ -8,6 +8,8 @@ from ...domain.entities.consultor import ( Consultor, CoordenacaoCapes, Consultoria, + IES, + VinculoConsultoria, Inscricao, AvaliacaoComissao, Premiacao, @@ -140,6 +142,7 @@ class ConsultorRepositoryImpl(ConsultorRepository): return None periodos: List[Periodo] = [] + vinculos: List[VinculoConsultoria] = [] situacoes: List[str] = [] areas: List[str] = [] @@ -162,9 +165,25 @@ class ConsultorRepositoryImpl(ConsultorRepository): if inicio and fim and fim < inicio: fim = None + + ies_data = dc.get("ies", {}) or {} + ies = None + if ies_data and (ies_data.get("id") or ies_data.get("nome")): + ies = IES( + id=str(ies_data.get("id", "")), + nome=ies_data.get("nome", ""), + sigla=ies_data.get("sigla"), + ) + if inicio: try: - periodos.append(Periodo(inicio=inicio, fim=fim)) + periodo = Periodo(inicio=inicio, fim=fim) + periodos.append(periodo) + vinculos.append(VinculoConsultoria( + periodo=periodo, + ies=ies, + situacao=situacao or "", + )) except ValueError: continue @@ -176,6 +195,8 @@ class ConsultorRepositoryImpl(ConsultorRepository): if not periodos: return None + vinculos.sort(key=lambda v: v.periodo.inicio, reverse=True) + mesclados = mesclar_periodos(periodos) periodo_ativo = next((p for p in mesclados if p.ativo), None) anos_consecutivos = periodo_ativo.anos_completos(datetime.now()) if periodo_ativo else 0 @@ -205,6 +226,7 @@ class ConsultorRepositoryImpl(ConsultorRepository): situacao=situacao_final, periodo=Periodo(inicio=primeiro_evento, fim=fim_final), periodos=mesclados, + vinculos=vinculos, areas=areas, anos_consecutivos=anos_consecutivos, retornos=retornos, diff --git a/backend/src/interface/schemas/consultor_schema.py b/backend/src/interface/schemas/consultor_schema.py index d9fb9bc..bf91590 100644 --- a/backend/src/interface/schemas/consultor_schema.py +++ b/backend/src/interface/schemas/consultor_schema.py @@ -9,6 +9,18 @@ class PeriodoSchema(BaseModel): anos_decorridos: float +class IESSchema(BaseModel): + id: str + nome: str + sigla: Optional[str] = None + + +class VinculoConsultoriaSchema(BaseModel): + periodo: PeriodoSchema + ies: Optional[IESSchema] = None + situacao: str = "" + + class CoordenacaoCapesSchema(BaseModel): codigo: str tipo: str @@ -23,6 +35,7 @@ class ConsultoriaSchema(BaseModel): codigo: str situacao: str periodo: PeriodoSchema + vinculos: List[VinculoConsultoriaSchema] = [] areas: List[str] anos_consecutivos: int retornos: int diff --git a/frontend/src/components/ConsultorCard.css b/frontend/src/components/ConsultorCard.css index 9283dcb..2ed7660 100644 --- a/frontend/src/components/ConsultorCard.css +++ b/frontend/src/components/ConsultorCard.css @@ -458,6 +458,13 @@ min-width: 50px; } +.list-item .ies-nome { + flex: 1; + color: var(--text); + font-weight: 500; + font-size: 0.85rem; +} + @media (max-width: 1200px) { .details-grid { grid-template-columns: repeat(3, 1fr); diff --git a/frontend/src/components/ConsultorCard.jsx b/frontend/src/components/ConsultorCard.jsx index 1c931f4..42a6945 100644 --- a/frontend/src/components/ConsultorCard.jsx +++ b/frontend/src/components/ConsultorCard.jsx @@ -346,11 +346,44 @@ const ConsultorCard = memo(({ consultor, highlight, selecionado, onToggleSelecio )} + {consultoria?.vinculos?.length > 0 && ( +