Rafael Lopez

buscador

...@@ -228,3 +228,145 @@ body { ...@@ -228,3 +228,145 @@ body {
228 .font-display { 228 .font-display {
229 font-family: var(--font-display); 229 font-family: var(--font-display);
230 } 230 }
231 +
232 +/* ============================================
233 + FALLBACK LAYOUT UTILITIES
234 + Respaldo para asegurar que flex siempre funcione
235 + ============================================ */
236 +
237 +/* Forzar flex en contenedores principales */
238 +.flex {
239 + display: flex !important;
240 +}
241 +
242 +.flex-1 {
243 + flex: 1 1 0% !important;
244 +}
245 +
246 +.flex-col {
247 + flex-direction: column !important;
248 +}
249 +
250 +.flex-row {
251 + flex-direction: row !important;
252 +}
253 +
254 +.flex-wrap {
255 + flex-wrap: wrap !important;
256 +}
257 +
258 +.flex-shrink-0 {
259 + flex-shrink: 0 !important;
260 +}
261 +
262 +.items-center {
263 + align-items: center !important;
264 +}
265 +
266 +.items-start {
267 + align-items: flex-start !important;
268 +}
269 +
270 +.justify-center {
271 + justify-content: center !important;
272 +}
273 +
274 +.justify-between {
275 + justify-content: space-between !important;
276 +}
277 +
278 +.gap-1 { gap: 0.25rem !important; }
279 +.gap-2 { gap: 0.5rem !important; }
280 +.gap-3 { gap: 0.75rem !important; }
281 +.gap-4 { gap: 1rem !important; }
282 +.gap-6 { gap: 1.5rem !important; }
283 +
284 +/* Grid fallbacks */
285 +.grid {
286 + display: grid !important;
287 +}
288 +
289 +/* Hidden utilities */
290 +.hidden {
291 + display: none !important;
292 +}
293 +
294 +/* Responsive: mostrar en lg+ */
295 +@media (min-width: 1024px) {
296 + .lg\:flex {
297 + display: flex !important;
298 + }
299 + .lg\:block {
300 + display: block !important;
301 + }
302 + .lg\:hidden {
303 + display: none !important;
304 + }
305 + .lg\:relative {
306 + position: relative !important;
307 + }
308 + .lg\:translate-x-0 {
309 + transform: translateX(0) !important;
310 + }
311 +}
312 +
313 +@media (min-width: 1280px) {
314 + .xl\:block {
315 + display: block !important;
316 + }
317 + .xl\:hidden {
318 + display: none !important;
319 + }
320 +}
321 +
322 +/* ============================================
323 + CLASIFICADORES LAYOUT - ESTILOS GLOBALES
324 + Forzar layout correcto en todas las páginas
325 + ============================================ */
326 +
327 +.clasificador-layout {
328 + display: flex !important;
329 + flex-direction: row !important;
330 +}
331 +
332 +.clasificador-layout > .sidebar-left {
333 + position: relative !important;
334 + transform: translateX(0) !important;
335 + flex-shrink: 0 !important;
336 + width: 18rem !important;
337 + z-index: auto !important;
338 + box-shadow: none !important;
339 +}
340 +
341 +.clasificador-layout > main {
342 + flex: 1 1 0% !important;
343 + min-width: 0 !important;
344 +}
345 +
346 +/* Forzar estilos correctos después del mount (para navegación cliente) */
347 +@media (min-width: 1024px) {
348 + .sidebar-mounted.sidebar-left {
349 + position: relative !important;
350 + transform: translateX(0) !important;
351 + flex-shrink: 0 !important;
352 + z-index: auto !important;
353 + box-shadow: none !important;
354 + }
355 +}
356 +
357 +@media (max-width: 1023px) {
358 + .clasificador-layout {
359 + display: block !important;
360 + }
361 +
362 + .clasificador-layout > .sidebar-left {
363 + position: fixed !important;
364 + transform: translateX(-100%) !important;
365 + width: 20rem !important;
366 + z-index: 50 !important;
367 + }
368 +
369 + .clasificador-layout > .sidebar-left.translate-x-0 {
370 + transform: translateX(0) !important;
371 + }
372 +}
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
17 17
18 %sveltekit.head% 18 %sveltekit.head%
19 </head> 19 </head>
20 - <body data-sveltekit-preload-data="hover"> 20 + <body data-sveltekit-preload-data="off">
21 <div style="display: contents">%sveltekit.body%</div> 21 <div style="display: contents">%sveltekit.body%</div>
22 </body> 22 </body>
23 </html> 23 </html>
......
...@@ -33,7 +33,12 @@ ...@@ -33,7 +33,12 @@
33 33
34 // Dimensiones del área del gráfico 34 // Dimensiones del área del gráfico
35 let effectiveHeight = $derived(fill ? measuredHeight : height); 35 let effectiveHeight = $derived(fill ? measuredHeight : height);
36 - let chartHeight = $derived(effectiveHeight - marginTop - marginBottom); 36 + let chartHeight = $derived(Math.max(effectiveHeight - marginTop - marginBottom, 50));
37 +
38 + // Máximo del eje Y (con guard para data vacía o valores undefined)
39 + let maxPerCapita = $derived(
40 + data.length > 0 ? Math.max(...data.map(d => d.perCapita || 0), 1) : 1
41 + );
37 42
38 // Escalas D3 43 // Escalas D3
39 let xScale = $derived( 44 let xScale = $derived(
...@@ -46,7 +51,7 @@ ...@@ -46,7 +51,7 @@
46 // Para CSS bottom positioning: 0 → 0%, max → 100% 51 // Para CSS bottom positioning: 0 → 0%, max → 100%
47 let yScale = $derived( 52 let yScale = $derived(
48 scaleLinear() 53 scaleLinear()
49 - .domain([0, Math.max(...data.map(d => d.perCapita))]) 54 + .domain([0, maxPerCapita])
50 .range([0, chartHeight]) 55 .range([0, chartHeight])
51 .nice() 56 .nice()
52 ); 57 );
...@@ -65,7 +70,8 @@ ...@@ -65,7 +70,8 @@
65 } 70 }
66 </script> 71 </script>
67 72
68 -<div class="chart-wrapper" bind:this={wrapperEl} style="height: {fill ? '100%' : height + 'px'}"> 73 +<div class="chart-wrapper" bind:this={wrapperEl} style="height: {fill ? '100%' : height + 'px'}; min-height: {height}px">
74 + {#if data.length > 0 && chartHeight > 0}
69 <!-- Contenedor principal con márgenes --> 75 <!-- Contenedor principal con márgenes -->
70 <div class="chart-inner" style="top: {marginTop}px; bottom: {marginBottom}px; left: {marginLeft}px; right: {marginRight}px"> 76 <div class="chart-inner" style="top: {marginTop}px; bottom: {marginBottom}px; left: {marginLeft}px; right: {marginRight}px">
71 77
...@@ -118,6 +124,11 @@ ...@@ -118,6 +124,11 @@
118 </span> 124 </span>
119 {/each} 125 {/each}
120 </div> 126 </div>
127 + {:else}
128 + <div class="chart-empty">
129 + <span>Sin datos</span>
130 + </div>
131 + {/if}
121 </div> 132 </div>
122 133
123 <style> 134 <style>
...@@ -210,4 +221,15 @@ ...@@ -210,4 +221,15 @@
210 .x-label.visible { 221 .x-label.visible {
211 opacity: 1; 222 opacity: 1;
212 } 223 }
224 +
225 + .chart-empty {
226 + position: absolute;
227 + inset: 0;
228 + display: flex;
229 + align-items: center;
230 + justify-content: center;
231 + color: var(--theme-texto);
232 + opacity: 0.5;
233 + font-size: 0.875rem;
234 + }
213 </style> 235 </style>
......
1 <script> 1 <script>
2 import { goto } from '$app/navigation'; 2 import { goto } from '$app/navigation';
3 - import { 3 + import { indexLoaded } from '$lib/stores/searchStore';
4 - query, 4 + import { search } from '$lib/services/search';
5 - results,
6 - isLoading as searchLoading,
7 - indexLoaded,
8 - selectedIndex,
9 - performSearch,
10 - clearSearch,
11 - navigateResults
12 - } from '$lib/stores/searchStore';
13 5
14 let { 6 let {
15 preserveVista = false, 7 preserveVista = false,
...@@ -19,19 +11,23 @@ ...@@ -19,19 +11,23 @@
19 let searchInputRef = $state(null); 11 let searchInputRef = $state(null);
20 let searchVal = $state(''); 12 let searchVal = $state('');
21 let searchFocused = $state(false); 13 let searchFocused = $state(false);
14 + let isSearching = $state(false);
15 + let localResults = $state([]);
16 + let selectedIdx = $state(-1);
22 let debounceTimer; 17 let debounceTimer;
23 18
24 - // Filtrar solo objeto_gasto
25 - let filteredSearchResults = $derived($results.filter(r => r.tipo === 'objeto_gasto'));
26 -
27 function handleSearchInput(e) { 19 function handleSearchInput(e) {
28 const val = e.target.value; 20 const val = e.target.value;
29 clearTimeout(debounceTimer); 21 clearTimeout(debounceTimer);
30 debounceTimer = setTimeout(() => { 22 debounceTimer = setTimeout(() => {
31 if (val.length >= 2) { 23 if (val.length >= 2) {
32 - performSearch(val); 24 + isSearching = true;
25 + // Buscar y filtrar solo objetos de gasto
26 + const allResults = search(val, 50);
27 + localResults = allResults.filter(r => r.tipo === 'objeto_gasto').slice(0, 15);
28 + isSearching = false;
33 } else { 29 } else {
34 - clearSearch(); 30 + localResults = [];
35 } 31 }
36 }, 200); 32 }, 200);
37 } 33 }
...@@ -39,14 +35,18 @@ ...@@ -39,14 +35,18 @@
39 function handleSearchKeydown(e) { 35 function handleSearchKeydown(e) {
40 if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { 36 if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
41 e.preventDefault(); 37 e.preventDefault();
42 - navigateResults(e.key === 'ArrowDown' ? 1 : -1, filteredSearchResults.length); 38 + if (e.key === 'ArrowDown') {
43 - } else if (e.key === 'Enter' && filteredSearchResults.length > 0) { 39 + selectedIdx = selectedIdx < localResults.length - 1 ? selectedIdx + 1 : 0;
40 + } else {
41 + selectedIdx = selectedIdx > 0 ? selectedIdx - 1 : localResults.length - 1;
42 + }
43 + } else if (e.key === 'Enter' && localResults.length > 0) {
44 e.preventDefault(); 44 e.preventDefault();
45 - const selected = filteredSearchResults[$selectedIndex]; 45 + const selected = localResults[selectedIdx >= 0 ? selectedIdx : 0];
46 if (selected) handleSearchSelect(selected); 46 if (selected) handleSearchSelect(selected);
47 } else if (e.key === 'Escape') { 47 } else if (e.key === 'Escape') {
48 searchVal = ''; 48 searchVal = '';
49 - clearSearch(); 49 + localResults = [];
50 searchInputRef?.blur(); 50 searchInputRef?.blur();
51 } 51 }
52 } 52 }
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
57 : `/objeto/${result.codigo}`; 57 : `/objeto/${result.codigo}`;
58 goto(url); 58 goto(url);
59 searchVal = ''; 59 searchVal = '';
60 - clearSearch(); 60 + localResults = [];
61 searchFocused = false; 61 searchFocused = false;
62 } 62 }
63 63
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
68 } 68 }
69 69
70 function handleClear() { 70 function handleClear() {
71 - clearSearch(); 71 + localResults = [];
72 searchVal = ''; 72 searchVal = '';
73 } 73 }
74 </script> 74 </script>
...@@ -97,17 +97,17 @@ ...@@ -97,17 +97,17 @@
97 {/if} 97 {/if}
98 {#if searchFocused && searchVal.length >= 2} 98 {#if searchFocused && searchVal.length >= 2}
99 <div class="search-dropdown"> 99 <div class="search-dropdown">
100 - {#if $searchLoading} 100 + {#if isSearching}
101 <div class="search-msg">Buscando...</div> 101 <div class="search-msg">Buscando...</div>
102 - {:else if filteredSearchResults.length === 0} 102 + {:else if localResults.length === 0}
103 <div class="search-msg">Sin resultados</div> 103 <div class="search-msg">Sin resultados</div>
104 {:else} 104 {:else}
105 - {#each filteredSearchResults as result, i} 105 + {#each localResults as result, i}
106 <button 106 <button
107 class="search-item" 107 class="search-item"
108 - class:selected={$selectedIndex === i} 108 + class:selected={selectedIdx === i}
109 onclick={() => handleSearchSelect(result)} 109 onclick={() => handleSearchSelect(result)}
110 - onmouseenter={() => selectedIndex.set(i)} 110 + onmouseenter={() => selectedIdx = i}
111 > 111 >
112 <span class="item-code">{result.codigo}</span> 112 <span class="item-code">{result.codigo}</span>
113 <span class="item-name">{result.nombre}</span> 113 <span class="item-name">{result.nombre}</span>
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
...@@ -54,7 +54,7 @@ export async function initSearchIndex() { ...@@ -54,7 +54,7 @@ export async function initSearchIndex() {
54 const { data: rubros, error: errorRubros } = await supabase 54 const { data: rubros, error: errorRubros } = await supabase
55 .schema('ppto') 55 .schema('ppto')
56 .from('clas_rubros') 56 .from('clas_rubros')
57 - .select('rubro, desc_rubros, nivel'); 57 + .select('rubro, desc_rubro, nivel');
58 58
59 if (errorRubros) { 59 if (errorRubros) {
60 console.error('Error cargando rubros:', errorRubros); 60 console.error('Error cargando rubros:', errorRubros);
...@@ -93,11 +93,11 @@ export async function initSearchIndex() { ...@@ -93,11 +93,11 @@ export async function initSearchIndex() {
93 const rubrosIndex = rubros.map(item => ({ 93 const rubrosIndex = rubros.map(item => ({
94 tipo: 'rubro', 94 tipo: 'rubro',
95 codigo: item.rubro, 95 codigo: item.rubro,
96 - nombre: item.desc_rubros, 96 + nombre: item.desc_rubro,
97 sigla: null, 97 sigla: null,
98 contexto: `Rubro · ${nivelLabelsRubros[item.nivel] || item.nivel}`, 98 contexto: `Rubro · ${nivelLabelsRubros[item.nivel] || item.nivel}`,
99 nivel: item.nivel, 99 nivel: item.nivel,
100 - nombre_normalizado: normalizeText(item.desc_rubros), 100 + nombre_normalizado: normalizeText(item.desc_rubro),
101 sigla_normalizada: '', 101 sigla_normalizada: '',
102 codigo_normalizado: item.rubro?.toString() || '' 102 codigo_normalizado: item.rubro?.toString() || ''
103 })); 103 }));
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
4 import favicon from '$lib/assets/favicon.svg'; 4 import favicon from '$lib/assets/favicon.svg';
5 import Navbar from '$lib/components/ui/Navbar.svelte'; 5 import Navbar from '$lib/components/ui/Navbar.svelte';
6 import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte'; 6 import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte';
7 + import SearchModal from '$lib/components/ui/SearchModal.svelte';
7 import { openDrawer } from '$lib/stores/drawer.js'; 8 import { openDrawer } from '$lib/stores/drawer.js';
8 import Spinner from '$lib/components/ui/Spinner.svelte'; 9 import Spinner from '$lib/components/ui/Spinner.svelte';
9 10
...@@ -12,6 +13,21 @@ ...@@ -12,6 +13,21 @@
12 // Don't show navbar on landing page 13 // Don't show navbar on landing page
13 let isLanding = $derived($page.url.pathname === '/'); 14 let isLanding = $derived($page.url.pathname === '/');
14 15
16 + // Global search modal state
17 + let searchModalOpen = $state(false);
18 +
19 + function openSearchModal() {
20 + searchModalOpen = true;
21 + }
22 +
23 + function handleGlobalKeydown(e) {
24 + // Cmd+K or Ctrl+K to open search
25 + if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
26 + e.preventDefault();
27 + searchModalOpen = true;
28 + }
29 + }
30 +
15 // Navigation loading state 31 // Navigation loading state
16 let showLoader = $state(false); 32 let showLoader = $state(false);
17 let loaderTimeout = null; 33 let loaderTimeout = null;
...@@ -42,12 +58,17 @@ ...@@ -42,12 +58,17 @@
42 <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" /> 58 <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=Instrument+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
43 </svelte:head> 59 </svelte:head>
44 60
61 +<svelte:window onkeydown={handleGlobalKeydown} />
62 +
45 <!-- Navigation Drawer (siempre presente) --> 63 <!-- Navigation Drawer (siempre presente) -->
46 <NavigationDrawer /> 64 <NavigationDrawer />
47 65
66 +<!-- Global Search Modal -->
67 +<SearchModal bind:open={searchModalOpen} />
68 +
48 <!-- Floating controls bar (fixed position - no afecta el flujo) --> 69 <!-- Floating controls bar (fixed position - no afecta el flujo) -->
49 {#if !isLanding} 70 {#if !isLanding}
50 - <Navbar onOpenDrawer={openDrawer} /> 71 + <Navbar onOpenDrawer={openDrawer} onOpenSearch={openSearchModal} />
51 {/if} 72 {/if}
52 73
53 <!-- Navigation loading overlay --> 74 <!-- Navigation loading overlay -->
......
This diff is collapsed. Click to expand it.
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
28 } 28 }
29 29
30 onMount(async () => { 30 onMount(async () => {
31 + // Forzar recálculo de layout para navegación cliente
32 + requestAnimationFrame(() => {
33 + document.body.offsetHeight; // Force reflow
34 + });
35 +
31 const { data, error } = await supabase 36 const { data, error } = await supabase
32 .schema('ppto') 37 .schema('ppto')
33 .from('clas_institucional') 38 .from('clas_institucional')
...@@ -307,7 +312,7 @@ ...@@ -307,7 +312,7 @@
307 </button> 312 </button>
308 </div> 313 </div>
309 314
310 - <div class="max-w-screen-xl mx-auto flex px-4"> 315 + <div class="clasificador-layout max-w-screen-xl mx-auto flex px-4" style="display: flex !important; flex-direction: row;">
311 <!-- Sidebar izquierda: Áreas --> 316 <!-- Sidebar izquierda: Áreas -->
312 <!-- En móvil: overlay, en desktop: sidebar fijo --> 317 <!-- En móvil: overlay, en desktop: sidebar fijo -->
313 {#if sidebarOpen} 318 {#if sidebarOpen}
...@@ -611,4 +616,33 @@ ...@@ -611,4 +616,33 @@
611 background-color: transparent; 616 background-color: transparent;
612 } 617 }
613 } 618 }
619 +
620 + /* Layout principal - fallback nativo */
621 + .clasificador-layout {
622 + display: flex !important;
623 + flex-direction: row !important;
624 + }
625 +
626 + /* Sidebar - fallback para responsive */
627 + .sidebar-left {
628 + position: fixed;
629 + transform: translateX(-100%);
630 + }
631 +
632 + @media (min-width: 1024px) {
633 + .sidebar-left {
634 + position: relative !important;
635 + transform: translateX(0) !important;
636 + flex-shrink: 0;
637 + width: 16rem;
638 + z-index: auto !important;
639 + box-shadow: none !important;
640 + }
641 + }
642 +
643 + @media (max-width: 1023px) {
644 + .clasificador-layout {
645 + display: block !important;
646 + }
647 + }
614 </style> 648 </style>
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 +import { supabase } from '$lib/supabase';
2 +import { error } from '@sveltejs/kit';
3 +
4 +// Funciones para derivar jerarquía del código finfun
5 +// Jerarquía: Finalidad (1 dígito) → Grupo Función (2 dígitos) → Función (3+ dígitos)
6 +function getFinalidadCode(finfun) {
7 + return finfun.charAt(0);
8 +}
9 +
10 +function getGrpFuncionCode(finfun) {
11 + return finfun.substring(0, 2);
12 +}
13 +
14 +function getNivel(finfun) {
15 + if (finfun.length === 1) return 'finalidad';
16 + if (finfun.length === 2) return 'grpfuncion';
17 + return 'funcion';
18 +}
19 +
20 +export async function load({ params }) {
21 + console.time('[SERVER] Total load finfun');
22 + const { codigo } = params;
23 +
24 + console.time('[SERVER] Query clas_finfun');
25 + const { data, error: dbError } = await supabase
26 + .schema('ppto')
27 + .from('clas_finfun')
28 + .select('*')
29 + .eq('finfun', codigo);
30 + console.timeEnd('[SERVER] Query clas_finfun');
31 +
32 + if (dbError || !data || data.length === 0) {
33 + throw error(404, 'Finalidad/Función no encontrada');
34 + }
35 +
36 + // Tomar el primer resultado
37 + const finfun = data[0];
38 + const nivel = finfun.nivel || getNivel(codigo);
39 +
40 + // Obtener jerarquía (padres e hijos)
41 + let padres = [];
42 + let hijos = [];
43 +
44 + console.time('[SERVER] Query padres');
45 +
46 + // Buscar padres según nivel
47 + if (nivel === 'funcion') {
48 + // Padres: finalidad y grupo función
49 + const finalidadCode = getFinalidadCode(codigo);
50 + const grpFuncionCode = getGrpFuncionCode(codigo);
51 +
52 + const { data: padresData } = await supabase
53 + .schema('ppto')
54 + .from('clas_finfun')
55 + .select('*')
56 + .in('finfun', [finalidadCode, grpFuncionCode])
57 + .order('finfun');
58 +
59 + if (padresData) padres = padresData;
60 + } else if (nivel === 'grpfuncion') {
61 + // Padre: finalidad
62 + const finalidadCode = getFinalidadCode(codigo);
63 +
64 + const { data: padresData } = await supabase
65 + .schema('ppto')
66 + .from('clas_finfun')
67 + .select('*')
68 + .eq('finfun', finalidadCode);
69 +
70 + if (padresData) padres = padresData;
71 + }
72 + // finalidad no tiene padres
73 +
74 + console.timeEnd('[SERVER] Query padres');
75 +
76 + console.time('[SERVER] Query hijos');
77 +
78 + // Buscar hijos según nivel
79 + if (nivel === 'finalidad') {
80 + // Hijos: grupos de función que empiecen con el mismo dígito
81 + const { data: hijosData } = await supabase
82 + .schema('ppto')
83 + .from('clas_finfun')
84 + .select('*')
85 + .eq('nivel', 'grpfuncion')
86 + .like('finfun', `${codigo}%`)
87 + .order('finfun');
88 +
89 + hijos = hijosData || [];
90 + } else if (nivel === 'grpfuncion') {
91 + // Hijos: funciones que empiecen con los mismos 2 dígitos
92 + const { data: hijosData } = await supabase
93 + .schema('ppto')
94 + .from('clas_finfun')
95 + .select('*')
96 + .eq('nivel', 'funcion')
97 + .like('finfun', `${codigo}%`)
98 + .order('finfun');
99 +
100 + hijos = hijosData || [];
101 + }
102 + // funcion no tiene hijos
103 +
104 + console.timeEnd('[SERVER] Query hijos');
105 +
106 + console.timeEnd('[SERVER] Total load finfun');
107 + return {
108 + finfun,
109 + padres,
110 + hijos
111 + };
112 +}
This diff could not be displayed because it is too large.
1 import { supabase } from '$lib/supabase'; 1 import { supabase } from '$lib/supabase';
2 import { error } from '@sveltejs/kit'; 2 import { error } from '@sveltejs/kit';
3 3
4 +// Funciones para derivar jerarquía del código objeto
5 +function getGrupoCode(objeto) {
6 + return objeto.charAt(0) + '0000';
7 +}
8 +
9 +function getSubgrupoCode(objeto) {
10 + return objeto.substring(0, 2) + '000';
11 +}
12 +
13 +function getPartidaCode(objeto) {
14 + return objeto.substring(0, 3) + '00';
15 +}
16 +
17 +function getNivel(objeto) {
18 + if (objeto.endsWith('0000')) return 'grupo';
19 + if (objeto.endsWith('000')) return 'subgrupo';
20 + if (objeto.endsWith('00')) return 'partida';
21 + return 'subpartida';
22 +}
23 +
4 export async function load({ params }) { 24 export async function load({ params }) {
5 console.time('[SERVER] Total load objeto'); 25 console.time('[SERVER] Total load objeto');
6 const { codigo } = params; 26 const { codigo } = params;
...@@ -17,95 +37,102 @@ export async function load({ params }) { ...@@ -17,95 +37,102 @@ export async function load({ params }) {
17 throw error(404, 'Objeto no encontrado'); 37 throw error(404, 'Objeto no encontrado');
18 } 38 }
19 39
20 - // Tomar el primer resultado (puede haber duplicados) 40 + // Tomar el primer resultado
21 const objeto = data[0]; 41 const objeto = data[0];
42 + const nivel = objeto.nivel || getNivel(codigo);
22 43
23 // Obtener jerarquía (padres e hijos) 44 // Obtener jerarquía (padres e hijos)
24 let padres = []; 45 let padres = [];
25 let hijos = []; 46 let hijos = [];
26 47
27 console.time('[SERVER] Query padres'); 48 console.time('[SERVER] Query padres');
28 - // Buscar padre según nivel 49 +
29 - if (objeto.nivel === 'subpartida') { 50 + // Buscar padres según nivel
30 - // Padre es partida 51 + if (nivel === 'subpartida') {
31 - const { data: padreData } = await supabase 52 + // Padres: grupo, subgrupo, partida
53 + const grupoCode = getGrupoCode(codigo);
54 + const subgrupoCode = getSubgrupoCode(codigo);
55 + const partidaCode = getPartidaCode(codigo);
56 +
57 + const { data: padresData } = await supabase
32 .schema('ppto') 58 .schema('ppto')
33 .from('clas_objetos') 59 .from('clas_objetos')
34 .select('*') 60 .select('*')
35 - .eq('nivel', 'partida') 61 + .in('objeto', [grupoCode, subgrupoCode, partidaCode])
36 - .eq('grupo', objeto.grupo) 62 + .order('objeto');
37 - .eq('subgrupo', objeto.subgrupo)
38 - .eq('partida', objeto.partida);
39 - if (padreData?.length) padres.push(padreData[0]);
40 - }
41 63
42 - if (objeto.nivel === 'partida' || objeto.nivel === 'subpartida') { 64 + if (padresData) padres = padresData;
43 - // Padre es subgrupo 65 + } else if (nivel === 'partida') {
44 - const { data: padreData } = await supabase 66 + // Padres: grupo, subgrupo
67 + const grupoCode = getGrupoCode(codigo);
68 + const subgrupoCode = getSubgrupoCode(codigo);
69 +
70 + const { data: padresData } = await supabase
45 .schema('ppto') 71 .schema('ppto')
46 .from('clas_objetos') 72 .from('clas_objetos')
47 .select('*') 73 .select('*')
48 - .eq('nivel', 'subgrupo') 74 + .in('objeto', [grupoCode, subgrupoCode])
49 - .eq('grupo', objeto.grupo) 75 + .order('objeto');
50 - .eq('subgrupo', objeto.subgrupo); 76 +
51 - if (padreData?.length) padres.unshift(padreData[0]); 77 + if (padresData) padres = padresData;
52 - } 78 + } else if (nivel === 'subgrupo') {
79 + // Padre: grupo
80 + const grupoCode = getGrupoCode(codigo);
53 81
54 - if (objeto.nivel !== 'grupo') { 82 + const { data: padresData } = await supabase
55 - // Padre es grupo
56 - const grupoCodigo = objeto.grupo + '0000';
57 - const { data: padreData } = await supabase
58 .schema('ppto') 83 .schema('ppto')
59 .from('clas_objetos') 84 .from('clas_objetos')
60 .select('*') 85 .select('*')
61 - .eq('nivel', 'grupo') 86 + .eq('objeto', grupoCode);
62 - .eq('objeto', grupoCodigo); 87 +
63 - if (padreData?.length) padres.unshift(padreData[0]); 88 + if (padresData) padres = padresData;
64 } 89 }
90 + // grupo no tiene padres
91 +
65 console.timeEnd('[SERVER] Query padres'); 92 console.timeEnd('[SERVER] Query padres');
66 93
67 console.time('[SERVER] Query hijos'); 94 console.time('[SERVER] Query hijos');
95 +
68 // Buscar hijos según nivel 96 // Buscar hijos según nivel
69 - if (objeto.nivel === 'grupo') { 97 + if (nivel === 'grupo') {
98 + // Hijos: subgrupos que empiecen con el mismo dígito
99 + const prefix = codigo.charAt(0);
70 const { data: hijosData } = await supabase 100 const { data: hijosData } = await supabase
71 .schema('ppto') 101 .schema('ppto')
72 .from('clas_objetos') 102 .from('clas_objetos')
73 .select('*') 103 .select('*')
74 .eq('nivel', 'subgrupo') 104 .eq('nivel', 'subgrupo')
75 - .eq('grupo', objeto.grupo) 105 + .like('objeto', `${prefix}%`)
76 .order('objeto'); 106 .order('objeto');
77 - hijos = hijosData?.reduce((acc, item) => { 107 +
78 - if (!acc.find(h => h.objeto === item.objeto)) acc.push(item); 108 + hijos = hijosData || [];
79 - return acc; 109 + } else if (nivel === 'subgrupo') {
80 - }, []) || []; 110 + // Hijos: partidas que empiecen con los mismos 2 dígitos
81 - } else if (objeto.nivel === 'subgrupo') { 111 + const prefix = codigo.substring(0, 2);
82 const { data: hijosData } = await supabase 112 const { data: hijosData } = await supabase
83 .schema('ppto') 113 .schema('ppto')
84 .from('clas_objetos') 114 .from('clas_objetos')
85 .select('*') 115 .select('*')
86 .eq('nivel', 'partida') 116 .eq('nivel', 'partida')
87 - .eq('grupo', objeto.grupo) 117 + .like('objeto', `${prefix}%`)
88 - .eq('subgrupo', objeto.subgrupo)
89 .order('objeto'); 118 .order('objeto');
90 - hijos = hijosData?.reduce((acc, item) => { 119 +
91 - if (!acc.find(h => h.objeto === item.objeto)) acc.push(item); 120 + hijos = hijosData || [];
92 - return acc; 121 + } else if (nivel === 'partida') {
93 - }, []) || []; 122 + // Hijos: subpartidas que empiecen con los mismos 3 dígitos
94 - } else if (objeto.nivel === 'partida') { 123 + const prefix = codigo.substring(0, 3);
95 const { data: hijosData } = await supabase 124 const { data: hijosData } = await supabase
96 .schema('ppto') 125 .schema('ppto')
97 .from('clas_objetos') 126 .from('clas_objetos')
98 .select('*') 127 .select('*')
99 .eq('nivel', 'subpartida') 128 .eq('nivel', 'subpartida')
100 - .eq('grupo', objeto.grupo) 129 + .like('objeto', `${prefix}%`)
101 - .eq('subgrupo', objeto.subgrupo)
102 - .eq('partida', objeto.partida)
103 .order('objeto'); 130 .order('objeto');
104 - hijos = hijosData?.reduce((acc, item) => { 131 +
105 - if (!acc.find(h => h.objeto === item.objeto)) acc.push(item); 132 + hijos = hijosData || [];
106 - return acc;
107 - }, []) || [];
108 } 133 }
134 + // subpartida no tiene hijos
135 +
109 console.timeEnd('[SERVER] Query hijos'); 136 console.timeEnd('[SERVER] Query hijos');
110 137
111 console.timeEnd('[SERVER] Total load objeto'); 138 console.timeEnd('[SERVER] Total load objeto');
......
...@@ -357,12 +357,16 @@ ...@@ -357,12 +357,16 @@
357 // VALORES DERIVADOS 357 // VALORES DERIVADOS
358 // ══════════════════════════════════════════════════════════════ 358 // ══════════════════════════════════════════════════════════════
359 359
360 + // Constante de población para cálculos per cápita
361 + const POBLACION = 12000000;
362 +
360 // Datos para el gráfico (monto o per_capita según preferencia) 363 // Datos para el gráfico (monto o per_capita según preferencia)
364 + // Nota: vista_objeto_entidad solo tiene 'devengado', no 'per_capita'
361 let gastoPerCapita = $derived( 365 let gastoPerCapita = $derived(
362 datosAnuales.map(d => ({ 366 datosAnuales.map(d => ({
363 año: d.gestion, 367 año: d.gestion,
364 - monto: selectedEntity ? d.monto : d.total, 368 + monto: selectedEntity ? (d.monto ?? d.devengado) : d.total,
365 - perCapita: d.per_capita 369 + perCapita: d.per_capita ?? (d.devengado ? d.devengado / POBLACION : 0)
366 })) 370 }))
367 ); 371 );
368 372
...@@ -991,7 +995,7 @@ ...@@ -991,7 +995,7 @@
991 <div class="page" class:mounted> 995 <div class="page" class:mounted>
992 <!-- Link a clasificadores --> 996 <!-- Link a clasificadores -->
993 <nav class="page-nav"> 997 <nav class="page-nav">
994 - <a href="/clasificadores/objeto-gasto" class="back-link"> 998 + <a href="/clasificadores/objeto-gasto" class="back-link" data-sveltekit-reload>
995 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 999 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
996 <path d="M19 12H5M12 19l-7-7 7-7"/> 1000 <path d="M19 12H5M12 19l-7-7 7-7"/>
997 </svg> 1001 </svg>
...@@ -1149,7 +1153,13 @@ ...@@ -1149,7 +1153,13 @@
1149 <div class="chart-header"> 1153 <div class="chart-header">
1150 <span class="chart-title">Bs/habitante <span class="chart-period">· {displayPeriodo}</span></span> 1154 <span class="chart-title">Bs/habitante <span class="chart-period">· {displayPeriodo}</span></span>
1151 </div> 1155 </div>
1156 + {#if loading}
1157 + <div class="chart-loading" style="height: 280px;">
1158 + <span>Cargando...</span>
1159 + </div>
1160 + {:else}
1152 <BarChart data={gastoPerCapita} bind:hoveredYear height={280} fill={!!selectedEntity} /> 1161 <BarChart data={gastoPerCapita} bind:hoveredYear height={280} fill={!!selectedEntity} />
1162 + {/if}
1153 1163
1154 <!-- KPIs abajo --> 1164 <!-- KPIs abajo -->
1155 <div class="kpis"> 1165 <div class="kpis">
...@@ -2581,6 +2591,17 @@ ...@@ -2581,6 +2591,17 @@
2581 letter-spacing: 0; 2591 letter-spacing: 0;
2582 } 2592 }
2583 2593
2594 + .chart-loading {
2595 + display: flex;
2596 + align-items: center;
2597 + justify-content: center;
2598 + color: var(--theme-texto);
2599 + opacity: 0.5;
2600 + font-size: 0.875rem;
2601 + border: 1px dashed var(--theme-borde);
2602 + border-radius: 8px;
2603 + }
2604 +
2584 .chart-controls { 2605 .chart-controls {
2585 display: flex; 2606 display: flex;
2586 align-items: center; 2607 align-items: center;
......
1 +<script>
2 + let institucion = "Ministerio de Economía y Finanzas Públicas";
3 + let pais = "Bolivia";
4 +</script>
5 +
6 +<svelte:head>
7 + <title>Sobre | Presupuesto Público</title>
8 + <link rel="preconnect" href="https://fonts.googleapis.com" />
9 + <link href="https://fonts.googleapis.com/css2?family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;1,8..60,300;1,8..60,400&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet" />
10 +</svelte:head>
11 +
12 +<div class="manifesto-outer">
13 + <div class="manifesto-inner">
14 +
15 + <div class="manifesto-label">
16 + Presentación &nbsp;·&nbsp; Liberación de la base histórica
17 + </div>
18 +
19 + <div class="manifesto-fecha">
20 + A fines de 2025 decidimos abrir los datos históricos<br>
21 + de presupuesto del Estado boliviano.
22 + </div>
23 +
24 + <div class="manifesto-cuerpo">
25 +
26 + <p class="p1">
27 + Cada año el Estado recibe y gasta dinero a tu nombre. ¿Sabes de dónde viene,
28 + en qué se usa y qué se logra con él? Esa información es tuya. Siempre lo fue,
29 + pero durante décadas permaneció encerrada en archivos PDF, en sistemas que pocos
30 + saben usar y en las paredes de un ministerio.
31 + </p>
32 +
33 + <div class="manifesto-hoy">Hoy cambia eso.</div>
34 +
35 + <p class="p3">
36 + Aquí puedes explorar más de veinte años de ejecución presupuestaria, con
37 + actualización semanal. Puedes ver qué hizo el Estado en tu región, en tu sector
38 + y en el año que te interesa. Puedes comparar, cuestionar y entender. Y si
39 + encuentras algo que no cuadra o que vale la pena contar, puedes hacerlo tuyo.
40 + </p>
41 +
42 + <hr class="manifesto-regla" />
43 +
44 + <p class="p4">
45 + No publicamos esto para cumplir un trámite de transparencia. Lo hacemos porque
46 + creemos que un Estado que trabaja de espaldas a su sociedad se vuelve impune,
47 + y una sociedad que no entiende al Estado se desconecta de él.
48 + </p>
49 +
50 + <blockquote class="manifesto-apuesta">
51 + Un presupuesto abierto es una apuesta: que la confianza se construye con
52 + <strong>información</strong> y no con promesas.
53 + </blockquote>
54 +
55 + </div>
56 +
57 + <div class="manifesto-cierre">
58 + <p class="manifesto-cierre-texto">Abrimos para no cerrar.</p>
59 + <p class="manifesto-cierre-sub">{institucion} &nbsp;·&nbsp; {pais}</p>
60 + </div>
61 +
62 + </div>
63 +</div>
64 +
65 +<style>
66 + .manifesto-outer {
67 + min-height: 100vh;
68 + background: var(--manifesto-bg);
69 + overflow: hidden;
70 + }
71 +
72 + .manifesto-inner {
73 + max-width: 640px;
74 + margin: 0 auto;
75 + padding: 72px 48px 88px;
76 + font-family: 'Source Serif 4', Georgia, 'Times New Roman', serif;
77 + color: var(--manifesto-text);
78 + }
79 +
80 + /* === etiqueta superior === */
81 +
82 + .manifesto-label {
83 + font-family: 'JetBrains Mono', 'Courier New', monospace;
84 + font-size: 10px;
85 + letter-spacing: 0.18em;
86 + color: var(--manifesto-muted);
87 + text-transform: uppercase;
88 + margin-bottom: 56px;
89 + display: flex;
90 + align-items: center;
91 + gap: 12px;
92 + opacity: 0;
93 + animation: fadeUp 0.6s ease forwards;
94 + }
95 +
96 + .manifesto-label::after {
97 + content: '';
98 + flex: 1;
99 + height: 0.5px;
100 + background: var(--manifesto-rule);
101 + }
102 +
103 + /* === fecha === */
104 +
105 + .manifesto-fecha {
106 + font-family: 'Source Serif 4', Georgia, serif;
107 + font-style: italic;
108 + font-weight: 300;
109 + font-size: 14px;
110 + color: var(--manifesto-subtle);
111 + letter-spacing: 0.02em;
112 + margin-bottom: 40px;
113 + opacity: 0;
114 + animation: fadeUp 0.6s ease 0.1s forwards;
115 + }
116 +
117 + /* === cuerpo === */
118 +
119 + .manifesto-cuerpo {
120 + font-size: 18.5px;
121 + line-height: 1.78;
122 + font-weight: 300;
123 + color: var(--manifesto-body);
124 + letter-spacing: 0.01em;
125 + }
126 +
127 + .p1 {
128 + opacity: 0;
129 + animation: fadeUp 0.7s ease 0.2s forwards;
130 + margin-bottom: 28px;
131 + margin-top: 0;
132 + }
133 +
134 + .manifesto-hoy {
135 + opacity: 0;
136 + animation: fadeUp 0.7s ease 0.5s forwards;
137 + margin: 44px 0;
138 + font-size: 26px;
139 + font-weight: 400;
140 + color: var(--manifesto-text);
141 + letter-spacing: -0.01em;
142 + line-height: 1.3;
143 + }
144 +
145 + .p3 {
146 + opacity: 0;
147 + animation: fadeUp 0.7s ease 0.65s forwards;
148 + margin-bottom: 28px;
149 + margin-top: 0;
150 + }
151 +
152 + .manifesto-regla {
153 + opacity: 0;
154 + animation: fadeUp 0.6s ease 0.75s forwards;
155 + border: none;
156 + border-top: 0.5px solid var(--manifesto-rule);
157 + margin: 44px 0;
158 + }
159 +
160 + .p4 {
161 + opacity: 0;
162 + animation: fadeUp 0.7s ease 0.85s forwards;
163 + margin-bottom: 0;
164 + margin-top: 0;
165 + }
166 +
167 + .manifesto-apuesta {
168 + opacity: 0;
169 + animation: fadeUp 0.7s ease 0.95s forwards;
170 + margin: 44px 0;
171 + padding: 0 0 0 24px;
172 + border-left: 1.5px solid var(--manifesto-quote-border);
173 + font-style: italic;
174 + font-weight: 300;
175 + font-size: 20px;
176 + line-height: 1.65;
177 + color: var(--manifesto-quote);
178 + }
179 +
180 + .manifesto-apuesta strong {
181 + font-style: normal;
182 + font-weight: 400;
183 + color: var(--manifesto-body);
184 + }
185 +
186 + /* === cierre === */
187 +
188 + .manifesto-cierre {
189 + opacity: 0;
190 + animation: fadeUp 0.8s ease 1.1s forwards;
191 + margin-top: 56px;
192 + padding-top: 36px;
193 + border-top: 0.5px solid var(--manifesto-rule);
194 + }
195 +
196 + .manifesto-cierre-texto {
197 + font-size: 36px;
198 + font-weight: 400;
199 + color: var(--color-gold);
200 + letter-spacing: -0.02em;
201 + line-height: 1.1;
202 + margin: 0;
203 + }
204 +
205 + .manifesto-cierre-sub {
206 + font-family: 'JetBrains Mono', 'Courier New', monospace;
207 + font-size: 10px;
208 + letter-spacing: 0.16em;
209 + color: var(--manifesto-muted);
210 + text-transform: uppercase;
211 + margin-top: 20px;
212 + margin-bottom: 0;
213 + }
214 +
215 + /* === animación === */
216 +
217 + @keyframes -global-fadeUp {
218 + from {
219 + opacity: 0;
220 + transform: translateY(14px);
221 + }
222 + to {
223 + opacity: 1;
224 + transform: translateY(0);
225 + }
226 + }
227 +
228 + /* === MODO OSCURO (default) === */
229 + .manifesto-outer {
230 + --manifesto-bg: #0C0C0C;
231 + --manifesto-text: #EDE9E1;
232 + --manifesto-body: #D9D4CB;
233 + --manifesto-muted: #5A5650;
234 + --manifesto-subtle: #6B6660;
235 + --manifesto-rule: #2A2822;
236 + --manifesto-quote: #C4BFB6;
237 + --manifesto-quote-border: #3A3530;
238 + }
239 +
240 + /* === MODO CLARO === */
241 + :global(html:not(.dark)) .manifesto-outer {
242 + --manifesto-bg: #FAFAF8;
243 + --manifesto-text: #1a1a1a;
244 + --manifesto-body: #3a3a3a;
245 + --manifesto-muted: #8a8a8a;
246 + --manifesto-subtle: #6a6a6a;
247 + --manifesto-rule: #e0e0e0;
248 + --manifesto-quote: #4a4a4a;
249 + --manifesto-quote-border: #d0d0d0;
250 + }
251 +
252 + /* === responsive === */
253 +
254 + @media (max-width: 600px) {
255 + .manifesto-inner {
256 + padding: 48px 28px 64px;
257 + }
258 +
259 + .manifesto-cuerpo {
260 + font-size: 17px;
261 + }
262 +
263 + .manifesto-hoy {
264 + font-size: 22px;
265 + }
266 +
267 + .manifesto-apuesta {
268 + font-size: 18px;
269 + }
270 +
271 + .manifesto-cierre-texto {
272 + font-size: 28px;
273 + }
274 + }
275 +</style>
This diff is collapsed. Click to expand it.
1 -gestion,objeto,devengado
2 -2025,11100,7010195151.280003
3 -2025,11210,5812831318.110004
4 -2025,11220,3960643633.5099993
5 -2025,11310,477377380.7399999
6 -2025,11321,611358929.5699998
7 -2025,11322,344284069.2800001
8 -2025,11323,8492765.11
9 -2025,11324,119472850.73000005
10 -2025,11331,3741918.29
11 -2025,11332,222831.86
12 -2025,11339,991911009.5900005
13 -2025,11400,2598265843.6899953
14 -2025,11510,115618445.64
15 -2025,11520,3684768.92
16 -2025,11600,347937539.3
17 -2025,11700,16012422722.88999
18 -2025,11810,1897633.19
19 -2025,11820,5236920.13
20 -2025,11910,418427037.15000004
21 -2025,11920,49552831.83999999
22 -2025,11930,141140855.32999998
23 -2025,11940,4603197.24
24 -2025,12100,3204535824.6000023
25 -2025,13110,3901953807.7400007
26 -2025,13120,619414677.6200007
27 -2025,13131,1362025647.0700052
28 -2025,13132,16517337.85
29 -2025,13200,780321050.7899984
30 -2025,14100,1139341529.1000001
31 -2025,15100,0
32 -2025,15200,0
33 -2025,15300,0
34 -2025,15400,4691146.74
35 -2025,21100,18994647.069999997
36 -2025,21200,1185053965.7300012
37 -2025,21300,187133399.53000006
38 -2025,21400,53465476.169999935
39 -2025,21500,32025660.46999999
40 -2025,21600,194624988.81999987
41 -2025,22110,91267157.03
42 -2025,22120,18054096.22
43 -2025,22210,150388054.74000007
44 -2025,22220,34629325.89
45 -2025,22300,1959574229.3100035
46 -2025,22400,2341473.77
47 -2025,22500,701307983.6299998
48 -2025,22600,129774059.36000001
49 -2025,23100,177818037.08
50 -2025,23200,705817678.630001
51 -2025,23300,267332.29
52 -2025,23400,102877216.77999996
53 -2025,24110,347305124.4300007
54 -2025,24120,495875030.0700008
55 -2025,24130,4734068.450000001
56 -2025,24200,186441122.7200001
57 -2025,24300,2640074895.480003
58 -2025,25120,968577449.3700005
59 -2025,25130,48331129.9
60 -2025,25210,124279655.01999998
61 -2025,25220,1788157637.100003
62 -2025,25230,38750297.28
63 -2025,25300,561398734.3999997
64 -2025,25400,878953716.1200004
65 -2025,25500,306587141.63999945
66 -2025,25600,313083444.33000004
67 -2025,25700,6976563.890000001
68 -2025,25810,119466843.19999999
69 -2025,25820,224542509.50000012
70 -2025,25900,237665083.13999984
71 -2025,26200,15655261.7
72 -2025,26300,34604066.51
73 -2025,26610,123238100.68000004
74 -2025,26620,65803840.900000006
75 -2025,26630,5856917.720000001
76 -2025,26640,12578208.32
77 -2025,26700,14848951.62
78 -2025,26910,663385.5700000003
79 -2025,26920,235250
80 -2025,26930,8978735.689999998
81 -2025,26940,66563836.099999994
82 -2025,26990,1503662082.0000036
83 -2025,27110,1825790733.1499996
84 -2025,27120,364659427.3
85 -2025,31110,542443427.4200002
86 -2025,31120,216381918.43999982
87 -2025,31130,664933780.7900007
88 -2025,31140,546467610.53
89 -2025,31150,58934973.47
90 -2025,31200,33094567.35000001
91 -2025,31300,2447995585.1100035
92 -2025,32100,143276681.52000007
93 -2025,32200,132315063.70000002
94 -2025,32300,5561928.509999998
95 -2025,32400,159320
96 -2025,32500,1136682.8700000003
97 -2025,33100,24196989.06
98 -2025,33200,44402437.73000002
99 -2025,33300,140159482.78000006
100 -2025,33400,44503112.73000003
101 -2025,34110,1233206780.699983
102 -2025,34120,22281523272.74
103 -2025,34130,210854679.98
104 -2025,34200,2971103666.2100005
105 -2025,34300,107990911.47999994
106 -2025,34400,7452099.93
107 -2025,34500,643334205.2399997
108 -2025,34600,256516535.3800006
109 -2025,34700,4499642859.289998
110 -2025,34800,31720752.09
111 -2025,34900,3886088.93
112 -2025,39100,110497000.54
113 -2025,39200,48738034.710000016
114 -2025,39300,4137761.2899999996
115 -2025,39400,88709616.89000002
116 -2025,39500,308909532.08000034
117 -2025,39600,13789089.190000001
118 -2025,39700,260527546.88999993
119 -2025,39800,711478491.3899997
120 -2025,39911,25451599.84
121 -2025,39912,229716435.09
122 -2025,39990,85810785.37999989
123 -2025,41100,4018555.16
124 -2025,41200,11738231.740000002
125 -2025,41300,0
126 -2025,42210,2475688.8800000004
127 -2025,42220,5772449.3
128 -2025,42230,3644887583.5699887
129 -2025,42240,119884214.51000004
130 -2025,42310,3262092590.7700067
131 -2025,42320,162411814.5
132 -2025,42400,748574159.99
133 -2025,42500,368399418.97999996
134 -2025,43110,68317124.34
135 -2025,43120,223834030.73999995
136 -2025,43200,145257372.71
137 -2025,43310,18770602.6
138 -2025,43320,7603011.960000001
139 -2025,43330,290260838.97
140 -2025,43340,4231983.279999999
141 -2025,43400,398948895.01000005
142 -2025,43500,328470832.01999986
143 -2025,43600,51696765.46999999
144 -2025,43700,169086594.95000005
145 -2025,46110,125733899.86000003
146 -2025,46120,74691013.75999999
147 -2025,46210,57348534.56999999
148 -2025,46220,9583654.360000001
149 -2025,46310,30831570.299999986
150 -2025,46320,2784494.7899999996
151 -2025,49100,41480273
152 -2025,49300,492862.94999999995
153 -2025,49400,85275
154 -2025,49900,660922.38
155 -2025,51100,0
156 -2025,51200,620015014.37
157 -2025,51600,481042090
158 -2025,51700,29014116.87
159 -2025,53410,52050949.82
160 -2025,53420,351090144.51
161 -2025,53430,3378151.15
162 -2025,53440,25716472.12
163 -2025,54300,77289717.23
164 -2025,54800,632675260.65
165 -2025,55130,0
166 -2025,56100,2147721911.8500004
167 -2025,57100,836275.9
168 -2025,57200,0
169 -2025,61100,70227731033.1
170 -2025,61200,3503973693.6899977
171 -2025,61300,7686201.74
172 -2025,61400,165316.78
173 -2025,61600,4660620766.32
174 -2025,61700,3610015023.1800003
175 -2025,61800,578767.5200000001
176 -2025,61900,560031.13
177 -2025,62100,106585021.14999999
178 -2025,62200,38756619.58
179 -2025,62300,3500499.439999999
180 -2025,62400,405.55
181 -2025,62600,4871024753.510001
182 -2025,62700,3091378138.92
183 -2025,62800,66184743.53000001
184 -2025,62900,236631.16
185 -2025,63100,6480763.82
186 -2025,63200,0
187 -2025,63300,100360.43
188 -2025,63400,462857.7
189 -2025,63500,35529.2
190 -2025,63600,0
191 -2025,63700,2369055.65
192 -2025,63800,0
193 -2025,63900,1087614.1800000002
194 -2025,64100,0
195 -2025,64200,0
196 -2025,65100,586447491.8299999
197 -2025,65210,398987753.5799999
198 -2025,65220,240387686.01999998
199 -2025,65230,132581381.01
200 -2025,65240,0
201 -2025,65300,2444818356.6
202 -2025,65400,644864385.6300001
203 -2025,65500,797927308.79
204 -2025,65600,622100032.9
205 -2025,65800,6897016.98
206 -2025,65900,66705420.480000004
207 -2025,66100,403828926.02000004
208 -2025,66210,2597435653.4000015
209 -2025,66220,1829365908.85
210 -2025,66230,646299428.4800001
211 -2025,66240,80232240.73
212 -2025,66250,209697820.00000003
213 -2025,66300,239921321.82000005
214 -2025,66400,134798065.3299999
215 -2025,66900,1844595842.08
216 -2025,67100,23208064.040000003
217 -2025,68200,230881944.88000008
218 -2025,69100,29581681.77
219 -2025,69200,203500678.33
220 -2025,71100,7630007944.97
221 -2025,71210,2392898.6
222 -2025,71220,322149243.09
223 -2025,71230,4408152.6
224 -2025,71300,26644234.430000003
225 -2025,71610,1883091507.0600004
226 -2025,71630,343800974.93
227 -2025,71700,65080225.449999996
228 -2025,71800,4478984170.690001
229 -2025,72200,2046897058.8299997
230 -2025,72420,316010589.22
231 -2025,72520,7584584602.65001
232 -2025,73100,2664574022.970007
233 -2025,73200,24681254941.659996
234 -2025,73410,4845222515.11
235 -2025,73420,315258596.03999996
236 -2025,73430,4314037.58
237 -2025,73440,1652127
238 -2025,73700,15015040108.359999
239 -2025,73820,1364681.67
240 -2025,75110,0
241 -2025,75120,683772757.9200002
242 -2025,75211,469127620.05
243 -2025,75212,50308009.760000005
244 -2025,75221,11649919.909999998
245 -2025,75222,305831148.79999995
246 -2025,75320,0
247 -2025,77100,346626643.30999994
248 -2025,77200,1783047515.49
249 -2025,77410,154404059.40000007
250 -2025,77440,13005182.149999999
251 -2025,77520,34542653.12
252 -2025,77530,1359464924.2300012
253 -2025,77700,0
254 -2025,77820,42931153.11
255 -2025,78100,346333.4
256 -2025,79100,20183063.04
257 -2025,79200,74336.66
258 -2025,79310,69600
259 -2025,81100,189467352
260 -2025,81200,1327189189.5399992
261 -2025,81300,551652107.6699998
262 -2025,81400,299412884.65
263 -2025,81500,2442434
264 -2025,81600,395569
265 -2025,81950,39137
266 -2025,81960,32790665.14
267 -2025,81990,2007174694.16
268 -2025,82100,879007438.5400001
269 -2025,83110,29113391.8
270 -2025,83120,4240482.35
271 -2025,83210,351070.72
272 -2025,83220,50460
273 -2025,84100,65887753.279999994
274 -2025,84230,394819507.59
275 -2025,84240,723835763.5600002
276 -2025,84250,65803251.14
277 -2025,84900,8503359.91
278 -2025,85100,94254502.63999999
279 -2025,85200,10478575.300000003
280 -2025,85400,6221225.630000003
281 -2025,85500,3581209.3799999994
282 -2025,85900,20640115.529999997
283 -2025,86100,29028844.980000008
284 -2025,91200,512102826.06
285 -2025,92100,0
286 -2025,94100,11526054.86
287 -2025,94200,7279202.82
288 -2025,94300,1551638.3499999999
289 -2025,95100,291453722.56
290 -2025,96100,5934986991.019999
291 -2025,96200,614954091.44
292 -2025,96900,2947311655.2299995
293 -2025,97100,70099589.35000001
294 -2025,98300,125000000
295 -2025,99100,0
296 -2025,99200,0
This diff is collapsed. Click to expand it.