Showing
6 changed files
with
976 additions
and
371 deletions
| ... | @@ -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);" | 986 | + > |
| 979 | - > | 987 | + <span class="truncate">{NIVEL_OPTIONS.find(o => o.value === treemapViewLevel)?.label || 'Seleccionar'}</span> |
| 980 | - {#each NIVEL_OPTIONS as opt} | 988 | + <svg class="w-4 h-4 shrink-0" style="color: var(--theme-texto);" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 981 | - <option value={opt.value}>{opt.label}</option> | 989 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> |
| 982 | - {/each} | 990 | + </svg> |
| 983 | - </select> | 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);"> | ||
| 994 | + {#each NIVEL_OPTIONS as opt} | ||
| 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> | ||
| 1003 | + {/each} | ||
| 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);" | 1015 | + > |
| 993 | - > | 1016 | + <span>{selectedYear || 'Año'}</span> |
| 994 | - {#each availableYears as year} | 1017 | + <svg class="w-4 h-4 shrink-0" style="color: var(--theme-texto);" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 995 | - <option value={year}>{year}</option> | 1018 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> |
| 996 | - {/each} | 1019 | + </svg> |
| 997 | - </select> | 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);"> | ||
| 1023 | + {#each availableYears as year} | ||
| 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> | ||
| 1031 | + {/each} | ||
| 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 | - | ||
| 47 | - // Dummy spending data (in millions Bs, population in millions) | ||
| 48 | - const gastoAnual = [ | ||
| 49 | - { año: 2005, monto: 8200, poblacion: 9.4 }, | ||
| 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 | 148 | ||
| 71 | - // Calculate per capita for each year | 149 | + let maxPerCapita = $derived( |
| 72 | - const gastoPerCapita = gastoAnual.map(g => ({ | 150 | + gastoPerCapita.length > 0 ? Math.max(...gastoPerCapita.map(g => g.perCapita)) : 1 |
| 73 | - ...g, | 151 | + ); |
| 74 | - perCapita: Math.round(g.monto / g.poblacion) | ||
| 75 | - })); | ||
| 76 | - | ||
| 77 | - // Top entidades por año (dummy data) | ||
| 78 | - const topEntidadesPorAño = { | ||
| 79 | - 2020: [ | ||
| 80 | - { nombre: 'Ministerio de Educación', monto: 12500, porcentaje: 31.4 }, | ||
| 81 | - { nombre: 'Ministerio de Salud', monto: 7800, porcentaje: 19.6 }, | ||
| 82 | - { nombre: 'Policía Boliviana', monto: 4200, porcentaje: 10.6 }, | ||
| 83 | - ], | ||
| 84 | - 2021: [ | ||
| 85 | - { nombre: 'Ministerio de Educación', monto: 13200, porcentaje: 31.4 }, | ||
| 86 | - { nombre: 'Ministerio de Salud', monto: 8100, porcentaje: 19.2 }, | ||
| 87 | - { nombre: 'Policía Boliviana', monto: 4600, porcentaje: 10.9 }, | ||
| 88 | - ], | ||
| 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 | - | ||
| 106 | - // Total histórico (para cuando no hay hover) | ||
| 107 | - const topEntidadesTotal = [ | ||
| 108 | - { nombre: 'Ministerio de Educación', monto: 74900, porcentaje: 32.6 }, | ||
| 109 | - { nombre: 'Ministerio de Salud', monto: 41900, porcentaje: 18.4 }, | ||
| 110 | - { nombre: 'Policía Boliviana', monto: 25800, porcentaje: 11.3 }, | ||
| 111 | - ]; | ||
| 112 | 152 | ||
| 113 | - // Datos para comparación (dummy) | 153 | + // Histórico |
| 114 | - const entidadesComparables = [ | 154 | + let totalHistorico = $derived(datosHistorico ? (selectedEntity ? datosHistorico.monto : datosHistorico.total) : 0); |
| 115 | - { | 155 | + let promedioPerCapita = $derived(datosHistorico?.per_capita || 0); |
| 116 | - id: 0, | 156 | + let totalPorcentaje = $derived(datosHistorico?.prop || 0); |
| 117 | - nombre: 'Ministerio de Educación', | 157 | + let ranking = $derived(datosHistorico?.ranking || 0); |
| 118 | - color: '#E8C547', | 158 | + let nPares = $derived(datosHistorico?.n_pares || 0); |
| 119 | - datos: [ | 159 | + let nEntidades = $derived(datosHistorico?.n_entidades || 0); |
| 120 | - { año: 2020, monto: 12500, perCapita: 1068 }, | 160 | + // Número de entidades - cambia según el año seleccionado y la entidad seleccionada |
| 121 | - { año: 2021, monto: 13200, perCapita: 1119 }, | 161 | + let totalEntidadesHistorico = $derived.by(() => { |
| 122 | - { año: 2022, monto: 14800, perCapita: 1233 }, | 162 | + if (selectedEntity && datosAnuales.length > 0) { |
| 123 | - { año: 2023, monto: 16200, perCapita: 1339 }, | 163 | + // Para entidad seleccionada: promedio de n_entidades de los años que tiene esta entidad |
| 124 | - { año: 2024, monto: 18200, perCapita: 1480 }, | 164 | + // Esto refleja cuántas entidades participaron durante el período de vida de esta entidad |
| 125 | - ], | 165 | + const suma = datosAnuales.reduce((acc, d) => acc + (d.n_entidades || 0), 0); |
| 126 | - total: 74900, | 166 | + return Math.round(suma / datosAnuales.length); |
| 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 | } | 167 | } |
| 179 | - ]; | 168 | + // Para "Todo el Estado": usar la lista del dropdown o el histórico |
| 169 | + return entidades.length || nEntidades; | ||
| 170 | + }); | ||
| 180 | 171 | ||
| 181 | - // Colores fijos por posición - teal y naranja | 172 | + let totalEntidades = $derived.by(() => { |
| 182 | - $: colorA = '#2a9d8f'; // teal | 173 | + if (hoveredYear) { |
| 183 | - $: colorB = '#f4a261'; // naranja | 174 | + // Buscar n_entidades del año específico |
| 175 | + const añoData = datosAnuales.find(d => d.gestion === hoveredYear); | ||
| 176 | + return añoData?.n_entidades || totalEntidadesHistorico; | ||
| 177 | + } | ||
| 178 | + return totalEntidadesHistorico; | ||
| 179 | + }); | ||
| 184 | 180 | ||
| 185 | - $: entA = entidadesComparables[entidadA]; | 181 | + // Para el ranking: valores reactivos según hover |
| 186 | - $: entB = entidadesComparables[entidadB]; | 182 | + let displayRankingValue = $derived.by(() => { |
| 187 | - $: maxComparacion = Math.max( | 183 | + if (hoveredYear) { |
| 188 | - ...entA.datos.map(d => d.perCapita), | 184 | + const añoData = datosAnuales.find(d => d.gestion === hoveredYear); |
| 189 | - ...entB.datos.map(d => d.perCapita) | 185 | + return añoData?.ranking || ranking; |
| 190 | - ); | 186 | + } |
| 191 | - $: años = entA.datos.map(d => d.año); | 187 | + return ranking; |
| 188 | + }); | ||
| 192 | 189 | ||
| 193 | - // Datos de comparación según hover | 190 | + let rankingDenominador = $derived.by(() => { |
| 194 | - $: hoveredDataA = hoveredYearComparar | 191 | + if (selectedEntity) { |
| 195 | - ? entA.datos.find(d => d.año === hoveredYearComparar) | 192 | + // Para entidad: usar n_entidades del año específico o del histórico |
| 196 | - : null; | 193 | + if (hoveredYear) { |
| 197 | - $: hoveredDataB = hoveredYearComparar | 194 | + const añoData = datosAnuales.find(d => d.gestion === hoveredYear); |
| 198 | - ? entB.datos.find(d => d.año === hoveredYearComparar) | 195 | + return añoData?.n_entidades || nEntidades; |
| 199 | - : null; | 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 | + }); | ||
| 200 | 203 | ||
| 201 | - $: displayMontoA = hoveredDataA ? hoveredDataA.monto : entA.total; | 204 | + // Top entidades histórico (solo para Todo el Estado) |
| 202 | - $: displayMontoLabelA = hoveredDataA ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; | 205 | + let topEntidadesTotal = $derived( |
| 203 | - $: displayPerCapitaA = hoveredDataA ? hoveredDataA.perCapita : entA.promedioPerCapita; | 206 | + datosHistorico && !selectedEntity ? [ |
| 204 | - $: displayPerCapitaLabelA = hoveredDataA ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)'; | 207 | + { nombre: datosHistorico.top1_entidad, monto: datosHistorico.top1_monto, porcentaje: datosHistorico.top1_pct }, |
| 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) : [] | ||
| 211 | + ); | ||
| 205 | 212 | ||
| 206 | - $: displayMontoB = hoveredDataB ? hoveredDataB.monto : entB.total; | 213 | + // Años disponibles |
| 207 | - $: displayMontoLabelB = hoveredDataB ? `gasto en ${hoveredYearComparar}` : 'gasto 2020-2024'; | 214 | + let primerAño = $derived(gastoPerCapita.length > 0 ? gastoPerCapita[0].año : 2005); |
| 208 | - $: displayPerCapitaB = hoveredDataB ? hoveredDataB.perCapita : entB.promedioPerCapita; | 215 | + let ultimoAño = $derived(gastoPerCapita.length > 0 ? gastoPerCapita[gastoPerCapita.length - 1].año : 2025); |
| 209 | - $: displayPerCapitaLabelB = hoveredDataB ? `por persona en ${hoveredYearComparar}` : 'por persona (prom.)'; | ||
| 210 | 216 | ||
| 211 | - $: displayPeriodoComparar = hoveredYearComparar ? hoveredYearComparar : '2020-2024'; | 217 | + // Current view data (changes on hover) |
| 218 | + let currentData = $derived( | ||
| 219 | + hoveredYear ? gastoPerCapita.find(g => g.año === hoveredYear) : null | ||
| 220 | + ); | ||
| 221 | + | ||
| 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( |
| 215 | - ? topEntidadesPorAño[hoveredYear] | 242 | + hoveredYear && topEntidadesPorAño[hoveredYear] |
| 216 | - : topEntidadesTotal; | 243 | + ? topEntidadesPorAño[hoveredYear] |
| 217 | - $: displayTopLabel = hoveredYear ? `en ${hoveredYear}` : '2005-2024'; | 244 | + : topEntidadesTotal |
| 218 | - | 245 | + ); |
| 219 | - // Context data | 246 | + let displayTopLabel = $derived(hoveredYear ? `en ${hoveredYear}` : `${primerAño}-${ultimoAño}`); |
| 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; | ||
| 272 | 319 | ||
| 273 | - $: twMontoA.set(displayMontoA); | 320 | + const tops = year && topsPorAño[year] ? topsPorAño[year] : topsTotal; |
| 274 | - $: twMontoB.set(displayMontoB); | ||
| 275 | - $: twPerCapitaA.set(displayPerCapitaA); | ||
| 276 | - $: twPerCapitaB.set(displayPerCapitaB); | ||
| 277 | 321 | ||
| 278 | - $: if (displayTopEntidades[0]) twTop1.set(displayTopEntidades[0].porcentaje); | 322 | + twTop1.set(toNumber(tops?.[0]?.porcentaje)); |
| 279 | - $: if (displayTopEntidades[1]) twTop2.set(displayTopEntidades[1].porcentaje); | 323 | + twTop2.set(toNumber(tops?.[1]?.porcentaje)); |
| 280 | - $: if (displayTopEntidades[2]) twTop3.set(displayTopEntidades[2].porcentaje); | 324 | + twTop3.set(toNumber(tops?.[2]?.porcentaje)); |
| 325 | + }); | ||
| 326 | + | ||
| 327 | + // ══════════════════════════════════════════════════════════════ | ||
| 328 | + // MOUNT | ||
| 329 | + // ══════════════════════════════════════════════════════════════ | ||
| 330 | + onMount(async () => { | ||
| 331 | + setTimeout(() => { mounted = true; }, 50); | ||
| 281 | 332 | ||
| 333 | + // Leer estado del tema | ||
| 334 | + isDark = document.documentElement.classList.contains('dark'); | ||
| 335 | + | ||
| 336 | + // Observar cambios de tema | ||
| 337 | + const observer = new MutationObserver(() => { | ||
| 338 | + isDark = document.documentElement.classList.contains('dark'); | ||
| 339 | + }); | ||
| 340 | + observer.observe(document.documentElement, { | ||
| 341 | + attributes: true, | ||
| 342 | + attributeFilter: ['class'] | ||
| 343 | + }); | ||
| 344 | + | ||
| 345 | + // Cargar datos | ||
| 346 | + await Promise.all([loadData(), loadEntidades()]); | ||
| 347 | + | ||
| 348 | + // Cerrar dropdown al hacer click fuera | ||
| 349 | + const handleClickOutside = (e) => { | ||
| 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 | + }); | ||
| 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"> |
| 376 | - Todo el sector público | 468 | + <button |
| 377 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | 469 | + class="selector-btn" |
| 378 | - <path d="M6 9l6 6 6-6"/> | 470 | + onclick={() => entityDropdownOpen = !entityDropdownOpen} |
| 379 | - </svg> | 471 | + > |
| 380 | - </button> | 472 | + <span class="truncate"> |
| 473 | + {#if selectedEntity} | ||
| 474 | + {selectedEntity.entidad_desc?.slice(0, 30)}{selectedEntity.entidad_desc?.length > 30 ? '...' : ''} | ||
| 475 | + {:else} | ||
| 476 | + Todo el sector público | ||
| 477 | + {/if} | ||
| 478 | + </span> | ||
| 479 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | ||
| 480 | + <path d="M6 9l6 6 6-6"/> | ||
| 481 | + </svg> | ||
| 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,28 +586,30 @@ | ... | @@ -448,28 +586,30 @@ |
| 448 | </div> | 586 | </div> |
| 449 | </div> | 587 | </div> |
| 450 | 588 | ||
| 451 | - <!-- Top entidades --> | 589 | + <!-- Top entidades (solo visible cuando es "Todo el Estado") --> |
| 452 | - <div class="top-section"> | 590 | + {#if !selectedEntity} |
| 453 | - <div class="top-header"> | 591 | + <div class="top-section"> |
| 454 | - <h3>Mayores ejecutores</h3> | 592 | + <div class="top-header"> |
| 455 | - <span class="top-context">{totalEntidades} entidades · {displayTopLabel}</span> | 593 | + <h3>Mayores ejecutores</h3> |
| 456 | - </div> | 594 | + <span class="top-context">{totalEntidades} entidades · {displayTopLabel}</span> |
| 595 | + </div> | ||
| 457 | 596 | ||
| 458 | - <div class="top-list"> | 597 | + <div class="top-list"> |
| 459 | - {#each displayTopEntidades as ent, i (ent.nombre)} | 598 | + {#each displayTopEntidades as ent, i (ent.nombre)} |
| 460 | - <div class="top-item"> | 599 | + <div class="top-item"> |
| 461 | - <span class="top-rank">#{i + 1}</span> | 600 | + <span class="top-rank">#{i + 1}</span> |
| 462 | - <div class="top-info"> | 601 | + <div class="top-info"> |
| 463 | - <span class="top-name">{ent.nombre}</span> | 602 | + <span class="top-name">{ent.nombre}</span> |
| 464 | - <div class="top-bar-bg"> | 603 | + <div class="top-bar-bg"> |
| 465 | - <div class="top-bar" style="width: {((i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3) / $twTop1) * 100}%"></div> | 604 | + <div class="top-bar" style="width: {((i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3) / $twTop1) * 100}%"></div> |
| 605 | + </div> | ||
| 466 | </div> | 606 | </div> |
| 607 | + <span class="top-pct">{(i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3).toFixed(1)}%</span> | ||
| 467 | </div> | 608 | </div> |
| 468 | - <span class="top-pct">{(i === 0 ? $twTop1 : i === 1 ? $twTop2 : $twTop3).toFixed(1)}%</span> | 609 | + {/each} |
| 469 | - </div> | 610 | + </div> |
| 470 | - {/each} | ||
| 471 | </div> | 611 | </div> |
| 472 | - </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 { | ... | ... |
static/muetra_base.csv
0 → 100644
| 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 |
-
Please register or login to post a comment