mirror of
https://github.com/Ladebeze66/devsite.git
synced 2026-02-04 03:30:21 +01:00
modifglossaire
This commit is contained in:
parent
395fed7f8c
commit
2b83679a02
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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",
|
||||||
}))}
|
}))}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user