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..94525a91 100644 --- a/packages/projects-docs/pages/_app.js +++ b/packages/projects-docs/pages/_app.js @@ -2,18 +2,15 @@ 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, - }); + useInitAnalytics(process.env.NEXT_PUBLIC_AMPLITUDE); 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: