diff --git a/frontend/src/components/Pages/CreatePost.jsx b/frontend/src/components/Pages/CreatePost.jsx
index 4ef68d0..b8fd90a 100644
--- a/frontend/src/components/Pages/CreatePost.jsx
+++ b/frontend/src/components/Pages/CreatePost.jsx
@@ -1,54 +1,133 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
import { uploadImage, createPost } from "../../wordpress";
+import { getToken } from "../../auth";
+import {
+ Box,
+ Container,
+ Typography,
+ TextField,
+ Button,
+ Paper,
+ Input,
+ IconButton,
+} from "@mui/material";
+import { ArrowBack, Publish } from "@mui/icons-material";
function CreatePost() {
- const [title, setTitle] = useState("");
- const [content, setContent] = useState("");
- const [file, setFile] = useState(null);
+ const [title, setTitle] = useState("");
+ const [content, setContent] = useState("");
+ const [file, setFile] = useState(null);
+ const navigate = useNavigate();
- const handleSubmit = async (e) => {
- e.preventDefault();
+ // ✅ Vérification de l'authentification : Redirection vers /login si l'utilisateur n'est pas connecté
+ useEffect(() => {
+ if (!getToken()) {
+ navigate("/admin/login");
+ }
+ }, [navigate]);
- try {
- console.log("📢 Tentative d'upload de l'image...");
- const imageId = await uploadImage(file);
+ const handleSubmit = async (e) => {
+ e.preventDefault();
- console.log("📢 Création du post avec l'image mise en avant...");
- await createPost(title, content, imageId);
+ try {
+ console.log("📢 Tentative d'upload de l'image...");
+ const imageId = await uploadImage(file);
- alert("✅ Post créé avec succès !");
- } catch (error) {
- alert("❌ Erreur lors de la création du post.");
- }
- };
+ console.log("📢 Création du post avec l'image mise en avant...");
+ await createPost(title, content, imageId);
- return (
-
-
Créer un post
-
-
- );
+ alert("✅ Post créé avec succès !");
+ navigate("/admin/gestion-articles"); // Redirection après la création
+ } catch (error) {
+ alert("❌ Erreur lors de la création du post.");
+ }
+ };
+
+ return (
+
+
+
+ {/* ✅ Bouton Retour à la Gestion des Articles */}
+ }
+ variant="outlined"
+ color="secondary"
+ onClick={() => navigate("/admin/gestion-articles")}
+ sx={{ mb: 2 }}
+ >
+ Retour à la gestion des articles
+
+
+ {/* ✅ Titre de la page */}
+
+ Créer un article
+
+
+ {/* ✅ Formulaire */}
+
+
+
+
+ );
}
export default CreatePost;
\ No newline at end of file
diff --git a/frontend/src/components/Pages/Dashboard.jsx b/frontend/src/components/Pages/Dashboard.jsx
index d5aa0c9..45d708e 100644
--- a/frontend/src/components/Pages/Dashboard.jsx
+++ b/frontend/src/components/Pages/Dashboard.jsx
@@ -1,4 +1,6 @@
+import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
+import { getToken, logout } from "../../auth"; // Importation de la fonction logout
import {
Box,
Typography,
@@ -6,12 +8,26 @@ import {
Card,
CardContent,
IconButton,
+ Button,
} from "@mui/material";
+import LogoutIcon from "@mui/icons-material/Logout"; // Icône de déconnexion
import ArticleIcon from "@mui/icons-material/Article";
import DashboardIcon from "@mui/icons-material/Dashboard";
function Dashboard() {
const navigate = useNavigate();
+ const token = getToken();
+
+ useEffect(() => {
+ if (!token) {
+ navigate("/admin/login"); // Redirige vers la page de connexion si non authentifié
+ }
+ }, [token, navigate]);
+
+ const handleLogout = () => {
+ logout(); // Déconnecte l'utilisateur
+ navigate("/admin/login"); // Redirige vers la page de connexion
+ };
return (
+ {/* Bouton de déconnexion placé en haut à gauche de la boîte blanche */}
+ }
+ onClick={handleLogout}
+ sx={{
+ position: "absolute",
+ top: 10,
+ left: 10,
+ backgroundColor: "#d32f2f",
+ "&:hover": { backgroundColor: "#b71c1c" },
+ fontSize: "0.875rem",
+ padding: "6px 12px",
+ }}
+ >
+
+
+
{/* En-tête */}
-
+
Tableau de Bord
@@ -47,7 +83,7 @@ function Dashboard() {
navigate("/posts")}
+ onClick={() => navigate("/admin/gestion-articles")}
sx={{
cursor: "pointer",
textAlign: "center",
diff --git a/frontend/src/components/Pages/GestionArticles.jsx b/frontend/src/components/Pages/GestionArticles.jsx
new file mode 100644
index 0000000..10420d0
--- /dev/null
+++ b/frontend/src/components/Pages/GestionArticles.jsx
@@ -0,0 +1,183 @@
+// ✅ Importations nécessaires
+import React, { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import {
+ Box, Typography, Button, Table, TableBody, TableCell,
+ TableContainer, TableHead, TableRow, Paper, TextField, Avatar
+} from "@mui/material";
+import { Add, ArrowBack, Edit, Delete } from "@mui/icons-material";
+import api from "../../api";
+import { getToken } from "../../auth";
+
+const GestionArticles = () => {
+ const [posts, setPosts] = useState([]);
+ const [searchTerm, setSearchTerm] = useState("");
+ const navigate = useNavigate();
+
+ // ✅ Vérifier si l'utilisateur est connecté
+ useEffect(() => {
+ if (!getToken()) {
+ navigate("/admin/login");
+ }
+ }, [navigate]);
+
+ // ✅ Récupérer les articles et leurs images associées
+ useEffect(() => {
+ const fetchPosts = async () => {
+ try {
+ const response = await api.get("wp/v2/posts?_fields=id,title,excerpt,featured_media");
+ const postsData = response.data;
+
+ const postsWithImages = await Promise.all(
+ postsData.map(async (post) => {
+ if (post.featured_media) {
+ try {
+ const mediaResponse = await api.get(`wp/v2/media/${post.featured_media}`);
+ return { ...post, image: mediaResponse.data.source_url, imageId: post.featured_media };
+ } catch (error) {
+ console.error(`❌ Erreur chargement image article ${post.id}:`, error);
+ return { ...post, image: null, imageId: null };
+ }
+ }
+ return { ...post, image: null, imageId: null };
+ })
+ );
+
+ setPosts(postsWithImages);
+ } catch (error) {
+ console.error("❌ Erreur chargement des articles :", error);
+ }
+ };
+
+ fetchPosts();
+ }, []);
+
+ // ✅ Supprimer un article et son image associée
+ const handleDeletePost = async (postId, imageId) => {
+ if (!window.confirm("⚠ Es-tu sûr de vouloir supprimer cet article ?")) return;
+
+ try {
+ const token = getToken(); // 🔑 Récupérer le token JWT
+ if (!token) {
+ alert("❌ Erreur : utilisateur non authentifié !");
+ navigate("/admin/login");
+ return;
+ }
+
+ const headers = {
+ Authorization: `Bearer ${token}`, // ✅ Ajout du token dans l'en-tête
+ "Content-Type": "application/json",
+ };
+
+ console.log(`📢 Suppression de l'article ID: ${postId}`);
+
+ // ✅ Suppression de l'article (avec force=true)
+ await api.delete(`wp/v2/posts/${postId}?force=true`, { headers });
+
+ // ✅ Suppression de l'image associée si elle existe
+ if (imageId) {
+ console.log(`📢 Suppression de l'image ID: ${imageId}`);
+ await api.delete(`wp/v2/media/${imageId}?force=true`, { headers });
+ }
+
+ // ✅ Mise à jour de la liste après suppression
+ setPosts(posts.filter((post) => post.id !== postId));
+
+ alert("✅ Article supprimé avec succès !");
+ } catch (error) {
+ console.error("❌ Erreur suppression article :", error);
+ alert(`⚠ Erreur : impossible de supprimer l'article.\n\nDétails: ${error.response?.data?.message || error.message}`);
+ }
+ };
+
+ // ✅ Filtrage des articles
+ const filteredPosts = posts.filter((post) =>
+ post.title.rendered.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ post.excerpt.rendered.replace(/(<([^>]+)>)/gi, "").toLowerCase().includes(searchTerm.toLowerCase())
+ );
+
+ return (
+
+ {/* ✅ En-tête avec retour et création */}
+
+ } variant="outlined" color="secondary" onClick={() => navigate("/admin/dashboard")}>
+ Retour au Dashboard
+
+
+ Gestion des Articles
+
+ } variant="contained" color="primary" onClick={() => navigate("/admin/create-post")}>
+ Créer un article
+
+
+
+ {/* ✅ Recherche */}
+ setSearchTerm(e.target.value)}
+ sx={{ mb: 3 }}
+ />
+
+ {/* ✅ Tableau des articles */}
+
+
+
+
+ Image
+ Titre
+ Résumé
+ Actions
+
+
+
+ {filteredPosts.length > 0 ? (
+ filteredPosts.map((post) => (
+
+ {/* ✅ Image en vedette */}
+
+
+
+ {/* ✅ Titre de l'article */}
+ {post.title.rendered}
+ {/* ✅ Résumé avec 100 caractères max */}
+
+ {post.excerpt.rendered.replace(/(<([^>]+)>)/gi, "").substring(0, 100)}...
+
+ {/* ✅ Boutons Modifier et Supprimer */}
+
+ } variant="outlined" color="warning" sx={{ mr: 1 }} >
+ Modifier
+
+ }
+ variant="outlined"
+ color="error"
+ onClick={() => handleDeletePost(post.id, post.imageId)}
+ >
+ Supprimer
+
+
+
+ ))
+ ) : (
+
+
+ Aucun article trouvé.
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default GestionArticles;
\ No newline at end of file
diff --git a/frontend/src/components/Pages/Login.jsx b/frontend/src/components/Pages/Login.jsx
index 9ceed01..4ff4e00 100644
--- a/frontend/src/components/Pages/Login.jsx
+++ b/frontend/src/components/Pages/Login.jsx
@@ -1,5 +1,6 @@
import { useState } from "react";
import { loginWithAppPassword, getToken, logout } from "../../auth";
+import { useNavigate } from "react-router-dom"; // Import de useNavigate pour la redirection
import {
Box,
Button,
@@ -16,13 +17,13 @@ function TestLogin() {
const [username, setUsername] = useState("");
const [appPassword, setAppPassword] = useState("");
const [token, setToken] = useState(getToken());
+ const navigate = useNavigate(); // Hook pour la navigation
const handleLogin = async (e) => {
e.preventDefault();
const newToken = await loginWithAppPassword(username, appPassword);
if (newToken) {
setToken(newToken);
- // alert("Connexion réussie !");
} else {
alert("Échec de la connexion !");
}
@@ -31,7 +32,6 @@ function TestLogin() {
const handleLogout = () => {
logout();
setToken(null);
- // alert("Déconnexion réussie !");
};
return (
@@ -66,14 +66,24 @@ function TestLogin() {
{token ? (
-
+ <>
+
+
+ >
) : (