MAJ fonction BO page accueil et correction de beug

This commit is contained in:
sebvtl728 2025-07-15 00:40:20 +02:00
parent f0e31c4639
commit 1042eecfd2
13 changed files with 5224 additions and 215 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -2,20 +2,20 @@
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>bureau détudes Maîtrise doeuvre</title>
<title>Centre de formations - metier du numérique</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<!-- ✅ Ajout Open Graph statique -->
<meta property="og:title" content="Titre par défaut - Bureau détudes">
<meta property="og:description" content="Bienvenue sur notre bureau détudes en Maîtrise dœuvre.">
<meta property="og:title" content="Titre par défaut - Centre de formations">
<meta property="og:description" content="Bienvenue sur notre Centre de formations aux métiers du numérique.">
<meta property="og:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
<meta property="og:type" content="website">
<meta property="og:url" content="https://ton-site.com">
<!-- ✅ Twitter Cards -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Titre par défaut - Bureau détudes">
<meta name="twitter:description" content="Bienvenue sur notre bureau détudes en Maîtrise dœuvre.">
<meta name="twitter:title" content="Titre par défaut - Centre de formations">
<meta name="twitter:description" content="Bienvenue sur notre Centre de formations aux métiers du numérique.">
<meta name="twitter:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
<!-- <link rel="preload" href="/src/assets/vendor-BFTbvl5C.js" as="script"> -->
<link rel="preload" href="/assets/index-CfManBR6.css" as="style">

Binary file not shown.

View File

@ -0,0 +1,10 @@
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap');
h1{
font-size: 3.2em;
line-height: 1.1;
font-family: "Nunito", sans-serif !important;
font-optical-sizing: auto;
font-weight: 800 !important;
font-style: normal;
}

View File

@ -147,7 +147,7 @@ const ConstructSection = ({
mt: 4,
backgroundColor: "#0e467f",
color: "#fff",
borderRadius: "12px",
borderRadius: "12px 0 12px 12px",
boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)",
}}
>

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { getToken } from "../auth";
import { getPageById, updatePageACF } from "../wordpress";
@ -15,7 +15,7 @@ import { Add, Delete, ArrowBack, Save } from "@mui/icons-material";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
const ETUDE_PAGE_ID = 272; // ID de la page Bureau d'Étude
const ETUDE_PAGE_ID = 272;
function EditPageEtude() {
const navigate = useNavigate();
@ -24,189 +24,195 @@ function EditPageEtude() {
const [error, setError] = useState(null);
const [successMessage, setSuccessMessage] = useState("");
// Vérification de l'authentification
// Vérifie l'authentification
useEffect(() => {
if (!getToken()) {
navigate("/admin/login");
}
if (!getToken()) navigate("/admin/login");
}, [navigate]);
// Récupération des champs ACF
// Récupère les champs ACF
useEffect(() => {
const fetchPage = async () => {
try {
const pageData = await getPageById(ETUDE_PAGE_ID);
if (!pageData || !pageData.acf) {
if (!pageData?.acf) {
setError("❌ Impossible de charger les champs ACF.");
setLoading(false);
return;
}
console.log("expertises_specifiques:", pageData.acf.expertises_specifiques);
setAcfFields(pageData.acf);
} catch (error) {
} catch {
setError("⚠ Impossible de récupérer les champs ACF.");
} finally {
setLoading(false);
}
};
fetchPage();
}, []);
// Mise à jour des champs ACF
// Mise à jour ACF
const handleUpdateACF = async (e) => {
e.preventDefault();
try {
await updatePageACF(ETUDE_PAGE_ID, acfFields);
setSuccessMessage("✅ Modifications enregistrées !");
setError(null);
setTimeout(() => navigate("/admin/dashboard"), 2000);
} catch (error) {
} catch {
setError("⚠ Impossible de mettre à jour les champs ACF.");
}
};
// Gestion des champs ACF
// Modifie un champ simple
const handleFieldChange = (field, value) => {
setAcfFields((prevFields) => ({
...prevFields,
[field]: value,
}));
setAcfFields(prev => ({ ...prev, [field]: value }));
};
// Gestion des répéteurs
// Normalise un repeater en tableau
const getRepeater = (field) => {
const data = acfFields[field];
return Array.isArray(data)
? data
: data
? Object.values(data)
: [];
};
// Modifie un item du repeater
const handleRepeaterChange = (field, index, subField, value) => {
const updatedList = [...acfFields[field]];
updatedList[index][subField] = value;
setAcfFields({ ...acfFields, [field]: updatedList });
const list = getRepeater(field);
const updated = [...list];
updated[index] = { ...updated[index], [subField]: value };
setAcfFields(prev => ({ ...prev, [field]: updated }));
};
// Ajoute un item au repeater
const addRepeaterItem = (field, template) => {
setAcfFields({
...acfFields,
[field]: [...(acfFields[field] || []), template],
});
const list = getRepeater(field);
setAcfFields(prev => ({ ...prev, [field]: [...list, template] }));
};
// Supprime un item du repeater
const deleteRepeaterItem = (field, index) => {
const updatedList = acfFields[field].filter((_, i) => i !== index);
setAcfFields({ ...acfFields, [field]: updatedList });
const list = getRepeater(field);
const updated = list.filter((_, i) => i !== index);
setAcfFields(prev => ({ ...prev, [field]: updated }));
};
if (loading) return <Typography>Chargement...</Typography>;
return (
<Box
sx={{
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#f5f5f5",
padding: 4,
}}
>
<Box sx={{ py: 4, backgroundColor: "#f5f5f5" }}>
<Container maxWidth="md">
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}>
<Paper sx={{ p: 4, borderRadius: 3 }} elevation={4}>
<Button
startIcon={<ArrowBack />}
variant="outlined"
color="secondary"
onClick={() => navigate("/admin/dashboard")}
sx={{ mb: 2 }}
>
Retour à la gestion des pages
Retour
</Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}>
<Typography variant="h4" sx={{ fontWeight: "bold", mb: 3, textAlign: "center" }}>
Modifier la Page Bureau d'Étude
</Typography>
{error && <Typography color="error" sx={{ mb: 2 }}>{error}</Typography>}
{successMessage && <Typography sx={{ color: "green", textAlign: "center", mb: 2 }}>{successMessage}</Typography>}
{successMessage && <Typography color="success.main" sx={{ mb: 2, textAlign: "center" }}>{successMessage}</Typography>}
<form onSubmit={handleUpdateACF}>
<Grid container spacing={2}>
{/* Introduction */}
<Grid container spacing={3}>
{/* Introduction */}
<Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Introduction</Typography>
<Typography variant="h6">Introduction</Typography>
<ReactQuill
theme="snow"
value={acfFields.introduction || ""}
onChange={(value) => handleFieldChange("introduction", value)}
onChange={(val) => handleFieldChange("introduction", val)}
/>
</Grid>
{/* Méthodologie */}
{/* Méthodologie */}
<Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Méthodologie</Typography>
<Typography variant="h6">Méthodologie</Typography>
<TextField
fullWidth
variant="outlined"
value={acfFields.methodologie || ""}
onChange={(e) => handleFieldChange("methodologie", e.target.value)}
/>
</Grid>
{/* Liste des compétences */}
{/* Liste des compétences */}
<Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Liste des compétences</Typography>
{acfFields.liste_des_competences?.map((item, index) => (
<Paper key={index} sx={{ padding: 2, mb: 2 }}>
<Typography variant="h6">Liste des compétences</Typography>
{getRepeater("liste_des_competences").map((item, i) => (
<Paper key={i} sx={{ p:2, mb:2 }}>
<TextField
label="Titre"
fullWidth
value={item.comp_titre}
onChange={(e) => handleRepeaterChange("liste_des_competences", index, "comp_titre", e.target.value)}
sx={{ mb: 1 }}
onChange={(e) => handleRepeaterChange("liste_des_competences", i, "comp_titre", e.target.value)}
sx={{ mb:1 }}
/>
<TextField
label="Description"
fullWidth
multiline
rows={3}
multiline rows={3}
value={item.comp_description}
onChange={(e) => handleRepeaterChange("liste_des_competences", index, "comp_description", e.target.value)}
onChange={(e) => handleRepeaterChange("liste_des_competences", i, "comp_description", e.target.value)}
sx={{ mb:1 }}
/>
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteRepeaterItem("liste_des_competences", index)}>
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteRepeaterItem("liste_des_competences", i)}>
Supprimer
</Button>
</Paper>
))}
<Button startIcon={<Add />} variant="contained" color="primary" onClick={() => addRepeaterItem("liste_des_competences", { comp_titre: "", comp_description: "" })}>
<Button startIcon={<Add />} variant="contained" onClick={() => addRepeaterItem("liste_des_competences", { comp_titre: "", comp_description: "" })}>
Ajouter une compétence
</Button>
</Grid>
{/* Expertises spécifiques */}
{/* Expertises spécifiques */}
<Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Expertises spécifiques</Typography>
{acfFields.expertises_specifiques?.map((item, index) => (
<Paper key={index} sx={{ padding: 2, mb: 2 }}>
<Typography variant="h6">Expertises spécifiques</Typography>
{getRepeater("expertises_specifiques").map((item, i) => (
<Paper key={i} sx={{ p:2, mb:2 }}>
<TextField
label="Nom"
fullWidth
value={item.expertise_nom}
onChange={(e) => handleRepeaterChange("expertises_specifiques", index, "expertise_nom", e.target.value)}
sx={{ mb: 1 }}
onChange={(e) => handleRepeaterChange("expertises_specifiques", i, "expertise_nom", e.target.value)}
sx={{ mb:1 }}
/>
<TextField
label="Détails"
fullWidth
multiline
rows={2}
fullWidth multiline rows={2}
value={item.expertise_details}
onChange={(e) => handleRepeaterChange("expertises_specifiques", index, "expertise_details", e.target.value)}
onChange={(e) => handleRepeaterChange("expertises_specifiques", i, "expertise_details", e.target.value)}
sx={{ mb:1 }}
/>
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteRepeaterItem("expertises_specifiques", index)}>
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteRepeaterItem("expertises_specifiques", i)}>
Supprimer
</Button>
</Paper>
))}
<Button startIcon={<Add />} variant="contained" onClick={() => addRepeaterItem("expertises_specifiques", { expertise_nom: "", expertise_details: "" })}>
Ajouter une expertise
</Button>
</Grid>
{/* Save */}
<Grid item xs={12}>
<Button
type="submit"
fullWidth
variant="contained"
startIcon={<Save />}
sx={{ py:1.5 }}
>
Enregistrer
</Button>
</Grid>
</Grid>
<Button type="submit" variant="contained" color="primary" fullWidth startIcon={<Save />} sx={{ mt: 3 }}>
Enregistrer les modifications
</Button>
</form>
</Paper>
</Container>

View File

@ -24,8 +24,8 @@ import ArticleIcon from "@mui/icons-material/Article";
import InfoIcon from "@mui/icons-material/Info";
import ContactMailIcon from "@mui/icons-material/ContactMail";
import WorkIcon from "@mui/icons-material/Work";
import Logo from "../assets/logo-in3.svg";
import LogoM from "../assets/logo-in3-mobil.svg";
import Logo from "../assets/centre-formation-logo.avif";
import LogoM from "../assets/centre-formation-logo.avif";
const Header = () => {
const [drawerOpen, setDrawerOpen] = useState(false);
@ -123,7 +123,7 @@ const Header = () => {
>
<img
src={Logo}
alt="Bureau d'études Le Mans"
alt="centre de formation numérique"
style={{
width: "50px",
height: "50px",

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Box, Typography, Button } from '@mui/material';
const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor }) => {
// Priorité pour appliquer le fond : Image > Dégradé > Aucun fond
const backgroundStyle = backgroundImage
@ -60,9 +61,10 @@ const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor
fontWeight: 'bold',
padding: { xs: '10px 20px', md: '15px 30px' }, // Responsive
fontSize: { xs: '0.8rem', md: '1rem' },
backgroundColor: '#315397',
}}
>
Explorer les Articles
Nos Formations
</Button>
</Box>
);

View File

@ -140,7 +140,7 @@ const About = () => {
mb: 4, mt:5
}}
>
La société IN3
La société IN3 !!!
</Typography>
<Typography
variant="body2"

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useMemo } from "react";
import {
Box,
Typography,
@ -15,26 +15,31 @@ const BureauEtudes = () => {
useEffect(() => {
const fetchPageData = async () => {
try {
const response = await api.get("wp/v2/pages/272?_fields=acf"); // Remplace XXX par l'ID WordPress de la page
const response = await api.get("wp/v2/pages/272?_fields=acf");
console.log("expertises_specifiques:", response.data.acf.expertises_specifiques);
setPageData(response.data.acf);
} catch (error) {
console.error(
"Erreur lors de la récupération des données ACF :",
error
);
console.error("Erreur lors de la récupération des données ACF :", error);
}
};
fetchPageData();
}, []);
const expertises = useMemo(() => {
if (!pageData?.expertises_specifiques) return [];
return Array.isArray(pageData.expertises_specifiques)
? pageData.expertises_specifiques
: Object.values(pageData.expertises_specifiques);
}, [pageData]);
if (!pageData) {
return <Typography>Chargement...</Typography>;
}
return (
<Box sx={{}}>
{/* Section Héros avec Image de Fond */}
<Box>
{/* Section Héros */}
<Box
sx={{
backgroundImage: "url('https://picsum.photos/1920/600.webp')",
@ -50,29 +55,32 @@ const BureauEtudes = () => {
px: 4,
}}
>
{/* Présentation Générale */}
<Box sx={{ mt: 6, textAlign: "center", px: 4 }}>
<Typography
variant="h1"
sx={{
fontSize: { xs: "3rem", md: "3rem", lg: "3.5rem" },
fontWeight: "bold",
mb: 2,
}}
>
Notre Bureau d'Études
</Typography>
<Typography
variant="body2"
sx={{ maxWidth: "900px", mx: "auto" }}
dangerouslySetInnerHTML={{ __html: pageData.introduction }}
/>
</Box>
</Box>
{/* Présentation Générale */}
<Box sx={{ mt: 6, textAlign: "center", px: 4 }}>
<Typography variant="h1" sx={{
fontSize: { xs: "3rem", md: "3rem", lg: "3.5rem" }, // Responsive
fontWeight: "bold",
mb: 2,
}}>
Notre Bureau d'Études
</Typography>
<Typography variant="body2" sx={{ maxWidth: "900px", mx: "auto" }}
dangerouslySetInnerHTML={{ __html: pageData.introduction }}>
</Typography>
</Box>
</Box>
{/* Domaines d'Intervention */}
<Box sx={{ mt: 6, px: 4 }}>
<Typography variant="h2" sx={{
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive
fontWeight: "bold",
textAlign: "center",
mb: 3 }}>
<Typography
variant="h2"
sx={{ fontSize: "2.5rem", fontWeight: "bold", textAlign: "center", mb: 3 }}
>
Nos Domaines d'Intervention
</Typography>
<Grid container spacing={4} justifyContent="center">
@ -90,16 +98,15 @@ const BureauEtudes = () => {
}}
>
<CardContent>
<Typography variant="h3" sx={{
fontWeight: "bold",
mb: 1,
fontSize: { xs: "1.8rem", md: "1.8rem", lg: "1.8rem" }, // Responsive
}}>
<Typography
variant="h3"
sx={{ fontWeight: "bold", mb: 1, fontSize: "1.8rem" }}
>
{competence.comp_titre}
</Typography>
<Typography variant="body2" sx={{
textAlign:"center"
}}>{competence.comp_description}</Typography>
<Typography variant="body2" sx={{ textAlign: "center" }}>
{competence.comp_description}
</Typography>
</CardContent>
</Card>
</Grid>
@ -109,11 +116,7 @@ const BureauEtudes = () => {
{/* Compétences Spécifiques */}
<Box sx={{ mt: 6, textAlign: "center", px: 4 }}>
<Typography variant="h2" sx={{
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive
fontWeight: "bold",
mb: 3
}}>
<Typography variant="h2" sx={{ fontSize: "2.5rem", fontWeight: "bold", mb: 3 }}>
Nos Compétences
</Typography>
<Typography variant="body1" sx={{ maxWidth: "900px", mx: "auto" }}>
@ -132,41 +135,40 @@ const BureauEtudes = () => {
color: "white",
}}
>
<Typography variant="h2" sx={{
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive
fontWeight: "bold",
mb: 3
}}>
<Typography variant="h2" sx={{ fontSize: "2.5rem", fontWeight: "bold", mb: 3 }}>
Engagement et Accompagnement
</Typography>
<Grid container spacing={4} justifyContent="center">
{pageData.expertises_specifiques?.map((expertise, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<Card
sx={{
textAlign: "center",
padding: 3,
backgroundColor: "rgba(255, 255, 255, 0.1)",
backdropFilter: "blur(10px)",
transition: "transform 0.3s, box-shadow 0.3s",
"&:hover": {
transform: "scale(1.05)",
boxShadow: "0px 10px 20px rgba(255, 255, 255, 0.2)",
},
}}
>
<CardContent>
<Typography variant="h3" sx={{
fontSize: { xs: "1.8rem", md: "1.8rem", lg: "1.8rem" }, // Responsive
fontWeight: "bold",
mb: 1 }}>
{expertise.expertise_nom}
</Typography>
<Typography variant="body2">{expertise.expertise_details}</Typography>
</CardContent>
</Card>
</Grid>
))}
{expertises.length > 0 ? (
expertises.map((expertise, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<Card
sx={{
textAlign: "center",
padding: 3,
backgroundColor: "rgba(255, 255, 255, 0.1)",
backdropFilter: "blur(10px)",
transition: "transform 0.3s, box-shadow 0.3s",
"&:hover": {
transform: "scale(1.05)",
boxShadow: "0px 10px 20px rgba(255, 255, 255, 0.2)",
},
}}
>
<CardContent>
<Typography variant="h3" sx={{ fontSize: "1.8rem", fontWeight: "bold", mb: 1 }}>
{expertise.expertise_nom}
</Typography>
<Typography variant="body2">
{expertise.expertise_details}
</Typography>
</CardContent>
</Card>
</Grid>
))
) : (
<Typography>Aucune expertise spécifique à afficher.</Typography>
)}
</Grid>
</Box>
@ -195,4 +197,4 @@ const BureauEtudes = () => {
);
};
export default BureauEtudes;
export default BureauEtudes;

View File

@ -12,19 +12,6 @@ import {
CircularProgress,
} from "@mui/material";
// Fonction pour sauvegarder les missions dans WordPress
const saveMissionsToWordPress = async (missions) => {
try {
const newData = { missions }; // Sauvegarde dans ACF
await updateHomePageACF(13, newData);
localStorage.setItem("missions", JSON.stringify(missions)); // Sauvegarde locale
console.log("✅ Missions enregistrées dans WordPress et localStorage !");
} catch (error) {
console.error("❌ Erreur lors de l'enregistrement des missions :", error);
}
};
const GestionPageAccueil = () => {
const navigate = useNavigate();
const [heroTitle, setHeroTitle] = useState("");
@ -35,14 +22,20 @@ const GestionPageAccueil = () => {
const [successMessage, setSuccessMessage] = useState("");
const [missions, setMissions] = useState([]);
// Vérification de l'authentification (Redirection si non connecté)
const [constructTitle, setConstructTitle] = useState("");
const [constructSubtitle, setConstructSubtitle] = useState("");
const [constructSector, setConstructSector] = useState("");
const [constructNote, setConstructNote] = useState("");
const [constructText, setConstructText] = useState("");
// Redirection si non connecté
useEffect(() => {
if (!getToken()) {
navigate("/admin/login");
}
}, [navigate]);
// Chargement des données ACF depuis WordPress
// Chargement des données ACF
useEffect(() => {
const fetchPageData = async () => {
try {
@ -64,7 +57,7 @@ const GestionPageAccueil = () => {
setHeroTitle(data.acf?.hero_title || "");
setHeroText(data.acf?.hero_text || "");
// Récupération de l'image Hero
// Image Hero
if (data.acf?.img_hero) {
const mediaResponse = await fetch(
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/media/${data.acf.img_hero}`
@ -73,6 +66,13 @@ const GestionPageAccueil = () => {
setHeroImage(mediaData.source_url);
}
// GDC Construct
setConstructTitle(data.acf?.gdc_construct?.construct_title || "");
setConstructSubtitle(data.acf?.gdc_construct?.construct_subtitle || "");
setConstructSector(data.acf?.gdc_construct?.construct_sector || "");
setConstructNote(data.acf?.gdc_construct?.construct_note || "");
setConstructText(data.acf?.gdc_construct?.construct_text || "");
setLoading(false);
} catch (error) {
console.error("❌ Erreur chargement ACF :", error);
@ -84,18 +84,24 @@ const GestionPageAccueil = () => {
fetchPageData();
}, []);
// Mise à jour des champs ACF
// Enregistrement ACF
const handleSave = async () => {
try {
const newData = {
hero_title: heroTitle, // Enregistre le H1
hero_text: heroText, // Enregistre le texte du Hero
mission: missions, // Enregistre les missions
hero_title: heroTitle,
hero_text: heroText,
mission: missions,
gdc_construct: {
construct_title: constructTitle,
construct_subtitle: constructSubtitle,
construct_sector: constructSector,
construct_note: constructNote,
construct_text: constructText,
},
};
await updateHomePageACF(13, newData);
localStorage.setItem("missions", JSON.stringify(missions)); // Sauvegarde locale
localStorage.setItem("missions", JSON.stringify(missions));
setSuccessMessage("✅ Modifications enregistrées avec succès !");
setTimeout(() => setSuccessMessage(""), 3000);
} catch (error) {
@ -104,22 +110,23 @@ const GestionPageAccueil = () => {
}
};
// Gérer le téléversement dune nouvelle image Hero
// Upload image
const handleImageUpload = async (event) => {
const file = event.target.files[0];
if (file) {
try {
const imageId = await uploadImage(file);
setHeroImage(URL.createObjectURL(file)); // Affichage immédiat
setHeroImage(URL.createObjectURL(file));
await updateHomePageACF(13, { img_hero: imageId });
setSuccessMessage("✅ Image mise à jour !");
} catch (error) {
console.error("❌ Erreur lors de l'upload :", error);
console.error("❌ Erreur upload image :", error);
setError("❌ Échec de l'upload de l'image.");
}
}
};
// Missions - LocalStorage
useEffect(() => {
const storedMissions = JSON.parse(localStorage.getItem("missions")) || [
{ title: "📌 Mission 1", details: "Détails de la mission 1" },
@ -133,20 +140,6 @@ const GestionPageAccueil = () => {
localStorage.setItem("missions", JSON.stringify(missions));
}, [missions]);
// Fonction pour sauvegarder les missions dans WordPress
const saveMissionsToWordPress = async (missions) => {
try {
const newData = { missions }; // Sauvegarde dans ACF
await updateHomePageACF(13, newData);
localStorage.setItem("missions", JSON.stringify(missions)); // Sauvegarde locale
console.log("✅ Missions enregistrées dans WordPress et localStorage !");
} catch (error) {
console.error("❌ Erreur lors de l'enregistrement des missions :", error);
}
};
// Ajouter une mission
const addMission = () => {
setMissions([
...missions,
@ -154,7 +147,6 @@ const GestionPageAccueil = () => {
]);
};
// Modifier une mission
const updateMission = (index, field, value) => {
const updatedMissions = missions.map((mission, i) =>
i === index ? { ...mission, [field]: value } : mission
@ -162,7 +154,6 @@ const GestionPageAccueil = () => {
setMissions(updatedMissions);
};
// Supprimer une mission
const deleteMission = (index) => {
setMissions(missions.filter((_, i) => i !== index));
};
@ -173,7 +164,6 @@ const GestionPageAccueil = () => {
elevation={6}
sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}
>
{/* ✅ En-tête */}
<Typography
variant="h4"
sx={{
@ -186,7 +176,6 @@ const GestionPageAccueil = () => {
🏠 Gestion de la Page d'Accueil
</Typography>
{/* ✅ Affichage du statut de chargement */}
{loading ? (
<Box display="flex" justifyContent="center" alignItems="center">
<CircularProgress />
@ -197,7 +186,6 @@ const GestionPageAccueil = () => {
</Typography>
) : (
<>
{/* ✅ Message de succès */}
{successMessage && (
<Box
sx={{
@ -216,7 +204,6 @@ const GestionPageAccueil = () => {
</Box>
)}
{/* ✅ Affichage de l'image actuelle */}
{heroImage && (
<Box sx={{ textAlign: "center", mb: 2 }}>
<img
@ -232,7 +219,6 @@ const GestionPageAccueil = () => {
</Box>
)}
{/* ✅ Bouton pour changer l'image */}
<Button
variant="contained"
component="label"
@ -243,7 +229,6 @@ const GestionPageAccueil = () => {
<input type="file" hidden onChange={handleImageUpload} />
</Button>
{/* ✅ Formulaire de modification */}
<TextField
label="Titre Héros"
fullWidth
@ -266,7 +251,7 @@ const GestionPageAccueil = () => {
</>
)}
{/* ✅ Gestion de l'Accordéon */}
{/* ✅ Bloc Missions */}
<Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
<Typography variant="h4" sx={{ marginBottom: "10px" }}>
Gestion de l'Accordéon page d'accueil
@ -317,8 +302,63 @@ const GestionPageAccueil = () => {
Ajouter une mission
</Button>
</Paper>
{/* ✅ Bloc Construct */}
<Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
<Typography variant="h4" sx={{ marginBottom: "10px" }}>
🔧 Zone Formations
</Typography>
<TextField
label="Titre h2"
fullWidth
variant="outlined"
value={constructTitle}
onChange={(e) => setConstructTitle(e.target.value)}
sx={{ mb: 2 }}
/>
<TextField
label="Sous-titre h2"
fullWidth
variant="outlined"
value={constructSubtitle}
onChange={(e) => setConstructSubtitle(e.target.value)}
sx={{ mb: 2 }}
/>
<TextField
label="Secteurs formations"
fullWidth
variant="outlined"
value={constructSector}
onChange={(e) => setConstructSector(e.target.value)}
sx={{ mb: 2 }}
/>
<TextField
label="Texte Construct"
fullWidth
multiline
rows={4}
variant="outlined"
value={constructText}
onChange={(e) => setConstructText(e.target.value)}
sx={{ mb: 2 }}
/>
<TextField
label="Note formation"
fullWidth
variant="outlined"
value={constructNote}
onChange={(e) => setConstructNote(e.target.value)}
sx={{ mb: 2 }}
/>
</Paper>
{/* ✅ Actions */}
<Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
{/* ✅ Bouton d'enregistrement */}
<Button
variant="contained"
color="primary"
@ -336,7 +376,6 @@ const GestionPageAccueil = () => {
💾 Enregistrer les modifications
</Button>
{/* ✅ Bouton retour au Dashboard */}
<Button
variant="outlined"
color="secondary"

View File

@ -6,8 +6,9 @@ import ModernSpinner from "../SpinnerModerne";
import Expertises from "../Expertises";
import ConstructSection from "../ConstructSection";
import { Box, Grid, Typography, Button, Card, CardContent } from "@mui/material";
import Logo from "../../assets/logo-in3-mobil.svg";
import Logo from "../../assets/centre-formation-logo.avif";
import Testi from "../Testi";
import '../../assets/style.css';
const Home = () => {
const [pageData, setPageData] = useState(null);
@ -72,8 +73,8 @@ const Home = () => {
{/* SEO */}
<SEO
postId={13} // ID WordPress valide
defaultTitle={rank_math_title || "Bureau d'études & Maîtrise d'oeuvre"}
defaultDescription={rank_math_description || "L'acteur indispensable pour vos projets de constructions"}
defaultTitle={rank_math_title || "centre de formation "}
defaultDescription={rank_math_description || "formation et reconversion professionnelle dans les métiers du numérique"}
defaultCanonicalUrl={acf?.canonical_url || window.location.href}
defaultOgImage={heroImage || "https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"}
/>

4949
frontend/stats.html Normal file

File diff suppressed because one or more lines are too long