modifglossaire

This commit is contained in:
Ladebeze66 2025-01-30 15:41:45 +00:00
parent 395fed7f8c
commit 2b83679a02
2 changed files with 73 additions and 41 deletions

View File

@ -1,6 +1,10 @@
import { fetchDataCompetences } from "../utils/fetchDataCompetences"; // ✅ Importation du bon fetch "use client";
import { fetchDataCompetences, fetchDataGlossaire } from "../utils/fetchDataCompetences";
import CarouselCompetences from "./CarouselCompetences"; import CarouselCompetences from "./CarouselCompetences";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { useState, useEffect } from "react";
import ModalGlossaire from "./ModalGlossaire"; // ✅ Import de la modale
interface ContentSectionProps { interface ContentSectionProps {
collection: string; collection: string;
@ -9,32 +13,82 @@ interface ContentSectionProps {
contentClass?: string; contentClass?: string;
} }
// ✅ Définition du type Glossaire
interface GlossaireItem {
mot_clef: string;
slug: string;
variantes: string[];
description: string;
images?: any[];
}
export default async function ContentSectionCompetences({ collection, slug, titleClass, contentClass }: ContentSectionProps) { export default async function ContentSectionCompetences({ collection, slug, titleClass, contentClass }: ContentSectionProps) {
const data = await fetchDataCompetences(collection, slug); // ✅ Utilisation du fetch spécifique const data = await fetchDataCompetences(collection, slug);
const glossaireData: GlossaireItem[] = await fetchDataGlossaire();
const [selectedMot, setSelectedMot] = useState<GlossaireItem | null>(null);
if (!data) { if (!data) {
return <div className="text-red-500 text-center"> Compétence introuvable.</div>; return <div className="text-red-500 text-center"> Compétence introuvable.</div>;
} }
const { name, content, picture } = data; // ✅ Assure-toi que `content` est bien récupéré au lieu de `Resum` const { name, content, picture } = data;
// 🔹 Transformation des images pour le carrousel des compétences
const images = picture?.map((img: any) => ({ const images = picture?.map((img: any) => ({
url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`, url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`,
alt: img.name || "Image de compétence", alt: img.name || "Image de compétence",
})) || []; })) || [];
// 🔥 Transformation du texte riche avec des <span> cliquables
function transformMarkdownWithKeywords(text: string) {
if (!glossaireData || glossaireData.length === 0) return text;
let modifiedText = text;
glossaireData.forEach(({ mot_clef, variantes }) => {
const regexVariants = (variantes || []).map((v: string) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
const regex = new RegExp(`\\b(${mot_clef}|${regexVariants})\\b`, "gi");
modifiedText = modifiedText.replace(regex, (match) => {
return `<span class="keyword" data-mot="${mot_clef}">${match}</span>`; // 🔥 Span cliquable
});
});
return modifiedText;
}
const contentWithLinks = transformMarkdownWithKeywords(content);
// ✅ Gestion des clics sur les mots-clés
useEffect(() => {
function handleKeywordClick(event: any) {
const target = event.target as HTMLElement;
if (target.classList.contains("keyword")) {
const mot = target.getAttribute("data-mot");
if (mot) {
const glossaireMot = glossaireData.find((g) => g.mot_clef === mot);
setSelectedMot(glossaireMot || null);
}
}
}
document.addEventListener("click", handleKeywordClick);
return () => document.removeEventListener("click", handleKeywordClick);
}, [glossaireData]);
return ( return (
<div className="max-w-3xl mx-auto p-6"> <div className="max-w-3xl mx-auto p-6">
<h1 className={titleClass || "text-3xl mb-6 font-bold text-gray-700"}>{name}</h1> <h1 className={titleClass || "text-3xl mb-6 font-bold text-gray-700"}>{name}</h1>
{/* Carrousel spécifique aux compétences */}
<CarouselCompetences images={images} className="w-full h-64" /> <CarouselCompetences images={images} className="w-full h-64" />
{/* Contenu en Markdown */} {/* 🔥 Affichage du texte riche avec mots-clés cliquables */}
<div className={contentClass || "mt-6 text-lg text-black-700"}> <div className={contentClass || "mt-6 text-lg text-black-700"}>
<ReactMarkdown>{content}</ReactMarkdown> {/* ✅ Utilisation de `content` au lieu de `Resum` */} <ReactMarkdown>{contentWithLinks}</ReactMarkdown>
</div> </div>
{/* 🚀 Modale pour afficher les infos des mots-clés */}
{selectedMot && <ModalGlossaire mot={selectedMot} onClose={() => setSelectedMot(null)} />}
</div> </div>
); );
} }

View File

@ -1,55 +1,33 @@
import { useState, useEffect } from "react";
import CarouselCompetences from "./CarouselCompetences"; import CarouselCompetences from "./CarouselCompetences";
interface ModalGlossaireProps { interface ModalGlossaireProps {
glossaire: any[]; // Liste complète des mots-clés mot: {
} mot_clef: string;
description: string;
export default function ModalGlossaire({ glossaire }: ModalGlossaireProps) { images?: any[];
const [selectedMot, setSelectedMot] = useState<any | null>(null);
useEffect(() => {
// 🔥 Détecter si un mot-clé est dans l'URL (#glossaire-mot)
function checkHash() {
const hash = window.location.hash.replace("#glossaire-", "");
const mot = glossaire.find((g) => g.mot_clef.toLowerCase() === hash.toLowerCase());
setSelectedMot(mot || null);
}
window.addEventListener("hashchange", checkHash);
checkHash(); // Vérifier au chargement
return () => {
window.removeEventListener("hashchange", checkHash);
}; };
}, [glossaire]); onClose: () => void;
}
if (!selectedMot) return null;
export default function ModalGlossaire({ mot, onClose }: ModalGlossaireProps) {
return ( return (
<div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50"> <div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
<div className="bg-white p-6 rounded-lg shadow-lg max-w-2xl w-full relative"> <div className="bg-white p-6 rounded-lg shadow-lg max-w-2xl w-full relative">
{/* Bouton de fermeture */} {/* Bouton de fermeture */}
<button <button className="absolute top-3 right-3 text-gray-700 text-2xl" onClick={onClose}>
className="absolute top-3 right-3 text-gray-700 text-2xl"
onClick={() => {
setSelectedMot(null);
window.location.hash = ""; // 🔄 Supprime le hash pour fermer la modale
}}
>
</button> </button>
{/* Titre */} {/* Titre */}
<h2 className="text-2xl font-bold mb-4">{selectedMot.mot_clef}</h2> <h2 className="text-2xl font-bold mb-4">{mot.mot_clef}</h2>
{/* Description */} {/* Description */}
<p className="text-gray-700 mb-4">{selectedMot.description}</p> <p className="text-gray-700 mb-4">{mot.description}</p>
{/* Carrousel d'images si disponible */} {/* Carrousel d'images si disponible */}
{selectedMot.images && selectedMot.images.length > 0 && ( {mot.images && mot.images.length > 0 && (
<CarouselCompetences <CarouselCompetences
images={selectedMot.images.map((img: any) => ({ images={mot.images.map((img: any) => ({
url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`, url: `http://localhost:1337${img?.formats?.large?.url || img?.url}`,
alt: img.name || "Illustration", alt: img.name || "Illustration",
}))} }))}