Rafael Lopez

test done

...@@ -27,3 +27,7 @@ claude.md ...@@ -27,3 +27,7 @@ claude.md
27 27
28 # Docs internos 28 # Docs internos
29 static/ESTILOS_*.md 29 static/ESTILOS_*.md
30 +.Rproj.user
31 +
32 +# Data
33 +/data
......
...@@ -32,10 +32,7 @@ ...@@ -32,10 +32,7 @@
32 <div class="fixed top-4 right-4 z-[110] md:hidden"> 32 <div class="fixed top-4 right-4 z-[110] md:hidden">
33 <button 33 <button
34 onclick={onOpenDrawer} 34 onclick={onOpenDrawer}
35 - class="p-2 rounded-full transition-all flex-shrink-0" 35 + class="nav-icon p-2 rounded-full transition-all flex-shrink-0"
36 - style="background-color: transparent; color: var(--theme-texto); cursor: pointer;"
37 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
38 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
39 aria-label="Abrir menú" 36 aria-label="Abrir menú"
40 > 37 >
41 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"> 38 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
...@@ -52,10 +49,7 @@ ...@@ -52,10 +49,7 @@
52 <div class="relative group/home"> 49 <div class="relative group/home">
53 <a 50 <a
54 href="/" 51 href="/"
55 - class="p-2 rounded-full transition-all block" 52 + class="nav-icon p-2 rounded-full transition-all block"
56 - style="background-color: transparent; color: var(--theme-texto);"
57 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
58 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
59 > 53 >
60 <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> 54 <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
61 <rect x="2" y="2" width="7" height="7" rx="1.5"/> 55 <rect x="2" y="2" width="7" height="7" rx="1.5"/>
...@@ -75,10 +69,7 @@ ...@@ -75,10 +69,7 @@
75 <div class="relative group/theme"> 69 <div class="relative group/theme">
76 <button 70 <button
77 onclick={toggleTheme} 71 onclick={toggleTheme}
78 - class="p-2 rounded-full transition-all" 72 + class="nav-icon p-2 rounded-full transition-all cursor-pointer"
79 - style="background-color: transparent; color: var(--theme-texto); cursor: pointer;"
80 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
81 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
82 aria-label={isDark ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro'} 73 aria-label={isDark ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro'}
83 > 74 >
84 {#if isDark} 75 {#if isDark}
...@@ -104,10 +95,7 @@ ...@@ -104,10 +95,7 @@
104 <div class="relative group/menu"> 95 <div class="relative group/menu">
105 <button 96 <button
106 onclick={onOpenDrawer} 97 onclick={onOpenDrawer}
107 - class="p-2 rounded-full transition-all" 98 + class="nav-icon p-2 rounded-full transition-all cursor-pointer"
108 - style="background-color: transparent; color: var(--theme-texto); cursor: pointer;"
109 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
110 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
111 aria-label="Abrir menú" 99 aria-label="Abrir menú"
112 > 100 >
113 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"> 101 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
...@@ -123,3 +111,23 @@ ...@@ -123,3 +111,23 @@
123 111
124 </div> 112 </div>
125 </div> 113 </div>
114 +
115 +<style>
116 + /* Iconos del navbar */
117 + .nav-icon {
118 + background-color: transparent;
119 + color: #8A8578;
120 + transition: color 0.2s ease;
121 + }
122 + .nav-icon:hover {
123 + color: #F5F0E8;
124 + }
125 +
126 + /* Modo claro */
127 + :global(html:not(.dark)) .nav-icon {
128 + color: #333333;
129 + }
130 + :global(html:not(.dark)) .nav-icon:hover {
131 + color: #1A1A18;
132 + }
133 +</style>
......
...@@ -202,15 +202,12 @@ ...@@ -202,15 +202,12 @@
202 {/if} 202 {/if}
203 203
204 <!-- Controles: Theme Toggle + Menú --> 204 <!-- Controles: Theme Toggle + Menú -->
205 - <div class="flex items-center gap-1"> 205 + <div class="header-controls">
206 <!-- Theme Toggle (solo desktop) --> 206 <!-- Theme Toggle (solo desktop) -->
207 <div class="relative group/theme hidden md:block"> 207 <div class="relative group/theme hidden md:block">
208 <button 208 <button
209 onclick={toggleTheme} 209 onclick={toggleTheme}
210 - class="p-2 rounded-full transition-all" 210 + class="nav-icon p-2 rounded-full transition-all"
211 - style="background-color: transparent; color: var(--theme-texto); cursor: pointer;"
212 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
213 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
214 aria-label={isDarkMode ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro'} 211 aria-label={isDarkMode ? 'Cambiar a modo claro' : 'Cambiar a modo oscuro'}
215 > 212 >
216 {#if isDarkMode} 213 {#if isDarkMode}
...@@ -234,10 +231,7 @@ ...@@ -234,10 +231,7 @@
234 <div class="relative group/menu"> 231 <div class="relative group/menu">
235 <button 232 <button
236 onclick={openDrawer} 233 onclick={openDrawer}
237 - class="p-2 rounded-full transition-all" 234 + class="nav-icon p-2 rounded-full transition-all"
238 - style="background-color: transparent; color: var(--theme-texto); cursor: pointer;"
239 - onmouseenter={(e) => e.currentTarget.style.color = 'var(--theme-titulo)'}
240 - onmouseleave={(e) => e.currentTarget.style.color = 'var(--theme-texto)'}
241 aria-label="Abrir menú" 235 aria-label="Abrir menú"
242 > 236 >
243 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"> 237 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
...@@ -465,6 +459,32 @@ ...@@ -465,6 +459,32 @@
465 } 459 }
466 .mefp-logo:hover { opacity: 1; } 460 .mefp-logo:hover { opacity: 1; }
467 461
462 + /* Header controls */
463 + .header-controls {
464 + display: flex;
465 + align-items: center;
466 + gap: 4px;
467 + }
468 +
469 + /* Nav icon styling - matching Navbar */
470 + .nav-icon {
471 + background-color: transparent;
472 + color: #8A8578;
473 + cursor: pointer;
474 + transition: color 0.2s ease;
475 + }
476 + .nav-icon:hover {
477 + color: #F5F0E8;
478 + }
479 +
480 + /* Light mode */
481 + :global(html:not(.dark)) .nav-icon {
482 + color: #333333;
483 + }
484 + :global(html:not(.dark)) .nav-icon:hover {
485 + color: #1A1A18;
486 + }
487 +
468 /* Fade in animation */ 488 /* Fade in animation */
469 .fade-in { 489 .fade-in {
470 opacity: 0; 490 opacity: 0;
...@@ -617,13 +637,33 @@ ...@@ -617,13 +637,33 @@
617 } 637 }
618 .search-bar { 638 .search-bar {
619 display: flex; align-items: center; 639 display: flex; align-items: center;
620 - background: var(--theme-surface); border: 1px solid var(--theme-borde); 640 + background: transparent;
641 + border: none;
642 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
621 border-radius: 12px; padding: 6px 24px; 643 border-radius: 12px; padding: 6px 24px;
622 transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); 644 transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
623 } 645 }
624 - .search-focused { background: var(--theme-fill); } 646 + .search-bar:hover {
647 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15);
648 + }
649 + .search-bar.search-focused,
650 + .search-bar.search-focused:hover {
651 + box-shadow: inset 0 0 0 1px var(--theme-accent);
652 + }
625 .search-has-results { border-radius: 12px 12px 0 0; } 653 .search-has-results { border-radius: 12px 12px 0 0; }
626 654
655 + /* Light mode search bar */
656 + :global(html:not(.dark)) .search-bar {
657 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
658 + }
659 + :global(html:not(.dark)) .search-bar:hover {
660 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15);
661 + }
662 + :global(html:not(.dark)) .search-bar.search-focused,
663 + :global(html:not(.dark)) .search-bar.search-focused:hover {
664 + box-shadow: inset 0 0 0 1px var(--theme-accent);
665 + }
666 +
627 .search-bar input { 667 .search-bar input {
628 flex: 1; background: transparent; border: none; outline: none; 668 flex: 1; background: transparent; border: none; outline: none;
629 color: var(--theme-titulo); font-size: 16px; font-family: 'DM Sans', sans-serif; 669 color: var(--theme-titulo); font-size: 16px; font-family: 'DM Sans', sans-serif;
...@@ -643,10 +683,14 @@ ...@@ -643,10 +683,14 @@
643 683
644 .search-loading { 684 .search-loading {
645 position: absolute; top: 100%; left: 0; right: 0; 685 position: absolute; top: 100%; left: 0; right: 0;
646 - background: var(--theme-surface); border: 1px solid var(--theme-borde); border-top: none; 686 + background: var(--theme-body);
687 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
647 border-radius: 0 0 14px 14px; padding: 12px 24px; 688 border-radius: 0 0 14px 14px; padding: 12px 24px;
648 font-family: 'DM Sans', sans-serif; font-size: 13px; color: var(--theme-texto); 689 font-family: 'DM Sans', sans-serif; font-size: 13px; color: var(--theme-texto);
649 } 690 }
691 + :global(html:not(.dark)) .search-loading {
692 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
693 + }
650 694
651 .search-error { 695 .search-error {
652 margin-top: 8px; font-family: 'DM Sans', sans-serif; 696 margin-top: 8px; font-family: 'DM Sans', sans-serif;
...@@ -656,10 +700,14 @@ ...@@ -656,10 +700,14 @@
656 /* Suggestions / Results */ 700 /* Suggestions / Results */
657 .suggestions { 701 .suggestions {
658 position: absolute; top: 100%; left: 0; right: 0; 702 position: absolute; top: 100%; left: 0; right: 0;
659 - background: var(--theme-surface); border: 1px solid var(--theme-borde); border-top: none; 703 + background: var(--theme-body);
704 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
660 border-radius: 0 0 14px 14px; overflow: hidden; z-index: 100; 705 border-radius: 0 0 14px 14px; overflow: hidden; z-index: 100;
661 max-height: 320px; overflow-y: auto; 706 max-height: 320px; overflow-y: auto;
662 } 707 }
708 + :global(html:not(.dark)) .suggestions {
709 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
710 + }
663 .suggestions.no-results { padding: 16px 24px; } 711 .suggestions.no-results { padding: 16px 24px; }
664 .no-results-text { 712 .no-results-text {
665 font-family: 'DM Sans', sans-serif; font-size: 14px; color: var(--theme-texto); 713 font-family: 'DM Sans', sans-serif; font-size: 14px; color: var(--theme-texto);
...@@ -670,8 +718,11 @@ ...@@ -670,8 +718,11 @@
670 padding: 12px 24px; display: flex; justify-content: space-between; 718 padding: 12px 24px; display: flex; justify-content: space-between;
671 align-items: center; cursor: pointer; transition: background 0.2s; 719 align-items: center; cursor: pointer; transition: background 0.2s;
672 } 720 }
673 - .suggestion-item:hover, .suggestion-selected { background: var(--theme-fill); } 721 + .suggestion-item:hover, .suggestion-selected { background: rgba(255,255,255,0.05); }
674 - .suggestion-border { border-top: 1px solid var(--theme-borde); } 722 + .suggestion-border { border-top: 1px solid rgba(255,255,255,0.06); }
723 + :global(html:not(.dark)) .suggestion-item:hover,
724 + :global(html:not(.dark)) .suggestion-selected { background: rgba(0,0,0,0.03); }
725 + :global(html:not(.dark)) .suggestion-border { border-top: 1px solid rgba(0,0,0,0.06); }
675 726
676 .suggestion-main { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; } 727 .suggestion-main { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
677 .suggestion-label { 728 .suggestion-label {
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
34 let selectedEntity = $state(null); // null = Todo el Estado 34 let selectedEntity = $state(null); // null = Todo el Estado
35 let entitySearchQuery = $state(''); 35 let entitySearchQuery = $state('');
36 let entityDropdownOpen = $state(false); 36 let entityDropdownOpen = $state(false);
37 + let levelDropdownOpen = $state(false);
38 + let yearDropdownOpen = $state(false);
37 39
38 // Estado para modo comparar 40 // Estado para modo comparar
39 let compareA = $state({ year: null, entity: null }); 41 let compareA = $state({ year: null, entity: null });
...@@ -672,6 +674,12 @@ ...@@ -672,6 +674,12 @@
672 if (entityDropdownOpen && !e.target.closest('.entity-dropdown-container')) { 674 if (entityDropdownOpen && !e.target.closest('.entity-dropdown-container')) {
673 entityDropdownOpen = false; 675 entityDropdownOpen = false;
674 } 676 }
677 + if (levelDropdownOpen && !e.target.closest('.level-dropdown-container')) {
678 + levelDropdownOpen = false;
679 + }
680 + if (yearDropdownOpen && !e.target.closest('.year-dropdown-container')) {
681 + yearDropdownOpen = false;
682 + }
675 if (compareADropdownOpen && !e.target.closest('.compare-a-dropdown')) { 683 if (compareADropdownOpen && !e.target.closest('.compare-a-dropdown')) {
676 compareADropdownOpen = false; 684 compareADropdownOpen = false;
677 } 685 }
...@@ -969,41 +977,69 @@ ...@@ -969,41 +977,69 @@
969 {#if viewMode === 'mapa'} 977 {#if viewMode === 'mapa'}
970 <div class="flex flex-wrap items-center gap-3 flex-shrink-0"> 978 <div class="flex flex-wrap items-center gap-3 flex-shrink-0">
971 <!-- Selector de nivel --> 979 <!-- Selector de nivel -->
972 - <div class="flex items-center gap-2"> 980 + <div class="flex items-center gap-2 level-dropdown-container">
973 - <label for="level-select" class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Vista</label> 981 + <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Vista</label>
974 - <select 982 + <div class="relative">
975 - id="level-select" 983 + <button
976 - bind:value={treemapViewLevel} 984 + class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[140px] text-left flex items-center justify-between gap-2"
977 - class="px-3 py-1.5 text-sm rounded-lg focus:outline-none focus:ring-2 cursor-pointer" 985 + onclick={() => { levelDropdownOpen = !levelDropdownOpen; yearDropdownOpen = false; entityDropdownOpen = false; }}
978 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
979 > 986 >
987 + <span class="truncate">{NIVEL_OPTIONS.find(o => o.value === treemapViewLevel)?.label || 'Seleccionar'}</span>
988 + <svg class="w-4 h-4 shrink-0" style="color: var(--theme-texto);" fill="none" stroke="currentColor" viewBox="0 0 24 24">
989 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
990 + </svg>
991 + </button>
992 + {#if levelDropdownOpen}
993 + <div class="dropdown-panel absolute top-full left-0 mt-1 w-52 rounded-lg shadow-lg z-50 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);">
980 {#each NIVEL_OPTIONS as opt} 994 {#each NIVEL_OPTIONS as opt}
981 - <option value={opt.value}>{opt.label}</option> 995 + <button
996 + class="w-full text-left px-3 py-2 text-sm hover:bg-black/5 dark:hover:bg-white/5"
997 + style="{treemapViewLevel === opt.value ? `background-color: color-mix(in srgb, var(--theme-accent) 15%, transparent);` : ``} color: var(--theme-titulo);"
998 + onclick={() => { treemapViewLevel = opt.value; levelDropdownOpen = false; }}
999 + >
1000 + <span class="font-medium">{opt.label}</span>
1001 + <span class="block text-xs mt-0.5" style="color: var(--theme-texto);">{opt.desc}</span>
1002 + </button>
982 {/each} 1003 {/each}
983 - </select> 1004 + </div>
1005 + {/if}
1006 + </div>
984 </div> 1007 </div>
985 1008
986 - <div class="flex items-center gap-2"> 1009 + <div class="flex items-center gap-2 year-dropdown-container">
987 - <label for="year-select" class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Año</label> 1010 + <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Año</label>
988 - <select 1011 + <div class="relative">
989 - id="year-select" 1012 + <button
990 - bind:value={selectedYear} 1013 + class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[80px] text-left flex items-center justify-between gap-2"
991 - class="px-3 py-1.5 text-sm rounded-lg focus:outline-none focus:ring-2 cursor-pointer" 1014 + onclick={() => { yearDropdownOpen = !yearDropdownOpen; levelDropdownOpen = false; entityDropdownOpen = false; }}
992 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
993 > 1015 >
1016 + <span>{selectedYear || 'Año'}</span>
1017 + <svg class="w-4 h-4 shrink-0" style="color: var(--theme-texto);" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1018 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
1019 + </svg>
1020 + </button>
1021 + {#if yearDropdownOpen}
1022 + <div class="dropdown-panel absolute top-full left-0 mt-1 w-24 rounded-lg shadow-lg z-50 max-h-64 overflow-y-auto" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);">
994 {#each availableYears as year} 1023 {#each availableYears as year}
995 - <option value={year}>{year}</option> 1024 + <button
1025 + class="w-full text-left px-3 py-2 text-sm hover:bg-black/5 dark:hover:bg-white/5"
1026 + style="{selectedYear === year ? `background-color: color-mix(in srgb, var(--theme-accent) 15%, transparent);` : ``} color: var(--theme-titulo);"
1027 + onclick={() => { selectedYear = year; yearDropdownOpen = false; }}
1028 + >
1029 + {year}
1030 + </button>
996 {/each} 1031 {/each}
997 - </select> 1032 + </div>
1033 + {/if}
1034 + </div>
998 </div> 1035 </div>
999 1036
1000 <div class="flex items-center gap-2 entity-dropdown-container"> 1037 <div class="flex items-center gap-2 entity-dropdown-container">
1001 <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Entidad</label> 1038 <label class="text-xs uppercase tracking-wide" style="font-family: 'DM Mono', monospace; color: var(--theme-texto);">Entidad</label>
1002 <div class="relative"> 1039 <div class="relative">
1003 <button 1040 <button
1004 - class="px-3 py-1.5 text-sm rounded-lg focus:outline-none focus:ring-2 min-w-[180px] text-left flex items-center justify-between gap-2" 1041 + class="dropdown-control px-3 py-1.5 text-sm rounded-lg focus:outline-none min-w-[180px] text-left flex items-center justify-between gap-2"
1005 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);" 1042 + onclick={() => { entityDropdownOpen = !entityDropdownOpen; levelDropdownOpen = false; yearDropdownOpen = false; }}
1006 - onclick={() => entityDropdownOpen = !entityDropdownOpen}
1007 > 1043 >
1008 <span class="truncate"> 1044 <span class="truncate">
1009 {#if selectedEntity} 1045 {#if selectedEntity}
...@@ -1018,7 +1054,7 @@ ...@@ -1018,7 +1054,7 @@
1018 </button> 1054 </button>
1019 1055
1020 {#if entityDropdownOpen} 1056 {#if entityDropdownOpen}
1021 - <div class="absolute top-full left-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);"> 1057 + <div class="dropdown-panel absolute top-full right-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);">
1022 <div class="p-2 border-b" style="border-color: var(--theme-borde);"> 1058 <div class="p-2 border-b" style="border-color: var(--theme-borde);">
1023 <input 1059 <input
1024 type="text" 1060 type="text"
...@@ -1703,8 +1739,7 @@ ...@@ -1703,8 +1739,7 @@
1703 <!-- Año A --> 1739 <!-- Año A -->
1704 <select 1740 <select
1705 bind:value={compareA.year} 1741 bind:value={compareA.year}
1706 - class="px-3 py-2 text-sm rounded-lg focus:outline-none focus:ring-2 cursor-pointer font-medium" 1742 + class="dropdown-control px-3 py-2 text-sm rounded-lg focus:outline-none cursor-pointer font-medium"
1707 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
1708 > 1743 >
1709 {#each availableYears as year} 1744 {#each availableYears as year}
1710 <option value={year}>{year}</option> 1745 <option value={year}>{year}</option>
...@@ -1714,8 +1749,7 @@ ...@@ -1714,8 +1749,7 @@
1714 <!-- Entidad A --> 1749 <!-- Entidad A -->
1715 <div class="relative compare-a-dropdown"> 1750 <div class="relative compare-a-dropdown">
1716 <button 1751 <button
1717 - class="px-3 py-2 text-sm rounded-lg focus:outline-none focus:ring-2 min-w-[200px] text-left flex items-center justify-between gap-2" 1752 + class="dropdown-control px-3 py-2 text-sm rounded-lg focus:outline-none min-w-[200px] text-left flex items-center justify-between gap-2"
1718 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
1719 onclick={() => compareADropdownOpen = !compareADropdownOpen} 1753 onclick={() => compareADropdownOpen = !compareADropdownOpen}
1720 > 1754 >
1721 <span class="truncate font-medium"> 1755 <span class="truncate font-medium">
...@@ -1731,7 +1765,7 @@ ...@@ -1731,7 +1765,7 @@
1731 </button> 1765 </button>
1732 1766
1733 {#if compareADropdownOpen} 1767 {#if compareADropdownOpen}
1734 - <div class="absolute top-full left-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);"> 1768 + <div class="dropdown-panel absolute top-full left-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);">
1735 <div class="p-2 border-b" style="border-color: var(--theme-borde);"> 1769 <div class="p-2 border-b" style="border-color: var(--theme-borde);">
1736 <input 1770 <input
1737 type="text" 1771 type="text"
...@@ -1803,8 +1837,7 @@ ...@@ -1803,8 +1837,7 @@
1803 <!-- Año B --> 1837 <!-- Año B -->
1804 <select 1838 <select
1805 bind:value={compareB.year} 1839 bind:value={compareB.year}
1806 - class="px-3 py-2 text-sm rounded-lg focus:outline-none focus:ring-2 cursor-pointer font-medium" 1840 + class="dropdown-control px-3 py-2 text-sm rounded-lg focus:outline-none cursor-pointer font-medium"
1807 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
1808 > 1841 >
1809 {#each availableYears as year} 1842 {#each availableYears as year}
1810 <option value={year}>{year}</option> 1843 <option value={year}>{year}</option>
...@@ -1814,8 +1847,7 @@ ...@@ -1814,8 +1847,7 @@
1814 <!-- Entidad B --> 1847 <!-- Entidad B -->
1815 <div class="relative compare-b-dropdown"> 1848 <div class="relative compare-b-dropdown">
1816 <button 1849 <button
1817 - class="px-3 py-2 text-sm rounded-lg focus:outline-none focus:ring-2 min-w-[200px] text-left flex items-center justify-between gap-2" 1850 + class="dropdown-control px-3 py-2 text-sm rounded-lg focus:outline-none min-w-[200px] text-left flex items-center justify-between gap-2"
1818 - style="border: 1px solid var(--theme-borde); background-color: var(--theme-surface); color: var(--theme-titulo);"
1819 onclick={() => compareBDropdownOpen = !compareBDropdownOpen} 1851 onclick={() => compareBDropdownOpen = !compareBDropdownOpen}
1820 > 1852 >
1821 <span class="truncate font-medium"> 1853 <span class="truncate font-medium">
...@@ -1831,7 +1863,7 @@ ...@@ -1831,7 +1863,7 @@
1831 </button> 1863 </button>
1832 1864
1833 {#if compareBDropdownOpen} 1865 {#if compareBDropdownOpen}
1834 - <div class="absolute top-full left-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);"> 1866 + <div class="dropdown-panel absolute top-full left-0 mt-1 w-80 rounded-lg shadow-lg z-50 max-h-72 overflow-hidden" style="background-color: var(--theme-surface); border: 1px solid var(--theme-borde);">
1835 <div class="p-2 border-b" style="border-color: var(--theme-borde);"> 1867 <div class="p-2 border-b" style="border-color: var(--theme-borde);">
1836 <input 1868 <input
1837 type="text" 1869 type="text"
...@@ -1961,6 +1993,41 @@ ...@@ -1961,6 +1993,41 @@
1961 {/if} 1993 {/if}
1962 1994
1963 <style> 1995 <style>
1996 + /* Dropdown controls - tema oscuro por defecto */
1997 + .dropdown-control {
1998 + border: 1px solid var(--theme-borde);
1999 + background-color: var(--theme-surface);
2000 + color: var(--theme-titulo);
2001 + transition: box-shadow 0.2s ease, border-color 0.2s ease;
2002 + }
2003 + .dropdown-control:focus {
2004 + box-shadow: 0 0 0 2px var(--theme-accent);
2005 + }
2006 +
2007 + /* Tema claro: transparente + ring sutil */
2008 + :global(.light) .dropdown-control,
2009 + :global(html:not(.dark)) .dropdown-control {
2010 + background: transparent;
2011 + border: none;
2012 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
2013 + }
2014 + :global(.light) .dropdown-control:focus,
2015 + :global(html:not(.dark)) .dropdown-control:focus {
2016 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15);
2017 + }
2018 + :global(.light) .dropdown-control:hover,
2019 + :global(html:not(.dark)) .dropdown-control:hover {
2020 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12);
2021 + }
2022 +
2023 + /* Dropdown panels en tema claro */
2024 + :global(.light) .dropdown-panel,
2025 + :global(html:not(.dark)) .dropdown-panel {
2026 + background: #FAFAF8 !important;
2027 + border: none !important;
2028 + box-shadow: 0 4px 20px rgba(0,0,0,0.08), inset 0 0 0 1px rgba(0,0,0,0.08) !important;
2029 + }
2030 +
1964 /* Sidebar izquierdo: con fondo en móvil, transparente en desktop */ 2031 /* Sidebar izquierdo: con fondo en móvil, transparente en desktop */
1965 .sidebar-left { 2032 .sidebar-left {
1966 background-color: var(--theme-surface); 2033 background-color: var(--theme-surface);
......
...@@ -2,292 +2,384 @@ ...@@ -2,292 +2,384 @@
2 import { onMount } from 'svelte'; 2 import { onMount } from 'svelte';
3 import { tweened } from 'svelte/motion'; 3 import { tweened } from 'svelte/motion';
4 import { cubicOut } from 'svelte/easing'; 4 import { cubicOut } from 'svelte/easing';
5 + import { supabase } from '$lib/supabase';
6 +
7 + // ══════════════════════════════════════════════════════════════
8 + // ESTADO GENERAL
9 + // ══════════════════════════════════════════════════════════════
10 + let mounted = $state(false);
11 + let loading = $state(true);
12 + let hoveredYear = $state(null);
13 + let showInfo = $state(false);
14 + let vista = $state('agregado'); // 'agregado' | 'comparar'
15 + let isDark = $state(true);
16 +
17 + // Objeto actual (hardcoded por ahora, luego vendrá de URL)
18 + const objetoCodigo = '10000';
19 + const objetoNivel = 'grupo';
20 +
21 + // Info del objeto
22 + let objeto = $state({
23 + codigo: '10000',
24 + nombre: 'Servicios Personales',
25 + nivel: 'Grupo',
26 + años: '2005-2025',
27 + descripcion: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente del sector público.'
28 + });
5 29
6 - let mounted = false; 30 + // ══════════════════════════════════════════════════════════════
7 - let hoveredYear = null; 31 + // ESTADO SELECTOR DE ENTIDAD
8 - let hoveredYearComparar = null; 32 + // ══════════════════════════════════════════════════════════════
9 - let showInfo = false; 33 + let entidades = $state([]); // Lista de entidades disponibles
10 - let vista = 'agregado'; // 'agregado' | 'comparar' 34 + let selectedEntity = $state(null); // null = Todo el Estado
11 - let isDark = true; 35 + let entitySearchQuery = $state('');
12 - let entidadA = 0; 36 + let entityDropdownOpen = $state(false);
13 - let entidadB = 1; 37 +
38 + // Filtrar entidades por búsqueda
39 + let filteredEntities = $derived.by(() => {
40 + if (!entitySearchQuery || entitySearchQuery.length < 2) {
41 + return entidades.slice(0, 20);
42 + }
43 + const q = entitySearchQuery.toLowerCase();
44 + return entidades
45 + .filter(e =>
46 + e.entidad_desc?.toLowerCase().includes(q) ||
47 + e.entidad?.toString().includes(q)
48 + )
49 + .slice(0, 30);
50 + });
14 51
15 - onMount(() => { 52 + function selectEntity(entity) {
16 - setTimeout(() => { mounted = true; }, 50); 53 + selectedEntity = entity;
54 + entityDropdownOpen = false;
55 + entitySearchQuery = '';
56 + loadData();
57 + }
58 +
59 + function clearEntity() {
60 + selectedEntity = null;
61 + entityDropdownOpen = false;
62 + entitySearchQuery = '';
63 + loadData();
64 + }
65 +
66 + // ══════════════════════════════════════════════════════════════
67 + // DATOS DE SUPABASE
68 + // ══════════════════════════════════════════════════════════════
69 + let datosAnuales = $state([]); // Datos por año
70 + let datosHistorico = $state(null); // Fila con gestion=0
71 + let topEntidadesPorAño = $state({}); // Top 3 por año (solo para Todo el Estado)
72 +
73 + // Cargar datos según selección
74 + async function loadData() {
75 + loading = true;
76 +
77 + if (selectedEntity) {
78 + // Cargar desde vista_objeto_entidad
79 + const { data, error } = await supabase
80 + .schema('ppto')
81 + .from('vista_objeto_entidad')
82 + .select('*')
83 + .eq('objeto', objetoCodigo)
84 + .eq('nivel', objetoNivel)
85 + .eq('entidad', selectedEntity.entidad)
86 + .order('gestion');
87 +
88 + if (data) {
89 + datosHistorico = data.find(d => d.gestion === 0) || null;
90 + datosAnuales = data.filter(d => d.gestion > 0);
91 + topEntidadesPorAño = {}; // No hay top entidades cuando se filtra por entidad
92 + }
93 + } else {
94 + // Cargar desde vista_objeto_estado (Todo el Estado)
95 + const { data, error } = await supabase
96 + .schema('ppto')
97 + .from('vista_objeto_estado')
98 + .select('*')
99 + .eq('objeto', objetoCodigo)
100 + .order('gestion');
101 +
102 + if (data) {
103 + datosHistorico = data.find(d => d.gestion === 0) || null;
104 + datosAnuales = data.filter(d => d.gestion > 0);
105 +
106 + // Construir top entidades por año
107 + topEntidadesPorAño = {};
108 + datosAnuales.forEach(d => {
109 + topEntidadesPorAño[d.gestion] = [
110 + { nombre: d.top1_entidad, monto: d.top1_monto, porcentaje: d.top1_pct },
111 + { nombre: d.top2_entidad, monto: d.top2_monto, porcentaje: d.top2_pct },
112 + { nombre: d.top3_entidad, monto: d.top3_monto, porcentaje: d.top3_pct },
113 + ].filter(e => e.nombre);
114 + });
115 + }
116 + }
17 117
18 - // Leer estado del tema 118 + loading = false;
19 - isDark = document.documentElement.classList.contains('dark'); 119 + }
20 120
21 - // Observar cambios de tema 121 + // Cargar lista de entidades disponibles para este objeto
22 - const observer = new MutationObserver(() => { 122 + async function loadEntidades() {
23 - isDark = document.documentElement.classList.contains('dark'); 123 + const { data, error } = await supabase
24 - }); 124 + .schema('ppto')
25 - observer.observe(document.documentElement, { 125 + .from('entidades_por_objeto')
26 - attributes: true, 126 + .select('entidad, entidad_desc')
27 - attributeFilter: ['class'] 127 + .eq('objeto', objetoCodigo)
28 - }); 128 + .eq('nivel', objetoNivel)
129 + .order('entidad_desc');
29 130
30 - return () => observer.disconnect(); 131 + if (data) {
31 - }); 132 + entidades = data;
133 + }
134 + }
32 135
136 + // ══════════════════════════════════════════════════════════════
137 + // VALORES DERIVADOS
138 + // ══════════════════════════════════════════════════════════════
33 139
34 - // Dummy data 140 + // Datos para el gráfico (monto o per_capita según preferencia)
35 - const objeto = { 141 + let gastoPerCapita = $derived(
36 - codigo: '10000', 142 + datosAnuales.map(d => ({
37 - nombre: 'Servicios Personales', 143 + año: d.gestion,
38 - nivel: 'Grupo', 144 + monto: selectedEntity ? d.monto : d.total,
39 - años: '2005-2024', 145 + perCapita: d.per_capita
40 - descripcion: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente del sector público.', 146 + }))
41 - variaciones: [ 147 + );
42 - { rango: '2012-2024', texto: 'Gastos por concepto de servicios prestados por el personal permanente y no permanente.' },
43 - { rango: '2005-2011', texto: 'Retribuciones y complementos al personal permanente y eventual.' }
44 - ]
45 - };
46 148
47 - // Dummy spending data (in millions Bs, population in millions) 149 + let maxPerCapita = $derived(
48 - const gastoAnual = [ 150 + gastoPerCapita.length > 0 ? Math.max(...gastoPerCapita.map(g => g.perCapita)) : 1
49 - { año: 2005, monto: 8200, poblacion: 9.4 }, 151 + );
50 - { año: 2006, monto: 8900, poblacion: 9.5 },
51 - { año: 2007, monto: 10200, poblacion: 9.6 },
52 - { año: 2008, monto: 12400, poblacion: 9.8 },
53 - { año: 2009, monto: 13100, poblacion: 9.9 },
54 - { año: 2010, monto: 14800, poblacion: 10.0 },
55 - { año: 2011, monto: 17200, poblacion: 10.2 },
56 - { año: 2012, monto: 19500, poblacion: 10.4 },
57 - { año: 2013, monto: 22800, poblacion: 10.5 },
58 - { año: 2014, monto: 28400, poblacion: 10.7 },
59 - { año: 2015, monto: 31200, poblacion: 10.9 },
60 - { año: 2016, monto: 33100, poblacion: 11.0 },
61 - { año: 2017, monto: 35600, poblacion: 11.2 },
62 - { año: 2018, monto: 38200, poblacion: 11.4 },
63 - { año: 2019, monto: 41500, poblacion: 11.5 },
64 - { año: 2020, monto: 39800, poblacion: 11.7 },
65 - { año: 2021, monto: 42100, poblacion: 11.8 },
66 - { año: 2022, monto: 45600, poblacion: 12.0 },
67 - { año: 2023, monto: 48900, poblacion: 12.1 },
68 - { año: 2024, monto: 52300, poblacion: 12.3 },
69 - ];
70 152
71 - // Calculate per capita for each year 153 + // Histórico
72 - const gastoPerCapita = gastoAnual.map(g => ({ 154 + let totalHistorico = $derived(datosHistorico ? (selectedEntity ? datosHistorico.monto : datosHistorico.total) : 0);
73 - ...g, 155 + let promedioPerCapita = $derived(datosHistorico?.per_capita || 0);
74 - perCapita: Math.round(g.monto / g.poblacion) 156 + let totalPorcentaje = $derived(datosHistorico?.prop || 0);
75 - })); 157 + let ranking = $derived(datosHistorico?.ranking || 0);
76 - 158 + let nPares = $derived(datosHistorico?.n_pares || 0);
77 - // Top entidades por año (dummy data) 159 + let nEntidades = $derived(datosHistorico?.n_entidades || 0);
78 - const topEntidadesPorAño = { 160 + // Número de entidades - cambia según el año seleccionado y la entidad seleccionada
79 - 2020: [ 161 + let totalEntidadesHistorico = $derived.by(() => {
80 - { nombre: 'Ministerio de Educación', monto: 12500, porcentaje: 31.4 }, 162 + if (selectedEntity && datosAnuales.length > 0) {
81 - { nombre: 'Ministerio de Salud', monto: 7800, porcentaje: 19.6 }, 163 + // Para entidad seleccionada: promedio de n_entidades de los años que tiene esta entidad
82 - { nombre: 'Policía Boliviana', monto: 4200, porcentaje: 10.6 }, 164 + // Esto refleja cuántas entidades participaron durante el período de vida de esta entidad
83 - ], 165 + const suma = datosAnuales.reduce((acc, d) => acc + (d.n_entidades || 0), 0);
84 - 2021: [ 166 + return Math.round(suma / datosAnuales.length);
85 - { nombre: 'Ministerio de Educación', monto: 13200, porcentaje: 31.4 }, 167 + }
86 - { nombre: 'Ministerio de Salud', monto: 8100, porcentaje: 19.2 }, 168 + // Para "Todo el Estado": usar la lista del dropdown o el histórico
87 - { nombre: 'Policía Boliviana', monto: 4600, porcentaje: 10.9 }, 169 + return entidades.length || nEntidades;
88 - ], 170 + });
89 - 2022: [
90 - { nombre: 'Ministerio de Educación', monto: 14800, porcentaje: 32.5 },
91 - { nombre: 'Ministerio de Salud', monto: 8400, porcentaje: 18.4 },
92 - { nombre: 'Policía Boliviana', monto: 5100, porcentaje: 11.2 },
93 - ],
94 - 2023: [
95 - { nombre: 'Ministerio de Educación', monto: 16200, porcentaje: 33.1 },
96 - { nombre: 'Ministerio de Salud', monto: 8700, porcentaje: 17.8 },
97 - { nombre: 'Policía Boliviana', monto: 5700, porcentaje: 11.7 },
98 - ],
99 - 2024: [
100 - { nombre: 'Ministerio de Educación', monto: 18200, porcentaje: 34.8 },
101 - { nombre: 'Ministerio de Salud', monto: 8900, porcentaje: 17.0 },
102 - { nombre: 'Policía Boliviana', monto: 6200, porcentaje: 11.9 },
103 - ],
104 - };
105 171
106 - // Total histórico (para cuando no hay hover) 172 + let totalEntidades = $derived.by(() => {
107 - const topEntidadesTotal = [ 173 + if (hoveredYear) {
108 - { nombre: 'Ministerio de Educación', monto: 74900, porcentaje: 32.6 }, 174 + // Buscar n_entidades del año específico
109 - { nombre: 'Ministerio de Salud', monto: 41900, porcentaje: 18.4 }, 175 + const añoData = datosAnuales.find(d => d.gestion === hoveredYear);
110 - { nombre: 'Policía Boliviana', monto: 25800, porcentaje: 11.3 }, 176 + return añoData?.n_entidades || totalEntidadesHistorico;
111 - ]; 177 + }
178 + return totalEntidadesHistorico;
179 + });
112 180
113 - // Datos para comparación (dummy) 181 + // Para el ranking: valores reactivos según hover
114 - const entidadesComparables = [ 182 + let displayRankingValue = $derived.by(() => {
115 - { 183 + if (hoveredYear) {
116 - id: 0, 184 + const añoData = datosAnuales.find(d => d.gestion === hoveredYear);
117 - nombre: 'Ministerio de Educación', 185 + return añoData?.ranking || ranking;
118 - color: '#E8C547',
119 - datos: [
120 - { año: 2020, monto: 12500, perCapita: 1068 },
121 - { año: 2021, monto: 13200, perCapita: 1119 },
122 - { año: 2022, monto: 14800, perCapita: 1233 },
123 - { año: 2023, monto: 16200, perCapita: 1339 },
124 - { año: 2024, monto: 18200, perCapita: 1480 },
125 - ],
126 - total: 74900,
127 - promedioPerCapita: 1248,
128 - porcentajeObjeto: 34.8,
129 - ranking: 1
130 - },
131 - {
132 - id: 1,
133 - nombre: 'Ministerio de Salud',
134 - color: '#4A8B6E',
135 - datos: [
136 - { año: 2020, monto: 6800, perCapita: 581 },
137 - { año: 2021, monto: 7200, perCapita: 610 },
138 - { año: 2022, monto: 7900, perCapita: 658 },
139 - { año: 2023, monto: 8400, perCapita: 694 },
140 - { año: 2024, monto: 8900, perCapita: 724 },
141 - ],
142 - total: 39200,
143 - promedioPerCapita: 653,
144 - porcentajeObjeto: 17.0,
145 - ranking: 2
146 - },
147 - {
148 - id: 2,
149 - nombre: 'Policía Boliviana',
150 - color: '#8B5CF6',
151 - datos: [
152 - { año: 2020, monto: 4200, perCapita: 359 },
153 - { año: 2021, monto: 4600, perCapita: 390 },
154 - { año: 2022, monto: 5100, perCapita: 425 },
155 - { año: 2023, monto: 5700, perCapita: 471 },
156 - { año: 2024, monto: 6200, perCapita: 504 },
157 - ],
158 - total: 25800,
159 - promedioPerCapita: 430,
160 - porcentajeObjeto: 11.9,
161 - ranking: 3
162 - },
163 - {
164 - id: 3,
165 - nombre: 'Gobierno Autónomo de Santa Cruz',
166 - color: '#F97316',
167 - datos: [
168 - { año: 2020, monto: 3100, perCapita: 265 },
169 - { año: 2021, monto: 3400, perCapita: 288 },
170 - { año: 2022, monto: 3800, perCapita: 317 },
171 - { año: 2023, monto: 4100, perCapita: 339 },
172 - { año: 2024, monto: 4500, perCapita: 366 },
173 - ],
174 - total: 18900,
175 - promedioPerCapita: 315,
176 - porcentajeObjeto: 8.6,
177 - ranking: 4
178 } 186 }
179 - ]; 187 + return ranking;
188 + });
180 189
181 - // Colores fijos por posición - teal y naranja 190 + let rankingDenominador = $derived.by(() => {
182 - $: colorA = '#2a9d8f'; // teal 191 + if (selectedEntity) {
183 - $: colorB = '#f4a261'; // naranja 192 + // Para entidad: usar n_entidades del año específico o del histórico
193 + if (hoveredYear) {
194 + const añoData = datosAnuales.find(d => d.gestion === hoveredYear);
195 + return añoData?.n_entidades || nEntidades;
196 + }
197 + // Sin hover: usar el n_entidades del histórico (total de entidades únicas del período)
198 + return nEntidades;
199 + }
200 + // Para "Todo el Estado": comparación entre objetos del mismo nivel
201 + return nPares;
202 + });
184 203
185 - $: entA = entidadesComparables[entidadA]; 204 + // Top entidades histórico (solo para Todo el Estado)
186 - $: entB = entidadesComparables[entidadB]; 205 + let topEntidadesTotal = $derived(
187 - $: maxComparacion = Math.max( 206 + datosHistorico && !selectedEntity ? [
188 - ...entA.datos.map(d => d.perCapita), 207 + { nombre: datosHistorico.top1_entidad, monto: datosHistorico.top1_monto, porcentaje: datosHistorico.top1_pct },
189 - ...entB.datos.map(d => d.perCapita) 208 + { nombre: datosHistorico.top2_entidad, monto: datosHistorico.top2_monto, porcentaje: datosHistorico.top2_pct },
209 + { nombre: datosHistorico.top3_entidad, monto: datosHistorico.top3_monto, porcentaje: datosHistorico.top3_pct },
210 + ].filter(e => e.nombre) : []
190 ); 211 );
191 - $: años = entA.datos.map(d => d.año);
192 -
193 - // Datos de comparación según hover
194 - $: hoveredDataA = hoveredYearComparar
195 - ? entA.datos.find(d => d.año === hoveredYearComparar)
196 - : null;
197 - $: hoveredDataB = hoveredYearComparar
198 - ? entB.datos.find(d => d.año === hoveredYearComparar)
199 - : null;
200 212
201 - $: displayMontoA = hoveredDataA ? hoveredDataA.monto : entA.total; 213 + // Años disponibles
202 - $: displayMontoLabelA = hoveredDataA ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; 214 + let primerAño = $derived(gastoPerCapita.length > 0 ? gastoPerCapita[0].año : 2005);
203 - $: displayPerCapitaA = hoveredDataA ? hoveredDataA.perCapita : entA.promedioPerCapita; 215 + let ultimoAño = $derived(gastoPerCapita.length > 0 ? gastoPerCapita[gastoPerCapita.length - 1].año : 2025);
204 - $: displayPerCapitaLabelA = hoveredDataA ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)';
205 216
206 - $: displayMontoB = hoveredDataB ? hoveredDataB.monto : entB.total; 217 + // Current view data (changes on hover)
207 - $: displayMontoLabelB = hoveredDataB ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; 218 + let currentData = $derived(
208 - $: displayPerCapitaB = hoveredDataB ? hoveredDataB.perCapita : entB.promedioPerCapita; 219 + hoveredYear ? gastoPerCapita.find(g => g.año === hoveredYear) : null
209 - $: displayPerCapitaLabelB = hoveredDataB ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)'; 220 + );
210 221
211 - $: displayPeriodoComparar = hoveredYearComparar ? hoveredYearComparar : '2020-2024'; 222 + let displayMonto = $derived(currentData ? currentData.monto : totalHistorico);
223 + let displayMontoLabel = $derived(currentData ? `gasto en ${currentData.año}` : `gasto ${primerAño}-${ultimoAño}`);
224 + let displayPerCapita = $derived(currentData ? currentData.perCapita : promedioPerCapita);
225 + let displayPerCapitaLabel = $derived(currentData ? `por persona en ${currentData.año}` : 'por persona (promedio)');
226 + let displayPorcentaje = $derived(
227 + currentData && totalHistorico > 0
228 + ? (totalPorcentaje * (currentData.monto / (totalHistorico / gastoPerCapita.length))).toFixed(1)
229 + : totalPorcentaje
230 + );
231 + let displayPorcentajeLabel = $derived(currentData ? `del gasto ${currentData.año}` : 'del gasto total');
232 + let displayPeriodo = $derived(currentData ? currentData.año : `${primerAño}-${ultimoAño}`);
233 + let displayRanking = $derived(`${displayRankingValue} de ${rankingDenominador}`);
234 + let displayRankingLabel = $derived(
235 + selectedEntity
236 + ? (currentData ? `entre las que más gastan en ${currentData.año}` : 'entre las que más gastan')
237 + : (currentData ? `ranking en ${currentData.año}` : 'ranking entre grupos')
238 + );
212 239
213 // Top entidades reactivo según hover 240 // Top entidades reactivo según hover
214 - $: displayTopEntidades = hoveredYear && topEntidadesPorAño[hoveredYear] 241 + let displayTopEntidades = $derived(
242 + hoveredYear && topEntidadesPorAño[hoveredYear]
215 ? topEntidadesPorAño[hoveredYear] 243 ? topEntidadesPorAño[hoveredYear]
216 - : topEntidadesTotal; 244 + : topEntidadesTotal
217 - $: displayTopLabel = hoveredYear ? `en ${hoveredYear}` : '2005-2024'; 245 + );
218 - 246 + let displayTopLabel = $derived(hoveredYear ? `en ${hoveredYear}` : `${primerAño}-${ultimoAño}`);
219 - // Context data
220 - const totalEntidades = 620;
221 - const totalGrupos = 9; // Total grupos in objeto del gasto
222 -
223 - // Calculations
224 - const maxPerCapita = Math.max(...gastoPerCapita.map(g => g.perCapita));
225 - const minPerCapita = Math.min(...gastoPerCapita.map(g => g.perCapita));
226 - const totalHistorico = gastoAnual.reduce((sum, g) => sum + g.monto, 0);
227 - const totalPoblacionAcum = gastoAnual.reduce((sum, g) => sum + g.poblacion, 0);
228 - const promedioPerCapita = Math.round(totalHistorico / totalPoblacionAcum);
229 - const totalPorcentaje = 14.2;
230 - const ranking = 1;
231 247
232 - // Current view data (changes on hover) 248 + // ══════════════════════════════════════════════════════════════
233 - $: currentData = hoveredYear 249 + // TWEENED VALUES
234 - ? gastoPerCapita.find(g => g.año === hoveredYear) 250 + // ══════════════════════════════════════════════════════════════
235 - : null;
236 -
237 - $: displayMonto = currentData ? currentData.monto : totalHistorico;
238 - $: displayMontoLabel = currentData ? `gasto en ${currentData.año}` : 'gasto 2005-2024';
239 - $: displayPerCapita = currentData ? currentData.perCapita : promedioPerCapita;
240 - $: displayPerCapitaLabel = currentData ? `por persona en ${currentData.año}` : 'por persona (promedio)';
241 - $: displayPorcentaje = currentData
242 - ? (totalPorcentaje * (currentData.monto / (totalHistorico / gastoAnual.length))).toFixed(1)
243 - : totalPorcentaje;
244 - $: displayPorcentajeLabel = currentData ? `del gasto ${currentData.año}` : 'del gasto total';
245 - $: displayPeriodo = currentData ? currentData.año : '2005-2024';
246 - $: displayRanking = `${ranking} de ${totalGrupos}`;
247 - $: displayRankingLabel = currentData ? `ranking en ${currentData.año}` : 'ranking entre grupos';
248 -
249 - // Tweened values para transiciones suaves
250 const tweenOptions = { duration: 300, easing: cubicOut }; 251 const tweenOptions = { duration: 300, easing: cubicOut };
251 252
252 - // Agregado 253 + const twMonto = tweened(0, tweenOptions);
253 - const twMonto = tweened(totalHistorico, tweenOptions); 254 + const twPerCapita = tweened(0, tweenOptions);
254 - const twPerCapita = tweened(promedioPerCapita, tweenOptions); 255 + const twPorcentaje = tweened(0, tweenOptions);
255 - const twPorcentaje = tweened(totalPorcentaje, tweenOptions); 256 + const twTop1 = tweened(0, tweenOptions);
257 + const twTop2 = tweened(0, tweenOptions);
258 + const twTop3 = tweened(0, tweenOptions);
256 259
257 - // Comparación 260 + // Comparación (placeholder por ahora - TODO: implementar)
258 const twMontoA = tweened(0, tweenOptions); 261 const twMontoA = tweened(0, tweenOptions);
259 const twMontoB = tweened(0, tweenOptions); 262 const twMontoB = tweened(0, tweenOptions);
260 const twPerCapitaA = tweened(0, tweenOptions); 263 const twPerCapitaA = tweened(0, tweenOptions);
261 const twPerCapitaB = tweened(0, tweenOptions); 264 const twPerCapitaB = tweened(0, tweenOptions);
262 265
263 - // Top entidades 266 + let hoveredYearComparar = $state(null);
264 - const twTop1 = tweened(0, tweenOptions); 267 + let entidadA = $state(0);
265 - const twTop2 = tweened(0, tweenOptions); 268 + let entidadB = $state(1);
266 - const twTop3 = tweened(0, tweenOptions); 269 + let displayPeriodoComparar = $derived(
270 + hoveredYearComparar ? hoveredYearComparar : `${años[0]}-${años[años.length - 1]}`
271 + );
272 +
273 + // Datos dummy para comparación (TODO: conectar a Supabase)
274 + const entidadesComparables = [
275 + { id: 0, nombre: 'Entidad A', datos: [], porcentajeObjeto: 0, ranking: 1 },
276 + { id: 1, nombre: 'Entidad B', datos: [], porcentajeObjeto: 0, ranking: 2 }
277 + ];
278 + let entA = $derived(entidadesComparables[entidadA] || entidadesComparables[0]);
279 + let entB = $derived(entidadesComparables[entidadB] || entidadesComparables[1]);
280 + let maxComparacion = $state(100);
281 + let años = $state([2020, 2021, 2022, 2023, 2024]);
282 + let colorA = '#2a9d8f';
283 + let colorB = '#f4a261';
284 +
285 + // Helper para asegurar que siempre tengamos un número
286 + function toNumber(val) {
287 + const n = parseFloat(val);
288 + return isNaN(n) ? 0 : n;
289 + }
267 290
268 // Actualizar tweened cuando cambian los valores 291 // Actualizar tweened cuando cambian los valores
269 - $: twMonto.set(displayMonto); 292 + // Leemos TODAS las dependencias explícitamente para que el effect se re-ejecute
270 - $: twPerCapita.set(displayPerCapita); 293 + $effect(() => {
271 - $: twPorcentaje.set(parseFloat(displayPorcentaje)); 294 + // Leer todas las dependencias primero (para que Svelte las trackee)
295 + const year = hoveredYear;
296 + const datos = gastoPerCapita; // Leer siempre
297 + const histTotal = toNumber(totalHistorico);
298 + const histPerCapita = toNumber(promedioPerCapita);
299 + const histPorcentaje = toNumber(totalPorcentaje);
300 +
301 + // Calcular valores
302 + const data = year ? datos.find(g => g.año === year) : null;
303 + const monto = toNumber(data ? data.monto : histTotal);
304 + const perCapita = toNumber(data ? data.perCapita : histPerCapita);
305 + const porcentaje = data && histTotal > 0
306 + ? toNumber((histPorcentaje * (data.monto / (histTotal / datos.length))).toFixed(1))
307 + : histPorcentaje;
308 +
309 + twMonto.set(monto);
310 + twPerCapita.set(perCapita);
311 + twPorcentaje.set(porcentaje);
312 + });
313 +
314 + $effect(() => {
315 + // Leer todas las dependencias primero
316 + const year = hoveredYear;
317 + const topsPorAño = topEntidadesPorAño;
318 + const topsTotal = topEntidadesTotal;
319 +
320 + const tops = year && topsPorAño[year] ? topsPorAño[year] : topsTotal;
321 +
322 + twTop1.set(toNumber(tops?.[0]?.porcentaje));
323 + twTop2.set(toNumber(tops?.[1]?.porcentaje));
324 + twTop3.set(toNumber(tops?.[2]?.porcentaje));
325 + });
326 +
327 + // ══════════════════════════════════════════════════════════════
328 + // MOUNT
329 + // ══════════════════════════════════════════════════════════════
330 + onMount(async () => {
331 + setTimeout(() => { mounted = true; }, 50);
332 +
333 + // Leer estado del tema
334 + isDark = document.documentElement.classList.contains('dark');
272 335
273 - $: twMontoA.set(displayMontoA); 336 + // Observar cambios de tema
274 - $: twMontoB.set(displayMontoB); 337 + const observer = new MutationObserver(() => {
275 - $: twPerCapitaA.set(displayPerCapitaA); 338 + isDark = document.documentElement.classList.contains('dark');
276 - $: twPerCapitaB.set(displayPerCapitaB); 339 + });
340 + observer.observe(document.documentElement, {
341 + attributes: true,
342 + attributeFilter: ['class']
343 + });
344 +
345 + // Cargar datos
346 + await Promise.all([loadData(), loadEntidades()]);
277 347
278 - $: if (displayTopEntidades[0]) twTop1.set(displayTopEntidades[0].porcentaje); 348 + // Cerrar dropdown al hacer click fuera
279 - $: if (displayTopEntidades[1]) twTop2.set(displayTopEntidades[1].porcentaje); 349 + const handleClickOutside = (e) => {
280 - $: if (displayTopEntidades[2]) twTop3.set(displayTopEntidades[2].porcentaje); 350 + if (entityDropdownOpen && !e.target.closest('.entity-dropdown-container')) {
351 + entityDropdownOpen = false;
352 + }
353 + };
354 + document.addEventListener('click', handleClickOutside);
355 +
356 + return () => {
357 + observer.disconnect();
358 + document.removeEventListener('click', handleClickOutside);
359 + };
360 + });
281 361
362 + // ══════════════════════════════════════════════════════════════
363 + // HELPERS
364 + // ══════════════════════════════════════════════════════════════
282 function formatMonto(m) { 365 function formatMonto(m) {
283 - if (m >= 1000000) return (m / 1000000).toFixed(1) + 'B'; 366 + if (m >= 1e9) return (m / 1e9).toFixed(1) + 'MM';
284 - if (m >= 1000) return Math.round(m / 1000) + 'K'; 367 + if (m >= 1e6) return (m / 1e6).toFixed(1) + 'M';
285 - return m.toLocaleString(); 368 + if (m >= 1e3) return Math.round(m / 1e3) + 'K';
369 + return m?.toLocaleString() || '0';
286 } 370 }
287 371
288 function formatMontoLargo(m) { 372 function formatMontoLargo(m) {
289 - if (m >= 1000) return (m / 1000).toFixed(1) + 'MM'; 373 + if (m >= 1e9) return Math.round(m / 1e9).toLocaleString('es-BO') + ' mil Mill.';
290 - return m.toLocaleString() + 'M'; 374 + if (m >= 1e6) return (m / 1e6).toLocaleString('es-BO', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' Mill.';
375 + return m?.toLocaleString('es-BO', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) || '0';
376 + }
377 +
378 + function formatPerCapita(n) {
379 + if (n >= 1000) return n.toLocaleString('es-BO', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
380 + if (n >= 10) return n.toLocaleString('es-BO', { minimumFractionDigits: 1, maximumFractionDigits: 1 });
381 + if (n < 1) return n.toLocaleString('es-BO', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
382 + return n.toLocaleString('es-BO', { minimumFractionDigits: 1, maximumFractionDigits: 1 });
291 } 383 }
292 </script> 384 </script>
293 385
...@@ -310,7 +402,7 @@ ...@@ -310,7 +402,7 @@
310 <span class="codigo">{objeto.codigo}</span> 402 <span class="codigo">{objeto.codigo}</span>
311 <h1> 403 <h1>
312 {objeto.nombre} 404 {objeto.nombre}
313 - <button class="help-icon" on:click={() => showInfo = !showInfo} class:active={showInfo}> 405 + <button class="help-icon" onclick={() => showInfo = !showInfo} class:active={showInfo}>
314 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 406 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
315 <circle cx="12" cy="12" r="10"/> 407 <circle cx="12" cy="12" r="10"/>
316 <path d="M9.5 9a3 3 0 1 1 3.5 2.95c-.5.17-1 .62-1 1.3V15"/> 408 <path d="M9.5 9a3 3 0 1 1 3.5 2.95c-.5.17-1 .62-1 1.3V15"/>
...@@ -355,14 +447,14 @@ ...@@ -355,14 +447,14 @@
355 <button 447 <button
356 class="toggle-btn" 448 class="toggle-btn"
357 class:active={vista === 'agregado'} 449 class:active={vista === 'agregado'}
358 - on:click={() => vista = 'agregado'} 450 + onclick={() => vista = 'agregado'}
359 > 451 >
360 Agregado 452 Agregado
361 </button> 453 </button>
362 <button 454 <button
363 class="toggle-btn" 455 class="toggle-btn"
364 class:active={vista === 'comparar'} 456 class:active={vista === 'comparar'}
365 - on:click={() => vista = 'comparar'} 457 + onclick={() => vista = 'comparar'}
366 > 458 >
367 Comparar 459 Comparar
368 </button> 460 </button>
...@@ -370,14 +462,60 @@ ...@@ -370,14 +462,60 @@
370 462
371 <!-- Selector de entidad (solo en vista agregado) --> 463 <!-- Selector de entidad (solo en vista agregado) -->
372 {#if vista === 'agregado'} 464 {#if vista === 'agregado'}
373 - <div class="entity-selector"> 465 + <div class="entity-selector entity-dropdown-container">
374 <span class="selector-label">Viendo</span> 466 <span class="selector-label">Viendo</span>
375 - <button class="selector-btn"> 467 + <div class="relative">
468 + <button
469 + class="selector-btn"
470 + onclick={() => entityDropdownOpen = !entityDropdownOpen}
471 + >
472 + <span class="truncate">
473 + {#if selectedEntity}
474 + {selectedEntity.entidad_desc?.slice(0, 30)}{selectedEntity.entidad_desc?.length > 30 ? '...' : ''}
475 + {:else}
376 Todo el sector público 476 Todo el sector público
477 + {/if}
478 + </span>
377 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 479 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
378 <path d="M6 9l6 6 6-6"/> 480 <path d="M6 9l6 6 6-6"/>
379 </svg> 481 </svg>
380 </button> 482 </button>
483 +
484 + {#if entityDropdownOpen}
485 + <div class="dropdown-panel">
486 + <div class="dropdown-search">
487 + <input
488 + type="text"
489 + bind:value={entitySearchQuery}
490 + placeholder="Buscar entidad..."
491 + class="search-input"
492 + />
493 + </div>
494 + <button
495 + class="dropdown-item"
496 + class:active={!selectedEntity}
497 + onclick={() => clearEntity()}
498 + >
499 + <span class="font-medium">Todo el sector público</span>
500 + <span class="text-xs opacity-60 ml-2">({nEntidades} entidades)</span>
501 + </button>
502 + <div class="dropdown-list">
503 + {#each filteredEntities as entity}
504 + <button
505 + class="dropdown-item"
506 + class:active={selectedEntity?.entidad === entity.entidad}
507 + onclick={() => selectEntity(entity)}
508 + >
509 + <span class="entity-code">{entity.entidad}</span>
510 + <span class="truncate">{entity.entidad_desc}</span>
511 + </button>
512 + {/each}
513 + </div>
514 + </div>
515 + {/if}
516 + </div>
517 + <span class="interaction-hint desktop-hint">Pasa el cursor sobre las barras para ver valores por año</span>
518 + <span class="interaction-hint mobile-hint">Toca las barras para ver valores por año</span>
381 </div> 519 </div>
382 {/if} 520 {/if}
383 521
...@@ -392,12 +530,12 @@ ...@@ -392,12 +530,12 @@
392 </div> 530 </div>
393 531
394 <div class="kpi"> 532 <div class="kpi">
395 - <span class="kpi-value">Bs {Math.round($twPerCapita).toLocaleString()}</span> 533 + <span class="kpi-value">Bs {formatPerCapita($twPerCapita)}</span>
396 <span class="kpi-label">{displayPerCapitaLabel}</span> 534 <span class="kpi-label">{displayPerCapitaLabel}</span>
397 </div> 535 </div>
398 536
399 <div class="kpi"> 537 <div class="kpi">
400 - <span class="kpi-value">{$twPorcentaje.toFixed(1)}%</span> 538 + <span class="kpi-value">{$twPorcentaje.toLocaleString('es-BO', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%</span>
401 <span class="kpi-label">{displayPorcentajeLabel}</span> 539 <span class="kpi-label">{displayPorcentajeLabel}</span>
402 </div> 540 </div>
403 541
...@@ -424,13 +562,13 @@ ...@@ -424,13 +562,13 @@
424 562
425 <div 563 <div
426 class="chart" 564 class="chart"
427 - on:mouseleave={() => hoveredYear = null} 565 + onmouseleave={() => hoveredYear = null}
428 role="group" 566 role="group"
429 > 567 >
430 {#each gastoPerCapita as g} 568 {#each gastoPerCapita as g}
431 <div 569 <div
432 class="bar-container" 570 class="bar-container"
433 - on:mouseenter={() => hoveredYear = g.año} 571 + onmouseenter={() => hoveredYear = g.año}
434 role="button" 572 role="button"
435 tabindex="0" 573 tabindex="0"
436 > 574 >
...@@ -448,7 +586,8 @@ ...@@ -448,7 +586,8 @@
448 </div> 586 </div>
449 </div> 587 </div>
450 588
451 - <!-- Top entidades --> 589 + <!-- Top entidades (solo visible cuando es "Todo el Estado") -->
590 + {#if !selectedEntity}
452 <div class="top-section"> 591 <div class="top-section">
453 <div class="top-header"> 592 <div class="top-header">
454 <h3>Mayores ejecutores</h3> 593 <h3>Mayores ejecutores</h3>
...@@ -470,6 +609,7 @@ ...@@ -470,6 +609,7 @@
470 {/each} 609 {/each}
471 </div> 610 </div>
472 </div> 611 </div>
612 + {/if}
473 613
474 {:else} 614 {:else}
475 <!-- VISTA COMPARAR --> 615 <!-- VISTA COMPARAR -->
...@@ -511,14 +651,14 @@ ...@@ -511,14 +651,14 @@
511 651
512 <div 652 <div
513 class="chart chart-comparar" 653 class="chart chart-comparar"
514 - on:mouseleave={() => hoveredYearComparar = null} 654 + onmouseleave={() => hoveredYearComparar = null}
515 role="group" 655 role="group"
516 > 656 >
517 {#each años as año, i} 657 {#each años as año, i}
518 <div 658 <div
519 class="bar-group" 659 class="bar-group"
520 class:active={hoveredYearComparar === año} 660 class:active={hoveredYearComparar === año}
521 - on:mouseenter={() => hoveredYearComparar = año} 661 + onmouseenter={() => hoveredYearComparar = año}
522 role="button" 662 role="button"
523 tabindex="0" 663 tabindex="0"
524 > 664 >
...@@ -738,6 +878,31 @@ ...@@ -738,6 +878,31 @@
738 align-items: center; 878 align-items: center;
739 gap: 0.75rem; 879 gap: 0.75rem;
740 margin-bottom: 0.75rem; 880 margin-bottom: 0.75rem;
881 + flex-wrap: wrap;
882 + }
883 +
884 + .interaction-hint {
885 + font-size: 0.688rem;
886 + color: #5A5650;
887 + font-style: italic;
888 + margin-left: auto;
889 + }
890 +
891 + .desktop-hint {
892 + display: inline;
893 + }
894 +
895 + .mobile-hint {
896 + display: none;
897 + }
898 +
899 + @media (max-width: 640px) {
900 + .desktop-hint {
901 + display: none;
902 + }
903 + .mobile-hint {
904 + display: inline;
905 + }
741 } 906 }
742 907
743 .selector-label { 908 .selector-label {
...@@ -750,8 +915,9 @@ ...@@ -750,8 +915,9 @@
750 align-items: center; 915 align-items: center;
751 gap: 0.5rem; 916 gap: 0.5rem;
752 padding: 0.5rem 1rem; 917 padding: 0.5rem 1rem;
753 - background: #161614; 918 + background: transparent;
754 - border: 1px solid #2E2E2C; 919 + border: none;
920 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
755 border-radius: 8px; 921 border-radius: 8px;
756 color: #F5F0E8; 922 color: #F5F0E8;
757 font-size: 0.875rem; 923 font-size: 0.875rem;
...@@ -759,12 +925,92 @@ ...@@ -759,12 +925,92 @@
759 transition: all 0.2s; 925 transition: all 0.2s;
760 } 926 }
761 .selector-btn:hover { 927 .selector-btn:hover {
762 - border-color: #E8C547; 928 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.15);
763 } 929 }
764 .selector-btn svg { 930 .selector-btn svg {
765 color: #5A5650; 931 color: #5A5650;
766 } 932 }
767 933
934 + .relative {
935 + position: relative;
936 + }
937 +
938 + .dropdown-panel {
939 + position: absolute;
940 + top: 100%;
941 + left: 0;
942 + margin-top: 0.25rem;
943 + width: min(500px, 90vw);
944 + max-height: 360px;
945 + overflow: hidden;
946 + background: #1A1A18;
947 + border: 1px solid #2E2E2C;
948 + border-radius: 8px;
949 + box-shadow: 0 10px 40px rgba(0,0,0,0.4);
950 + z-index: 50;
951 + }
952 +
953 + .dropdown-search {
954 + padding: 0.5rem;
955 + border-bottom: 1px solid #2E2E2C;
956 + }
957 +
958 + .search-input {
959 + width: 100%;
960 + padding: 0.5rem 0.75rem;
961 + background: #111110;
962 + border: 1px solid #2E2E2C;
963 + border-radius: 6px;
964 + color: #F5F0E8;
965 + font-size: 0.875rem;
966 + }
967 + .search-input:focus {
968 + outline: none;
969 + border-color: #E8C547;
970 + }
971 + .search-input::placeholder {
972 + color: #5A5650;
973 + }
974 +
975 + .dropdown-item {
976 + display: flex;
977 + align-items: center;
978 + gap: 0.5rem;
979 + width: 100%;
980 + padding: 0.625rem 0.75rem;
981 + background: transparent;
982 + border: none;
983 + color: #F5F0E8;
984 + font-size: 0.813rem;
985 + text-align: left;
986 + cursor: pointer;
987 + transition: background 0.15s;
988 + }
989 + .dropdown-item:hover {
990 + background: rgba(255,255,255,0.05);
991 + }
992 + .dropdown-item.active {
993 + background: rgba(232,197,71,0.15);
994 + }
995 +
996 + .dropdown-list {
997 + max-height: 240px;
998 + overflow-y: auto;
999 + }
1000 +
1001 + .entity-code {
1002 + font-family: 'DM Mono', monospace;
1003 + font-size: 0.688rem;
1004 + color: #5A5650;
1005 + min-width: 2.5rem;
1006 + }
1007 +
1008 + .truncate {
1009 + overflow: hidden;
1010 + text-overflow: ellipsis;
1011 + white-space: nowrap;
1012 + }
1013 +
768 /* Chart */ 1014 /* Chart */
769 .chart-section { 1015 .chart-section {
770 margin-bottom: 1rem; 1016 margin-bottom: 1rem;
...@@ -868,7 +1114,7 @@ ...@@ -868,7 +1114,7 @@
868 } 1114 }
869 1115
870 .bar.hovered { 1116 .bar.hovered {
871 - background: #264653; 1117 + background: #E8C547;
872 transform: scaleX(1.1); 1118 transform: scaleX(1.1);
873 } 1119 }
874 1120
...@@ -891,11 +1137,12 @@ ...@@ -891,11 +1137,12 @@
891 } 1137 }
892 1138
893 .kpi { 1139 .kpi {
894 - background: #131312; 1140 + background: transparent;
1141 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
895 padding: 0.75rem 0.75rem; 1142 padding: 0.75rem 0.75rem;
896 text-align: center; 1143 text-align: center;
897 border-radius: 8px; 1144 border-radius: 8px;
898 - transition: background 0.3s ease; 1145 + transition: box-shadow 0.3s ease;
899 } 1146 }
900 1147
901 .kpi-value { 1148 .kpi-value {
...@@ -958,8 +1205,14 @@ ...@@ -958,8 +1205,14 @@
958 align-items: center; 1205 align-items: center;
959 gap: 0.75rem; 1206 gap: 0.75rem;
960 padding: 0.625rem 0.875rem; 1207 padding: 0.625rem 0.875rem;
961 - background: #131312; 1208 + background: transparent;
1209 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
962 border-radius: 8px; 1210 border-radius: 8px;
1211 + transition: box-shadow 0.25s ease;
1212 + }
1213 +
1214 + .top-item:hover {
1215 + box-shadow: inset 0 0 0 1px rgba(201, 167, 81, 0.4);
963 } 1216 }
964 1217
965 .top-rank { 1218 .top-rank {
...@@ -989,9 +1242,13 @@ ...@@ -989,9 +1242,13 @@
989 1242
990 .top-bar { 1243 .top-bar {
991 height: 100%; 1244 height: 100%;
992 - background: #2a9d8f; 1245 + background: #C9A751;
993 border-radius: 2px; 1246 border-radius: 2px;
994 - transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1); 1247 + transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1), background 0.25s ease;
1248 + }
1249 +
1250 + .top-item:hover .top-bar {
1251 + background: #E8C547;
995 } 1252 }
996 1253
997 .top-pct { 1254 .top-pct {
...@@ -1025,8 +1282,8 @@ ...@@ -1025,8 +1282,8 @@
1025 /* Vista Toggle */ 1282 /* Vista Toggle */
1026 .vista-toggle { 1283 .vista-toggle {
1027 display: inline-flex; 1284 display: inline-flex;
1028 - background: #131312; 1285 + background: transparent;
1029 - border: 1px solid #2E2E2C; 1286 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
1030 border-radius: 10px; 1287 border-radius: 10px;
1031 padding: 4px; 1288 padding: 4px;
1032 margin-bottom: 0.75rem; 1289 margin-bottom: 0.75rem;
...@@ -1077,8 +1334,9 @@ ...@@ -1077,8 +1334,9 @@
1077 1334
1078 .selector-group select { 1335 .selector-group select {
1079 flex: 1; 1336 flex: 1;
1080 - background: #1A1A18; 1337 + background: transparent;
1081 - border: 1px solid #2E2E2C; 1338 + border: none;
1339 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
1082 border-radius: 8px; 1340 border-radius: 8px;
1083 padding: 0.5rem 0.75rem; 1341 padding: 0.5rem 0.75rem;
1084 color: #F5F0E8; 1342 color: #F5F0E8;
...@@ -1179,7 +1437,8 @@ ...@@ -1179,7 +1437,8 @@
1179 1437
1180 /* KPIs comparativos - tabla */ 1438 /* KPIs comparativos - tabla */
1181 .kpis-tabla { 1439 .kpis-tabla {
1182 - background: #131312; 1440 + background: transparent;
1441 + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
1183 border-radius: 10px; 1442 border-radius: 10px;
1184 padding: 0.5rem 1rem; 1443 padding: 0.5rem 1rem;
1185 } 1444 }
...@@ -1388,8 +1647,8 @@ ...@@ -1388,8 +1647,8 @@
1388 } 1647 }
1389 1648
1390 .tema-claro .vista-toggle { 1649 .tema-claro .vista-toggle {
1391 - background: #F0F0EC; 1650 + background: transparent;
1392 - border-color: #E5E5E0; 1651 + box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
1393 } 1652 }
1394 1653
1395 .tema-claro .toggle-btn { 1654 .tema-claro .toggle-btn {
...@@ -1407,6 +1666,10 @@ ...@@ -1407,6 +1666,10 @@
1407 color: #8A8578; 1666 color: #8A8578;
1408 } 1667 }
1409 1668
1669 + .tema-claro .interaction-hint {
1670 + color: #8A8578;
1671 + }
1672 +
1410 .tema-claro .selector-btn { 1673 .tema-claro .selector-btn {
1411 background: transparent; 1674 background: transparent;
1412 border: none; 1675 border: none;
...@@ -1420,6 +1683,41 @@ ...@@ -1420,6 +1683,41 @@
1420 color: #8A8578; 1683 color: #8A8578;
1421 } 1684 }
1422 1685
1686 + .tema-claro .dropdown-panel {
1687 + background: #FAFAF8;
1688 + border-color: #E5E5E0;
1689 + }
1690 +
1691 + .tema-claro .dropdown-search {
1692 + border-bottom-color: #E5E5E0;
1693 + }
1694 +
1695 + .tema-claro .search-input {
1696 + background: #F5F5F3;
1697 + border-color: #E5E5E0;
1698 + color: #1A1A18;
1699 + }
1700 + .tema-claro .search-input:focus {
1701 + border-color: #C9A227;
1702 + }
1703 + .tema-claro .search-input::placeholder {
1704 + color: #8A8578;
1705 + }
1706 +
1707 + .tema-claro .dropdown-item {
1708 + color: #1A1A18;
1709 + }
1710 + .tema-claro .dropdown-item:hover {
1711 + background: rgba(0,0,0,0.03);
1712 + }
1713 + .tema-claro .dropdown-item.active {
1714 + background: rgba(201,162,39,0.12);
1715 + }
1716 +
1717 + .tema-claro .entity-code {
1718 + color: #8A8578;
1719 + }
1720 +
1423 .tema-claro .chart-title { 1721 .tema-claro .chart-title {
1424 color: #8A8578; 1722 color: #8A8578;
1425 } 1723 }
...@@ -1441,7 +1739,7 @@ ...@@ -1441,7 +1739,7 @@
1441 } 1739 }
1442 1740
1443 .tema-claro .bar-container .bar.hovered { 1741 .tema-claro .bar-container .bar.hovered {
1444 - background: #264653; 1742 + background: #C9A751;
1445 } 1743 }
1446 1744
1447 /* Las barras de comparación mantienen su --bar-color */ 1745 /* Las barras de comparación mantienen su --bar-color */
...@@ -1497,7 +1795,15 @@ ...@@ -1497,7 +1795,15 @@
1497 } 1795 }
1498 1796
1499 .tema-claro .top-bar { 1797 .tema-claro .top-bar {
1500 - background: #2a9d8f; 1798 + background: #C9A751;
1799 + }
1800 +
1801 + .tema-claro .top-item:hover {
1802 + box-shadow: inset 0 0 0 1px rgba(201, 167, 81, 0.5);
1803 + }
1804 +
1805 + .tema-claro .top-item:hover .top-bar {
1806 + background: #B8963F;
1501 } 1807 }
1502 1808
1503 .tema-claro .top-pct { 1809 .tema-claro .top-pct {
......
1 +gestion,objeto_grupo,poblacion,top1_entidad_desc_entidad,top2_entidad_desc_entidad,top3_entidad_desc_entidad,top1_monto,top2_monto,top3_monto,top1_per_capita,top2_per_capita,top3_per_capita
2 +2005,1,9475861,Prefectura Del Departamento De La Paz,Ministerio De Defensa Nacional,Prefectura Del Departamento De Santa Cruz,2165989973.2400002,905520472.9100002,783374374.3100002,229,96,83
3 +2005,2,9475861,Municipalidad De La Paz,Servicio Nacional De Caminos,Municipalidad De Santa Cruz De La Sierra,318052922.3799999,262671532.11,239287121.05999997,34,28,25
4 +2005,3,9475861,Yacimientos Petroliferos Fiscales Bolivianos,Ministerio De Gobierno,Ministerio De Defensa Nacional,484404998.67,180713886.04,173790141.18999997,51,19,18
5 +2005,4,9475861,Servicio Nacional De Caminos,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,1602531309.59,426412889.85000026,382531081.14000034,169,45,40
6 +2005,5,9475861,Caja Petrolera De Salud,Secretaría Ejecutiva - Pl 480,Ministerio De Desarrollo Económico,78565985.56999998,54276791.55,40008080,8,6,4
7 +2005,6,9475861,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,Municipalidad De Cochabamba,665938007.79,657951217.52,169294372.42000002,70,69,18
8 +2005,8,9475861,Ministerio De Salud Y Deportes,Ministerio De Gobierno,Ministerio De Asuntos Campesinos Y Agropecuarios,28920132.529999997,22942835.43,20474175.640000004,3,2,2
9 +2005,9,9475861,Municipalidad De Santa Cruz De La Sierra,Ministerio De Hacienda,Ministerio De Salud Y Deportes,32578836.83,28846336.569999993,10778676.76,3,3,1
10 +2006,1,9586372,Prefectura Del Departamento De La Paz,Prefectura Del Departamento De Cochabamba,Ministerio De Defensa Nacional,2521168290.18,1453310487.180001,958445497.76,263,152,100
11 +2006,2,9586372,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,"Ministerio Desarrollo Rural, Agropecuario Y Medio Ambiente",386921568.65,373646357.0700001,335461208.43000007,40,39,35
12 +2006,3,9586372,Yacimientos Petroliferos Fiscales Bolivianos,Ministerio De Gobierno,Ministerio De Defensa Nacional,3688964112.9000006,191526679.58999985,185658779.26999998,385,20,19
13 +2006,4,9586372,Servicio Nacional De Caminos,Prefectura Del Departamento De Cochabamba,Municipalidad De Santa Cruz De La Sierra,1137837735.4099996,968591768.2700002,698595527.3600003,119,101,73
14 +2006,5,9586372,"Ministerio De Obras Públicas, Servicios Y Vivienda",Caja Petrolera De Salud,Municipalidad De Yacuiba,282218297.76,72856150.11,63875244.99,29,8,7
15 +2006,6,9586372,Municipalidad De La Paz,Municipalidad De Cochabamba,Municipalidad De Santa Cruz De La Sierra,462669962.51,288088976.76000005,158705430.61000004,48,30,17
16 +2006,8,9586372,Yacimientos Petroliferos Fiscales Bolivianos,Ministerio De Gobierno,Ministerio De Salud Y Deportes,21867029.55,14706981.85,11468759,2,2,1
17 +2006,9,9586372,Ministerio De Hacienda,Municipalidad De Santa Cruz De La Sierra,Municipalidad De Cochabamba,18617331.94,14999868.350000001,7530275.42,2,2,1
18 +2007,1,9701623,Prefectura Del Departamento De La Paz,Prefectura Del Departamento De Santa Cruz,Prefectura Del Departamento De Cochabamba,2793147580.559999,2142569168.739999,1632277442.7800004,288,221,168
19 +2007,2,9701623,Yacimientos Petrolíferos Fiscales Bolivianos,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,6865924650.49,570437686.4999995,413919119.86999947,708,59,43
20 +2007,3,9701623,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalurgica Vinto - Nacionalizada,Ministerio De Defensa Nacional,7975908085.859999,739852915,299348102.3900001,822,76,31
21 +2007,4,9701623,Prefectura Del Departamento De Tarija,Administradora Boliviana De Carreteras,Municipalidad De Santa Cruz De La Sierra,1754016115.350001,1664453307.4500003,912613170.5799996,181,172,94
22 +2007,5,9701623,Yacimientos Petrolíferos Fiscales Bolivianos,"Ministerio De Obras Públicas, Servicios Y Vivienda",Ministerio De Educación Y Culturas,875200000,323258427.12,293981501.01,90,33,30
23 +2007,6,9701623,Municipalidad De La Paz,Empresa Metalurgica Vinto - Nacionalizada,Municipalidad De Cochabamba,328357628.21,212924215.28,199598641.12,34,22,21
24 +2007,8,9701623,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalurgica Vinto - Nacionalizada,Corporación Minera De Bolivia,6108127707.92,78096547.46,57274853.559999995,630,8,6
25 +2007,9,9701623,Ministerio De Planificación Del Desarrollo,Fondo Nacional De Desarrollo Regional,Universidad Técnica Del Beni Mariscal José Ballivián,23223678.370000005,10374204.82,7515221.790000001,2,1,1
26 +2008,1,9794695,Prefectura Del Departamento De La Paz,Prefectura Del Departamento De Santa Cruz,Prefectura Del Departamento De Cochabamba,3304344591.9199996,2440880970.3999987,1879310925.0499995,337,249,192
27 +2008,2,9794695,Yacimientos Petrolíferos Fiscales Bolivianos,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,10540777751.59,832618443.9699998,486105833.6400003,1076,85,50
28 +2008,3,9794695,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa De Apoyo A La Producción De Alimentos,12750659412.59,553722043.84,323532907.1,1302,57,33
29 +2008,4,9794695,Administradora Boliviana De Carreteras,Municipalidad De Santa Cruz De La Sierra,Prefectura Del Departamento De Tarija,1784228297.3000002,1399548427.8799996,1343000284.6599998,182,143,137
30 +2008,5,9794695,Yacimientos Petrolíferos Fiscales Bolivianos,"Ministerio De Obras Públicas, Servicios Y Vivienda",Ministerio De Educación Y Culturas,971747928.51,510793167.83,375442575.77,99,52,38
31 +2008,6,9794695,Yacimientos Petrolíferos Fiscales Bolivianos,Municipalidad De La Paz,Administradora Boliviana De Carreteras,3224810049.3399997,315244661.84,255818640.30000004,329,32,26
32 +2008,8,9794695,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Ministerio De Gobierno,13519956258.039999,37036040.6,18330223.33,1380,4,2
33 +2008,9,9794695,Administradora Boliviana De Carreteras,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,113603369.12,27399435.18,11836288.28,12,3,1
34 +2009,1,9914126,Prefectura Del Departamento De La Paz,Prefectura Del Departamento De Santa Cruz,Prefectura Del Departamento De Cochabamba,3899616135.439999,2887049199.470001,2235925941.379999,393,291,226
35 +2009,2,9914126,Yacimientos Petrolíferos Fiscales Bolivianos,Municipalidad De Santa Cruz De La Sierra,Municipalidad De La Paz,7564355044.880002,701651121.6799995,580517613.5999999,763,71,59
36 +2009,3,9914126,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa De Apoyo A La Producción De Alimentos,8731254829.150007,925604794.5600002,376708709.00999993,881,93,38
37 +2009,4,9914126,Administradora Boliviana De Carreteras,Municipalidad De Santa Cruz De La Sierra,Prefectura Del Departamento De Tarija,1849296985.0799997,1742795400.2699997,1548134098.3300002,187,176,156
38 +2009,5,9914126,Yacimientos Petrolíferos Fiscales Bolivianos,"Ministerio De Obras Públicas, Servicios Y Vivienda",Ministerio De Educación,1170386094.97,434830717.7,376007546,118,44,38
39 +2009,6,9914126,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Municipalidad De La Paz,9325932684.32,327683508.02,296420207.86,941,33,30
40 +2009,8,9914126,Yacimientos Petrolíferos Fiscales Bolivianos,Insumos Bolivia,Ministerio De Desarrollo Rural Y Tierras,10137117443.170002,52402515.13,41841174.120000005,1022,5,4
41 +2009,9,9914126,"Ministerio De Obras Públicas, Servicios Y Vivienda",Insumos Bolivia,Municipalidad De Santa Cruz De La Sierra,43601631.86000001,37134926.97,28062882.32,4,4,3
42 +2010,1,10076577,Gobierno Autónomo Departamental De La Paz,Gobierno Autónomo Departamental De Santa Cruz,Gobierno Autónomo Departamental De Cochabamba,2810106264.169998,2137233770.1199992,1836796512.7199996,279,212,182
43 +2010,2,10076577,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De La Paz,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,9606702121.769981,676447080.2400005,602260582.9700004,953,67,60
44 +2010,3,10076577,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Caja Nacional De Salud,11529013071.720003,1458247042.1600003,548694335.04,1144,145,54
45 +2010,4,10076577,Administradora Boliviana De Carreteras,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,1985831172.3600008,1696096057.1899998,1038623520.9000003,197,168,103
46 +2010,5,10076577,"Ministerio De Obras Públicas, Servicios Y Vivienda",Ministerio De Educación,Yacimientos Petrolíferos Fiscales Bolivianos,459994511.27,380911925,340916150,46,38,34
47 +2010,6,10076577,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De La Paz,4855770372.599999,1861584187.78,321448950.00000006,482,185,32
48 +2010,8,10076577,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Boliviana De Aviación,12188366174.610003,44830822.26,33681016.910000004,1210,4,3
49 +2010,9,10076577,"Ministerio De Obras Públicas, Servicios Y Vivienda",Servicio Nacional De Caminos Residual,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,58059361,33742361.86,28328004.700000003,6,3,3
50 +2011,1,10245917,Gobierno Autónomo Departamental De La Paz,Gobierno Autónomo Departamental De Santa Cruz,Ministerio De Defensa,1876956964.2700002,1804439437.8100004,1556478749.2300003,183,176,152
51 +2011,2,10245917,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,13204806505.260015,804986517.4700001,787026639.7400014,1289,79,77
52 +2011,3,10245917,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Caja Nacional De Salud,13780371139.590004,2159717591.2599993,582317617.3299998,1345,211,57
53 +2011,4,10245917,Administradora Boliviana De Carreteras,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Yacimientos Petrolíferos Fiscales Bolivianos,2465488536.6099977,1704115304.1899993,1531693615.8200006,241,166,149
54 +2011,5,10245917,Empresa Nacional De Electricidad,"Ministerio De Obras Públicas, Servicios Y Vivienda",Ministerio De Educación,1510144149.62,901708727.05,385003557,147,88,38
55 +2011,6,10245917,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De La Paz,7726615699.2,1290865675.49,375963545.81000006,754,126,37
56 +2011,8,10245917,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Empresa Nacional De Electricidad,15017787489.499998,125388025.87000002,57048585.58,1466,12,6
57 +2011,9,10245917,"Ministerio De Obras Públicas, Servicios Y Vivienda",Caja Nacional De Salud,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,61578780.45,51994601.55,34273741.39,6,5,3
58 +2012,1,10423115,Direc. Dptal. De Educación La Paz,Caja Nacional De Salud,Ministerio De Defensa,2178855137.349999,1925343592.33,1684327813.1800003,209,185,162
59 +2012,2,10423115,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,17973240238.90001,1311516368.7199996,814786896.2800003,1724,126,78
60 +2012,3,10423115,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Caja Nacional De Salud,19479875947.16001,1521240239.9999998,671854019.02,1869,146,64
61 +2012,4,10423115,Administradora Boliviana De Carreteras,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,3130613069.040002,2822379744.7300005,2049979455.1000009,300,271,197
62 +2012,5,10423115,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Ministerio De Educación,673150100.45,492478179.62,410000000,65,47,39
63 +2012,6,10423115,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De La Paz,9572994860.94,1895579903.53,458481402.33000004,918,182,44
64 +2012,8,10423115,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Empresa De Apoyo A La Producción De Alimentos,20916596450.64,131307470.30999999,108596765.45,2007,13,10
65 +2012,9,10423115,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,"Ministerio De Obras Públicas, Servicios Y Vivienda",Boliviana De Aviación,57394472.559999995,38691808.54,35344609.77,6,4,3
66 +2013,1,10594727,Direc. Dptal. De Educación La Paz,Caja Nacional De Salud,Ministerio De Defensa,2465770684.7899985,2102127300.6700006,1841582021.9000006,233,198,174
67 +2013,2,10594727,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,15222969613.420012,1927333922.0499985,971798623.0599998,1437,182,92
68 +2013,3,10594727,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa De Apoyo A La Producción De Alimentos,18382938405.059982,1558969740.2699997,1097138758.1899996,1735,147,104
69 +2013,4,10594727,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Yacimientos Petrolíferos Fiscales Bolivianos,Administradora Boliviana De Carreteras,3225470030.7300024,3190680164.83,3034059058.33,304,301,286
70 +2013,5,10594727,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Ministerio De Desarrollo Productivo Y Economía Plural,1337786918,799283319.38,691318068,126,75,65
71 +2013,6,10594727,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De La Paz,11784575051.26,859275990.37,564119758.4100002,1112,81,53
72 +2013,8,10594727,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Empresa De Apoyo A La Producción De Alimentos,20173943280.710007,194273165.46999997,105063157,1904,18,10
73 +2013,9,10594727,Boliviana De Aviación,Corporación Minera De Bolivia,Banco Central De Bolivia,57684106.2,53791625.059999995,53424591.01,5,5,5
74 +2014,1,10755947,Direc. Dptal. De Educación La Paz,Caja Nacional De Salud,Ministerio De Defensa,2811018706.470001,2304927163.9799995,2035516674.87,261,214,189
75 +2014,2,10755947,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,21881733206.499973,2047821424.639999,1147577972.3400004,2034,190,107
76 +2014,3,10755947,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa De Apoyo A La Producción De Alimentos,16816252742.549992,1983467373.26,1314485796.1200001,1563,184,122
77 +2014,4,10755947,Administradora Boliviana De Carreteras,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,4471471523.549995,3755573664.629999,3477692771.4199977,416,349,323
78 +2014,5,10755947,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Agencia Estatal De Vivienda,4265946873.46,1462769345.48,818183427.37,397,136,76
79 +2014,6,10755947,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De La Paz,11854737861.820002,2781496625.57,544169180.4100001,1102,259,51
80 +2014,8,10755947,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Empresa De Apoyo A La Producción De Alimentos,26673361520.7,214615925.63000003,125756483.42,2480,20,12
81 +2014,9,10755947,Universidad Mayor De San Andrés,Banco Central De Bolivia,Boliviana De Aviación,108160591.77,96364078.23,62332474.08,10,9,6
82 +2015,1,10920682,Direc. Dptal. De Educación La Paz,Caja Nacional De Salud,Direc. Dptal. De Educación Santa Cruz,3196815000.4999995,2738632510.4600005,2271681464.6499996,293,251,208
83 +2015,2,10920682,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Gobierno Autónomo Municipal De La Paz,15225224067.17999,2280195269.949999,1259558084.6999996,1394,209,115
84 +2015,3,10920682,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Caja Nacional De Salud,13962141991.250006,1184540725.5100005,1180563599.2299998,1279,108,108
85 +2015,4,10920682,Administradora Boliviana De Carreteras,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,5283567362.979995,4105956510.9300027,2426933014.4799995,484,376,222
86 +2015,5,10920682,Agencia Estatal De Vivienda,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,1762595577.75,827607758.94,612045786.45,161,76,56
87 +2015,6,10920682,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,11437993835.62,2906358811.79,610331236.6100001,1047,266,56
88 +2015,8,10920682,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Boliviana De Aviación,17929743923.690006,199769833.98,132315787.19,1642,18,12
89 +2015,9,10920682,Banco Central De Bolivia,Caja Nacional De Salud,Boliviana De Aviación,95867427.66,94162422.64,82113934.15000002,9,9,8
90 +2016,1,11083605,Direc. Dptal. De Educación La Paz,Direc. Dptal. De Educación Santa Cruz,Ministerio De Gobierno,3465552656.159999,2449443247.3900003,2330036692.6399994,313,221,210
91 +2016,2,11083605,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Boliviana De Aviación,9415208327.389984,1032000691.63,849049341.5999997,849,93,77
92 +2016,3,11083605,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa Boliviana De Almendras Y Derivados,11995575952.039991,1206341190.64,732246377.3599999,1082,109,66
93 +2016,4,11083605,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Yacimientos Petrolíferos Fiscales Bolivianos,5999681602.410001,5634582638.270006,3098315467.750001,541,508,280
94 +2016,5,11083605,Empresa Nacional De Electricidad,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,5261840669.4,1366523942.8099997,972552767.6100001,475,123,88
95 +2016,6,11083605,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Empresa Metalúrgica Vinto - Nacionalizada,7765027451.250001,394791768.53,330332851.33,701,36,30
96 +2016,8,11083605,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Empresa Nacional De Electricidad,9808513908.54,160757331.21000004,147405871.45000002,885,15,13
97 +2016,9,11083605,Boliviana De Aviación,Caja Nacional De Salud,Administradora Boliviana De Carreteras,109840520.94999999,107514545.48,76898776.27,10,10,7
98 +2017,1,11242712,Direc. Dptal. De Educación La Paz,Direc. Dptal. De Educación Santa Cruz,Ministerio De Gobierno,3874606490.6599994,2744823898.3900023,2493300380.2599983,345,244,222
99 +2017,2,11242712,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Boliviana De Aviación,10362979117.190002,984205158.9000002,967109692.0599998,922,88,86
100 +2017,3,11242712,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Boliviana De Aviación,13886105538.840004,1569234959.89,720203230.6099997,1235,140,64
101 +2017,4,11242712,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Yacimientos Petrolíferos Fiscales Bolivianos,7375401157.710002,5045553363.139998,1778189094.459999,656,449,158
102 +2017,5,11242712,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,Agencia Estatal De Vivienda,4282944874.2,1338320661.5,1080172168.1000001,381,119,96
103 +2017,6,11242712,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Gobierno Autónomo Municipal De La Paz,5593851462.229998,486578573.28999996,306025127.0400001,498,43,27
104 +2017,8,11242712,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Boliviana De Aviación,10025830419.640001,256911748.07999998,158304505.88,892,23,14
105 +2017,9,11242712,Boliviana De Aviación,Caja Nacional De Salud,Yacimientos Petrolíferos Fiscales Bolivianos,117635851.57000001,91799962.57999998,64431692.18,10,8,6
106 +2018,1,11386175,Direc. Dptal. De Educación La Paz,Direc. Dptal. De Educación Santa Cruz,Ministerio De Gobierno,4191569716.8,2975066506.47,2706347368.1199985,368,261,238
107 +2018,2,11386175,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Boliviana De Aviación,12124672578.34001,1301082019.210002,966253684.1299998,1065,114,85
108 +2018,3,11386175,Yacimientos Petrolíferos Fiscales Bolivianos,Servicio De Desarrollo De Las Empresas Púb. Productivas,Empresa Metalúrgica Vinto - Nacionalizada,16387061058.159994,1298236513.18,1298030760.4099998,1439,114,114
109 +2018,4,11386175,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,"Ministerio De Obras Públicas, Servicios Y Vivienda",5730403130.989998,3576398449.12,1899554860.9500005,503,314,167
110 +2018,5,11386175,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,Agencia Estatal De Vivienda,2268651016.11,1245671100.6899996,1143026979.1299999,199,109,100
111 +2018,6,11386175,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,5979699985.56,618413095.1300001,271553612.1600001,525,54,24
112 +2018,8,11386175,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Boliviana De Aviación,10863973990.950005,341118744.01,177250112.42,954,30,16
113 +2018,9,11386175,Ministerio De Minería Y Metalurgia,Boliviana De Aviación,Caja Nacional De Salud,292762367.8,123667362.07000001,87580774.85999998,26,11,8
114 +2019,1,11514867,Direc. Dptal. De Educación La Paz,Direc. Dptal. De Educación Santa Cruz,Ministerio De Gobierno,4445368441.799999,3179774881.9200006,2863143021.3999996,386,276,249
115 +2019,2,11514867,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Boliviana De Aviación,12399169775.690004,1149738875.3499997,984473441.33,1077,100,85
116 +2019,3,11514867,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Servicio De Desarrollo De Las Empresas Púb. Productivas,16128293385.159996,1243175128.7700002,1172150575.88,1401,108,102
117 +2019,4,11514867,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,"Ministerio De Obras Públicas, Servicios Y Vivienda",4742184448.900001,2440511194.83,1505655193.4299994,412,212,131
118 +2019,5,11514867,Empresa Nacional De Electricidad,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,1385406712.56,1245574650.5099995,994833219.8699995,120,108,86
119 +2019,6,11514867,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,5097730559.569998,808853725.55,343341873.52,443,70,30
120 +2019,8,11514867,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Ministerio De Salud,13676470168.299997,349216136.52,94543209.52000001,1188,30,8
121 +2019,9,11514867,Corporación Minera De Bolivia,Boliviana De Aviación,Caja Nacional De Salud,189732167.96,100038655.35,94859332.39,16,9,8
122 +2020,1,11640016,Direc. Dptal. De Educación La Paz,Ministerio De Gobierno,Direc. Dptal. De Educación Santa Cruz,4429675686.170002,3419104302.1800013,3177788417.15,381,294,273
123 +2020,2,11640016,Yacimientos Petrolíferos Fiscales Bolivianos,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,Boliviana De Aviación,11067634469.870028,813835312.9199992,578725648.1,951,70,50
124 +2020,3,11640016,Yacimientos Petrolíferos Fiscales Bolivianos,Servicio De Desarrollo De Las Empresas Púb. Productivas,Caja Nacional De Salud,12120739599.430012,919302775.8099996,691197545.6700004,1041,79,59
125 +2020,4,11640016,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Agencia De Infraestructura En Salud Y Equipamiento Médico,1487141693.4999998,773611906.3300003,576669319.43,128,66,50
126 +2020,5,11640016,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,Ministerio De Planificación Del Desarrollo,1144516445.1899998,781212704.9700003,696917732.38,98,67,60
127 +2020,6,11640016,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,7747696767.070001,932360905.0999999,445927065.0999999,666,80,38
128 +2020,8,11640016,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Ministerio De Gobierno,10404488193.79,441941245.26999986,142071449,894,38,12
129 +2020,9,11640016,Ministerio De Economía Y Finanzas Públicas,Caja Nacional De Salud,Yacimientos Petrolíferos Fiscales Bolivianos,1719605616.72,117836366.7,100171351.09,148,10,9
130 +2021,1,11733918,Direc. Dptal. De Educación La Paz,Ministerio De Gobierno,Direc. Dptal. De Educación Santa Cruz,4564962871.270001,3537614877.54,3285843136.2100024,389,301,280
131 +2021,2,11733918,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,11689007816.880007,895829529.25,779934846.1699996,996,76,66
132 +2021,3,11733918,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Central De Abastecimiento Y Suministros De Salud,18075018944.410007,2160914234.3399997,1462972262.26,1540,184,125
133 +2021,4,11733918,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Empresa Siderúrgica Del Mutún,4190952268.700001,1946375233.7599995,892134155.1199999,357,166,76
134 +2021,5,11733918,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,Empresa Nacional De Electricidad,1227901537.4199998,764948406.0900004,712096992,105,65,61
135 +2021,6,11733918,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Gobierno Autónomo Municipal De La Paz,4149112793.8799987,857640236.7900001,332464021.41,354,73,28
136 +2021,8,11733918,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Corporación Minera De Bolivia,11471035531.929996,290929719.37,121814649.52000001,978,25,10
137 +2021,9,11733918,Ministerio De Economía Y Finanzas Públicas,Caja Nacional De Salud,Yacimientos Petrolíferos Fiscales Bolivianos,2994885782.73,116538207.02999997,92378391.36000001,255,10,8
138 +2022,1,11798231,Direc. Dptal. De Educación La Paz,Ministerio De Gobierno,Direc. Dptal. De Educación Santa Cruz,4758751431.880001,3532934303.2099977,3438997772.909999,403,299,291
139 +2022,2,11798231,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,13717458912.900005,1110985098.7200003,955449487.3299994,1163,94,81
140 +2022,3,11798231,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Servicio De Desarrollo De Las Empresas Púb. Productivas,28312956060.750008,1848141879.07,1192873663.4899995,2400,157,101
141 +2022,4,11798231,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Empresa Siderúrgica Del Mutún,3555223496.210001,1337968824.280001,951681893.51,301,113,81
142 +2022,5,11798231,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,Ministerio De Planificación Del Desarrollo,1320180516.8000002,601282313.7799997,551926936.63,112,51,47
143 +2022,6,11798231,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,5423504844.51,1410832328.29,579018729.71,460,120,49
144 +2022,8,11798231,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Corporación Minera De Bolivia,14205993480.05,229356840.79999998,104066634.27,1204,19,9
145 +2022,9,11798231,Ministerio De Economía Y Finanzas Públicas,Boliviana De Aviación,Yacimientos Petrolíferos Fiscales Bolivianos,902785870.27,137850755.29,110599063.64999999,77,12,9
146 +2023,1,11872175,Direc. Dptal. De Educación La Paz,Ministerio De Gobierno,Direc. Dptal. De Educación Santa Cruz,4973944023.320004,3688766663.190001,3612275492.66,419,311,304
147 +2023,2,11872175,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,11773261573.190014,1438304348.9699996,963027065.9399998,992,121,81
148 +2023,3,11872175,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Metalúrgica Vinto - Nacionalizada,Empresa De Apoyo A La Producción De Alimentos,25825266301.549976,1564632540.7500002,1488076715.9700003,2175,132,125
149 +2023,4,11872175,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Yacimientos Petrolíferos Fiscales Bolivianos,2379102996.9099994,1022917720.0299999,1020797708.61,200,86,86
150 +2023,5,11872175,Agencia Estatal De Vivienda,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,1358834807.8799996,928522446,502983373.2699999,114,78,42
151 +2023,6,11872175,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Empresa Pública Nacional Estratégica De Yacimientos De Litio Bolivianos,6295789937.63,1502652979.0599997,537055177.3,530,127,45
152 +2023,8,11872175,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Empresa Nacional De Electricidad,13497380795.739998,215762276.84000003,130044604.64,1137,18,11
153 +2023,9,11872175,Ministerio De Economía Y Finanzas Públicas,Banco Central De Bolivia,Boliviana De Aviación,1733803266.3999999,176140756.1,150713617.13,146,15,13
154 +2024,1,11916453,Direc. Dptal. De Educación La Paz,Ministerio De Gobierno,Direc. Dptal. De Educación Santa Cruz,5221331121.370003,3901879569.17,3803816523.320001,438,327,319
155 +2024,2,11916453,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,10545646955.190002,1376143999.17,879273588.7200005,885,115,74
156 +2024,3,11916453,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Empresa Metalúrgica Vinto - Nacionalizada,27468850081.29001,2139035246.8700004,1867921535.0000002,2305,180,157
157 +2024,4,11916453,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Yacimientos Petrolíferos Fiscales Bolivianos,2610044614.9600015,1048343616.21,923019707.7399994,219,88,77
158 +2024,5,11916453,Agencia Estatal De Vivienda,Empresa Nacional De Electricidad,Fondo Nacional De Desarrollo Regional,1401782953.1999996,434716061,392456028.9200001,118,36,33
159 +2024,6,11916453,Yacimientos Petrolíferos Fiscales Bolivianos,Empresa Nacional De Electricidad,Banco Central De Bolivia,5927935075.01,1780339292.0800002,957010270.5400001,497,149,80
160 +2024,8,11916453,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Empresa Nacional De Electricidad,11910441582.309998,253598777.35999998,154447306.18,999,21,13
161 +2024,9,11916453,Banco Central De Bolivia,Caja Nacional De Salud,Boliviana De Aviación,1263368809.8,141667378.23999998,112172417.82,106,12,9
162 +2025,1,11945263,Direc. Dptal. De Educación La Paz,Caja Nacional De Salud,Ministerio De Gobierno,5619472435.389999,5151839169.6900015,4152695264.629999,470,431,348
163 +2025,2,11945263,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Gobierno Autónomo Municipal De Santa Cruz De La Sierra,10022617652.269995,1307238585.7,776894490.02,839,109,65
164 +2025,3,11945263,Yacimientos Petrolíferos Fiscales Bolivianos,Corporación Minera De Bolivia,Caja Nacional De Salud,25440956502.62997,3031843418.9,2453131090.32,2130,254,205
165 +2025,4,11945263,Administradora Boliviana De Carreteras,Empresa Nacional De Electricidad,Gobierno Autónomo Departamental De Potosí,1805588926,1772658039.970001,613522280.5799998,151,148,51
166 +2025,5,11945263,Agencia Estatal De Vivienda,Fondo Nacional De Desarrollo Regional,Ministerio De Planificación Del Desarrollo,1495638952.1900003,584557374.4000002,391180329.65999997,125,49,33
167 +2025,6,11945263,Yacimientos Petrolíferos Fiscales Bolivianos,Banco Central De Bolivia,Empresa Nacional De Electricidad,4459749775.39,3691286335.43,1975389838.2800002,373,309,165
168 +2025,8,11945263,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,Empresa Nacional De Electricidad,7472329158.12,319021370.38000005,200966153.25000003,626,27,17
169 +2025,9,11945263,Banco Central De Bolivia,Yacimientos Petrolíferos Fiscales Bolivianos,Boliviana De Aviación,7846357463.4,6611439447.179998,595725550.83,657,553,50