Gestion de page ACF

This commit is contained in:
sebvtl728
2025-02-10 00:52:15 +01:00
parent d1cee7b3ae
commit 4a4d7104ac
4 changed files with 321 additions and 112 deletions
+93 -52
View File
@@ -1,88 +1,129 @@
import React, { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom"; 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"; import { getToken } from "../auth";
import { fetchPages } from "../wordpress";
import {
Box,
Typography,
Button,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from "@mui/material";
import { Edit, ArrowBack, Add } from "@mui/icons-material";
const GestionPagesACF = () => { const HOME_PAGE_ID = 13; // Remplace 13 par l'ID réel de la page d'accueil
const [pages, setPages] = useState([]);
const [searchTerm, setSearchTerm] = useState(""); function GestionPagesACF() {
const navigate = useNavigate(); const navigate = useNavigate();
const [pages, setPages] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// ✅ Vérifie l'authentification // ✅ Vérifier l'authentification
useEffect(() => { useEffect(() => {
if (!getToken()) { if (!getToken()) {
navigate("/admin/login"); navigate("/admin/login");
} }
}, [navigate]); }, [navigate]);
// ✅ Récupère les pages contenant des champs ACF // ✅ Récupérer les pages ACF et exclure la page d'accueil
useEffect(() => { useEffect(() => {
const fetchPages = async () => { const loadPages = async () => {
try { try {
const response = await api.get("wp/v2/pages?_fields=id,title,acf"); const response = await fetchPages();
const pagesData = response.data; console.log("📢 Données reçues de fetchPages() :", response);
// Filtrer les pages qui ont au moins un champ ACF if (!Array.isArray(response)) {
const pagesWithACF = pagesData.filter(page => page.acf && Object.keys(page.acf).length > 0); console.error(
"❌ La réponse `fetchPages()` n'est pas un tableau :",
response
);
setError("❌ Impossible de charger les pages ACF.");
setLoading(false);
return;
}
setPages(pagesWithACF); // ✅ Exclure la page d'accueil
const filteredPages = response.filter(
(page) => page.id !== HOME_PAGE_ID
);
setPages(filteredPages);
} catch (error) { } catch (error) {
console.error("❌ Erreur chargement des pages :", error); console.error("❌ Erreur chargement pages ACF :", error);
setError("Impossible de charger les pages.");
} finally {
setLoading(false);
} }
}; };
fetchPages(); loadPages();
}, []); }, []);
// ✅ Filtrage des pages par recherche if (loading) return <Typography>Chargement...</Typography>;
const filteredPages = pages.filter(page => if (error) return <Typography color="error">{error}</Typography>;
page.title.rendered.toLowerCase().includes(searchTerm.toLowerCase())
);
return ( return (
<Box sx={{ padding: "40px 20px" }}> <Box sx={{ padding: "40px 20px", mt: 6 }}>
{/* ✅ En-tête */} <Box
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 4, mt:5 }}> sx={{
<Button startIcon={<ArrowBack />} variant="outlined" color="secondary" onClick={() => navigate("/admin/dashboard")}> 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 Retour au Dashboard
</Button> </Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", flexGrow: 1 }}> <Typography
Gestion des Pages ACF variant="h4"
sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}
>
📄 Gestion des Pages
</Typography> </Typography>
<Button
startIcon={<Add />}
variant="contained"
color="primary"
onClick={() =>
window.open("https://it.sveitl.synology.me/", "_blank")
}
>
Ticket
</Button>
</Box> </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}> <TableContainer component={Paper}>
<Table> <Table>
<TableHead sx={{ backgroundColor: "#0e467f" }}> <TableHead sx={{ backgroundColor: "#0e467f" }}>
<TableRow> <TableRow>
<TableCell sx={{ color: "white", fontWeight: "bold" }}>Titre</TableCell> <TableCell sx={{ color: "white", fontWeight: "bold" }}>
<TableCell sx={{ color: "white", fontWeight: "bold", textAlign: "center" }}>Actions</TableCell> Titre
</TableCell>
<TableCell
sx={{ color: "white", fontWeight: "bold", textAlign: "center" }}
>
Actions
</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{filteredPages.length > 0 ? ( {pages.length > 0 ? (
filteredPages.map((page) => ( pages.map((page) => (
<TableRow key={page.id}> <TableRow key={page.id}>
{/* ✅ Titre de la page */} <TableCell sx={{ fontWeight: "bold" }}>
<TableCell sx={{ fontWeight: "bold" }}>{page.title.rendered}</TableCell> {page.title.rendered}
</TableCell>
{/* ✅ Bouton Modifier */}
<TableCell sx={{ textAlign: "center" }}> <TableCell sx={{ textAlign: "center" }}>
<Button <Button
startIcon={<Edit />} startIcon={<Edit />}
@@ -98,7 +139,7 @@ const GestionPagesACF = () => {
) : ( ) : (
<TableRow> <TableRow>
<TableCell colSpan={2} sx={{ textAlign: "center", py: 2 }}> <TableCell colSpan={2} sx={{ textAlign: "center", py: 2 }}>
Aucune page avec champs ACF trouvée. Aucune page avec des champs ACF trouvée.
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
@@ -107,6 +148,6 @@ const GestionPagesACF = () => {
</TableContainer> </TableContainer>
</Box> </Box>
); );
}; }
export default GestionPagesACF; export default GestionPagesACF;
+1 -1
View File
@@ -168,7 +168,7 @@ function Dashboard() {
<ArticleIcon fontSize="inherit" /> <ArticleIcon fontSize="inherit" />
</IconButton> </IconButton>
<Typography variant="h6" sx={{ fontWeight: "bold" }}> <Typography variant="h6" sx={{ fontWeight: "bold" }}>
Gérer les Pages ACF Gérer les Pages
</Typography> </Typography>
</CardContent> </CardContent>
</Card> </Card>
+176 -40
View File
@@ -20,6 +20,13 @@ function EditPageACF() {
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 deleteFAQItem = (index) => {
const updatedArray = acfFields.faq?.faq_list.filter((_, i) => i !== index);
setAcfFields({
...acfFields,
faq: { ...acfFields.faq, faq_list: updatedArray },
});
};
// ✅ Vérifier l'authentification // ✅ Vérifier l'authentification
useEffect(() => { useEffect(() => {
@@ -31,26 +38,30 @@ function EditPageACF() {
// ✅ Récupérer les champs ACF des pages // ✅ Récupérer les champs ACF des pages
useEffect(() => { useEffect(() => {
const fetchPage = async () => { const fetchPage = async () => {
try { console.log("📢 Récupération de la page avec ID :", id);
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) { if (!id) {
setError("❌ Aucun champ ACF disponible pour cette page."); setError("❌ L'ID de la page est invalide !");
setLoading(false);
return;
}
const pageData = await getPageById(id);
if (!pageData || !pageData.acf) {
setError("❌ Impossible de charger les champs ACF.");
setLoading(false); setLoading(false);
return; return;
} }
setAcfFields({ setAcfFields({
...pageData.acf, ...pageData.acf,
faq_list: Array.isArray(pageData.acf?.faq_list) ? pageData.acf.faq_list : [], faq_list: Array.isArray(pageData.acf?.faq?.faq_list)
? pageData.acf.faq.faq_list
: [], // ✅ On s'assure d'avoir un tableau vide si ce n'est pas défini
}); });
setLoading(false); setLoading(false);
} catch (error) {
console.error("❌ Erreur chargement des champs ACF :", error);
setError("⚠ Impossible de charger les champs ACF.");
setLoading(false);
}
}; };
fetchPage(); fetchPage();
@@ -113,13 +124,15 @@ function EditPageACF() {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
backgroundImage: "url('https://source.unsplash.com/1600x900/?technology')", backgroundImage:
"url('https://source.unsplash.com/1600x900/?technology')",
backgroundSize: "cover", backgroundSize: "cover",
backgroundPosition: "center", backgroundPosition: "center",
}} }}
> >
<Container maxWidth="sm"> <Container maxWidth="sm">
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}> <Paper elevation={6} sx={{ padding: 4, borderRadius: 3, mt:10 }}>
<Button <Button
startIcon={<ArrowBack />} startIcon={<ArrowBack />}
variant="outlined" variant="outlined"
@@ -130,47 +143,137 @@ function EditPageACF() {
Retour à la gestion des pages Retour à la gestion des pages
</Button> </Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}> <Typography
Modifier les Champs ACF variant="h4"
sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}
>
Modifier les Champs
</Typography> </Typography>
{error && <Typography textAlign="center" color="error" sx={{ mb: 2 }}>{error}</Typography>} {error && (
{successMessage && <Typography sx={{ color: "green", textAlign: "center", mb: 2 }}>{successMessage}</Typography>} <Typography textAlign="center" color="error" sx={{ mb: 2 }}>
{error}
</Typography>
)}
{successMessage && (
<Typography sx={{ color: "green", textAlign: "center", mb: 2 }}>
{successMessage}
</Typography>
)}
<form onSubmit={handleUpdateACF}> <form onSubmit={handleUpdateACF}>
{/* ✅ Affichage dynamique des champs ACF */} {/* ✅ Affichage dynamique des champs ACF */}
{Object.keys(acfFields).map((field) => { {Object.keys(acfFields).map((field) => {
const value = acfFields[field]; const value = acfFields[field];
if (typeof value === "object" && !Array.isArray(value)) { // ✅ Si c'est un objet (comme `faq`), afficher ses sous-champs
if (field === "faq") {
return null; // ✅ On ne veut pas afficher `faq` en tant que champ texte
}
if (field === "faq_list" && Array.isArray(value)) {
const faqData = acfFields.faq?.faq_list || [];
return ( return (
<Box key={field} sx={{ mb: 3 }}> <Box key="faq_list" sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}> <Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
{field.replace(/_/g, " ")} FAQ
</Typography> </Typography>
{Object.keys(value).map((subField) => ( {faqData.map((item, index) => (
<Box
key={index}
sx={{
border: "1px solid #ccc",
padding: 2,
borderRadius: 2,
mb: 2,
position: "relative", // Permet d'aligner le bouton en haut à droite
}}
>
<TextField <TextField
key={subField} label="Question"
label={subField.replace(/_/g, " ")}
fullWidth fullWidth
variant="outlined" variant="outlined"
value={value[subField] || ""} value={item.question || ""}
onChange={(e) => onChange={(e) => {
handleFieldChange(field, { ...value, [subField]: e.target.value }) const updatedArray = [...faqData];
} updatedArray[index].question = e.target.value;
setAcfFields({
...acfFields,
faq: { ...acfFields.faq, faq_list: updatedArray },
});
}}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }} sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/> />
<TextField
label="Réponse"
fullWidth
variant="outlined"
multiline
rows={3}
value={item.answer || ""}
onChange={(e) => {
const updatedArray = [...faqData];
updatedArray[index].answer = e.target.value;
setAcfFields({
...acfFields,
faq: { ...acfFields.faq, faq_list: updatedArray },
});
}}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/>
<Button
startIcon={<Delete />}
variant="outlined"
color="error"
onClick={() => deleteFAQItem(index)}
sx={{
position: "absolute",
top: 10,
right: 10,
}}
>
Supprimer
</Button>
</Box>
))} ))}
<Button
startIcon={<Add />}
variant="contained"
color="primary"
onClick={() => {
setAcfFields({
...acfFields,
faq: {
...acfFields.faq,
faq_list: [...faqData, { question: "", answer: "" }],
},
});
}}
sx={{ mb: 2 }}
>
Ajouter une question
</Button>
</Box> </Box>
); );
} else if (Array.isArray(value)) { }
// ✅ Si c'est un tableau (comme `faq_list`), afficher chaque élément
if (Array.isArray(value)) {
return ( return (
<Box key={field} sx={{ mb: 3 }}> <Box key={field} sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}> <Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
{field.replace(/_/g, " ")} {field.replace(/_/g, " ")}
</Typography> </Typography>
{value.map((item, index) => ( {value.map((item, index) => (
<Box key={index} sx={{ border: "1px solid #ccc", padding: 2, borderRadius: 2, mb: 2 }}> <Box
key={index}
sx={{
border: "1px solid #ccc",
padding: 2,
borderRadius: 2,
mb: 2,
}}
>
{Object.keys(item).map((subField) => ( {Object.keys(item).map((subField) => (
<TextField <TextField
key={subField} key={subField}
@@ -181,29 +284,62 @@ function EditPageACF() {
onChange={(e) => { onChange={(e) => {
const updatedArray = [...value]; const updatedArray = [...value];
updatedArray[index][subField] = e.target.value; updatedArray[index][subField] = e.target.value;
setAcfFields({ ...acfFields, [field]: updatedArray }); setAcfFields({
...acfFields,
[field]: updatedArray,
});
}}
sx={{
mb: 1,
backgroundColor: "#fff",
borderRadius: "5px",
}} }}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/> />
))} ))}
<Button startIcon={<Delete />} variant="outlined" color="error" onClick={() => deleteFAQ(index)}>
Supprimer cette entrée
</Button>
</Box> </Box>
))} ))}
<Button startIcon={<Add />} variant="contained" color="primary" onClick={addFAQ} sx={{ mb: 2 }}> <Button
startIcon={<Add />}
variant="contained"
color="primary"
onClick={() => {
setAcfFields({
...acfFields,
[field]: [...value, {}], // ✅ Ajoute un objet vide
});
}}
sx={{ mb: 2 }}
>
Ajouter une entrée Ajouter une entrée
</Button> </Button>
</Box> </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" }} />
);
} }
// ✅ Gérer les champs simples
return (
<TextField
key={field}
label={field.replace(/_/g, " ")}
fullWidth
variant="outlined"
value={value || ""}
onChange={(e) =>
setAcfFields({ ...acfFields, [field]: e.target.value })
}
sx={{ mb: 2, backgroundColor: "#fff", borderRadius: "5px" }}
/>
);
})} })}
<Button type="submit" variant="contained" color="primary" fullWidth startIcon={<Save />} sx={{ mt: 3 }}> <Button
type="submit"
variant="contained"
color="primary"
fullWidth
startIcon={<Save />}
sx={{ mt: 3 }}
>
Enregistrer les modifications Enregistrer les modifications
</Button> </Button>
</form> </form>
+36 -4
View File
@@ -219,13 +219,19 @@ export async function updateHomePageACF(pageId, newData) {
* @returns {Promise<Object>} - Données de la page avec ACF * @returns {Promise<Object>} - Données de la page avec ACF
*/ */
export async function getPageById(pageId) { export async function getPageById(pageId) {
if (!pageId) {
console.error("❌ Erreur : l'ID de la page est `undefined` !");
return null;
}
try { try {
const response = await axios.get(`${API_URL}/pages/${pageId}?_fields=id,title,acf`); const response = await axios.get(
console.log("📢 Données de la page récupérées :", response.data); `https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages/${pageId}?_fields=id,title,acf`
);
return response.data; return response.data;
} catch (error) { } catch (error) {
console.error("❌ Erreur récupération page :", error.response?.data || error.message); console.error("❌ Erreur récupération page :", error);
throw error; return null;
} }
} }
@@ -262,3 +268,29 @@ export async function updatePageACF(pageId, acfData) {
throw error; throw error;
} }
} }
/**
* 🔹 Récupère la liste des pages WordPress ayant des champs ACF
* @returns {Promise<Array>} - Liste des pages
*/
export async function fetchPages() {
try {
const response = await fetch("https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages?_fields=id,title");
if (!response.ok) {
throw new Error("❌ Erreur lors de la récupération des pages WordPress.");
}
const data = await response.json();
if (!data || typeof data !== "object" || !Array.isArray(data)) {
console.error("❌ Données invalides reçues :", data);
return []; // ✅ Retourne un tableau vide pour éviter toute erreur
}
return data;
} catch (error) {
console.error("❌ Erreur `fetchPages()` :", error);
return []; // ✅ Retourne un tableau vide en cas d'erreur pour éviter le plantage
}
}