diff --git a/backend/scripts/popular_componente_b.py b/backend/scripts/popular_componente_b.py old mode 100755 new mode 100644 index c1cfee4..4c3b2d1 --- a/backend/scripts/popular_componente_b.py +++ b/backend/scripts/popular_componente_b.py @@ -1,18 +1,58 @@ #!/usr/bin/env python3 -import sys +""" +Preenche o Componente B (PPG) na TB_RANKING_CONSULTOR usando Oracle remoto. +Usa .env para credenciais e busca em lotes (IN) para reduzir round-trips. +""" import os -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) +import sys +from datetime import datetime +from itertools import islice +from pathlib import Path import cx_Oracle -from datetime import datetime +from dotenv import load_dotenv -ORACLE_LOCAL_USER = "local123" -ORACLE_LOCAL_PASSWORD = "local123" -ORACLE_LOCAL_DSN = "127.0.0.1:1521/XEPDB1" +ROOT_DIR = Path(__file__).resolve().parents[1] +load_dotenv(ROOT_DIR / ".env") + +ORACLE_LOCAL_USER = os.getenv("ORACLE_LOCAL_USER", "local123") +ORACLE_LOCAL_PASSWORD = os.getenv("ORACLE_LOCAL_PASSWORD", "local123") +ORACLE_LOCAL_DSN = os.getenv("ORACLE_LOCAL_DSN", "oracle18c:1521/XEPDB1") + +ORACLE_REMOTE_USER = os.getenv("ORACLE_REMOTE_USER") +ORACLE_REMOTE_PASSWORD = os.getenv("ORACLE_REMOTE_PASSWORD") +ORACLE_REMOTE_DSN = os.getenv("ORACLE_REMOTE_DSN") + +BATCH_IDS = 500 +BATCH_UPDATES = 1000 + +QUERY_BASE = """ + SELECT + c.ID_PESSOA, + c.ID_PROGRAMA_SNPG, + p.NM_PROGRAMA, + p.CD_PROGRAMA_PPG, + p.DS_CONCEITO, + p.NM_PROGRAMA_MODALIDADE, + aa.NM_AREA_AVALIACAO, + c.DT_INICIO_VIGENCIA, + c.DT_FIM_VIGENCIA + FROM SUCUPIRA_PAINEL.VM_COORDENADOR c + INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA + LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO + LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO + WHERE c.ID_PESSOA IN ({placeholders}) +""" + + +def chunked(iterable, size): + it = iter(iterable) + while True: + bloco = list(islice(it, size)) + if not bloco: + break + yield bloco -ORACLE_REMOTE_USER = "FREDERICOAC" -ORACLE_REMOTE_PASSWORD = "FREDEricoac" -ORACLE_REMOTE_DSN = "oracledhtsrv02.hom.capes.gov.br:1521/hom_dr" def calcular_componente_b(coordenacoes): if not coordenacoes: @@ -21,115 +61,124 @@ def calcular_componente_b(coordenacoes): base = 70 anos_totais = 0 for c in coordenacoes: - inicio = c[7] - fim = c[8] - if fim: - anos = (fim - inicio).days // 365 - else: - anos = (datetime.now() - inicio).days // 365 - anos_totais += anos + inicio = c.get("DT_INICIO_VIGENCIA") + fim = c.get("DT_FIM_VIGENCIA") + if not inicio: + continue + if fim and fim < inicio: + fim = None + fim_ref = fim or datetime.now() + anos_totais += (fim_ref - inicio).days // 365 tempo = min(int(anos_totais * 5), 50) - programas_distintos = len(set(c[1] for c in coordenacoes)) + programas_distintos = len({c.get("ID_PROGRAMA_SNPG") for c in coordenacoes}) extras = min((programas_distintos - 1) * 20, 40) if programas_distintos > 1 else 0 maior_nota = 0 for c in coordenacoes: - nota_str = str(c[4]).strip() if c[4] else "" - if nota_str == '7': - maior_nota = max(maior_nota, 7) - elif nota_str == '6': - maior_nota = max(maior_nota, 6) - elif nota_str == '5': - maior_nota = max(maior_nota, 5) - elif nota_str == '4': - maior_nota = max(maior_nota, 4) - elif nota_str == '3': - maior_nota = max(maior_nota, 3) + nota_str = str(c.get("DS_CONCEITO") or "").strip() + if nota_str in ("7", "6", "5", "4", "3"): + maior_nota = max(maior_nota, int(nota_str)) - mapa_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0} - bonus = mapa_nota.get(maior_nota, 0) + bonus_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}.get(maior_nota, 0) - return base + tempo + extras + bonus + return base + tempo + extras + bonus_nota -print("Conectando Oracle LOCAL...") -conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN) -print("Conectando Oracle REMOTO...") -conn_remote = cx_Oracle.connect(ORACLE_REMOTE_USER, ORACLE_REMOTE_PASSWORD, ORACLE_REMOTE_DSN) +def fetch_ppg_por_lote(cursor_remote, ids): + placeholders = ", ".join(f":id{i}" for i in range(len(ids))) + params = {f"id{i}": val for i, val in enumerate(ids)} + cursor_remote.execute(QUERY_BASE.format(placeholders=placeholders), params) + cols = [col[0] for col in cursor_remote.description] + return [dict(zip(cols, row)) for row in cursor_remote.fetchall()] -cursor_local = conn_local.cursor() -cursor_remote = conn_remote.cursor() -print("Buscando consultores...") -cursor_local.execute("SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR") -ids_pessoas = [row[0] for row in cursor_local.fetchall()] -print(f"Total: {len(ids_pessoas)} consultores") +def main(): + if not ORACLE_REMOTE_USER or not ORACLE_REMOTE_PASSWORD or not ORACLE_REMOTE_DSN: + print("ERRO: credenciais do Oracle REMOTO não encontradas (.env).") + sys.exit(1) -processados = 0 -com_ppg = 0 -batch_updates = [] + print("Conectando Oracle LOCAL (ranking)...") + conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN) + cursor_local = conn_local.cursor() -for id_pessoa in ids_pessoas: - try: - cursor_remote.execute(""" - SELECT - c.ID_PESSOA, - c.ID_PROGRAMA_SNPG, - p.NM_PROGRAMA, - p.CD_PROGRAMA_PPG, - p.DS_CONCEITO, - p.NM_PROGRAMA_MODALIDADE, - aa.NM_AREA_AVALIACAO, - c.DT_INICIO_VIGENCIA, - c.DT_FIM_VIGENCIA - FROM SUCUPIRA_PAINEL.VM_COORDENADOR c - INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA - LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO - LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO - WHERE c.ID_PESSOA = :id_pessoa - """, {"id_pessoa": id_pessoa}) + print("Conectando Oracle REMOTO (SUCUPIRA_PAINEL)...") + conn_remote = cx_Oracle.connect(ORACLE_REMOTE_USER, ORACLE_REMOTE_PASSWORD, ORACLE_REMOTE_DSN) + cursor_remote = conn_remote.cursor() + cursor_remote.arraysize = 2000 - coordenacoes = cursor_remote.fetchall() + print("Buscando IDs a atualizar (COMPONENTE_B = 0)...") + cursor_local.execute("SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR WHERE NVL(COMPONENTE_B,0) = 0") + ids_pessoas = [row[0] for row in cursor_local.fetchall()] + total_ids = len(ids_pessoas) + print(f"Total: {total_ids} consultores pendentes") + + processados = 0 + com_ppg = 0 + batch_updates = [] + + for lote_ids in chunked(ids_pessoas, BATCH_IDS): + try: + registros = fetch_ppg_por_lote(cursor_remote, lote_ids) + except Exception as e: + print(f"Erro ao buscar PPG para lote (IDs {lote_ids[:3]}...): {e}") + continue + + coord_por_pessoa = {} + for r in registros: + coord_por_pessoa.setdefault(r["ID_PESSOA"], []).append(r) + + for id_pessoa in lote_ids: + coordenacoes = coord_por_pessoa.get(id_pessoa, []) + if not coordenacoes: + processados += 1 + continue - if coordenacoes: comp_b = calcular_componente_b(coordenacoes) - batch_updates.append((comp_b, id_pessoa)) + batch_updates.append({"comp_b": comp_b, "id_pessoa": id_pessoa}) com_ppg += 1 + processados += 1 - processados += 1 + if len(batch_updates) >= BATCH_UPDATES: + cursor_local.executemany( + """ + UPDATE TB_RANKING_CONSULTOR + SET COMPONENTE_B = :comp_b, + PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D + WHERE ID_PESSOA = :id_pessoa + """, + batch_updates, + ) + conn_local.commit() + print(f"Processados: {processados}/{total_ids} | Com PPG: {com_ppg}") + batch_updates = [] - if len(batch_updates) >= 1000: - cursor_local.executemany( - "UPDATE TB_RANKING_CONSULTOR SET COMPONENTE_B = :comp_b, PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D WHERE ID_PESSOA = :id_pessoa", - batch_updates - ) - conn_local.commit() - print(f"Processados: {processados}/{len(ids_pessoas)} | Com PPG: {com_ppg}") - batch_updates = [] + if batch_updates: + cursor_local.executemany( + """ + UPDATE TB_RANKING_CONSULTOR + SET COMPONENTE_B = :comp_b, + PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D + WHERE ID_PESSOA = :id_pessoa + """, + batch_updates, + ) + conn_local.commit() - except Exception as e: - print(f"Erro no consultor {id_pessoa}: {e}") - -if batch_updates: - cursor_local.executemany( - "UPDATE TB_RANKING_CONSULTOR SET COMPONENTE_B = :comp_b, PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D WHERE ID_PESSOA = :id_pessoa", - batch_updates - ) + print("\nAtualizando posições...") + cursor_local.callproc("SP_ATUALIZAR_POSICOES") conn_local.commit() -print(f"\nFinalizado!") -print(f"Total processados: {processados}") -print(f"Com PPG: {com_ppg}") + print("\nFinalizado!") + print(f"Total processados: {processados}") + print(f"Com PPG: {com_ppg}") -print("\nAtualizando posições...") -cursor_local.callproc("SP_ATUALIZAR_POSICOES") -conn_local.commit() + cursor_local.close() + cursor_remote.close() + conn_local.close() + conn_remote.close() -cursor_local.close() -cursor_remote.close() -conn_local.close() -conn_remote.close() -print("Concluído!") +if __name__ == "__main__": + main() diff --git a/backend/src/application/jobs/popular_componente_b_job.py b/backend/src/application/jobs/popular_componente_b_job.py new file mode 100644 index 0000000..0ab5a9a --- /dev/null +++ b/backend/src/application/jobs/popular_componente_b_job.py @@ -0,0 +1,159 @@ +from datetime import datetime +from itertools import islice +from typing import Dict, List, Optional + +from ...infrastructure.oracle.client import OracleClient + + +class PopularComponenteBJob: + """ + Preenche COMPONENTE_B na TB_RANKING_CONSULTOR usando PPGs do Oracle remoto. + Usa lotes (IN) para reduzir round-trips. + """ + + def __init__(self, oracle_local_client: OracleClient, oracle_remote_client: OracleClient): + self.oracle_local = oracle_local_client + self.oracle_remote = oracle_remote_client + + @staticmethod + def _chunked(iterable, size): + it = iter(iterable) + while True: + bloco = list(islice(it, size)) + if not bloco: + break + yield bloco + + @staticmethod + def _calcular_componente_b(coordenacoes: List[Dict]) -> int: + if not coordenacoes: + return 0 + + base = 70 + anos_totais = 0 + for c in coordenacoes: + inicio = c.get("DT_INICIO_VIGENCIA") + fim = c.get("DT_FIM_VIGENCIA") + if not inicio: + continue + if fim and fim < inicio: + fim = None + fim_ref = fim or datetime.now() + anos_totais += (fim_ref - inicio).days // 365 + + tempo = min(int(anos_totais * 5), 50) + + programas_distintos = len({c.get("ID_PROGRAMA_SNPG") for c in coordenacoes}) + extras = min((programas_distintos - 1) * 20, 40) if programas_distintos > 1 else 0 + + maior_nota = 0 + for c in coordenacoes: + nota_str = str(c.get("DS_CONCEITO") or "").strip() + if nota_str in ("7", "6", "5", "4", "3"): + maior_nota = max(maior_nota, int(nota_str)) + + bonus_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}.get(maior_nota, 0) + + return base + tempo + extras + bonus_nota + + def _buscar_ids_pendentes(self) -> List[int]: + query = "SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR WHERE NVL(COMPONENTE_B,0) = 0" + resultados = self.oracle_local.executar_query(query) + return [int(r["ID_PESSOA"]) for r in resultados] if resultados else [] + + def _buscar_ppg_lote(self, ids: List[int]) -> List[Dict]: + placeholders = ", ".join(f":id{i}" for i in range(len(ids))) + params = {f"id{i}": val for i, val in enumerate(ids)} + query = f""" + SELECT + c.ID_PESSOA, + c.ID_PROGRAMA_SNPG, + p.NM_PROGRAMA, + p.CD_PROGRAMA_PPG, + p.DS_CONCEITO, + p.NM_PROGRAMA_MODALIDADE, + aa.NM_AREA_AVALIACAO, + c.DT_INICIO_VIGENCIA, + c.DT_FIM_VIGENCIA + FROM SUCUPIRA_PAINEL.VM_COORDENADOR c + INNER JOIN SUCUPIRA_PAINEL.VM_PROGRAMA_SUCUPIRA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA + LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO + LEFT JOIN SUCUPIRA_PAINEL.VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO + WHERE c.ID_PESSOA IN ({placeholders}) + """ + return self.oracle_remote.executar_query(query, params) + + def executar(self, batch_ids: int = 500, batch_updates: int = 1000) -> None: + """ + Executa a rotina de preenchimento do Componente B. + Este método é síncrono; use asyncio.to_thread quando chamá-lo em corrotina. + """ + if not self.oracle_local.is_connected: + print("PopularComponenteB: Oracle LOCAL não conectado, abortando.") + return + if not self.oracle_remote.is_connected: + print("PopularComponenteB: Oracle REMOTO não conectado, abortando.") + return + + ids_pessoas = self._buscar_ids_pendentes() + total_ids = len(ids_pessoas) + print(f"PopularComponenteB: {total_ids} consultores pendentes para COMPONENTE_B") + + processados = 0 + com_ppg = 0 + batch = [] + + for lote in self._chunked(ids_pessoas, batch_ids): + try: + registros = self._buscar_ppg_lote(lote) + except Exception as e: + print(f"PopularComponenteB: erro ao buscar lote {lote[:3]}... -> {e}") + continue + + por_pessoa: Dict[int, List[Dict]] = {} + for r in registros: + por_pessoa.setdefault(int(r["ID_PESSOA"]), []).append(r) + + for id_pessoa in lote: + coordenacoes = por_pessoa.get(id_pessoa, []) + if not coordenacoes: + processados += 1 + continue + + comp_b = self._calcular_componente_b(coordenacoes) + batch.append({"comp_b": comp_b, "id_pessoa": id_pessoa}) + com_ppg += 1 + processados += 1 + + if len(batch) >= batch_updates: + self._aplicar_batch(batch) + print(f"PopularComponenteB: Processados {processados}/{total_ids} | Com PPG: {com_ppg}") + batch = [] + + if batch: + self._aplicar_batch(batch) + + self._atualizar_posicoes() + print(f"PopularComponenteB: Finalizado. Processados={processados} Com PPG={com_ppg}") + + def _aplicar_batch(self, batch: List[Dict[str, int]]) -> None: + if not batch: + return + update_sql = """ + UPDATE TB_RANKING_CONSULTOR + SET COMPONENTE_B = :comp_b, + PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D + WHERE ID_PESSOA = :id_pessoa + """ + with self.oracle_local.get_connection() as conn: + cursor = conn.cursor() + cursor.executemany(update_sql, batch) + conn.commit() + cursor.close() + + def _atualizar_posicoes(self) -> None: + with self.oracle_local.get_connection() as conn: + cursor = conn.cursor() + cursor.callproc("SP_ATUALIZAR_POSICOES") + conn.commit() + cursor.close() diff --git a/backend/src/application/jobs/scheduler.py b/backend/src/application/jobs/scheduler.py index ec69885..e352e8e 100644 --- a/backend/src/application/jobs/scheduler.py +++ b/backend/src/application/jobs/scheduler.py @@ -3,11 +3,13 @@ from datetime import datetime, time, timedelta from typing import Optional from .processar_ranking import ProcessarRankingJob +from .popular_componente_b_job import PopularComponenteBJob class RankingScheduler: - def __init__(self, job: ProcessarRankingJob): + def __init__(self, job: ProcessarRankingJob, job_componente_b: PopularComponenteBJob | None = None): self.job = job + self.job_componente_b = job_componente_b self.task: Optional[asyncio.Task] = None self.running = False @@ -40,6 +42,10 @@ class RankingScheduler: print(f"[{datetime.now().strftime('%d/%m/%Y %H:%M:%S')}] Executando job de ranking automático") await self.job.executar(limpar_antes=True) + if self.job_componente_b: + print(f"[{datetime.now().strftime('%d/%m/%Y %H:%M:%S')}] Executando popular_componente_b após ranking") + await asyncio.to_thread(self.job_componente_b.executar) + except asyncio.CancelledError: print("Scheduler cancelado") break diff --git a/backend/src/interface/api/app.py b/backend/src/interface/api/app.py index c8bb22b..ffb6d46 100644 --- a/backend/src/interface/api/app.py +++ b/backend/src/interface/api/app.py @@ -4,7 +4,13 @@ from contextlib import asynccontextmanager from .routes import router from .config import settings -from .dependencies import es_client, oracle_local_client, oracle_remote_client, get_processar_job +from .dependencies import ( + es_client, + oracle_local_client, + oracle_remote_client, + get_processar_job, + get_popular_componente_b_job, +) from ...application.jobs.scheduler import RankingScheduler @@ -29,7 +35,8 @@ async def lifespan(app: FastAPI): scheduler = None try: job = get_processar_job() - scheduler = RankingScheduler(job) + job_b = get_popular_componente_b_job() + scheduler = RankingScheduler(job, job_componente_b=job_b) await scheduler.iniciar() except Exception as e: print(f"AVISO: Scheduler não iniciou: {e}") diff --git a/backend/src/interface/api/dependencies.py b/backend/src/interface/api/dependencies.py index ab21c1e..a43158f 100644 --- a/backend/src/interface/api/dependencies.py +++ b/backend/src/interface/api/dependencies.py @@ -3,6 +3,7 @@ from ...infrastructure.oracle.client import OracleClient from ...infrastructure.oracle.ranking_repository import RankingOracleRepository from ...infrastructure.repositories.consultor_repository_impl import ConsultorRepositoryImpl from ...application.jobs.processar_ranking import ProcessarRankingJob +from ...application.jobs.popular_componente_b_job import PopularComponenteBJob from .config import settings @@ -30,6 +31,7 @@ oracle_remote_client = OracleClient( _repository: ConsultorRepositoryImpl = None _ranking_repository: RankingOracleRepository = None _processar_job: ProcessarRankingJob = None +_popular_b_job: PopularComponenteBJob = None def get_repository() -> ConsultorRepositoryImpl: @@ -56,3 +58,13 @@ def get_processar_job() -> ProcessarRankingJob: ranking_repo=get_ranking_repository() ) return _processar_job + + +def get_popular_componente_b_job() -> PopularComponenteBJob: + global _popular_b_job + if _popular_b_job is None: + _popular_b_job = PopularComponenteBJob( + oracle_local_client=oracle_local_client, + oracle_remote_client=oracle_remote_client + ) + return _popular_b_job diff --git a/backend/src/interface/api/routes.py b/backend/src/interface/api/routes.py index 0e74270..5d4bc97 100644 --- a/backend/src/interface/api/routes.py +++ b/backend/src/interface/api/routes.py @@ -20,6 +20,7 @@ from ..schemas.ranking_schema import ( ) from .dependencies import get_repository, get_ranking_repository, get_processar_job from ...application.jobs.job_status import job_status +import json router = APIRouter(prefix="/api/v1", tags=["ranking"]) @@ -102,19 +103,7 @@ async def ranking_paginado( total_pages = (total + size - 1) // size consultores_schema = [ - ConsultorRankingResumoSchema( - id_pessoa=c.id_pessoa, - nome=c.nome, - posicao=c.posicao, - pontuacao_total=c.pontuacao_total, - componente_a=c.componente_a, - componente_b=c.componente_b, - componente_c=c.componente_c, - componente_d=c.componente_d, - ativo=c.ativo, - anos_atuacao=c.anos_atuacao - ) - for c in consultores + _consultor_resumo_from_ranking(c) for c in consultores ] return RankingPaginadoResponseSchema( @@ -126,6 +115,29 @@ async def ranking_paginado( ) +def _consultor_resumo_from_ranking(c): + consultoria = None + try: + jd = json.loads(c.json_detalhes) if c.json_detalhes else {} + consultoria = jd.get("consultoria") if isinstance(jd, dict) else None + except Exception: + consultoria = None + + return ConsultorRankingResumoSchema( + id_pessoa=c.id_pessoa, + nome=c.nome, + posicao=c.posicao, + pontuacao_total=c.pontuacao_total, + componente_a=c.componente_a, + componente_b=c.componente_b, + componente_c=c.componente_c, + componente_d=c.componente_d, + ativo=c.ativo, + anos_atuacao=c.anos_atuacao, + consultoria=consultoria, + ) + + @router.get("/ranking/estatisticas", response_model=EstatisticasRankingSchema) async def ranking_estatisticas( ranking_repo = Depends(get_ranking_repository), diff --git a/backend/src/interface/schemas/ranking_schema.py b/backend/src/interface/schemas/ranking_schema.py index 559c9f9..a2b72f8 100644 --- a/backend/src/interface/schemas/ranking_schema.py +++ b/backend/src/interface/schemas/ranking_schema.py @@ -14,6 +14,7 @@ class ConsultorRankingResumoSchema(BaseModel): componente_d: float ativo: bool anos_atuacao: float + consultoria: Optional[dict] = None class RankingPaginadoResponseSchema(BaseModel): diff --git a/frontend/src/App.css b/frontend/src/App.css index c562dea..abd08dd 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -46,36 +46,6 @@ background: var(--accent-2); } -.mode-selector { - display: flex; - gap: 0.5rem; - margin: 1.5rem 0; - justify-content: center; -} - -.mode-selector button { - padding: 0.75rem 1.5rem; - background: rgba(255,255,255,0.06); - border: 1px solid var(--stroke); - border-radius: 8px; - color: var(--muted); - font-size: 0.95rem; - font-weight: 500; - cursor: pointer; - transition: all 200ms; -} - -.mode-selector button:hover { - border-color: var(--accent-2); - color: var(--text); -} - -.mode-selector button.active { - background: var(--accent); - border-color: var(--accent); - color: white; -} - .controls { margin: 1.5rem 0; display: flex; diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 12d7b67..4549c6e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,7 +1,6 @@ import { useState, useEffect } from 'react'; import Header from './components/Header'; import ConsultorCard from './components/ConsultorCard'; -import RankingPaginado from './components/RankingPaginado'; import { rankingService } from './services/api'; import './App.css'; @@ -10,8 +9,7 @@ function App() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [total, setTotal] = useState(0); - const [limite, setLimite] = useState(10); - const [modo, setModo] = useState('completo'); + const [limite, setLimite] = useState(100); useEffect(() => { loadRanking(); @@ -56,45 +54,24 @@ function App() {