Files
portfolio-generator-app/composables/useFormat.ts
Valentin Le Moign 1c1604ea2f Pages équipe + conclusion, colonnes de page libre, réglages couverture
- Page « équipe » : roster statique du collectif (composables/useTeam),
  portraits servis depuis public/portraits, grille ≤ 3 colonnes équilibrée
  (portrait/nom/rôle/présentation), sélection + surcharges par membre
  (repliables, réordonnables, réinitialisables) dans le composeur
- Page « conclusion » : « Merci ! » centré + rappel des contacts
- Page libre : 1 à 3 colonnes texte (markdown) ou images avec sous-titres,
  réordonnables ; migration des champs body/media legacy vers columns
- Couverture : texte libre sous l'intro, adresse en bas à gauche (3 lignes),
  case « afficher les années » au niveau du document
- Sommaire : items centrés (self-center), filet en points dégradés stable
- Retrait de la dépendance morte pagedjs
- Docs à jour (CLAUDE.md, README) ; spec marquée comme archive réalisée
2026-07-13 16:00:59 +02:00

56 lines
2.3 KiB
TypeScript

import type { Project } from '~~/types'
const dateFormatter = new Intl.DateTimeFormat('fr-FR', { month: 'long', year: 'numeric' })
export function formatProjectDate(iso?: string): string {
if (!iso) return ''
const d = new Date(iso)
return Number.isNaN(d.getTime()) ? '' : dateFormatter.format(d)
}
/** Mois + année capitalisés (ex. « Juillet 2026 ») ; sans argument = aujourd'hui.
* Sert de date par défaut de l'en-tête de couverture. */
export function formatMonthYear(iso?: string): string {
const d = iso ? new Date(iso) : new Date()
if (Number.isNaN(d.getTime())) return ''
const s = dateFormatter.format(d)
return s.charAt(0).toUpperCase() + s.slice(1)
}
/** Retire le paramètre `target=_blank` que Grav colle à chaque URL — dans une
* URL simple comme dans une chaîne HTML (href des liens du corps de texte). */
export function stripTargetParam(s?: string): string {
if (!s) return ''
return s
.replace(/\?target=_blank&/g, '?') // seul paramètre en tête, suivi d'autres
.replace(/&target=_blank/g, '') // paramètre en fin de chaîne de requête
.replace(/\?target=_blank\b/g, '') // seul paramètre
}
export const CATEGORY_LABELS: Record<string, string> = {
culturelle: 'Culturelle',
publique: 'Publique',
sociale: 'Sociale',
}
// Valeurs par défaut du header de la vue publique (surchargées par la couverture).
export const DEFAULT_SITE_URL = 'https://figureslibres.cc'
export const DEFAULT_CONTACT_EMAIL = 'bonjour@figureslibres.io'
export const DEFAULT_ADDRESS = 'Figures Libres, 55 avenue Jean Jaurès, 94110 Arcueil'
/**
* URL de miniature basse résolution d'un média de projet.
* `base` permet de router vers la route publique (/api/public/<id>/media) en
* vue publique au lieu de la route authentifiée par défaut.
*/
export function mediaUrl(projectId: string, filename: string, width?: number, base = '/api/media'): string {
const url = `${base}/${projectId.split('/').map(encodeURIComponent).join('/')}/${encodeURIComponent(filename)}`
return width ? `${url}?w=${width}` : url
}
/** Vignette de couverture d'un projet (aura.image ou 1re image). */
export function projectThumb(project: Project, width = 480): string | null {
const cover = project.coverImage ?? project.media.find(m => m.type === 'image')?.filename
return cover ? mediaUrl(project.id, cover, width) : null
}