Mise en place OG statique

This commit is contained in:
sebvtl728 2025-01-16 22:55:26 +01:00
parent a9f17ff11f
commit 76cc5b3737
9 changed files with 188 additions and 5176 deletions

View File

@ -5,6 +5,18 @@
<title>bureau détudes Maîtrise doeuvre</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<!-- ✅ Ajout Open Graph statique -->
<meta property="og:title" content="Titre par défaut - Bureau détudes">
<meta property="og:description" content="Bienvenue sur notre bureau détudes en Maîtrise dœuvre.">
<meta property="og:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
<meta property="og:type" content="website">
<meta property="og:url" content="https://ton-site.com">
<!-- ✅ Twitter Cards -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Titre par défaut - Bureau détudes">
<meta name="twitter:description" content="Bienvenue sur notre bureau détudes en Maîtrise dœuvre.">
<meta name="twitter:image" content="https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif">
<link rel="preload" href="/src/assets/vendor-BFTbvl5C.js" as="script">
</head>
<body>

View File

@ -6,17 +6,7 @@ const api = axios.create({
withCredentials: true,
});
// Méthode pour récupérer les données OpenGraph d'un article spécifique
export const fetchPostWithOG = async (postId) => {
try {
const response = await api.get(`/wp/v2/posts/${postId}`);
const { opengraph } = response.data; // Les métadonnées OpenGraph doivent être disponibles ici
return opengraph;
} catch (error) {
console.error('Erreur lors de la récupération des données OpenGraph :', error);
throw error;
}
};
// Exportation de l'instance Axios par défaut
export default api;

View File

@ -5,107 +5,33 @@ import api from "../../api";
import ModernSpinner from "../SpinnerModerne";
import Expertises from "../Expertises";
import ConstructSection from "../ConstructSection";
import {
Box,
Grid,
Typography,
Button,
Card,
CardContent,
} from "@mui/material";
import { Box, Grid, Typography, Button, Card, CardContent } from "@mui/material";
import Logo from "../../assets/logo-in3-mobil.svg";
const Home = () => {
const [backgroundImage, setBackgroundImage] = useState(null);
const [useGradient, setUseGradient] = useState(false);
const [heroTitle, setHeroTitle] = useState("Titre par défaut");
const [heroTitleColor, setHeroTitleColor] = useState("#ffffff");
const [heroText, setHeroText] = useState("Texte par défaut");
const [heroTextColor, setHeroTextColor] = useState("#ffffff");
const [metaTitle, setMetaTitle] = useState("Titre par défaut");
const [metaDescription, setMetaDescription] = useState(
"Description par défaut."
);
//optionally
const [canonicalUrl, setCanonicalUrl] = useState(null);
const [pageData, setPageData] = useState(null);
const [heroImage, setHeroImage] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
//Construction
const [constructTitle, setConstructTitle] = useState("Construisons vos projets");
const [constructSubtitle, setConstructSubtitle] = useState("ensemble");
const [constructSector, setConstructSector] = useState("Industriel | Tertiaire | Privée");
const [constructText, setConstructText] = useState("IN3, bureau d'études Ingénierie et Maîtrise dœuvre, est implanté depuis 20 ans sur Le Mans. Nous étudions vos projets en structure, gros œuvre, électricité courants forts et faibles, et fluides, tout corps d'état.");
const [constructNoteText, setConstructNoteText] = useState("À votre écoute, nous travaillons avec et pour vous dans le but de satisfaire vos besoins tant sur le plan architectural que technique, méthodologique et/ou économique.");
const [missionTitle, setMissionTitle] = useState("Nos missions principales");
const [missionItems, setMissionItems] = useState([
{ label: "Diagnostics | Sécurité", top: "20%", left: "50%" },
{ label: "Études techniques", top: "50%", left: "30%" },
{ label: "Bureau d'études | Maîtrise dœuvre", top: "70%", left: "60%" },
]);
// ACF Expertise
const [expertiseTitle, setExpertiseTitle] = useState(
"Titre Pourquoi Nous Choisir ?"
);
const [expertise1Title, setExpertise1Title] = useState("Titre Expertise 1");
const [expertise1Text, setExpertise1Text] = useState("Text Expertise 1");
const [expertise2Title, setExpertise2Title] = useState("Titre Expertise 2");
const [expertise2Text, setExpertise2Text] = useState("Text Expertise 2");
const [expertise3Title, setExpertise3Title] = useState("Titre Expertise 3");
const [expertise3Text, setExpertise3Text] = useState("Text Expertise 3");
useEffect(() => {
const fetchPageData = async () => {
try {
setIsLoading(true);
setError(null);
const response = await api.get(
"wp/v2/pages/13?_fields=acf,rank_math_title,rank_math_description"
);
const { acf, rank_math_title, rank_math_description } = response.data;
const response = await api.get("wp/v2/pages/13?_fields=acf,rank_math_title,rank_math_description");
const pageContent = response.data;
setPageData(pageContent);
// Métadonnées SEO
setMetaTitle(rank_math_title || "Titre par défaut");
setMetaDescription(rank_math_description || "Description par défaut.");
// Récupération de l'URL canonique
setCanonicalUrl(acf?.canonical_url || window.location.href);
// Données Hero
setUseGradient(acf?.enable_gradient ?? false);
setHeroTitle(acf?.hero_title || "Titre principal");
setHeroTitleColor(acf?.hero_title_color || "#ffffff");
setHeroText(acf?.hero_text || "Texte par défaut");
setHeroTextColor(acf?.hero_text_color || "#ffffff");
// Image de fond
if (acf?.img_hero) {
const mediaResponse = await api.get(`wp/v2/media/${acf.img_hero}`);
setBackgroundImage(mediaResponse.data.source_url);
// Vérification et récupération de l'image Hero depuis l'API media de WordPress
const heroImageId = pageContent.acf?.img_hero;
if (heroImageId) {
const mediaResponse = await api.get(`wp/v2/media/${heroImageId}`);
setHeroImage(mediaResponse.data.source_url);
} else {
setBackgroundImage(null);
setHeroImage(null);
}
// Section Construction
setConstructTitle(acf?.gdc_construct.construct_title || "Construisons vos projets");
setConstructSubtitle(acf?.gdc_construct.construct_subtitle || "ensemble");
setConstructText(acf?.gdc_construct.construct_text || "Texte par défaut");
setConstructSector(acf?.gdc_construct.construct_sector || "Secteurs");
setConstructNoteText(acf?.gdc_construct.construct_note || "Note société");
setMissionTitle(acf?.mission_title || "Nos missions principales");
setMissionItems(acf?.mission_items || []);
// Récupération du titre de la deuxième section Expertise
setExpertiseTitle(acf?.titre_expertise || "Pourquoi Nous Choisir ?");
setExpertise1Title(acf?.gdc_expert.titre_expertise_1 || "Expertise 1");
setExpertise1Text(acf?.gdc_expert.text_expertise_1 || "Text Expertise 1");
setExpertise2Title(acf?.gdc_expert.titre_expertise_2 || "Expertise 2");
setExpertise2Text(acf?.gdc_expert.text_expertise_2 || "Text Expertise 2");
setExpertise3Title(acf?.gdc_expert.titre_expertise_3 || "Expertise 3");
setExpertise3Text(acf?.gdc_expert.text_expertise_3 || "Text Expertise 3");
} catch (error) {
console.error("Erreur lors de la récupération des données :", error);
setError("Impossible de charger les données. Veuillez réessayer.");
@ -129,106 +55,82 @@ const Home = () => {
if (error) {
return (
<div style={{ textAlign: "center", padding: "50px" }}>
<img
src={Logo}
alt="Logo"
style={{
height: "200px",
width: "auto",
}}
/>
<img src={Logo} alt="Logo" style={{ height: "200px", width: "auto" }} />
<p style={{ color: "red" }}>{error}</p>
</div>
);
}
const { acf, rank_math_title, rank_math_description } = pageData || {};
return (
<div>
{/* SEO */}
<SEO title={metaTitle} description={metaDescription} canonicalUrl={canonicalUrl}/>
<SEO
postId={13} // ID WordPress valide
defaultTitle={rank_math_title || "Accueil - Mon Site"}
defaultDescription={rank_math_description || "Bienvenue sur notre site !"}
defaultCanonicalUrl={acf?.canonical_url || window.location.href}
defaultOgImage={heroImage || "https://preprod.octopusdesign.fr/api-octopus/server/wp-content/uploads/2025/01/Construction-logements-au-Mans-01.avif"}
/>
{/* Section Héros */}
<Hero
backgroundImage={backgroundImage}
useGradient={useGradient}
title={heroTitle}
titleColor={heroTitleColor}
text={heroText}
textColor={heroTextColor}
backgroundImage={heroImage}
useGradient={acf?.enable_gradient ?? false}
title={acf?.hero_title || "Titre principal"}
titleColor={acf?.hero_title_color || "#ffffff"}
text={acf?.hero_text || "Texte par défaut"}
textColor={acf?.hero_text_color || "#ffffff"}
/>
{/* Section Construisons */}
<ConstructSection
constructTitle={constructTitle}
constructSubtitle={constructSubtitle}
constructNoteText={constructNoteText}
constructText={constructText}
constructSector={constructSector}
missionTitle={missionTitle}
missionItems={missionItems}
constructTitle={acf?.gdc_construct?.construct_title || "Construisons vos projets"}
constructSubtitle={acf?.gdc_construct?.construct_subtitle || "ensemble"}
constructText={acf?.gdc_construct?.construct_text || "Texte par défaut"}
constructSector={acf?.gdc_construct?.construct_sector || "Secteurs"}
constructNoteText={acf?.gdc_construct?.construct_note || "Note société"}
missionTitle={acf?.mission_title || "Nos missions principales"}
missionItems={acf?.mission_items || []}
/>
{/* Section Expertises */}
<Expertises
expertiseTitle={expertiseTitle}
expertise1Title={expertise1Title}
expertise1Text={expertise1Text}
expertise2Title={expertise2Title}
expertise2Text={expertise2Text}
expertise3Title={expertise3Title}
expertise3Text={expertise3Text}
expertiseTitle={acf?.titre_expertise || "Pourquoi Nous Choisir ?"}
expertise1Title={acf?.gdc_expert?.titre_expertise_1 || "Expertise 1"}
expertise1Text={acf?.gdc_expert?.text_expertise_1 || "Text Expertise 1"}
expertise2Title={acf?.gdc_expert?.titre_expertise_2 || "Expertise 2"}
expertise2Text={acf?.gdc_expert?.text_expertise_2 || "Text Expertise 2"}
expertise3Title={acf?.gdc_expert?.titre_expertise_3 || "Expertise 3"}
expertise3Text={acf?.gdc_expert?.text_expertise_3 || "Text Expertise 3"}
/>
{/* Section Désir : Témoignages */}
{/* Section Témoignages */}
<Box sx={{ py: 6, px: 4 }}>
<Typography
variant="h2"
sx={{
fontWeight: "bold",
mb: 2,
textAlign: "center",
fontSize: { xs: "2rem", md: "2rem", lg: "3rem" }, // Responsive
}}
>
<Typography variant="h2" sx={{ fontWeight: "bold", mb: 2, textAlign: "center", fontSize: { xs: "2rem", md: "2rem", lg: "3rem" } }}>
Ce que disent nos clients
</Typography>
<Grid container spacing={4}>
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 3,
padding: 2,
backgroundColor: "#f9f9f9",
}}
>
<Card sx={{ boxShadow: 3, padding: 2, backgroundColor: "#f9f9f9" }}>
<CardContent>
<Typography variant="body1" sx={{ fontStyle: "italic" }}>
"Un service exceptionnel, une équipe formidable!"
"Un service exceptionnel, une équipe formidable !"
</Typography>
<Typography
variant="body2"
sx={{ mt: 2, textAlign: "right", fontWeight: "bold" }}
>
<Typography variant="body2" sx={{ mt: 2, textAlign: "right", fontWeight: "bold" }}>
- Client 1
</Typography>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6}>
<Card
sx={{
boxShadow: 3,
padding: 2,
backgroundColor: "#f9f9f9",
}}
>
<Card sx={{ boxShadow: 3, padding: 2, backgroundColor: "#f9f9f9" }}>
<CardContent>
<Typography variant="body1" sx={{ fontStyle: "italic" }}>
"Des résultats impressionnants pour notre projet."
</Typography>
<Typography
variant="body2"
sx={{ mt: 2, textAlign: "right", fontWeight: "bold" }}
>
<Typography variant="body2" sx={{ mt: 2, textAlign: "right", fontWeight: "bold" }}>
- Client 2
</Typography>
</CardContent>
@ -239,12 +141,7 @@ const Home = () => {
{/* Section Action */}
<Box sx={{ py: 6, px: 4, backgroundColor: "#0e467f", color: "#fff" }}>
<Typography
variant="h2"
align="center"
gutterBottom
sx={{ fontWeight: "bold", mb: 4 }}
>
<Typography variant="h2" align="center" gutterBottom sx={{ fontWeight: "bold", mb: 4 }}>
Prêt à Commencer ?
</Typography>
<Box textAlign="center">

View File

@ -1,39 +0,0 @@
import React, { useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useParams } from "react-router-dom";
import { fetchPostWithOG } from "../../api"; // Assure-toi que le chemin est correct
const PostPage = () => {
const { id } = useParams();
const [ogData, setOgData] = useState(null);
useEffect(() => {
const getOgData = async () => {
try {
const data = await fetchPostWithOG(id);
setOgData(data);
} catch (error) {
console.error("Erreur lors de la récupération des métadonnées OpenGraph :", error);
}
};
getOgData();
}, [id]);
if (!ogData) return <div>Chargement...</div>;
return (
<div>
<Helmet>
<meta property="og:title" content={ogData.title} />
<meta property="og:description" content={ogData.description} />
<meta property="og:image" content={ogData.image} />
</Helmet>
<h1>{ogData.title}</h1>
<p>{ogData.description}</p>
<img src={ogData.image} alt={ogData.title} />
</div>
);
};
export default PostPage;

View File

@ -2,14 +2,21 @@ import React, { useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import axios from "axios";
const SEO = ({ postId, defaultTitle, defaultDescription, defaultCanonicalUrl, defaultOgImage }) => {
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 [metaData, setMetaData] = useState({
title: defaultTitle || "Titre par défaut",
description: defaultDescription || "Description par défaut",
canonicalUrl: defaultCanonicalUrl || "",
ogTitle: "",
ogDescription: "",
ogImage: defaultOgImage || "https://votre-site.com/default-image.jpg",
title: defaultTitle,
description: defaultDescription,
canonicalUrl: defaultCanonicalUrl,
ogTitle: defaultTitle,
ogDescription: defaultDescription,
ogImage: defaultOgImage,
});
useEffect(() => {
@ -17,21 +24,23 @@ const SEO = ({ postId, defaultTitle, defaultDescription, defaultCanonicalUrl, de
const fetchMetaData = async () => {
try {
const response = await axios.get(
const { data } = await axios.get(
`https://preprod.octopusdesign.fr/api-octopus/server/wp-json/wp/v2/pages/${postId}`
);
const ogData = response.data.rank_math_og || {};
if (data && data.rank_math_og) {
setMetaData((prev) => ({
...prev,
title: ogData.og_title || prev.title,
description: ogData.og_description || prev.description,
ogTitle: ogData.og_title || "",
ogDescription: ogData.og_description || "",
ogImage: ogData.og_image || prev.ogImage,
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
}));
}
} catch (error) {
console.error("Erreur lors de la récupération des données SEO :", error);
console.error("⚠️ Erreur lors de la récupération des métadonnées SEO :", error);
}
};
@ -40,12 +49,23 @@ const SEO = ({ postId, defaultTitle, defaultDescription, defaultCanonicalUrl, de
return (
<Helmet>
{/* SEO standard */}
<title>{metaData.title}</title>
<meta name="description" content={metaData.description} />
{metaData.canonicalUrl && <link rel="canonical" href={metaData.canonicalUrl} />}
<meta property="og:title" content={metaData.ogTitle || metaData.title} />
<meta property="og:description" content={metaData.ogDescription || metaData.description} />
{/* 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} />
</Helmet>
);
};

View File

@ -11,7 +11,6 @@ const Home = lazy(() => import('./components/pages/Home.jsx'));
const About = lazy(() => import('./components/Pages/About'));
const Contact = lazy(() => import('./components/Pages/Contact'));
const Post = lazy(() => import('./components/Pages/Post'));
const PostPage = lazy(() => import('./components/Pages/PostPage')); // Nouveau composant pour un post spécifique
const Search = lazy(() => import('./components/Pages/Search')); // Nouveau composant pour la recherche
// Import des services
@ -31,7 +30,6 @@ function YourApp() {
<Routes>
<Route path="/" element={<Home />} />
<Route path="/posts" element={<Post />} />
<Route path="/posts/:id" element={<PostPage />} /> {/* Route dynamique pour un post spécifique */}
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/search" element={<Search />} /> {/* Route pour les recherches */}

File diff suppressed because one or more lines are too long

78
package-lock.json generated Normal file
View File

@ -0,0 +1,78 @@
{
"name": "Octopus-React-Wp",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"react-helmet-async": "^2.0.5"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
"license": "MIT"
},
"node_modules/react-helmet-async": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz",
"integrity": "sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==",
"license": "Apache-2.0",
"dependencies": {
"invariant": "^2.2.4",
"react-fast-compare": "^3.2.2",
"shallowequal": "^1.1.0"
},
"peerDependencies": {
"react": "^16.6.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
"license": "MIT"
}
}
}

5
package.json Normal file
View File

@ -0,0 +1,5 @@
{
"dependencies": {
"react-helmet-async": "^2.0.5"
}
}