MAJ fonction BO page accueil et correction de beug
This commit is contained in:
parent
f0e31c4639
commit
1042eecfd2
@ -2,20 +2,20 @@
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>bureau d’études Maîtrise d’oeuvre</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">
|
||||
|
||||
BIN
frontend/src/assets/centre-formation-logo.avif
Normal file
BIN
frontend/src/assets/centre-formation-logo.avif
Normal file
Binary file not shown.
10
frontend/src/assets/style.css
Normal file
10
frontend/src/assets/style.css
Normal 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;
|
||||
}
|
||||
@ -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)",
|
||||
}}
|
||||
>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -140,7 +140,7 @@ const About = () => {
|
||||
mb: 4, mt:5
|
||||
}}
|
||||
>
|
||||
La société IN3
|
||||
La société IN3 !!!
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 d’une 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"
|
||||
|
||||
@ -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
4949
frontend/stats.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user