Segunda área que deja `src/content`. El texto ya no es un archivo: es la versión vigente de un documento, con la fecha en que entró en vigor. Eso no es burocracia —quien compró en marzo se acogió a las condiciones de marzo, y si hay una reclamación hay que poder demostrar qué decían—, y de paso la fecha que se enseña deja de ser un campo que alguien puede olvidarse de tocar. Un documento sin versión vigente no sale en el índice, así que se puede preparar un texto nuevo sin que aparezca en el pie a medias. El pie los recibe del layout en vez de importarlos: un componente no puede consultar. Y el mapa del sitio saca de la base tanto el taller como los legales, en un solo `Promise.all`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>master
parent
b22d7fbff2
commit
2506e90bde
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Los textos legales, leídos de la base.
|
||||
*
|
||||
* El texto no vive en `documento_legal` sino en sus versiones, y eso no es
|
||||
* burocracia: quien compró en marzo se acogió a las condiciones de marzo, y si
|
||||
* hay una reclamación hay que poder demostrar qué decían entonces. Una versión
|
||||
* publicada no se toca; cambiar el texto es publicar otra.
|
||||
*
|
||||
* De ahí sale también la fecha que se enseña: no es un campo escrito a mano que
|
||||
* alguien puede olvidarse de actualizar, es cuándo entró en vigor lo que se
|
||||
* está leyendo.
|
||||
*/
|
||||
|
||||
import { asc, eq } from 'drizzle-orm';
|
||||
import { db } from './db';
|
||||
import { documentoLegal, versionLegal } from './db/schema';
|
||||
import { componer } from './markdown';
|
||||
import { encabezadosDe, type Encabezado } from '$lib/content/anclas';
|
||||
|
||||
/** Una entrada del índice legal y del pie de página. */
|
||||
export interface EnElIndiceLegal {
|
||||
slug: string;
|
||||
titulo: string;
|
||||
resumen: string;
|
||||
orden: number;
|
||||
/** ISO `YYYY-MM-DD` de la versión en vigor. */
|
||||
actualizada: string;
|
||||
}
|
||||
|
||||
/** Un documento con el texto de su versión vigente, ya compuesto. */
|
||||
export interface DocumentoConTexto extends EnElIndiceLegal {
|
||||
html: string;
|
||||
apartados: Encabezado[];
|
||||
/** Qué cambió respecto de la anterior, si se anotó. */
|
||||
cambios: string | null;
|
||||
}
|
||||
|
||||
/** Solo `YYYY-MM-DD`: la hora no significa nada en una fecha de vigencia. */
|
||||
function enDia(fecha: Date): string {
|
||||
return fecha.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* El índice: todos los documentos con versión en vigor.
|
||||
*
|
||||
* Un documento sin versión vigente no sale. Es lo que permite preparar un texto
|
||||
* nuevo —crearlo y escribir su versión— sin que aparezca en el pie a medias.
|
||||
*/
|
||||
export async function indiceLegal(): Promise<EnElIndiceLegal[]> {
|
||||
const filas = await db
|
||||
.select({
|
||||
slug: documentoLegal.slug,
|
||||
titulo: documentoLegal.titulo,
|
||||
resumen: documentoLegal.resumen,
|
||||
orden: documentoLegal.orden,
|
||||
vigenteDesde: versionLegal.vigenteDesde
|
||||
})
|
||||
.from(documentoLegal)
|
||||
.innerJoin(versionLegal, eq(versionLegal.id, documentoLegal.versionVigenteId))
|
||||
.orderBy(asc(documentoLegal.orden), asc(documentoLegal.titulo));
|
||||
|
||||
return filas.map((f) => ({
|
||||
slug: f.slug,
|
||||
titulo: f.titulo,
|
||||
resumen: f.resumen,
|
||||
orden: f.orden,
|
||||
actualizada: enDia(f.vigenteDesde)
|
||||
}));
|
||||
}
|
||||
|
||||
export async function obtenerLegal(slug: string): Promise<DocumentoConTexto | null> {
|
||||
const [fila] = await db
|
||||
.select({
|
||||
slug: documentoLegal.slug,
|
||||
titulo: documentoLegal.titulo,
|
||||
resumen: documentoLegal.resumen,
|
||||
orden: documentoLegal.orden,
|
||||
cuerpo: versionLegal.cuerpo,
|
||||
cambios: versionLegal.resumenCambios,
|
||||
vigenteDesde: versionLegal.vigenteDesde
|
||||
})
|
||||
.from(documentoLegal)
|
||||
.innerJoin(versionLegal, eq(versionLegal.id, documentoLegal.versionVigenteId))
|
||||
.where(eq(documentoLegal.slug, slug))
|
||||
.limit(1);
|
||||
|
||||
if (!fila) return null;
|
||||
|
||||
return {
|
||||
slug: fila.slug,
|
||||
titulo: fila.titulo,
|
||||
resumen: fila.resumen,
|
||||
orden: fila.orden,
|
||||
actualizada: enDia(fila.vigenteDesde),
|
||||
html: componer(fila.cuerpo),
|
||||
/*
|
||||
* Los apartados salen del Markdown en bruto y con la misma función que
|
||||
* pone las anclas al componer: así el índice y sus destinos no pueden
|
||||
* separarse, que es un fallo que no da error, solo no lleva a ningún
|
||||
* sitio.
|
||||
*/
|
||||
apartados: encabezadosDe(fila.cuerpo),
|
||||
cambios: fila.cambios
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import { indiceLegal } from '$lib/server/legal';
|
||||
|
||||
/**
|
||||
* Pasa a ser `+page.server.ts` porque consulta la base: el cargador universal
|
||||
* corre también en el navegador, y allí no hay base a la que preguntar.
|
||||
*/
|
||||
export async function load() {
|
||||
return { legales: await indiceLegal() };
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
import { legales } from '$lib/content';
|
||||
|
||||
export function load() {
|
||||
return { legales };
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { indiceLegal, obtenerLegal } from '$lib/server/legal';
|
||||
|
||||
export async function load({ params }) {
|
||||
/*
|
||||
* Las dos consultas a la vez: el índice acompaña al texto en la columna de
|
||||
* al lado, así que hace falta siempre y encadenarlas sería un viaje de más.
|
||||
*/
|
||||
const [pagina, legales] = await Promise.all([obtenerLegal(params.slug), indiceLegal()]);
|
||||
if (!pagina) error(404, 'Esa página no existe.');
|
||||
|
||||
return { pagina, legales };
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { cuerpoLegal, indiceLegal, legales, obtenerLegal } from '$lib/content';
|
||||
|
||||
export function load({ params }) {
|
||||
const pagina = obtenerLegal(params.slug);
|
||||
if (!pagina) error(404, 'Esa página no existe.');
|
||||
|
||||
return {
|
||||
pagina,
|
||||
legales,
|
||||
Cuerpo: cuerpoLegal(params.slug),
|
||||
indice: indiceLegal(params.slug)
|
||||
};
|
||||
}
|
||||
Loading…
Reference in new issue