"use client"; import { useParams } from "next/navigation"; import { useEffect, useState } from "react"; import Link from "next/link"; import { getApiUrl } from "../../utils/getApiUrl"; import VignetteCarousel from "../../components/VignetteCarousel"; import ContentSectionCompetencesContainer from "../../components/ContentSectionCompetencesContainer"; /** * Page détail d'une compétence — double rendu selon le contenu Strapi. * * Cas 1 — la compétence est liée à une ou plusieurs `realisation-ia` : * on affiche une **grille de vignettes** alignée sur le pattern éditorial * de `app/portfolio/page.jsx` (grille asymétrique 2/3 + 1/3 alternée). Chaque * vignette est cliquable (lien externe si le champ `link` de la réalisation * est renseigné, sinon lien interne vers la future page détail * `/competences/[slug]/[realisation]` — pour l'instant 404 tant que cette * route n'est pas ajoutée, ce qui est attendu pour l'étape de test du listing). * * Cas 2 — aucune réalisation liée : * on conserve le rendu historique `ContentSectionCompetencesContainer` qui * affiche le `content` richtext de la compétence. Les compétences Web / 3D * restent donc strictement inchangées tant qu'on ne leur associe pas de * réalisation côté Strapi. * * Endpoint Strapi utilisé : `/api/realisation-ias` (pluralName par défaut * Strapi 5 pour un singularName `realisation-ia`). Filtre sur le slug de la * compétence parente via la relation `competences` (many-to-many d'après * le content-type créé côté admin). */ const spanPattern = ["md:col-span-4", "md:col-span-2", "md:col-span-2", "md:col-span-4"]; type Realisation = { id: number; name: string; slug?: string; description?: string; link?: string; order?: number; picture?: Array<{ url?: string; name?: string }>; }; type Competence = { id: number; name?: string; slug?: string; content?: string; }; export default function CompetencePage() { const params = useParams(); const slug = typeof params?.slug === "string" ? params.slug : null; const [competence, setCompetence] = useState(null); const [realisations, setRealisations] = useState(null); const [isLoading, setIsLoading] = useState(true); const apiUrl = getApiUrl(); useEffect(() => { if (!slug) return; let cancelled = false; async function fetchData() { setIsLoading(true); try { // 1. Compétence parente (pour le titre de la page vignettes). const resCompet = await fetch( `${apiUrl}/api/competences?filters[slug][$eq]=${encodeURIComponent( slug! )}&populate=*` ); const dataCompet = resCompet.ok ? await resCompet.json() : null; const fetchedCompetence: Competence | null = dataCompet?.data?.[0] ?? null; // 2. Réalisations IA liées à cette compétence. // Si l'endpoint n'existe pas encore côté Strapi (404), on bascule // silencieusement sur le rendu historique au lieu de crasher. let fetchedRealisations: Realisation[] = []; const resReal = await fetch( `${apiUrl}/api/realisation-ias?filters[competences][slug][$eq]=${encodeURIComponent( slug! )}&populate=picture&sort=order:asc` ); if (resReal.ok) { const dataReal = await resReal.json(); fetchedRealisations = (dataReal?.data ?? []).sort( (a: Realisation, b: Realisation) => (a.order ?? 999) - (b.order ?? 999) ); } else if (resReal.status !== 404) { // 404 = content-type absent, cas légitime → on log seulement les // vraies erreurs HTTP (500, 403, etc.). console.warn( `⚠️ [competences/${slug}] realisation-ias HTTP ${resReal.status}` ); } if (!cancelled) { setCompetence(fetchedCompetence); setRealisations(fetchedRealisations); } } catch (err) { if (!cancelled) { console.error("❌ [competences/[slug]] Erreur fetch :", err); setRealisations([]); } } finally { if (!cancelled) setIsLoading(false); } } fetchData(); return () => { cancelled = true; }; }, [apiUrl, slug]); if (!slug) { return (

⏳ Chargement...

); } // Squelette commun pendant le premier fetch (avant de savoir s'il y a des vignettes ou pas). if (isLoading) { return (
{Array.from({ length: 4 }).map((_, idx) => (
))}
); } // Pas de réalisations associées → rendu historique (toutes les compétences hors IA). if (!realisations || realisations.length === 0) { return ( ); } // Rendu vignettes. return (
Retour aux compétences Compétence · Réalisations

{competence?.name ?? "Compétence"}

Une sélection de réalisations qui illustrent cette compétence en contexte — ouvrez une vignette pour en voir le détail.

{realisations.map((realisation, idx) => { const pictures = realisation.picture ?? []; const images = pictures.map((img) => ({ url: img?.url ? `${apiUrl}${img.url}` : "/placeholder.jpg", alt: img?.name || `Visuel de la réalisation ${realisation.name}`, })); const firstImage = images[0]; // Comportement voulu : la vignette renvoie TOUJOURS vers la fiche // détail interne (`/competences/[slug]/[realisation]`). Le lien // externe `realisation.link` est exposé en tant que CTA jewel en bas // de la fiche détail par `ContentSection`, pas en court-circuit // depuis la vignette. Cohérent avec le comportement des fiches // `project` du portfolio. const href = realisation.slug ? `/competences/${slug}/${realisation.slug}` : "#"; return (
{images.length > 1 ? ( ) : firstImage ? ( {firstImage.alt} ) : (
)}
Réalisation

{realisation.name}

{realisation.description && (

{realisation.description}

)} Découvrir
); })}
); }