Maj Api and create contact page

This commit is contained in:
sebvtl728
2025-01-03 13:10:09 +01:00
parent 8fcd728887
commit 037cffaa0a
7 changed files with 994 additions and 81 deletions
+25 -25
View File
@@ -124,19 +124,6 @@ const Header = () => {
{/* Navigation Links */}
<Box sx={{ display: { xs: "none", md: "flex" }, gap: 2 }}>
<Button
component={Link}
to="/posts"
color="inherit"
sx={{
fontWeight: location.pathname === "/posts" ? "bold" : "normal",
textDecoration:
location.pathname === "/posts" ? "underline" : "none",
...commonButtonStyles,
}}
>
Articles
</Button>
{/* Services Dropdown */}
<Button
@@ -148,11 +135,11 @@ const Header = () => {
fontWeight: location.pathname.includes("/services")
? "bold"
: "normal",
textDecoration: location.pathname.includes("/services")
textDecoration: location.pathname.includes("/services")
? "underline"
: "none",
...commonButtonStyles,
}}
...commonButtonStyles,
}}
>
Services
</Button>
@@ -191,6 +178,19 @@ const Header = () => {
</MenuItem>
</Menu>
<Button
component={Link}
to="/posts"
color="inherit"
sx={{
fontWeight: location.pathname === "/posts" ? "bold" : "normal",
textDecoration:
location.pathname === "/posts" ? "underline" : "none",
...commonButtonStyles,
}}
>
Articles
</Button>
<Button
component={Link}
to="/about"
@@ -300,15 +300,6 @@ const Header = () => {
<HomeIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Accueil" />
</ListItem>
<ListItem
button
component={Link}
to="/posts"
selected={location.pathname === "/posts"}
>
<ArticleIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Articles" />
</ListItem>
<ListItem
button
component={Link}
@@ -336,6 +327,15 @@ const Header = () => {
<WorkIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Formation Video" />
</ListItem>
<ListItem
button
component={Link}
to="/posts"
selected={location.pathname === "/posts"}
>
<ArticleIcon sx={{ marginRight: 1 }} />
<ListItemText primary="Articles" />
</ListItem>
<ListItem
button
component={Link}
+462 -31
View File
@@ -1,40 +1,471 @@
import React, { useState, useEffect } from 'react';
import SEO from '../SEO';
import api from '../../api';
import React, { useState, useEffect } from "react";
import {
Box,
TextField,
Typography,
Button,
Grid,
Checkbox,
FormControlLabel,
Snackbar,
Alert,
CircularProgress,
Card,
CardContent,
Modal,
Fade,
Backdrop,
} from "@mui/material";
import SEO from "../SEO";
import api from "../../api";
const Contact = () => {
// États pour le SEO
const [metaTitle, setMetaTitle] = useState('Titre par défaut');
const [metaDescription, setMetaDescription] = useState('Description par défaut.');
const [metaTitle, setMetaTitle] = useState("Contactez-nous");
const [metaDescription, setMetaDescription] = useState(
"Contactez notre équipe pour plus d'informations."
);
const [formData, setFormData] = useState({
name: "",
email: "",
subject: "",
message: "",
consent: false,
});
const [errors, setErrors] = useState({});
const [loading, setLoading] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [openLightbox, setOpenLightbox] = useState(false);
useEffect(() => {
const fetchPageData = async () => {
try {
// Appel à l'API pour récupérer les données
const response = await api.get('wp/v2/pages/116?_fields=acf,rank_math_title,rank_math_description');
const { rank_math_title, rank_math_description } = response.data;
useEffect(() => {
const fetchSEOData = async () => {
try {
const response = await api.get(
"wp/v2/pages/116?_fields=acf,rank_math_title,rank_math_description"
);
const { rank_math_title, rank_math_description } = response.data;
setMetaTitle(rank_math_title || "Contactez-nous");
setMetaDescription(rank_math_description || "Contactez notre équipe.");
} catch (error) {
console.error("Erreur lors de la récupération des données SEO :", error);
}
};
// Mise à jour des métadonnées SEO
setMetaTitle(rank_math_title || 'Titre par défaut');
setMetaDescription(rank_math_description || 'Description par défaut.');
} catch (error) {
console.error('Erreur lors de la récupération des données :', error);
fetchSEOData();
}, []);
const validate = () => {
const newErrors = {};
if (!formData.name) newErrors.name = "Le nom est requis.";
if (!formData.email || !/\S+@\S+\.\S+/.test(formData.email))
newErrors.email = "Une adresse e-mail valide est requise.";
if (!formData.subject) newErrors.subject = "Le sujet est requis.";
if (!formData.message) newErrors.message = "Le message est requis.";
if (!formData.consent)
newErrors.consent = "Vous devez accepter la politique de confidentialité.";
return newErrors;
};
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData({
...formData,
[name]: type === "checkbox" ? checked : value,
});
};
const handleSubmit = async (e) => {
e.preventDefault();
const validationErrors = validate();
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
setLoading(true);
try {
const response = await fetch(
"https://votre-site.com/wp-json/custom/v1/contact",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
}
);
if (response.ok) {
setSuccessMessage(true);
setFormData({
name: "",
email: "",
subject: "",
message: "",
consent: false,
});
} else {
const errorData = await response.json();
throw new Error(errorData.message || "Une erreur est survenue.");
}
} catch (error) {
alert(error.message);
} finally {
setLoading(false);
}
}
};
const handleOpenLightbox = () => {
setOpenLightbox(true);
};
const handleCloseLightbox = () => {
setOpenLightbox(false);
};
return (
<Box>
{/* SEO */}
<SEO title={metaTitle} description={metaDescription} />
{/* Section Attention */}
<Box
sx={{
backgroundImage: `url('https://via.placeholder.com/1920x800')`,
backgroundSize: "cover",
backgroundPosition: "center",
minHeight: "50vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
textAlign: "center",
color: "white",
padding: "20px",
}}
>
<Box>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
fontSize: { xs: "2rem", md: "3rem", lg: "4rem" },
mb: 2,
}}
>
Contactez-nous
</Typography>
<Typography
variant="h6"
sx={{
fontSize: { xs: "1rem", md: "1.5rem" },
maxWidth: "600px",
mx: "auto",
}}
>
Une question? Une demande particulière? Remplissez le formulaire
ci-dessous ou consultez nos coordonnées.
</Typography>
</Box>
</Box>
{/* Section Coordonnées et Horaires */}
<Box sx={{ padding: "40px 20px", backgroundColor: "#f9f9f9" }}>
<Grid container spacing={4}>
{/* Coordonnées */}
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 2,
borderRadius: 2,
padding: "20px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
height: "80%",
}}
>
<Box>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
color: "#0e467f",
mb: 2,
}}
>
Nos coordonnées
</Typography>
<Typography variant="body1">
<strong>Adresse :</strong> 123 Rue Exemple, Paris, France
</Typography>
<Typography variant="body1">
<strong>Téléphone :</strong> +33 1 23 45 67 89
</Typography>
<Typography variant="body1">
<strong>Email :</strong> contact@exemple.com
</Typography>
</Box>
<Box
onClick={handleOpenLightbox}
sx={{
position: "relative",
cursor: "pointer",
width: "120px",
height: "120px",
borderRadius: "8px",
overflow: "hidden",
boxShadow: 3,
"&:hover": {
transform: "scale(1.10)",
boxShadow: 4,
transition: "transform 0.3s, box-shadow 0.3s",
},
"&:hover::before": {
content: '"Cliquez pour agrandir"',
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "rgba(0, 0, 0, 0.7)",
color: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "12px",
},
}}
>
<img
src="https://picsum.photos/id/442/300.webp"
alt="Carte"
style={{ width: "100%", height: "100%", objectFit: "cover" }}
/>
</Box>
</Card>
</Grid>
{/* Horaires */}
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 2,
borderRadius: 2,
padding: "20px",
backgroundColor: "#fff",
height: "80%",
}}
>
<CardContent>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
color: "#0e467f",
mb: 2,
textAlign: "center",
}}
>
Horaires d'ouverture
</Typography>
<Typography variant="body1">
<strong>Lundi - Vendredi :</strong> 9h00 - 18h00
</Typography>
<Typography variant="body1">
<strong>Samedi :</strong> 10h00 - 14h00
</Typography>
<Typography variant="body1">
<strong>Dimanche :</strong> Fermé
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
{/* Lightbox */}
<Modal
open={openLightbox}
onClose={handleCloseLightbox}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{ timeout: 500 }}
>
<Fade in={openLightbox}>
<Box
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "90%",
maxWidth: "800px",
backgroundColor: "white",
boxShadow: 24,
borderRadius: 2,
overflow: "hidden",
}}
>
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2624.9998319324044!2d2.2922928156738935!3d48.85884407928788!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47e66fdf78ccf6fd%3A0x10793db0b6d65f9b!2sEiffel+Tower!5e0!3m2!1sen!2sfr!4v1618237600000!5m2!1sen!2sfr"
width="100%"
height="400"
title="Carte interactive"
style={{ border: 0 }}
allowFullScreen=""
loading="lazy"
/>
</Box>
</Fade>
</Modal>
{/* Section Action */}
<Box
sx={{
backgroundColor: "#0e467f",
color: "white",
textAlign: "center",
padding: "40px 20px",
}}
>
<Typography
variant="h4"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "1.5rem", md: "2rem" },
}}
>
Besoin d'une assistance immédiate?
</Typography>
<Button
href="/aide"
variant="contained"
sx={{
textTransform: "none",
fontWeight: "bold",
backgroundColor: "#00bcd4",
"&:hover": { backgroundColor: "#0288d1" },
}}
>
Obtenez de l'aide maintenant
</Button>
</Box>
{/* Formulaire de contact */}
<Box
sx={{
maxWidth: "800px",
margin: "auto",
padding: "40px",
boxShadow: 3,
borderRadius: 2,
backgroundColor: "#fff",
mt: 4,
}}
>
<Typography
variant="h4"
sx={{ mb: 4, textAlign: "center", fontWeight: "bold", color: "#0e467f" }}
>
Formulaire de contact
</Typography>
<form onSubmit={handleSubmit} noValidate>
<TextField
label="Nom"
name="name"
value={formData.name}
onChange={handleChange}
error={Boolean(errors.name)}
helperText={errors.name}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="E-mail"
name="email"
value={formData.email}
onChange={handleChange}
error={Boolean(errors.email)}
helperText={errors.email}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="Sujet"
name="subject"
value={formData.subject}
onChange={handleChange}
error={Boolean(errors.subject)}
helperText={errors.subject}
fullWidth
margin="normal"
variant="outlined"
/>
<TextField
label="Message"
name="message"
value={formData.message}
onChange={handleChange}
error={Boolean(errors.message)}
helperText={errors.message}
fullWidth
margin="normal"
variant="outlined"
multiline
rows={4}
/>
<FormControlLabel
control={
<Checkbox
name="consent"
checked={formData.consent}
onChange={handleChange}
/>
}
};
label="J'accepte la politique de confidentialité."
sx={{ mt: 2, color: errors.consent ? "red" : "inherit" }}
/>
{errors.consent && (
<Typography variant="caption" color="error">
{errors.consent}
</Typography>
)}
<Box sx={{ textAlign: "center", mt: 3 }}>
<Button
type="submit"
variant="contained"
color="primary"
size="large"
disabled={loading}
sx={{
textTransform: "none",
fontWeight: "bold",
px: 5,
}}
>
{loading ? (
<CircularProgress size={24} sx={{ color: "#fff" }} />
) : (
"Envoyer"
)}
</Button>
</Box>
</form>
fetchPageData();
}, []); // Exécution au premier rendu
return (
<div>
{/* SEO */}
<SEO title={metaTitle} description={metaDescription} />
{/* Contenu de la page */}
<h1>Contact</h1>
<p>Bienvenue sur la page Contact.</p>
</div>
);
<Snackbar
open={successMessage}
autoHideDuration={6000}
onClose={() => setSuccessMessage(false)}
>
<Alert onClose={() => setSuccessMessage(false)} severity="success">
Message envoyé avec succès!
</Alert>
</Snackbar>
</Box>
</Box>
);
};
export default Contact;
+145 -25
View File
@@ -15,7 +15,6 @@ import { Navigation, Pagination, Autoplay } from "swiper/modules";
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/pagination";
import { useIsTouchDevice } from "./hooks/useIsTouchDevice"; // Import du hook
const ServicePageTemplate = ({
title,
@@ -29,7 +28,6 @@ const ServicePageTemplate = ({
}) => {
const [openModal, setOpenModal] = useState(false);
const [modalContent, setModalContent] = useState({});
const isTouchDevice = useIsTouchDevice(); // Détection des écrans tactiles
const handleCardClick = (feature) => {
setModalContent({
@@ -68,18 +66,25 @@ const ServicePageTemplate = ({
}}
>
<Box sx={{ maxWidth: "800px" }}>
<Typography
variant="h1"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "2rem", md: "3rem", lg: "4rem" },
<Typography
variant="h1"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: '2rem', md: '3rem', lg: '4rem' }, // Responsive
}}
>
{title}
</Typography>
<Typography variant="body1" sx={{ mb: 4 }}>
<Typography
variant="body1"
sx={{
mb: 4
}}
>
{subtitle}
</Typography>
<Button
href={ctaLink}
@@ -100,16 +105,16 @@ const ServicePageTemplate = ({
{/* Section Interest */}
<Box sx={{ padding: "40px 20px", textAlign: "center" }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
<Typography variant="h2"
sx={{
fontWeight: "bold",
mb: 2,
fontSize: { xs: "2rem", md: "3rem", lg: "3rem" },
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Pourquoi choisir ce service ?
Pourquoi choisir notre formation ?
</Typography>
<Typography
variant="body1"
sx={{ maxWidth: "800px", margin: "0 auto", mb: 4 }}
@@ -122,15 +127,17 @@ const ServicePageTemplate = ({
<Box sx={{ backgroundColor: "#ffffff", padding: "40px 20px" }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
textAlign: "center",
mb: 4,
fontSize: { xs: "2rem", md: "3rem", lg: "3rem" },
}}
sx={{
fontWeight: "bold",
textAlign: "center",
mb: 4,
fontSize: { xs: '2rem', md: '3rem', lg: '3rem' }, // Responsive
}}
>
Ce que nous offrons
</Typography>
<Grid container spacing={4} justifyContent="center">
{features.map((feature, index) => (
<Grid item xs={12} md={4} key={index}>
@@ -144,11 +151,17 @@ const ServicePageTemplate = ({
cursor: "pointer",
transition: "transform 0.3s ease, box-shadow 0.3s ease",
"&:hover": {
transform: isTouchDevice ? "none" : "scale(1.05)",
boxShadow: isTouchDevice ? 2 : 5,
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)",
transform: "scale(0.98)", // Effet de clic pour le tactile
boxShadow: 3,
},
}}
@@ -238,8 +251,115 @@ const ServicePageTemplate = ({
</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 aujourdhui 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;
export default ServicePageTemplate;