Gestion de page ACF
This commit is contained in:
parent
d1cee7b3ae
commit
4a4d7104ac
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user