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

View File

@ -1,88 +1,129 @@
import React, { useState, useEffect } from "react";
import { 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";
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 [pages, setPages] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const HOME_PAGE_ID = 13; // Remplace 13 par l'ID réel de la page d'accueil
function GestionPagesACF() {
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(() => {
if (!getToken()) {
navigate("/admin/login");
}
}, [navigate]);
// Récupère les pages contenant des champs ACF
// Récupérer les pages ACF et exclure la page d'accueil
useEffect(() => {
const fetchPages = async () => {
const loadPages = async () => {
try {
const response = await api.get("wp/v2/pages?_fields=id,title,acf");
const pagesData = response.data;
const response = await fetchPages();
console.log("📢 Données reçues de fetchPages() :", response);
// 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);
if (!Array.isArray(response)) {
console.error(
"❌ La réponse `fetchPages()` n'est pas un tableau :",
response
);
setError("❌ Impossible de charger les pages ACF.");
setLoading(false);
return;
}
// Exclure la page d'accueil
const filteredPages = response.filter(
(page) => page.id !== HOME_PAGE_ID
);
setPages(filteredPages);
} 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
const filteredPages = pages.filter(page =>
page.title.rendered.toLowerCase().includes(searchTerm.toLowerCase())
);
if (loading) return <Typography>Chargement...</Typography>;
if (error) return <Typography color="error">{error}</Typography>;
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")}>
<Box sx={{ padding: "40px 20px", mt: 6 }}>
<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
variant="h4"
sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}
>
📄 Gestion des Pages
</Typography>
<Button
startIcon={<Add />}
variant="contained"
color="primary"
onClick={() =>
window.open("https://it.sveitl.synology.me/", "_blank")
}
>
Ticket
</Button>
</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>
<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) => (
{pages.length > 0 ? (
pages.map((page) => (
<TableRow key={page.id}>
{/* ✅ Titre de la page */}
<TableCell sx={{ fontWeight: "bold" }}>{page.title.rendered}</TableCell>
{/* ✅ Bouton Modifier */}
<TableCell sx={{ fontWeight: "bold" }}>
{page.title.rendered}
</TableCell>
<TableCell sx={{ textAlign: "center" }}>
<Button
startIcon={<Edit />}
@ -98,7 +139,7 @@ const GestionPagesACF = () => {
) : (
<TableRow>
<TableCell colSpan={2} sx={{ textAlign: "center", py: 2 }}>
Aucune page avec champs ACF trouvée.
Aucune page avec des champs ACF trouvée.
</TableCell>
</TableRow>
)}
@ -107,6 +148,6 @@ const GestionPagesACF = () => {
</TableContainer>
</Box>
);
};
}
export default GestionPagesACF;
export default GestionPagesACF;

View File

@ -168,7 +168,7 @@ function Dashboard() {
<ArticleIcon fontSize="inherit" />
</IconButton>
<Typography variant="h6" sx={{ fontWeight: "bold" }}>
Gérer les Pages ACF
Gérer les Pages
</Typography>
</CardContent>
</Card>

View File

@ -20,6 +20,13 @@ function EditPageACF() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
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
useEffect(() => {
@ -31,26 +38,30 @@ function EditPageACF() {
// 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));
console.log("📢 Récupération de la page avec ID :", id);
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.");
if (!id) {
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);
return;
}
setAcfFields({
...pageData.acf,
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);
};
fetchPage();
@ -113,13 +124,15 @@ function EditPageACF() {
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundImage: "url('https://source.unsplash.com/1600x900/?technology')",
backgroundImage:
"url('https://source.unsplash.com/1600x900/?technology')",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<Container maxWidth="sm">
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}>
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3, mt:10 }}>
<Button
startIcon={<ArrowBack />}
variant="outlined"
@ -130,47 +143,137 @@ function EditPageACF() {
Retour à la gestion des pages
</Button>
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}>
Modifier les Champs ACF
<Typography
variant="h4"
sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}
>
Modifier les Champs
</Typography>
{error && <Typography textAlign="center" color="error" sx={{ mb: 2 }}>{error}</Typography>}
{successMessage && <Typography sx={{ color: "green", textAlign: "center", mb: 2 }}>{successMessage}</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)) {
// 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 (
<Box key={field} sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
{field.replace(/_/g, " ")}
</Typography>
{Object.keys(value).map((subField) => (
<Box key="faq_list" sx={{ mb: 3 }}>
<Typography variant="h6" sx={{ fontWeight: "bold", mb: 1 }}>
FAQ
</Typography>
{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
key={subField}
label={subField.replace(/_/g, " ")}
label="Question"
fullWidth
variant="outlined"
value={value[subField] || ""}
onChange={(e) =>
handleFieldChange(field, { ...value, [subField]: e.target.value })
}
value={item.question || ""}
onChange={(e) => {
const updatedArray = [...faqData];
updatedArray[index].question = e.target.value;
setAcfFields({
...acfFields,
faq: { ...acfFields.faq, faq_list: updatedArray },
});
}}
sx={{ mb: 1, backgroundColor: "#fff", borderRadius: "5px" }}
/>
))}
</Box>
);
} else if (Array.isArray(value)) {
<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>
);
}
// Si c'est un tableau (comme `faq_list`), afficher chaque élément
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 }}>
<Box
key={index}
sx={{
border: "1px solid #ccc",
padding: 2,
borderRadius: 2,
mb: 2,
}}
>
{Object.keys(item).map((subField) => (
<TextField
key={subField}
@ -181,29 +284,62 @@ function EditPageACF() {
onChange={(e) => {
const updatedArray = [...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>
))}
<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
</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" }} />
);
}
// 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
</Button>
</form>
@ -213,4 +349,4 @@ function EditPageACF() {
);
}
export default EditPageACF;
export default EditPageACF;

View File

@ -219,13 +219,19 @@ export async function updateHomePageACF(pageId, newData) {
* @returns {Promise<Object>} - Données de la page avec ACF
*/
export async function getPageById(pageId) {
if (!pageId) {
console.error("❌ Erreur : l'ID de la page est `undefined` !");
return null;
}
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);
const response = await axios.get(
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages/${pageId}?_fields=id,title,acf`
);
return response.data;
} catch (error) {
console.error("❌ Erreur récupération page :", error.response?.data || error.message);
throw error;
console.error("❌ Erreur récupération page :", error);
return null;
}
}
@ -261,4 +267,30 @@ export async function updatePageACF(pageId, acfData) {
console.error("❌ Erreur mise à jour ACF :", error.response?.data || error.message);
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
}
}