import { useState, useMemo, isValidElement, useEffect } from "react";
import {
Box,
Typography,
Button,
Grid,
Card,
CardContent,
Modal,
Fade,
Backdrop,
CircularProgress,
} from "@mui/material";
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation, Pagination, Autoplay } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import PropTypes from "prop-types";
import { Helmet } from "react-helmet-async";
import {
extractFirstImageSrc,
removeImageTags,
stripHtml,
} from "../utils/htmlHelpers";
const DEFAULT_CARD_IMAGE = "https://picsum.photos/id/43/800/400.webp";
const ServicePageTemplate = ({
title,
subtitle,
interestTitle,
description,
desirTitle,
features,
image,
heroBackground,
ctaText,
ctaLink,
carouselItems,
seo, // ✅ Ajout de l'objet SEO
}) => {
const [openModal, setOpenModal] = useState(false);
const [modalContent, setModalContent] = useState({});
const [loadingImage, setLoadingImage] = useState(true);
const [heroLoaded, setHeroLoaded] = useState(false);
const renderRichText = (value) => {
if (value === null || value === undefined) return null;
if (isValidElement(value)) return value;
if (typeof value === "string") {
return ;
}
return null;
};
const descriptionIsElement = isValidElement(description);
const rawDescriptionHtml =
!descriptionIsElement && typeof description === "string" ? description : "";
const descriptionMedia = useMemo(() => {
if (!rawDescriptionHtml) {
return { html: rawDescriptionHtml, media: null };
}
if (typeof window === "undefined" || !window.DOMParser) {
return { html: rawDescriptionHtml, media: null };
}
const parser = new window.DOMParser();
const doc = parser.parseFromString(rawDescriptionHtml, "text/html");
const firstImage = doc.body.querySelector("img");
if (firstImage) {
const media = {
src: firstImage.getAttribute("src"),
alt: firstImage.getAttribute("alt") || "",
};
firstImage.remove();
return { html: doc.body.innerHTML, media };
}
return { html: rawDescriptionHtml, media: null };
}, [rawDescriptionHtml]);
useEffect(() => {
if (!image) {
setHeroLoaded(true);
return;
}
setHeroLoaded(false);
const img = new Image();
img.src = image;
img.onload = () => setHeroLoaded(true);
img.onerror = () => setHeroLoaded(true);
return () => {
img.onload = null;
img.onerror = null;
};
}, [image]);
const defaultGradient = "linear-gradient(135deg, #0a1930 0%, #020710 85%)";
const activeGradient = heroBackground || defaultGradient;
const heroBackgroundStyle =
heroLoaded && image
? `url(${image}), ${activeGradient}`
: activeGradient;
const buildModalPayload = (feature) => {
const rawHtml = feature.modalText || feature.description || "";
const sanitizedHtml = removeImageTags(rawHtml);
const extractedImage = extractFirstImageSrc(rawHtml);
const dynamicImage =
extractedImage || feature.modalImage || DEFAULT_CARD_IMAGE;
return {
title: feature.title,
text: sanitizedHtml || "Texte non disponible.",
image: dynamicImage,
};
};
const getPreviewText = (feature) => {
const rawPreview = feature.description || feature.modalText || "";
const textOnly = stripHtml(removeImageTags(rawPreview));
if (!textOnly) return "Contenu indisponible.";
return textOnly.length > 140 ? `${textOnly.substring(0, 140)}…` : textOnly;
};
const handleCardClick = (feature) => {
setModalContent(buildModalPayload(feature));
setLoadingImage(true);
setOpenModal(true);
};
const handleCloseModal = () => {
setOpenModal(false);
};
return (
{/* ✅ SEO META TAGS */}
{seo?.metaTitle || title}
{/* Section Hero */}
{title}
{renderRichText(subtitle) ?? subtitle ?? ""}
{/* Section numero 1 */}
{interestTitle}
{descriptionIsElement ? (
{renderRichText(description)}
) : (
{descriptionMedia.media?.src && (
)}
)}
{/* Section numero 2 */}
{desirTitle}
{features.map((feature, index) => (
handleCardClick(feature)}
sx={{
cursor: "pointer",
textAlign: "center",
padding: "20px",
backgroundColor: "#f9f9f9",
transition: "0.3s",
"&:hover": { transform: "scale(1.05)", boxShadow: 5 },
}}
>
{feature.title}
{getPreviewText(feature)}
))}
{/* Modal */}
{loadingImage && }
setLoadingImage(false)}
/>
{modalContent.title}
{/* Carousel Section */}
{carouselItems && carouselItems.length > 0 && (
Découvrez nos réalisations
{carouselItems.map((item, index) => (
{item.title}
{item.description}
))}
)}
);
};
// ✅ Définition des types des props avec PropTypes
ServicePageTemplate.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string.isRequired,
interestTitle: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
desirTitle: PropTypes.string.isRequired,
features: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
modalText: PropTypes.string,
modalImage: PropTypes.string,
})
).isRequired,
image: PropTypes.string,
ctaText: PropTypes.string.isRequired,
ctaLink: PropTypes.string.isRequired,
carouselItems: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
image: PropTypes.string.isRequired,
})
),
heroBackground: PropTypes.string,
seo: PropTypes.object, // ✅ Ajout du SEO en option
};
// ✅ Valeurs par défaut
ServicePageTemplate.defaultProps = {
carouselItems: [],
heroBackground: "linear-gradient(135deg, #0a1930 0%, #020710 85%)",
seo: {}, // ✅ SEO par défaut vide
};
export default ServicePageTemplate;