Gestion de page ACF

This commit is contained in:
sebvtl728 2025-02-09 19:27:56 +01:00
parent 20c2e3500d
commit d1cee7b3ae
7 changed files with 436 additions and 17 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,112 @@
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import {
Box, Typography, Button, Table, TableBody, TableCell,
TableContainer, TableHead, TableRow, Paper, TextField
} from "@mui/material";
import { Edit, ArrowBack } from "@mui/icons-material";
import api from "../api";
import { getToken } from "../auth";
const GestionPagesACF = () => {
const [pages, setPages] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const navigate = useNavigate();
// Vérifie l'authentification
useEffect(() => {
if (!getToken()) {
navigate("/admin/login");
}
}, [navigate]);
// Récupère les pages contenant des champs ACF
useEffect(() => {
const fetchPages = async () => {
try {
const response = await api.get("wp/v2/pages?_fields=id,title,acf");
const pagesData = response.data;
// Filtrer les pages qui ont au moins un champ ACF
const pagesWithACF = pagesData.filter(page => page.acf && Object.keys(page.acf).length > 0);
setPages(pagesWithACF);
} catch (error) {
console.error("❌ Erreur chargement des pages :", error);
}
};
fetchPages();
}, []);
// Filtrage des pages par recherche
const filteredPages = pages.filter(page =>
page.title.rendered.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<Box sx={{ padding: "40px 20px" }}>
{/* ✅ En-tête */}
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 4, mt:5 }}>
<Button startIcon={<ArrowBack />} variant="outlined" color="secondary" onClick={() => navigate("/admin/dashboard")}>
Retour au Dashboard
</Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", flexGrow: 1 }}>
Gestion des Pages ACF
</Typography>
</Box>
{/* ✅ Recherche */}
<TextField
label="Rechercher une page..."
variant="outlined"
fullWidth
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
sx={{ mb: 3 }}
/>
{/* ✅ Tableau des pages */}
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#0e467f" }}>
<TableRow>
<TableCell sx={{ color: "white", fontWeight: "bold" }}>Titre</TableCell>
<TableCell sx={{ color: "white", fontWeight: "bold", textAlign: "center" }}>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredPages.length > 0 ? (
filteredPages.map((page) => (
<TableRow key={page.id}>
{/* ✅ Titre de la page */}
<TableCell sx={{ fontWeight: "bold" }}>{page.title.rendered}</TableCell>
{/* ✅ Bouton Modifier */}
<TableCell sx={{ textAlign: "center" }}>
<Button
startIcon={<Edit />}
variant="outlined"
color="warning"
onClick={() => navigate(`/admin/edit-page/${page.id}`)}
>
Modifier
</Button>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={2} sx={{ textAlign: "center", py: 2 }}>
Aucune page avec champs ACF trouvée.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Box>
);
};
export default GestionPagesACF;

View File

@ -34,7 +34,8 @@ function Dashboard() {
<Box
sx={{
minHeight: "100vh",
backgroundImage: "url('https://source.unsplash.com/1600x900/?technology,office')",
backgroundImage:
"url('https://source.unsplash.com/1600x900/?technology,office')",
backgroundSize: "cover",
backgroundPosition: "center",
display: "flex",
@ -141,10 +142,41 @@ function Dashboard() {
</CardContent>
</Card>
</Grid>
{/* ✅ Gérer les Pages ACF - Nouvelle carte */}
<Grid item xs={12} sm={6}>
<Card
onClick={() => navigate("/admin/pages-acf")}
sx={{
cursor: "pointer",
textAlign: "center",
padding: 2,
transition: "0.3s",
"&:hover": {
backgroundColor: "#0e467f",
color: "#ffffff",
transform: "scale(1.05)",
boxShadow: 8,
},
"&:hover svg": {
fill: "#ffffff",
},
}}
>
<CardContent>
<IconButton sx={{ fontSize: 40, color: "#0e467f" }}>
<ArticleIcon fontSize="inherit" />
</IconButton>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>
Gérer les Pages ACF
</Typography>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</Box>
);
}
export default Dashboard;
export default Dashboard;

View File

@ -0,0 +1,216 @@
import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { getToken } from "../../auth";
import { getPageById, updatePageACF } from "../../wordpress";
import {
Box,
Container,
Typography,
TextField,
Button,
Paper,
CircularProgress,
} from "@mui/material";
import { Add, Delete, ArrowBack, Save } from "@mui/icons-material";
function EditPageACF() {
const { id } = useParams();
const navigate = useNavigate();
const [acfFields, setAcfFields] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [successMessage, setSuccessMessage] = useState("");
// Vérifier l'authentification
useEffect(() => {
if (!getToken()) {
navigate("/admin/login");
}
}, [navigate]);
// Récupérer les champs ACF des pages
useEffect(() => {
const fetchPage = async () => {
try {
const pageData = await getPageById(id);
console.log("📢 Données ACF reçues :", JSON.stringify(pageData.acf, null, 2));
if (!pageData.acf || Object.keys(pageData.acf).length === 0) {
setError("❌ Aucun champ ACF disponible pour cette page.");
setLoading(false);
return;
}
setAcfFields({
...pageData.acf,
faq_list: Array.isArray(pageData.acf?.faq_list) ? pageData.acf.faq_list : [],
});
setLoading(false);
} catch (error) {
console.error("❌ Erreur chargement des champs ACF :", error);
setError("⚠ Impossible de charger les champs ACF.");
setLoading(false);
}
};
fetchPage();
}, [id]);
// Mise à jour des champs ACF
const handleUpdateACF = async (e) => {
e.preventDefault();
const updatedACF = {
...acfFields,
faq_list: Array.isArray(acfFields?.faq_list) ? acfFields.faq_list : [],
};
try {
await updatePageACF(id, updatedACF);
setSuccessMessage("✅ Modifications enregistrées !");
setTimeout(() => navigate("/admin/pages-acf"), 2000);
} catch (error) {
console.error("❌ Erreur mise à jour ACF :", error);
setError("⚠ Impossible de mettre à jour les champs ACF.");
}
};
// Gérer la modification des champs ACF (y compris objets et tableaux)
const handleFieldChange = (field, value) => {
setAcfFields((prevFields) => ({
...prevFields,
[field]: value,
}));
};
// Ajouter, modifier et supprimer des entrées de FAQ
const handleFAQChange = (index, field, value) => {
const updatedFAQ = [...acfFields.faq_list];
updatedFAQ[index][field] = value;
setAcfFields({ ...acfFields, faq_list: updatedFAQ });
};
const addFAQ = () => {
setAcfFields({
...acfFields,
faq_list: [...acfFields.faq_list, { question: "", answer: "" }],
});
};
const deleteFAQ = (index) => {
setAcfFields({
...acfFields,
faq_list: acfFields.faq_list.filter((_, i) => i !== index),
});
};
if (loading) return <Typography>Chargement...</Typography>;
return (
<Box
sx={{
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundImage: "url('https://source.unsplash.com/1600x900/?technology')",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<Container maxWidth="sm">
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}>
<Button
startIcon={<ArrowBack />}
variant="outlined"
color="secondary"
onClick={() => navigate("/admin/pages-acf")}
sx={{ mb: 2 }}
>
Retour à la gestion des pages
</Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}>
Modifier les Champs ACF
</Typography>
{error && <Typography textAlign="center" color="error" sx={{ mb: 2 }}>{error}</Typography>}
{successMessage && <Typography sx={{ color: "green", textAlign: "center", mb: 2 }}>{successMessage}</Typography>}
<form onSubmit={handleUpdateACF}>
{/* ✅ Affichage dynamique des champs ACF */}
{Object.keys(acfFields).map((field) => {
const value = acfFields[field];
if (typeof value === "object" && !Array.isArray(value)) {
return (
<Box key={field} sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
{field.replace(/_/g, " ")}
</Typography>
{Object.keys(value).map((subField) => (
<TextField
key={subField}
label={subField.replace(/_/g, " ")}
fullWidth
variant="outlined"
value={value[subField] || ""}
onChange={(e) =>
handleFieldChange(field, { ...value, [subField]: e.target.value })
}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/>
))}
</Box>
);
} else if (Array.isArray(value)) {
return (
<Box key={field} sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
{field.replace(/_/g, " ")}
</Typography>
{value.map((item, index) => (
<Box key={index} sx={{ border: "1px solid #ccc", padding: 2, borderRadius: 2, mb: 2 }}>
{Object.keys(item).map((subField) => (
<TextField
key={subField}
label={subField.replace(/_/g, " ")}
fullWidth
variant="outlined"
value={item[subField] || ""}
onChange={(e) => {
const updatedArray = [...value];
updatedArray[index][subField] = e.target.value;
setAcfFields({ ...acfFields, [field]: updatedArray });
}}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/>
))}
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteFAQ(index)}>
Supprimer cette entrée
</Button>
</Box>
))}
<Button startIcon={<Add />} variant="contained" color="primary" onClick={addFAQ} sx={{ mb: 2 }}>
Ajouter une entrée
</Button>
</Box>
);
} else {
return (
<TextField key={field} label={field.replace(/_/g, " ")} fullWidth variant="outlined" value={value || ""} onChange={(e) => handleFieldChange(field, e.target.value)} sx={{ mb: 2, backgroundColor: "#fff", borderRadius: "5px" }} />
);
}
})}
<Button type="submit" variant="contained" color="primary" fullWidth startIcon={<Save />} sx={{ mt: 3 }}>
Enregistrer les modifications
</Button>
</form>
</Paper>
</Container>
</Box>
);
}
export default EditPageACF;

View File

@ -9,17 +9,18 @@ import NotFound from "./components/Pages/NotFound.jsx";
import GestionArticles from "./components/Pages/GestionArticles";
import EditPost from "./components/EditPost";
import GestionPageAccueil from "./components/Pages/GestionPageAccueil";
import GestionPagesACF from "./components/GestionPagesACF";
import EditPageACF from "./components/Pages/EditPageACF";
// Lazy loading des pages
const Home = lazy(() => import('./components/Pages/Home.jsx'));
const About = lazy(() => import('./components/Pages/About'));
const Contact = lazy(() => import('./components/Pages/Contact'));
const Post = lazy(() => import('./components/Pages/Post'));
const PostDetails = lazy(() => import('./components/PostDetails')) ;
const Search = lazy(() => import('./components/Pages/Search')); // Nouveau composant pour la recherche
const BureauEtude = lazy(() => import('./components/Pages/BureauEtude'));
const CreatePost = lazy(() => import('./components/Pages/CreatePost'));
const Home = lazy(() => import("./components/Pages/Home.jsx"));
const About = lazy(() => import("./components/Pages/About"));
const Contact = lazy(() => import("./components/Pages/Contact"));
const Post = lazy(() => import("./components/Pages/Post"));
const PostDetails = lazy(() => import("./components/PostDetails"));
const Search = lazy(() => import("./components/Pages/Search")); // Nouveau composant pour la recherche
const BureauEtude = lazy(() => import("./components/Pages/BureauEtude"));
const CreatePost = lazy(() => import("./components/Pages/CreatePost"));
const PostList = lazy(() => import("./components/Pages/PostList"));
const Login = lazy(() => import("./components/Pages/Login"));
const Dashboard = lazy(() => import("./components/Pages/Dashboard"));
@ -32,7 +33,7 @@ import ServiceQuatre from "./components/Pages/ServiceQuatre.jsx";
// Import des composants globaux
import Header from "./components/Header";
import Footer from './components/Footer';
import Footer from "./components/Footer";
function YourApp() {
return (
@ -46,11 +47,17 @@ function YourApp() {
<Route path="/bureauEtude" element={<BureauEtude />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/search" element={<Search />} /> {/* Route pour les recherches */}
<Route path="/search" element={<Search />} />{" "}
{/* Route pour les recherches */}
{/* Routes pour les services */}
<Route path="/services/prestation-maitrise-oeuvre" element={<ServiceUn />} />
<Route path="/services/structure-beton-charpente-metallique-bois" element={<ServiceDeux />} />
<Route
path="/services/prestation-maitrise-oeuvre"
element={<ServiceUn />}
/>
<Route
path="/services/structure-beton-charpente-metallique-bois"
element={<ServiceDeux />}
/>
<Route path="/services/electricite" element={<ServiceTrois />} />
<Route path="/services/ssi" element={<ServiceQuatre />} />
<Route path="/admin/create-post" element={<CreatePost />} />
@ -61,6 +68,8 @@ function YourApp() {
<Route path="/admin/gestion-articles" element={<GestionArticles />} />
<Route path="/admin/gestion-page-accueil" element={<GestionPageAccueil />} />
<Route path="*" element={<NotFound />} />
<Route path="/admin/pages-acf" element={<GestionPagesACF />} />
<Route path="/admin/edit-page/:id" element={<EditPageACF />} />
</Routes>
<Footer />
</Router>

View File

@ -211,4 +211,54 @@ export async function updateHomePageACF(pageId, newData) {
console.error("❌ Erreur mise à jour ACF :", error);
throw error;
}
}
/**
* 🔹 Récupère une page WordPress par son ID avec ses champs ACF
* @param {number} pageId - L'ID de la page à récupérer
* @returns {Promise<Object>} - Données de la page avec ACF
*/
export async function getPageById(pageId) {
try {
const response = await axios.get(`${API_URL}/pages/${pageId}?_fields=id,title,acf`);
console.log("📢 Données de la page récupérées :", response.data);
return response.data;
} catch (error) {
console.error("❌ Erreur récupération page :", error.response?.data || error.message);
throw error;
}
}
/**
* 🔹 Met à jour les champs ACF d'une page WordPress
* @param {number} pageId - L'ID de la page à modifier
* @param {object} acfData - Les nouvelles données ACF à mettre à jour
*/
export async function updatePageACF(pageId, acfData) {
const token = getToken();
if (!token) {
console.error("❌ Aucun token trouvé !");
throw new Error("Utilisateur non authentifié.");
}
try {
console.log(`📢 Mise à jour des ACF pour la page ${pageId}...`, acfData);
const response = await axios.post(
`${API_URL}/pages/${pageId}`,
{ acf: acfData }, // Envoi uniquement les champs ACF
{
headers: {
"Authorization": `Basic ${token}`,
"Content-Type": "application/json",
},
}
);
console.log("✅ Mise à jour ACF réussie :", response.data);
return response.data;
} catch (error) {
console.error("❌ Erreur mise à jour ACF :", error.response?.data || error.message);
throw error;
}
}

File diff suppressed because one or more lines are too long