"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { getApiUrl } from "../utils/getApiUrl"; import VignetteCarousel from "../components/VignetteCarousel"; import "../assets/main.css"; import "../globals.css"; /** * Liste des projets — refonte "Digital Atelier" (étape 6). * * Règle DESIGN.md §6 "No-Grid-Lock" : on bannit la grille 3 colonnes symétrique. * On utilise une grille asymétrique 2/3 + 1/3 alternée par paires, qui crée un * rythme éditorial plutôt qu'un catalogue. Arbitrage acté dans REFONTE-VISUELLE.md §2 : * le carousel reste réservé aux fiches détail (étape 7) — la liste n'affiche que * la première image, ce qui allège le rendu et clarifie la hiérarchie. * * Pattern de spans (modulo 4 sur desktop 6 colonnes) : * idx 0 → col-span-4 (vedette) * idx 1 → col-span-2 * idx 2 → col-span-2 * idx 3 → col-span-4 * → répète l'alternance pour éviter la monotonie sans dépendre du nombre d'items. */ const spanPattern = ["md:col-span-4", "md:col-span-2", "md:col-span-2", "md:col-span-4"]; export default function Page() { const [projects, setProjects] = useState([]); const [isLoading, setIsLoading] = useState(true); const apiUrl = getApiUrl(); useEffect(() => { async function fetchProjects() { try { const response = await fetch( `${apiUrl}/api/projects?populate=picture&sort=order:asc` ); if (!response.ok) { throw new Error(`Erreur de récupération des projets : ${response.statusText}`); } const data = await response.json(); const sortedProjects = (data.data ?? []).sort( (a, b) => (a.order || 999) - (b.order || 999) ); setProjects(sortedProjects); } catch (error) { console.error("❌ Erreur lors de la récupération des projets :", error); } finally { setIsLoading(false); } } fetchProjects(); }, [apiUrl]); return (
Une sélection de réalisations pédagogiques, personnelles et professionnelles — cliquez sur une carte pour en découvrir la genèse, les choix techniques et les visuels.
Aucun projet à afficher pour le moment.
{project.description}
)} Découvrir