feat(analytics): integrate GTM with consent mode and cookie banner
This commit is contained in:
+227
-185
@@ -1,5 +1,6 @@
|
|||||||
import Footer from "@/component/layout/Footer";
|
import Footer from "@/component/layout/Footer";
|
||||||
import NavBar from "@/component/layout/NavBar";
|
import NavBar from "@/component/layout/NavBar";
|
||||||
|
import CookieBanner from "@/component/ui/CookieBanner";
|
||||||
import GridBackground from "@/component/ui/GridBackground";
|
import GridBackground from "@/component/ui/GridBackground";
|
||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
@@ -7,216 +8,257 @@ import { ReactNode } from "react";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
display: "swap",
|
display: "swap",
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
});
|
});
|
||||||
|
|
||||||
const siteConfig = {
|
const siteConfig = {
|
||||||
name: "Jessy David",
|
name: "Jessy David",
|
||||||
title: "Jessy David | Développeur Web Full-Stack",
|
title: "Jessy David | Développeur Web Full-Stack",
|
||||||
description:
|
description:
|
||||||
"Développeur Web Full-Stack passionné, spécialisé en React, Next.js et Node.js. Création de sites web modernes, applications web performantes et solutions digitales sur mesure.",
|
"Développeur Web Full-Stack passionné, spécialisé en React, Next.js et Node.js. Création de sites web modernes, applications web performantes et solutions digitales sur mesure.",
|
||||||
url: "https://jessy-david.dev",
|
url: "https://jessy-david.dev",
|
||||||
ogImage: "https://jessy-david.dev/og-image.jpg",
|
ogImage: "https://jessy-david.dev/og-image.jpg",
|
||||||
author: "Jessy David",
|
author: "Jessy David",
|
||||||
keywords: [
|
keywords: [
|
||||||
"développeur web",
|
"développeur web",
|
||||||
"développeur full-stack",
|
"développeur full-stack",
|
||||||
"React",
|
"React",
|
||||||
"Next.js",
|
"Next.js",
|
||||||
"Node.js",
|
"Node.js",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
"création site web",
|
"création site web",
|
||||||
"freelance",
|
"freelance",
|
||||||
"France",
|
"France",
|
||||||
"portfolio",
|
"portfolio",
|
||||||
"Jessy David",
|
"Jessy David",
|
||||||
],
|
],
|
||||||
twitterHandle: "@jessydavid",
|
twitterHandle: "@jessydavid",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
default: siteConfig.title,
|
default: siteConfig.title,
|
||||||
template: `%s | ${siteConfig.name}`,
|
template: `%s | ${siteConfig.name}`,
|
||||||
|
},
|
||||||
|
description: siteConfig.description,
|
||||||
|
keywords: siteConfig.keywords,
|
||||||
|
authors: [{ name: siteConfig.author, url: siteConfig.url }],
|
||||||
|
creator: siteConfig.author,
|
||||||
|
publisher: siteConfig.author,
|
||||||
|
|
||||||
|
metadataBase: new URL(siteConfig.url),
|
||||||
|
alternates: {
|
||||||
|
canonical: "/",
|
||||||
|
languages: {
|
||||||
|
"fr-FR": "/",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
nocache: false,
|
||||||
|
googleBot: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
noimageindex: false,
|
||||||
|
"max-video-preview": -1,
|
||||||
|
"max-image-preview": "large",
|
||||||
|
"max-snippet": -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
openGraph: {
|
||||||
|
type: "website",
|
||||||
|
locale: "fr_FR",
|
||||||
|
url: siteConfig.url,
|
||||||
|
siteName: siteConfig.name,
|
||||||
|
title: siteConfig.title,
|
||||||
description: siteConfig.description,
|
description: siteConfig.description,
|
||||||
keywords: siteConfig.keywords,
|
images: [
|
||||||
authors: [{ name: siteConfig.author, url: siteConfig.url }],
|
{
|
||||||
creator: siteConfig.author,
|
url: siteConfig.ogImage,
|
||||||
publisher: siteConfig.author,
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: `${siteConfig.name} - Portfolio Développeur Web`,
|
||||||
|
type: "image/jpeg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
metadataBase: new URL(siteConfig.url),
|
twitter: {
|
||||||
alternates: {
|
card: "summary_large_image",
|
||||||
canonical: "/",
|
title: siteConfig.title,
|
||||||
languages: {
|
description: siteConfig.description,
|
||||||
"fr-FR": "/",
|
creator: siteConfig.twitterHandle,
|
||||||
},
|
images: [siteConfig.ogImage],
|
||||||
},
|
},
|
||||||
|
|
||||||
robots: {
|
icons: {
|
||||||
index: true,
|
icon: [
|
||||||
follow: true,
|
{ url: "/favicon.ico", sizes: "any" },
|
||||||
nocache: false,
|
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
|
||||||
googleBot: {
|
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
|
||||||
index: true,
|
],
|
||||||
follow: true,
|
apple: [
|
||||||
noimageindex: false,
|
{
|
||||||
"max-video-preview": -1,
|
url: "/apple-touch-icon.png",
|
||||||
"max-image-preview": "large",
|
sizes: "180x180",
|
||||||
"max-snippet": -1,
|
type: "image/png",
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
|
},
|
||||||
|
|
||||||
openGraph: {
|
manifest: "/site.webmanifest",
|
||||||
type: "website",
|
|
||||||
locale: "fr_FR",
|
|
||||||
url: siteConfig.url,
|
|
||||||
siteName: siteConfig.name,
|
|
||||||
title: siteConfig.title,
|
|
||||||
description: siteConfig.description,
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: siteConfig.ogImage,
|
|
||||||
width: 1200,
|
|
||||||
height: 630,
|
|
||||||
alt: `${siteConfig.name} - Portfolio Développeur Web`,
|
|
||||||
type: "image/jpeg",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
twitter: {
|
category: "technology",
|
||||||
card: "summary_large_image",
|
|
||||||
title: siteConfig.title,
|
|
||||||
description: siteConfig.description,
|
|
||||||
creator: siteConfig.twitterHandle,
|
|
||||||
images: [siteConfig.ogImage],
|
|
||||||
},
|
|
||||||
|
|
||||||
icons: {
|
verification: {
|
||||||
icon: [
|
google: "ton-code-google-search-console",
|
||||||
{ url: "/favicon.ico", sizes: "any" },
|
},
|
||||||
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
|
|
||||||
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
|
|
||||||
],
|
|
||||||
apple: [
|
|
||||||
{
|
|
||||||
url: "/apple-touch-icon.png",
|
|
||||||
sizes: "180x180",
|
|
||||||
type: "image/png",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
manifest: "/site.webmanifest",
|
other: {
|
||||||
|
"msapplication-TileColor": "#0f172a",
|
||||||
category: "technology",
|
"theme-color": "#0f172a",
|
||||||
|
},
|
||||||
verification: {
|
|
||||||
google: "ton-code-google-search-console",
|
|
||||||
},
|
|
||||||
|
|
||||||
other: {
|
|
||||||
"msapplication-TileColor": "#0f172a",
|
|
||||||
"theme-color": "#0f172a",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const viewport: Viewport = {
|
export const viewport: Viewport = {
|
||||||
themeColor: [
|
themeColor: [
|
||||||
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
|
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
|
||||||
{ media: "(prefers-color-scheme: dark)", color: "#0f172a" },
|
{ media: "(prefers-color-scheme: dark)", color: "#0f172a" },
|
||||||
],
|
],
|
||||||
width: "device-width",
|
width: "device-width",
|
||||||
initialScale: 1,
|
initialScale: 1,
|
||||||
maximumScale: 5,
|
maximumScale: 5,
|
||||||
userScalable: true,
|
userScalable: true,
|
||||||
colorScheme: "dark",
|
colorScheme: "dark",
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"@type": "WebSite",
|
"@type": "WebSite",
|
||||||
"@id": `${siteConfig.url}/#website`,
|
"@id": `${siteConfig.url}/#website`,
|
||||||
url: siteConfig.url,
|
url: siteConfig.url,
|
||||||
name: siteConfig.name,
|
name: siteConfig.name,
|
||||||
description: siteConfig.description,
|
description: siteConfig.description,
|
||||||
inLanguage: "fr-FR",
|
inLanguage: "fr-FR",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"@id": `${siteConfig.url}/#person`,
|
"@id": `${siteConfig.url}/#person`,
|
||||||
name: siteConfig.name,
|
name: siteConfig.name,
|
||||||
url: siteConfig.url,
|
url: siteConfig.url,
|
||||||
image: siteConfig.ogImage,
|
image: siteConfig.ogImage,
|
||||||
jobTitle: "Développeur Web Full-Stack",
|
jobTitle: "Développeur Web Full-Stack",
|
||||||
description: siteConfig.description,
|
description: siteConfig.description,
|
||||||
sameAs: [
|
sameAs: [
|
||||||
"https://github.com/jessydavid-dev",
|
"https://github.com/jessydavid-dev",
|
||||||
"https://linkedin.com/in/jessy-david",
|
"https://linkedin.com/in/jessy-david",
|
||||||
"https://twitter.com/jessydavid",
|
"https://twitter.com/jessydavid",
|
||||||
],
|
],
|
||||||
knowsAbout: [
|
knowsAbout: [
|
||||||
"React",
|
"React",
|
||||||
"Next.js",
|
"Next.js",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
"Node.js",
|
"Node.js",
|
||||||
"JavaScript",
|
"JavaScript",
|
||||||
"Tailwind CSS",
|
"Tailwind CSS",
|
||||||
"PostgreSQL",
|
"PostgreSQL",
|
||||||
"MongoDB",
|
"MongoDB",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"@type": "ProfilePage",
|
"@type": "ProfilePage",
|
||||||
"@id": `${siteConfig.url}/#profilepage`,
|
"@id": `${siteConfig.url}/#profilepage`,
|
||||||
url: siteConfig.url,
|
url: siteConfig.url,
|
||||||
name: `Portfolio de ${siteConfig.name}`,
|
name: `Portfolio de ${siteConfig.name}`,
|
||||||
description: siteConfig.description,
|
description: siteConfig.description,
|
||||||
mainEntity: { "@id": `${siteConfig.url}/#person` },
|
mainEntity: { "@id": `${siteConfig.url}/#person` },
|
||||||
inLanguage: "fr-FR",
|
inLanguage: "fr-FR",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="fr" className={inter.variable}>
|
<html lang="fr" className={inter.variable}>
|
||||||
<head>
|
<head>
|
||||||
<script
|
{/* Google Tag Manager - Consent Mode par défaut */}
|
||||||
type="application/ld+json"
|
<script
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
dangerouslySetInnerHTML={{
|
||||||
/>
|
__html: `
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
// Consentement par défaut : tout refusé (RGPD compliant)
|
||||||
<link
|
gtag('consent', 'default', {
|
||||||
rel="preconnect"
|
'analytics_storage': 'denied',
|
||||||
href="https://fonts.gstatic.com"
|
'ad_storage': 'denied',
|
||||||
crossOrigin="anonymous"
|
'ad_user_data': 'denied',
|
||||||
/>
|
'ad_personalization': 'denied',
|
||||||
|
'wait_for_update': 500
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Google Tag Manager */}
|
||||||
|
<script
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
(function(w,d,s,l,i){w[l]=w[l]||[];
|
||||||
|
w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});
|
||||||
|
var f=d.getElementsByTagName(s)[0],
|
||||||
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';
|
||||||
|
j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
|
||||||
|
f.parentNode.insertBefore(j,f);
|
||||||
|
})(window,document,'script','dataLayer','GTM-TJDQ3XJD');
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<link
|
<script
|
||||||
rel="dns-prefetch"
|
type="application/ld+json"
|
||||||
href="https://www.google-analytics.com"
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||||
/>
|
/>
|
||||||
</head>
|
|
||||||
<body className={`${inter.className} antialiased`}>
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<GridBackground variant="light" />
|
<link
|
||||||
<NavBar />
|
rel="preconnect"
|
||||||
<main id="main-content" className="relative z-10">
|
href="https://fonts.gstatic.com"
|
||||||
{children}
|
crossOrigin="anonymous"
|
||||||
</main>
|
/>
|
||||||
<Footer />
|
|
||||||
</body>
|
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
|
||||||
</html>
|
</head>
|
||||||
);
|
<body className={`${inter.className} antialiased`}>
|
||||||
|
{/* Google Tag Manager (noscript) */}
|
||||||
|
<noscript>
|
||||||
|
<iframe
|
||||||
|
src="https://www.googletagmanager.com/ns.html?id=GTM-TJDQ3XJD"
|
||||||
|
height="0"
|
||||||
|
width="0"
|
||||||
|
style={{ display: "none", visibility: "hidden" }}
|
||||||
|
/>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<GridBackground variant="light" />
|
||||||
|
<NavBar />
|
||||||
|
<main id="main-content" className="relative z-10">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
<CookieBanner />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user