integration SEORank
This commit is contained in:
parent
718eb8353b
commit
e7e5c5e3fd
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user