From 8d7ce6c60ceffc8de5b5e5e72a816fb0aaadda6b Mon Sep 17 00:00:00 2001 From: Alexandra Goff Date: Tue, 1 Oct 2024 13:46:53 -0700 Subject: [PATCH] feat: app directory (#541) --- .eslintrc.js | 8 ++ app/[locale]/error.tsx | 12 ++ app/[locale]/layout.tsx | 80 ++++++++++++ app/[locale]/page.tsx | 72 +++++++++++ app/[locale]/scripts.tsx | 81 ++++++++++++ app/layout.tsx | 9 ++ app/not-found.tsx | 45 +++++++ components/global/Body/index.js | 2 + components/global/Footer/index.js | 1 + components/global/Header/index.js | 25 ++-- components/organisms/Error/index.tsx | 21 ++++ components/organisms/PageWrapper/index.tsx | 48 ++++++++ components/templates/HomePage/index.js | 10 +- contexts/Authentication.js | 7 +- contexts/GlobalData.js | 1 + contexts/{i18next.jsx => i18next.tsx} | 11 +- hooks/useAuthentication.js | 31 +---- lib/api/entries.js | 2 +- lib/api/globals/index.ts | 85 +++++++++++++ lib/api/metadata/index.ts | 45 +++++++ lib/api/noirlabReleases.js | 11 +- lib/helpers/site.ts | 7 ++ lib/i18n/client.js | 3 +- lib/i18n/{index.js => index.ts} | 12 +- lib/i18n/server.ts | 11 ++ lib/styles/font.ts | 12 ++ lib/styles/registry.tsx | 29 +++++ lib/utils.js | 14 --- pages/[locale]/[...uriSegments]/index.js | 2 +- pages/[locale]/index.js | 137 --------------------- pages/_app.js | 9 +- pages/api/preview.js | 6 +- pages/api/revalidate.js | 16 ++- theme/styles/base/_base.scss | 21 +--- theme/styles/globalStyles.js | 1 + tsconfig.json | 115 ++++++----------- types/next.d.ts | 14 +++ 37 files changed, 683 insertions(+), 333 deletions(-) create mode 100644 app/[locale]/error.tsx create mode 100644 app/[locale]/layout.tsx create mode 100644 app/[locale]/page.tsx create mode 100644 app/[locale]/scripts.tsx create mode 100644 app/layout.tsx create mode 100644 app/not-found.tsx create mode 100644 components/organisms/Error/index.tsx create mode 100644 components/organisms/PageWrapper/index.tsx rename contexts/{i18next.jsx => i18next.tsx} (65%) create mode 100644 lib/api/globals/index.ts create mode 100644 lib/api/metadata/index.ts create mode 100644 lib/helpers/site.ts rename lib/i18n/{index.js => index.ts} (83%) create mode 100644 lib/i18n/server.ts create mode 100644 lib/styles/font.ts create mode 100644 lib/styles/registry.tsx delete mode 100644 pages/[locale]/index.js create mode 100644 types/next.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 94955577..2907fa79 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -104,4 +104,12 @@ module.exports = { "plugin:storybook/recommended", "plugin:cypress/recommended", ], + overrides: [ + { + files: ["*.ts", "*.tsx"], + rules: { + "no-undef": "off", + }, + }, + ], }; diff --git a/app/[locale]/error.tsx b/app/[locale]/error.tsx new file mode 100644 index 00000000..3750bf93 --- /dev/null +++ b/app/[locale]/error.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { FunctionComponent } from "react"; +import { useTranslation } from "react-i18next"; +import Error from "@/components/organisms/Error"; + +const ErrorPage: FunctionComponent = ({ error }) => { + const { t } = useTranslation(); + return ; +}; + +export default ErrorPage; diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 00000000..ea09fb79 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,80 @@ +import { FunctionComponent, PropsWithChildren, Suspense } from "react"; +import { Metadata } from "next"; +import { GoogleOAuthProvider } from "@react-oauth/google"; +import GlobalStyles from "@/styles/globalStyles"; +import { getGlobalData } from "@/lib/api/globals"; +import { languages } from "@/lib/i18n/settings"; +import SourceSansPro from "@/lib/styles/font"; +import StyledComponentsRegistry from "@/lib/styles/registry"; +import I18NextClientProvider from "@/contexts/i18next"; +import { AuthenticationContextProvider } from "@/contexts/Authentication"; +import PageWrapper from "@/components/organisms/PageWrapper"; +import RootScripts from "./scripts"; + +const GOOGLE_APP_ID = process.env.NEXT_PUBLIC_GOOGLE_APP_ID || ""; + +export async function generateMetadata({ + params: { locale }, +}: LocaleProps): Promise { + const { siteInfo: metadata } = await getGlobalData(locale); + const { siteTitle, siteDescription, siteImage, language } = metadata; + const { url, width, height, altText: alt } = siteImage[0]; + + return { + title: { + default: siteTitle, + template: `%s | ${siteTitle}`, + }, + description: siteDescription, + manifest: "/site.webmanifest", + openGraph: { + locale: language, + images: [{ url, width, height, alt }], + }, + icons: { + icon: [ + { 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" }, + ], + }, + }; +} + +export const generateStaticParams = () => { + return languages.map((locale) => { + return { locale }; + }); +}; + +export const dynamicParams = false; + +const LocaleLayout: FunctionComponent> = async ({ + params: { locale }, + children, +}) => { + return ( + + + + + + + + + + {children} + + + + + + + + + ); +}; + +export default LocaleLayout; diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx new file mode 100644 index 00000000..3a328328 --- /dev/null +++ b/app/[locale]/page.tsx @@ -0,0 +1,72 @@ +import { FunctionComponent } from "react"; +import { notFound } from "next/navigation"; +import { draftMode } from "next/headers"; +import { getEntrySectionTypeByUri, getEntryDataByUri } from "@/api/entry"; +import { setEdcLog } from "@/lib/edc-log"; +import { getSiteFromLocale } from "@/lib/helpers/site"; +import { purgeNextjsStaticFiles } from "@/lib/purgeStaticFiles"; +import HomePageTemplate from "@/templates/HomePage"; +import { Metadata } from "next"; +import { getEntryMetadataByUri } from "@/lib/api/metadata"; + +const CRAFT_HOMEPAGE_URI = "__home__"; + +export async function generateMetadata({ + params: { locale }, + searchParams = {}, +}: LocaleProps): Promise { + const uri = CRAFT_HOMEPAGE_URI; + let previewToken: string | undefined; + + if (draftMode().isEnabled) { + previewToken = Array.isArray(searchParams.preview) + ? searchParams.preview[0] + : searchParams?.preview; + } + + const { + entry: { title, description }, + } = await getEntryMetadataByUri(uri, locale, previewToken); + + return { title, description }; +} + +const RootPage: FunctionComponent = async ({ + params: { locale }, + searchParams = {}, +}) => { + const runId = Date.now().toString(); + const site = getSiteFromLocale(locale); + const uri = CRAFT_HOMEPAGE_URI; + let previewToken: string | undefined; + + if (draftMode().isEnabled) { + previewToken = Array.isArray(searchParams.preview) + ? searchParams.preview[0] + : searchParams.preview; + } + + const entrySectionType = await getEntrySectionTypeByUri( + uri, + site, + previewToken + ); + + const { sectionHandle: section, typeHandle: type } = entrySectionType; + const data = await getEntryDataByUri(uri, section, type, site, previewToken); + + const currentId = data?.id || data?.entry?.id; + + // Handle 404 if there is no data + if (!currentId) { + setEdcLog(runId, "404 encountered building for " + uri, "BUILD_ERROR_404"); + await purgeNextjsStaticFiles(locale); + notFound(); + } + + setEdcLog(runId, "Done building for " + uri, "BUILD_COMPLETE"); + + return ; +}; + +export default RootPage; diff --git a/app/[locale]/scripts.tsx b/app/[locale]/scripts.tsx new file mode 100644 index 00000000..e3533118 --- /dev/null +++ b/app/[locale]/scripts.tsx @@ -0,0 +1,81 @@ +import { FunctionComponent } from "react"; +import Script from "next/script"; + +const PLAUSIBLE_DOMAIN = process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN; +const SURVEY_SPARROW = process.env.NEXT_PUBLIC_SURVEY_SPARROW; + +const RootScripts: FunctionComponent<{ locale: string }> = ({ locale }) => { + return ( + <> + {PLAUSIBLE_DOMAIN && ( +