+page.svelte
5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<script>
let { data } = $props();
const objeto = data.objeto;
const padres = data.padres;
const hijos = data.hijos;
function parseDescripciones(descripcionesStr) {
if (!descripcionesStr) return [];
try {
const parsed = JSON.parse(descripcionesStr);
// Ordenar por año más reciente (extraer el máximo año de cada rango)
return parsed.sort((a, b) => {
const maxYearA = getMaxYear(a.rangos);
const maxYearB = getMaxYear(b.rangos);
return maxYearB - maxYearA; // Descendente, más reciente primero
});
} catch {
return [];
}
}
function getMaxYear(rangos) {
if (!rangos) return 0;
const years = rangos.match(/\d{4}/g);
if (!years) return 0;
return Math.max(...years.map(y => parseInt(y)));
}
function getNivelLabel(nivel) {
const labels = { grupo: 'Grupo', subgrupo: 'Subgrupo', partida: 'Partida', subpartida: 'Subpartida' };
return labels[nivel] || nivel;
}
const descripciones = parseDescripciones(objeto.descripciones);
</script>
<svelte:head>
<title>{objeto.desc_objeto} | Presupuesto Público</title>
</svelte:head>
<div class="min-h-screen bg-white">
<header class="border-b bg-slate-50">
<div class="max-w-4xl mx-auto px-6 py-4">
<nav class="text-sm text-slate-500 mb-2 flex flex-wrap items-center gap-1">
<a href="/" class="hover:text-slate-700">Inicio</a>
<span>/</span>
<a href="/clasificadores/objeto-gasto" class="hover:text-slate-700">Objeto del Gasto</a>
{#each padres as padre}
<span>/</span>
<a href="/objeto/{padre.objeto}" class="hover:text-slate-700">{padre.desc_objeto}</a>
{/each}
<span>/</span>
<span class="text-slate-900">{objeto.desc_objeto}</span>
</nav>
</div>
</header>
<main class="max-w-4xl mx-auto px-6 py-8">
<!-- Encabezado -->
<div class="mb-8">
<p class="text-xs text-slate-500 uppercase tracking-wide mb-1">{getNivelLabel(objeto.nivel)}</p>
<div class="flex items-baseline gap-4">
<span class="font-mono text-2xl text-slate-400">{objeto.objeto}</span>
<h1 class="text-2xl font-medium text-slate-900">{objeto.desc_objeto}</h1>
</div>
</div>
<!-- Descripciones -->
<section class="mb-10">
<h2 class="text-sm font-medium text-slate-700 mb-4">
{#if descripciones.length > 1}
Descripciones ({descripciones.length} variaciones)
{:else}
Descripción
{/if}
</h2>
<div class="space-y-4">
{#each descripciones as desc, i}
<div class="border-l-2 {i === 0 ? 'border-blue-500 bg-blue-50/50' : 'border-slate-200 bg-slate-50/50'} pl-4 py-3 rounded-r">
<p class="text-xs text-slate-500 mb-2">
{#if i === 0 && descripciones.length > 1}
<span class="text-blue-600 font-medium">Vigente</span>
<span class="mx-1">·</span>
{/if}
{desc.rangos}
</p>
<p class="text-slate-700 leading-relaxed">{desc.descripcion}</p>
</div>
{/each}
</div>
</section>
<!-- Jerarquía -->
{#if padres.length > 0}
<section class="mb-10">
<h2 class="text-sm font-medium text-slate-700 mb-4">Jerarquía</h2>
<div class="space-y-2">
{#each padres as padre, i}
<div class="flex items-center gap-2" style="margin-left: {i * 1.5}rem">
<span class="text-slate-300">└</span>
<a
href="/objeto/{padre.objeto}"
class="text-sm text-slate-600 hover:text-blue-600"
>
<span class="font-mono text-xs text-slate-400">{padre.objeto}</span>
{padre.desc_objeto}
</a>
</div>
{/each}
<div class="flex items-center gap-2" style="margin-left: {padres.length * 1.5}rem">
<span class="text-blue-500">└</span>
<span class="text-sm font-medium text-blue-600">
<span class="font-mono text-xs">{objeto.objeto}</span>
{objeto.desc_objeto}
</span>
</div>
</div>
</section>
{/if}
<!-- Hijos -->
{#if hijos.length > 0}
<section class="mb-10">
<h2 class="text-sm font-medium text-slate-700 mb-4">
{#if objeto.nivel === 'grupo'}
Subgrupos ({hijos.length})
{:else if objeto.nivel === 'subgrupo'}
Partidas ({hijos.length})
{:else if objeto.nivel === 'partida'}
Subpartidas ({hijos.length})
{/if}
</h2>
<div class="border rounded-lg divide-y">
{#each hijos as hijo}
{@const hijoDescs = parseDescripciones(hijo.descripciones)}
<a
href="/objeto/{hijo.objeto}"
class="block px-4 py-3 hover:bg-slate-50 transition-colors"
>
<div class="flex items-baseline gap-3">
<span class="font-mono text-sm text-slate-400">{hijo.objeto}</span>
<span class="text-slate-900">{hijo.desc_objeto}</span>
{#if hijo.n_variaciones > 1}
<span class="text-xs text-orange-500">({hijo.n_variaciones} var.)</span>
{/if}
</div>
{#if hijoDescs.length > 0}
<p class="text-sm text-slate-500 mt-1 ml-16">{hijoDescs[0].descripcion}</p>
{/if}
</a>
{/each}
</div>
</section>
{/if}
<!-- Volver -->
<div class="pt-6 border-t">
<a
href="/clasificadores/objeto-gasto"
class="text-sm text-blue-600 hover:underline"
>
Volver al clasificador
</a>
</div>
</main>
</div>