- Champ Présentation prérempli avec le texte Grav, suivi modifié/synchro (textOverride undefined = suit Grav, sinon figé ; bouton Réinitialiser) - Fix génération PDF : savePortfolio écrit en atomique (temp+rename) pour éviter la corruption de lecture par l'autosave concurrent du rendu print - Toast d'échec de génération explicite (message serveur) - Refonte architecture visuelle, fond unifié white/tint/full, dispositions couverture bandeau/colonne, instantané public complet, index 3 lignes
30 lines
1.2 KiB
TypeScript
30 lines
1.2 KiB
TypeScript
// Données de la vue publique : le portfolio partagé + ses projets figés.
|
|
// Public (hors mot de passe), mais uniquement si publicShare est actif et
|
|
// qu'un instantané figé existe. On ne sert jamais le contenu live.
|
|
import { getPortfolio } from '../../lib/portfolio-store'
|
|
import type { Portfolio, Project } from '~~/types'
|
|
|
|
export default defineEventHandler(async (event): Promise<{ portfolio: Portfolio; projects: Record<string, Project> }> => {
|
|
const id = getRouterParam(event, 'id') ?? ''
|
|
if (!/^[\w-]+$/.test(id)) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Identifiant invalide' })
|
|
}
|
|
|
|
const portfolio = await getPortfolio(id)
|
|
if (!portfolio?.publicShare || !portfolio.frozenContent) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Portfolio introuvable' })
|
|
}
|
|
|
|
// Vrai instantané : on rend la composition figée (pages + mise en forme), pas
|
|
// la composition live. Repli sur le live pour les instantanés antérieurs.
|
|
const frozen = portfolio.frozenContent
|
|
const snapshot: Portfolio = {
|
|
...portfolio,
|
|
name: frozen.name ?? portfolio.name,
|
|
cover: frozen.cover ?? portfolio.cover,
|
|
pages: frozen.pages ?? portfolio.pages,
|
|
}
|
|
|
|
return { portfolio: snapshot, projects: frozen.projects }
|
|
})
|