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
Vendored
BIN
View File
Binary file not shown.
+5 -5
View File
@@ -2,20 +2,20 @@
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="UTF-8" /> <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" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<!-- ✅ Ajout Open Graph statique --> <!-- ✅ Ajout Open Graph statique -->
<meta property="og:title" content="Titre par défaut - Bureau d’études"> <meta property="og:title" content="Titre par défaut - Centre de formations">
<meta property="og:description" content="Bienvenue sur notre bureau d’études en Maîtrise d’œuvre."> <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: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:type" content="website">
<meta property="og:url" content="https://ton-site.com"> <meta property="og:url" content="https://ton-site.com">
<!-- ✅ Twitter Cards --> <!-- ✅ Twitter Cards -->
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Titre par défaut - Bureau d’études"> <meta name="twitter:title" content="Titre par défaut - Centre de formations">
<meta name="twitter:description" content="Bienvenue sur notre bureau d’études en Maîtrise d’œuvre."> <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"> <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="/src/assets/vendor-BFTbvl5C.js" as="script"> -->
<link rel="preload" href="/assets/index-CfManBR6.css" as="style"> <link rel="preload" href="/assets/index-CfManBR6.css" as="style">
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

+10
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;
}
+1 -1
View File
@@ -147,7 +147,7 @@ const ConstructSection = ({
mt: 4, mt: 4,
backgroundColor: "#0e467f", backgroundColor: "#0e467f",
color: "#fff", color: "#fff",
borderRadius: "12px", borderRadius: "12px 0 12px 12px",
boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)", boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)",
}} }}
> >
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { getToken } from "../auth"; import { getToken } from "../auth";
import { getPageById, updatePageACF } from "../wordpress"; import { getPageById, updatePageACF } from "../wordpress";
@@ -15,7 +15,7 @@ import { Add, Delete, ArrowBack, Save } from "@mui/icons-material";
import ReactQuill from "react-quill"; import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css"; 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() { function EditPageEtude() {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -24,189 +24,195 @@ function EditPageEtude() {
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [successMessage, setSuccessMessage] = useState(""); const [successMessage, setSuccessMessage] = useState("");
// Vérification de l'authentification // Vérifie l'authentification
useEffect(() => { useEffect(() => {
if (!getToken()) { if (!getToken()) navigate("/admin/login");
navigate("/admin/login");
}
}, [navigate]); }, [navigate]);
// Récupération des champs ACF // Récupère les champs ACF
useEffect(() => { useEffect(() => {
const fetchPage = async () => { const fetchPage = async () => {
try { try {
const pageData = await getPageById(ETUDE_PAGE_ID); const pageData = await getPageById(ETUDE_PAGE_ID);
if (!pageData || !pageData.acf) { if (!pageData?.acf) {
setError("❌ Impossible de charger les champs ACF."); setError("❌ Impossible de charger les champs ACF.");
setLoading(false);
return; return;
} }
console.log("expertises_specifiques:", pageData.acf.expertises_specifiques);
setAcfFields(pageData.acf); setAcfFields(pageData.acf);
} catch (error) { } catch {
setError("⚠ Impossible de récupérer les champs ACF."); setError("⚠ Impossible de récupérer les champs ACF.");
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; };
fetchPage(); fetchPage();
}, []); }, []);
// Mise à jour des champs ACF // Mise à jour ACF
const handleUpdateACF = async (e) => { const handleUpdateACF = async (e) => {
e.preventDefault(); e.preventDefault();
try { try {
await updatePageACF(ETUDE_PAGE_ID, acfFields); await updatePageACF(ETUDE_PAGE_ID, acfFields);
setSuccessMessage("✅ Modifications enregistrées !"); setSuccessMessage("✅ Modifications enregistrées !");
setError(null);
setTimeout(() => navigate("/admin/dashboard"), 2000); setTimeout(() => navigate("/admin/dashboard"), 2000);
} catch (error) { } catch {
setError("⚠ Impossible de mettre à jour les champs ACF."); setError("⚠ Impossible de mettre à jour les champs ACF.");
} }
}; };
// ✅ Gestion des champs ACF // Modifie un champ simple
const handleFieldChange = (field, value) => { const handleFieldChange = (field, value) => {
setAcfFields((prevFields) => ({ setAcfFields(prev => ({ ...prev, [field]: value }));
...prevFields,
[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 handleRepeaterChange = (field, index, subField, value) => {
const updatedList = [...acfFields[field]]; const list = getRepeater(field);
updatedList[index][subField] = value; const updated = [...list];
setAcfFields({ ...acfFields, [field]: updatedList }); updated[index] = { ...updated[index], [subField]: value };
setAcfFields(prev => ({ ...prev, [field]: updated }));
}; };
// Ajoute un item au repeater
const addRepeaterItem = (field, template) => { const addRepeaterItem = (field, template) => {
setAcfFields({ const list = getRepeater(field);
...acfFields, setAcfFields(prev => ({ ...prev, [field]: [...list, template] }));
[field]: [...(acfFields[field] || []), template],
});
}; };
// Supprime un item du repeater
const deleteRepeaterItem = (field, index) => { const deleteRepeaterItem = (field, index) => {
const updatedList = acfFields[field].filter((_, i) => i !== index); const list = getRepeater(field);
setAcfFields({ ...acfFields, [field]: updatedList }); const updated = list.filter((_, i) => i !== index);
setAcfFields(prev => ({ ...prev, [field]: updated }));
}; };
if (loading) return <Typography>Chargement...</Typography>; if (loading) return <Typography>Chargement...</Typography>;
return ( return (
<Box <Box sx={{ py: 4, backgroundColor: "#f5f5f5" }}>
sx={{
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#f5f5f5",
padding: 4,
}}
>
<Container maxWidth="md"> <Container maxWidth="md">
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}> <Paper sx={{ p: 4, borderRadius: 3 }} elevation={4}>
<Button <Button
startIcon={<ArrowBack />} startIcon={<ArrowBack />}
variant="outlined" variant="outlined"
color="secondary"
onClick={() => navigate("/admin/dashboard")} onClick={() => navigate("/admin/dashboard")}
sx={{ mb: 2 }} sx={{ mb: 2 }}
> >
Retour à la gestion des pages Retour
</Button> </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 Modifier la Page Bureau d'Étude
</Typography> </Typography>
{error && <Typography color="error" sx={{ mb: 2 }}>{error}</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}> <form onSubmit={handleUpdateACF}>
<Grid container spacing={2}> <Grid container spacing={3}>
{/* Introduction */} {/* Introduction */}
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Introduction</Typography> <Typography variant="h6">Introduction</Typography>
<ReactQuill <ReactQuill
theme="snow"
value={acfFields.introduction || ""} value={acfFields.introduction || ""}
onChange={(value) => handleFieldChange("introduction", value)} onChange={(val) => handleFieldChange("introduction", val)}
/> />
</Grid> </Grid>
{/* Méthodologie */} {/* Méthodologie */}
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Méthodologie</Typography> <Typography variant="h6">Méthodologie</Typography>
<TextField <TextField
fullWidth fullWidth
variant="outlined"
value={acfFields.methodologie || ""} value={acfFields.methodologie || ""}
onChange={(e) => handleFieldChange("methodologie", e.target.value)} onChange={(e) => handleFieldChange("methodologie", e.target.value)}
/> />
</Grid> </Grid>
{/* Liste des compétences */} {/* Liste des compétences */}
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Liste des compétences</Typography> <Typography variant="h6">Liste des compétences</Typography>
{acfFields.liste_des_competences?.map((item, index) => ( {getRepeater("liste_des_competences").map((item, i) => (
<Paper key={index} sx={{ padding: 2, mb: 2 }}> <Paper key={i} sx={{ p:2, mb:2 }}>
<TextField <TextField
label="Titre" label="Titre"
fullWidth fullWidth
value={item.comp_titre} value={item.comp_titre}
onChange={(e) => handleRepeaterChange("liste_des_competences", index, "comp_titre", e.target.value)} onChange={(e) => handleRepeaterChange("liste_des_competences", i, "comp_titre", e.target.value)}
sx={{ mb:1 }} sx={{ mb:1 }}
/> />
<TextField <TextField
label="Description" label="Description"
fullWidth fullWidth
multiline multiline rows={3}
rows={3}
value={item.comp_description} 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 Supprimer
</Button> </Button>
</Paper> </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 Ajouter une compétence
</Button> </Button>
</Grid> </Grid>
{/* Expertises spécifiques */} {/* Expertises spécifiques */}
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>Expertises spécifiques</Typography> <Typography variant="h6">Expertises spécifiques</Typography>
{acfFields.expertises_specifiques?.map((item, index) => ( {getRepeater("expertises_specifiques").map((item, i) => (
<Paper key={index} sx={{ padding: 2, mb: 2 }}> <Paper key={i} sx={{ p:2, mb:2 }}>
<TextField <TextField
label="Nom" label="Nom"
fullWidth fullWidth
value={item.expertise_nom} value={item.expertise_nom}
onChange={(e) => handleRepeaterChange("expertises_specifiques", index, "expertise_nom", e.target.value)} onChange={(e) => handleRepeaterChange("expertises_specifiques", i, "expertise_nom", e.target.value)}
sx={{ mb:1 }} sx={{ mb:1 }}
/> />
<TextField <TextField
label="Détails" label="Détails"
fullWidth fullWidth multiline rows={2}
multiline
rows={2}
value={item.expertise_details} 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 Supprimer
</Button> </Button>
</Paper> </Paper>
))} ))}
</Grid> <Button startIcon={<Add />} variant="contained" onClick={() => addRepeaterItem("expertises_specifiques", { expertise_nom: "", expertise_details: "" })}>
Ajouter une expertise
</Button>
</Grid> </Grid>
<Button type="submit" variant="contained" color="primary" fullWidth startIcon={<Save />} sx={{ mt: 3 }}> {/* Save */}
Enregistrer les modifications <Grid item xs={12}>
<Button
type="submit"
fullWidth
variant="contained"
startIcon={<Save />}
sx={{ py:1.5 }}
>
Enregistrer
</Button> </Button>
</Grid>
</Grid>
</form> </form>
</Paper> </Paper>
</Container> </Container>
+3 -3
View File
@@ -24,8 +24,8 @@ import ArticleIcon from "@mui/icons-material/Article";
import InfoIcon from "@mui/icons-material/Info"; import InfoIcon from "@mui/icons-material/Info";
import ContactMailIcon from "@mui/icons-material/ContactMail"; import ContactMailIcon from "@mui/icons-material/ContactMail";
import WorkIcon from "@mui/icons-material/Work"; import WorkIcon from "@mui/icons-material/Work";
import Logo from "../assets/logo-in3.svg"; import Logo from "../assets/centre-formation-logo.avif";
import LogoM from "../assets/logo-in3-mobil.svg"; import LogoM from "../assets/centre-formation-logo.avif";
const Header = () => { const Header = () => {
const [drawerOpen, setDrawerOpen] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false);
@@ -123,7 +123,7 @@ const Header = () => {
> >
<img <img
src={Logo} src={Logo}
alt="Bureau d'études Le Mans" alt="centre de formation numérique"
style={{ style={{
width: "50px", width: "50px",
height: "50px", height: "50px",
+3 -1
View File
@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { Box, Typography, Button } from '@mui/material'; import { Box, Typography, Button } from '@mui/material';
const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor }) => { const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor }) => {
// Priorité pour appliquer le fond : Image > Dégradé > Aucun fond // Priorité pour appliquer le fond : Image > Dégradé > Aucun fond
const backgroundStyle = backgroundImage const backgroundStyle = backgroundImage
@@ -60,9 +61,10 @@ const Hero = ({ backgroundImage, useGradient, title, titleColor, text, textColor
fontWeight: 'bold', fontWeight: 'bold',
padding: { xs: '10px 20px', md: '15px 30px' }, // Responsive padding: { xs: '10px 20px', md: '15px 30px' }, // Responsive
fontSize: { xs: '0.8rem', md: '1rem' }, fontSize: { xs: '0.8rem', md: '1rem' },
backgroundColor: '#315397',
}} }}
> >
Explorer les Articles Nos Formations
</Button> </Button>
</Box> </Box>
); );
+1 -1
View File
@@ -140,7 +140,7 @@ const About = () => {
mb: 4, mt:5 mb: 4, mt:5
}} }}
> >
La société IN3 La société IN3 !!!
</Typography> </Typography>
<Typography <Typography
variant="body2" variant="body2"
+49 -47
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useMemo } from "react";
import { import {
Box, Box,
Typography, Typography,
@@ -15,26 +15,31 @@ const BureauEtudes = () => {
useEffect(() => { useEffect(() => {
const fetchPageData = async () => { const fetchPageData = async () => {
try { 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); setPageData(response.data.acf);
} catch (error) { } catch (error) {
console.error( console.error("Erreur lors de la récupération des données ACF :", error);
"Erreur lors de la récupération des données ACF :",
error
);
} }
}; };
fetchPageData(); 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) { if (!pageData) {
return <Typography>Chargement...</Typography>; return <Typography>Chargement...</Typography>;
} }
return ( return (
<Box sx={{}}> <Box>
{/* Section Héros avec Image de Fond */} {/* Section Héros */}
<Box <Box
sx={{ sx={{
backgroundImage: "url('https://picsum.photos/1920/600.webp')", backgroundImage: "url('https://picsum.photos/1920/600.webp')",
@@ -50,29 +55,32 @@ const BureauEtudes = () => {
px: 4, px: 4,
}} }}
> >
{/* Présentation Générale */} {/* Présentation Générale */}
<Box sx={{ mt: 6, textAlign: "center", px: 4 }}> <Box sx={{ mt: 6, textAlign: "center", px: 4 }}>
<Typography variant="h1" sx={{ <Typography
fontSize: { xs: "3rem", md: "3rem", lg: "3.5rem" }, // Responsive variant="h1"
sx={{
fontSize: { xs: "3rem", md: "3rem", lg: "3.5rem" },
fontWeight: "bold", fontWeight: "bold",
mb: 2, mb: 2,
}}> }}
>
Notre Bureau d'Études Notre Bureau d'Études
</Typography> </Typography>
<Typography variant="body2" sx={{ maxWidth: "900px", mx: "auto" }} <Typography
dangerouslySetInnerHTML={{ __html: pageData.introduction }}> variant="body2"
sx={{ maxWidth: "900px", mx: "auto" }}
dangerouslySetInnerHTML={{ __html: pageData.introduction }}
/>
</Box>
</Box>
</Typography>
</Box>
</Box>
{/* Domaines d'Intervention */} {/* Domaines d'Intervention */}
<Box sx={{ mt: 6, px: 4 }}> <Box sx={{ mt: 6, px: 4 }}>
<Typography variant="h2" sx={{ <Typography
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive variant="h2"
fontWeight: "bold", sx={{ fontSize: "2.5rem", fontWeight: "bold", textAlign: "center", mb: 3 }}
textAlign: "center", >
mb: 3 }}>
Nos Domaines d'Intervention Nos Domaines d'Intervention
</Typography> </Typography>
<Grid container spacing={4} justifyContent="center"> <Grid container spacing={4} justifyContent="center">
@@ -90,16 +98,15 @@ const BureauEtudes = () => {
}} }}
> >
<CardContent> <CardContent>
<Typography variant="h3" sx={{ <Typography
fontWeight: "bold", variant="h3"
mb: 1, sx={{ fontWeight: "bold", mb: 1, fontSize: "1.8rem" }}
fontSize: { xs: "1.8rem", md: "1.8rem", lg: "1.8rem" }, // Responsive >
}}>
{competence.comp_titre} {competence.comp_titre}
</Typography> </Typography>
<Typography variant="body2" sx={{ <Typography variant="body2" sx={{ textAlign: "center" }}>
textAlign:"center" {competence.comp_description}
}}>{competence.comp_description}</Typography> </Typography>
</CardContent> </CardContent>
</Card> </Card>
</Grid> </Grid>
@@ -109,11 +116,7 @@ const BureauEtudes = () => {
{/* Compétences Spécifiques */} {/* Compétences Spécifiques */}
<Box sx={{ mt: 6, textAlign: "center", px: 4 }}> <Box sx={{ mt: 6, textAlign: "center", px: 4 }}>
<Typography variant="h2" sx={{ <Typography variant="h2" sx={{ fontSize: "2.5rem", fontWeight: "bold", mb: 3 }}>
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive
fontWeight: "bold",
mb: 3
}}>
Nos Compétences Nos Compétences
</Typography> </Typography>
<Typography variant="body1" sx={{ maxWidth: "900px", mx: "auto" }}> <Typography variant="body1" sx={{ maxWidth: "900px", mx: "auto" }}>
@@ -132,15 +135,12 @@ const BureauEtudes = () => {
color: "white", color: "white",
}} }}
> >
<Typography variant="h2" sx={{ <Typography variant="h2" sx={{ fontSize: "2.5rem", fontWeight: "bold", mb: 3 }}>
fontSize: { xs: "2.5rem", md: "2.5rem", lg: "2.5rem" }, // Responsive
fontWeight: "bold",
mb: 3
}}>
Engagement et Accompagnement Engagement et Accompagnement
</Typography> </Typography>
<Grid container spacing={4} justifyContent="center"> <Grid container spacing={4} justifyContent="center">
{pageData.expertises_specifiques?.map((expertise, index) => ( {expertises.length > 0 ? (
expertises.map((expertise, index) => (
<Grid item xs={12} sm={6} md={4} key={index}> <Grid item xs={12} sm={6} md={4} key={index}>
<Card <Card
sx={{ sx={{
@@ -156,17 +156,19 @@ const BureauEtudes = () => {
}} }}
> >
<CardContent> <CardContent>
<Typography variant="h3" sx={{ <Typography variant="h3" sx={{ fontSize: "1.8rem", fontWeight: "bold", mb: 1 }}>
fontSize: { xs: "1.8rem", md: "1.8rem", lg: "1.8rem" }, // Responsive
fontWeight: "bold",
mb: 1 }}>
{expertise.expertise_nom} {expertise.expertise_nom}
</Typography> </Typography>
<Typography variant="body2">{expertise.expertise_details}</Typography> <Typography variant="body2">
{expertise.expertise_details}
</Typography>
</CardContent> </CardContent>
</Card> </Card>
</Grid> </Grid>
))} ))
) : (
<Typography>Aucune expertise spécifique à afficher.</Typography>
)}
</Grid> </Grid>
</Box> </Box>
@@ -12,19 +12,6 @@ import {
CircularProgress, CircularProgress,
} from "@mui/material"; } 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 GestionPageAccueil = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [heroTitle, setHeroTitle] = useState(""); const [heroTitle, setHeroTitle] = useState("");
@@ -35,14 +22,20 @@ const GestionPageAccueil = () => {
const [successMessage, setSuccessMessage] = useState(""); const [successMessage, setSuccessMessage] = useState("");
const [missions, setMissions] = 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(() => { useEffect(() => {
if (!getToken()) { if (!getToken()) {
navigate("/admin/login"); navigate("/admin/login");
} }
}, [navigate]); }, [navigate]);
// ✅ Chargement des données ACF depuis WordPress // ✅ Chargement des données ACF
useEffect(() => { useEffect(() => {
const fetchPageData = async () => { const fetchPageData = async () => {
try { try {
@@ -64,7 +57,7 @@ const GestionPageAccueil = () => {
setHeroTitle(data.acf?.hero_title || ""); setHeroTitle(data.acf?.hero_title || "");
setHeroText(data.acf?.hero_text || ""); setHeroText(data.acf?.hero_text || "");
// ✅ Récupération de l'image Hero // ✅ Image Hero
if (data.acf?.img_hero) { if (data.acf?.img_hero) {
const mediaResponse = await fetch( const mediaResponse = await fetch(
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/media/${data.acf.img_hero}` `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); 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); setLoading(false);
} catch (error) { } catch (error) {
console.error("❌ Erreur chargement ACF :", error); console.error("❌ Erreur chargement ACF :", error);
@@ -84,18 +84,24 @@ const GestionPageAccueil = () => {
fetchPageData(); fetchPageData();
}, []); }, []);
// ✅ Mise à jour des champs ACF // ✅ Enregistrement ACF
const handleSave = async () => { const handleSave = async () => {
try { try {
const newData = { const newData = {
hero_title: heroTitle, // ✅ Enregistre le H1 hero_title: heroTitle,
hero_text: heroText, // ✅ Enregistre le texte du Hero hero_text: heroText,
mission: missions, // ✅ Enregistre les missions mission: missions,
gdc_construct: {
construct_title: constructTitle,
construct_subtitle: constructSubtitle,
construct_sector: constructSector,
construct_note: constructNote,
construct_text: constructText,
},
}; };
await updateHomePageACF(13, newData); 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 !"); setSuccessMessage("✅ Modifications enregistrées avec succès !");
setTimeout(() => setSuccessMessage(""), 3000); setTimeout(() => setSuccessMessage(""), 3000);
} catch (error) { } 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 handleImageUpload = async (event) => {
const file = event.target.files[0]; const file = event.target.files[0];
if (file) { if (file) {
try { try {
const imageId = await uploadImage(file); const imageId = await uploadImage(file);
setHeroImage(URL.createObjectURL(file)); // Affichage immédiat setHeroImage(URL.createObjectURL(file));
await updateHomePageACF(13, { img_hero: imageId }); await updateHomePageACF(13, { img_hero: imageId });
setSuccessMessage("✅ Image mise à jour !"); setSuccessMessage("✅ Image mise à jour !");
} catch (error) { } catch (error) {
console.error("❌ Erreur lors de l'upload :", error); console.error("❌ Erreur upload image :", error);
setError("❌ Échec de l'upload de l'image."); setError("❌ Échec de l'upload de l'image.");
} }
} }
}; };
// ✅ Missions - LocalStorage
useEffect(() => { useEffect(() => {
const storedMissions = JSON.parse(localStorage.getItem("missions")) || [ const storedMissions = JSON.parse(localStorage.getItem("missions")) || [
{ title: "📌 Mission 1", details: "Détails de la mission 1" }, { title: "📌 Mission 1", details: "Détails de la mission 1" },
@@ -133,20 +140,6 @@ const GestionPageAccueil = () => {
localStorage.setItem("missions", JSON.stringify(missions)); localStorage.setItem("missions", JSON.stringify(missions));
}, [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 = () => { const addMission = () => {
setMissions([ setMissions([
...missions, ...missions,
@@ -154,7 +147,6 @@ const GestionPageAccueil = () => {
]); ]);
}; };
// ✅ Modifier une mission
const updateMission = (index, field, value) => { const updateMission = (index, field, value) => {
const updatedMissions = missions.map((mission, i) => const updatedMissions = missions.map((mission, i) =>
i === index ? { ...mission, [field]: value } : mission i === index ? { ...mission, [field]: value } : mission
@@ -162,7 +154,6 @@ const GestionPageAccueil = () => {
setMissions(updatedMissions); setMissions(updatedMissions);
}; };
// ✅ Supprimer une mission
const deleteMission = (index) => { const deleteMission = (index) => {
setMissions(missions.filter((_, i) => i !== index)); setMissions(missions.filter((_, i) => i !== index));
}; };
@@ -173,7 +164,6 @@ const GestionPageAccueil = () => {
elevation={6} elevation={6}
sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }} sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}
> >
{/* ✅ En-tête */}
<Typography <Typography
variant="h4" variant="h4"
sx={{ sx={{
@@ -186,7 +176,6 @@ const GestionPageAccueil = () => {
🏠 Gestion de la Page d'Accueil 🏠 Gestion de la Page d'Accueil
</Typography> </Typography>
{/* ✅ Affichage du statut de chargement */}
{loading ? ( {loading ? (
<Box display="flex" justifyContent="center" alignItems="center"> <Box display="flex" justifyContent="center" alignItems="center">
<CircularProgress /> <CircularProgress />
@@ -197,7 +186,6 @@ const GestionPageAccueil = () => {
</Typography> </Typography>
) : ( ) : (
<> <>
{/* ✅ Message de succès */}
{successMessage && ( {successMessage && (
<Box <Box
sx={{ sx={{
@@ -216,7 +204,6 @@ const GestionPageAccueil = () => {
</Box> </Box>
)} )}
{/* ✅ Affichage de l'image actuelle */}
{heroImage && ( {heroImage && (
<Box sx={{ textAlign: "center", mb: 2 }}> <Box sx={{ textAlign: "center", mb: 2 }}>
<img <img
@@ -232,7 +219,6 @@ const GestionPageAccueil = () => {
</Box> </Box>
)} )}
{/* ✅ Bouton pour changer l'image */}
<Button <Button
variant="contained" variant="contained"
component="label" component="label"
@@ -243,7 +229,6 @@ const GestionPageAccueil = () => {
<input type="file" hidden onChange={handleImageUpload} /> <input type="file" hidden onChange={handleImageUpload} />
</Button> </Button>
{/* ✅ Formulaire de modification */}
<TextField <TextField
label="Titre Héros" label="Titre Héros"
fullWidth fullWidth
@@ -266,7 +251,7 @@ const GestionPageAccueil = () => {
</> </>
)} )}
{/* ✅ Gestion de l'Accordéon */} {/* ✅ Bloc Missions */}
<Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}> <Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
<Typography variant="h4" sx={{ marginBottom: "10px" }}> <Typography variant="h4" sx={{ marginBottom: "10px" }}>
Gestion de l'Accordéon page d'accueil Gestion de l'Accordéon page d'accueil
@@ -317,8 +302,63 @@ const GestionPageAccueil = () => {
Ajouter une mission Ajouter une mission
</Button> </Button>
</Paper> </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" }}> <Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
{/* ✅ Bouton d'enregistrement */}
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
@@ -336,7 +376,6 @@ const GestionPageAccueil = () => {
💾 Enregistrer les modifications 💾 Enregistrer les modifications
</Button> </Button>
{/* ✅ Bouton retour au Dashboard */}
<Button <Button
variant="outlined" variant="outlined"
color="secondary" color="secondary"
+4 -3
View File
@@ -6,8 +6,9 @@ import ModernSpinner from "../SpinnerModerne";
import Expertises from "../Expertises"; import Expertises from "../Expertises";
import ConstructSection from "../ConstructSection"; import ConstructSection from "../ConstructSection";
import { Box, Grid, Typography, Button, Card, CardContent } from "@mui/material"; 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 Testi from "../Testi";
import '../../assets/style.css';
const Home = () => { const Home = () => {
const [pageData, setPageData] = useState(null); const [pageData, setPageData] = useState(null);
@@ -72,8 +73,8 @@ const Home = () => {
{/* SEO */} {/* SEO */}
<SEO <SEO
postId={13} // ID WordPress valide postId={13} // ID WordPress valide
defaultTitle={rank_math_title || "Bureau d'études & Maîtrise d'oeuvre"} defaultTitle={rank_math_title || "centre de formation "}
defaultDescription={rank_math_description || "L'acteur indispensable pour vos projets de constructions"} defaultDescription={rank_math_description || "formation et reconversion professionnelle dans les métiers du numérique"}
defaultCanonicalUrl={acf?.canonical_url || window.location.href} 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"} defaultOgImage={heroImage || "https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"}
/> />
+4949
View File
File diff suppressed because one or more lines are too long