integration SEORank

This commit is contained in:
sebvtl728 2025-10-26 01:32:28 +02:00
parent 718eb8353b
commit e7e5c5e3fd
4 changed files with 810 additions and 379 deletions

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ const Home = () => {
// 🔥 Ajout d'un timestamp pour éviter la mise en cache
const response = await api.get(
`wp/v2/pages/13?_fields=acf,rank_math_title,rank_math_description&_=${new Date().getTime()}`
`wp/v2/pages/13?_fields=acf,link,rank_math_title,rank_math_description&_=${new Date().getTime()}`
);
const pageContent = response.data;
setPageData(pageContent);
@ -79,19 +79,22 @@ const Home = () => {
);
}
const { acf, rank_math_title, rank_math_description } = pageData || {};
const { acf, rank_math_title, rank_math_description, link } = pageData || {};
const fallbackUrl =
typeof window !== "undefined" ? window.location.href : "";
const pageUrl = link || fallbackUrl;
return (
<div>
{/* SEO */}
<SEO
postId={13} // ID WordPress valide
pageUrl={pageUrl}
defaultTitle={rank_math_title || "centre de formation "}
defaultDescription={
rank_math_description ||
"formation et reconversion professionnelle dans les métiers du numérique"
}
defaultCanonicalUrl={acf?.canonical_url || window.location.href}
defaultCanonicalUrl={acf?.canonical_url || pageUrl}
defaultOgImage={
heroImage ||
"https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"

View File

@ -1,73 +1,155 @@
import React, { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { Helmet } from "react-helmet-async";
import axios from "axios";
import api from "../api";
const SEO = ({
postId,
defaultTitle = "Titre par défaut",
defaultDescription = "Description par défaut",
defaultCanonicalUrl = "",
defaultOgImage = "https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
const DEFAULT_OG_IMAGE =
"https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif";
const getWindowHref = () =>
typeof window !== "undefined" ? window.location.href : "";
const SEO = ({
pageUrl,
defaultTitle = "Titre par défaut",
defaultDescription = "Description par défaut",
defaultCanonicalUrl = "",
defaultOgImage = DEFAULT_OG_IMAGE,
}) => {
const [metaData, setMetaData] = useState({
title: defaultTitle,
description: defaultDescription,
canonicalUrl: defaultCanonicalUrl,
ogTitle: defaultTitle,
ogDescription: defaultDescription,
ogImage: defaultOgImage,
});
const [rankMathHead, setRankMathHead] = useState(null);
const [shouldFallback, setShouldFallback] = useState(true);
const resolvedCanonical = defaultCanonicalUrl || getWindowHref();
const targetUrl = pageUrl || resolvedCanonical;
useEffect(() => {
if (!postId) return;
if (!targetUrl) return;
const fetchMetaData = async () => {
const controller = new AbortController();
const fetchRankMathHead = async () => {
try {
const { data } = await axios.get(
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages/${postId}`
);
const { data } = await api.get("rankmath/v1/getHead", {
params: { url: targetUrl },
signal: controller.signal,
});
if (data && data.rank_math_og) {
setMetaData((prev) => ({
...prev,
title: data.rank_math_og.og_title || prev.title,
description: data.rank_math_og.og_description || prev.description,
ogTitle: data.rank_math_og.og_title || prev.title,
ogDescription: data.rank_math_og.og_description || prev.description,
ogImage: data.rank_math_og.og_image || prev.ogImage,
canonicalUrl: data.acf?.canonical_url || window.location.href
if (
data?.success &&
data.head &&
typeof window !== "undefined" &&
window.DOMParser
) {
const parser = new window.DOMParser();
const parsedDocument = parser.parseFromString(
`<!doctype html><html><head>${data.head}</head><body></body></html>`,
"text/html"
);
const headElement = parsedDocument.head;
const attributesToObject = (element) =>
Array.from(element.attributes).reduce((acc, attr) => {
acc[attr.name] = attr.value;
return acc;
}, {});
const metaTags = Array.from(headElement.querySelectorAll("meta")).map(
(meta) => attributesToObject(meta)
);
const linkTags = Array.from(headElement.querySelectorAll("link")).map(
(link) => attributesToObject(link)
);
const scriptTags = Array.from(
headElement.querySelectorAll("script")
).map((script) => ({
attributes: attributesToObject(script),
innerHTML: script.innerHTML,
}));
setRankMathHead({
title: headElement.querySelector("title")?.textContent || "",
meta: metaTags,
links: linkTags,
scripts: scriptTags,
});
setShouldFallback(false);
} else {
setRankMathHead(null);
setShouldFallback(true);
}
} catch (error) {
console.error("⚠️ Erreur lors de la récupération des métadonnées SEO :", error);
if (!controller.signal.aborted) {
console.error(
"⚠️ Erreur lors de la récupération des métadonnées Rank Math :",
error
);
}
setRankMathHead(null);
setShouldFallback(true);
}
};
fetchMetaData();
}, [postId]);
fetchRankMathHead();
return () => controller.abort();
}, [targetUrl]);
const rankMathNodes = useMemo(() => {
if (!rankMathHead) return null;
const renderMetaTags = rankMathHead.meta.map((attributes, index) => (
<meta key={`rank-math-meta-${index}`} {...attributes} />
));
const renderLinkTags = rankMathHead.links.map((attributes, index) => (
<link key={`rank-math-link-${index}`} {...attributes} />
));
const renderScriptTags = rankMathHead.scripts.map((script, index) => (
<script
key={`rank-math-script-${index}`}
{...script.attributes}
dangerouslySetInnerHTML={{ __html: script.innerHTML }}
/>
));
return (
<>
{rankMathHead.title && <title>{rankMathHead.title}</title>}
{renderMetaTags}
{renderLinkTags}
{renderScriptTags}
</>
);
}, [rankMathHead]);
return (
<Helmet>
{/* SEO standard */}
<title>{metaData.title}</title>
<meta name="description" content={metaData.description} />
{metaData.canonicalUrl && <link rel="canonical" href={metaData.canonicalUrl} />}
{/* Open Graph (Facebook, LinkedIn) */}
<meta property="og:type" content="website" />
<meta property="og:title" content={metaData.ogTitle} />
<meta property="og:description" content={metaData.ogDescription} />
<meta property="og:image" content={metaData.ogImage} />
<meta property="og:url" content={metaData.canonicalUrl} />
{/* Twitter Cards */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={metaData.ogTitle} />
<meta name="twitter:description" content={metaData.ogDescription} />
<meta name="twitter:image" content={metaData.ogImage} />
{!shouldFallback && rankMathNodes}
{shouldFallback && (
<>
<title>{defaultTitle}</title>
<meta name="description" content={defaultDescription} />
{resolvedCanonical && (
<link rel="canonical" href={resolvedCanonical} />
)}
<meta property="og:type" content="website" />
<meta property="og:title" content={defaultTitle} />
<meta property="og:description" content={defaultDescription} />
<meta property="og:image" content={defaultOgImage} />
{resolvedCanonical && (
<meta property="og:url" content={resolvedCanonical} />
)}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={defaultTitle} />
<meta name="twitter:description" content={defaultDescription} />
<meta name="twitter:image" content={defaultOgImage} />
</>
)}
</Helmet>
);
};
export default SEO;
export default SEO;

View File

@ -2,6 +2,7 @@ import axios from "axios";
import { getToken } from "./auth"; // 🔥 Import du token pour l'authentification
const API_URL = "https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2";
const RANKMATH_API_URL = "https://preprod.octopusdesign.fr/api-octopus/server/wp-json/rankmath/v1";
/**
* 🔹 Upload une image et retourne son ID
@ -334,3 +335,52 @@ export async function fetchPages() {
return []; // ✅ Retourne un tableau vide en cas d'erreur pour éviter le plantage
}
}
/**
* 🔹 Met à jour les métadonnées Rank Math (SEO) d'une page/post.
* @param {number} objectId - ID WordPress de la page.
* @param {object} meta - Objet des champs Rank Math à modifier.
*/
export async function updateRankMathMeta(objectId, meta = {}) {
const token = getToken();
if (!token) {
console.error("❌ Aucun token Rank Math trouvé !");
throw new Error("Utilisateur non authentifié.");
}
const sanitizedMeta = Object.entries(meta).reduce((acc, [key, value]) => {
const cleanedValue = typeof value === "string" ? value.trim() : value;
if (cleanedValue) {
acc[key] = cleanedValue;
}
return acc;
}, {});
if (!Object.keys(sanitizedMeta).length) {
console.warn(" Aucun champ Rank Math fourni, mise à jour ignorée.");
return null;
}
try {
const response = await axios.post(
`${RANKMATH_API_URL}/updateMeta`,
{
objectType: "post", // ✅ Les pages WordPress sont du type "post".
objectID: objectId,
meta: sanitizedMeta,
},
{
headers: {
Authorization: `Basic ${token}`,
"Content-Type": "application/json",
},
}
);
console.log("✅ Métadonnées Rank Math mises à jour :", response.data);
return response.data;
} catch (error) {
console.error("❌ Erreur mise à jour Rank Math :", error.response?.data || error.message);
throw error;
}
}