integration SEORank
This commit is contained in:
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
|
// 🔥 Ajout d'un timestamp pour éviter la mise en cache
|
||||||
const response = await api.get(
|
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;
|
const pageContent = response.data;
|
||||||
setPageData(pageContent);
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* SEO */}
|
{/* SEO */}
|
||||||
<SEO
|
<SEO
|
||||||
postId={13} // ID WordPress valide
|
pageUrl={pageUrl}
|
||||||
defaultTitle={rank_math_title || "centre de formation "}
|
defaultTitle={rank_math_title || "centre de formation "}
|
||||||
defaultDescription={
|
defaultDescription={
|
||||||
rank_math_description ||
|
rank_math_description ||
|
||||||
"formation et reconversion professionnelle dans les métiers du numérique"
|
"formation et reconversion professionnelle dans les métiers du numérique"
|
||||||
}
|
}
|
||||||
defaultCanonicalUrl={acf?.canonical_url || window.location.href}
|
defaultCanonicalUrl={acf?.canonical_url || pageUrl}
|
||||||
defaultOgImage={
|
defaultOgImage={
|
||||||
heroImage ||
|
heroImage ||
|
||||||
"https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
|
"https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
|
||||||
|
|||||||
+134
-52
@@ -1,73 +1,155 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import axios from "axios";
|
import api from "../api";
|
||||||
|
|
||||||
const SEO = ({
|
const DEFAULT_OG_IMAGE =
|
||||||
postId,
|
"https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif";
|
||||||
defaultTitle = "Titre par défaut",
|
|
||||||
defaultDescription = "Description par défaut",
|
const getWindowHref = () =>
|
||||||
defaultCanonicalUrl = "",
|
typeof window !== "undefined" ? window.location.href : "";
|
||||||
defaultOgImage = "https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"
|
|
||||||
|
const SEO = ({
|
||||||
|
pageUrl,
|
||||||
|
defaultTitle = "Titre par défaut",
|
||||||
|
defaultDescription = "Description par défaut",
|
||||||
|
defaultCanonicalUrl = "",
|
||||||
|
defaultOgImage = DEFAULT_OG_IMAGE,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [rankMathHead, setRankMathHead] = useState(null);
|
||||||
const [metaData, setMetaData] = useState({
|
const [shouldFallback, setShouldFallback] = useState(true);
|
||||||
title: defaultTitle,
|
|
||||||
description: defaultDescription,
|
const resolvedCanonical = defaultCanonicalUrl || getWindowHref();
|
||||||
canonicalUrl: defaultCanonicalUrl,
|
const targetUrl = pageUrl || resolvedCanonical;
|
||||||
ogTitle: defaultTitle,
|
|
||||||
ogDescription: defaultDescription,
|
|
||||||
ogImage: defaultOgImage,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!postId) return;
|
if (!targetUrl) return;
|
||||||
|
|
||||||
const fetchMetaData = async () => {
|
const controller = new AbortController();
|
||||||
|
|
||||||
|
const fetchRankMathHead = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(
|
const { data } = await api.get("rankmath/v1/getHead", {
|
||||||
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages/${postId}`
|
params: { url: targetUrl },
|
||||||
);
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
if (data && data.rank_math_og) {
|
if (
|
||||||
setMetaData((prev) => ({
|
data?.success &&
|
||||||
...prev,
|
data.head &&
|
||||||
title: data.rank_math_og.og_title || prev.title,
|
typeof window !== "undefined" &&
|
||||||
description: data.rank_math_og.og_description || prev.description,
|
window.DOMParser
|
||||||
ogTitle: data.rank_math_og.og_title || prev.title,
|
) {
|
||||||
ogDescription: data.rank_math_og.og_description || prev.description,
|
const parser = new window.DOMParser();
|
||||||
ogImage: data.rank_math_og.og_image || prev.ogImage,
|
const parsedDocument = parser.parseFromString(
|
||||||
canonicalUrl: data.acf?.canonical_url || window.location.href
|
`<!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) {
|
} 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();
|
fetchRankMathHead();
|
||||||
}, [postId]);
|
|
||||||
|
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 (
|
return (
|
||||||
<Helmet>
|
<Helmet>
|
||||||
{/* SEO standard */}
|
{!shouldFallback && rankMathNodes}
|
||||||
<title>{metaData.title}</title>
|
|
||||||
<meta name="description" content={metaData.description} />
|
{shouldFallback && (
|
||||||
{metaData.canonicalUrl && <link rel="canonical" href={metaData.canonicalUrl} />}
|
<>
|
||||||
|
<title>{defaultTitle}</title>
|
||||||
{/* Open Graph (Facebook, LinkedIn) */}
|
<meta name="description" content={defaultDescription} />
|
||||||
<meta property="og:type" content="website" />
|
{resolvedCanonical && (
|
||||||
<meta property="og:title" content={metaData.ogTitle} />
|
<link rel="canonical" href={resolvedCanonical} />
|
||||||
<meta property="og:description" content={metaData.ogDescription} />
|
)}
|
||||||
<meta property="og:image" content={metaData.ogImage} />
|
|
||||||
<meta property="og:url" content={metaData.canonicalUrl} />
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content={defaultTitle} />
|
||||||
{/* Twitter Cards */}
|
<meta property="og:description" content={defaultDescription} />
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta property="og:image" content={defaultOgImage} />
|
||||||
<meta name="twitter:title" content={metaData.ogTitle} />
|
{resolvedCanonical && (
|
||||||
<meta name="twitter:description" content={metaData.ogDescription} />
|
<meta property="og:url" content={resolvedCanonical} />
|
||||||
<meta name="twitter:image" content={metaData.ogImage} />
|
)}
|
||||||
|
|
||||||
|
<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>
|
</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
|
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 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
|
* 🔹 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
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user