Rafael Lopez

comparador

...@@ -257,31 +257,216 @@ ...@@ -257,31 +257,216 @@
257 const twTop2 = tweened(0, tweenOptions); 257 const twTop2 = tweened(0, tweenOptions);
258 const twTop3 = tweened(0, tweenOptions); 258 const twTop3 = tweened(0, tweenOptions);
259 259
260 - // Comparación (placeholder por ahora - TODO: implementar) 260 + // ══════════════════════════════════════════════════════════════
261 + // COMPARACIÓN - Datos reales de Supabase
262 + // ══════════════════════════════════════════════════════════════
261 const twMontoA = tweened(0, tweenOptions); 263 const twMontoA = tweened(0, tweenOptions);
262 const twMontoB = tweened(0, tweenOptions); 264 const twMontoB = tweened(0, tweenOptions);
263 const twPerCapitaA = tweened(0, tweenOptions); 265 const twPerCapitaA = tweened(0, tweenOptions);
264 const twPerCapitaB = tweened(0, tweenOptions); 266 const twPerCapitaB = tweened(0, tweenOptions);
265 267
266 let hoveredYearComparar = $state(null); 268 let hoveredYearComparar = $state(null);
267 - let entidadA = $state(0); 269 + let loadingComparacion = $state(false);
268 - let entidadB = $state(1); 270 +
269 - let displayPeriodoComparar = $derived( 271 + // Entidades seleccionadas para comparar
270 - hoveredYearComparar ? hoveredYearComparar : `${años[0]}-${años[años.length - 1]}` 272 + let entidadCompararA = $state(null);
271 - ); 273 + let entidadCompararB = $state(null);
274 +
275 + // Datos cargados de cada entidad
276 + let datosCompararA = $state({ anual: [], historico: null });
277 + let datosCompararB = $state({ anual: [], historico: null });
278 +
279 + // Dropdown states para comparación
280 + let dropdownAOpen = $state(false);
281 + let dropdownBOpen = $state(false);
282 + let searchA = $state('');
283 + let searchB = $state('');
284 +
285 + // Filtrar entidades para cada dropdown
286 + let filteredEntidadesA = $derived.by(() => {
287 + if (!searchA || searchA.length < 2) return entidades.slice(0, 20);
288 + const q = searchA.toLowerCase();
289 + return entidades.filter(e =>
290 + e.entidad_desc?.toLowerCase().includes(q) ||
291 + e.entidad?.toString().includes(q)
292 + ).slice(0, 30);
293 + });
294 +
295 + let filteredEntidadesB = $derived.by(() => {
296 + if (!searchB || searchB.length < 2) return entidades.slice(0, 20);
297 + const q = searchB.toLowerCase();
298 + return entidades.filter(e =>
299 + e.entidad_desc?.toLowerCase().includes(q) ||
300 + e.entidad?.toString().includes(q)
301 + ).slice(0, 30);
302 + });
303 +
304 + // Cargar datos de una entidad para comparación
305 + async function loadDatosEntidad(entidad) {
306 + const { data, error } = await supabase
307 + .schema('ppto')
308 + .from('vista_objeto_entidad')
309 + .select('*')
310 + .eq('objeto', objetoCodigo)
311 + .eq('nivel', objetoNivel)
312 + .eq('entidad', entidad.entidad)
313 + .order('gestion');
314 +
315 + if (data) {
316 + return {
317 + anual: data.filter(d => d.gestion > 0),
318 + historico: data.find(d => d.gestion === 0) || null
319 + };
320 + }
321 + return { anual: [], historico: null };
322 + }
323 +
324 + // Seleccionar entidad A
325 + async function selectEntidadA(entidad) {
326 + entidadCompararA = entidad;
327 + dropdownAOpen = false;
328 + searchA = '';
329 + loadingComparacion = true;
330 + datosCompararA = await loadDatosEntidad(entidad);
331 + loadingComparacion = false;
332 + }
333 +
334 + // Seleccionar entidad B
335 + async function selectEntidadB(entidad) {
336 + entidadCompararB = entidad;
337 + dropdownBOpen = false;
338 + searchB = '';
339 + loadingComparacion = true;
340 + datosCompararB = await loadDatosEntidad(entidad);
341 + loadingComparacion = false;
342 + }
343 +
344 + // Selector de rango de años
345 + let rangoAños = $state('10'); // '5', '10', '15', 'todo'
346 +
347 + // Años comunes entre ambas entidades
348 + let añosComunesTotales = $derived.by(() => {
349 + if (!datosCompararA.anual.length || !datosCompararB.anual.length) return [];
350 + const añosA = new Set(datosCompararA.anual.map(d => d.gestion));
351 + const añosB = new Set(datosCompararB.anual.map(d => d.gestion));
352 + return [...añosA].filter(a => añosB.has(a)).sort((a, b) => a - b);
353 + });
354 +
355 + // Años filtrados según el rango seleccionado
356 + let añosComparacion = $derived.by(() => {
357 + if (rangoAños === 'todo') return añosComunesTotales;
358 + const n = parseInt(rangoAños);
359 + return añosComunesTotales.slice(-n);
360 + });
361 +
362 + // Datos formateados para el gráfico
363 + let datosGraficoA = $derived.by(() => {
364 + const map = new Map(datosCompararA.anual.map(d => [d.gestion, d]));
365 + return añosComparacion.map(año => map.get(año) || { per_capita: 0, monto: 0 });
366 + });
367 +
368 + let datosGraficoB = $derived.by(() => {
369 + const map = new Map(datosCompararB.anual.map(d => [d.gestion, d]));
370 + return añosComparacion.map(año => map.get(año) || { per_capita: 0, monto: 0 });
371 + });
372 +
373 + // Máximo para escala del gráfico
374 + let maxComparacion = $derived.by(() => {
375 + const allPerCapita = [
376 + ...datosGraficoA.map(d => d.per_capita || 0),
377 + ...datosGraficoB.map(d => d.per_capita || 0)
378 + ];
379 + return Math.max(...allPerCapita, 1) * 1.1;
380 + });
381 +
382 + // Período para mostrar
383 + let displayPeriodoComparar = $derived.by(() => {
384 + if (hoveredYearComparar) return hoveredYearComparar;
385 + if (añosComparacion.length > 0) {
386 + return `${añosComparacion[0]}-${añosComparacion[añosComparacion.length - 1]}`;
387 + }
388 + return 'Selecciona entidades';
389 + });
272 390
273 - // Datos dummy para comparación (TODO: conectar a Supabase) 391 + // Valores reactivos para comparación (peso y ranking)
274 - const entidadesComparables = [ 392 + let displayPropA = $derived.by(() => {
275 - { id: 0, nombre: 'Entidad A', datos: [], porcentajeObjeto: 0, ranking: 1 }, 393 + if (hoveredYearComparar) {
276 - { id: 1, nombre: 'Entidad B', datos: [], porcentajeObjeto: 0, ranking: 2 } 394 + const idx = añosComparacion.indexOf(hoveredYearComparar);
277 - ]; 395 + if (idx >= 0) return datosGraficoA[idx]?.prop || 0;
278 - let entA = $derived(entidadesComparables[entidadA] || entidadesComparables[0]); 396 + }
279 - let entB = $derived(entidadesComparables[entidadB] || entidadesComparables[1]); 397 + return datosCompararA.historico?.prop || 0;
280 - let maxComparacion = $state(100); 398 + });
281 - let años = $state([2020, 2021, 2022, 2023, 2024]); 399 +
400 + let displayPropB = $derived.by(() => {
401 + if (hoveredYearComparar) {
402 + const idx = añosComparacion.indexOf(hoveredYearComparar);
403 + if (idx >= 0) return datosGraficoB[idx]?.prop || 0;
404 + }
405 + return datosCompararB.historico?.prop || 0;
406 + });
407 +
408 + let displayRankingA = $derived.by(() => {
409 + if (hoveredYearComparar) {
410 + const idx = añosComparacion.indexOf(hoveredYearComparar);
411 + if (idx >= 0) return datosGraficoA[idx]?.ranking || '-';
412 + }
413 + return datosCompararA.historico?.ranking || '-';
414 + });
415 +
416 + let displayRankingB = $derived.by(() => {
417 + if (hoveredYearComparar) {
418 + const idx = añosComparacion.indexOf(hoveredYearComparar);
419 + if (idx >= 0) return datosGraficoB[idx]?.ranking || '-';
420 + }
421 + return datosCompararB.historico?.ranking || '-';
422 + });
423 +
424 + let displayNEntidadesA = $derived.by(() => {
425 + if (hoveredYearComparar) {
426 + const idx = añosComparacion.indexOf(hoveredYearComparar);
427 + if (idx >= 0) return datosGraficoA[idx]?.n_entidades || '-';
428 + }
429 + return datosCompararA.historico?.n_entidades || '-';
430 + });
431 +
432 + let displayNEntidadesB = $derived.by(() => {
433 + if (hoveredYearComparar) {
434 + const idx = añosComparacion.indexOf(hoveredYearComparar);
435 + if (idx >= 0) return datosGraficoB[idx]?.n_entidades || '-';
436 + }
437 + return datosCompararB.historico?.n_entidades || '-';
438 + });
439 +
440 + // Colores
282 let colorA = '#2a9d8f'; 441 let colorA = '#2a9d8f';
283 let colorB = '#f4a261'; 442 let colorB = '#f4a261';
284 443
444 + // Actualizar tweened de comparación
445 + $effect(() => {
446 + const year = hoveredYearComparar;
447 + const idx = year ? añosComparacion.indexOf(year) : -1;
448 +
449 + let montoA, montoB, perCapitaA, perCapitaB;
450 +
451 + if (idx >= 0) {
452 + montoA = toNumber(datosGraficoA[idx]?.monto);
453 + montoB = toNumber(datosGraficoB[idx]?.monto);
454 + perCapitaA = toNumber(datosGraficoA[idx]?.per_capita);
455 + perCapitaB = toNumber(datosGraficoB[idx]?.per_capita);
456 + } else {
457 + // Histórico o suma
458 + montoA = toNumber(datosCompararA.historico?.monto);
459 + montoB = toNumber(datosCompararB.historico?.monto);
460 + perCapitaA = toNumber(datosCompararA.historico?.per_capita);
461 + perCapitaB = toNumber(datosCompararB.historico?.per_capita);
462 + }
463 +
464 + twMontoA.set(montoA);
465 + twMontoB.set(montoB);
466 + twPerCapitaA.set(perCapitaA);
467 + twPerCapitaB.set(perCapitaB);
468 + });
469 +
285 // Helper para asegurar que siempre tengamos un número 470 // Helper para asegurar que siempre tengamos un número
286 function toNumber(val) { 471 function toNumber(val) {
287 const n = parseFloat(val); 472 const n = parseFloat(val);
...@@ -350,6 +535,13 @@ ...@@ -350,6 +535,13 @@
350 if (entityDropdownOpen && !e.target.closest('.entity-dropdown-container')) { 535 if (entityDropdownOpen && !e.target.closest('.entity-dropdown-container')) {
351 entityDropdownOpen = false; 536 entityDropdownOpen = false;
352 } 537 }
538 + // Dropdowns de comparación
539 + if (dropdownAOpen && !e.target.closest('.comparador-dropdown-container')) {
540 + dropdownAOpen = false;
541 + }
542 + if (dropdownBOpen && !e.target.closest('.comparador-dropdown-container')) {
543 + dropdownBOpen = false;
544 + }
353 }; 545 };
354 document.addEventListener('click', handleClickOutside); 546 document.addEventListener('click', handleClickOutside);
355 547
...@@ -614,109 +806,190 @@ ...@@ -614,109 +806,190 @@
614 {:else} 806 {:else}
615 <!-- VISTA COMPARAR --> 807 <!-- VISTA COMPARAR -->
616 808
617 - <!-- Selectores de entidades --> 809 + <!-- Selectores de entidades con búsqueda -->
618 <div class="comparador-selectors"> 810 <div class="comparador-selectors">
619 - <div class="selector-group"> 811 + <div class="selector-group comparador-dropdown-container">
620 <span class="selector-dot" style="background: {colorA}"></span> 812 <span class="selector-dot" style="background: {colorA}"></span>
621 - <select bind:value={entidadA}> 813 + <div class="relative" style="flex: 1;">
622 - {#each entidadesComparables as ent} 814 + <button class="selector-btn comparador-btn" onclick={() => { dropdownAOpen = !dropdownAOpen; dropdownBOpen = false; }}>
623 - <option value={ent.id} disabled={ent.id === entidadB}>{ent.nombre}</option> 815 + <span class="truncate">
624 - {/each} 816 + {entidadCompararA ? entidadCompararA.entidad_desc : 'Seleccionar entidad'}
625 - </select> 817 + </span>
818 + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
819 + <path d="M6 9l6 6 6-6"/>
820 + </svg>
821 + </button>
822 + {#if dropdownAOpen}
823 + <div class="dropdown-panel comparador-panel">
824 + <div class="dropdown-search">
825 + <input type="text" bind:value={searchA} placeholder="Buscar entidad..." class="search-input" />
826 + </div>
827 + <div class="dropdown-list">
828 + {#each filteredEntidadesA as entity}
829 + <button
830 + class="dropdown-item"
831 + class:active={entidadCompararA?.entidad === entity.entidad}
832 + disabled={entidadCompararB?.entidad === entity.entidad}
833 + onclick={() => selectEntidadA(entity)}
834 + >
835 + <span class="entity-code">{entity.entidad}</span>
836 + <span class="truncate">{entity.entidad_desc}</span>
837 + </button>
838 + {/each}
839 + </div>
840 + </div>
841 + {/if}
842 + </div>
626 </div> 843 </div>
844 +
627 <span class="vs">vs</span> 845 <span class="vs">vs</span>
628 - <div class="selector-group"> 846 +
847 + <div class="selector-group comparador-dropdown-container">
629 <span class="selector-dot" style="background: {colorB}"></span> 848 <span class="selector-dot" style="background: {colorB}"></span>
630 - <select bind:value={entidadB}> 849 + <div class="relative" style="flex: 1;">
631 - {#each entidadesComparables as ent} 850 + <button class="selector-btn comparador-btn" onclick={() => { dropdownBOpen = !dropdownBOpen; dropdownAOpen = false; }}>
632 - <option value={ent.id} disabled={ent.id === entidadA}>{ent.nombre}</option> 851 + <span class="truncate">
633 - {/each} 852 + {entidadCompararB ? entidadCompararB.entidad_desc : 'Seleccionar entidad'}
634 - </select> 853 + </span>
854 + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
855 + <path d="M6 9l6 6 6-6"/>
856 + </svg>
857 + </button>
858 + {#if dropdownBOpen}
859 + <div class="dropdown-panel comparador-panel">
860 + <div class="dropdown-search">
861 + <input type="text" bind:value={searchB} placeholder="Buscar entidad..." class="search-input" />
862 + </div>
863 + <div class="dropdown-list">
864 + {#each filteredEntidadesB as entity}
865 + <button
866 + class="dropdown-item"
867 + class:active={entidadCompararB?.entidad === entity.entidad}
868 + disabled={entidadCompararA?.entidad === entity.entidad}
869 + onclick={() => selectEntidadB(entity)}
870 + >
871 + <span class="entity-code">{entity.entidad}</span>
872 + <span class="truncate">{entity.entidad_desc}</span>
873 + </button>
874 + {/each}
875 + </div>
876 + </div>
877 + {/if}
878 + </div>
635 </div> 879 </div>
636 </div> 880 </div>
637 881
638 - <!-- Gráfico comparativo --> 882 + <!-- Mensaje si no hay entidades seleccionadas -->
639 - <div class="chart-section"> 883 + {#if !entidadCompararA || !entidadCompararB}
640 - <div class="chart-header"> 884 + <div class="comparador-placeholder">
641 - <span class="chart-title">Gasto por persona</span> 885 + <p>Selecciona dos entidades para comparar su gasto en <strong>{objeto.nombre}</strong></p>
642 - <span class="chart-period">{displayPeriodoComparar}</span> 886 + </div>
887 + {:else if loadingComparacion}
888 + <div class="comparador-placeholder">
889 + <p>Cargando datos...</p>
643 </div> 890 </div>
891 + {:else if añosComparacion.length === 0}
892 + <div class="comparador-placeholder">
893 + <p>Las entidades seleccionadas no tienen años en común para este objeto.</p>
894 + </div>
895 + {:else}
896 + <!-- Selector de rango de años -->
897 + {#if añosComunesTotales.length > 10}
898 + <div class="rango-selector">
899 + <span class="rango-label">Mostrar:</span>
900 + <div class="rango-options">
901 + <button class="rango-btn" class:active={rangoAños === '5'} onclick={() => rangoAños = '5'}>5 años</button>
902 + <button class="rango-btn" class:active={rangoAños === '10'} onclick={() => rangoAños = '10'}>10 años</button>
903 + <button class="rango-btn" class:active={rangoAños === '15'} onclick={() => rangoAños = '15'}>15 años</button>
904 + <button class="rango-btn" class:active={rangoAños === 'todo'} onclick={() => rangoAños = 'todo'}>Todo ({añosComunesTotales.length})</button>
905 + </div>
906 + </div>
907 + {/if}
644 908
645 - <div class="chart-wrapper"> 909 + <!-- Gráfico comparativo -->
646 - <div class="y-axis"> 910 + <div class="chart-section">
647 - <span class="y-label">Bs {formatMonto(maxComparacion)}</span> 911 + <div class="chart-header">
648 - <span class="y-label">Bs {formatMonto(Math.round(maxComparacion / 2))}</span> 912 + <span class="chart-title">Gasto por persona</span>
649 - <span class="y-label">Bs 0</span> 913 + <span class="chart-period">{displayPeriodoComparar}</span>
650 </div> 914 </div>
651 915
652 - <div 916 + <div class="chart-wrapper chart-wrapper-scroll">
653 - class="chart chart-comparar" 917 + <div class="y-axis y-axis-sticky">
654 - onmouseleave={() => hoveredYearComparar = null} 918 + <span class="y-label">Bs {formatMonto(Math.round(maxComparacion))}</span>
655 - role="group" 919 + <span class="y-label">Bs {formatMonto(Math.round(maxComparacion / 2))}</span>
656 - > 920 + <span class="y-label">Bs 0</span>
657 - {#each años as año, i} 921 + </div>
658 - <div 922 +
659 - class="bar-group" 923 + <div
660 - class:active={hoveredYearComparar === año} 924 + class="chart chart-comparar"
661 - onmouseenter={() => hoveredYearComparar = año} 925 + class:chart-muchos-años={añosComparacion.length > 15}
662 - role="button" 926 + onmouseleave={() => hoveredYearComparar = null}
663 - tabindex="0" 927 + role="group"
664 - > 928 + >
665 - <div class="bars-pair"> 929 + {#each añosComparacion as año, i}
666 - <div 930 + <div
667 - class="bar bar-a" 931 + class="bar-group"
668 - style="height: {(entA.datos[i].perCapita / maxComparacion) * 100}%; --bar-color: {colorA}" 932 + class:active={hoveredYearComparar === año}
669 - ></div> 933 + onmouseenter={() => hoveredYearComparar = año}
670 - <div 934 + role="button"
671 - class="bar bar-b" 935 + tabindex="0"
672 - style="height: {(entB.datos[i].perCapita / maxComparacion) * 100}%; --bar-color: {colorB}" 936 + >
673 - ></div> 937 + <div class="bars-pair">
938 + <div
939 + class="bar bar-a"
940 + style="height: {((datosGraficoA[i]?.per_capita || 0) / maxComparacion) * 100}%; --bar-color: {colorA}"
941 + ></div>
942 + <div
943 + class="bar bar-b"
944 + style="height: {((datosGraficoB[i]?.per_capita || 0) / maxComparacion) * 100}%; --bar-color: {colorB}"
945 + ></div>
946 + </div>
947 + <span class="bar-label visible" class:highlight={hoveredYearComparar === año}>{año}</span>
674 </div> 948 </div>
675 - <span class="bar-label visible" class:highlight={hoveredYearComparar === año}>{año}</span> 949 + {/each}
676 - </div> 950 + </div>
677 - {/each}
678 </div> 951 </div>
679 - </div>
680 952
681 - <div class="chart-legend"> 953 + <div class="chart-legend">
682 - <span class="legend-item"><span class="legend-dot" style="background: {colorA}"></span>{entA.nombre}</span> 954 + <span class="legend-item"><span class="legend-dot" style="background: {colorA}"></span>{entidadCompararA.entidad_desc}</span>
683 - <span class="legend-item"><span class="legend-dot" style="background: {colorB}"></span>{entB.nombre}</span> 955 + <span class="legend-item"><span class="legend-dot" style="background: {colorB}"></span>{entidadCompararB.entidad_desc}</span>
956 + </div>
684 </div> 957 </div>
685 - </div>
686 958
687 - <!-- KPIs comparativos - tabla --> 959 + <!-- KPIs comparativos - tabla -->
688 - <div class="kpis-tabla"> 960 + <div class="kpis-tabla">
689 - <!-- Header --> 961 + <!-- Header -->
690 - <div class="tabla-header"> 962 + <div class="tabla-header">
691 - <span class="tabla-label"></span> 963 + <span class="tabla-label"></span>
692 - <span class="tabla-val" style="color: {colorA}">{entA.nombre}</span> 964 + <span class="tabla-val" style="color: {colorA}">{entidadCompararA.entidad_desc?.slice(0, 20)}{entidadCompararA.entidad_desc?.length > 20 ? '...' : ''}</span>
693 - <span class="tabla-val" style="color: {colorB}">{entB.nombre}</span> 965 + <span class="tabla-val" style="color: {colorB}">{entidadCompararB.entidad_desc?.slice(0, 20)}{entidadCompararB.entidad_desc?.length > 20 ? '...' : ''}</span>
694 - </div> 966 + </div>
695 - <!-- Gasto --> 967 + <!-- Gasto -->
696 - <div class="tabla-row"> 968 + <div class="tabla-row">
697 - <span class="tabla-label">{hoveredYearComparar ? `Gasto ${hoveredYearComparar}` : 'Gasto 2020-2024'}</span> 969 + <span class="tabla-label">{hoveredYearComparar ? `Gasto ${hoveredYearComparar}` : `Gasto ${añosComparacion[0]}-${añosComparacion[añosComparacion.length-1]}`}</span>
698 - <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoA))}</span> 970 + <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoA))}</span>
699 - <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoB))}</span> 971 + <span class="tabla-val">Bs {formatMontoLargo(Math.round($twMontoB))}</span>
700 - </div> 972 + </div>
701 - <!-- Per cápita --> 973 + <!-- Per cápita -->
702 - <div class="tabla-row"> 974 + <div class="tabla-row">
703 - <span class="tabla-label">{hoveredYearComparar ? `Por persona ${hoveredYearComparar}` : 'Por persona (prom.)'}</span> 975 + <span class="tabla-label">{hoveredYearComparar ? `Por persona ${hoveredYearComparar}` : 'Por persona (prom.)'}</span>
704 - <span class="tabla-val">Bs {Math.round($twPerCapitaA).toLocaleString()}</span> 976 + <span class="tabla-val">Bs {formatPerCapita($twPerCapitaA)}</span>
705 - <span class="tabla-val">Bs {Math.round($twPerCapitaB).toLocaleString()}</span> 977 + <span class="tabla-val">Bs {formatPerCapita($twPerCapitaB)}</span>
706 - </div> 978 + </div>
707 - <!-- Porcentaje --> 979 + <!-- Porcentaje -->
708 - <div class="tabla-row"> 980 + <div class="tabla-row">
709 - <span class="tabla-label">Peso sobre total</span> 981 + <span class="tabla-label">{hoveredYearComparar ? `Peso en ${hoveredYearComparar}` : 'Peso sobre total'}</span>
710 - <span class="tabla-val">{entA.porcentajeObjeto}%</span> 982 + <span class="tabla-val">{displayPropA.toLocaleString('es-BO', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%</span>
711 - <span class="tabla-val">{entB.porcentajeObjeto}%</span> 983 + <span class="tabla-val">{displayPropB.toLocaleString('es-BO', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%</span>
712 - </div> 984 + </div>
713 - <!-- Ranking --> 985 + <!-- Ranking -->
714 - <div class="tabla-row"> 986 + <div class="tabla-row">
715 - <span class="tabla-label">Ranking</span> 987 + <span class="tabla-label">{hoveredYearComparar ? `Ranking ${hoveredYearComparar}` : 'Ranking'}</span>
716 - <span class="tabla-val">#{entA.ranking}</span> 988 + <span class="tabla-val">#{displayRankingA} de {displayNEntidadesA}</span>
717 - <span class="tabla-val">#{entB.ranking}</span> 989 + <span class="tabla-val">#{displayRankingB} de {displayNEntidadesB}</span>
990 + </div>
718 </div> 991 </div>
719 - </div> 992 + {/if}
720 993
721 {/if} 994 {/if}
722 995
...@@ -1070,6 +1343,43 @@ ...@@ -1070,6 +1343,43 @@
1070 gap: 0.75rem; 1343 gap: 0.75rem;
1071 } 1344 }
1072 1345
1346 + .chart-wrapper-scroll {
1347 + overflow-x: auto;
1348 + overflow-y: visible;
1349 + scrollbar-width: thin;
1350 + scrollbar-color: #2E2E2C transparent;
1351 + }
1352 +
1353 + .chart-wrapper-scroll::-webkit-scrollbar {
1354 + height: 6px;
1355 + }
1356 +
1357 + .chart-wrapper-scroll::-webkit-scrollbar-track {
1358 + background: transparent;
1359 + }
1360 +
1361 + .chart-wrapper-scroll::-webkit-scrollbar-thumb {
1362 + background: #2E2E2C;
1363 + border-radius: 3px;
1364 + }
1365 +
1366 + .y-axis-sticky {
1367 + position: sticky;
1368 + left: 0;
1369 + z-index: 10;
1370 + background: #0A0A0A;
1371 + padding-right: 0.5rem;
1372 + margin-right: -0.5rem;
1373 + }
1374 +
1375 + .chart-muchos-años {
1376 + min-width: max-content;
1377 + }
1378 +
1379 + .chart-muchos-años .bar-group {
1380 + min-width: 40px;
1381 + }
1382 +
1073 .y-axis { 1383 .y-axis {
1074 display: flex; 1384 display: flex;
1075 flex-direction: column; 1385 flex-direction: column;
...@@ -1313,11 +1623,91 @@ ...@@ -1313,11 +1623,91 @@
1313 /* Comparador inline - ajustado para 100vh */ 1623 /* Comparador inline - ajustado para 100vh */
1314 .comparador-selectors { 1624 .comparador-selectors {
1315 display: flex; 1625 display: flex;
1316 - align-items: center; 1626 + align-items: flex-start;
1317 gap: 1rem; 1627 gap: 1rem;
1318 margin-bottom: 0.75rem; 1628 margin-bottom: 0.75rem;
1319 } 1629 }
1320 1630
1631 + .comparador-dropdown-container {
1632 + position: relative;
1633 + }
1634 +
1635 + .comparador-btn {
1636 + width: 100%;
1637 + justify-content: space-between;
1638 + }
1639 +
1640 + .comparador-panel {
1641 + width: 100%;
1642 + min-width: 280px;
1643 + }
1644 +
1645 + .comparador-placeholder {
1646 + display: flex;
1647 + align-items: center;
1648 + justify-content: center;
1649 + min-height: 200px;
1650 + background: rgba(255,255,255,0.02);
1651 + border: 1px dashed #2E2E2C;
1652 + border-radius: 12px;
1653 + padding: 2rem;
1654 + text-align: center;
1655 + }
1656 +
1657 + .comparador-placeholder p {
1658 + color: #5A5650;
1659 + font-size: 0.875rem;
1660 + margin: 0;
1661 + }
1662 +
1663 + .comparador-placeholder strong {
1664 + color: #8A8578;
1665 + }
1666 +
1667 + .rango-selector {
1668 + display: flex;
1669 + align-items: center;
1670 + gap: 0.75rem;
1671 + margin-bottom: 0.75rem;
1672 + }
1673 +
1674 + .rango-label {
1675 + font-size: 0.75rem;
1676 + color: #5A5650;
1677 + }
1678 +
1679 + .rango-options {
1680 + display: flex;
1681 + gap: 0.25rem;
1682 + }
1683 +
1684 + .rango-btn {
1685 + padding: 0.375rem 0.75rem;
1686 + font-size: 0.6875rem;
1687 + font-family: 'DM Mono', monospace;
1688 + color: #5A5650;
1689 + background: transparent;
1690 + border: none;
1691 + border-radius: 6px;
1692 + cursor: pointer;
1693 + transition: all 0.2s;
1694 + }
1695 +
1696 + .rango-btn:hover {
1697 + color: #8A8578;
1698 + background: rgba(255,255,255,0.05);
1699 + }
1700 +
1701 + .rango-btn.active {
1702 + color: #F5F0E8;
1703 + background: rgba(255,255,255,0.1);
1704 + }
1705 +
1706 + .dropdown-item:disabled {
1707 + opacity: 0.4;
1708 + cursor: not-allowed;
1709 + }
1710 +
1321 .selector-group { 1711 .selector-group {
1322 flex: 1; 1712 flex: 1;
1323 display: flex; 1713 display: flex;
...@@ -1730,6 +2120,14 @@ ...@@ -1730,6 +2120,14 @@
1730 color: #8A8578; 2120 color: #8A8578;
1731 } 2121 }
1732 2122
2123 + .tema-claro .y-axis-sticky {
2124 + background: #FAFAF8;
2125 + }
2126 +
2127 + .tema-claro .chart-wrapper-scroll::-webkit-scrollbar-thumb {
2128 + background: #D5D5D0;
2129 + }
2130 +
1733 .tema-claro .chart { 2131 .tema-claro .chart {
1734 border-left-color: #E5E5E0; 2132 border-left-color: #E5E5E0;
1735 } 2133 }
...@@ -1822,6 +2220,37 @@ ...@@ -1822,6 +2220,37 @@
1822 outline: none; 2220 outline: none;
1823 } 2221 }
1824 2222
2223 + .tema-claro .comparador-placeholder {
2224 + background: rgba(0,0,0,0.02);
2225 + border-color: #E5E5E0;
2226 + }
2227 +
2228 + .tema-claro .comparador-placeholder p {
2229 + color: #8A8578;
2230 + }
2231 +
2232 + .tema-claro .comparador-placeholder strong {
2233 + color: #5A5650;
2234 + }
2235 +
2236 + .tema-claro .rango-label {
2237 + color: #8A8578;
2238 + }
2239 +
2240 + .tema-claro .rango-btn {
2241 + color: #8A8578;
2242 + }
2243 +
2244 + .tema-claro .rango-btn:hover {
2245 + color: #5A5650;
2246 + background: rgba(0,0,0,0.05);
2247 + }
2248 +
2249 + .tema-claro .rango-btn.active {
2250 + color: #1A1A18;
2251 + background: rgba(0,0,0,0.08);
2252 + }
2253 +
1825 .tema-claro .vs { 2254 .tema-claro .vs {
1826 color: #8A8578; 2255 color: #8A8578;
1827 } 2256 }
......