add accordeon auto page home
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
} from "@mui/material";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import api from "../api";
|
||||
|
||||
const ConstructSection = ({
|
||||
constructTitle,
|
||||
@@ -17,85 +21,96 @@ const ConstructSection = ({
|
||||
missionTitle,
|
||||
missionItems,
|
||||
}) => {
|
||||
// État pour gérer l'ouverture des accordéons
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [missions, setMissions] = useState([]);
|
||||
|
||||
// ✅ Chargement initial des missions depuis localStorage ou WordPress
|
||||
useEffect(() => {
|
||||
const loadMissions = async () => {
|
||||
try {
|
||||
// 1️⃣ Vérifier si localStorage contient des missions
|
||||
const storedMissions = JSON.parse(localStorage.getItem("missions"));
|
||||
|
||||
if (storedMissions && storedMissions.length > 0) {
|
||||
setMissions(storedMissions);
|
||||
console.log("✅ Missions chargées depuis localStorage !");
|
||||
} else {
|
||||
// 2️⃣ Si localStorage est vide, récupérer les missions depuis WordPress
|
||||
const response = await api.get("wp/v2/pages/13?_fields=acf");
|
||||
|
||||
if (response.data.acf?.mission && Array.isArray(response.data.acf.mission)) {
|
||||
setMissions(response.data.acf.mission);
|
||||
localStorage.setItem("missions", JSON.stringify(response.data.acf.mission)); // Sauvegarde en local
|
||||
console.log("✅ Missions chargées depuis WordPress !");
|
||||
} else {
|
||||
// 3️⃣ Si aucune mission dans WordPress, utiliser des valeurs par défaut
|
||||
const defaultMissions = [
|
||||
{ title: "📌 Mission 1", details: "Détails de la mission 1" },
|
||||
{ title: "📌 Mission 2", details: "Détails de la mission 2" },
|
||||
{ title: "📌 Mission 3", details: "Détails de la mission 3" },
|
||||
];
|
||||
setMissions(defaultMissions);
|
||||
localStorage.setItem("missions", JSON.stringify(defaultMissions));
|
||||
console.log("⚠️ Aucune mission trouvée, valeurs par défaut utilisées !");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Erreur lors du chargement des missions :", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadMissions();
|
||||
}, []);
|
||||
|
||||
// ✅ Mise à jour automatique de localStorage quand missions change
|
||||
useEffect(() => {
|
||||
if (missions.length > 0) {
|
||||
localStorage.setItem("missions", JSON.stringify(missions));
|
||||
}
|
||||
}, [missions]);
|
||||
|
||||
// ✅ Gestion des changements depuis d'autres onglets/navigateurs
|
||||
useEffect(() => {
|
||||
const handleStorageChange = () => {
|
||||
const updatedMissions = JSON.parse(localStorage.getItem("missions")) || [];
|
||||
setMissions(updatedMissions);
|
||||
};
|
||||
|
||||
window.addEventListener("storage", handleStorageChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("storage", handleStorageChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ✅ Gestion des accordéons
|
||||
const handleAccordionChange = (panel) => (event, isExpanded) => {
|
||||
setExpanded(isExpanded ? panel : false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
py: 6,
|
||||
px: 4,
|
||||
backgroundColor: "#f9f9f9",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ py: 6, px: 4, backgroundColor: "#f9f9f9" }}>
|
||||
<Grid container spacing={4}>
|
||||
{/* Left Column */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Box>
|
||||
{/* Titre et sous-titre */}
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
fontSize: { xs: "2rem", md: "2.4rem", lg: "3rem" },
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2" sx={{ fontWeight: "bold", fontSize: { xs: "2rem", md: "2.4rem", lg: "3rem" } }}>
|
||||
{constructTitle}{" "}
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
fontSize: { xs: "2rem", md: "2rem", lg: "2rem" },
|
||||
|
||||
color: "#0e467f",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
<Typography component="span" sx={{ fontSize: { xs: "2rem", md: "2rem", lg: "2rem" }, color: "#0e467f", fontWeight: "bold" }}>
|
||||
{constructSubtitle}
|
||||
</Typography>
|
||||
</Typography>
|
||||
|
||||
{/* Secteurs */}
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
mt: 2,
|
||||
fontWeight: "bold",
|
||||
color: "#0e467f",
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" sx={{ mt: 2, fontWeight: "bold", color: "#0e467f" }}>
|
||||
Secteurs : {constructSector}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
mt: 2,
|
||||
fontSize: { xs: "1rem", md: "1.2rem" },
|
||||
lineHeight: 1.6,
|
||||
color: "#666",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: constructText }}
|
||||
/>
|
||||
<Typography variant="body1" sx={{ mt: 2, fontSize: { xs: "1rem", md: "1.2rem" }, lineHeight: 1.6, color: "#666" }} dangerouslySetInnerHTML={{ __html: constructText }} />
|
||||
|
||||
{/* Cadre résumé */}
|
||||
<Card
|
||||
sx={{
|
||||
mt: 4,
|
||||
backgroundColor: "#0e467f",
|
||||
color: "#fff",
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)",
|
||||
}}
|
||||
>
|
||||
<Card sx={{ mt: 4, backgroundColor: "#0e467f", color: "#fff", borderRadius: "12px", boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)" }}>
|
||||
<CardContent>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontSize: "1.1rem",
|
||||
lineHeight: 1.6,
|
||||
textAlign: "center",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: constructNoteText }}
|
||||
/>
|
||||
<Typography variant="body1" sx={{ fontSize: "1.1rem", lineHeight: 1.6, textAlign: "center" }} dangerouslySetInnerHTML={{ __html: constructNoteText }} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
@@ -103,54 +118,36 @@ const ConstructSection = ({
|
||||
|
||||
{/* Right Column */}
|
||||
<Grid item xs={12} md={6}>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "12px",
|
||||
boxShadow: "0 8px 20px rgba(0, 0, 0, 0.1)",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
mt: 3,
|
||||
mb: 2,
|
||||
color: "#0e467f",
|
||||
}}
|
||||
>
|
||||
{missionTitle}
|
||||
</Typography>
|
||||
<Box sx={{ backgroundColor: "#fff", padding: 3 }}>
|
||||
{/* ✅ Correction de la duplication du titre */}
|
||||
{missionTitle && (
|
||||
<Typography variant="h3" sx={{ fontWeight: "bold", textAlign: "center", mt: 3, mb: 2, color: "#0e467f" }}>
|
||||
{missionTitle}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
height: "400px",
|
||||
backgroundImage: `url('https://via.placeholder.com/600x400')`, // Replace with the image URL or dynamic data
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
}}
|
||||
>
|
||||
{missionItems.map((item, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: item.top,
|
||||
left: item.left,
|
||||
transform: "translate(-50%, -50%)",
|
||||
backgroundColor: "#0e467f",
|
||||
color: "#fff",
|
||||
padding: "6px 12px",
|
||||
borderRadius: "20px",
|
||||
fontSize: "0.9rem",
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{/* ✅ Accordéon dynamique */}
|
||||
{missions.length > 0 ? (
|
||||
missions.map((mission, index) => (
|
||||
<Accordion key={index} expanded={expanded === `panel${index}`} onChange={handleAccordionChange(`panel${index}`)}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography variant="h6">{mission.title}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography>{mission.details}</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))
|
||||
) : (
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Aucune mission définie.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{/* Background Image et Items */}
|
||||
<Box sx={{ position: "relative", height: "auto", mt: 3 }}>
|
||||
{missionItems?.map((item, index) => (
|
||||
<Box key={index} sx={{ position: "absolute", top: item.top, left: item.left, transform: "translate(-50%, -50%)", backgroundColor: "#0e467f", color: "#fff", padding: "6px 12px", borderRadius: "20px", fontSize: "0.9rem", fontWeight: "bold", textAlign: "center" }}>
|
||||
{item.label}
|
||||
</Box>
|
||||
))}
|
||||
|
||||
@@ -12,6 +12,19 @@ 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("");
|
||||
@@ -20,6 +33,7 @@ const GestionPageAccueil = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [successMessage, setSuccessMessage] = useState("");
|
||||
const [missions, setMissions] = useState([]);
|
||||
|
||||
// ✅ Vérification de l'authentification (Redirection si non connecté)
|
||||
useEffect(() => {
|
||||
@@ -71,23 +85,24 @@ const GestionPageAccueil = () => {
|
||||
}, []);
|
||||
|
||||
// ✅ Mise à jour des champs ACF
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const newData = {
|
||||
hero_title: heroTitle,
|
||||
hero_text: heroText,
|
||||
};
|
||||
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
|
||||
};
|
||||
|
||||
console.log("📢 Données envoyées pour mise à jour ACF :", newData);
|
||||
await updateHomePageACF(13, newData);
|
||||
await updateHomePageACF(13, newData);
|
||||
localStorage.setItem("missions", JSON.stringify(missions)); // ✅ Sauvegarde locale
|
||||
|
||||
setSuccessMessage("✅ Modifications enregistrées avec succès !");
|
||||
setTimeout(() => setSuccessMessage(""), 3000);
|
||||
} catch (error) {
|
||||
console.error("❌ Erreur mise à jour :", error);
|
||||
setError("⚠ Impossible de mettre à jour les champs ACF.");
|
||||
}
|
||||
};
|
||||
setSuccessMessage("✅ Modifications enregistrées avec succès !");
|
||||
setTimeout(() => setSuccessMessage(""), 3000);
|
||||
} catch (error) {
|
||||
console.error("❌ Erreur mise à jour :", error);
|
||||
setError("⚠ Impossible de mettre à jour les champs ACF.");
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ Gérer le téléversement d’une nouvelle image Hero
|
||||
const handleImageUpload = async (event) => {
|
||||
@@ -105,11 +120,69 @@ const GestionPageAccueil = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const storedMissions = JSON.parse(localStorage.getItem("missions")) || [
|
||||
{ title: "📌 Mission 1", details: "Détails de la mission 1" },
|
||||
{ title: "📌 Mission 2", details: "Détails de la mission 2" },
|
||||
{ title: "📌 Mission 3", details: "Détails de la mission 3" },
|
||||
];
|
||||
setMissions(storedMissions);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
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,
|
||||
{ title: "Nouvelle mission", details: "Détails de la nouvelle mission" },
|
||||
]);
|
||||
};
|
||||
|
||||
// ✅ Modifier une mission
|
||||
const updateMission = (index, field, value) => {
|
||||
const updatedMissions = missions.map((mission, i) =>
|
||||
i === index ? { ...mission, [field]: value } : mission
|
||||
);
|
||||
setMissions(updatedMissions);
|
||||
};
|
||||
|
||||
// ✅ Supprimer une mission
|
||||
const deleteMission = (index) => {
|
||||
setMissions(missions.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
return (
|
||||
<Container maxWidth="md" sx={{ mt: 16, mb:5 }}>
|
||||
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}>
|
||||
<Container maxWidth="md" sx={{ mt: 16, mb: 5 }}>
|
||||
<Paper
|
||||
elevation={6}
|
||||
sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}
|
||||
>
|
||||
{/* ✅ En-tête */}
|
||||
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3, color: "#0e467f" }}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
mb: 3,
|
||||
color: "#0e467f",
|
||||
}}
|
||||
>
|
||||
🏠 Gestion de la Page d'Accueil
|
||||
</Typography>
|
||||
|
||||
@@ -160,7 +233,12 @@ const GestionPageAccueil = () => {
|
||||
)}
|
||||
|
||||
{/* ✅ Bouton pour changer l'image */}
|
||||
<Button variant="contained" component="label" fullWidth sx={{ mb: 2 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
component="label"
|
||||
fullWidth
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
📸 Modifier l'Image Hero
|
||||
<input type="file" hidden onChange={handleImageUpload} />
|
||||
</Button>
|
||||
@@ -195,7 +273,7 @@ const GestionPageAccueil = () => {
|
||||
mt: 3,
|
||||
fontSize: "1.1rem",
|
||||
fontWeight: "bold",
|
||||
color:"#ffffff",
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#0e467f",
|
||||
"&:hover": { backgroundColor: "#093a6b" },
|
||||
}}
|
||||
@@ -209,13 +287,71 @@ const GestionPageAccueil = () => {
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
fullWidth
|
||||
sx={{ mt: 2, fontSize: "1rem", fontWeight: "bold", borderColor: "#0e467f", color: "#0e467f" }}
|
||||
sx={{
|
||||
mt: 2,
|
||||
fontSize: "1rem",
|
||||
fontWeight: "bold",
|
||||
borderColor: "#0e467f",
|
||||
color: "#0e467f",
|
||||
}}
|
||||
onClick={() => navigate("/admin/dashboard")}
|
||||
>
|
||||
⬅ Retour au Dashboard
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* ✅ Gestion de l'Accordéon */}
|
||||
<Paper elevation={3} sx={{ marginTop: "30px", padding: "20px" }}>
|
||||
<Typography variant="h4" sx={{ marginBottom: "10px" }}>
|
||||
Gestion de l'Accordéon page d'accueil
|
||||
</Typography>
|
||||
|
||||
{missions.map((mission, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
marginBottom: 2,
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
label="Titre"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={mission.title}
|
||||
onChange={(e) => updateMission(index, "title", e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
label="Détails"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={mission.details}
|
||||
onChange={(e) =>
|
||||
updateMission(index, "details", e.target.value)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => deleteMission(index)}
|
||||
variant="contained"
|
||||
color="error"
|
||||
>
|
||||
❌ Supprimer
|
||||
</Button>
|
||||
</Box>
|
||||
))}
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={addMission}
|
||||
sx={{ marginTop: "10px" }}
|
||||
>
|
||||
➕ Ajouter une mission
|
||||
</Button>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -55,7 +55,7 @@ const Posts = () => {
|
||||
return (
|
||||
<Box sx={{ padding: "40px 20px" }}>
|
||||
<Typography variant="h2" sx={{ fontWeight: "bold", textAlign: "center", mb: 4, mt:5 }}>
|
||||
Nos Articles
|
||||
IN3 Bureau d'études & Maîtrise d'oeuvre
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={4} justifyContent="center">
|
||||
|
||||
+4949
File diff suppressed because one or more lines are too long
@@ -290,3 +290,18 @@ add_action('rest_api_init', function () {
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// accordeon
|
||||
function update_acf_mission($post, $request) {
|
||||
if (!function_exists('update_field')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params = $request->get_json_params();
|
||||
|
||||
if (isset($params['mission']) && is_array($params['mission'])) {
|
||||
update_field('mission', $params['mission'], $post->ID);
|
||||
}
|
||||
}
|
||||
|
||||
add_action('rest_after_insert_page', 'update_acf_mission', 10, 2);
|
||||
Reference in New Issue
Block a user