add accordeon auto page home

This commit is contained in:
sebvtl728
2025-02-04 17:05:12 +01:00
parent b24600acd2
commit 9ceb17a6f4
6 changed files with 5236 additions and 139 deletions
Vendored
BIN
View File
Binary file not shown.
+108 -111
View File
@@ -1,12 +1,16 @@
import React from "react"; import React, { useState, useEffect } from "react";
import { import {
Box, Box,
Typography, Typography,
Button,
Grid, Grid,
Card, Card,
CardContent, CardContent,
Accordion,
AccordionSummary,
AccordionDetails,
} from "@mui/material"; } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import api from "../api";
const ConstructSection = ({ const ConstructSection = ({
constructTitle, constructTitle,
@@ -17,85 +21,96 @@ const ConstructSection = ({
missionTitle, missionTitle,
missionItems, 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 ( return (
<Box <Box sx={{ py: 6, px: 4, backgroundColor: "#f9f9f9" }}>
sx={{
py: 6,
px: 4,
backgroundColor: "#f9f9f9",
}}
>
<Grid container spacing={4}> <Grid container spacing={4}>
{/* Left Column */} {/* Left Column */}
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Box> <Box>
{/* Titre et sous-titre */} <Typography variant="h2" sx={{ fontWeight: "bold", fontSize: { xs: "2rem", md: "2.4rem", lg: "3rem" } }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
fontSize: { xs: "2rem", md: "2.4rem", lg: "3rem" },
display: "flex",
flexDirection: "column",
}}
>
{constructTitle}{" "} {constructTitle}{" "}
<Typography <Typography component="span" sx={{ fontSize: { xs: "2rem", md: "2rem", lg: "2rem" }, color: "#0e467f", fontWeight: "bold" }}>
component="span"
sx={{
fontSize: { xs: "2rem", md: "2rem", lg: "2rem" },
color: "#0e467f",
fontWeight: "bold",
}}
>
{constructSubtitle} {constructSubtitle}
</Typography> </Typography>
</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} Secteurs : {constructSector}
</Typography> </Typography>
<Typography <Typography variant="body1" sx={{ mt: 2, fontSize: { xs: "1rem", md: "1.2rem" }, lineHeight: 1.6, color: "#666" }} dangerouslySetInnerHTML={{ __html: constructText }} />
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> <CardContent>
<Typography <Typography variant="body1" sx={{ fontSize: "1.1rem", lineHeight: 1.6, textAlign: "center" }} dangerouslySetInnerHTML={{ __html: constructNoteText }} />
variant="body1"
sx={{
fontSize: "1.1rem",
lineHeight: 1.6,
textAlign: "center",
}}
dangerouslySetInnerHTML={{ __html: constructNoteText }}
/>
</CardContent> </CardContent>
</Card> </Card>
</Box> </Box>
@@ -103,54 +118,36 @@ const ConstructSection = ({
{/* Right Column */} {/* Right Column */}
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Box <Box sx={{ backgroundColor: "#fff", padding: 3 }}>
sx={{ {/* ✅ Correction de la duplication du titre */}
backgroundColor: "#fff", {missionTitle && (
borderRadius: "12px", <Typography variant="h3" sx={{ fontWeight: "bold", textAlign: "center", mt: 3, mb: 2, color: "#0e467f" }}>
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} {missionTitle}
</Typography> </Typography>
)}
<Box {/* ✅ Accordéon dynamique */}
sx={{ {missions.length > 0 ? (
position: "relative", missions.map((mission, index) => (
height: "400px", <Accordion key={index} expanded={expanded === `panel${index}`} onChange={handleAccordionChange(`panel${index}`)}>
backgroundImage: `url('https://via.placeholder.com/600x400')`, // Replace with the image URL or dynamic data <AccordionSummary expandIcon={<ExpandMoreIcon />}>
backgroundSize: "cover", <Typography variant="h6">{mission.title}</Typography>
backgroundPosition: "center", </AccordionSummary>
}} <AccordionDetails>
> <Typography>{mission.details}</Typography>
{missionItems.map((item, index) => ( </AccordionDetails>
<Box </Accordion>
key={index} ))
sx={{ ) : (
position: "absolute", <Typography variant="body2" color="textSecondary">
top: item.top, Aucune mission définie.
left: item.left, </Typography>
transform: "translate(-50%, -50%)", )}
backgroundColor: "#0e467f",
color: "#fff", {/* Background Image et Items */}
padding: "6px 12px", <Box sx={{ position: "relative", height: "auto", mt: 3 }}>
borderRadius: "20px", {missionItems?.map((item, index) => (
fontSize: "0.9rem", <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" }}>
fontWeight: "bold",
textAlign: "center",
}}
>
{item.label} {item.label}
</Box> </Box>
))} ))}
@@ -12,6 +12,19 @@ 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("");
@@ -20,6 +33,7 @@ const GestionPageAccueil = () => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [successMessage, setSuccessMessage] = useState(""); const [successMessage, setSuccessMessage] = useState("");
const [missions, setMissions] = useState([]);
// ✅ Vérification de l'authentification (Redirection si non connecté) // ✅ Vérification de l'authentification (Redirection si non connecté)
useEffect(() => { useEffect(() => {
@@ -74,12 +88,13 @@ const GestionPageAccueil = () => {
const handleSave = async () => { const handleSave = async () => {
try { try {
const newData = { const newData = {
hero_title: heroTitle, hero_title: heroTitle, // ✅ Enregistre le H1
hero_text: heroText, 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 !"); setSuccessMessage("✅ Modifications enregistrées avec succès !");
setTimeout(() => setSuccessMessage(""), 3000); setTimeout(() => setSuccessMessage(""), 3000);
@@ -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 ( return (
<Container maxWidth="md" sx={{ mt: 16, mb: 5 }}> <Container maxWidth="md" sx={{ mt: 16, mb: 5 }}>
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}> <Paper
elevation={6}
sx={{ padding: 4, borderRadius: 3, backgroundColor: "#f8f9fa" }}
>
{/* ✅ En-tête */} {/* ✅ 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 🏠 Gestion de la Page d'Accueil
</Typography> </Typography>
@@ -160,7 +233,12 @@ const GestionPageAccueil = () => {
)} )}
{/* ✅ Bouton pour changer l'image */} {/* ✅ 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 📸 Modifier l'Image Hero
<input type="file" hidden onChange={handleImageUpload} /> <input type="file" hidden onChange={handleImageUpload} />
</Button> </Button>
@@ -209,13 +287,71 @@ const GestionPageAccueil = () => {
variant="outlined" variant="outlined"
color="secondary" color="secondary"
fullWidth 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")} onClick={() => navigate("/admin/dashboard")}
> >
Retour au Dashboard Retour au Dashboard
</Button> </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> </Paper>
</Container> </Container>
); );
+1 -1
View File
@@ -55,7 +55,7 @@ const Posts = () => {
return ( return (
<Box sx={{ padding: "40px 20px" }}> <Box sx={{ padding: "40px 20px" }}>
<Typography variant="h2" sx={{ fontWeight: "bold", textAlign: "center", mb: 4, mt:5 }}> <Typography variant="h2" sx={{ fontWeight: "bold", textAlign: "center", mb: 4, mt:5 }}>
Nos Articles IN3 Bureau d'études & Maîtrise d'oeuvre
</Typography> </Typography>
<Grid container spacing={4} justifyContent="center"> <Grid container spacing={4} justifyContent="center">
+4949
View File
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);