Skip to content

Commit c67dad3

Browse files
Merge pull request eduardconstantin#110 from eduardconstantin/feat/cookie-consent
Feat/cookie consent
2 parents 818638d + 27dd12b commit c67dad3

File tree

6 files changed

+248
-25
lines changed

6 files changed

+248
-25
lines changed

app/layout.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { type ReactNode } from "react";
22
import { type Metadata } from "next";
33
import TopNav from "@azure-fundamentals/components/TopNav";
4-
import Footer from "../components/Footer";
4+
import Footer from "@azure-fundamentals/components/Footer";
55
import ApolloProvider from "@azure-fundamentals/components/ApolloProvider";
6-
import { GoogleAnalytics, GoogleTagManager } from "@next/third-parties/google";
6+
import Cookie from "@azure-fundamentals/components/Cookie";
77
import "styles/globals.css";
88

99
export const metadata: Metadata = {
@@ -19,35 +19,16 @@ type RootLayoutProps = {
1919
};
2020

2121
export default function RootLayout({ children }: RootLayoutProps) {
22-
const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID;
23-
2422
return (
2523
<html lang="en">
2624
<body className="bg-slate-900">
2725
<ApolloProvider>
2826
<TopNav />
29-
<main className="flex flex-col justify-between h-[calc(100vh-2.5rem-64px)]">
27+
<main className="flex flex-col justify-between md:h-[calc(100vh-2.5rem-64px)]">
3028
{children}
3129
<Footer />
30+
<Cookie />
3231
</main>
33-
<GoogleTagManager gtmId={GA_TRACKING_ID!} />
34-
<GoogleAnalytics gaId={GA_TRACKING_ID!} />
35-
{/*<Script
36-
src={
37-
"https://www.googletagmanager.com/gtag/js?id=" + GA_TRACKING_ID
38-
}
39-
/>
40-
<Script id="google-analytics">
41-
{`
42-
window.dataLayer = window.dataLayer || [];
43-
function gtag(){dataLayer.push(arguments);}
44-
gtag('js', new Date());
45-
46-
gtag('config', '${GA_TRACKING_ID}', {
47-
page_path: window.location.pathname,
48-
});
49-
`}
50-
</Script>*/}
5132
</ApolloProvider>
5233
</body>
5334
</html>

components/Cookie.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use client";
2+
import { useEffect, type FC } from "react";
3+
import "vanilla-cookieconsent/dist/cookieconsent.css";
4+
import * as CookieConsent from "vanilla-cookieconsent";
5+
import getConfig from "@azure-fundamentals/utils/CookieConfig";
6+
import addCookieConsentListeners from "@azure-fundamentals/utils/CookieListeners";
7+
import Script from "next/script";
8+
9+
const Cookie: FC = () => {
10+
const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID;
11+
12+
useEffect(() => {
13+
addCookieConsentListeners();
14+
CookieConsent.run(getConfig());
15+
}, []);
16+
17+
return (
18+
<>
19+
<button
20+
className="w-fit self-center md:absolute bg-white p-2 rounded-2xl md:bottom-1 md:left-1"
21+
type="button"
22+
onClick={CookieConsent.showPreferences}
23+
>
24+
<svg
25+
xmlns="http://www.w3.org/2000/svg"
26+
width="16"
27+
height="16"
28+
fill="currentColor"
29+
viewBox="0 0 16 16"
30+
>
31+
<path d="M6 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m4.5.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m-.5 3.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0" />
32+
<path d="M8 0a7.96 7.96 0 0 0-4.075 1.114q-.245.102-.437.28A8 8 0 1 0 8 0m3.25 14.201a1.5 1.5 0 0 0-2.13.71A7 7 0 0 1 8 15a6.97 6.97 0 0 1-3.845-1.15 1.5 1.5 0 1 0-2.005-2.005A6.97 6.97 0 0 1 1 8c0-1.953.8-3.719 2.09-4.989a1.5 1.5 0 1 0 2.469-1.574A7 7 0 0 1 8 1c1.42 0 2.742.423 3.845 1.15a1.5 1.5 0 1 0 2.005 2.005A6.97 6.97 0 0 1 15 8c0 .596-.074 1.174-.214 1.727a1.5 1.5 0 1 0-1.025 2.25 7 7 0 0 1-2.51 2.224Z" />
33+
</svg>
34+
</button>
35+
<Script
36+
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
37+
/>
38+
<Script id="google-analytics">
39+
{`
40+
window.dataLayer = window.dataLayer || [];
41+
function gtag(){dataLayer.push(arguments);}
42+
gtag('js', new Date());
43+
gtag('consent', 'default', {'analytics_storage':'denied'})
44+
gtag('config', '${GA_TRACKING_ID}', {
45+
page_path: window.location.pathname,
46+
});
47+
`}
48+
</Script>
49+
</>
50+
);
51+
};
52+
53+
export default Cookie;

package-lock.json

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"react-github-btn": "^1.4.0",
3636
"react-hook-form": "^7.43.1",
3737
"reflect-metadata": "^0.1.13",
38-
"typescript": "4.9.5"
38+
"typescript": "4.9.5",
39+
"vanilla-cookieconsent": "^3.0.0"
3940
},
4041
"devDependencies": {
4142
"@babel/preset-typescript": "^7.23.3",

utils/CookieConfig.tsx

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { CookieConsentConfig } from "vanilla-cookieconsent";
2+
3+
const getConfig = () => {
4+
const config: CookieConsentConfig = {
5+
// root: 'body',
6+
// autoShow: true,
7+
disablePageInteraction: true,
8+
// hideFromBots: true,
9+
// mode: 'opt-in',
10+
// revision: 0,
11+
12+
cookie: {
13+
name: "ace_cookie",
14+
// domain: location.hostname,
15+
// path: '/',
16+
// sameSite: "Lax",
17+
// expiresAfterDays: 365,
18+
},
19+
20+
/**
21+
* Callback functions
22+
*/
23+
// onFirstConsent: ({ cookie }) => {
24+
// console.log('onFirstConsent fired', cookie);
25+
// },
26+
27+
//onConsent: ({ cookie }) => {
28+
//console.log("onConsent fired!", cookie);
29+
//},
30+
31+
//onChange: ({ changedCategories, changedServices }) => {
32+
//console.log("onChange fired!", changedCategories, changedServices);
33+
//},
34+
35+
// onModalReady: ({ modalName }) => {
36+
// console.log('ready:', modalName);
37+
// },
38+
39+
// onModalShow: ({ modalName }) => {
40+
// console.log('visible:', modalName);
41+
// },
42+
43+
// onModalHide: ({ modalName }) => {
44+
// console.log('hidden:', modalName);
45+
// },
46+
47+
guiOptions: {
48+
consentModal: {
49+
layout: "cloud inline",
50+
position: "bottom center",
51+
equalWeightButtons: true,
52+
flipButtons: false,
53+
},
54+
preferencesModal: {
55+
layout: "box",
56+
position: "right",
57+
equalWeightButtons: true,
58+
flipButtons: false,
59+
},
60+
},
61+
categories: {
62+
necessary: {
63+
enabled: true,
64+
readOnly: true,
65+
},
66+
analytics: {
67+
autoClear: {
68+
cookies: [
69+
{
70+
name: /^(_ga_|_gid)/,
71+
},
72+
],
73+
},
74+
},
75+
},
76+
language: {
77+
default: "en",
78+
autoDetect: "browser",
79+
translations: {
80+
en: {
81+
consentModal: {
82+
title: "We use cookies!",
83+
description:
84+
"Hello, this website uses essential cookies to ensure its proper functioning and tracking cookies to understand how you interact with it. The latter is only set after permission.",
85+
acceptAllBtn: "Accept all",
86+
acceptNecessaryBtn: "Reject all",
87+
showPreferencesBtn: "Manage preferences",
88+
},
89+
preferencesModal: {
90+
title: "Manage cookie preferences",
91+
acceptAllBtn: "Accept all",
92+
acceptNecessaryBtn: "Reject all",
93+
savePreferencesBtn: "Save preferences",
94+
closeIconLabel: "Close modal",
95+
serviceCounterLabel: "Service|Services",
96+
sections: [
97+
{
98+
title: "Cookie Usage",
99+
description:
100+
"We use cookies to ensure basic website functionality and to improve your online experience. You can choose to opt in or out of each category whenever you want.",
101+
},
102+
{
103+
title:
104+
'Strictly Necessary Cookies <span class="pm__badge">Always Enabled</span>',
105+
description:
106+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
107+
linkedCategory: "necessary",
108+
},
109+
{
110+
title: "Analytics Cookies",
111+
description:
112+
"Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.",
113+
linkedCategory: "analytics",
114+
cookieTable: {
115+
headers: {
116+
name: "Name",
117+
domain: "Service",
118+
description: "Description",
119+
expiration: "Expiration",
120+
},
121+
body: [
122+
{
123+
name: "_ga",
124+
domain: "Google Analytics",
125+
description:
126+
'Cookie set by <a href="#das">Google Analytics</a>.',
127+
expiration: "Expires after 12 days",
128+
},
129+
],
130+
},
131+
},
132+
{
133+
title: "More information",
134+
description:
135+
"For any queries in relation to our policy on cookies and your choices, please contact us.",
136+
},
137+
],
138+
},
139+
},
140+
},
141+
},
142+
};
143+
144+
return config;
145+
};
146+
147+
export default getConfig;

utils/CookieListeners.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client";
2+
3+
import { acceptedCategory } from "vanilla-cookieconsent";
4+
5+
declare global {
6+
interface Window {
7+
_ccRun: boolean;
8+
gtag: any;
9+
dataLayer: Record<string, unknown>[];
10+
}
11+
}
12+
13+
const addCookieConsentListeners = () => {
14+
/**
15+
* React specific fix: avoid adding event listeners twice
16+
*/
17+
if (window._ccRun) return;
18+
19+
const updateGtagConsent = () => {
20+
window.gtag("consent", "update", {
21+
analytics_storage: acceptedCategory("analytics") ? "granted" : "denied",
22+
security_storage: "granted", //necessary
23+
});
24+
};
25+
26+
window.addEventListener("cc:onConsent", () => {
27+
updateGtagConsent();
28+
});
29+
30+
window.addEventListener("cc:onChange", () => {
31+
updateGtagConsent();
32+
});
33+
};
34+
35+
export default addCookieConsentListeners;

0 commit comments

Comments
 (0)