/** * Fonctions utilitaires pour récupérer des données depuis WordPress REST API * Compatible avec Next.js (client et serveur) */ const WORDPRESS_API_BASE = "https://deligraph.com/wp-json/wp/v2"; const PORTFOLIO_ENDPOINT = `${WORDPRESS_API_BASE}/portfolio`; /** * Construit une URL avec des paramètres de requête * @param {string} baseUrl - URL de base * @param {Object} params - Paramètres de requête * @returns {string} URL complète avec paramètres */ function buildUrl(baseUrl, params = {}) { const url = new URL(baseUrl); Object.keys(params).forEach((key) => { if (params[key] !== undefined && params[key] !== null) { url.searchParams.append(key, params[key]); } }); return url.toString(); } /** * Récupère les informations d'un média WordPress par son ID * @param {number} mediaId - ID du média WordPress * @param {Object} fetchOptions - Options supplémentaires pour fetch * @returns {Promise} URL de l'image ou null si non trouvé */ export async function fetchMedia(mediaId, fetchOptions = {}) { if (!mediaId) { return null; } const url = `${WORDPRESS_API_BASE}/media/${mediaId}`; try { const response = await fetch(url, { ...fetchOptions, }); if (!response.ok) { if (response.status === 404) { return null; } throw new Error(`Erreur HTTP: ${response.status} - ${response.statusText}`); } const data = await response.json(); // Retourner l'URL source de l'image return data.source_url || null; } catch (error) { console.error(`Erreur lors de la récupération du média ID "${mediaId}":`, error); return null; // Retourner null au lieu de throw pour éviter de casser le rendu } } /** * Récupère les posts de type portfolio depuis WordPress * @param {Object} options - Options de récupération * @param {number} options.perPage - Nombre de posts par page (défaut: 10) * @param {number} options.page - Numéro de page (défaut: 1) * @param {string} options.search - Terme de recherche * @param {number} options.categories - ID de catégorie * @param {string} options.order - Ordre de tri (asc/desc, défaut: desc) * @param {string} options.orderby - Champ de tri (date, title, etc., défaut: date) * @param {Object} options.fetchOptions - Options supplémentaires pour fetch (utile pour revalidate côté serveur) * @returns {Promise} Tableau de posts portfolio */ export async function fetchPortfolioPosts(options = {}) { const { perPage = 10, page = 1, search, categories, order = "desc", orderby = "date", fetchOptions = {}, } = options; const params = { per_page: perPage, page: page, order: order, orderby: orderby, }; if (search) { params.search = search; } if (categories) { params.categories = categories; } const url = buildUrl(PORTFOLIO_ENDPOINT, params); try { const response = await fetch(url, { ...fetchOptions, }); if (!response.ok) { throw new Error(`Erreur HTTP: ${response.status} - ${response.statusText}`); } const data = await response.json(); // Récupérer les images de couverture pour chaque post en parallèle const postsWithImages = await Promise.all( data.map(async (post) => { if (post.featured_media) { try { const mediaUrl = await fetchMedia(post.featured_media, fetchOptions); return { ...post, featuredImageUrl: mediaUrl }; } catch (error) { console.error(`Erreur lors de la récupération de l'image pour le post ${post.id}:`, error); return { ...post, featuredImageUrl: null }; } } return { ...post, featuredImageUrl: null }; }), ); return { posts: postsWithImages, totalPages: parseInt(response.headers.get("X-WP-TotalPages") || "1", 10), total: parseInt(response.headers.get("X-WP-Total") || "0", 10), }; } catch (error) { console.error("Erreur lors de la récupération des posts portfolio:", error); throw error; } } /** * Récupère un post portfolio spécifique par son slug * @param {string} slug - Slug du post * @param {Object} fetchOptions - Options supplémentaires pour fetch * @returns {Promise} Post portfolio ou null si non trouvé */ export async function fetchPortfolioPostBySlug(slug, fetchOptions = {}) { if (!slug) { throw new Error("Le slug est requis"); } const url = buildUrl(PORTFOLIO_ENDPOINT, { slug }); console.log("URL de recherche:", url); try { const response = await fetch(url, { ...fetchOptions, }); console.log("Statut de la réponse:", response.status, response.statusText); if (!response.ok) { if (response.status === 404) { console.log("Aucun post trouvé avec le paramètre slug, tentative avec fallback..."); // Fallback: récupérer tous les posts et filtrer par slug return await fetchPortfolioPostBySlugFallback(slug, fetchOptions); } throw new Error(`Erreur HTTP: ${response.status} - ${response.statusText}`); } const data = await response.json(); console.log( "Données reçues:", Array.isArray(data) ? `${data.length} résultat(s)` : "Format inattendu", data, ); // L'API WordPress retourne un tableau même pour un seul résultat if (Array.isArray(data)) { if (data.length > 0) { return data[0]; } // Si le tableau est vide, essayer le fallback console.log("Tableau vide, tentative avec fallback..."); return await fetchPortfolioPostBySlugFallback(slug, fetchOptions); } // Si ce n'est pas un tableau, retourner directement (cas où l'API retourne un objet unique) return data; } catch (error) { console.error(`Erreur lors de la récupération du post portfolio "${slug}":`, error); // En cas d'erreur, essayer le fallback try { return await fetchPortfolioPostBySlugFallback(slug, fetchOptions); } catch { throw error; // Relancer l'erreur originale } } } /** * Fallback: récupère tous les posts et filtre par slug * @param {string} slug - Slug du post * @param {Object} fetchOptions - Options supplémentaires pour fetch * @returns {Promise} Post portfolio ou null si non trouvé */ async function fetchPortfolioPostBySlugFallback(slug, fetchOptions = {}) { console.log("Utilisation du fallback pour le slug:", slug); try { const allPosts = await fetchAllPortfolioPosts({ fetchOptions }); const post = allPosts.find((p) => p.slug === slug); if (post) { console.log("Post trouvé via fallback"); return post; } console.log("Aucun post trouvé avec le slug:", slug); return null; } catch (error) { console.error("Erreur dans le fallback:", error); throw error; } } /** * Récupère un post portfolio par son ID * @param {number} id - ID du post * @param {Object} fetchOptions - Options supplémentaires pour fetch * @returns {Promise} Post portfolio ou null si non trouvé */ export async function fetchPortfolioPostById(id, fetchOptions = {}) { if (!id) { throw new Error("L'ID est requis"); } const url = `${PORTFOLIO_ENDPOINT}/${id}`; try { const response = await fetch(url, { ...fetchOptions, }); if (!response.ok) { if (response.status === 404) { return null; } throw new Error(`Erreur HTTP: ${response.status} - ${response.statusText}`); } return await response.json(); } catch (error) { console.error(`Erreur lors de la récupération du post portfolio ID "${id}":`, error); throw error; } } /** * Récupère tous les posts portfolio (sans pagination) * @param {Object} options - Options de récupération (mêmes que fetchPortfolioPosts) * @returns {Promise} Tableau de tous les posts portfolio */ export async function fetchAllPortfolioPosts(options = {}) { const allPosts = []; let page = 1; let hasMore = true; while (hasMore) { const result = await fetchPortfolioPosts({ ...options, page, perPage: 100, // Maximum recommandé par WordPress REST API }); allPosts.push(...result.posts); if (page >= result.totalPages) { hasMore = false; } else { page++; } } return allPosts; }