Showing
9 changed files
with
513 additions
and
141 deletions
| ... | @@ -13,6 +13,7 @@ | ... | @@ -13,6 +13,7 @@ |
| 13 | "d3": "^7.9.0", | 13 | "d3": "^7.9.0", |
| 14 | "fuse.js": "^7.1.0", | 14 | "fuse.js": "^7.1.0", |
| 15 | "hyparquet": "^1.25.1", | 15 | "hyparquet": "^1.25.1", |
| 16 | + "hyparquet-compressors": "^1.1.1", | ||
| 16 | "marked": "^17.0.4" | 17 | "marked": "^17.0.4" |
| 17 | }, | 18 | }, |
| 18 | "devDependencies": { | 19 | "devDependencies": { |
| ... | @@ -1984,6 +1985,12 @@ | ... | @@ -1984,6 +1985,12 @@ |
| 1984 | "node": ">=10" | 1985 | "node": ">=10" |
| 1985 | } | 1986 | } |
| 1986 | }, | 1987 | }, |
| 1988 | + "node_modules/fzstd": { | ||
| 1989 | + "version": "0.1.1", | ||
| 1990 | + "resolved": "https://registry.npmjs.org/fzstd/-/fzstd-0.1.1.tgz", | ||
| 1991 | + "integrity": "sha512-dkuVSOKKwh3eas5VkJy1AW1vFpet8TA/fGmVA5krThl8YcOVE/8ZIoEA1+U1vEn5ckxxhLirSdY837azmbaNHA==", | ||
| 1992 | + "license": "MIT" | ||
| 1993 | + }, | ||
| 1987 | "node_modules/graceful-fs": { | 1994 | "node_modules/graceful-fs": { |
| 1988 | "version": "4.2.11", | 1995 | "version": "4.2.11", |
| 1989 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", | 1996 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", |
| ... | @@ -2009,6 +2016,22 @@ | ... | @@ -2009,6 +2016,22 @@ |
| 2009 | "integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==", | 2016 | "integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==", |
| 2010 | "license": "MIT" | 2017 | "license": "MIT" |
| 2011 | }, | 2018 | }, |
| 2019 | + "node_modules/hyparquet-compressors": { | ||
| 2020 | + "version": "1.1.1", | ||
| 2021 | + "resolved": "https://registry.npmjs.org/hyparquet-compressors/-/hyparquet-compressors-1.1.1.tgz", | ||
| 2022 | + "integrity": "sha512-yx7aA3Rhj0YycbdV71+XznQSLAefa4cT0urpgNXy4aM6eSeCknaVDNne8y45Uz74Fb15yyXUzOStlceOJBan7A==", | ||
| 2023 | + "license": "MIT", | ||
| 2024 | + "dependencies": { | ||
| 2025 | + "fzstd": "0.1.1", | ||
| 2026 | + "hysnappy": "1.0.0" | ||
| 2027 | + } | ||
| 2028 | + }, | ||
| 2029 | + "node_modules/hysnappy": { | ||
| 2030 | + "version": "1.0.0", | ||
| 2031 | + "resolved": "https://registry.npmjs.org/hysnappy/-/hysnappy-1.0.0.tgz", | ||
| 2032 | + "integrity": "sha512-MNrC4NfwDGPb889O6gIfEtbvEZCSWUsSEhsz4Oq2FRcpGtXHfeVz3KciSPp5Pnnz1NjFMgDQNfxdJozymJEDDA==", | ||
| 2033 | + "license": "MIT" | ||
| 2034 | + }, | ||
| 2012 | "node_modules/iconv-lite": { | 2035 | "node_modules/iconv-lite": { |
| 2013 | "version": "0.6.3", | 2036 | "version": "0.6.3", |
| 2014 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | 2037 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | ... | ... |
| 1 | -import { API_HOST } from '$lib/api.js'; | 1 | +const API_BASE = 'https://abierto.economiayfinanzas.gob.bo/presupuesto/_api/diario'; |
| 2 | -const API_BASE = API_HOST + '/api/diario'; | ||
| 3 | 2 | ||
| 4 | export async function GET({ url }) { | 3 | export async function GET({ url }) { |
| 5 | const tipo = url.searchParams.get('tipo') || 'status'; | 4 | const tipo = url.searchParams.get('tipo') || 'status'; | ... | ... |
| ... | @@ -5,6 +5,9 @@ | ... | @@ -5,6 +5,9 @@ |
| 5 | 5 | ||
| 6 | let mounted = $state(false); | 6 | let mounted = $state(false); |
| 7 | let dataset = $state('gasto'); | 7 | let dataset = $state('gasto'); |
| 8 | + let datasetMasivo = $state('gasto'); | ||
| 9 | + let formatoGestion = $state('csv'); | ||
| 10 | + let formatoEntidad = $state('csv'); | ||
| 8 | let entities = $state([]); | 11 | let entities = $state([]); |
| 9 | let entitySearch = $state(''); | 12 | let entitySearch = $state(''); |
| 10 | let entityFocused = $state(false); | 13 | let entityFocused = $state(false); |
| ... | @@ -12,22 +15,50 @@ | ... | @@ -12,22 +15,50 @@ |
| 12 | let selectedIdx = $state(-1); | 15 | let selectedIdx = $state(-1); |
| 13 | let loadingEntities = $state(true); | 16 | let loadingEntities = $state(true); |
| 14 | let entityError = $state(false); | 17 | let entityError = $state(false); |
| 18 | + let copiedUrl = $state(null); | ||
| 15 | 19 | ||
| 16 | function normalizeText(val) { | 20 | function normalizeText(val) { |
| 17 | return val.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim(); | 21 | return val.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim(); |
| 18 | } | 22 | } |
| 19 | 23 | ||
| 20 | - function buildUrl(partition, value) { | 24 | + function buildUrl(partition, value, formato = 'csv') { |
| 21 | if (partition === 'completo') { | 25 | if (partition === 'completo') { |
| 26 | + if (formato === 'parquet') { | ||
| 27 | + return `${DOWNLOAD_BASE}${dataset}/ultimo/${dataset}.parquet`; | ||
| 28 | + } | ||
| 22 | return `${DOWNLOAD_BASE}${dataset}/ultimo/${dataset}.zip`; | 29 | return `${DOWNLOAD_BASE}${dataset}/ultimo/${dataset}.zip`; |
| 23 | } else if (partition === 'gestion') { | 30 | } else if (partition === 'gestion') { |
| 31 | + if (formato === 'xlsx') { | ||
| 32 | + return `${DOWNLOAD_BASE}${dataset}/gestiones/gestion_${value}_xlsx.zip`; | ||
| 33 | + } | ||
| 24 | return `${DOWNLOAD_BASE}${dataset}/gestiones/gestion_${value}.zip`; | 34 | return `${DOWNLOAD_BASE}${dataset}/gestiones/gestion_${value}.zip`; |
| 25 | } else if (partition === 'entidad') { | 35 | } else if (partition === 'entidad') { |
| 36 | + if (formato === 'xlsx') { | ||
| 37 | + return `${DOWNLOAD_BASE}${dataset}/entidades/entidad_${value}_xlsx.zip`; | ||
| 38 | + } | ||
| 26 | return `${DOWNLOAD_BASE}${dataset}/entidades/entidad_${value}.zip`; | 39 | return `${DOWNLOAD_BASE}${dataset}/entidades/entidad_${value}.zip`; |
| 27 | } | 40 | } |
| 28 | return `${DOWNLOAD_BASE}${dataset}/${partition}/${value}`; | 41 | return `${DOWNLOAD_BASE}${dataset}/${partition}/${value}`; |
| 29 | } | 42 | } |
| 30 | 43 | ||
| 44 | + function buildMasivoUrl(formato) { | ||
| 45 | + if (formato === 'parquet') { | ||
| 46 | + return `${DOWNLOAD_BASE}${datasetMasivo}/ultimo/${datasetMasivo}.parquet`; | ||
| 47 | + } | ||
| 48 | + return `${DOWNLOAD_BASE}${datasetMasivo}/ultimo/${datasetMasivo}.zip`; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + const parquetUrls = { | ||
| 52 | + gasto: `${DOWNLOAD_BASE}gasto/ultimo/gasto.parquet`, | ||
| 53 | + ingreso: `${DOWNLOAD_BASE}ingreso/ultimo/ingreso.parquet` | ||
| 54 | + }; | ||
| 55 | + | ||
| 56 | + async function copyUrl(url) { | ||
| 57 | + await navigator.clipboard.writeText(url); | ||
| 58 | + copiedUrl = url; | ||
| 59 | + setTimeout(() => copiedUrl = null, 2000); | ||
| 60 | + } | ||
| 61 | + | ||
| 31 | let filteredEntities = $derived.by(() => { | 62 | let filteredEntities = $derived.by(() => { |
| 32 | if (!entitySearch || entitySearch.length < 2) return []; | 63 | if (!entitySearch || entitySearch.length < 2) return []; |
| 33 | const q = normalizeText(entitySearch); | 64 | const q = normalizeText(entitySearch); |
| ... | @@ -69,105 +100,213 @@ | ... | @@ -69,105 +100,213 @@ |
| 69 | } | 100 | } |
| 70 | } catch { entityError = true; } | 101 | } catch { entityError = true; } |
| 71 | loadingEntities = false; | 102 | loadingEntities = false; |
| 72 | - console.log('[DESCARGAS] entities loaded:', entities.length); | ||
| 73 | }); | 103 | }); |
| 74 | </script> | 104 | </script> |
| 75 | 105 | ||
| 76 | <svelte:head> | 106 | <svelte:head> |
| 77 | <title>Descargas | Presupuesto Público</title> | 107 | <title>Descargas | Presupuesto Público</title> |
| 78 | - <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| 79 | - <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet" /> | ||
| 80 | </svelte:head> | 108 | </svelte:head> |
| 81 | 109 | ||
| 82 | <div class="page" class:mounted> | 110 | <div class="page" class:mounted> |
| 83 | <div class="layout"> | 111 | <div class="layout"> |
| 84 | <h1 class="title">Descargas</h1> | 112 | <h1 class="title">Descargas</h1> |
| 85 | - <p class="subtitle">Descarga los datos del presupuesto público en formato CSV</p> | 113 | + <p class="subtitle">Descarga los datos del presupuesto público boliviano en distintos formatos</p> |
| 86 | - | 114 | + |
| 87 | - <!-- Dataset toggle --> | 115 | + <!-- ═══════════════════════════════════════════ --> |
| 88 | - <div class="section"> | 116 | + <!-- SECCIÓN 1: Por gestión o entidad --> |
| 89 | - <span class="section-label">Base</span> | 117 | + <!-- ═══════════════════════════════════════════ --> |
| 90 | - <div class="toggle-group"> | 118 | + <section class="section"> |
| 91 | - <button class="toggle-btn" class:active={dataset === 'gasto'} onclick={() => { dataset = 'gasto'; selectedEntity = null; }}>Gastos</button> | 119 | + <h2 class="section-title">Por gestión o entidad</h2> |
| 92 | - <button class="toggle-btn" class:active={dataset === 'ingreso'} onclick={() => { dataset = 'ingreso'; selectedEntity = null; }}>Ingresos</button> | 120 | + <p class="section-desc">Descarga los datos segmentados por año fiscal o por entidad pública</p> |
| 121 | + | ||
| 122 | + <!-- Toggle Gastos/Ingresos --> | ||
| 123 | + <div class="control-row"> | ||
| 124 | + <span class="control-label">Base</span> | ||
| 125 | + <div class="toggle-group"> | ||
| 126 | + <button class="toggle-btn" class:active={dataset === 'gasto'} onclick={() => { dataset = 'gasto'; selectedEntity = null; }}>Gastos</button> | ||
| 127 | + <button class="toggle-btn" class:active={dataset === 'ingreso'} onclick={() => { dataset = 'ingreso'; selectedEntity = null; }}>Ingresos</button> | ||
| 128 | + </div> | ||
| 93 | </div> | 129 | </div> |
| 94 | - </div> | 130 | + |
| 95 | - | 131 | + <!-- Por gestión --> |
| 96 | - <!-- Completo --> | 132 | + <div class="subsection"> |
| 97 | - <div class="section"> | 133 | + <div class="subsection-header"> |
| 98 | - <h2 class="section-title">Completo</h2> | 134 | + <h3 class="subsection-title">Por gestión</h3> |
| 99 | - <a href={buildUrl('completo', 'ultimo')} download rel="external" class="download-link"> | 135 | + <div class="formato-toggle"> |
| 100 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | 136 | + <span class="formato-label">Formato:</span> |
| 101 | - Descargar todo ({dataset === 'gasto' ? 'gastos' : 'ingresos'}) | 137 | + <button class="fmt-btn" class:active={formatoGestion === 'csv'} onclick={() => formatoGestion = 'csv'}>CSV</button> |
| 102 | - </a> | 138 | + <button class="fmt-btn" class:active={formatoGestion === 'xlsx'} onclick={() => formatoGestion = 'xlsx'}>Excel</button> |
| 103 | - </div> | 139 | + </div> |
| 104 | - | 140 | + </div> |
| 105 | - <!-- Por gestión --> | 141 | + <div class="year-grid"> |
| 106 | - <div class="section"> | 142 | + {#each YEARS as year} |
| 107 | - <h2 class="section-title">Por gestión</h2> | 143 | + <a href={buildUrl('gestion', year, formatoGestion)} download rel="external" class="year-link"> |
| 108 | - <div class="year-grid"> | 144 | + <span class="year-num">{year}</span> |
| 109 | - {#each YEARS as year} | 145 | + <span class="year-fmt">.{formatoGestion === 'xlsx' ? 'xlsx' : 'csv'}</span> |
| 110 | - <a href={buildUrl('gestion', year)} download rel="external" class="year-link">{year}</a> | 146 | + </a> |
| 111 | - {/each} | 147 | + {/each} |
| 148 | + </div> | ||
| 112 | </div> | 149 | </div> |
| 113 | - </div> | ||
| 114 | - | ||
| 115 | - <!-- Por entidad --> | ||
| 116 | - <div class="section"> | ||
| 117 | - <h2 class="section-title">Por entidad</h2> | ||
| 118 | - <div class="entity-search"> | ||
| 119 | - <input | ||
| 120 | - type="text" | ||
| 121 | - bind:value={entitySearch} | ||
| 122 | - onfocus={() => { entityFocused = true; selectedEntity = null; }} | ||
| 123 | - onblur={() => setTimeout(() => entityFocused = false, 150)} | ||
| 124 | - onkeydown={handleKeydown} | ||
| 125 | - placeholder="Buscar entidad..." | ||
| 126 | - class="search-input" | ||
| 127 | - /> | ||
| 128 | - {#if loadingEntities} | ||
| 129 | - <span class="search-status">Cargando entidades...</span> | ||
| 130 | - {:else if entityError} | ||
| 131 | - <span class="search-status error">No se pudieron cargar las entidades</span> | ||
| 132 | - {/if} | ||
| 133 | 150 | ||
| 134 | - {#if entityFocused && filteredEntities.length > 0} | 151 | + <!-- Por entidad --> |
| 135 | - <div class="search-dropdown"> | 152 | + <div class="subsection"> |
| 136 | - {#each filteredEntities as entity, i} | 153 | + <div class="subsection-header"> |
| 137 | - <button | 154 | + <h3 class="subsection-title">Por entidad</h3> |
| 138 | - class="search-item" | 155 | + <div class="formato-toggle"> |
| 139 | - class:active={selectedIdx === i} | 156 | + <span class="formato-label">Formato:</span> |
| 140 | - onmouseenter={() => selectedIdx = i} | 157 | + <button class="fmt-btn" class:active={formatoEntidad === 'csv'} onclick={() => formatoEntidad = 'csv'}>CSV</button> |
| 141 | - onclick={() => selectEntity(entity)} | 158 | + <button class="fmt-btn" class:active={formatoEntidad === 'xlsx'} onclick={() => formatoEntidad = 'xlsx'}>Excel</button> |
| 142 | - > | ||
| 143 | - <span class="item-name">{entity.desc_entidad}</span> | ||
| 144 | - <span class="item-code">Código {entity.entidad}</span> | ||
| 145 | - </button> | ||
| 146 | - {/each} | ||
| 147 | </div> | 159 | </div> |
| 148 | - {/if} | 160 | + </div> |
| 161 | + <div class="entity-search"> | ||
| 162 | + <input | ||
| 163 | + type="text" | ||
| 164 | + bind:value={entitySearch} | ||
| 165 | + onfocus={() => { entityFocused = true; selectedEntity = null; }} | ||
| 166 | + onblur={() => setTimeout(() => entityFocused = false, 150)} | ||
| 167 | + onkeydown={handleKeydown} | ||
| 168 | + placeholder="Buscar entidad..." | ||
| 169 | + class="search-input" | ||
| 170 | + /> | ||
| 171 | + {#if loadingEntities} | ||
| 172 | + <span class="search-status">Cargando entidades...</span> | ||
| 173 | + {:else if entityError} | ||
| 174 | + <span class="search-status error">No se pudieron cargar las entidades</span> | ||
| 175 | + {/if} | ||
| 176 | + | ||
| 177 | + {#if entityFocused && filteredEntities.length > 0} | ||
| 178 | + <div class="search-dropdown"> | ||
| 179 | + {#each filteredEntities as entity, i} | ||
| 180 | + <button | ||
| 181 | + class="search-item" | ||
| 182 | + class:active={selectedIdx === i} | ||
| 183 | + onmouseenter={() => selectedIdx = i} | ||
| 184 | + onclick={() => selectEntity(entity)} | ||
| 185 | + > | ||
| 186 | + <span class="item-name">{entity.desc_entidad}</span> | ||
| 187 | + <span class="item-code">Código {entity.entidad}</span> | ||
| 188 | + </button> | ||
| 189 | + {/each} | ||
| 190 | + </div> | ||
| 191 | + {/if} | ||
| 192 | + | ||
| 193 | + {#if entityFocused && entitySearch.length >= 2 && filteredEntities.length === 0 && !loadingEntities} | ||
| 194 | + <span class="search-status error">Sin resultados</span> | ||
| 195 | + {/if} | ||
| 196 | + </div> | ||
| 149 | 197 | ||
| 150 | - {#if entityFocused && entitySearch.length >= 2 && filteredEntities.length === 0 && !loadingEntities} | 198 | + {#if selectedEntity} |
| 151 | - <span class="search-status error">Sin resultados</span> | 199 | + <div class="entity-result"> |
| 200 | + <span class="entity-name">{selectedEntity.desc_entidad}</span> | ||
| 201 | + <a href={buildUrl('entidad', selectedEntity.entidad, formatoEntidad)} download rel="external" class="download-link"> | ||
| 202 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | ||
| 203 | + Descargar .{formatoEntidad === 'xlsx' ? 'xlsx' : 'csv'} | ||
| 204 | + </a> | ||
| 205 | + </div> | ||
| 152 | {/if} | 206 | {/if} |
| 153 | </div> | 207 | </div> |
| 208 | + </section> | ||
| 209 | + | ||
| 210 | + <!-- ═══════════════════════════════════════════ --> | ||
| 211 | + <!-- SECCIÓN 2: Base completa --> | ||
| 212 | + <!-- ═══════════════════════════════════════════ --> | ||
| 213 | + <section class="section"> | ||
| 214 | + <h2 class="section-title">Base completa</h2> | ||
| 215 | + <p class="section-desc">Toda la base histórica: todas las entidades y todos los años en un solo archivo</p> | ||
| 216 | + | ||
| 217 | + <div class="control-row"> | ||
| 218 | + <span class="control-label">Base</span> | ||
| 219 | + <div class="toggle-group"> | ||
| 220 | + <button class="toggle-btn" class:active={datasetMasivo === 'gasto'} onclick={() => datasetMasivo = 'gasto'}>Gastos</button> | ||
| 221 | + <button class="toggle-btn" class:active={datasetMasivo === 'ingreso'} onclick={() => datasetMasivo = 'ingreso'}>Ingresos</button> | ||
| 222 | + </div> | ||
| 223 | + </div> | ||
| 154 | 224 | ||
| 155 | - {#if selectedEntity} | 225 | + <div class="masivo-downloads"> |
| 156 | - <div class="entity-result"> | 226 | + <a href={buildMasivoUrl('csv')} download rel="external" class="download-card"> |
| 157 | - <span class="entity-name">{selectedEntity.desc_entidad}</span> | 227 | + <div class="download-card-icon"> |
| 158 | - <a href={buildUrl('entidad', selectedEntity.entidad)} download rel="external" class="download-link"> | 228 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> |
| 159 | - <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | 229 | + </div> |
| 160 | - Descargar | 230 | + <div class="download-card-info"> |
| 161 | - </a> | 231 | + <span class="download-card-format">CSV</span> |
| 232 | + <span class="download-card-desc">{datasetMasivo === 'gasto' ? 'Gastos' : 'Ingresos'} — archivo .zip</span> | ||
| 233 | + </div> | ||
| 234 | + </a> | ||
| 235 | + <a href={buildMasivoUrl('parquet')} download rel="external" class="download-card"> | ||
| 236 | + <div class="download-card-icon"> | ||
| 237 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | ||
| 238 | + </div> | ||
| 239 | + <div class="download-card-info"> | ||
| 240 | + <span class="download-card-format">Parquet</span> | ||
| 241 | + <span class="download-card-desc">{datasetMasivo === 'gasto' ? 'Gastos' : 'Ingresos'} — archivo .parquet</span> | ||
| 242 | + </div> | ||
| 243 | + </a> | ||
| 244 | + </div> | ||
| 245 | + </section> | ||
| 246 | + | ||
| 247 | + <!-- ═══════════════════════════════════════════ --> | ||
| 248 | + <!-- SECCIÓN 3: Ejecución semanal --> | ||
| 249 | + <!-- ═══════════════════════════════════════════ --> | ||
| 250 | + <section class="section"> | ||
| 251 | + <h2 class="section-title">Ejecución semanal</h2> | ||
| 252 | + <p class="section-desc">Reporte semanal de ejecución presupuestaria con datos agregados</p> | ||
| 253 | + | ||
| 254 | + <div class="semanal-downloads disabled"> | ||
| 255 | + <span class="semanal-badge">Próximamente</span> | ||
| 256 | + <div class="masivo-downloads"> | ||
| 257 | + <span class="download-card download-card-disabled"> | ||
| 258 | + <div class="download-card-icon"> | ||
| 259 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | ||
| 260 | + </div> | ||
| 261 | + <div class="download-card-info"> | ||
| 262 | + <span class="download-card-format">CSV</span> | ||
| 263 | + <span class="download-card-desc">Ejecución semanal — archivo .zip</span> | ||
| 264 | + </div> | ||
| 265 | + </span> | ||
| 266 | + <span class="download-card download-card-disabled"> | ||
| 267 | + <div class="download-card-icon"> | ||
| 268 | + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg> | ||
| 269 | + </div> | ||
| 270 | + <div class="download-card-info"> | ||
| 271 | + <span class="download-card-format">Excel</span> | ||
| 272 | + <span class="download-card-desc">Ejecución semanal — archivo .xlsx</span> | ||
| 273 | + </div> | ||
| 274 | + </span> | ||
| 162 | </div> | 275 | </div> |
| 163 | - {/if} | 276 | + </div> |
| 164 | - </div> | 277 | + </section> |
| 278 | + | ||
| 279 | + <!-- ═══════════════════════════════════════════ --> | ||
| 280 | + <!-- SECCIÓN 4: Para desarrolladores --> | ||
| 281 | + <!-- ═══════════════════════════════════════════ --> | ||
| 282 | + <section class="section section-last"> | ||
| 283 | + <h2 class="section-title">Para desarrolladores</h2> | ||
| 284 | + <p class="section-desc">URLs directas a los archivos Parquet para consumo programático</p> | ||
| 285 | + | ||
| 286 | + <div class="dev-urls"> | ||
| 287 | + {#each Object.entries(parquetUrls) as [key, url]} | ||
| 288 | + <div class="dev-url-row"> | ||
| 289 | + <span class="dev-url-label">{key === 'gasto' ? 'Gastos' : 'Ingresos'}</span> | ||
| 290 | + <div class="dev-url-block"> | ||
| 291 | + <code class="dev-url-code">{url}</code> | ||
| 292 | + <button class="dev-url-copy" onclick={() => copyUrl(url)}> | ||
| 293 | + {#if copiedUrl === url} | ||
| 294 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> | ||
| 295 | + {:else} | ||
| 296 | + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> | ||
| 297 | + {/if} | ||
| 298 | + </button> | ||
| 299 | + </div> | ||
| 300 | + </div> | ||
| 301 | + {/each} | ||
| 302 | + </div> | ||
| 303 | + </section> | ||
| 165 | </div> | 304 | </div> |
| 166 | </div> | 305 | </div> |
| 167 | 306 | ||
| 168 | <style> | 307 | <style> |
| 169 | .page { | 308 | .page { |
| 170 | - height: 100dvh; | 309 | + min-height: 100dvh; |
| 171 | background: var(--theme-body); | 310 | background: var(--theme-body); |
| 172 | color: var(--theme-titulo); | 311 | color: var(--theme-titulo); |
| 173 | font-family: 'Qanelas', var(--font-sans), -apple-system, sans-serif; | 312 | font-family: 'Qanelas', var(--font-sans), -apple-system, sans-serif; |
| ... | @@ -181,9 +320,9 @@ | ... | @@ -181,9 +320,9 @@ |
| 181 | :global(html:not(.dark)) .page { background: #f2f2f4; } | 320 | :global(html:not(.dark)) .page { background: #f2f2f4; } |
| 182 | 321 | ||
| 183 | .layout { | 322 | .layout { |
| 184 | - max-width: 700px; | 323 | + max-width: 760px; |
| 185 | margin: 0 auto; | 324 | margin: 0 auto; |
| 186 | - padding: 1.5rem 2rem 2rem; | 325 | + padding: 1.5rem 2rem 3rem; |
| 187 | } | 326 | } |
| 188 | 327 | ||
| 189 | .title { | 328 | .title { |
| ... | @@ -195,38 +334,50 @@ | ... | @@ -195,38 +334,50 @@ |
| 195 | } | 334 | } |
| 196 | 335 | ||
| 197 | .subtitle { | 336 | .subtitle { |
| 198 | - font-family: 'Qanelas', var(--font-sans); | ||
| 199 | font-size: 0.875rem; | 337 | font-size: 0.875rem; |
| 200 | color: var(--theme-texto); | 338 | color: var(--theme-texto); |
| 201 | opacity: 0.7; | 339 | opacity: 0.7; |
| 202 | - margin: 0 0 1.5rem; | 340 | + margin: 0 0 2rem; |
| 203 | line-height: 1.5; | 341 | line-height: 1.5; |
| 204 | } | 342 | } |
| 205 | 343 | ||
| 344 | + /* Sections */ | ||
| 206 | .section { | 345 | .section { |
| 207 | - margin-bottom: 1.25rem; | 346 | + margin-bottom: 2rem; |
| 208 | - padding-bottom: 1.25rem; | 347 | + padding-bottom: 2rem; |
| 209 | border-bottom: 1px solid var(--theme-borde); | 348 | border-bottom: 1px solid var(--theme-borde); |
| 210 | } | 349 | } |
| 211 | - .section:last-child { border-bottom: none; } | 350 | + .section-last { border-bottom: none; } |
| 212 | 351 | ||
| 213 | - .section-label { | 352 | + .section-title { |
| 214 | - font-family: 'JetBrains Mono', 'DM Mono', monospace; | 353 | + font-size: 1.125rem; |
| 354 | + font-weight: 700; | ||
| 355 | + margin: 0 0 0.25rem; | ||
| 356 | + letter-spacing: -0.01em; | ||
| 357 | + } | ||
| 358 | + | ||
| 359 | + .section-desc { | ||
| 360 | + font-size: 0.8125rem; | ||
| 361 | + color: var(--theme-texto); | ||
| 362 | + opacity: 0.6; | ||
| 363 | + margin: 0 0 1.25rem; | ||
| 364 | + line-height: 1.5; | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + /* Control row */ | ||
| 368 | + .control-row { | ||
| 369 | + display: flex; | ||
| 370 | + align-items: center; | ||
| 371 | + gap: 0.75rem; | ||
| 372 | + margin-bottom: 1.25rem; | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | + .control-label { | ||
| 215 | font-size: 0.6875rem; | 376 | font-size: 0.6875rem; |
| 216 | letter-spacing: 0.12em; | 377 | letter-spacing: 0.12em; |
| 217 | text-transform: uppercase; | 378 | text-transform: uppercase; |
| 218 | color: var(--theme-texto); | 379 | color: var(--theme-texto); |
| 219 | opacity: 0.5; | 380 | opacity: 0.5; |
| 220 | - display: block; | ||
| 221 | - margin-bottom: 0.75rem; | ||
| 222 | - } | ||
| 223 | - | ||
| 224 | - .section-title { | ||
| 225 | - font-family: 'Qanelas', var(--font-sans); | ||
| 226 | - font-size: 1rem; | ||
| 227 | - font-weight: 800; | ||
| 228 | - margin: 0 0 0.625rem; | ||
| 229 | - letter-spacing: -0.01em; | ||
| 230 | } | 381 | } |
| 231 | 382 | ||
| 232 | /* Toggle */ | 383 | /* Toggle */ |
| ... | @@ -239,12 +390,13 @@ | ... | @@ -239,12 +390,13 @@ |
| 239 | padding: 3px; | 390 | padding: 3px; |
| 240 | width: fit-content; | 391 | width: fit-content; |
| 241 | } | 392 | } |
| 393 | + :global(html:not(.dark)) .toggle-group { background: #ffffff; border-color: rgba(0,0,0,0.08); } | ||
| 242 | 394 | ||
| 243 | .toggle-btn { | 395 | .toggle-btn { |
| 244 | font-family: 'Qanelas', var(--font-sans); | 396 | font-family: 'Qanelas', var(--font-sans); |
| 245 | - font-size: 0.875rem; | 397 | + font-size: 0.8125rem; |
| 246 | font-weight: 600; | 398 | font-weight: 600; |
| 247 | - padding: 0.5rem 1.5rem; | 399 | + padding: 0.4rem 1.25rem; |
| 248 | border: none; | 400 | border: none; |
| 249 | border-radius: 8px; | 401 | border-radius: 8px; |
| 250 | background: transparent; | 402 | background: transparent; |
| ... | @@ -263,46 +415,78 @@ | ... | @@ -263,46 +415,78 @@ |
| 263 | color: var(--theme-titulo); | 415 | color: var(--theme-titulo); |
| 264 | } | 416 | } |
| 265 | 417 | ||
| 266 | - /* Download link */ | 418 | + /* Subsections */ |
| 267 | - .download-link { | 419 | + .subsection { |
| 268 | - display: inline-flex; | 420 | + margin-bottom: 1.5rem; |
| 421 | + } | ||
| 422 | + .subsection:last-child { margin-bottom: 0; } | ||
| 423 | + | ||
| 424 | + .subsection-header { | ||
| 425 | + display: flex; | ||
| 269 | align-items: center; | 426 | align-items: center; |
| 427 | + justify-content: space-between; | ||
| 428 | + margin-bottom: 0.75rem; | ||
| 429 | + flex-wrap: wrap; | ||
| 270 | gap: 0.5rem; | 430 | gap: 0.5rem; |
| 271 | - font-family: 'Qanelas', var(--font-sans); | 431 | + } |
| 432 | + | ||
| 433 | + .subsection-title { | ||
| 272 | font-size: 0.9375rem; | 434 | font-size: 0.9375rem; |
| 273 | font-weight: 600; | 435 | font-weight: 600; |
| 274 | - color: var(--theme-accent); | 436 | + margin: 0; |
| 275 | - text-decoration: none; | ||
| 276 | - padding: 0.625rem 1.25rem; | ||
| 277 | - border: 1px solid var(--theme-accent); | ||
| 278 | - border-radius: 10px; | ||
| 279 | - transition: all 0.2s; | ||
| 280 | } | 437 | } |
| 281 | - .download-link:hover { | 438 | + |
| 282 | - background: var(--theme-accent); | 439 | + /* Formato toggle */ |
| 283 | - color: #1C1C1A; | 440 | + .formato-toggle { |
| 441 | + display: flex; | ||
| 442 | + align-items: center; | ||
| 443 | + gap: 0.375rem; | ||
| 284 | } | 444 | } |
| 285 | - :global(html:not(.dark)) .download-link { | 445 | + |
| 286 | - border-color: rgba(0,0,0,0.12); | 446 | + .formato-label { |
| 287 | - color: var(--theme-titulo); | 447 | + font-size: 0.6875rem; |
| 448 | + color: var(--theme-texto); | ||
| 449 | + opacity: 0.5; | ||
| 450 | + margin-right: 0.25rem; | ||
| 288 | } | 451 | } |
| 289 | - :global(html:not(.dark)) .download-link:hover { | 452 | + |
| 290 | - background: rgba(0,0,0,0.03); | 453 | + .fmt-btn { |
| 291 | - color: var(--theme-titulo); | 454 | + font-family: 'Qanelas', var(--font-sans); |
| 455 | + font-size: 0.6875rem; | ||
| 456 | + font-weight: 600; | ||
| 457 | + padding: 0.3rem 0.75rem; | ||
| 458 | + border: 1px solid var(--theme-borde); | ||
| 459 | + border-radius: 6px; | ||
| 460 | + background: transparent; | ||
| 461 | + color: var(--theme-texto); | ||
| 462 | + cursor: pointer; | ||
| 463 | + transition: all 0.15s; | ||
| 464 | + } | ||
| 465 | + .fmt-btn:hover { border-color: var(--theme-accent); color: var(--theme-titulo); } | ||
| 466 | + .fmt-btn.active { | ||
| 467 | + background: var(--theme-titulo); | ||
| 468 | + border-color: var(--theme-titulo); | ||
| 469 | + color: var(--theme-body); | ||
| 470 | + } | ||
| 471 | + :global(.dark) .fmt-btn.active { | ||
| 472 | + background: var(--theme-accent); | ||
| 473 | + border-color: var(--theme-accent); | ||
| 474 | + color: var(--theme-body); | ||
| 292 | } | 475 | } |
| 293 | 476 | ||
| 294 | /* Years */ | 477 | /* Years */ |
| 295 | .year-grid { | 478 | .year-grid { |
| 296 | display: grid; | 479 | display: grid; |
| 297 | - grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); | 480 | + grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); |
| 298 | - gap: 0.625rem; | 481 | + gap: 0.5rem; |
| 299 | } | 482 | } |
| 300 | 483 | ||
| 301 | .year-link { | 484 | .year-link { |
| 302 | - font-family: 'Qanelas', var(--font-sans); | 485 | + display: flex; |
| 303 | - font-size: 0.8125rem; | 486 | + flex-direction: column; |
| 304 | - font-weight: 500; | 487 | + align-items: center; |
| 305 | - padding: 0.625rem; | 488 | + gap: 0.1rem; |
| 489 | + padding: 0.625rem 0.5rem; | ||
| 306 | background: #2A2A27; | 490 | background: #2A2A27; |
| 307 | border: 1px solid rgba(255,255,255,0.08); | 491 | border: 1px solid rgba(255,255,255,0.08); |
| 308 | border-radius: 10px; | 492 | border-radius: 10px; |
| ... | @@ -317,6 +501,7 @@ | ... | @@ -317,6 +501,7 @@ |
| 317 | background: rgba(201, 167, 81, 0.06); | 501 | background: rgba(201, 167, 81, 0.06); |
| 318 | transform: translateY(-1px); | 502 | transform: translateY(-1px); |
| 319 | } | 503 | } |
| 504 | + :global(html:not(.dark)) .year-link { background: #ffffff; border-color: rgba(0,0,0,0.08); } | ||
| 320 | :global(html:not(.dark)) .year-link:hover { | 505 | :global(html:not(.dark)) .year-link:hover { |
| 321 | border-color: rgba(0,0,0,0.15); | 506 | border-color: rgba(0,0,0,0.15); |
| 322 | color: var(--theme-titulo); | 507 | color: var(--theme-titulo); |
| ... | @@ -324,11 +509,49 @@ | ... | @@ -324,11 +509,49 @@ |
| 324 | transform: none; | 509 | transform: none; |
| 325 | } | 510 | } |
| 326 | 511 | ||
| 327 | - /* Entity search */ | 512 | + .year-num { |
| 328 | - .entity-search { | 513 | + font-size: 0.8125rem; |
| 329 | - position: relative; | 514 | + font-weight: 600; |
| 330 | } | 515 | } |
| 331 | 516 | ||
| 517 | + .year-fmt { | ||
| 518 | + font-size: 0.6rem; | ||
| 519 | + color: var(--theme-texto); | ||
| 520 | + opacity: 0.4; | ||
| 521 | + text-transform: uppercase; | ||
| 522 | + letter-spacing: 0.05em; | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + /* Download link */ | ||
| 526 | + .download-link { | ||
| 527 | + display: inline-flex; | ||
| 528 | + align-items: center; | ||
| 529 | + gap: 0.5rem; | ||
| 530 | + font-size: 0.875rem; | ||
| 531 | + font-weight: 600; | ||
| 532 | + color: var(--theme-accent); | ||
| 533 | + text-decoration: none; | ||
| 534 | + padding: 0.5rem 1rem; | ||
| 535 | + border: 1px solid var(--theme-accent); | ||
| 536 | + border-radius: 10px; | ||
| 537 | + transition: all 0.2s; | ||
| 538 | + flex-shrink: 0; | ||
| 539 | + } | ||
| 540 | + .download-link:hover { | ||
| 541 | + background: var(--theme-accent); | ||
| 542 | + color: #1C1C1A; | ||
| 543 | + } | ||
| 544 | + :global(html:not(.dark)) .download-link { | ||
| 545 | + border-color: rgba(0,0,0,0.12); | ||
| 546 | + color: var(--theme-titulo); | ||
| 547 | + } | ||
| 548 | + :global(html:not(.dark)) .download-link:hover { | ||
| 549 | + background: rgba(0,0,0,0.03); | ||
| 550 | + } | ||
| 551 | + | ||
| 552 | + /* Entity search */ | ||
| 553 | + .entity-search { position: relative; } | ||
| 554 | + | ||
| 332 | .search-input { | 555 | .search-input { |
| 333 | width: 100%; | 556 | width: 100%; |
| 334 | max-width: 480px; | 557 | max-width: 480px; |
| ... | @@ -338,7 +561,7 @@ | ... | @@ -338,7 +561,7 @@ |
| 338 | background: #2A2A27; | 561 | background: #2A2A27; |
| 339 | color: var(--theme-titulo); | 562 | color: var(--theme-titulo); |
| 340 | font-family: 'Qanelas', var(--font-sans); | 563 | font-family: 'Qanelas', var(--font-sans); |
| 341 | - font-size: 0.9375rem; | 564 | + font-size: 0.875rem; |
| 342 | outline: none; | 565 | outline: none; |
| 343 | transition: border-color 0.2s, box-shadow 0.2s; | 566 | transition: border-color 0.2s, box-shadow 0.2s; |
| 344 | } | 567 | } |
| ... | @@ -347,6 +570,7 @@ | ... | @@ -347,6 +570,7 @@ |
| 347 | box-shadow: 0 0 0 3px rgba(201, 167, 81, 0.12); | 570 | box-shadow: 0 0 0 3px rgba(201, 167, 81, 0.12); |
| 348 | } | 571 | } |
| 349 | .search-input::placeholder { color: var(--theme-texto); opacity: 0.4; } | 572 | .search-input::placeholder { color: var(--theme-texto); opacity: 0.4; } |
| 573 | + :global(html:not(.dark)) .search-input { background: #ffffff; border-color: rgba(0,0,0,0.08); } | ||
| 350 | 574 | ||
| 351 | .search-status { | 575 | .search-status { |
| 352 | display: block; | 576 | display: block; |
| ... | @@ -371,6 +595,7 @@ | ... | @@ -371,6 +595,7 @@ |
| 371 | overflow-y: auto; | 595 | overflow-y: auto; |
| 372 | z-index: 10; | 596 | z-index: 10; |
| 373 | } | 597 | } |
| 598 | + :global(html:not(.dark)) .search-dropdown { background: #ffffff; } | ||
| 374 | 599 | ||
| 375 | .search-item { | 600 | .search-item { |
| 376 | display: flex; | 601 | display: flex; |
| ... | @@ -388,13 +613,12 @@ | ... | @@ -388,13 +613,12 @@ |
| 388 | .search-item:hover, .search-item.active { background: var(--theme-surface-hover, rgba(128,128,128,0.06)); } | 613 | .search-item:hover, .search-item.active { background: var(--theme-surface-hover, rgba(128,128,128,0.06)); } |
| 389 | 614 | ||
| 390 | .item-name { | 615 | .item-name { |
| 391 | - font-family: 'Qanelas', var(--font-sans); | ||
| 392 | font-size: 0.875rem; | 616 | font-size: 0.875rem; |
| 393 | font-weight: 500; | 617 | font-weight: 500; |
| 394 | color: var(--theme-titulo); | 618 | color: var(--theme-titulo); |
| 395 | } | 619 | } |
| 396 | .item-code { | 620 | .item-code { |
| 397 | - font-family: 'JetBrains Mono', 'DM Mono', monospace; | 621 | + font-family: 'JetBrains Mono', monospace; |
| 398 | font-size: 0.6875rem; | 622 | font-size: 0.6875rem; |
| 399 | color: var(--theme-texto); | 623 | color: var(--theme-texto); |
| 400 | opacity: 0.4; | 624 | opacity: 0.4; |
| ... | @@ -406,32 +630,157 @@ | ... | @@ -406,32 +630,157 @@ |
| 406 | align-items: center; | 630 | align-items: center; |
| 407 | justify-content: space-between; | 631 | justify-content: space-between; |
| 408 | gap: 1rem; | 632 | gap: 1rem; |
| 409 | - margin-top: 1.25rem; | 633 | + margin-top: 1rem; |
| 410 | padding: 1rem 1.25rem; | 634 | padding: 1rem 1.25rem; |
| 411 | background: #2A2A27; | 635 | background: #2A2A27; |
| 412 | border: 1px solid rgba(255,255,255,0.08); | 636 | border: 1px solid rgba(255,255,255,0.08); |
| 413 | border-radius: 12px; | 637 | border-radius: 12px; |
| 414 | max-width: 480px; | 638 | max-width: 480px; |
| 415 | } | 639 | } |
| 640 | + :global(html:not(.dark)) .entity-result { background: #ffffff; border-color: rgba(0,0,0,0.08); } | ||
| 416 | 641 | ||
| 417 | .entity-name { | 642 | .entity-name { |
| 418 | - font-family: 'Qanelas', var(--font-sans); | 643 | + font-size: 0.875rem; |
| 644 | + font-weight: 600; | ||
| 645 | + color: var(--theme-titulo); | ||
| 646 | + } | ||
| 647 | + | ||
| 648 | + /* Masivo downloads */ | ||
| 649 | + .masivo-downloads { | ||
| 650 | + display: grid; | ||
| 651 | + grid-template-columns: 1fr 1fr; | ||
| 652 | + gap: 0.75rem; | ||
| 653 | + max-width: 480px; | ||
| 654 | + } | ||
| 655 | + | ||
| 656 | + .download-card { | ||
| 657 | + display: flex; | ||
| 658 | + align-items: center; | ||
| 659 | + gap: 0.75rem; | ||
| 660 | + padding: 1rem 1.25rem; | ||
| 661 | + background: #2A2A27; | ||
| 662 | + border: 1px solid rgba(255,255,255,0.08); | ||
| 663 | + border-radius: 12px; | ||
| 664 | + text-decoration: none; | ||
| 665 | + color: var(--theme-titulo); | ||
| 666 | + transition: all 0.2s; | ||
| 667 | + } | ||
| 668 | + .download-card:hover { | ||
| 669 | + border-color: var(--theme-accent); | ||
| 670 | + transform: translateY(-1px); | ||
| 671 | + } | ||
| 672 | + :global(html:not(.dark)) .download-card { background: #ffffff; border-color: rgba(0,0,0,0.08); } | ||
| 673 | + :global(html:not(.dark)) .download-card:hover { border-color: rgba(0,0,0,0.15); transform: none; } | ||
| 674 | + | ||
| 675 | + .download-card-icon { | ||
| 676 | + color: var(--theme-accent); | ||
| 677 | + flex-shrink: 0; | ||
| 678 | + } | ||
| 679 | + | ||
| 680 | + .download-card-info { | ||
| 681 | + display: flex; | ||
| 682 | + flex-direction: column; | ||
| 683 | + gap: 0.1rem; | ||
| 684 | + } | ||
| 685 | + | ||
| 686 | + .download-card-format { | ||
| 419 | font-size: 0.9375rem; | 687 | font-size: 0.9375rem; |
| 688 | + font-weight: 700; | ||
| 689 | + } | ||
| 690 | + | ||
| 691 | + .download-card-desc { | ||
| 692 | + font-size: 0.6875rem; | ||
| 693 | + color: var(--theme-texto); | ||
| 694 | + opacity: 0.5; | ||
| 695 | + } | ||
| 696 | + | ||
| 697 | + /* Semanal */ | ||
| 698 | + .semanal-downloads { | ||
| 699 | + position: relative; | ||
| 700 | + } | ||
| 701 | + .semanal-downloads.disabled { opacity: 0.5; pointer-events: none; } | ||
| 702 | + | ||
| 703 | + .semanal-badge { | ||
| 704 | + font-size: 0.6rem; | ||
| 705 | + font-weight: 600; | ||
| 706 | + text-transform: uppercase; | ||
| 707 | + letter-spacing: 0.08em; | ||
| 708 | + padding: 0.2rem 0.5rem; | ||
| 709 | + border-radius: 4px; | ||
| 710 | + background: var(--theme-borde); | ||
| 711 | + color: var(--theme-texto); | ||
| 712 | + margin-bottom: 0.75rem; | ||
| 713 | + display: inline-block; | ||
| 714 | + } | ||
| 715 | + | ||
| 716 | + .download-card-disabled { | ||
| 717 | + cursor: default; | ||
| 718 | + pointer-events: none; | ||
| 719 | + } | ||
| 720 | + | ||
| 721 | + /* Dev URLs */ | ||
| 722 | + .dev-urls { | ||
| 723 | + display: flex; | ||
| 724 | + flex-direction: column; | ||
| 725 | + gap: 0.75rem; | ||
| 726 | + } | ||
| 727 | + | ||
| 728 | + .dev-url-row { | ||
| 729 | + display: flex; | ||
| 730 | + flex-direction: column; | ||
| 731 | + gap: 0.35rem; | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + .dev-url-label { | ||
| 735 | + font-size: 0.75rem; | ||
| 420 | font-weight: 600; | 736 | font-weight: 600; |
| 421 | color: var(--theme-titulo); | 737 | color: var(--theme-titulo); |
| 422 | } | 738 | } |
| 423 | 739 | ||
| 424 | - :global(html:not(.dark)) .toggle-group { background: #ffffff; border-color: rgba(0,0,0,0.08); } | 740 | + .dev-url-block { |
| 425 | - :global(html:not(.dark)) .year-link { background: #ffffff; border-color: rgba(0,0,0,0.08); } | 741 | + display: flex; |
| 426 | - :global(html:not(.dark)) .search-input { background: #ffffff; border-color: rgba(0,0,0,0.08); } | 742 | + align-items: center; |
| 427 | - :global(html:not(.dark)) .entity-result { background: #ffffff; border-color: rgba(0,0,0,0.08); } | 743 | + gap: 0; |
| 428 | - :global(html:not(.dark)) .search-dropdown { background: #ffffff; } | 744 | + background: #1a1a18; |
| 745 | + border: 1px solid rgba(255,255,255,0.08); | ||
| 746 | + border-radius: 8px; | ||
| 747 | + overflow: hidden; | ||
| 748 | + } | ||
| 749 | + :global(html:not(.dark)) .dev-url-block { background: #f5f5f5; border-color: rgba(0,0,0,0.08); } | ||
| 750 | + | ||
| 751 | + .dev-url-code { | ||
| 752 | + flex: 1; | ||
| 753 | + padding: 0.75rem 1rem; | ||
| 754 | + font-family: 'JetBrains Mono', monospace; | ||
| 755 | + font-size: 0.75rem; | ||
| 756 | + color: var(--theme-accent); | ||
| 757 | + white-space: nowrap; | ||
| 758 | + overflow-x: auto; | ||
| 759 | + scrollbar-width: none; | ||
| 760 | + } | ||
| 761 | + .dev-url-code::-webkit-scrollbar { display: none; } | ||
| 762 | + :global(html:not(.dark)) .dev-url-code { color: var(--theme-titulo); } | ||
| 763 | + | ||
| 764 | + .dev-url-copy { | ||
| 765 | + padding: 0.75rem 1rem; | ||
| 766 | + background: transparent; | ||
| 767 | + border: none; | ||
| 768 | + border-left: 1px solid rgba(255,255,255,0.08); | ||
| 769 | + color: var(--theme-texto); | ||
| 770 | + cursor: pointer; | ||
| 771 | + transition: all 0.15s; | ||
| 772 | + flex-shrink: 0; | ||
| 773 | + } | ||
| 774 | + .dev-url-copy:hover { color: var(--theme-accent); } | ||
| 775 | + :global(html:not(.dark)) .dev-url-copy { border-left-color: rgba(0,0,0,0.08); } | ||
| 429 | 776 | ||
| 430 | @media (max-width: 640px) { | 777 | @media (max-width: 640px) { |
| 431 | .layout { padding: 1rem 1rem 2rem; } | 778 | .layout { padding: 1rem 1rem 2rem; } |
| 432 | - .title { font-size: 2rem; } | 779 | + .title { font-size: 1.75rem; } |
| 433 | - .year-grid { grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); } | 780 | + .year-grid { grid-template-columns: repeat(auto-fill, minmax(75px, 1fr)); } |
| 781 | + .masivo-downloads { grid-template-columns: 1fr; } | ||
| 434 | .search-input, .search-dropdown, .entity-result { max-width: 100%; } | 782 | .search-input, .search-dropdown, .entity-result { max-width: 100%; } |
| 435 | - .toggle-btn { padding: 0.5rem 1rem; font-size: 0.8125rem; } | 783 | + .subsection-header { flex-direction: column; align-items: flex-start; } |
| 784 | + .entity-result { flex-direction: column; align-items: flex-start; } | ||
| 436 | } | 785 | } |
| 437 | </style> | 786 | </style> | ... | ... |
| ... | @@ -278,7 +278,7 @@ | ... | @@ -278,7 +278,7 @@ |
| 278 | }; | 278 | }; |
| 279 | }); | 279 | }); |
| 280 | 280 | ||
| 281 | - // Filtrar datos por transferencias y días | 281 | + // Filtrar datos por transferencias |
| 282 | let timeserieFiltered = $derived( | 282 | let timeserieFiltered = $derived( |
| 283 | timeserie.filter(d => d.transferencias === undefined || d.transferencias === incluirTransferencias) | 283 | timeserie.filter(d => d.transferencias === undefined || d.transferencias === incluirTransferencias) |
| 284 | ); | 284 | ); | ... | ... |
static/ejecucion_clasificadores.parquet
0 → 100644
No preview for this file type
static/ejecucion_entidades.parquet
0 → 100644
No preview for this file type
static/timeline.parquet
0 → 100644
No preview for this file type
static/timeserie.parquet
0 → 100644
No preview for this file type
-
Please register or login to post a comment