Rafael Lopez

docs

...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
114 } else if (!isClass) { 114 } else if (!isClass) {
115 // Programa/proyecto 115 // Programa/proyecto
116 tipo = 'programa'; 116 tipo = 'programa';
117 - codigo = `${meta.entidad || ''}-${meta.programa || ''}-${meta.proyecto || ''}`; 117 + codigo = String(meta.programa || '');
118 } 118 }
119 119
120 // Contexto extra para programas 120 // Contexto extra para programas
...@@ -213,7 +213,7 @@ ...@@ -213,7 +213,7 @@
213 rubro: '/rubro/', 213 rubro: '/rubro/',
214 organismo: '/organismo/', 214 organismo: '/organismo/',
215 ubigeo: '/ubicacion/', 215 ubigeo: '/ubicacion/',
216 - programa: '/proyecto/' 216 + programa: '/programa/'
217 }; 217 };
218 const base = routes[item.tipo]; 218 const base = routes[item.tipo];
219 if (base) { 219 if (base) {
......
...@@ -807,7 +807,7 @@ ...@@ -807,7 +807,7 @@
807 } else if (isClassResult) { 807 } else if (isClassResult) {
808 url = `/clasificador/${hit.document.class_}/${hit.document.id}`; 808 url = `/clasificador/${hit.document.class_}/${hit.document.id}`;
809 } else { 809 } else {
810 - url = `/proyecto/${generateCodigo(hit)}`; 810 + url = `/programa/${generateCodigo(hit)}`;
811 } 811 }
812 812
813 window.location.href = url; 813 window.location.href = url;
...@@ -921,7 +921,7 @@ ...@@ -921,7 +921,7 @@
921 921
922 function generateCodigo(hit) { 922 function generateCodigo(hit) {
923 const m = parseMetadatos(hit.document.metadatos); 923 const m = parseMetadatos(hit.document.metadatos);
924 - return `${m.entidad}-${m.programa}-${m.proyecto}-${m.actividad}-${hit.document.gestion}`; 924 + return String(m.programa || '');
925 } 925 }
926 926
927 $: stats = filteredHits.length > 0 ? getStats( 927 $: stats = filteredHits.length > 0 ? getStats(
...@@ -1485,7 +1485,7 @@ ...@@ -1485,7 +1485,7 @@
1485 ? `/rubro/${rubroCode}` 1485 ? `/rubro/${rubroCode}`
1486 : isOrganismoClass 1486 : isOrganismoClass
1487 ? `/organismo/${meta.organismo || ''}` 1487 ? `/organismo/${meta.organismo || ''}`
1488 - : (isClassResult ? `/clasificador/${hit.document.class_}/${hit.document.id}` : `/proyecto/${generateCodigo(hit)}`)} 1488 + : (isClassResult ? `/clasificador/${hit.document.class_}/${hit.document.id}` : `/programa/${generateCodigo(hit)}`)}
1489 class="result-card" 1489 class="result-card"
1490 class:result-card-selected={selectedResultIndex === i} 1490 class:result-card-selected={selectedResultIndex === i}
1491 data-result-index={i} 1491 data-result-index={i}
......
1 +const API_BASE = 'http://136.112.29.74/api/diario';
2 +
3 +export async function GET({ url }) {
4 + const tipo = url.searchParams.get('tipo') || 'status';
5 +
6 + const endpoints = {
7 + status: `${API_BASE}/status`,
8 + timeseries: `${API_BASE}/timeseries`,
9 + entidades: `${API_BASE}/entidades`,
10 + clasificadores: `${API_BASE}/clasificadores`,
11 + timeline: `${API_BASE}/timeline`
12 + };
13 +
14 + const apiUrl = endpoints[tipo];
15 + if (!apiUrl) {
16 + return new Response(JSON.stringify({ error: 'Invalid tipo' }), {
17 + status: 400, headers: { 'Content-Type': 'application/json' }
18 + });
19 + }
20 +
21 + try {
22 + const response = await fetch(apiUrl);
23 + if (!response.ok) {
24 + return new Response(JSON.stringify({ error: `API error: ${response.status}` }), {
25 + status: response.status, headers: { 'Content-Type': 'application/json' }
26 + });
27 + }
28 + const data = await response.json();
29 + return new Response(JSON.stringify(data), {
30 + headers: { 'Content-Type': 'application/json' }
31 + });
32 + } catch {
33 + return new Response(JSON.stringify({ error: 'Failed to fetch' }), {
34 + status: 500, headers: { 'Content-Type': 'application/json' }
35 + });
36 + }
37 +}
1 +const API_BASE = 'http://136.112.29.74/api/programa_proyecto';
2 +
3 +export async function GET({ url }) {
4 + const codigo = url.searchParams.get('codigo') || '';
5 + const tipo = url.searchParams.get('tipo') || 'detalle'; // detalle, proyectos
6 +
7 + if (!codigo) {
8 + return new Response(JSON.stringify({ error: 'Missing codigo' }), {
9 + status: 400, headers: { 'Content-Type': 'application/json' }
10 + });
11 + }
12 +
13 + const apiUrl = tipo === 'proyectos'
14 + ? `${API_BASE}/${codigo}/proyectos`
15 + : `${API_BASE}/${codigo}`;
16 +
17 + try {
18 + const response = await fetch(apiUrl);
19 + if (!response.ok) {
20 + return new Response(JSON.stringify({ error: `API error: ${response.status}` }), {
21 + status: response.status, headers: { 'Content-Type': 'application/json' }
22 + });
23 + }
24 + const data = await response.json();
25 + return new Response(JSON.stringify(data), {
26 + headers: { 'Content-Type': 'application/json' }
27 + });
28 + } catch {
29 + return new Response(JSON.stringify({ error: 'Failed to fetch' }), {
30 + status: 500, headers: { 'Content-Type': 'application/json' }
31 + });
32 + }
33 +}
...@@ -11,8 +11,11 @@ ...@@ -11,8 +11,11 @@
11 let searchableItems = $state([]); 11 let searchableItems = $state([]);
12 let isMac = $state(false); 12 let isMac = $state(false);
13 13
14 - // Markdown content embedded directly 14 + // Markdown content loaded from static file
15 - const MARKDOWN_CONTENT = `# Introducción 15 + let MARKDOWN_CONTENT = $state('');
16 +
17 + // Placeholder for initial content
18 + const INITIAL_CONTENT = `# Introducción
16 19
17 El presupuesto público se presenta en dos distribuciones de datos: una histórica y otra en tiempo real. 20 El presupuesto público se presenta en dos distribuciones de datos: una histórica y otra en tiempo real.
18 21
...@@ -1043,6 +1046,12 @@ Ejemplo: \`5637823.65\`. ...@@ -1043,6 +1046,12 @@ Ejemplo: \`5637823.65\`.
1043 isMac = typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform); 1046 isMac = typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
1044 1047
1045 try { 1048 try {
1049 + // Cargar markdown desde archivo estático
1050 + const res = await fetch('/docs.md');
1051 + if (res.ok) {
1052 + MARKDOWN_CONTENT = await res.text();
1053 + }
1054 +
1046 const result = processMarkdown(MARKDOWN_CONTENT); 1055 const result = processMarkdown(MARKDOWN_CONTENT);
1047 content = result.html; 1056 content = result.html;
1048 headings = buildHierarchy(result.headings); 1057 headings = buildHierarchy(result.headings);
...@@ -1327,12 +1336,6 @@ Ejemplo: \`5637823.65\`. ...@@ -1327,12 +1336,6 @@ Ejemplo: \`5637823.65\`.
1327 <header class="docs-header"> 1336 <header class="docs-header">
1328 <div class="docs-header-content"> 1337 <div class="docs-header-content">
1329 <div class="docs-header-top"> 1338 <div class="docs-header-top">
1330 - <a href="/" class="docs-back">
1331 - <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1332 - <path d="M19 12H5M12 19l-7-7 7-7"/>
1333 - </svg>
1334 - <span>Inicio</span>
1335 - </a>
1336 1339
1337 <!-- Mobile menu button --> 1340 <!-- Mobile menu button -->
1338 <button class="docs-menu-btn" onclick={toggleSidebar}> 1341 <button class="docs-menu-btn" onclick={toggleSidebar}>
...@@ -1501,7 +1504,7 @@ Ejemplo: \`5637823.65\`. ...@@ -1501,7 +1504,7 @@ Ejemplo: \`5637823.65\`.
1501 .docs-header-content { 1504 .docs-header-content {
1502 max-width: 1000px; 1505 max-width: 1000px;
1503 margin: 0 auto; 1506 margin: 0 auto;
1504 - padding: 1rem 1.5rem; 1507 + padding: 80px 1.5rem 1rem;
1505 } 1508 }
1506 1509
1507 .docs-header-top { 1510 .docs-header-top {
...@@ -1568,7 +1571,7 @@ Ejemplo: \`5637823.65\`. ...@@ -1568,7 +1571,7 @@ Ejemplo: \`5637823.65\`.
1568 .docs-layout-wrapper { 1571 .docs-layout-wrapper {
1569 max-width: 700px; 1572 max-width: 700px;
1570 margin: 0 auto; 1573 margin: 0 auto;
1571 - padding-top: 140px; 1574 + padding-top: 340px;
1572 padding-left: 1.5rem; 1575 padding-left: 1.5rem;
1573 padding-right: 1.5rem; 1576 padding-right: 1.5rem;
1574 } 1577 }
...@@ -1603,7 +1606,7 @@ Ejemplo: \`5637823.65\`. ...@@ -1603,7 +1606,7 @@ Ejemplo: \`5637823.65\`.
1603 1606
1604 .docs-sidebar-content { 1607 .docs-sidebar-content {
1605 padding-right: 1rem; 1608 padding-right: 1rem;
1606 - padding-top: 2rem; 1609 + padding-top: 10rem;
1607 } 1610 }
1608 } 1611 }
1609 1612
...@@ -1824,7 +1827,7 @@ Ejemplo: \`5637823.65\`. ...@@ -1824,7 +1827,7 @@ Ejemplo: \`5637823.65\`.
1824 1827
1825 @media (max-width: 1099px) { 1828 @media (max-width: 1099px) {
1826 .docs-layout-wrapper { 1829 .docs-layout-wrapper {
1827 - padding-top: 130px; 1830 + padding-top: 330px;
1828 padding-left: 1rem; 1831 padding-left: 1rem;
1829 padding-right: 1rem; 1832 padding-right: 1rem;
1830 } 1833 }
...@@ -2049,6 +2052,7 @@ Ejemplo: \`5637823.65\`. ...@@ -2049,6 +2052,7 @@ Ejemplo: \`5637823.65\`.
2049 .docs-search-container { 2052 .docs-search-container {
2050 position: relative; 2053 position: relative;
2051 margin-top: 1rem; 2054 margin-top: 1rem;
2055 + max-width: 320px;
2052 } 2056 }
2053 2057
2054 .docs-search-input-wrapper { 2058 .docs-search-input-wrapper {
......
This diff is collapsed. Click to expand it.
...@@ -35,18 +35,24 @@ ...@@ -35,18 +35,24 @@
35 } 35 }
36 36
37 async function cargarMapa(codigo) { 37 async function cargarMapa(codigo) {
38 + console.time('[MAPA] total');
38 const cached = get(mapaCache); 39 const cached = get(mapaCache);
39 if (cached) { 40 if (cached) {
41 + console.log('[MAPA] usando cache');
40 mapaData = cached; 42 mapaData = cached;
41 } else if (!mapaData) { 43 } else if (!mapaData) {
44 + console.time('[MAPA] fetch');
42 const res = await fetch('/mapa.json'); 45 const res = await fetch('/mapa.json');
43 mapaData = await res.json(); 46 mapaData = await res.json();
47 + console.timeEnd('[MAPA] fetch');
44 delete mapaData.bolivia.crs; 48 delete mapaData.bolivia.crs;
45 delete mapaData.departamentos.crs; 49 delete mapaData.departamentos.crs;
46 delete mapaData.municipios.crs; 50 delete mapaData.municipios.crs;
51 + console.time('[MAPA] fixWinding');
47 fixWinding(mapaData.bolivia); 52 fixWinding(mapaData.bolivia);
48 fixWinding(mapaData.departamentos); 53 fixWinding(mapaData.departamentos);
49 fixWinding(mapaData.municipios); 54 fixWinding(mapaData.municipios);
55 + console.timeEnd('[MAPA] fixWinding');
50 mapaCache.set(mapaData); 56 mapaCache.set(mapaData);
51 } 57 }
52 58
...@@ -73,11 +79,14 @@ ...@@ -73,11 +79,14 @@
73 mapaMunicipioPath = pathGen(municipio) || ''; 79 mapaMunicipioPath = pathGen(municipio) || '';
74 80
75 // Pre-calcular paths de todos los municipios para hover 81 // Pre-calcular paths de todos los municipios para hover
82 + console.time('[MAPA] allMunisPaths');
76 mapaAllMunisPaths = mapaData.municipios.features.map(f => ({ 83 mapaAllMunisPaths = mapaData.municipios.features.map(f => ({
77 path: pathGen(f) || '', 84 path: pathGen(f) || '',
78 codigo: f.properties.codigo, 85 codigo: f.properties.codigo,
79 isCurrent: f.properties.codigo === codigoNum 86 isCurrent: f.properties.codigo === codigoNum
80 })); 87 }));
88 + console.timeEnd('[MAPA] allMunisPaths');
89 + console.timeEnd('[MAPA] total');
81 } 90 }
82 91
83 // Datos desde el loader (Supabase) 92 // Datos desde el loader (Supabase)
......
1 <script> 1 <script>
2 import { onMount } from 'svelte'; 2 import { onMount } from 'svelte';
3 - import { parquetReadObjects, asyncBufferFromUrl } from 'hyparquet';
4 import * as Plot from '@observablehq/plot'; 3 import * as Plot from '@observablehq/plot';
5 import * as d3 from 'd3'; 4 import * as d3 from 'd3';
6 5
...@@ -124,10 +123,9 @@ ...@@ -124,10 +123,9 @@
124 // Navegación interna entre secciones 123 // Navegación interna entre secciones
125 let seccionActiva = $state(0); 124 let seccionActiva = $state(0);
126 const secciones = [ 125 const secciones = [
127 - { id: 'clasificadores', titulo: '¿En qué se gasta?' },
128 - { id: 'entidades', titulo: '¿Quién ejecuta más?' },
129 { id: 'programas', titulo: '¿En qué programas?' }, 126 { id: 'programas', titulo: '¿En qué programas?' },
130 - { id: 'evolucion', titulo: '¿Cómo evoluciona?' } 127 + { id: 'clasificadores', titulo: '¿En qué se gasta?' },
128 + { id: 'entidades', titulo: '¿Quién ejecuta más?' }
131 ]; 129 ];
132 130
133 function irASeccion(index) { 131 function irASeccion(index) {
...@@ -219,11 +217,11 @@ ...@@ -219,11 +217,11 @@
219 }; 217 };
220 } 218 }
221 219
222 - // Cargar archivo Parquet usando asyncBufferFromUrl 220 + // Cargar datos desde API
223 - async function cargarParquet(url) { 221 + async function cargarDiario(tipo) {
224 - const file = await asyncBufferFromUrl({ url }); 222 + const res = await fetch(`/api/diario?tipo=${tipo}`);
225 - const data = await parquetReadObjects({ file }); 223 + if (!res.ok) throw new Error(`Error ${res.status}`);
226 - return data; 224 + return await res.json();
227 } 225 }
228 226
229 onMount(async () => { 227 onMount(async () => {
...@@ -251,12 +249,12 @@ ...@@ -251,12 +249,12 @@
251 themeObserver.observe(document.documentElement, { attributes: true }); 249 themeObserver.observe(document.documentElement, { attributes: true });
252 250
253 try { 251 try {
254 - // Cargar todos los parquets en paralelo 252 + // Cargar todos los datos en paralelo desde API
255 const [tsData, entData, clasData, tlData] = await Promise.all([ 253 const [tsData, entData, clasData, tlData] = await Promise.all([
256 - cargarParquet('/timeserie@4.parquet'), 254 + cargarDiario('timeseries'),
257 - cargarParquet('/ejecucion_entidades@1.parquet'), 255 + cargarDiario('entidades'),
258 - cargarParquet('/ejecucion_clasificadores@3.parquet'), 256 + cargarDiario('clasificadores'),
259 - cargarParquet('/timeline@1.parquet') 257 + cargarDiario('timeline')
260 ]); 258 ]);
261 259
262 // Los datos ya vienen como objetos 260 // Los datos ya vienen como objetos
...@@ -282,12 +280,12 @@ ...@@ -282,12 +280,12 @@
282 280
283 // Filtrar datos por transferencias y días 281 // Filtrar datos por transferencias y días
284 let timeserieFiltered = $derived( 282 let timeserieFiltered = $derived(
285 - timeserie.filter(d => d.transferencias === incluirTransferencias) 283 + timeserie.filter(d => d.transferencias === undefined || d.transferencias === incluirTransferencias)
286 ); 284 );
287 285
288 // Datos por clasificador para las barras apiladas (sin agrupar, cada hijo es un segmento) 286 // Datos por clasificador para las barras apiladas (sin agrupar, cada hijo es un segmento)
289 let datosPorClasificador = $derived.by(() => { 287 let datosPorClasificador = $derived.by(() => {
290 - const dias = diasSeleccionados === 'total' ? 'total' : diasSeleccionados; 288 + const dias = diasSeleccionados === 'total' ? 0 : parseInt(diasSeleccionados);
291 const result = {}; 289 const result = {};
292 290
293 clasificadores.forEach(({ key }) => { 291 clasificadores.forEach(({ key }) => {
...@@ -447,7 +445,7 @@ ...@@ -447,7 +445,7 @@
447 445
448 // Todas las entidades filtradas (sin límite) 446 // Todas las entidades filtradas (sin límite)
449 let entidadesTodas = $derived.by(() => { 447 let entidadesTodas = $derived.by(() => {
450 - const dias = diasSeleccionados === 'total' ? 'total' : diasSeleccionados; 448 + const dias = diasSeleccionados === 'total' ? 0 : parseInt(diasSeleccionados);
451 let filtered = ejecucionEntidades.filter(d => 449 let filtered = ejecucionEntidades.filter(d =>
452 d.transferencias === incluirTransferencias && 450 d.transferencias === incluirTransferencias &&
453 String(d.dias) === String(dias) && 451 String(d.dias) === String(dias) &&
...@@ -975,7 +973,71 @@ ...@@ -975,7 +973,71 @@
975 <p>Error: {error}</p> 973 <p>Error: {error}</p>
976 </div> 974 </div>
977 {:else} 975 {:else}
978 - <!-- SECCIÓN 1: Clasificadores --> 976 + <!-- SECCIÓN 1: Programas (timeline) -->
977 + <section class="vista-seccion" id="programas">
978 + <div class="vista-contenido">
979 + <div class="contenido-centrado">
980 + <header class="seccion-header seccion-header-centrado">
981 + <h1 class="seccion-titulo-grande">¿En qué programas?</h1>
982 + <p class="seccion-descripcion">Detalle de programas y actividades ejecutadas en la última semana</p>
983 + </header>
984 +
985 + <div class="timeline-search">
986 + <svg class="search-icon" viewBox="0 0 20 20" fill="currentColor">
987 + <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
988 + </svg>
989 + <input
990 + type="text"
991 + placeholder="Buscar entidad..."
992 + bind:value={busquedaTimeline}
993 + class="timeline-search-input"
994 + />
995 + </div>
996 +
997 + <div class="timeline-cards">
998 + {#each timelineByEntity as entidad}
999 + <div class="timeline-card">
1000 + <div class="timeline-card-header">
1001 + <div class="timeline-card-entity">
1002 + <span class="timeline-card-area">{entidad.entidad_desc_area}</span>
1003 + <span class="timeline-card-nombre">{entidad.entidad_desc_entidad}</span>
1004 + </div>
1005 + <div class="timeline-card-total">
1006 + <span class="timeline-card-total-label">Ejecutado</span>
1007 + <span class="timeline-card-total-monto">Bs. {formatearNumeroCompleto(entidad.total)}</span>
1008 + </div>
1009 + </div>
1010 + <div class="timeline-card-body">
1011 + {#each cardsExpandidas[entidad.entidad] ? entidad.programas : entidad.programas.slice(0, 8) as prog}
1012 + <div class="timeline-prog-row">
1013 + <div class="timeline-prog-info">
1014 + <span class="timeline-prog-programa">{prog.programa}</span>
1015 + <span class="timeline-prog-actividad">{prog.actividad}</span>
1016 + </div>
1017 + <span class="timeline-prog-monto">Bs. {formatearNumeroCompleto(prog.devengado)}</span>
1018 + </div>
1019 + {/each}
1020 +
1021 + {#if entidad.programas.length > 8}
1022 + <button
1023 + class="timeline-ver-mas"
1024 + onclick={() => {
1025 + cardsExpandidas[entidad.entidad] = !cardsExpandidas[entidad.entidad];
1026 + cardsExpandidas = { ...cardsExpandidas };
1027 + }}
1028 + >
1029 + {cardsExpandidas[entidad.entidad] ? 'Ver menos' : `Ver ${entidad.programas.length - 8} más...`}
1030 + </button>
1031 + {/if}
1032 + </div>
1033 + </div>
1034 + {/each}
1035 + </div>
1036 + </div>
1037 + </div>
1038 + </section>
1039 +
1040 + <!-- SECCIÓN 2: Clasificadores -->
979 <section class="vista-seccion" id="clasificadores"> 1041 <section class="vista-seccion" id="clasificadores">
980 <div class="vista-contenido"> 1042 <div class="vista-contenido">
981 <div class="contenido-centrado"> 1043 <div class="contenido-centrado">
...@@ -1143,73 +1205,9 @@ ...@@ -1143,73 +1205,9 @@
1143 </div> 1205 </div>
1144 </section> 1206 </section>
1145 1207
1146 - <!-- SECCIÓN 3: Programas -->
1147 - <section class="vista-seccion" id="programas">
1148 - <div class="vista-contenido">
1149 - <div class="contenido-centrado">
1150 - <!-- Título de sección -->
1151 - <header class="seccion-header seccion-header-centrado">
1152 - <h1 class="seccion-titulo-grande">¿En qué programas?</h1>
1153 - <p class="seccion-descripcion">Detalle de programas y actividades ejecutadas en la última semana</p>
1154 - </header>
1155 -
1156 - <div class="timeline-search">
1157 - <svg class="search-icon" viewBox="0 0 20 20" fill="currentColor">
1158 - <path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
1159 - </svg>
1160 - <input
1161 - type="text"
1162 - placeholder="Buscar entidad..."
1163 - bind:value={busquedaTimeline}
1164 - class="timeline-search-input"
1165 - />
1166 - </div>
1167 -
1168 - <div class="timeline-cards">
1169 - {#each timelineByEntity as entidad}
1170 - <div class="timeline-card">
1171 - <div class="timeline-card-header">
1172 - <div class="timeline-card-entity">
1173 - <span class="timeline-card-area">{entidad.entidad_desc_area}</span>
1174 - <span class="timeline-card-nombre">{entidad.entidad_desc_entidad}</span>
1175 - </div>
1176 - <div class="timeline-card-total">
1177 - <span class="timeline-card-total-label">Ejecutado</span>
1178 - <span class="timeline-card-total-monto">Bs. {formatearNumeroCompleto(entidad.total)}</span>
1179 - </div>
1180 - </div>
1181 - <div class="timeline-card-body">
1182 - {#each cardsExpandidas[entidad.entidad] ? entidad.programas : entidad.programas.slice(0, 8) as prog}
1183 - <div class="timeline-prog-row">
1184 - <div class="timeline-prog-info">
1185 - <span class="timeline-prog-programa">{prog.programa}</span>
1186 - <span class="timeline-prog-actividad">{prog.actividad}</span>
1187 - </div>
1188 - <span class="timeline-prog-monto">Bs. {formatearNumeroCompleto(prog.devengado)}</span>
1189 - </div>
1190 - {/each}
1191 -
1192 - {#if entidad.programas.length > 8}
1193 - <button
1194 - class="timeline-ver-mas"
1195 - onclick={() => {
1196 - cardsExpandidas[entidad.entidad] = !cardsExpandidas[entidad.entidad];
1197 - cardsExpandidas = { ...cardsExpandidas };
1198 - }}
1199 - >
1200 - {cardsExpandidas[entidad.entidad] ? 'Ver menos' : `Ver ${entidad.programas.length - 8} más...`}
1201 - </button>
1202 - {/if}
1203 - </div>
1204 - </div>
1205 - {/each}
1206 - </div>
1207 - </div>
1208 - </div>
1209 - </section>
1210 1208
1211 <!-- SECCIÓN 4: Evolución temporal --> 1209 <!-- SECCIÓN 4: Evolución temporal -->
1212 - <section class="vista-seccion" id="evolucion"> 1210 + <section class="vista-seccion" id="evolucion" style="display: none;">
1213 <div class="vista-contenido"> 1211 <div class="vista-contenido">
1214 <div class="contenido-centrado"> 1212 <div class="contenido-centrado">
1215 <!-- Título de sección --> 1213 <!-- Título de sección -->
......
This diff could not be displayed because it is too large.