From ad3a4c0ef10b425be48cdbf9b15964cec2b816d4 Mon Sep 17 00:00:00 2001 From: Alex Moldovan Date: Thu, 7 Mar 2024 10:00:13 +0000 Subject: [PATCH 1/2] feat: add cookie consent --- packages/projects-docs/package.json | 3 +- packages/projects-docs/pages/_app.js | 13 ++-- .../utils/cookieConsentConfig.js | 64 +++++++++++++++++++ .../utils/cookieConsentTheme.css | 54 ++++++++++++++++ .../projects-docs/utils/useInitAnalytics.js | 51 +++++++++++++++ pnpm-lock.yaml | 7 ++ 6 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 packages/projects-docs/utils/cookieConsentConfig.js create mode 100644 packages/projects-docs/utils/cookieConsentTheme.css create mode 100644 packages/projects-docs/utils/useInitAnalytics.js diff --git a/packages/projects-docs/package.json b/packages/projects-docs/package.json index b293f818..2afb15d8 100644 --- a/packages/projects-docs/package.json +++ b/packages/projects-docs/package.json @@ -19,7 +19,8 @@ "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "vanilla-cookieconsent": "3.0.0" }, "devDependencies": { "autoprefixer": "^10.4.16", diff --git a/packages/projects-docs/pages/_app.js b/packages/projects-docs/pages/_app.js index c4a0f2e5..1701e357 100644 --- a/packages/projects-docs/pages/_app.js +++ b/packages/projects-docs/pages/_app.js @@ -2,18 +2,17 @@ import "../styles.css"; import amplitude from "amplitude-js"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; +import { useInitAnalytics } from "../utils/useInitAnalytics"; +import "vanilla-cookieconsent/dist/cookieconsent.css"; +import "../utils/cookieConsentTheme.css"; export default function Nextra({ Component, pageProps }) { const getLayout = Component.getLayout || ((page) => page); const router = useRouter(); - // Init Amplitude - amplitude.getInstance().init(process.env.NEXT_PUBLIC_AMPLITUDE, null, { - includeReferrer: true, - saveEvents: true, - includeUtm: true, - saveParamsReferrerOncePerSession: false, - }); + // "a205ed9b06a7baf5a594bdd30293aa80" + // process.env.NEXT_PUBLIC_AMPLITUDE + useInitAnalytics("a205ed9b06a7baf5a594bdd30293aa80"); const isBrowser = typeof window !== "undefined"; const [initialRouteTracked, setInitialRouteTracked] = useState(false); diff --git a/packages/projects-docs/utils/cookieConsentConfig.js b/packages/projects-docs/utils/cookieConsentConfig.js new file mode 100644 index 00000000..5c27fe81 --- /dev/null +++ b/packages/projects-docs/utils/cookieConsentConfig.js @@ -0,0 +1,64 @@ +export const cookieConsentConfig = { + categories: { + functional: { + enabled: true, + readOnly: true, + }, + analytical: {}, + marketing: {}, + }, + + guiOptions: { + consentModal: { + layout: "box wide", + }, + }, + + language: { + default: "en", + translations: { + en: { + consentModal: { + title: "🍪 Yes, we use cookies", + description: + "This website utilizes cookies to enable essential site functionality and analytics. You may change your settings at any time or accept the default settings. You may close this banner to continue with only essential cookies.
Read more about this in our privacy and cookie statement.", + acceptAllBtn: "Accept all", + acceptNecessaryBtn: "Reject all", + showPreferencesBtn: "Manage preferences", + }, + preferencesModal: { + title: "Cookie preferences", + acceptAllBtn: "Accept all", + acceptNecessaryBtn: "Reject all", + savePreferencesBtn: "Save preferences", + closeIconLabel: "Close", + sections: [ + { + title: "Functional cookies", + description: + "These cookies are essential for the proper functioning of our services and cannot be disabled.", + linkedCategory: "functional", + }, + { + title: "Analytical cookies", + description: + "These cookies collect information about how you use our services or potential errors you encounter. Based on this information we are able to improve your experience and react to any issues.", + linkedCategory: "analytical", + }, + { + title: "Marketing cookies", + description: + "We currently do not collect cookies for marketing purposes. Though unlikely, by consenting to marketing cookies, you agree to allow us to show you advertisements relevant to you through our advertising partner in the future.", + linkedCategory: "marketing", + }, + { + title: "Read more", + description: + 'For more detailed information about the use of cookies across our websites, read our privacy and cookie statement.', + }, + ], + }, + }, + }, + }, +}; diff --git a/packages/projects-docs/utils/cookieConsentTheme.css b/packages/projects-docs/utils/cookieConsentTheme.css new file mode 100644 index 00000000..26f9124a --- /dev/null +++ b/packages/projects-docs/utils/cookieConsentTheme.css @@ -0,0 +1,54 @@ +#cc-main { + color-scheme: dark; + font-family: Inter; + + --cc-bg: #333; + --cc-primary-color: #fff; + --cc-secondary-color: #ccc; + + --cc-separator-border-color: #525252; + + --cc-btn-primary-bg: #eaff96; + --cc-btn-primary-color: #000; + --cc-btn-primary-border-color: #eaff96; + --cc-btn-primary-hover-bg: #f5ffcb; + --cc-btn-primary-hover-color: #000; + --cc-btn-primary-hover-border-color: #eaff96; + + --cc-btn-secondary-bg: #333; + --cc-btn-secondary-color: #e5e5e5; + --cc-btn-secondary-border-color: #525252; + --cc-btn-secondary-hover-bg: #ffffff1a; + --cc-btn-secondary-hover-color: #e5e5e5; + --cc-btn-secondary-hover-border-color: #525252; + + --cc-cookie-category-block-bg: #333; + --cc-cookie-category-block-border: #525252; + --cc-cookie-category-block-hover-bg: #ffffff1a; + --cc-cookie-category-block-hover-border: #525252; + --cc-cookie-category-expanded-block-hover-bg: #525252; + + --cc-toggle-on-bg: var(--cc-btn-primary-bg); + --cc-toggle-off-bg: #525252; + --cc-toggle-on-knob-bg: var(--cc-btn-primary-color); + --cc-toggle-off-knob-bg: #fff; + --cc-toggle-readonly-bg: #525252; + --cc-toggle-readonly-knob-bg: #adadad; + + --cc-btn-border-radius: 4px; +} + +#cc-main .cm__body { + padding-top: 4px; +} + +#cc-main .pm__btn, +#cc-main .cm__btn { + font-size: 1em; + font-weight: 400; +} + +#cc-main .cm__btn-group, +#cc-main .pm__btn-group { + gap: 8px; +} diff --git a/packages/projects-docs/utils/useInitAnalytics.js b/packages/projects-docs/utils/useInitAnalytics.js new file mode 100644 index 00000000..90cfb4d3 --- /dev/null +++ b/packages/projects-docs/utils/useInitAnalytics.js @@ -0,0 +1,51 @@ +import { useEffect, useState } from "react"; +import amplitude from "amplitude-js"; +import * as CookieConsent from "vanilla-cookieconsent"; +import { cookieConsentConfig } from "./cookieConsentConfig"; + +export const useInitAnalytics = (amplitudeApiKey) => { + const [analyticsInitialized, setAnalyticsInitialized] = useState(false); + + const initAnalytics = () => { + if (!amplitudeApiKey || analyticsInitialized) { + return; + } + + amplitude.init(amplitudeApiKey, null, { + includeReferrer: true, + saveEvents: true, + includeUtm: true, + saveParamsReferrerOncePerSession: false, + }); + + setAnalyticsInitialized(true); + }; + + const handleCookieConsent = (cookie) => { + const allowAnalytics = cookie?.categories?.includes("analytical") || false; + if (allowAnalytics && !analyticsInitialized) { + initAnalytics(); + } + + amplitude.getInstance().setOptOut(!allowAnalytics); + }; + + useEffect(() => { + if (process.env.NODE_ENV !== "production") { + return; + } + + CookieConsent.run({ + onFirstConsent: ({ cookie }) => { + handleCookieConsent(cookie); + }, + onChange: ({ cookie }) => { + handleCookieConsent(cookie); + }, + ...cookieConsentConfig, + }); + + const cookieConsent = CookieConsent.getCookie(); + handleCookieConsent(cookieConsent); + }, []); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e96bffc0..b1003356 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + vanilla-cookieconsent: + specifier: 3.0.0 + version: 3.0.0 devDependencies: autoprefixer: specifier: ^10.4.16 @@ -5390,6 +5393,10 @@ packages: spdx-expression-parse: 3.0.1 dev: false + /vanilla-cookieconsent@3.0.0: + resolution: {integrity: sha512-oeK7FivRDb424mt3/UT8DG98Pu5m6Uuye7JsdzO6HnYkstW5QHXhkslXyUpE3phtKz3NEeIo7hzLUtfRNRMfbQ==} + dev: false + /vfile-find-up@6.1.0: resolution: {integrity: sha512-plN64Ff/wLPvKC8ucTzyB97cgV7SdIcFL74HLCSmI/79FqOI1WACbNM4noKrJa+dZRgN6Gwp4BQElm/yBDqC3w==} dependencies: From 657597e89ded36272ae9ef6f22c3ed54fb39a858 Mon Sep 17 00:00:00 2001 From: Alex Moldovan Date: Thu, 7 Mar 2024 10:01:46 +0000 Subject: [PATCH 2/2] fix: Fix passing ENV variable to useInitAnalytics function --- packages/projects-docs/pages/_app.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/projects-docs/pages/_app.js b/packages/projects-docs/pages/_app.js index 1701e357..94525a91 100644 --- a/packages/projects-docs/pages/_app.js +++ b/packages/projects-docs/pages/_app.js @@ -10,9 +10,7 @@ export default function Nextra({ Component, pageProps }) { const getLayout = Component.getLayout || ((page) => page); const router = useRouter(); - // "a205ed9b06a7baf5a594bdd30293aa80" - // process.env.NEXT_PUBLIC_AMPLITUDE - useInitAnalytics("a205ed9b06a7baf5a594bdd30293aa80"); + useInitAnalytics(process.env.NEXT_PUBLIC_AMPLITUDE); const isBrowser = typeof window !== "undefined"; const [initialRouteTracked, setInitialRouteTracked] = useState(false);