Rafael Lopez

diario y descargas

......@@ -13,6 +13,7 @@
"d3": "^7.9.0",
"fuse.js": "^7.1.0",
"hyparquet": "^1.25.1",
"hyparquet-compressors": "^1.1.1",
"marked": "^17.0.4"
},
"devDependencies": {
......@@ -1984,6 +1985,12 @@
"node": ">=10"
}
},
"node_modules/fzstd": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/fzstd/-/fzstd-0.1.1.tgz",
"integrity": "sha512-dkuVSOKKwh3eas5VkJy1AW1vFpet8TA/fGmVA5krThl8YcOVE/8ZIoEA1+U1vEn5ckxxhLirSdY837azmbaNHA==",
"license": "MIT"
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
......@@ -2009,6 +2016,22 @@
"integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==",
"license": "MIT"
},
"node_modules/hyparquet-compressors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/hyparquet-compressors/-/hyparquet-compressors-1.1.1.tgz",
"integrity": "sha512-yx7aA3Rhj0YycbdV71+XznQSLAefa4cT0urpgNXy4aM6eSeCknaVDNne8y45Uz74Fb15yyXUzOStlceOJBan7A==",
"license": "MIT",
"dependencies": {
"fzstd": "0.1.1",
"hysnappy": "1.0.0"
}
},
"node_modules/hysnappy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hysnappy/-/hysnappy-1.0.0.tgz",
"integrity": "sha512-MNrC4NfwDGPb889O6gIfEtbvEZCSWUsSEhsz4Oq2FRcpGtXHfeVz3KciSPp5Pnnz1NjFMgDQNfxdJozymJEDDA==",
"license": "MIT"
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
......
......@@ -24,6 +24,7 @@
"d3": "^7.9.0",
"fuse.js": "^7.1.0",
"hyparquet": "^1.25.1",
"hyparquet-compressors": "^1.1.1",
"marked": "^17.0.4"
}
}
......
import { API_HOST } from '$lib/api.js';
const API_BASE = API_HOST + '/api/diario';
const API_BASE = 'https://abierto.economiayfinanzas.gob.bo/presupuesto/_api/diario';
export async function GET({ url }) {
const tipo = url.searchParams.get('tipo') || 'status';
......
......@@ -5,6 +5,9 @@
let mounted = $state(false);
let dataset = $state('gasto');
let datasetMasivo = $state('gasto');
let formatoGestion = $state('csv');
let formatoEntidad = $state('csv');
let entities = $state([]);
let entitySearch = $state('');
let entityFocused = $state(false);
......@@ -12,22 +15,50 @@
let selectedIdx = $state(-1);
let loadingEntities = $state(true);
let entityError = $state(false);
let copiedUrl = $state(null);
function normalizeText(val) {
return val.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim();
}
function buildUrl(partition, value) {
function buildUrl(partition, value, formato = 'csv') {
if (partition === 'completo') {
if (formato === 'parquet') {
return `${DOWNLOAD_BASE}${dataset}/ultimo/${dataset}.parquet`;
}
return `${DOWNLOAD_BASE}${dataset}/ultimo/${dataset}.zip`;
} else if (partition === 'gestion') {
if (formato === 'xlsx') {
return `${DOWNLOAD_BASE}${dataset}/gestiones/gestion_${value}_xlsx.zip`;
}
return `${DOWNLOAD_BASE}${dataset}/gestiones/gestion_${value}.zip`;
} else if (partition === 'entidad') {
if (formato === 'xlsx') {
return `${DOWNLOAD_BASE}${dataset}/entidades/entidad_${value}_xlsx.zip`;
}
return `${DOWNLOAD_BASE}${dataset}/entidades/entidad_${value}.zip`;
}
return `${DOWNLOAD_BASE}${dataset}/${partition}/${value}`;
}
function buildMasivoUrl(formato) {
if (formato === 'parquet') {
return `${DOWNLOAD_BASE}${datasetMasivo}/ultimo/${datasetMasivo}.parquet`;
}
return `${DOWNLOAD_BASE}${datasetMasivo}/ultimo/${datasetMasivo}.zip`;
}
const parquetUrls = {
gasto: `${DOWNLOAD_BASE}gasto/ultimo/gasto.parquet`,
ingreso: `${DOWNLOAD_BASE}ingreso/ultimo/ingreso.parquet`
};
async function copyUrl(url) {
await navigator.clipboard.writeText(url);
copiedUrl = url;
setTimeout(() => copiedUrl = null, 2000);
}
let filteredEntities = $derived.by(() => {
if (!entitySearch || entitySearch.length < 2) return [];
const q = normalizeText(entitySearch);
......@@ -69,52 +100,64 @@
}
} catch { entityError = true; }
loadingEntities = false;
console.log('[DESCARGAS] entities loaded:', entities.length);
});
</script>
<svelte:head>
<title>Descargas | Presupuesto Público</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet" />
</svelte:head>
<div class="page" class:mounted>
<div class="layout">
<h1 class="title">Descargas</h1>
<p class="subtitle">Descarga los datos del presupuesto público en formato CSV</p>
<!-- Dataset toggle -->
<div class="section">
<span class="section-label">Base</span>
<p class="subtitle">Descarga los datos del presupuesto público boliviano en distintos formatos</p>
<!-- ═══════════════════════════════════════════ -->
<!-- SECCIÓN 1: Por gestión o entidad -->
<!-- ═══════════════════════════════════════════ -->
<section class="section">
<h2 class="section-title">Por gestión o entidad</h2>
<p class="section-desc">Descarga los datos segmentados por año fiscal o por entidad pública</p>
<!-- Toggle Gastos/Ingresos -->
<div class="control-row">
<span class="control-label">Base</span>
<div class="toggle-group">
<button class="toggle-btn" class:active={dataset === 'gasto'} onclick={() => { dataset = 'gasto'; selectedEntity = null; }}>Gastos</button>
<button class="toggle-btn" class:active={dataset === 'ingreso'} onclick={() => { dataset = 'ingreso'; selectedEntity = null; }}>Ingresos</button>
</div>
</div>
<!-- Completo -->
<div class="section">
<h2 class="section-title">Completo</h2>
<a href={buildUrl('completo', 'ultimo')} download rel="external" class="download-link">
<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>
Descargar todo ({dataset === 'gasto' ? 'gastos' : 'ingresos'})
</a>
</div>
<!-- Por gestión -->
<div class="section">
<h2 class="section-title">Por gestión</h2>
<div class="subsection">
<div class="subsection-header">
<h3 class="subsection-title">Por gestión</h3>
<div class="formato-toggle">
<span class="formato-label">Formato:</span>
<button class="fmt-btn" class:active={formatoGestion === 'csv'} onclick={() => formatoGestion = 'csv'}>CSV</button>
<button class="fmt-btn" class:active={formatoGestion === 'xlsx'} onclick={() => formatoGestion = 'xlsx'}>Excel</button>
</div>
</div>
<div class="year-grid">
{#each YEARS as year}
<a href={buildUrl('gestion', year)} download rel="external" class="year-link">{year}</a>
<a href={buildUrl('gestion', year, formatoGestion)} download rel="external" class="year-link">
<span class="year-num">{year}</span>
<span class="year-fmt">.{formatoGestion === 'xlsx' ? 'xlsx' : 'csv'}</span>
</a>
{/each}
</div>
</div>
<!-- Por entidad -->
<div class="section">
<h2 class="section-title">Por entidad</h2>
<div class="subsection">
<div class="subsection-header">
<h3 class="subsection-title">Por entidad</h3>
<div class="formato-toggle">
<span class="formato-label">Formato:</span>
<button class="fmt-btn" class:active={formatoEntidad === 'csv'} onclick={() => formatoEntidad = 'csv'}>CSV</button>
<button class="fmt-btn" class:active={formatoEntidad === 'xlsx'} onclick={() => formatoEntidad = 'xlsx'}>Excel</button>
</div>
</div>
<div class="entity-search">
<input
type="text"
......@@ -155,19 +198,115 @@
{#if selectedEntity}
<div class="entity-result">
<span class="entity-name">{selectedEntity.desc_entidad}</span>
<a href={buildUrl('entidad', selectedEntity.entidad)} download rel="external" class="download-link">
<a href={buildUrl('entidad', selectedEntity.entidad, formatoEntidad)} download rel="external" class="download-link">
<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>
Descargar
Descargar .{formatoEntidad === 'xlsx' ? 'xlsx' : 'csv'}
</a>
</div>
{/if}
</div>
</section>
<!-- ═══════════════════════════════════════════ -->
<!-- SECCIÓN 2: Base completa -->
<!-- ═══════════════════════════════════════════ -->
<section class="section">
<h2 class="section-title">Base completa</h2>
<p class="section-desc">Toda la base histórica: todas las entidades y todos los años en un solo archivo</p>
<div class="control-row">
<span class="control-label">Base</span>
<div class="toggle-group">
<button class="toggle-btn" class:active={datasetMasivo === 'gasto'} onclick={() => datasetMasivo = 'gasto'}>Gastos</button>
<button class="toggle-btn" class:active={datasetMasivo === 'ingreso'} onclick={() => datasetMasivo = 'ingreso'}>Ingresos</button>
</div>
</div>
<div class="masivo-downloads">
<a href={buildMasivoUrl('csv')} download rel="external" class="download-card">
<div class="download-card-icon">
<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>
</div>
<div class="download-card-info">
<span class="download-card-format">CSV</span>
<span class="download-card-desc">{datasetMasivo === 'gasto' ? 'Gastos' : 'Ingresos'} — archivo .zip</span>
</div>
</a>
<a href={buildMasivoUrl('parquet')} download rel="external" class="download-card">
<div class="download-card-icon">
<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>
</div>
<div class="download-card-info">
<span class="download-card-format">Parquet</span>
<span class="download-card-desc">{datasetMasivo === 'gasto' ? 'Gastos' : 'Ingresos'} — archivo .parquet</span>
</div>
</a>
</div>
</section>
<!-- ═══════════════════════════════════════════ -->
<!-- SECCIÓN 3: Ejecución semanal -->
<!-- ═══════════════════════════════════════════ -->
<section class="section">
<h2 class="section-title">Ejecución semanal</h2>
<p class="section-desc">Reporte semanal de ejecución presupuestaria con datos agregados</p>
<div class="semanal-downloads disabled">
<span class="semanal-badge">Próximamente</span>
<div class="masivo-downloads">
<span class="download-card download-card-disabled">
<div class="download-card-icon">
<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>
</div>
<div class="download-card-info">
<span class="download-card-format">CSV</span>
<span class="download-card-desc">Ejecución semanal — archivo .zip</span>
</div>
</span>
<span class="download-card download-card-disabled">
<div class="download-card-icon">
<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>
</div>
<div class="download-card-info">
<span class="download-card-format">Excel</span>
<span class="download-card-desc">Ejecución semanal — archivo .xlsx</span>
</div>
</span>
</div>
</div>
</section>
<!-- ═══════════════════════════════════════════ -->
<!-- SECCIÓN 4: Para desarrolladores -->
<!-- ═══════════════════════════════════════════ -->
<section class="section section-last">
<h2 class="section-title">Para desarrolladores</h2>
<p class="section-desc">URLs directas a los archivos Parquet para consumo programático</p>
<div class="dev-urls">
{#each Object.entries(parquetUrls) as [key, url]}
<div class="dev-url-row">
<span class="dev-url-label">{key === 'gasto' ? 'Gastos' : 'Ingresos'}</span>
<div class="dev-url-block">
<code class="dev-url-code">{url}</code>
<button class="dev-url-copy" onclick={() => copyUrl(url)}>
{#if copiedUrl === url}
<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>
{:else}
<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>
{/if}
</button>
</div>
</div>
{/each}
</div>
</section>
</div>
</div>
<style>
.page {
height: 100dvh;
min-height: 100dvh;
background: var(--theme-body);
color: var(--theme-titulo);
font-family: 'Qanelas', var(--font-sans), -apple-system, sans-serif;
......@@ -181,9 +320,9 @@
:global(html:not(.dark)) .page { background: #f2f2f4; }
.layout {
max-width: 700px;
max-width: 760px;
margin: 0 auto;
padding: 1.5rem 2rem 2rem;
padding: 1.5rem 2rem 3rem;
}
.title {
......@@ -195,38 +334,50 @@
}
.subtitle {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.875rem;
color: var(--theme-texto);
opacity: 0.7;
margin: 0 0 1.5rem;
margin: 0 0 2rem;
line-height: 1.5;
}
/* Sections */
.section {
margin-bottom: 1.25rem;
padding-bottom: 1.25rem;
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid var(--theme-borde);
}
.section:last-child { border-bottom: none; }
.section-last { border-bottom: none; }
.section-title {
font-size: 1.125rem;
font-weight: 700;
margin: 0 0 0.25rem;
letter-spacing: -0.01em;
}
.section-desc {
font-size: 0.8125rem;
color: var(--theme-texto);
opacity: 0.6;
margin: 0 0 1.25rem;
line-height: 1.5;
}
/* Control row */
.control-row {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1.25rem;
}
.section-label {
font-family: 'JetBrains Mono', 'DM Mono', monospace;
.control-label {
font-size: 0.6875rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--theme-texto);
opacity: 0.5;
display: block;
margin-bottom: 0.75rem;
}
.section-title {
font-family: 'Qanelas', var(--font-sans);
font-size: 1rem;
font-weight: 800;
margin: 0 0 0.625rem;
letter-spacing: -0.01em;
}
/* Toggle */
......@@ -239,12 +390,13 @@
padding: 3px;
width: fit-content;
}
:global(html:not(.dark)) .toggle-group { background: #ffffff; border-color: rgba(0,0,0,0.08); }
.toggle-btn {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.875rem;
font-size: 0.8125rem;
font-weight: 600;
padding: 0.5rem 1.5rem;
padding: 0.4rem 1.25rem;
border: none;
border-radius: 8px;
background: transparent;
......@@ -263,46 +415,78 @@
color: var(--theme-titulo);
}
/* Download link */
.download-link {
display: inline-flex;
/* Subsections */
.subsection {
margin-bottom: 1.5rem;
}
.subsection:last-child { margin-bottom: 0; }
.subsection-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.75rem;
flex-wrap: wrap;
gap: 0.5rem;
font-family: 'Qanelas', var(--font-sans);
}
.subsection-title {
font-size: 0.9375rem;
font-weight: 600;
color: var(--theme-accent);
text-decoration: none;
padding: 0.625rem 1.25rem;
border: 1px solid var(--theme-accent);
border-radius: 10px;
transition: all 0.2s;
margin: 0;
}
.download-link:hover {
background: var(--theme-accent);
color: #1C1C1A;
/* Formato toggle */
.formato-toggle {
display: flex;
align-items: center;
gap: 0.375rem;
}
:global(html:not(.dark)) .download-link {
border-color: rgba(0,0,0,0.12);
color: var(--theme-titulo);
.formato-label {
font-size: 0.6875rem;
color: var(--theme-texto);
opacity: 0.5;
margin-right: 0.25rem;
}
:global(html:not(.dark)) .download-link:hover {
background: rgba(0,0,0,0.03);
color: var(--theme-titulo);
.fmt-btn {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.6875rem;
font-weight: 600;
padding: 0.3rem 0.75rem;
border: 1px solid var(--theme-borde);
border-radius: 6px;
background: transparent;
color: var(--theme-texto);
cursor: pointer;
transition: all 0.15s;
}
.fmt-btn:hover { border-color: var(--theme-accent); color: var(--theme-titulo); }
.fmt-btn.active {
background: var(--theme-titulo);
border-color: var(--theme-titulo);
color: var(--theme-body);
}
:global(.dark) .fmt-btn.active {
background: var(--theme-accent);
border-color: var(--theme-accent);
color: var(--theme-body);
}
/* Years */
.year-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 0.625rem;
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
gap: 0.5rem;
}
.year-link {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.8125rem;
font-weight: 500;
padding: 0.625rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.1rem;
padding: 0.625rem 0.5rem;
background: #2A2A27;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 10px;
......@@ -317,6 +501,7 @@
background: rgba(201, 167, 81, 0.06);
transform: translateY(-1px);
}
:global(html:not(.dark)) .year-link { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .year-link:hover {
border-color: rgba(0,0,0,0.15);
color: var(--theme-titulo);
......@@ -324,11 +509,49 @@
transform: none;
}
/* Entity search */
.entity-search {
position: relative;
.year-num {
font-size: 0.8125rem;
font-weight: 600;
}
.year-fmt {
font-size: 0.6rem;
color: var(--theme-texto);
opacity: 0.4;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Download link */
.download-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
font-weight: 600;
color: var(--theme-accent);
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid var(--theme-accent);
border-radius: 10px;
transition: all 0.2s;
flex-shrink: 0;
}
.download-link:hover {
background: var(--theme-accent);
color: #1C1C1A;
}
:global(html:not(.dark)) .download-link {
border-color: rgba(0,0,0,0.12);
color: var(--theme-titulo);
}
:global(html:not(.dark)) .download-link:hover {
background: rgba(0,0,0,0.03);
}
/* Entity search */
.entity-search { position: relative; }
.search-input {
width: 100%;
max-width: 480px;
......@@ -338,7 +561,7 @@
background: #2A2A27;
color: var(--theme-titulo);
font-family: 'Qanelas', var(--font-sans);
font-size: 0.9375rem;
font-size: 0.875rem;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
}
......@@ -347,6 +570,7 @@
box-shadow: 0 0 0 3px rgba(201, 167, 81, 0.12);
}
.search-input::placeholder { color: var(--theme-texto); opacity: 0.4; }
:global(html:not(.dark)) .search-input { background: #ffffff; border-color: rgba(0,0,0,0.08); }
.search-status {
display: block;
......@@ -371,6 +595,7 @@
overflow-y: auto;
z-index: 10;
}
:global(html:not(.dark)) .search-dropdown { background: #ffffff; }
.search-item {
display: flex;
......@@ -388,13 +613,12 @@
.search-item:hover, .search-item.active { background: var(--theme-surface-hover, rgba(128,128,128,0.06)); }
.item-name {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.875rem;
font-weight: 500;
color: var(--theme-titulo);
}
.item-code {
font-family: 'JetBrains Mono', 'DM Mono', monospace;
font-family: 'JetBrains Mono', monospace;
font-size: 0.6875rem;
color: var(--theme-texto);
opacity: 0.4;
......@@ -406,32 +630,157 @@
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-top: 1.25rem;
margin-top: 1rem;
padding: 1rem 1.25rem;
background: #2A2A27;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
max-width: 480px;
}
:global(html:not(.dark)) .entity-result { background: #ffffff; border-color: rgba(0,0,0,0.08); }
.entity-name {
font-family: 'Qanelas', var(--font-sans);
font-size: 0.875rem;
font-weight: 600;
color: var(--theme-titulo);
}
/* Masivo downloads */
.masivo-downloads {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
max-width: 480px;
}
.download-card {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem 1.25rem;
background: #2A2A27;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
text-decoration: none;
color: var(--theme-titulo);
transition: all 0.2s;
}
.download-card:hover {
border-color: var(--theme-accent);
transform: translateY(-1px);
}
:global(html:not(.dark)) .download-card { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .download-card:hover { border-color: rgba(0,0,0,0.15); transform: none; }
.download-card-icon {
color: var(--theme-accent);
flex-shrink: 0;
}
.download-card-info {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.download-card-format {
font-size: 0.9375rem;
font-weight: 700;
}
.download-card-desc {
font-size: 0.6875rem;
color: var(--theme-texto);
opacity: 0.5;
}
/* Semanal */
.semanal-downloads {
position: relative;
}
.semanal-downloads.disabled { opacity: 0.5; pointer-events: none; }
.semanal-badge {
font-size: 0.6rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0.2rem 0.5rem;
border-radius: 4px;
background: var(--theme-borde);
color: var(--theme-texto);
margin-bottom: 0.75rem;
display: inline-block;
}
.download-card-disabled {
cursor: default;
pointer-events: none;
}
/* Dev URLs */
.dev-urls {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.dev-url-row {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.dev-url-label {
font-size: 0.75rem;
font-weight: 600;
color: var(--theme-titulo);
}
:global(html:not(.dark)) .toggle-group { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .year-link { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .search-input { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .entity-result { background: #ffffff; border-color: rgba(0,0,0,0.08); }
:global(html:not(.dark)) .search-dropdown { background: #ffffff; }
.dev-url-block {
display: flex;
align-items: center;
gap: 0;
background: #1a1a18;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 8px;
overflow: hidden;
}
:global(html:not(.dark)) .dev-url-block { background: #f5f5f5; border-color: rgba(0,0,0,0.08); }
.dev-url-code {
flex: 1;
padding: 0.75rem 1rem;
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: var(--theme-accent);
white-space: nowrap;
overflow-x: auto;
scrollbar-width: none;
}
.dev-url-code::-webkit-scrollbar { display: none; }
:global(html:not(.dark)) .dev-url-code { color: var(--theme-titulo); }
.dev-url-copy {
padding: 0.75rem 1rem;
background: transparent;
border: none;
border-left: 1px solid rgba(255,255,255,0.08);
color: var(--theme-texto);
cursor: pointer;
transition: all 0.15s;
flex-shrink: 0;
}
.dev-url-copy:hover { color: var(--theme-accent); }
:global(html:not(.dark)) .dev-url-copy { border-left-color: rgba(0,0,0,0.08); }
@media (max-width: 640px) {
.layout { padding: 1rem 1rem 2rem; }
.title { font-size: 2rem; }
.year-grid { grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); }
.title { font-size: 1.75rem; }
.year-grid { grid-template-columns: repeat(auto-fill, minmax(75px, 1fr)); }
.masivo-downloads { grid-template-columns: 1fr; }
.search-input, .search-dropdown, .entity-result { max-width: 100%; }
.toggle-btn { padding: 0.5rem 1rem; font-size: 0.8125rem; }
.subsection-header { flex-direction: column; align-items: flex-start; }
.entity-result { flex-direction: column; align-items: flex-start; }
}
</style>
......
......@@ -278,7 +278,7 @@
};
});
// Filtrar datos por transferencias y días
// Filtrar datos por transferencias
let timeserieFiltered = $derived(
timeserie.filter(d => d.transferencias === undefined || d.transferencias === incluirTransferencias)
);
......
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type