Showing
9 changed files
with
181 additions
and
100 deletions
| ... | @@ -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} | ... | ... |
src/routes/api/diario/+server.js
0 → 100644
| 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 | +} |
src/routes/api/programa/+server.js
0 → 100644
| 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 { | ... | ... |
src/routes/programa/[codigo]/+page.svelte
0 → 100644
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.
-
Please register or login to post a comment