From ce4fc52d4ab234dc5c25703d8945a3f79dccebc4 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Sat, 4 Oct 2025 20:41:23 -0300 Subject: [PATCH] refactor: Migra todos los widgets nacionales a CSS Modules para encapsular estilos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Esta refactorización modifica la forma en que los widgets manejan sus estilos para prevenir conflictos con los CSS de los sitios anfitriones donde se incrustan. Se ha migrado el sistema de estilos de CSS global a CSS Modules para todos los componentes principales y sus hijos, asegurando que todas las clases sean únicas y estén aisladas. Cambios principales: - Se actualizan los componentes .tsx para importar y usar los módulos de estilos (`import styles from ...`). - Se renombran los archivos `.css` a `.module.css`. - Se añade una regla en cada módulo para proteger la `font-family` y el `box-sizing` del widget, evitando que sean sobreescritos por estilos externos. - Se ajustan los selectores para librerías de terceros (react-select, react-simple-maps) usando `:global()` para mantener la compatibilidad. - Se mueven las variables CSS de `:root` a las clases principales de cada widget para evitar colisiones en el scope global. Como resultado, los widgets (`HomeCarouselWidget`, `PanelNacionalWidget`, `ResultadosNacionalesCardsWidget`, `CongresoNacionalWidget`) son ahora más robustos, portátiles y visualmente consistentes en cualquier entorno. --- .../legislativas/DevAppLegislativas.tsx | 2 +- .../CongresoNacionalWidget.module.css | 282 ++++++ .../nacionales/CongresoNacionalWidget.tsx | 50 +- .../nacionales/HomeCarouselWidget.css | 240 ----- .../nacionales/HomeCarouselWidget.module.css | 309 ++++++ .../nacionales/HomeCarouselWidget.tsx | 124 +-- .../legislativas/nacionales/PanelNacional.css | 920 ------------------ .../nacionales/PanelNacional.module.css | 539 ++++++++++ .../nacionales/PanelNacionalWidget.tsx | 87 +- ...esultadosNacionalesCardsWidget.module.css} | 142 ++- .../ResultadosNacionalesCardsWidget.tsx | 14 +- .../nacionales/components/Breadcrumbs.tsx | 23 +- .../nacionales/components/CabaLupa.tsx | 8 +- .../nacionales/components/MapaNacional.tsx | 30 +- .../nacionales/components/MapaProvincial.tsx | 10 +- .../nacionales/components/MiniMapaSvg.tsx | 31 +- .../nacionales/components/MunicipioSearch.tsx | 30 +- .../nacionales/components/PanelResultados.tsx | 39 +- .../components/PanelResultadosSkeleton.tsx | 25 +- .../nacionales/components/ProvinciaCard.tsx | 48 +- .../net9.0/Elecciones.Api.AssemblyInfo.cs | 2 +- 21 files changed, 1476 insertions(+), 1479 deletions(-) create mode 100644 Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.module.css delete mode 100644 Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.css create mode 100644 Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.module.css delete mode 100644 Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.css create mode 100644 Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.module.css rename Elecciones-Web/frontend/src/features/legislativas/nacionales/{ResultadosNacionalesCardsWidget.css => ResultadosNacionalesCardsWidget.module.css} (61%) diff --git a/Elecciones-Web/frontend/src/features/legislativas/DevAppLegislativas.tsx b/Elecciones-Web/frontend/src/features/legislativas/DevAppLegislativas.tsx index 1c07354..2bbe9dd 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/DevAppLegislativas.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/DevAppLegislativas.tsx @@ -27,7 +27,7 @@ export const DevAppLegislativas = () => { const sectionStyle = { border: '2px solid #007bff', borderRadius: '8px', - padding: '1rem 2rem', + padding: '2px', marginTop: '3rem', marginBottom: '3rem', backgroundColor: '#f8f9fa' diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.module.css b/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.module.css new file mode 100644 index 0000000..07c6f50 --- /dev/null +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.module.css @@ -0,0 +1,282 @@ +/* src/features/legislativas/nacionales/CongresoNacionalWidget.module.css */ + +/* --- SOLUCIÓN PARA FUENTES Y ESTILOS GLOBALES --- */ +.congresoContainer, +.congresoContainer * { + font-family: "Public Sans", system-ui, sans-serif !important; + box-sizing: border-box; +} + +.congresoContainer { + display: flex; + gap: 1.5rem; + background-color: #ffffff; + border: 1px solid #e0e0e0; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); + padding: 1rem; + border-radius: 8px; + max-width: 900px; + margin: 20px auto; + color: #333333; + --primary-accent-color: #007bff; /* Movida aquí desde :root */ +} + +.congresoGrafico { + flex: 2; + min-width: 300px; + display: flex; + flex-direction: column; +} + +.congresoHemicicloWrapper { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + width: 100%; +} + +.congresoHemicicloWrapper.isHovering :global(.party-block:not(:hover)) { + opacity: 0.3; +} + +.congresoGrafico svg { + width: 100%; + height: auto; + animation: fadeIn 0.8s ease-in-out; +} + +@keyframes fadeIn { + from { opacity: 0; transform: scale(0.9); } + to { opacity: 1; transform: scale(1); } +} + +.congresoFooter { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 0.5rem 0 0.5rem; + margin-top: auto; + font-size: 0.8em; + color: #666; + border-top: 1px solid #eee; +} + +.footerLegend { + display: flex; + gap: 1.25rem; + align-items: center; +} + +.footerLegendItem { + display: flex; + align-items: center; + gap: 0.6rem; + font-size: 1.1em; +} + +.legendIcon { + display: inline-block; + width: 14px; + height: 14px; + border-radius: 50%; + box-sizing: border-box; +} + +.legendIconSolid { + background-color: #888; + border: 1px solid #777; +} + +.legendIconRing { + background-color: rgba(136, 136, 136, 0.3); + border: 1px solid #888; +} + +.footerTimestamp { + font-weight: 500; + font-size: 0.75em; +} + +.congresoSummary { + flex: 1; + border-left: 1px solid #e0e0e0; + padding-left: 1.25rem; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.congresoSummary h3 { + margin-top: 0; + margin-bottom: 0.75rem; + font-size: 1.4em; + color: #212529; +} + +.chamberTabs { + display: flex; + margin-bottom: 1rem; + border: 1px solid #dee2e6; + border-radius: 6px; + overflow: hidden; +} + +.chamberTabs button { + flex: 1; + padding: 0.5rem 0.5rem; + border: none; + background-color: #f8f9fa; + color: #6c757d; + font-family: inherit; + font-size: 1em; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease-in-out; +} + +.chamberTabs button:first-child { + border-right: 1px solid #dee2e6; +} + +.chamberTabs button:hover { + background-color: #e9ecef; +} + +.chamberTabs button.active { + background-color: var(--primary-accent-color); + color: #ffffff; +} + +.summaryMetric { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 0.25rem; + font-size: 1.1em; +} + +.summaryMetric strong { + font-size: 1.5em; + font-weight: 700; + color: var(--primary-accent-color); +} + +.congresoSummary hr { + border: none; + border-top: 1px solid #e0e0e0; + margin: 1rem 0; +} + +.partidoListaContainer { + flex-grow: 1; + overflow-y: auto; + min-height: 0; + padding-right: 8px; +} + +.partidoLista { + list-style: none; + padding: 0; + margin: 0; +} + +.partidoLista li { + display: flex; + align-items: center; + margin-bottom: 0.85rem; +} + +.partidoColorBox { + width: 16px; + height: 16px; + border-radius: 4px; + margin-right: 12px; + flex-shrink: 0; +} + +.partidoNombre { + flex-grow: 1; +} + +.partidoBancas { + font-weight: 700; + font-size: 1.1em; +} + +@media (max-width: 768px) { + .congresoContainer { + flex-direction: column; + padding: 0.5rem; + height: auto; + max-height: none; + } + + .congresoSummary { + border-left: none; + padding-left: 0; + border-top: 1px solid #e0e0e0; + } + + .partidoListaContainer { + overflow-y: visible; + max-height: none; + } + + .congresoFooter { + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 0rem; + } + + .footerLegend { + gap: 0.75rem; + } + + .footerLegendItem{ + font-size: 1em; + } + + .footerTimestamp { + font-size: 0.75em; + } +} + +@media (min-width: 769px) { + .congresoContainer { + flex-direction: row; + align-items: stretch; + height: 500px; + } +} + +/* --- ESTILOS PARA TOOLTIP --- */ +/* Usamos :global() para apuntar a clases e IDs generados por la librería react-tooltip */ +:global(.seat-tooltip) { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + padding: 8px; + background-color: white; +} +:global(.seat-tooltip img) { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: cover; + border: 2px solid #ccc; +} +:global(.seat-tooltip p) { + margin: 0; + font-size: 12px; + font-weight: bold; + color: #333; +} + +:global(#seat-tooltip.react-tooltip) { + opacity: 1 !important; + background-color: white; +} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.tsx index 514af51..a8a5da3 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/CongresoNacionalWidget.tsx @@ -5,7 +5,8 @@ import { Tooltip } from 'react-tooltip'; import { DiputadosNacionalesLayout } from '../../../components/common/DiputadosNacionalesLayout'; import { SenadoresNacionalesLayout } from '../../../components/common/SenadoresNacionalesLayout'; import { getComposicionNacional, type ComposicionNacionalData, type PartidoComposicionNacional } from '../../../apiService'; -import '../provinciales/CongresoWidget.css'; +// 1. La importación de CSS ahora se hace como un módulo +import styles from './CongresoNacionalWidget.module.css'; interface CongresoNacionalWidgetProps { eleccionId: number; @@ -71,11 +72,12 @@ const WidgetContent = ({ eleccionId }: CongresoNacionalWidgetProps) => { return adjustedPartyData; }, [partidosOrdenados, datosCamaraActual.presidenteBancada, camaraActiva]); + // 2. Todas las props 'className' ahora usan el objeto 'styles' return ( -
-
+
+
setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} > @@ -92,53 +94,51 @@ const WidgetContent = ({ eleccionId }: CongresoNacionalWidgetProps) => { /> }
-
-
-
- {/* Usamos la nueva clase CSS para el círculo sólido */} - +
+
+
+ Bancas en juego
-
- {/* Reemplazamos el SVG por un span con la nueva clase para el anillo */} - +
+ Bancas previas
-
+
Última Actualización: {formatTimestamp(datosCamaraActual.ultimaActualizacion)}
-
-
- -

{datosCamaraActual.camaraNombre}

-
+
Total de Bancas {datosCamaraActual.totalBancas}
-
+
Bancas en Juego {datosCamaraActual.bancasEnJuego}

-
-
    +
    +
      {partidosOrdenados .filter(p => p.bancasTotales > 0) .map((partido: PartidoComposicionNacional) => (
    • - - {partido.nombreCorto || partido.nombre} + + {partido.nombreCorto || partido.nombre} {partido.bancasTotales} @@ -155,7 +155,7 @@ const WidgetContent = ({ eleccionId }: CongresoNacionalWidgetProps) => { export const CongresoNacionalWidget = ({ eleccionId }: CongresoNacionalWidgetProps) => { return ( - Cargando composición del congreso...
    }> + Cargando composición del congreso...
}> ); diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.css b/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.css deleted file mode 100644 index 9e79dfa..0000000 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.css +++ /dev/null @@ -1,240 +0,0 @@ -/* src/features/legislativas/nacionales/HomeCarouselWidget.css */ - -.home-carousel-widget { - --primary-text: #212529; - --secondary-text: #6c757d; - --border-color: #dee2e6; - --background-light: #f8f9fa; - --background-white: #ffffff; - --shadow: 0 2px 8px rgba(0, 0, 0, 0.07); - --font-family-sans: "Roboto", system-ui, sans-serif; -} - -.home-carousel-widget { - font-family: var(--font-family-sans); - background-color: var(--background-white); - border: 1px solid var(--border-color); - border-radius: 8px; - padding: 0.75rem; - max-width: 1200px; - margin: 2rem auto; -} - -.widget-title { - font-size: 1.2rem; - font-weight: 900; - color: var(--primary-text); - margin: 0 0 0.5rem 0; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border-color); - text-align: left; -} - -.top-stats-bar { - display: flex; - justify-content: space-around; - background-color: transparent; - border: 1px solid var(--border-color); - border-radius: 8px; - padding: 0.3rem 0.5rem; - margin-bottom: 0.5rem; -} - -.top-stats-bar > div { - display: flex; - align-items: baseline; - gap: 0.5rem; - border-right: 1px solid var(--border-color); - padding: 0 0.5rem; - flex-grow: 1; - justify-content: center; -} -.top-stats-bar > div:last-child { border-right: none; } -.top-stats-bar span { font-size: 0.9rem; color: var(--secondary-text); } -.top-stats-bar strong { font-size: 0.9rem; font-weight: 600; color: var(--primary-text); } - -.candidate-card { - display: flex; - align-items: center; - gap: 0.75rem; - background: var(--background-white); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 0.75rem; - box-shadow: var(--shadow); - height: 100%; - border-left: 5px solid; - border-left-color: var(--candidate-color, #ccc); - position: relative; -} - -.candidate-photo-wrapper { - flex-shrink: 0; - width: 60px; - height: 60px; - border-radius: 8px; - overflow: hidden; - background-color: var(--candidate-color, #e9ecef); -} - -.candidate-photo { - width: 100%; - height: 100%; - object-fit: cover; - box-sizing: border-box; -} - -.candidate-details { - flex-grow: 1; - display: flex; - justify-content: space-between; - align-items: center; - min-width: 0; -} - -.candidate-info { - display: flex; - flex-direction: column; - justify-content: center; - align-items:flex-start; - gap: 0.1rem; - min-width: 0; - margin-right: 0.75rem; -} - -.candidate-name, .party-name { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: block; - width: 100%; - text-transform: uppercase; -} -.candidate-name { - font-size: 0.95rem; - text-align: left; - font-weight: 700; - color: var(--primary-text); -} -.party-name { - font-size: 0.8rem; - text-align: left; - text-transform: uppercase; - color: var(--secondary-text); - text-transform: uppercase; -} - -.candidate-results { text-align: right; flex-shrink: 0; } -.percentage { - display: block; - font-size: 1.2rem; - font-weight: 700; - color: var(--primary-text); - line-height: 1.1; -} -.votes { - font-size: 0.75rem; - color: var(--secondary-text); - white-space: nowrap; -} - -.swiper-slide:not(:last-child) .candidate-card::after { - content: ''; - position: absolute; - right: -8px; - top: 20%; - bottom: 20%; - width: 1px; - background-color: var(--border-color); -} - -.swiper-button-prev, .swiper-button-next { - width: 30px; height: 30px; - background-color: rgba(255, 255, 255, 0.9); - border: 1px solid var(--border-color); - border-radius: 50%; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); - transition: opacity 0.2s; - color: var(--secondary-text); -} -.swiper-button-prev:after, .swiper-button-next:after { - font-size: 18px; - font-weight: bold; -} -.swiper-button-prev { left: -10px; } -.swiper-button-next { right: -10px; } -.swiper-button-disabled { opacity: 0; pointer-events: none; } - -.widget-footer { - text-align: right; - font-size: 0.75rem; - color: var(--secondary-text); - margin-top: 0.5rem; -} - -.short-text { - display: none; /* Oculto por defecto en la vista de escritorio */ -} - -/* --- INICIO DE LA SECCIÓN DE ESTILOS PARA MÓVIL --- */ -@media (max-width: 768px) { - .home-carousel-widget { - padding: 0.75rem; - } - - /* 1. Centrar el título en móvil */ - .widget-title { - text-align: center; - font-size: 1.1rem; - } - - /* 2. Reestructurar la barra de estadísticas a 2x2 y usar textos cortos */ - .top-stats-bar { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.2rem; - padding: 0.3rem; - } - - .top-stats-bar > div { - padding: 0.25rem 0.5rem; - border-right: none; /* Quitar todos los bordes derechos */ - } - - .top-stats-bar > div:nth-child(odd) { - border-right: 1px solid var(--border-color); /* Restablecer borde solo para la columna izquierda */ - } - - /* Lógica de visibilidad de textos */ - .long-text { - display: none; /* Ocultar el texto largo en móvil */ - } - .short-text { - display:inline; /* Mostrar el texto corto en móvil */ - } - - /* Reducir fuentes para que quepan mejor */ - .top-stats-bar span { font-size: 0.8rem; text-align: left; } - .top-stats-bar strong { font-size: 0.85rem; text-align: right;} - - /* --- Botones del Carrusel (sin cambios) --- */ - .swiper-button-prev, .swiper-button-next { - width: 32px; - height: 32px; - top: 45%; - } - .swiper-button-prev { left: 2px; } - .swiper-button-next { right: 2px; } - - /* --- Ajustes en la tarjeta (sin cambios) --- */ - .candidate-card { gap: 0.5rem; padding: 0.5rem; } - .candidate-photo-wrapper { width: 50px; height: 50px; } - .candidate-name { font-size: 0.9rem; } - .percentage { font-size: 1.1rem; } - .votes { font-size: 0.7rem; } - - /* 3. Centrar el footer en móvil */ - .widget-footer { - text-align: center; - } -} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.module.css b/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.module.css new file mode 100644 index 0000000..71615fd --- /dev/null +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.module.css @@ -0,0 +1,309 @@ +/* src/features/legislativas/nacionales/HomeCarouselWidget.module.css */ + +.homeCarouselWidget { + --primary-text: #212529; + --secondary-text: #6c757d; + --border-color: #dee2e6; + --background-light: #f8f9fa; + --background-white: #ffffff; + --shadow: 0 2px 8px rgba(0, 0, 0, 0.07); + --font-family-sans: "Roboto", system-ui, sans-serif; +} + +.homeCarouselWidget, +.homeCarouselWidget * { + font-family: var(--font-family-sans) !important; + box-sizing: border-box; +} + +.homeCarouselWidget { + background-color: var(--background-white); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 0.75rem; + max-width: 1200px; + margin: 2rem auto; + position: relative; +} + +.carouselContainer { + position: relative; +} + +.widgetTitle { + font-size: 1.2rem; + font-weight: 900; + color: var(--primary-text); + margin: 0 0 0.5rem 0; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border-color); + text-align: left; +} + +.topStatsBar { + display: flex; + justify-content: space-around; + background-color: transparent; + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 0.3rem 0.5rem; + margin-bottom: 0.5rem; +} + +.topStatsBar > div { + display: flex; + align-items: baseline; + gap: 0.5rem; + border-right: 1px solid var(--border-color); + padding: 0 0.5rem; + flex-grow: 1; + justify-content: center; +} +.topStatsBar > div:last-child { border-right: none; } +.topStatsBar span { font-size: 0.9rem; color: var(--secondary-text); } +.topStatsBar strong { font-size: 0.9rem; font-weight: 600; color: var(--primary-text); } + +.candidateCard { + display: flex; + align-items: center; + gap: 0.75rem; + background: var(--background-white); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 0.75rem; + box-shadow: var(--shadow); + border-left: 5px solid; + border-left-color: var(--candidate-color, #ccc); + position: relative; +} + +.candidatePhotoWrapper { + flex-shrink: 0; + width: 60px; + height: 60px; + border-radius: 8px; + overflow: hidden; + background-color: var(--candidate-color, #e9ecef); +} + +.candidatePhoto { + width: 100% !important; + height: 100% !important; + object-fit: cover; + box-sizing: border-box; +} + +.candidateDetails { + flex-grow: 1; + display: flex; + justify-content: space-between; + align-items: center; + min-width: 0; +} + +.candidateInfo { + display: flex; + flex-direction: column; + justify-content: center; + align-items:flex-start; + gap: 0.1rem; + min-width: 0; + margin-right: 0.75rem; +} + +.candidateName, .partyName { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + width: 100%; + margin: 0; + color: var(--primary-text); + text-align: left; +} +.candidateName { + font-size: 0.95rem; + font-weight: 700; +} +.partyName { + font-size: 0.8rem; + text-transform: uppercase; + color: var(--secondary-text); + font-weight: 400; +} + +.candidateResults { text-align: right; flex-shrink: 0; } +.percentage { + display: block; + font-size: 1.2rem; + font-weight: 700; + color: var(--primary-text); + line-height: 1.1; +} +.votes { + font-size: 0.75rem; + color: var(--secondary-text); + white-space: nowrap; +} + +/* Estilo base para ambos botones */ +.navButton { + width: 30px; + height: 30px; + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid var(--border-color); + border-radius: 50%; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); + transition: opacity 0.2s; + position: absolute; + top: 50%; + transform: translateY(-50%); + margin-top: 0; + z-index: 10; + cursor: pointer; +} + +/* Usamos el pseudo-elemento ::after para mostrar el icono SVG como fondo */ +.navButton::after { + content: ''; /* Es necesario para que el pseudo-elemento se muestre */ + display: block; + width: 100%; + height: 100%; + background-repeat: no-repeat; + background-position: center; + /* Ajustamos el tamaño del icono dentro del botón */ + background-size: 75%; +} + +/* Posición y contenido específico para cada botón */ +.navButtonPrev { + left: 10px; +} +.navButtonPrev::after { + /* SVG de flecha izquierda (chevron) codificado en Base64 */ + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSIxNSA2IDkgMTIgMTUgMTgiPjwvcG9seWxpbmU+PC9zdmc+"); +} + +.navButtonNext { + right: 10px; +} +.navButtonNext::after { + /* SVG de flecha derecha (chevron) codificado en Base64 */ + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSI5IDYgMTUgMTIgOSAxOCI+PC9wb2x5bGluZT48L3N2Zz4="); +} + +/* Swiper añade esta clase al botón cuando está deshabilitado */ +.navButton.swiper-button-disabled { + opacity: 0; + pointer-events: none; +} + +.homeCarouselWidget :global(.swiper-slide) { + background: transparent !important; + color: initial !important; + text-align: left !important; + height: auto !important; +} + +.homeCarouselWidget :global(.swiper-slide:not(:last-child)) .candidateCard::after { + content: ''; + position: absolute; + right: -8px; + top: 20%; + bottom: 20%; + width: 1px; + background-color: var(--border-color); +} + +/* --- INICIO DE LA MODIFICACIÓN DE FLECHAS --- */ + +.homeCarouselWidget :global(.swiper-button-prev), +.homeCarouselWidget :global(.swiper-button-next) { + width: 30px !important; + height: 30px !important; + background-color: rgba(255, 255, 255, 0.9) !important; + border: 1px solid var(--border-color); + border-radius: 50%; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); + transition: opacity 0.2s; + position: absolute !important; + top: 50% !important; + transform: translateY(-50%) !important; + margin-top: 0 !important; + z-index: 10; +} + +.homeCarouselWidget :global(.swiper-button-prev:after), +.homeCarouselWidget :global(.swiper-button-next:after) { + display: block !important; + font-family: 'swiper-icons'; + font-size: 14px !important; + font-weight: bold !important; + color: var(--primary-text) !important; +} + +.homeCarouselWidget :global(.swiper-button-prev) { left: 10px !important; } +.homeCarouselWidget :global(.swiper-button-next) { right: 10px !important; } + +.homeCarouselWidget :global(.swiper-button-disabled) { + opacity: 0 !important; + pointer-events: none !important; +} + +.widgetFooter { + text-align: right; + font-size: 0.75rem; + color: var(--secondary-text); + margin-top: 0.5rem; +} + +.shortText { + display: none; +} + +@media (max-width: 768px) { + .homeCarouselWidget .widgetTitle { text-align: center; font-size: 1.1rem; } + .homeCarouselWidget .topStatsBar { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.2rem; padding: 0.3rem; } + .homeCarouselWidget .topStatsBar > div { padding: 0.25rem 0.5rem; border-right: none; } + .homeCarouselWidget .topStatsBar > div:nth-child(odd) { border-right: 1px solid var(--border-color); } + .homeCarouselWidget .longText { display: none; } + .homeCarouselWidget .shortText { display:inline; } + .homeCarouselWidget .topStatsBar span { font-size: 0.8rem; text-align: left; } + .homeCarouselWidget .topStatsBar strong { font-size: 0.85rem; text-align: right; } + + /* Ajustamos los botones custom en mobile */ + .navButton { + width: 32px; + height: 32px; + } + .navButton::after { + line-height: 32px; + } + .navButtonPrev { left: 5px; } + .navButtonNext { right: 5px; } + + .homeCarouselWidget .candidateCard { gap: 0.5rem; padding: 0.5rem; } + .homeCarouselWidget .candidatePhotoWrapper { width: 50px; height: 50px; } + .homeCarouselWidget .candidateName { font-size: 0.9rem; } + .homeCarouselWidget .percentage { font-size: 1.1rem; } + .homeCarouselWidget .votes { font-size: 0.7rem; } + .homeCarouselWidget .widgetFooter { text-align: center; } +} + +/* Mantenemos estos estilos globales por si acaso */ +.homeCarouselWidget :global(.swiper-slide) { + background: transparent !important; + color: initial !important; + text-align: left !important; + height: auto !important; +} + +.homeCarouselWidget :global(.swiper-slide:not(:last-child)) .candidateCard::after { + content: ''; + position: absolute; + right: -8px; + top: 20%; + bottom: 20%; + width: 1px; + background-color: var(--border-color); +} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.tsx index 10ecee8..4d9967d 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/HomeCarouselWidget.tsx @@ -10,7 +10,7 @@ import { Navigation, A11y } from 'swiper/modules'; import 'swiper/css'; // @ts-ignore import 'swiper/css/navigation'; -import './HomeCarouselWidget.css'; +import styles from './HomeCarouselWidget.module.css'; interface Props { eleccionId: number; @@ -22,14 +22,12 @@ interface Props { const formatPercent = (num: number | null | undefined) => `${(num || 0).toFixed(2).replace('.', ',')}%`; const formatNumber = (num: number) => num.toLocaleString('es-AR'); -// --- Lógica de formateo de fecha --- const formatDateTime = (dateString: string | undefined | null) => { if (!dateString) return '...'; try { const date = new Date(dateString); - // Verificar si la fecha es válida if (isNaN(date.getTime())) { - return dateString; // Si no se puede parsear, devolver el string original + return dateString; } const day = String(date.getDate()).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0'); @@ -38,7 +36,7 @@ const formatDateTime = (dateString: string | undefined | null) => { const minutes = String(date.getMinutes()).padStart(2, '0'); return `${day}/${month}/${year}, ${hours}:${minutes} hs.`; } catch (e) { - return dateString; // En caso de cualquier error, devolver el string original + return dateString; } }; @@ -52,82 +50,94 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo if (error || !data) return
No se pudieron cargar los datos.
; return ( -
-

{titulo}

+
+

{titulo}

-
+
Participación {formatPercent(data.estadoRecuento?.participacionPorcentaje)}
- Mesas escrutadas - Escrutado + Mesas escrutadas + Escrutado {formatPercent(data.estadoRecuento?.mesasTotalizadasPorcentaje)}
- Votos en blanco - En blanco + Votos en blanco + En blanco {formatPercent(data.votosEnBlancoPorcentaje)}
- Votos totales - Votos + Votos totales + Votos {formatNumber(data.votosTotales)}
- - {data.resultados.map(candidato => ( - -
+
+ + {data.resultados.map(candidato => ( + +
-
- -
+
+ +
-
-
- {candidato.nombreCandidato ? ( - // CASO 1: Hay un candidato (se muestran dos líneas) - <> - - {candidato.nombreCandidato} - - +
+
+ {candidato.nombreCandidato ? ( + <> + + {candidato.nombreCandidato} + + + {candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion} + + + ) : ( + {candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion} - - ) : ( - // CASO 2: No hay candidato (se muestra solo una línea) - - {candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion} - - )} -
-
- {formatPercent(candidato.porcentaje)} - {formatNumber(candidato.votos)} votos + )} +
+
+ {formatPercent(candidato.porcentaje)} + {formatNumber(candidato.votos)} votos +
+
+ + ))} + -
- - ))} - +
+
+
-
+
Última actualización: {formatDateTime(data.ultimaActualizacion)}
diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.css b/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.css deleted file mode 100644 index 52bfafe..0000000 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.css +++ /dev/null @@ -1,920 +0,0 @@ -/* src/features/legislativas/nacionales/PanelNacional.css */ -.panel-nacional-container { - font-family: 'Roboto', sans-serif; - max-width: 1200px; - margin: auto; - border: 1px solid #e0e0e0; - border-radius: 8px; - position: relative; - padding: 10px; -} - -.panel-header { - padding: 1rem 1.5rem; - border-bottom: 1px solid #e0e0e0; - position: relative; - z-index: 20; - background-color: white; -} - -/* Contenedor para alinear título y selector */ -.header-top-row { - display: flex; - justify-content: flex-start; - /* Alinea los items al inicio */ - align-items: center; - gap: 2rem; - /* Añade un espacio de separación de 2rem entre el selector y el breadcrumb */ -} - -.categoria-selector { - min-width: 220px; -} - -/* El contenedor principal del selector (la parte visible antes de hacer clic) */ -.categoria-selector__control { - border-radius: 8px !important; - border: 1px solid #e0e0e0 !important; - box-shadow: none !important; - transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; -} - -/* Estilo cuando el selector está enfocado (seleccionado) */ -.categoria-selector__control--is-focused { - border-color: #007bff !important; - box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important; -} - -/* El texto del valor seleccionado */ -.categoria-selector__single-value { - font-weight: 500; - color: #333; -} - -/* El menú desplegable que contiene las opciones */ -.categoria-selector__menu { - border-radius: 8px !important; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important; - border: 1px solid #e0e0e0 !important; - margin-top: 4px !important; - /* Pequeño espacio entre el control y el menú */ -} - -/* Cada una de las opciones en la lista */ -.categoria-selector__option { - cursor: pointer; - transition: background-color 0.2s, color 0.2s; -} - -/* Estilo de una opción cuando pasas el mouse por encima (estado 'focused') */ -.categoria-selector__option--is-focused { - background-color: #f0f8ff; - /* Un azul muy claro */ - color: #333; -} - -/* Estilo de la opción que está actualmente seleccionada */ -.categoria-selector__option--is-selected { - background-color: #007bff; - color: white; -} - -/* La pequeña línea vertical que separa el contenido del indicador (la flecha) */ -.categoria-selector__indicator-separator { - display: none; - /* La ocultamos para un look más limpio */ -} - -/* El indicador (la flecha hacia abajo) */ -.categoria-selector__indicator { - color: #a0a0a0; - transition: color 0.2s; -} - -.categoria-selector__indicator:hover { - color: #333; -} - -/* --- ESTILOS MODERNOS PARA BREADCRUMBS --- */ - -.breadcrumbs-container { - display: flex; - align-items: center; - gap: 0.5rem; - /* Espacio entre elementos */ - font-size: 1rem; -} - -.breadcrumb-item, -.breadcrumb-item-actual { - display: flex; - align-items: center; - padding: 0.4rem 0.8rem; - border-radius: 8px; - /* Bordes redondeados para efecto píldora */ - transition: background-color 0.2s ease-in-out; -} - -.breadcrumb-item { - background-color: #f0f0f0; - border: 1px solid #e0e0e0; - color: #333; - cursor: pointer; - font-weight: 500; -} - -.breadcrumb-item:hover { - background-color: #e0e0e0; - border-color: #d1d1d1; -} - -.breadcrumb-item-actual { - background-color: transparent; - color: #000; - font-weight: 700; - /* Más peso para el nivel actual */ -} - -.breadcrumb-icon { - margin-right: 0.4rem; - font-size: 1rem; -} - -.breadcrumb-separator { - color: #a0a0a0; - /* Color sutil para el separador */ - font-size: 1.2rem; -} - -.panel-main-content { - display: flex; - height: 75vh; - min-height: 500px; - transition: all 0.5s ease-in-out; -} - -.mapa-column { - flex: 2; - position: relative; - transition: flex 0.5s ease-in-out; -} - -.resultados-column { - flex: 1; - overflow-y: auto; - padding: 1.25rem; - transition: all 0.5s ease-in-out; - min-width: 320px; -} - -/* --- AJUSTES EN FILAS DE PARTIDOS --- */ -.partido-fila { - display: flex; - align-items: center; - gap: 0.75rem; - /* ANTES: 1rem */ - padding: 0.75rem 0; - /* ANTES: 1rem 0 */ - border-bottom: 1px solid #f0f0f0; - border-left: 5px solid; - border-radius: 12px; - padding-left: 1rem; -} - -.partido-logo { - flex-shrink: 0; - width: 65px; - height: 65px; - border-radius: 12px; - box-sizing: border-box; -} - -.partido-logo img { - width: 100%; - height: 100%; - border-radius: 12px; -} - -.partido-main-content { - flex-grow: 1; - display: grid; - grid-template-columns: 1fr auto; - grid-template-rows: auto auto; - align-items: center; - gap: 0.25rem 0.75rem; - /* ANTES: gap: 0.25rem 1rem */ -} - -.partido-top-row { - display: contents; -} - -.partido-info-wrapper { - min-width: 0; - text-align: left; -} - -.partido-nombre { - font-weight: 700; - font-size: 1rem; - color: #212529; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1.2; - text-transform: uppercase; -} - -.partido-nombre-normal { - font-size: 1rem; - color: #212529; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1.2; - text-transform: uppercase; -} - -.candidato-nombre { - font-size: 0.75rem; - /* ANTES: 0.8rem */ - color: #6c757d; - text-transform: uppercase; - font-weight: 500; - line-height: 1.1; -} - -.partido-stats { - flex-shrink: 0; - text-align: right; - padding-left: 1rem; -} - -.partido-porcentaje { - font-size: 1.35rem; - /* ANTES: 1.5rem */ - font-weight: 700; - display: block; -} - -.partido-votos { - font-size: 0.9rem; - /* ANTES: 1rem */ - color: #666; - display: block; -} - -.partido-barra-background { - height: 12px; - /* ANTES: 20px */ - background-color: #f0f0f0; - border-radius: 4px; - grid-column: 1 / 3; -} - -.partido-barra-foreground { - height: 100%; - border-radius: 4px; - transition: width 0.5s ease-in-out; -} - -/* --- AJUSTES EN CÍRCULOS DE ESTADÍSTICAS --- */ -.panel-estado-recuento { - display: flex; - justify-content: space-around; - padding-bottom: 1rem; - /* ANTES: 1.5rem */ - margin-bottom: 1rem; - /* ANTES: 1.5rem */ - border-bottom: 1px solid #e0e0e0; -} - -.estado-item { - width: 95px; - /* ANTES: 100px */ - text-align: center; -} - -.estado-item span { - margin-top: 0.5rem; - font-size: 0.85rem; - /* ANTES: 0.9rem */ - color: #666; - display: block; -} - -/* --- MAPA Y ELEMENTOS ASOCIADOS --- */ -.mapa-componente-container { - width: 100%; - height: 100%; - position: relative; - overflow: hidden; -} - -.mapa-render-area { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.mapa-volver-btn { - position: absolute; - top: 10px; - left: 10px; - z-index: 10; - padding: 8px 12px; - background-color: white; - border: 1px solid #ccc; - border-radius: 4px; - cursor: pointer; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.rsm-zoomable-group { - transition: transform 0.75s ease-in-out; -} - -/* Desactivar la transición durante el arrastre */ -.rsm-zoomable-group.panning { - transition: none; -} - -.panel-main-content.panel-collapsed .mapa-column { - flex: 1 1 100%; -} - -.panel-main-content.panel-collapsed .resultados-column { - flex-basis: 0; - min-width: 0; - max-width: 0; - padding: 0; - overflow: hidden; -} - -.panel-toggle-btn { - position: absolute; - top: 50%; - right: 10px; - transform: translateY(-50%); - z-index: 10; - width: 30px; - height: 50px; - border: 1px solid #ccc; - background-color: white; - border-radius: 4px 0 0 4px; - cursor: pointer; - font-size: 1.3rem; - font-weight: bold; - color: #555; - display: flex; - align-items: center; - justify-content: center; - box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); - transition: background-color 0.2s; -} - -.panel-toggle-btn:hover { - background-color: #f0f0f0; -} - -.rsm-geography { - stroke: #000000; - stroke-width: 0.25px; - outline: none; - transition: filter 0.2s ease-in-out; -} - -.rsm-geography:not(.selected):hover { - filter: brightness(1.25); - /* Mantenemos el brillo */ - stroke: #ffffff; - /* Color del borde a blanco */ - stroke-width: 0.25px; - paint-order: stroke; - /* Asegura que el borde se dibuje encima del relleno */ -} - -.rsm-geography.selected { - stroke: #000000; - stroke-width: 0.25px; - filter: none; - pointer-events: none; -} - -.rsm-geography-faded, -.rsm-geography-faded-municipality { - opacity: 0.5; - pointer-events: none; -} - -.caba-comuna-geography { - stroke: #000000; - stroke-width: 0.05px; -} - -.caba-comuna-geography:not(.selected):hover { - stroke: #000000; - stroke-width: 0.055px; - filter: brightness(1.25); -} - -.transition-spinner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(255, 255, 255, 0.5); - z-index: 20; - display: flex; - align-items: center; - justify-content: center; -} - -.transition-spinner::after { - content: ''; - width: 50px; - height: 50px; - border: 5px solid rgba(0, 0, 0, 0.2); - border-top-color: #007bff; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.caba-magnifier-container { - position: absolute; - height: auto; - transform: translate(-50%, -50%); - pointer-events: none; -} - -.caba-lupa-svg { - width: 100%; - height: auto; - pointer-events: none; -} - -.caba-lupa-interactive-area { - pointer-events: all; - cursor: pointer; - filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.25)); - transition: transform 0.2s ease-in-out; -} - -.caba-lupa-interactive-area:hover { - filter: brightness(1.15); - stroke: #ffffff; - stroke-width: 0.25px; -} - -.skeleton-fila div { - background: #f6f7f8; - background-image: linear-gradient(to right, #f6f7f8 0%, #edeef1 20%, #f6f7f8 40%, #f6f7f8 100%); - background-repeat: no-repeat; - background-size: 800px 104px; - animation: shimmer 1s linear infinite; - border-radius: 4px; -} - -.skeleton-logo { - width: 65px; - height: 65px; -} - -.skeleton-text { - height: 1em; -} - -.skeleton-bar { - height: 20px; - margin-top: 4px; -} - -/* --- ESTILOS PARA LOS BOTONES DE ZOOM DEL MAPA --- */ -.zoom-controls-container { - position: absolute; - top: 5px; - right: 10px; - z-index: 30; - display: flex; - flex-direction: column; - gap: 5px; -} - -.zoom-btn { - width: 40px; - height: 40px; - background-color: white; - border: 1px solid #ccc; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.2rem; - transition: background-color 0.2s; -} - -.zoom-icon-wrapper { - display: flex; - align-items: center; - justify-content: center; -} - -.zoom-icon-wrapper svg { - width: 20px; - height: 20px; - color: #333; -} - -.zoom-btn.disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.zoom-btn:hover { - background-color: #f0f0f0; -} - -/* --- ESTILOS DE CURSOR PARA EL ARRASTRE DEL MAPA --- */ -.map-locked .rsm-geography { - cursor: pointer; -} - -.map-pannable .rsm-geography { - cursor: grab; -} - -.header-bottom-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 1rem; - gap: 1rem; -} - -.municipio-search-container { - min-width: 280px; - /* Ancho mínimo para el buscador en desktop */ -} - -/* --- MEDIA QUERY PARA RESPONSIVE (REFACTORIZADA) --- */ -@media (max-width: 800px) { - - .panel-nacional-container { - display: flex; - flex-direction: column; - height: 100vh; - padding: 0; - border: none; - border-radius: 0; - } - - .panel-header { - flex-shrink: 0; - padding: 1rem; - border-radius: 0; - } - - .panel-main-content { - flex-grow: 1; - position: relative; - height: auto; - min-height: 0; - } - - .panel-toggle-btn { - display: none; - } - - .header-top-row { - flex-direction: column; - align-items: flex-start; - gap: 1rem; - } - - .categoria-selector { - width: 100%; - } - - .mapa-column, - .resultados-column { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - box-sizing: border-box; - transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; - } - - .mapa-column { - z-index: 10; - } - - .resultados-column { - padding: 1rem; - overflow-y: auto; - z-index: 15; - } - - .panel-main-content.mobile-view-mapa .resultados-column { - opacity: 0; - visibility: hidden; - pointer-events: none; - } - - .panel-main-content.mobile-view-resultados .mapa-column { - opacity: 0; - visibility: hidden; - pointer-events: none; - } - - .resultados-column { - padding: 0.5rem; - overflow-y: auto; - z-index: 15; - padding-bottom: 50px; -} - -.mapa-column .mapa-componente-container, -.mapa-column .mapa-render-area { - height: 100%; -} - -.panel-partidos-container { - padding-bottom: 0; -} - -.zoom-controls-container, .mapa-volver-btn { - top: 15px; -} - - .header-bottom-row { - flex-direction: column; - align-items: stretch; - gap: 1rem; - } - - .municipio-search-container { - min-width: 100%; - /* El buscador ocupa todo el ancho en móvil */ - } - - @media (max-width: 900px) and (orientation: landscape) { - .panel-main-content { - display: flex; - flex-direction: row; - position: static; - height: 85vh; - min-height: 400px; - } - - .mapa-column, - .resultados-column { - position: static; - height: auto; - width: auto; - opacity: 1; - visibility: visible; - pointer-events: auto; - flex: 3; - overflow-y: auto; - } - - .resultados-column { - flex: 2; - min-width: 300px; - } - - .mobile-results-card-container { - display: none; - } - - .panel-toggle-btn { - display: flex; - - } - } -} - -/* --- ESTILOS PARA LA TARJETA DE RESULTADOS EN MÓVIL (ACTUALIZADOS) --- */ -.mobile-results-card-container { - position: absolute; - bottom: 0px; - left: 50%; - transform: translateX(-50%); - z-index: 40; - width: 95%; - max-width: 450px; - background-color: rgba(255, 255, 255, 0.95); - border-radius: 12px; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - border: 1px solid rgba(0, 0, 0, 0.1); - transition: all 0.3s ease-in-out; - display: flex; - flex-direction: column; -} - -.mobile-results-card-container.view-resultados .collapsible-section { - display: none; -} - -.mobile-results-card-container.view-resultados .mobile-card-view-toggle { - border-top: none; -} - - -.collapsible-section { - display: flex; - flex-direction: column; -} - -.mobile-results-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 18px; - cursor: pointer; -} - -.mobile-results-header .header-info { - display: flex; - align-items: baseline; - gap: 12px; -} - -.mobile-results-header .header-info h4 { - margin: 0; - font-size: 1.2rem; - font-weight: 700; -} - -/* SELECTOR ESPECÍFICO PARA EL TEXTO DE ACCIÓN */ -.mobile-results-header .header-info .header-action-text { - font-size: 0.8rem; - color: #6c757d; - font-weight: 500; - text-transform: uppercase; -} - -.mobile-results-header .header-toggle-icon { - font-size: 1.5rem; - color: #007bff; - transition: transform 0.3s; -} - -.mobile-results-content { - max-height: 0; - opacity: 0; - overflow: hidden; - transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out, padding 0.3s ease-in-out; - padding: 0 15px; - border-top: 1px solid transparent; -} - -.mobile-results-card-container.expanded .mobile-results-content { - max-height: 500px; - opacity: 1; - padding: 5px 15px 15px 15px; - border-top-color: #e0e0e0; -} - -.mobile-result-row { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 0; - border-bottom: 1px solid #f0f0f0; - border-left: 4px solid; - padding-left: 8px; -} - -.mobile-result-row:last-child { - border-bottom: none; -} - -.mobile-result-logo { - flex-shrink: 0; - width: 40px; - height: 40px; - border-radius: 8px; - box-sizing: border-box; -} - -.mobile-result-logo img { - width: 100%; - height: 100%; - border-radius: 8px; -} - -.mobile-result-info { - flex-grow: 1; - min-width: 0; -} - -.mobile-result-party-name { - display: block; - font-weight: 600; - font-size: 0.9rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-result-candidate-name { - display: block; - font-size: 0.75rem; - color: #6c757d; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.mobile-result-stats { - display: flex; - flex-direction: column; - align-items: flex-end; - flex-shrink: 0; -} - -.mobile-result-stats strong { - font-size: 0.95rem; - font-weight: 700; -} - -.mobile-result-stats span { - font-size: 0.7rem; - color: #6c757d; -} - -.no-results-text { - padding: 1rem; - text-align: center; - color: #6c757d; - font-size: 0.9rem; -} - -.mobile-card-view-toggle { - display: flex; - padding: 5px; - background-color: rgba(230, 230, 230, 0.6); - border-top: 1px solid rgba(0, 0, 0, 0.08); -} - -.mobile-card-view-toggle .toggle-btn { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 10px 15px; - /* Aumentado para pantallas más grandes */ - border: none; - background-color: transparent; - border-radius: 25px; - cursor: pointer; - font-size: 1rem; - /* Mantenido para pantallas más grandes */ - font-weight: 500; - color: #555; - transition: all 0.2s ease-in-out; -} - -.mobile-card-view-toggle .toggle-btn.active { - background-color: #007bff; - color: white; - box-shadow: 0 2px 5px rgba(0, 123, 255, 0.2); -} - -/* Ajustes para pantallas pequeñas como el iPhone SE */ -@media (max-width: 380px) { - .mobile-results-header { - padding: 4px 10px; - } - - .mobile-results-header .header-info h4 { - font-size: 0.75rem; - text-transform: uppercase; - } - - .mobile-results-header .header-info .header-action-text { - font-size: 0.7rem; - } - - .mobile-card-view-toggle .toggle-btn { - padding: 6px 10px; - font-size: 0.8rem; - } -} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.module.css b/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.module.css new file mode 100644 index 0000000..41ae60b --- /dev/null +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacional.module.css @@ -0,0 +1,539 @@ +/* src/features/legislativas/nacionales/PanelNacional.module.css */ + +/* --- SOLUCIÓN PARA FUENTES Y ESTILOS GLOBALES --- */ +.panelNacionalContainer, +.panelNacionalContainer * { + font-family: 'Roboto', sans-serif !important; + box-sizing: border-box; +} + +.panelNacionalContainer { + max-width: 1200px; + margin: auto; + border: 1px solid #e0e0e0; + border-radius: 8px; + position: relative; + padding: 10px; +} + +.panelHeader { + padding: 1rem 1.5rem; + border-bottom: 1px solid #e0e0e0; + position: relative; + z-index: 20; + background-color: white; +} + +.headerTopRow { + display: flex; + justify-content: flex-start; + align-items: center; + gap: 2rem; +} + +/* --- ESTILOS PARA REACT-SELECT USANDO MÓDULOS --- */ +.categoriaSelectorContainer { + min-width: 220px; +} + +.categoriaSelectorContainer :global(.categoriaSelector__control) { + border-radius: 8px !important; + border: 1px solid #e0e0e0 !important; + box-shadow: none !important; + transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; +} + +.categoriaSelectorContainer :global(.categoriaSelector__control--is-focused) { + border-color: #007bff !important; + box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important; +} + +.categoriaSelectorContainer :global(.categoriaSelector__single-value) { + font-weight: 500; + color: #333; +} + +.categoriaSelectorContainer :global(.categoriaSelector__menu) { + border-radius: 8px !important; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important; + border: 1px solid #e0e0e0 !important; + margin-top: 4px !important; +} + +.categoriaSelectorContainer :global(.categoriaSelector__option) { + cursor: pointer; + transition: background-color 0.2s, color 0.2s; +} + +.categoriaSelectorContainer :global(.categoriaSelector__option--is-focused) { + background-color: #f0f8ff; + color: #333; +} + +.categoriaSelectorContainer :global(.categoriaSelector__option--is-selected) { + background-color: #007bff; + color: white; +} + +.categoriaSelectorContainer :global(.categoriaSelector__indicator-separator) { + display: none; +} + +.categoriaSelectorContainer :global(.categoriaSelector__indicator) { + color: #a0a0a0; + transition: color 0.2s; +} + +.categoriaSelectorContainer :global(.categoriaSelector__indicator:hover) { + color: #333; +} + + +/* --- ESTILOS MODERNOS PARA BREADCRUMBS --- */ +.breadcrumbsContainer { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 1rem; +} + +.breadcrumbItem, +.breadcrumbItemActual { + display: flex; + align-items: center; + padding: 0.4rem 0.8rem; + border-radius: 8px; + transition: background-color 0.2s ease-in-out; +} + +.breadcrumbItem { + background-color: #f0f0f0; + border: 1px solid #e0e0e0; + color: #333; + cursor: pointer; + font-weight: 500; +} + +.breadcrumbItem:hover { + background-color: #e0e0e0; + border-color: #d1d1d1; +} + +.breadcrumbItemActual { + background-color: transparent; + color: #000; + font-weight: 700; +} + +.breadcrumbIcon { + margin-right: 0.4rem; + font-size: 1rem; +} + +.breadcrumbSeparator { + color: #a0a0a0; + font-size: 1.2rem; +} + +.panelMainContent { + display: flex; + height: 75vh; + min-height: 500px; + transition: all 0.5s ease-in-out; +} + +.mapaColumn { + flex: 2; + position: relative; + transition: flex 0.5s ease-in-out; +} + +.resultadosColumn { + flex: 1; + overflow-y: auto; + padding: 1.25rem; + transition: all 0.5s ease-in-out; + min-width: 320px; +} + +.partidoFila { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 0; + border-bottom: 1px solid #f0f0f0; + border-left: 5px solid; + border-radius: 12px; + padding-left: 1rem; +} + +.partidoLogo { + flex-shrink: 0; + width: 65px; + height: 65px; + border-radius: 12px; +} + +.partidoLogo img { + width: 100%; + height: 100%; + border-radius: 12px; +} + +.partidoMainContent { + flex-grow: 1; + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: auto auto; + align-items: center; + gap: 0.25rem 0.75rem; +} + +.partidoTopRow { display: contents; } +.partidoInfoWrapper { min-width: 0; text-align: left; } + +.partidoNombre { + font-weight: 700; + font-size: 1rem; + color: #212529; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.2; + text-transform: uppercase; +} + +.partidoNombreNormal { + font-size: 1rem; + color: #212529; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.2; + text-transform: uppercase; +} + +.candidatoNombre { + font-size: 0.75rem; + color: #6c757d; + text-transform: uppercase; + font-weight: 500; + line-height: 1.1; +} + +.partidoStats { flex-shrink: 0; text-align: right; padding-left: 1rem; } +.partidoPorcentaje { font-size: 1.35rem; font-weight: 700; display: block; } +.partidoVotos { font-size: 0.9rem; color: #666; display: block; } + +.partidoBarraBackground { + height: 12px; + background-color: #f0f0f0; + border-radius: 4px; + grid-column: 1 / 3; +} + +.partidoBarraForeground { + height: 100%; + border-radius: 4px; + transition: width 0.5s ease-in-out; +} + +.panelEstadoRecuento { + display: flex; + justify-content: space-around; + padding-bottom: 1rem; + margin-bottom: 1rem; + border-bottom: 1px solid #e0e0e0; +} + +.estadoItem { + width: 95px; + text-align: center; +} + +.estadoItem span { + margin-top: 0.5rem; + font-size: 0.85rem; + color: #666; + display: block; +} + +/* --- ESTILOS PARA MAPA --- */ +/* --- INICIO DE LA CORRECCIÓN --- */ +.mapaComponenteContainer { + width: 100%; + height: 100%; + position: relative; /* Esta línea es la que faltaba */ + overflow: hidden; +} +/* --- FIN DE LA CORRECCIÓN --- */ + +.mapaRenderArea { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + +.mapaVolverBtn, +.zoomBtn { + background-color: #ffffff; + border: 1px solid #e0e0e0; /* Borde más sutil */ + border-radius: 8px; /* Bordes más suaves */ + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); /* Sombra más pronunciada y moderna */ + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease-in-out; /* Transición suave para todos los efectos */ + color: #333; +} + +.mapaVolverBtn:hover, +.zoomBtn:hover:not(:disabled) { + border-color: #007bff; /* Borde de acento */ + color: #007bff; /* Icono/texto de acento */ + transform: translateY(-2px); /* Efecto de "levantar" */ + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12); +} + +.mapaVolverBtn:active, +.zoomBtn:active:not(:disabled) { + transform: translateY(0px); /* Botón "presionado" */ + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); /* Sombra interior */ + background-color: #f8f9fa; +} + +.mapaVolverBtn { + position: absolute; + top: 10px; + left: 10px; + z-index: 10; + padding: 8px 12px; + font-weight: 500; +} + +:global(.rsm-zoomable-group) { transition: transform 0.75s ease-in-out; } +:global(.rsm-zoomable-group.panning) { transition: none; } + +.panelMainContent.panelCollapsed .mapaColumn { flex: 1 1 100%; } + +.panelMainContent.panelCollapsed .resultadosColumn { + flex-basis: 0; + min-width: 0; + max-width: 0; + padding: 0; + overflow: hidden; +} + +.panelToggleBtn { + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + z-index: 10; + width: 30px; + height: 50px; + border: 1px solid #ccc; + background-color: white; + border-radius: 4px 0 0 4px; + cursor: pointer; + font-size: 1.3rem; + font-weight: bold; + color: #555; + display: flex; + align-items: center; + justify-content: center; + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); + transition: background-color 0.2s; +} + +.panelToggleBtn:hover { background-color: #f0f0f0; } + +:global(.rsm-geography) { + stroke: #000000; + stroke-width: 0.25px; + outline: none; + transition: filter 0.2s ease-in-out; +} +:global(.rsm-geography:not(.selected):hover) { + filter: brightness(1.25); + stroke: #ffffff; + stroke-width: 0.25px; + paint-order: stroke; +} +:global(.rsm-geography.selected) { + stroke: #000000; + stroke-width: 0.25px; + filter: none; + pointer-events: none; +} +:global(.rsm-geography-faded), :global(.rsm-geography-faded-municipality) { + opacity: 0.5; + pointer-events: none; +} +:global(.caba-comuna-geography) { + stroke: #000000; + stroke-width: 0.05px; +} +:global(.caba-comuna-geography:not(.selected):hover) { + stroke: #000000; + stroke-width: 0.055px; + filter: brightness(1.25); +} + +.transitionSpinner { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.5); + z-index: 20; + display: flex; + align-items: center; + justify-content: center; +} +.transitionSpinner::after { + content: ''; + width: 50px; + height: 50px; + border: 5px solid rgba(0, 0, 0, 0.2); + border-top-color: #007bff; + border-radius: 50%; + animation: spin 1s linear infinite; +} +@keyframes spin { to { transform: rotate(360deg); } } + +.cabaMagnifierContainer { position: absolute; height: auto; transform: translate(-50%, -50%); pointer-events: none; } +.cabaLupaSvg { width: 100%; height: auto; pointer-events: none; } +.cabaLupaInteractiveArea { pointer-events: all; cursor: pointer; filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.25)); transition: transform 0.2s ease-in-out; } +.cabaLupaInteractiveArea:hover { filter: brightness(1.15); stroke: #ffffff; stroke-width: 0.25px; } + +.skeletonFila div { + background: #f6f7f8; + background-image: linear-gradient(to right, #f6f7f8 0%, #edeef1 20%, #f6f7f8 40%, #f6f7f8 100%); + background-repeat: no-repeat; + background-size: 800px 104px; + animation: shimmer 1s linear infinite; + border-radius: 4px; +} +.skeletonLogo { width: 65px; height: 65px; } +.skeletonText { height: 1em; } +.skeletonBar { height: 20px; margin-top: 4px; } + +.zoomControlsContainer { + position: absolute; + top: 10px; + right: 10px; + z-index: 30; + display: flex; + flex-direction: column; + gap: 8px; /* Un poco más de espacio */ +} + +/* Estilos específicos para los botones de zoom */ +.zoomBtn { + width: 40px; + height: 40px; +} + +.zoomIconWrapper svg { + width: 22px; /* Iconos ligeramente más grandes */ + height: 22px; +} + +/* Estilo para el botón deshabilitado */ +.zoomBtn:disabled, +.zoomBtn.disabled { /* Cubrimos ambos casos */ + opacity: 0.6; + cursor: not-allowed; + background-color: #f8f9fa; +} + +:global(.map-locked .rsm-geography) { cursor: pointer; } +:global(.map-pannable .rsm-geography) { cursor: grab; } + +.headerBottomRow { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 1rem; + gap: 1rem; +} +.municipioSearchContainer { min-width: 280px; } + +@media (max-width: 800px) { + .panelNacionalContainer { display: flex; flex-direction: column; height: 100vh; padding: 0; border: none; border-radius: 0; } + .panelHeader { flex-shrink: 0; padding: 1rem; border-radius: 0; } + .panelMainContent { flex-grow: 1; position: relative; height: auto; min-height: 0; } + .panelToggleBtn { display: none; } + .headerTopRow { flex-direction: column; align-items: flex-start; gap: 1rem; } + .categoriaSelectorContainer { width: 100%; } + .mapaColumn, + .resultadosColumn { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; } + .mapaColumn { z-index: 10; } + .resultadosColumn { padding: 1rem; overflow-y: auto; z-index: 15; } + .panelMainContent.mobile-view-mapa .resultadosColumn { opacity: 0; visibility: hidden; pointer-events: none; } + .panelMainContent.mobile-view-resultados .mapaColumn { opacity: 0; visibility: hidden; pointer-events: none; } + .resultadosColumn { padding: 0.5rem; padding-bottom: 50px; } + .mapaColumn .mapaComponenteContainer, .mapaColumn .mapaRenderArea { height: 100%; } + .panelPartidosContainer { padding-bottom: 0; } + .zoomControlsContainer, .mapaVolverBtn { top: 15px; } + .headerBottomRow { flex-direction: column; align-items: stretch; gap: 1rem; } + .municipioSearchContainer { min-width: 100%; } + + @media (max-width: 900px) and (orientation: landscape) { + .panelMainContent { display: flex; flex-direction: row; position: static; height: 85vh; min-height: 400px; } + .mapaColumn, + .resultadosColumn { position: static; height: auto; width: auto; opacity: 1; visibility: visible; pointer-events: auto; flex: 3; overflow-y: auto; } + .resultadosColumn { flex: 2; min-width: 300px; } + .mobileResultsCardContainer { display: none; } + .panelToggleBtn { display: flex; } + } +} + +.mobileResultsCardContainer { + position: absolute; + bottom: 0px; + left: 50%; + transform: translateX(-50%); + z-index: 40; + width: 95%; + max-width: 450px; + background-color: rgba(255, 255, 255, 0.95); + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: 1px solid rgba(0, 0, 0, 0.1); + transition: all 0.3s ease-in-out; + display: flex; + flex-direction: column; +} +.mobileResultsCardContainer.view-resultados .collapsibleSection { display: none; } +.mobileResultsCardContainer.view-resultados .mobileCardViewToggle { border-top: none; } +.collapsibleSection { display: flex; flex-direction: column; } +.mobileResultsHeader { display: flex; justify-content: space-between; align-items: center; padding: 12px 18px; cursor: pointer; } +.mobileResultsHeader .headerInfo { display: flex; align-items: baseline; gap: 12px; } +.mobileResultsHeader .headerInfo h4 { margin: 0; font-size: 1.2rem; font-weight: 700; } +.mobileResultsHeader .headerInfo .headerActionText { font-size: 0.8rem; color: #6c757d; font-weight: 500; text-transform: uppercase; } +.mobileResultsHeader .headerToggleIcon { font-size: 1.5rem; color: #007bff; transition: transform 0.3s; } +.mobileResultsContent { max-height: 0; opacity: 0; overflow: hidden; transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out, padding 0.3s ease-in-out; padding: 0 15px; border-top: 1px solid transparent; } +.mobileResultsCardContainer.expanded .mobileResultsContent { max-height: 500px; opacity: 1; padding: 5px 15px 15px 15px; border-top-color: #e0e0e0; } +.mobileResultRow { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid #f0f0f0; border-left: 4px solid; padding-left: 8px; } +.mobileResultRow:last-child { border-bottom: none; } +.mobileResultLogo { flex-shrink: 0; width: 40px; height: 40px; border-radius: 8px; } +.mobileResultLogo img { width: 100%; height: 100%; border-radius: 8px; } +.mobileResultInfo { flex-grow: 1; min-width: 0; } +.mobileResultPartyName { display: block; font-weight: 600; font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.mobileResultCandidateName { display: block; font-size: 0.75rem; color: #6c757d; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.mobileResultStats { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; } +.mobileResultStats strong { font-size: 0.95rem; font-weight: 700; } +.mobileResultStats span { font-size: 0.7rem; color: #6c757d; } +.noResultsText { padding: 1rem; text-align: center; color: #6c757d; font-size: 0.9rem; } +.mobileCardViewToggle { display: flex; padding: 5px; background-color: rgba(230, 230, 230, 0.6); border-top: 1px solid rgba(0, 0, 0, 0.08); } +.mobileCardViewToggle .toggleBtn { flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 15px; border: none; background-color: transparent; border-radius: 25px; cursor: pointer; font-size: 1rem; font-weight: 500; color: #555; transition: all 0.2s ease-in-out; } +.mobileCardViewToggle .toggleBtn.active { background-color: #007bff; color: white; box-shadow: 0 2px 5px rgba(0, 123, 255, 0.2); } + +@media (max-width: 380px) { + .mobileResultsHeader { padding: 4px 10px; } + .mobileResultsHeader .headerInfo h4 { font-size: 0.75rem; text-transform: uppercase; } + .mobileResultsHeader .headerInfo .headerActionText { font-size: 0.7rem; } + .mobileCardViewToggle .toggleBtn { padding: 6px 10px; font-size: 0.8rem; } +} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacionalWidget.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacionalWidget.tsx index 3ccca55..da099fc 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacionalWidget.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/PanelNacionalWidget.tsx @@ -1,3 +1,5 @@ +// src/features/legislativas/nacionales/PanelNacionalWidget.tsx + import { useMemo, useState, Suspense, useEffect } from 'react'; import { useSuspenseQuery } from '@tanstack/react-query'; import { getPanelElectoral } from '../../../apiService'; @@ -5,7 +7,8 @@ import { MapaNacional } from './components/MapaNacional'; import { PanelResultados } from './components/PanelResultados'; import { Breadcrumbs } from './components/Breadcrumbs'; import { MunicipioSearch } from './components/MunicipioSearch'; -import './PanelNacional.css'; +// 1. La importación de CSS ahora se hace como un módulo +import styles from './PanelNacional.module.css'; import Select from 'react-select'; import type { PanelElectoralDto, ResultadoTicker } from '../../../types/types'; import { FiMap, FiList, FiChevronDown, FiChevronUp } from 'react-icons/fi'; @@ -26,22 +29,23 @@ interface MobileResultsCardProps { const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`; // --- SUB-COMPONENTE PARA UNA FILA DE RESULTADO --- +// 2. Todas las props 'className' ahora usan el objeto 'styles' const ResultRow = ({ partido }: { partido: ResultadoTicker }) => ( -
-
+
+
-
+
{partido.nombreCandidato ? ( <> - {partido.nombreCandidato} - {partido.nombreCorto || partido.nombre} + {partido.nombreCandidato} + {partido.nombreCorto || partido.nombre} ) : ( - {partido.nombreCorto || partido.nombre} + {partido.nombreCorto || partido.nombre} )}
-
+
{formatPercent(partido.porcentaje)} {partido.votos.toLocaleString('es-AR')}
@@ -80,44 +84,50 @@ const MobileResultsCard = ({ return null; } + // 3. Clases condicionales también se construyen con el objeto 'styles' + const cardClasses = [ + styles.mobileResultsCardContainer, + isExpanded ? styles.expanded : '', + styles[`view-${mobileView}`] + ].join(' '); + return ( -
+
{/* Sección Colapsable con Resultados */} -
-
setIsExpanded(!isExpanded)}> -
+
+
setIsExpanded(!isExpanded)}> +

{ambitoNombre}

- {/* Se añade una clase para estilizar este texto específicamente */} - {isExpanded ? 'Ocultar resultados' : 'Ver top 3'} + {isExpanded ? 'Ocultar resultados' : 'Ver top 3'}
-
+
{isExpanded ? : }
-
+
{topResults.length > 0 ? ( topResults.map(partido => ) ) : ( -

No hay resultados para esta selección.

+

No hay resultados para esta selección.

)}
{/* Footer Fijo con Botones de Navegación */} -
+
@@ -190,22 +200,28 @@ export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) => [categoriaId] ); + const mainContentClasses = [ + styles.panelMainContent, + !isPanelOpen ? styles.panelCollapsed : '', + isMobile ? styles[`mobile-view-${mobileView}`] : '' + ].join(' '); + return ( -
- -
-
+
+ +
+
'No se encontraron municipios'} />
diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultados.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultados.tsx index a0575fc..ca5b9be 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultados.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultados.tsx @@ -5,6 +5,8 @@ import { assetBaseUrl } from '../../../../apiService'; import { AnimatedNumber } from './AnimatedNumber'; import { CircularProgressbar, buildStyles } from 'react-circular-progressbar'; import 'react-circular-progressbar/dist/styles.css'; +// 1. Importamos el archivo de estilos como un módulo CSS +import styles from '../PanelNacional.module.css'; const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`; const formatVotes = (num: number) => Math.round(num).toLocaleString('es-AR'); @@ -30,11 +32,12 @@ interface PanelResultadosProps { } export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosProps) => { + // 2. Todas las props 'className' ahora usan el objeto 'styles' return ( -
+
-
-
+
+
Participación
-
+
-
+
{resultados.map(partido => (
-
+
-
-
-
+
+
+
{partido.nombreCandidato ? ( <> - {partido.nombreCandidato} - {partido.nombreCorto || partido.nombre} + {partido.nombreCandidato} + {partido.nombreCorto || partido.nombre} ) : ( - {partido.nombreCorto || partido.nombre} + {partido.nombreCorto || partido.nombre} )}
-
- +
+ - + votos
-
-
+
+
diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultadosSkeleton.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultadosSkeleton.tsx index 7534e0c..14212b3 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultadosSkeleton.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultadosSkeleton.tsx @@ -1,21 +1,26 @@ // src/features/legislativas/nacionales/components/PanelResultadosSkeleton.tsx + +// 1. Importamos el archivo de estilos como un módulo CSS +import styles from '../PanelNacional.module.css'; + +// 2. Todas las props 'className' ahora usan el objeto 'styles' const SkeletonRow = () => ( -
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
); export const PanelResultadosSkeleton = () => ( -
+
{[...Array(5)].map((_, i) => )}
); \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/ProvinciaCard.tsx b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/ProvinciaCard.tsx index 0394731..71b12ce 100644 --- a/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/ProvinciaCard.tsx +++ b/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/ProvinciaCard.tsx @@ -3,6 +3,8 @@ import type { ResumenProvincia, CategoriaResumen } from '../../../../types/types import { MiniMapaSvg } from './MiniMapaSvg'; import { ImageWithFallback } from '../../../../components/common/ImageWithFallback'; import { assetBaseUrl } from '../../../../apiService'; +// 1. Importamos el archivo de estilos como un módulo CSS +import styles from '../ResultadosNacionalesCardsWidget.module.css'; interface CategoriaDisplayProps { categoria: CategoriaResumen; @@ -17,48 +19,47 @@ interface ProvinciaCardProps { const formatNumber = (num: number) => num.toLocaleString('es-AR'); const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`; +// 2. Todas las props 'className' en este sub-componente ahora usan el objeto 'styles' const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) => { return ( -
-

{categoria.categoriaNombre}

+
+

{categoria.categoriaNombre}

{categoria.resultados.map(res => (
- {/* --- INICIO DE LA MODIFICACIÓN --- */} -
+
- {/* --- FIN DE LA MODIFICACIÓN --- */} -
+
{res.nombreCandidato ? ( <> - {res.nombreCandidato} - {res.nombreCortoAgrupacion || res.nombreAgrupacion} + {res.nombreCandidato} + {res.nombreCortoAgrupacion || res.nombreAgrupacion} ) : ( - {res.nombreCortoAgrupacion || res.nombreAgrupacion} + {res.nombreCortoAgrupacion || res.nombreAgrupacion} )} -
-
+
+
-
- {formatPercent(res.porcentaje)} - {formatNumber(res.votos)} votos +
+ {formatPercent(res.porcentaje)} + {formatNumber(res.votos)} votos
{mostrarBancas && ( -
+
+{res.bancasObtenidas} Bancas
@@ -66,7 +67,7 @@ const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) =
))} -