maj template page
This commit is contained in:
@@ -1,40 +1,37 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Modal,
|
||||
Fade,
|
||||
Backdrop,
|
||||
} from "@mui/material";
|
||||
import { useState } 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";
|
||||
|
||||
const ServicePageTemplate = ({
|
||||
title,
|
||||
subtitle,
|
||||
interestTitle,
|
||||
description,
|
||||
desirTitle,
|
||||
features,
|
||||
image,
|
||||
ctaText,
|
||||
ctaLink,
|
||||
carouselItems,
|
||||
seo, // ✅ Ajout de l'objet SEO
|
||||
}) => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [modalContent, setModalContent] = useState({});
|
||||
const [loadingImage, setLoadingImage] = useState(true);
|
||||
|
||||
const handleCardClick = (feature) => {
|
||||
setModalContent({
|
||||
title: feature.title,
|
||||
text: feature.modalText || "Texte non disponible.",
|
||||
image: feature.modalImage || "https://picsum.photos/id/43/800/400.webp", // Image par défaut
|
||||
image: feature.modalImage || "https://picsum.photos/id/43/800/400.webp",
|
||||
});
|
||||
setLoadingImage(true);
|
||||
setOpenModal(true);
|
||||
};
|
||||
|
||||
@@ -43,14 +40,23 @@ const ServicePageTemplate = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "#f5f5f5",
|
||||
color: "#333",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
}}
|
||||
>
|
||||
{/* Section Attention */}
|
||||
<Box sx={{ backgroundColor: "#f5f5f5", color: "#333", fontFamily: "Arial, sans-serif" }}>
|
||||
{/* ✅ SEO META TAGS */}
|
||||
<Helmet>
|
||||
<title>{seo?.metaTitle || title}</title>
|
||||
<meta name="description" content={seo?.metaDescription || ""} />
|
||||
<meta name="keywords" content={seo?.keywords || ""} />
|
||||
<meta property="og:title" content={seo?.metaTitle || title} />
|
||||
<meta property="og:description" content={seo?.metaDescription || ""} />
|
||||
<meta property="og:image" content={seo?.ogImage || image} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:title" content={seo?.metaTitle || title} />
|
||||
<meta name="twitter:description" content={seo?.metaDescription || ""} />
|
||||
<meta name="twitter:image" content={seo?.ogImage || image} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</Helmet>
|
||||
|
||||
{/* Section Hero */}
|
||||
<Box
|
||||
sx={{
|
||||
backgroundImage: `url(${image})`,
|
||||
@@ -66,116 +72,56 @@ const ServicePageTemplate = ({
|
||||
}}
|
||||
>
|
||||
<Box sx={{ maxWidth: "800px" }}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
mb: 2,
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '4rem' }, // Responsive
|
||||
}}
|
||||
>
|
||||
<Typography variant="h1" sx={{ fontWeight: "bold", mb: 2, fontSize: { xs: "2rem", md: "3rem", lg: "4rem" } }}>
|
||||
{title}
|
||||
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
mb: 4
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" sx={{ mb: 4 }}>
|
||||
{subtitle}
|
||||
|
||||
</Typography>
|
||||
<Button
|
||||
href={ctaLink}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: " #0e467f",
|
||||
"&:hover": { backgroundColor: "#00bcd4" },
|
||||
}}
|
||||
sx={{ textTransform: "none", fontWeight: "bold", backgroundColor: "#0e467f", "&:hover": { backgroundColor: "#00bcd4" } }}
|
||||
>
|
||||
{ctaText}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Section Interest */}
|
||||
{/* Section numero 1 */}
|
||||
<Box sx={{ padding: "40px 20px", textAlign: "center" }}>
|
||||
<Typography variant="h2"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
mb: 2,
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
|
||||
}}
|
||||
>
|
||||
Pourquoi choisir notre formation ?
|
||||
<Typography variant="h2" sx={{ fontWeight: "bold", mb: 2, fontSize: { xs: "2rem", md: "2rem", lg: "2rem" }, whiteSpace: "pre-line" }}>
|
||||
{interestTitle}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ maxWidth: "800px", margin: "0 auto", mb: 4 }}
|
||||
>
|
||||
<Typography variant="body1" sx={{ maxWidth: "1000px", margin: "0 auto", mb: 4, whiteSpace: "pre-line" }}>
|
||||
{description}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Section Desire */}
|
||||
{/* Section numero 2 */}
|
||||
<Box sx={{ backgroundColor: "#ffffff", padding: "40px 20px" }}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
mb: 4,
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
|
||||
|
||||
}}
|
||||
>
|
||||
Ce que nous offrons
|
||||
<Typography variant="h2" sx={{ fontWeight: "bold", textAlign: "center", mb: 4, fontSize: { xs: "2rem", md: "3rem", lg: "2rem" } }}>
|
||||
{desirTitle}
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={4} justifyContent="center">
|
||||
{features.map((feature, index) => (
|
||||
<Grid item xs={12} md={4} key={index}>
|
||||
<Card
|
||||
onClick={() => handleCardClick(feature)}
|
||||
sx={{
|
||||
boxShadow: 2,
|
||||
borderRadius: 2,
|
||||
cursor: "pointer",
|
||||
textAlign: "center",
|
||||
padding: "20px",
|
||||
backgroundColor: "#f9f9f9",
|
||||
cursor: "pointer",
|
||||
transition: "transform 0.3s ease, box-shadow 0.3s ease",
|
||||
"&:hover": {
|
||||
transform: {
|
||||
xs: "none", // Pas d'hover sur mobile
|
||||
md: "scale(1.05)", // Hover sur desktop uniquement
|
||||
},
|
||||
boxShadow: {
|
||||
xs: 2, // Pas d'effet de hover sur mobile
|
||||
md: 5, // Augmentation de l'ombre sur desktop
|
||||
},
|
||||
},
|
||||
"&:active": {
|
||||
transform: "scale(0.98)", // Effet de clic pour le tactile
|
||||
boxShadow: 3,
|
||||
},
|
||||
transition: "0.3s",
|
||||
"&:hover": { transform: "scale(1.05)", boxShadow: 5 },
|
||||
}}
|
||||
onClick={() => handleCardClick(feature)}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
mb: 2,
|
||||
fontSize: 26,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h3" sx={{ fontWeight: "bold", mb: 2, fontSize: 26 }}>
|
||||
{feature.title}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: "#555" }}>
|
||||
@@ -189,177 +135,46 @@ const ServicePageTemplate = ({
|
||||
</Box>
|
||||
|
||||
{/* Modal */}
|
||||
<Modal
|
||||
open={openModal}
|
||||
onClose={handleCloseModal}
|
||||
closeAfterTransition
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 500,
|
||||
}}
|
||||
>
|
||||
<Modal open={openModal} onClose={handleCloseModal} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 500 }}>
|
||||
<Fade in={openModal}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "90%",
|
||||
maxWidth: "600px",
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
background: "rgb(255, 255, 255)",
|
||||
boxShadow: "0 4px 30px rgba(0, 0, 0, 0.1)",
|
||||
backdropFilter: "blur(10px)",
|
||||
borderRadius: 2,
|
||||
border: "1px solid rgba(255, 255, 255, 0.3)",
|
||||
color: "rgb(0, 0, 0)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={modalContent.image}
|
||||
alt={modalContent.title}
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: "90%", maxWidth: "600px", p: 4, textAlign: "center", background: "white", borderRadius: 2, boxShadow: 3 }}>
|
||||
{loadingImage && <CircularProgress sx={{ mb: 2 }} />}
|
||||
<img src={modalContent.image} alt={modalContent.title} style={{ width: "100%", borderRadius: "8px", display: loadingImage ? "none" : "block" }} onLoad={() => setLoadingImage(false)} />
|
||||
<Typography variant="h3" sx={{ fontWeight: "bold", mb: 2 }}>
|
||||
{modalContent.title}
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ mb: 4 }}>
|
||||
{modalContent.text}
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={handleCloseModal}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: "#f44336",
|
||||
"&:hover": { backgroundColor: "#d32f2f" },
|
||||
}}
|
||||
>
|
||||
Quitter
|
||||
<Button onClick={handleCloseModal} variant="contained" color="secondary">
|
||||
Retour
|
||||
</Button>
|
||||
</Box>
|
||||
</Fade>
|
||||
</Modal>
|
||||
|
||||
{/* Carousel Section */}
|
||||
{carouselItems && carouselItems.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
padding: "40px 20px",
|
||||
textAlign: "center",
|
||||
backgroundColor: "#f9f9f9",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2"
|
||||
sx={{
|
||||
marginBottom: 4,
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
|
||||
|
||||
}}
|
||||
>
|
||||
Découvrez nos réalisations
|
||||
</Typography>
|
||||
|
||||
<Swiper
|
||||
modules={[Navigation, Pagination, Autoplay]}
|
||||
spaceBetween={20}
|
||||
slidesPerView={1}
|
||||
navigation
|
||||
pagination={{ clickable: true }}
|
||||
autoplay={{ delay: 3000 }}
|
||||
loop
|
||||
>
|
||||
{carouselItems.map((item, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
<Card
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "20px",
|
||||
boxShadow: 3,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
style={{
|
||||
width: "40%",
|
||||
height: "auto",
|
||||
objectFit: "cover",
|
||||
borderRadius: "8px",
|
||||
marginRight: "20px",
|
||||
}}
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography variant="h3"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
fontSize: { xs: '1.2rem', md: '2rem', lg: '2.5rem' }, // Responsive
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Typography>
|
||||
<Typography variant="body2">{item.description}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Section Action */}
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "#0e467f",
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
padding: "40px 20px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
mb: 2,
|
||||
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
|
||||
|
||||
}}
|
||||
>
|
||||
Prêt à démarrer ?
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body1" sx={{ mb: 4 }}>
|
||||
Rejoignez-nous dès aujourd’hui et profitez de nos services pour
|
||||
booster votre activité.
|
||||
</Typography>
|
||||
<Button
|
||||
href={ctaLink}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: "#00bcd4",
|
||||
"&:hover": { backgroundColor: "#0288d1" },
|
||||
}}
|
||||
>
|
||||
{ctaText}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServicePageTemplate;
|
||||
// ✅ 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.isRequired,
|
||||
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 })),
|
||||
seo: PropTypes.object, // ✅ Ajout du SEO en option
|
||||
};
|
||||
|
||||
// ✅ Valeurs par défaut
|
||||
ServicePageTemplate.defaultProps = {
|
||||
carouselItems: [],
|
||||
seo: {}, // ✅ SEO par défaut vide
|
||||
};
|
||||
|
||||
export default ServicePageTemplate;
|
||||
Reference in New Issue
Block a user