creation backend fonction import img cloudinary
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { Box, Typography, ImageList, ImageListItem } from "@mui/material";
|
||||
|
||||
const BACKEND_URL = "https://api.sveitl.synology.me/api/cloudinary-images"; // adapte pour prod
|
||||
|
||||
const CloudinaryGallerySelector = ({ onSelect }) => {
|
||||
const [images, setImages] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const BACKEND_URL = import.meta.env.VITE_CLOUDINARY_BACKEND_URL;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchImages = async () => {
|
||||
try {
|
||||
const response = await axios.get(`${BACKEND_URL}/api/cloudinary-images`);
|
||||
const data = response.data;
|
||||
|
||||
// 🔐 sécurisation du format de réponse
|
||||
const validImages = Array.isArray(data)
|
||||
? data
|
||||
: Array.isArray(data.resources)
|
||||
? data.resources
|
||||
: [];
|
||||
|
||||
if (validImages.length === 0) {
|
||||
setError("Aucune image trouvée.");
|
||||
}
|
||||
|
||||
setImages(validImages);
|
||||
} catch (err) {
|
||||
console.error("❌ Erreur Cloudinary complète :", JSON.stringify(err.response?.data || err.message, null, 2));
|
||||
setError("Impossible de récupérer les images depuis le serveur.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchImages();
|
||||
}, []);
|
||||
|
||||
if (loading) return <Typography>Chargement des images Cloudinary...</Typography>;
|
||||
if (error) return <Typography color="error">{error}</Typography>;
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: "bold", mb: 1 }}>
|
||||
Galerie Cloudinary :
|
||||
</Typography>
|
||||
|
||||
<ImageList cols={3} gap={12}>
|
||||
{images.map((img) => (
|
||||
<ImageListItem key={img.public_id} onClick={() => onSelect(img.secure_url)}>
|
||||
<img
|
||||
src={img.secure_url.replace("/upload/", "/upload/f_webp,w_300/")}
|
||||
alt={img.public_id}
|
||||
loading="lazy"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
borderRadius: 8,
|
||||
boxShadow: "0 2px 6px rgba(0,0,0,0.2)",
|
||||
}}
|
||||
/>
|
||||
</ImageListItem>
|
||||
))}
|
||||
</ImageList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloudinaryGallerySelector;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
|
||||
const CLOUD_NAME = "dh5qgexjo";
|
||||
const UPLOAD_PRESET = "preset_articles"; // crée-le dans Cloudinary si ce n’est pas encore fait
|
||||
const FOLDER = "articles-octopus";
|
||||
|
||||
const ImageUploaderCloudinary = ({ onUploadSuccess }) => {
|
||||
const handleUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("upload_preset", UPLOAD_PRESET);
|
||||
formData.append("folder", FOLDER);
|
||||
|
||||
const res = await fetch(`https://api.cloudinary.com/v1_1/${CLOUD_NAME}/image/upload`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.secure_url) {
|
||||
const transformedUrl = data.secure_url.replace("/upload/", "/upload/f_webp,w_800/");
|
||||
onUploadSuccess(transformedUrl); // ← Ici on t'envoie l’URL optimisée
|
||||
} else {
|
||||
alert("❌ Erreur lors de l'upload");
|
||||
}
|
||||
};
|
||||
|
||||
return <input type="file" accept="image/*" onChange={handleUpload} />;
|
||||
};
|
||||
|
||||
export default ImageUploaderCloudinary;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { uploadImage, createPost } from "../../wordpress";
|
||||
import { uploadImageFromUrl, createPost } from "../../wordpress";
|
||||
import { getToken } from "../../auth";
|
||||
import {
|
||||
Box,
|
||||
@@ -9,18 +9,18 @@ import {
|
||||
TextField,
|
||||
Button,
|
||||
Paper,
|
||||
Input,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
import { ArrowBack, Publish } from "@mui/icons-material";
|
||||
import ImageUploaderCloudinary from "../ImageUploaderCloudinary";
|
||||
import CloudinaryGallerySelector from "../CloudinaryGallerySelector";
|
||||
|
||||
function CreatePost() {
|
||||
const [title, setTitle] = useState("");
|
||||
const [content, setContent] = useState("");
|
||||
const [file, setFile] = useState(null);
|
||||
const [imageUrl, setImageUrl] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// ✅ Vérification de l'authentification : Redirection vers /login si l'utilisateur n'est pas connecté
|
||||
// Vérification de l'authentification
|
||||
useEffect(() => {
|
||||
if (!getToken()) {
|
||||
navigate("/admin/login");
|
||||
@@ -31,17 +31,15 @@ function CreatePost() {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
console.log("📢 Tentative d'upload de l'image...");
|
||||
const imageId = await uploadImage(file);
|
||||
|
||||
console.log("📢 Création du post avec l'image mise en avant...");
|
||||
const imageId = await uploadImageFromUrl(imageUrl);
|
||||
await createPost(title, content, imageId);
|
||||
alert("✅ Article publié !");
|
||||
navigate("/admin/gestion-articles");
|
||||
} catch (error) {
|
||||
console.error("❌ Erreur création post :", error.response?.data || error.message);
|
||||
alert("❌ Erreur lors de la création du 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 (
|
||||
@@ -51,14 +49,14 @@ function CreatePost() {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundImage: "url('https://source.unsplash.com/1600x900/?office,writing')",
|
||||
backgroundImage:
|
||||
"url('https://source.unsplash.com/1600x900/?office,writing')",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="sm">
|
||||
<Paper elevation={6} sx={{ padding: 4, borderRadius: 3 }}>
|
||||
{/* ✅ Bouton Retour à la Gestion des Articles */}
|
||||
<Button
|
||||
startIcon={<ArrowBack />}
|
||||
variant="outlined"
|
||||
@@ -66,15 +64,16 @@ function CreatePost() {
|
||||
onClick={() => navigate("/admin/gestion-articles")}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
Retour à la gestion des articles
|
||||
Retour
|
||||
</Button>
|
||||
|
||||
{/* ✅ Titre de la page */}
|
||||
<Typography variant="h4" sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{ fontWeight: "bold", textAlign: "center", mb: 3 }}
|
||||
>
|
||||
Créer un article
|
||||
</Typography>
|
||||
|
||||
{/* ✅ Formulaire */}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<TextField
|
||||
label="Titre de l'article"
|
||||
@@ -98,28 +97,47 @@ function CreatePost() {
|
||||
required
|
||||
/>
|
||||
|
||||
{/* ✅ Upload de l'image */}
|
||||
<Box sx={{ mt: 2 }}>
|
||||
{/* Upload via Cloudinary */}
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: "bold", mb: 1 }}>
|
||||
Image en vedette :
|
||||
Importer une image depuis ton ordi :
|
||||
</Typography>
|
||||
<Input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={(e) => setFile(e.target.files[0])}
|
||||
required
|
||||
sx={{ display: "block", mb: 2 }}
|
||||
/>
|
||||
<ImageUploaderCloudinary onUploadSuccess={(url) => setImageUrl(url)} />
|
||||
</Box>
|
||||
|
||||
{/* ✅ Bouton Publier */}
|
||||
{/* Sélection galerie Cloudinary */}
|
||||
<Box sx={{ mt: 4 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: "bold", mb: 1 }}>
|
||||
Ou choisir une image déjà envoyée :
|
||||
</Typography>
|
||||
<CloudinaryGallerySelector onSelect={(url) => setImageUrl(url)} />
|
||||
</Box>
|
||||
|
||||
{/* Aperçu */}
|
||||
{imageUrl && (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Typography variant="body2">Aperçu de l’image :</Typography>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="Image sélectionnée"
|
||||
style={{
|
||||
width: "100%",
|
||||
maxHeight: 200,
|
||||
objectFit: "cover",
|
||||
borderRadius: 8,
|
||||
marginTop: 8,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
startIcon={<Publish />}
|
||||
sx={{ mt: 3 }}
|
||||
sx={{ mt: 4 }}
|
||||
>
|
||||
Publier l'article
|
||||
</Button>
|
||||
@@ -130,4 +148,4 @@ function CreatePost() {
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePost;
|
||||
export default CreatePost;
|
||||
|
||||
Reference in New Issue
Block a user