diff --git a/website/.stylelintrc.js b/website/.stylelintrc.js new file mode 100644 index 000000000..cb9f2ff5f --- /dev/null +++ b/website/.stylelintrc.js @@ -0,0 +1,4 @@ +module.exports = { + ...require('@hashicorp/nextjs-scripts/.stylelintrc.js'), + /* Specify overrides here */ +} diff --git a/website/components/subnav/index.jsx b/website/components/subnav/index.jsx index 7fe62a169..d24103f47 100644 --- a/website/components/subnav/index.jsx +++ b/website/components/subnav/index.jsx @@ -1,5 +1,5 @@ import Subnav from '@hashicorp/react-subnav' -import subnavItems from '../../data/subnav' +import subnavItems from 'data/subnav' import { useRouter } from 'next/router' export default function PackerSubnav() { diff --git a/website/jsconfig.json b/website/jsconfig.json new file mode 100644 index 000000000..36aa1a4dc --- /dev/null +++ b/website/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/website/lib/bugsnag.js b/website/lib/bugsnag.js deleted file mode 100644 index 90847c41d..000000000 --- a/website/lib/bugsnag.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import Bugsnag from '@bugsnag/js' -import BugsnagReact from '@bugsnag/plugin-react' - -const apiKey = - typeof window === 'undefined' - ? 'b6c57b27a37e531a5de94f065dd98bc0' - : 'de0b822b269aa57b620efd8927e03744' - -if (!Bugsnag._client) { - Bugsnag.start({ - apiKey, - plugins: [new BugsnagReact(React)], - otherOptions: { releaseStage: process.env.NODE_ENV || 'development' }, - }) -} - -export default Bugsnag diff --git a/website/lib/consent-manager-config.js b/website/lib/consent-manager-config.js deleted file mode 100644 index 36d3e5890..000000000 --- a/website/lib/consent-manager-config.js +++ /dev/null @@ -1,76 +0,0 @@ -const isProd = process.env.NODE_ENV === 'production' - -const segmentWriteKey = isProd - ? 'AjXdfmTTk1I9q9dfyePuDFHBrz1tCO3l' - : '0EXTgkNx0Ydje2PGXVbRhpKKoe5wtzcE' - -// TODO: refactor into web components -let utilityServerRoot = isProd - ? 'https://util.hashicorp.com' - : 'https://hashicorp-web-util-staging.herokuapp.com' - -if (process.env.UTIL_SERVER) { - utilityServerRoot = process.env.UTIL_SERVER.replace(/\/$/, '') -} - -// Consent manager configuration -export default { - version: 3, - container: '#consent-manager', - companyName: 'HashiCorp', - privacyPolicyLink: '/privacy', - segmentWriteKey: segmentWriteKey, - utilServerRoot: utilityServerRoot, - segmentServices: [ - { - key: 'googleanalytics', - name: 'Google Analytics', - description: - 'Google Analytics is a popular service for tracking web traffic. We use this data to determine what content our users find important so that we can dedicate more resources toward it.', - category: 'Analytics', - }, - { - name: 'Hotjar', - description: - 'Hotjar is a service that generates heatmaps of where users click on our sites. We use this information to ensure that our site is not confusing, and simple to use and navigate.', - category: 'Analytics', - }, - { - name: 'LinkedIn Insight Tag', - description: - 'This small script allows us to see how effective our linkedin campaigns are by showing which users have clicked through to our site.', - category: 'Analytics', - }, - { - name: 'Marketo V2', - description: - 'Marketo is a marketing automation tool that allows us to segment users into different categories based off of their behaviors. We use this information to provide tailored information to users in our email campaigns.', - }, - ], - categories: [ - { - name: 'Functional', - description: - 'Functional services provide a utility to the website, such as the ability to log in, or to get live support. Disabling any of these scripts will cause that utility to be missing from the site.', - }, - { - name: 'Analytics', - description: - 'Analytics services keep track of page traffic and user behavior while browsing the site. We use this data internally to improve the usability and performance of the site. Disabling any of these scripts makes it more difficult for us to understand how our site is being used, and slower to improve it.', - }, - { - name: 'Email Marketing', - description: - 'Email Marketing services track user behavior while browsing the site. We use this data internally in our marketing efforts to provide users contextually relevant information based off of their behaviors. Disabling any of these scripts makes it more difficult for us to provide you contextually relevant information.', - }, - ], - additionalServices: [ - { - name: 'OptinMonster', - description: - "OptinMonster is a service that we use to show a prompt to sign up for our newsletter if it's perceived that you are interested in our content.", - category: 'Functional', - body: `var om598c8e3a6e43d,om598c8e3a6e43d_poll=function(){var r=0;return function(n,l){clearInterval(r),r=setInterval(n,l)}}();!function(e,t,n){if(e.getElementById(n)){om598c8e3a6e43d_poll(function(){if(window['om_loaded']){if(!om598c8e3a6e43d){om598c8e3a6e43d=new OptinMonsterApp();return om598c8e3a6e43d.init({"s":"35109.598c8e3a6e43d","staging":0,"dev":0,"beta":0});}}},25);return;}var d=false,o=e.createElement(t);o.id=n,o.src="https://a.optnmstr.com/app/js/api.min.js",o.async=true,o.onload=o.onreadystatechange=function(){if(!d){if(!this.readyState||this.readyState==="loaded"||this.readyState==="complete"){try{d=om_loaded=true;om598c8e3a6e43d=new OptinMonsterApp();om598c8e3a6e43d.init({"s":"35109.598c8e3a6e43d","staging":0,"dev":0,"beta":0});o.onload=o.onreadystatechange=null;}catch(t){}}}};(document.getElementsByTagName("head")[0]||document.documentElement).appendChild(o)}(document,"script","omapi-script");`, - }, - ], -} diff --git a/website/netlify.toml b/website/netlify.toml index 52eb75214..b2f42f3e3 100644 --- a/website/netlify.toml +++ b/website/netlify.toml @@ -7,10 +7,10 @@ command = "npm run static" [context.production] - environment = { HASHI_ENV = "production", NODE_ENV = "production"} + environment = { HASHI_ENV = "production", NODE_ENV = "production" } [context.deploy-preview] - environment = { HASHI_ENV = "staging" } + environment = { HASHI_ENV = "staging", NODE_ENV = "production" } [[headers]] for = "/*" diff --git a/website/next.config.js b/website/next.config.js index 1f7b742ac..72d9a5033 100644 --- a/website/next.config.js +++ b/website/next.config.js @@ -7,7 +7,6 @@ module.exports = withHashicorp({ mdx: { resolveIncludes: path.join(__dirname, 'pages/partials') }, })({ experimental: { - css: true, modern: true, rewrites: () => [ { @@ -19,5 +18,8 @@ module.exports = withHashicorp({ exportTrailingSlash: true, env: { HASHI_ENV: process.env.HASHI_ENV, + SEGMENT_WRITE_KEY: 'AjXdfmTTk1I9q9dfyePuDFHBrz1tCO3l', + BUGSNAG_CLIENT_KEY: 'de0b822b269aa57b620efd8927e03744', + BUGSNAG_SERVER_KEY: 'b6c57b27a37e531a5de94f065dd98bc0', }, }) diff --git a/website/pages/404.jsx b/website/pages/404.jsx new file mode 100644 index 000000000..6261cc08a --- /dev/null +++ b/website/pages/404.jsx @@ -0,0 +1,32 @@ +import Link from 'next/link' +import { useEffect } from 'react' + +export default function NotFound() { + useEffect(() => { + if ( + typeof window !== 'undefined' && + typeof window?.analytics?.track === 'function' && + typeof window?.document?.referrer === 'string' && + typeof window?.location?.href === 'string' + ) + window.analytics.track(window.location.href, { + category: '404 Response', + label: window.document.referrer || 'No Referrer', + }) + }, []) + + return ( +
+

Page Not Found

+

+ We're sorry but we can't find the page you're looking + for. +

+

+ + Back to Home + +

+
+ ) +} diff --git a/website/pages/_app.js b/website/pages/_app.js index 9b1d661ef..f34ccfded 100644 --- a/website/pages/_app.js +++ b/website/pages/_app.js @@ -1,87 +1,78 @@ import './style.css' -import App from 'next/app' -import NProgress from 'nprogress' -import Router from 'next/router' -import ProductSubnav from '../components/subnav' +import '@hashicorp/nextjs-scripts/lib/nprogress/style.css' + +import ProductSubnav from 'components/subnav' import MegaNav from '@hashicorp/react-mega-nav' -import Footer from '../components/footer' -import { ConsentManager, open } from '@hashicorp/react-consent-manager' -import consentManagerConfig from '../lib/consent-manager-config' -import bugsnagClient from '../lib/bugsnag' +import Footer from 'components/footer' import Error from './_error' import Head from 'next/head' import HashiHead from '@hashicorp/react-head' +import Router from 'next/router' +import NProgress from '@hashicorp/nextjs-scripts/lib/nprogress' +import createConsentManager from '@hashicorp/nextjs-scripts/lib/consent-manager' +import { ErrorBoundary } from '@hashicorp/nextjs-scripts/lib/bugsnag' +import useAnchorLinkAnalytics from '@hashicorp/nextjs-scripts/lib/anchor-link-analytics' -Router.events.on('routeChangeStart', NProgress.start) -Router.events.on('routeChangeError', NProgress.done) -Router.events.on('routeChangeComplete', (url) => { - setTimeout(() => window.analytics.page(url), 0) - NProgress.done() +NProgress({ Router }) +const { ConsentManager, openConsentManager } = createConsentManager({ + preset: 'oss', }) -// Bugsnag -const ErrorBoundary = bugsnagClient.getPlugin('react') +export default function App({ Component, pageProps }) { + useAnchorLinkAnalytics() -class NextApp extends App { - static async getInitialProps({ Component, ctx }) { - let pageProps = {} - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx) - } else if (Component.isMDXComponent) { - // fix for https://github.com/mdx-js/mdx/issues/382 - const mdxLayoutComponent = Component({}).props.originalType - if (mdxLayoutComponent.getInitialProps) { - pageProps = await mdxLayoutComponent.getInitialProps(ctx) - } - } - - return { pageProps } - } - - render() { - const { Component, pageProps } = this.props - - return ( - - - - -
- -
-