From 5fc4e5912768f11a32cafb134667b6b16baf4b8e Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 28 Nov 2023 12:18:15 -0700 Subject: [PATCH 01/21] wip --- packages/nextjs/app/global.css | 13 ++- .../{pages/index.tsx => app/home-page.tsx} | 31 +------ packages/nextjs/app/layout.tsx | 25 ++++- packages/nextjs/app/page.tsx | 32 +++++++ packages/nextjs/app/ui/shared-ui.ts | 5 + packages/nextjs/components/CartContext.tsx | 2 + packages/nextjs/components/Header/Header.tsx | 6 +- packages/nextjs/package.json | 6 +- packages/nextjs/tailwind.config.js | 1 + packages/shared-ui/components/cart/Cart.tsx | 2 +- pnpm-lock.yaml | 92 +++++++++---------- 11 files changed, 134 insertions(+), 81 deletions(-) rename packages/nextjs/{pages/index.tsx => app/home-page.tsx} (77%) create mode 100644 packages/nextjs/app/page.tsx create mode 100644 packages/nextjs/app/ui/shared-ui.ts diff --git a/packages/nextjs/app/global.css b/packages/nextjs/app/global.css index 60925395..f4484f31 100644 --- a/packages/nextjs/app/global.css +++ b/packages/nextjs/app/global.css @@ -1,3 +1,14 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + body { - margin: 0; + @apply bg-secondary; +} + + +code { + @apply rounded-md bg-black/5 px-1.5 py-0.5; + font-family: 'JetBrains Mono', monospace; + font-size: 0.9rem; } \ No newline at end of file diff --git a/packages/nextjs/pages/index.tsx b/packages/nextjs/app/home-page.tsx similarity index 77% rename from packages/nextjs/pages/index.tsx rename to packages/nextjs/app/home-page.tsx index eb848b02..84d17103 100644 --- a/packages/nextjs/pages/index.tsx +++ b/packages/nextjs/app/home-page.tsx @@ -1,21 +1,18 @@ +"use client"; + import type { Categories, Products } from "utils/groqTypes/ProductList"; import * as React from "react"; -import { GetServerSideProps, NextPage } from "next"; +import { NextPage } from "next"; import { FiArrowRight } from "react-icons/fi"; import Link from "next/link"; import NextImage from "next/legacy/image"; -import { Button, FeaturedQuote } from "shared-ui"; -import { setCachingHeaders } from "utils/setCachingHeaders"; +import { Button, FeaturedQuote } from "./ui/shared-ui"; import { localImageLoader } from "utils/localImageLoader"; -import { SanityType } from "utils/consts"; -import { getAllCategories } from "utils/getAllCategoriesQuery"; -import { getRecommendations } from "utils/getRecommendationsQuery"; import featuredImg from "assets/featured-story.jpg"; import { FeaturedList } from "components/FeaturedList"; import { Image } from "components/Image"; -import { PageHead } from "components/PageHead"; interface PageProps { data?: { @@ -27,10 +24,6 @@ interface PageProps { const Home: NextPage = ({ data }) => { return ( <> -
@@ -101,20 +94,4 @@ const TitleBanner = ({ children }: React.PropsWithChildren) => (
); -export const getServerSideProps = (async ({ res }) => { - setCachingHeaders(res, [SanityType.Category, SanityType.CategoryImage]); - - const categories = await getAllCategories(); - const products = await getRecommendations(); - - return { - props: { - data: { - products, - categories, - }, - }, - }; -}) satisfies GetServerSideProps; - export default Home; diff --git a/packages/nextjs/app/layout.tsx b/packages/nextjs/app/layout.tsx index 0f479fcc..7a2a0bd4 100644 --- a/packages/nextjs/app/layout.tsx +++ b/packages/nextjs/app/layout.tsx @@ -1,9 +1,32 @@ +import { Footer, MobileNavProvider } from "./ui/shared-ui"; import "./global.css"; +import { Header } from "components/Header/Header"; +import { Metadata } from "next"; +import { CartProvider } from "components/CartContext"; + +export const metadata: Metadata = { + title: "Home", + description: "Welcome to Next.js", +}; + +export const viewport = { + width: "device-width", +}; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - {children} + + +
+ +
+ +
{children}
+
+
+
+ ); } diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx new file mode 100644 index 00000000..77b42ff7 --- /dev/null +++ b/packages/nextjs/app/page.tsx @@ -0,0 +1,32 @@ +import { Metadata } from "next"; +import HomePage from "./home-page"; +import { getAllCategories } from "utils/getAllCategoriesQuery"; +import { getRecommendations } from "utils/getRecommendationsQuery"; + +export const metadata: Metadata = { + title: "Home – Formidable Boulangerie", + description: "Formidable Boulangerie home page. A showcase of Next.js, Sanity CMS, and Fastly CDN.", + openGraph: { + title: "Home – Formidable Boulangerie", + description: "Formidable Boulangerie home page. A showcase of Next.js, Sanity CMS, and Fastly CDN.", + }, +}; + +async function getData() { + const categories = await getAllCategories(); + const products = await getRecommendations(); + + return { + products, + categories, + }; +} + +export default async function Page() { + const data = await getData(); + return ( + <> + ) + + ); +} diff --git a/packages/nextjs/app/ui/shared-ui.ts b/packages/nextjs/app/ui/shared-ui.ts new file mode 100644 index 00000000..325b7d14 --- /dev/null +++ b/packages/nextjs/app/ui/shared-ui.ts @@ -0,0 +1,5 @@ +"use client"; + +// TODO: separate imports so this isn't needed +// for server-compatible components like Footer +export * from "shared-ui"; diff --git a/packages/nextjs/components/CartContext.tsx b/packages/nextjs/components/CartContext.tsx index 1a06fd89..47216339 100644 --- a/packages/nextjs/components/CartContext.tsx +++ b/packages/nextjs/components/CartContext.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as React from "react"; import { q } from "groqd"; import { CartItem, CartUpdate, CartProvider as SharedCartProvider } from "shared-ui"; diff --git a/packages/nextjs/components/Header/Header.tsx b/packages/nextjs/components/Header/Header.tsx index 829082a2..49a03b71 100644 --- a/packages/nextjs/components/Header/Header.tsx +++ b/packages/nextjs/components/Header/Header.tsx @@ -1,8 +1,10 @@ +"use client"; // TODO: move down the tree + import * as React from "react"; import Link from "next/link"; import { Button, useCart, Header as BaseHeader, useMobileNav } from "shared-ui"; import { Search } from "components/Search"; -import { useRouter } from "next/router"; +import { usePathname } from "next/navigation"; import { NAV_ITEMS } from "shared-ui"; import { DesktopNavItem } from "shared-ui"; import { Cart } from "shared-ui"; @@ -12,7 +14,7 @@ import { MobileNavMenu } from "shared-ui"; export const Header = () => { const { toggleCartOpen } = useCart(); - const { pathname } = useRouter(); + const pathname = usePathname() || ""; const { onMobileNavClose } = useMobileNav(); return ( diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3d43fccb..e7488df3 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -36,7 +36,7 @@ "groqd-playground": "^0.0.12", "jsdom": "^20.0.0", "lodash.debounce": "^4.0.8", - "next": "^13.4.0", + "next": "^14.0.3", "next-sanity": "^4.2.0", "node-fetch": "^3.2.10", "nuka-carousel": "^5.3.0", @@ -45,8 +45,8 @@ "react-icons": "^4.4.0", "react-is": "^18.2.0", "sanity": "^3.9.1", - "styled-components": "^5.2.0", - "shared-ui": "workspace:*" + "shared-ui": "workspace:*", + "styled-components": "^5.2.0" }, "devDependencies": { "@portabletext/types": "^2.0.0", diff --git a/packages/nextjs/tailwind.config.js b/packages/nextjs/tailwind.config.js index 145d9d32..6dc93414 100644 --- a/packages/nextjs/tailwind.config.js +++ b/packages/nextjs/tailwind.config.js @@ -3,6 +3,7 @@ const defaultTheme = require("tailwindcss/defaultTheme"); /** @type {import('tailwindcss').Config} */ module.exports = { content: [ + "./app/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}", "./views/**/*.{js,jsx,ts,tsx}", diff --git a/packages/shared-ui/components/cart/Cart.tsx b/packages/shared-ui/components/cart/Cart.tsx index cff31d03..30402f76 100644 --- a/packages/shared-ui/components/cart/Cart.tsx +++ b/packages/shared-ui/components/cart/Cart.tsx @@ -47,7 +47,7 @@ export const Cart = ({ onMobileNavClose, children }: React.PropsWithChildren - Cart + Cart {isLoading ? ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0a8e751..ed69eca0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,11 +130,11 @@ importers: specifier: ^4.0.8 version: 4.0.8 next: - specifier: ^13.4.0 - version: 13.5.6(react-dom@18.2.0)(react@18.2.0) + specifier: ^14.0.3 + version: 14.0.3(react-dom@18.2.0)(react@18.2.0) next-sanity: specifier: ^4.2.0 - version: 4.3.3(@sanity/ui@1.9.0)(@types/styled-components@5.1.29)(next@13.5.6)(react@18.2.0)(sanity@3.19.1)(styled-components@5.3.11) + version: 4.3.3(@sanity/ui@1.9.0)(@types/styled-components@5.1.29)(next@14.0.3)(react@18.2.0)(sanity@3.19.1)(styled-components@5.3.11) node-fetch: specifier: ^3.2.10 version: 3.3.2 @@ -171,7 +171,7 @@ importers: version: 3.19.1 '@storybook/nextjs': specifier: ^7.4.0 - version: 7.5.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(next@13.5.6)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) + version: 7.5.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(next@14.0.3)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@testing-library/jest-dom': specifier: ^5.16.5 version: 5.17.0 @@ -3018,7 +3018,7 @@ packages: '@motionone/easing': 10.16.3 '@motionone/types': 10.16.3 '@motionone/utils': 10.16.3 - tslib: 2.4.0 + tslib: 2.6.2 dev: false /@motionone/dom@10.16.4: @@ -3029,14 +3029,14 @@ packages: '@motionone/types': 10.16.3 '@motionone/utils': 10.16.3 hey-listen: 1.0.8 - tslib: 2.4.0 + tslib: 2.6.2 dev: false /@motionone/easing@10.16.3: resolution: {integrity: sha512-HWTMZbTmZojzwEuKT/xCdvoMPXjYSyQvuVM6jmM0yoGU6BWzsmYMeB4bn38UFf618fJCNtP9XeC/zxtKWfbr0w==} dependencies: '@motionone/utils': 10.16.3 - tslib: 2.4.0 + tslib: 2.6.2 dev: false /@motionone/generators@10.16.4: @@ -3044,7 +3044,7 @@ packages: dependencies: '@motionone/types': 10.16.3 '@motionone/utils': 10.16.3 - tslib: 2.4.0 + tslib: 2.6.2 dev: false /@motionone/types@10.16.3: @@ -3056,7 +3056,7 @@ packages: dependencies: '@motionone/types': 10.16.3 hey-listen: 1.0.8 - tslib: 2.4.0 + tslib: 2.6.2 dev: false /@mswjs/cookies@0.2.2: @@ -3091,8 +3091,8 @@ packages: tar-fs: 2.1.1 dev: true - /@next/env@13.5.6: - resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==} + /@next/env@14.0.3: + resolution: {integrity: sha512-7xRqh9nMvP5xrW4/+L0jgRRX+HoNRGnfJpD+5Wq6/13j3dsdzxO3BCXn7D3hMqsDb+vjZnJq+vI7+EtgrYZTeA==} /@next/eslint-plugin-next@12.2.5: resolution: {integrity: sha512-VBjVbmqEzGiOTBq4+wpeVXt/KgknnGB6ahvC/AxiIGnN93/RCSyXhFRI4uSfftM2Ba3w7ZO7076bfKasZsA0fw==} @@ -3100,72 +3100,72 @@ packages: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.5.6: - resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==} + /@next/swc-darwin-arm64@14.0.3: + resolution: {integrity: sha512-64JbSvi3nbbcEtyitNn2LEDS/hcleAFpHdykpcnrstITFlzFgB/bW0ER5/SJJwUPj+ZPY+z3e+1jAfcczRLVGw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@next/swc-darwin-x64@13.5.6: - resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==} + /@next/swc-darwin-x64@14.0.3: + resolution: {integrity: sha512-RkTf+KbAD0SgYdVn1XzqE/+sIxYGB7NLMZRn9I4Z24afrhUpVJx6L8hsRnIwxz3ERE2NFURNliPjJ2QNfnWicQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@next/swc-linux-arm64-gnu@13.5.6: - resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==} + /@next/swc-linux-arm64-gnu@14.0.3: + resolution: {integrity: sha512-3tBWGgz7M9RKLO6sPWC6c4pAw4geujSwQ7q7Si4d6bo0l6cLs4tmO+lnSwFp1Tm3lxwfMk0SgkJT7EdwYSJvcg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-arm64-musl@13.5.6: - resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==} + /@next/swc-linux-arm64-musl@14.0.3: + resolution: {integrity: sha512-v0v8Kb8j8T23jvVUWZeA2D8+izWspeyeDGNaT2/mTHWp7+37fiNfL8bmBWiOmeumXkacM/AB0XOUQvEbncSnHA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-gnu@13.5.6: - resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==} + /@next/swc-linux-x64-gnu@14.0.3: + resolution: {integrity: sha512-VM1aE1tJKLBwMGtyBR21yy+STfl0MapMQnNrXkxeyLs0GFv/kZqXS5Jw/TQ3TSUnbv0QPDf/X8sDXuMtSgG6eg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-linux-x64-musl@13.5.6: - resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==} + /@next/swc-linux-x64-musl@14.0.3: + resolution: {integrity: sha512-64EnmKy18MYFL5CzLaSuUn561hbO1Gk16jM/KHznYP3iCIfF9e3yULtHaMy0D8zbHfxset9LTOv6cuYKJgcOxg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@next/swc-win32-arm64-msvc@13.5.6: - resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==} + /@next/swc-win32-arm64-msvc@14.0.3: + resolution: {integrity: sha512-WRDp8QrmsL1bbGtsh5GqQ/KWulmrnMBgbnb+59qNTW1kVi1nG/2ndZLkcbs2GX7NpFLlToLRMWSQXmPzQm4tog==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-ia32-msvc@13.5.6: - resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==} + /@next/swc-win32-ia32-msvc@14.0.3: + resolution: {integrity: sha512-EKffQeqCrj+t6qFFhIFTRoqb2QwX1mU7iTOvMyLbYw3QtqTw9sMwjykyiMlZlrfm2a4fA84+/aeW+PMg1MjuTg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@next/swc-win32-x64-msvc@13.5.6: - resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==} + /@next/swc-win32-x64-msvc@14.0.3: + resolution: {integrity: sha512-ERhKPSJ1vQrPiwrs15Pjz/rvDHZmkmvbf/BjPN/UCOI++ODftT0GtasDPi0j+y6PPJi5HsXw+dpRaXUaw4vjuQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -5203,7 +5203,7 @@ packages: resolution: {integrity: sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==} dev: true - /@storybook/nextjs@7.5.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(next@13.5.6)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2): + /@storybook/nextjs@7.5.3(@types/react-dom@18.2.0)(@types/react@18.2.0)(next@14.0.3)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2): resolution: {integrity: sha512-PYi9AJga6x46IN4aub9CuiKNF9mT3maTh1F9dXqE4kO+ZrbesiKcJ3Uud0D78c56/Jlr8FmHEDpO19OlgRM4kQ==} engines: {node: '>=16.0.0'} peerDependencies: @@ -5250,7 +5250,7 @@ packages: fs-extra: 11.1.1 image-size: 1.0.2 loader-utils: 3.2.1 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) node-polyfill-webpack-plugin: 2.0.1 pnp-webpack-plugin: 1.7.0(typescript@5.2.2) postcss: 8.4.31 @@ -13968,7 +13968,7 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true - /next-sanity@4.3.3(@sanity/ui@1.9.0)(@types/styled-components@5.1.29)(next@13.5.6)(react@18.2.0)(sanity@3.19.1)(styled-components@5.3.11): + /next-sanity@4.3.3(@sanity/ui@1.9.0)(@types/styled-components@5.1.29)(next@14.0.3)(react@18.2.0)(sanity@3.19.1)(styled-components@5.3.11): resolution: {integrity: sha512-537xLC4hpTgV3SGj6w4aXvBvm/nHdyZGtZ5IcZpH33p70J7UjptF4+D4FvWKZGPjF3v3SFk75AHjxWVPORL9RA==} engines: {node: '>=16'} peerDependencies: @@ -13987,7 +13987,7 @@ packages: '@sanity/webhook': 2.0.0 '@types/styled-components': 5.1.29 groq: 3.19.1 - next: 13.5.6(react-dom@18.2.0)(react@18.2.0) + next: 14.0.3(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 sanity: 3.19.1(@types/node@20.8.7)(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11) styled-components: 5.3.11(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) @@ -13995,9 +13995,9 @@ packages: - supports-color dev: false - /next@13.5.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==} - engines: {node: '>=16.14.0'} + /next@14.0.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-AbYdRNfImBr3XGtvnwOxq8ekVCwbFTv/UJoLwmaX89nk9i051AEY4/HAWzU0YpaTDw8IofUpmuIlvzWF13jxIw==} + engines: {node: '>=18.17.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -14010,7 +14010,7 @@ packages: sass: optional: true dependencies: - '@next/env': 13.5.6 + '@next/env': 14.0.3 '@swc/helpers': 0.5.2 busboy: 1.6.0 caniuse-lite: 1.0.30001561 @@ -14020,15 +14020,15 @@ packages: styled-jsx: 5.1.1(@babel/core@7.23.2)(react@18.2.0) watchpack: 2.4.0 optionalDependencies: - '@next/swc-darwin-arm64': 13.5.6 - '@next/swc-darwin-x64': 13.5.6 - '@next/swc-linux-arm64-gnu': 13.5.6 - '@next/swc-linux-arm64-musl': 13.5.6 - '@next/swc-linux-x64-gnu': 13.5.6 - '@next/swc-linux-x64-musl': 13.5.6 - '@next/swc-win32-arm64-msvc': 13.5.6 - '@next/swc-win32-ia32-msvc': 13.5.6 - '@next/swc-win32-x64-msvc': 13.5.6 + '@next/swc-darwin-arm64': 14.0.3 + '@next/swc-darwin-x64': 14.0.3 + '@next/swc-linux-arm64-gnu': 14.0.3 + '@next/swc-linux-arm64-musl': 14.0.3 + '@next/swc-linux-x64-gnu': 14.0.3 + '@next/swc-linux-x64-musl': 14.0.3 + '@next/swc-win32-arm64-msvc': 14.0.3 + '@next/swc-win32-ia32-msvc': 14.0.3 + '@next/swc-win32-x64-msvc': 14.0.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros From 87c6310de1022653b52be3a67ec0ae052c2868a2 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 28 Nov 2023 12:18:43 -0700 Subject: [PATCH 02/21] fonts --- packages/nextjs/app/global.css | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/packages/nextjs/app/global.css b/packages/nextjs/app/global.css index f4484f31..8c7d56cd 100644 --- a/packages/nextjs/app/global.css +++ b/packages/nextjs/app/global.css @@ -6,6 +6,77 @@ body { @apply bg-secondary; } +/* Cabinet Grotesk */ +/* See: https://api.fontshare.com/v2/css?f[]=cabinet-grotesk@500,700,400,300&display=swap */ +/* See: https://nextjs.org/docs/app/building-your-application/styling/css-modules#external-stylesheets */ +@font-face { + font-family: 'Cabinet Grotesk'; + src: url('//cdn.fontshare.com/wf/7GWNQ5AHAZORLOWZ7ELKPLOIQITAR5S5/NYLYMGXMB4RANWVNJSIHG2IKPZ44CN5E/MT4CWVHB3N2C6KFUZ75QK4JQ2FYK4J4M.woff2') format('woff2'), + url('//cdn.fontshare.com/wf/7GWNQ5AHAZORLOWZ7ELKPLOIQITAR5S5/NYLYMGXMB4RANWVNJSIHG2IKPZ44CN5E/MT4CWVHB3N2C6KFUZ75QK4JQ2FYK4J4M.woff') format('woff'), + url('//cdn.fontshare.com/wf/7GWNQ5AHAZORLOWZ7ELKPLOIQITAR5S5/NYLYMGXMB4RANWVNJSIHG2IKPZ44CN5E/MT4CWVHB3N2C6KFUZ75QK4JQ2FYK4J4M.ttf') format('truetype'); + font-weight: 300; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: 'Cabinet Grotesk'; + src: url('//cdn.fontshare.com/wf/J6PPRPKWXDUIYA47IXLEQB4R4OPVYDQH/N2ZXAXWEHVMLISD2TIXJC7EF4GOY43L4/NXM4Z4TDCMYWBZ7AVI2N6DQ5VMWNENMU.woff2') format('woff2'), + url('//cdn.fontshare.com/wf/J6PPRPKWXDUIYA47IXLEQB4R4OPVYDQH/N2ZXAXWEHVMLISD2TIXJC7EF4GOY43L4/NXM4Z4TDCMYWBZ7AVI2N6DQ5VMWNENMU.woff') format('woff'), + url('//cdn.fontshare.com/wf/J6PPRPKWXDUIYA47IXLEQB4R4OPVYDQH/N2ZXAXWEHVMLISD2TIXJC7EF4GOY43L4/NXM4Z4TDCMYWBZ7AVI2N6DQ5VMWNENMU.ttf') format('truetype'); + font-weight: 400; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: 'Cabinet Grotesk'; + src: url('//cdn.fontshare.com/wf/CKQBK2QBTCDREE7L3MXZ3PPW7LDNJCWU/OTOY7FQFSFOJVZKJWKO2EHUJLOGBDN4Q/4CO2ETY7NITKLUDKMYJ75RHJSPHOJ7XT.woff2') format('woff2'), + url('//cdn.fontshare.com/wf/CKQBK2QBTCDREE7L3MXZ3PPW7LDNJCWU/OTOY7FQFSFOJVZKJWKO2EHUJLOGBDN4Q/4CO2ETY7NITKLUDKMYJ75RHJSPHOJ7XT.woff') format('woff'), + url('//cdn.fontshare.com/wf/CKQBK2QBTCDREE7L3MXZ3PPW7LDNJCWU/OTOY7FQFSFOJVZKJWKO2EHUJLOGBDN4Q/4CO2ETY7NITKLUDKMYJ75RHJSPHOJ7XT.ttf') format('truetype'); + font-weight: 500; + font-display: swap; + font-style: normal; +} + +@font-face { + font-family: 'Cabinet Grotesk'; + src: url('//cdn.fontshare.com/wf/XMXWOHABYLQDJ42L65EFRYNVRY37HQCB/B2O4O6V3JMFM2WDCYQI3A47L5U4THDUL/WN5274VQ3AUBDFP74GB4EC4XYJ3EKVNE.woff2') format('woff2'), + url('//cdn.fontshare.com/wf/XMXWOHABYLQDJ42L65EFRYNVRY37HQCB/B2O4O6V3JMFM2WDCYQI3A47L5U4THDUL/WN5274VQ3AUBDFP74GB4EC4XYJ3EKVNE.woff') format('woff'), + url('//cdn.fontshare.com/wf/XMXWOHABYLQDJ42L65EFRYNVRY37HQCB/B2O4O6V3JMFM2WDCYQI3A47L5U4THDUL/WN5274VQ3AUBDFP74GB4EC4XYJ3EKVNE.ttf') format('truetype'); + font-weight: 700; + font-display: swap; + font-style: normal; +} + + +@font-face { + font-family: "JeanLuc"; + src: url('../assets/fonts/jeanluc/jeanlucweb-bold.woff'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: "JeanLuc"; + src: url('../assets/fonts/jeanluc/jeanlucweb-thin.woff'); + font-weight: auto; + font-style: normal; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url('../assets/fonts/jetbrainsmono/JetBrainsMono-Bold.woff2'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url('../assets/fonts/jetbrainsmono/JetBrainsMono-Regular.woff2'); + font-weight: auto; + font-style: normal; +} code { @apply rounded-md bg-black/5 px-1.5 py-0.5; From 0652f76da932f96be2aede48c913d0825ddd3b59 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 28 Nov 2023 13:51:23 -0700 Subject: [PATCH 03/21] page animations --- packages/nextjs/app/layout.tsx | 25 ++++++++++++++++--------- packages/nextjs/app/template.tsx | 8 ++++++++ packages/nextjs/app/ui/framer.tsx | 3 +++ 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 packages/nextjs/app/template.tsx create mode 100644 packages/nextjs/app/ui/framer.tsx diff --git a/packages/nextjs/app/layout.tsx b/packages/nextjs/app/layout.tsx index 7a2a0bd4..205793b2 100644 --- a/packages/nextjs/app/layout.tsx +++ b/packages/nextjs/app/layout.tsx @@ -3,6 +3,7 @@ import "./global.css"; import { Header } from "components/Header/Header"; import { Metadata } from "next"; import { CartProvider } from "components/CartContext"; +import { AnimatePresence, MotionConfig } from "./ui/framer"; export const metadata: Metadata = { title: "Home", @@ -17,15 +18,21 @@ export default function RootLayout({ children }: { children: React.ReactNode }) return ( - -
- -
- -
{children}
-
-
-
+ + +
+ +
+ +
+ + {children} + +
+
+
+
+
); diff --git a/packages/nextjs/app/template.tsx b/packages/nextjs/app/template.tsx new file mode 100644 index 00000000..dbf0fa0b --- /dev/null +++ b/packages/nextjs/app/template.tsx @@ -0,0 +1,8 @@ +"use client"; + +import { FadeInOut } from "./ui/shared-ui"; + +// TODO: exit animations currently do not work in next app router +export default function Template({ children }: { children: React.ReactNode }) { + return {children}; +} diff --git a/packages/nextjs/app/ui/framer.tsx b/packages/nextjs/app/ui/framer.tsx new file mode 100644 index 00000000..ead38288 --- /dev/null +++ b/packages/nextjs/app/ui/framer.tsx @@ -0,0 +1,3 @@ +"use client"; + +export { MotionConfig, AnimatePresence } from "framer-motion"; From 8fc6368be05d7b83c886480c94d0746139dd7403 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 28 Nov 2023 13:54:12 -0700 Subject: [PATCH 04/21] mv --- packages/nextjs/app/{ => migration}/home-page.tsx | 2 +- packages/nextjs/app/page.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/nextjs/app/{ => migration}/home-page.tsx (98%) diff --git a/packages/nextjs/app/home-page.tsx b/packages/nextjs/app/migration/home-page.tsx similarity index 98% rename from packages/nextjs/app/home-page.tsx rename to packages/nextjs/app/migration/home-page.tsx index 84d17103..645d6b15 100644 --- a/packages/nextjs/app/home-page.tsx +++ b/packages/nextjs/app/migration/home-page.tsx @@ -7,7 +7,7 @@ import { FiArrowRight } from "react-icons/fi"; import Link from "next/link"; import NextImage from "next/legacy/image"; -import { Button, FeaturedQuote } from "./ui/shared-ui"; +import { Button, FeaturedQuote } from "../ui/shared-ui"; import { localImageLoader } from "utils/localImageLoader"; import featuredImg from "assets/featured-story.jpg"; diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 77b42ff7..6fe79c8d 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -1,5 +1,5 @@ import { Metadata } from "next"; -import HomePage from "./home-page"; +import HomePage from "./migration/home-page"; import { getAllCategories } from "utils/getAllCategoriesQuery"; import { getRecommendations } from "utils/getRecommendationsQuery"; From 53c2f02ddbc58586e77d9245acebf8ecba8fcd70 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Thu, 30 Nov 2023 10:08:20 -0700 Subject: [PATCH 05/21] wip --- packages/nextjs/middleware.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 packages/nextjs/middleware.ts diff --git a/packages/nextjs/middleware.ts b/packages/nextjs/middleware.ts new file mode 100644 index 00000000..fafbd9f1 --- /dev/null +++ b/packages/nextjs/middleware.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from "next/server"; +import { SanityType } from "utils/consts"; + +export const config = { + matcher: ["/", "/categories", "/products", "/products/:slug*"], +}; + +export function middleware(request: NextRequest) { + const responseHeaders = new Headers(); + responseHeaders.set("Cache-Control", "public, max-age=0"); + responseHeaders.set("Surrogate-Control", "max-age=604800, stale-while-revalidate=120000, stale-if-error=600000"); + + if (request.nextUrl.pathname === "/" || request.nextUrl.pathname === "/categories") { + const keys = [SanityType.Category, SanityType.CategoryImage]; + responseHeaders.set("Surrogate-Key", keys.join(" ")); + } + + if (request.nextUrl.pathname === "/products") { + const keys = [SanityType.Product, SanityType.Style, SanityType.Flavour, SanityType.Variant]; + responseHeaders.set("Surrogate-Key", keys.join(" ")); + } else if (request.nextUrl.pathname.startsWith("/products")) { + const [, slug] = request.nextUrl.pathname.split("/"); + // TODO + } + + const response = NextResponse.next({ + headers: responseHeaders, + }); + + return response; +} From c7b943d66972231b0ed2266dd4c0ac05fe0b2401 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Thu, 30 Nov 2023 10:55:23 -0700 Subject: [PATCH 06/21] middleware --- packages/nextjs/middleware.ts | 19 ++++++++++++++++--- packages/nextjs/pages/categories.tsx | 6 +----- packages/nextjs/pages/products/[slug].tsx | 14 +------------- packages/nextjs/pages/products/index.tsx | 4 ---- packages/nextjs/utils/setCachingHeaders.ts | 15 --------------- 5 files changed, 18 insertions(+), 40 deletions(-) delete mode 100644 packages/nextjs/utils/setCachingHeaders.ts diff --git a/packages/nextjs/middleware.ts b/packages/nextjs/middleware.ts index fafbd9f1..c5ae3137 100644 --- a/packages/nextjs/middleware.ts +++ b/packages/nextjs/middleware.ts @@ -1,11 +1,14 @@ import { NextRequest, NextResponse } from "next/server"; import { SanityType } from "utils/consts"; +import { getProductBySlug } from "utils/getProductBySlug"; + +type Product = Awaited>[number]; export const config = { matcher: ["/", "/categories", "/products", "/products/:slug*"], }; -export function middleware(request: NextRequest) { +export async function middleware(request: NextRequest) { const responseHeaders = new Headers(); responseHeaders.set("Cache-Control", "public, max-age=0"); responseHeaders.set("Surrogate-Control", "max-age=604800, stale-while-revalidate=120000, stale-if-error=600000"); @@ -19,8 +22,18 @@ export function middleware(request: NextRequest) { const keys = [SanityType.Product, SanityType.Style, SanityType.Flavour, SanityType.Variant]; responseHeaders.set("Surrogate-Key", keys.join(" ")); } else if (request.nextUrl.pathname.startsWith("/products")) { - const [, slug] = request.nextUrl.pathname.split("/"); - // TODO + const [, , slug] = request.nextUrl.pathname.split("/"); + const products = await getProductBySlug(slug); + const variantSlugs = (products[0]?.variants?.map((v: Product["variants"][number]) => v?.slug) || []).filter( + Boolean + ); + + const cacheKeys = [ + `${SanityType.Product}_${slug}`, + "testing", + ...variantSlugs.map((s) => `${SanityType.Variant}_${s}`), + ]; + responseHeaders.set("Surrogate-Key2", cacheKeys.join(" ")); } const response = NextResponse.next({ diff --git a/packages/nextjs/pages/categories.tsx b/packages/nextjs/pages/categories.tsx index a9921974..d04e23c3 100644 --- a/packages/nextjs/pages/categories.tsx +++ b/packages/nextjs/pages/categories.tsx @@ -1,8 +1,6 @@ import { GetServerSideProps, NextPage } from "next"; import { WeDontSellBreadBanner } from "shared-ui"; -import { setCachingHeaders } from "utils/setCachingHeaders"; -import { SanityType } from "utils/consts"; import { isString, pluralize } from "utils/pluralize"; import { getAllCategories } from "utils/getAllCategoriesQuery"; @@ -34,9 +32,7 @@ const CategoriesPage: NextPage = ({ categories, categoryNames }) => { ); }; -export const getServerSideProps = (async ({ res }) => { - setCachingHeaders(res, [SanityType.Category, SanityType.CategoryImage]); - +export const getServerSideProps = (async () => { const categories = await getAllCategories(); const categoryNames = pluralize((categories || []).map((cat) => cat.name).filter(isString)); diff --git a/packages/nextjs/pages/products/[slug].tsx b/packages/nextjs/pages/products/[slug].tsx index 6b85fc8d..923f0317 100644 --- a/packages/nextjs/pages/products/[slug].tsx +++ b/packages/nextjs/pages/products/[slug].tsx @@ -6,9 +6,7 @@ import { useRouter } from "next/router"; import { AnimatePresence } from "framer-motion"; import { H6, FadeInOut, BlockContent, Price, QuantityInput, useCart } from "shared-ui"; -import { setCachingHeaders } from "utils/setCachingHeaders"; import { isSlug } from "utils/isSlug"; -import { SanityType } from "utils/consts"; import { getRecommendations } from "utils/getRecommendationsQuery"; import { getProductBySlug } from "utils/getProductBySlug"; @@ -149,22 +147,12 @@ const PageBody = ({ variant, product }: { product?: ProductType; variant?: Varia ); }; -export const getServerSideProps = (async ({ res, query }) => { +export const getServerSideProps = (async ({ query }) => { const { slug } = query; - const cacheKeys = [] as string[]; - if (isSlug(slug)) { - cacheKeys.push(`${SanityType.Product}_${slug}`); - } - const products = await getProductBySlug(isSlug(slug) ? slug : ""); const recommendations = await getRecommendations(); - // Extract variant slugs to add to cache keys, in case any of those change. - const variantSlugs: string[] = (products[0]?.variants?.map((v: any) => v?.slug) || []).filter(Boolean); - cacheKeys.push(...variantSlugs.map((s) => `${SanityType.Variant}_${s}`)); - setCachingHeaders(res, cacheKeys); - return { props: { data: { diff --git a/packages/nextjs/pages/products/index.tsx b/packages/nextjs/pages/products/index.tsx index 7095c43e..d85f2aa5 100644 --- a/packages/nextjs/pages/products/index.tsx +++ b/packages/nextjs/pages/products/index.tsx @@ -10,8 +10,6 @@ import { getCategoryFilters, getFlavourFilters, getStyleFilters } from "utils/ge import { getPaginationFromQuery } from "utils/getPaginationFromQuery"; import { getFiltersFromQuery } from "utils/getFiltersFromQuery"; import { getOrderingFromQuery } from "shared-ui"; -import { setCachingHeaders } from "utils/setCachingHeaders"; -import { SanityType } from "utils/consts"; import { pluralize } from "utils/pluralize"; import { CategoryFilterItem, FlavourFilterItem, PLPVariant, StyleFilterItem } from "utils/groqTypes/ProductList"; import { useDeviceSize } from "utils/useDeviceSize"; @@ -143,8 +141,6 @@ const ProductsPage: NextPage = ({ }; export const getServerSideProps = (async ({ query, res, resolvedUrl }) => { - setCachingHeaders(res, [SanityType.Product, SanityType.Style, SanityType.Flavour, SanityType.Variant]); - // Sort/ordering. const order = getOrderingFromQuery(query); diff --git a/packages/nextjs/utils/setCachingHeaders.ts b/packages/nextjs/utils/setCachingHeaders.ts deleted file mode 100644 index c9441a2d..00000000 --- a/packages/nextjs/utils/setCachingHeaders.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ServerResponse } from "http"; - -/** - * sets appropriate caching response header, along with an optional set of surrogate keys - * note: when running `next dev`, these headers are overwritten to prevent local caching - * @param res - ServerResponse object - * @param keys - array of keys to add to the Surrogate-Key response header - */ -export const setCachingHeaders = (res: ServerResponse, keys?: string[]): void => { - res.setHeader("Cache-Control", "public, max-age=0"); - res.setHeader("Surrogate-Control", "max-age=604800, stale-while-revalidate=120000, stale-if-error=600000"); - if (keys && keys.length > 0) { - res.setHeader("Surrogate-Key", keys.join(" ")); - } -}; From d90c97ec5ec0db30cd279f0548aa147e1102338d Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Fri, 1 Dec 2023 12:40:35 -0700 Subject: [PATCH 07/21] mv --- packages/nextjs/app/about/page.tsx | 5 +++ .../nextjs/{pages => app/migration}/about.tsx | 6 ++- .../{pages => app/migration}/categories.tsx | 2 + .../{pages => app/migration}/components.tsx | 2 + .../migration}/products/[slug].tsx | 13 +++---- .../migration}/products/index.tsx | 6 ++- packages/nextjs/app/page.tsx | 6 +-- packages/nextjs/pages/_app.tsx | 38 ------------------- packages/nextjs/pages/_document.tsx | 18 --------- 9 files changed, 23 insertions(+), 73 deletions(-) create mode 100644 packages/nextjs/app/about/page.tsx rename packages/nextjs/{pages => app/migration}/about.tsx (98%) rename packages/nextjs/{pages => app/migration}/categories.tsx (98%) rename packages/nextjs/{pages => app/migration}/components.tsx (99%) rename packages/nextjs/{pages => app/migration}/products/[slug].tsx (96%) rename packages/nextjs/{pages => app/migration}/products/index.tsx (98%) delete mode 100644 packages/nextjs/pages/_app.tsx delete mode 100644 packages/nextjs/pages/_document.tsx diff --git a/packages/nextjs/app/about/page.tsx b/packages/nextjs/app/about/page.tsx new file mode 100644 index 00000000..6b0b1666 --- /dev/null +++ b/packages/nextjs/app/about/page.tsx @@ -0,0 +1,5 @@ +import AboutPage from "app/migration/about"; + +export default async function Page() { + return ; +} diff --git a/packages/nextjs/pages/about.tsx b/packages/nextjs/app/migration/about.tsx similarity index 98% rename from packages/nextjs/pages/about.tsx rename to packages/nextjs/app/migration/about.tsx index 94fa38b1..f436da54 100644 --- a/packages/nextjs/pages/about.tsx +++ b/packages/nextjs/app/migration/about.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as React from "react"; import { NextPage } from "next"; import NextImage from "next/legacy/image"; @@ -58,7 +60,7 @@ const AboutPage: NextPage = () => {
{

= ({ data }) => { - const { query } = useRouter(); + const query = useSearchParams(); const product = data?.products[0]; const selectedVariant = @@ -79,12 +81,7 @@ const PageBody = ({ variant, product }: { product?: ProductType; variant?: Varia const setSelectedVariant = React.useCallback( (slug: string) => { - replace({ - pathname: window.location.pathname, - query: { - variant: slug, - }, - }).catch(() => null); + replace(`${window.location.pathname}?variant=${slug}`); }, [replace] ); diff --git a/packages/nextjs/pages/products/index.tsx b/packages/nextjs/app/migration/products/index.tsx similarity index 98% rename from packages/nextjs/pages/products/index.tsx rename to packages/nextjs/app/migration/products/index.tsx index d85f2aa5..aa3b42bf 100644 --- a/packages/nextjs/pages/products/index.tsx +++ b/packages/nextjs/app/migration/products/index.tsx @@ -1,7 +1,8 @@ +"use client"; + import { GetServerSideProps, GetServerSidePropsResult, NextPage } from "next"; import * as React from "react"; import { AnimatePresence } from "framer-motion"; -import { useRouter } from "next/router"; import classNames from "classnames"; import { H6, WeDontSellBreadBanner, FadeInOut } from "shared-ui"; @@ -22,6 +23,7 @@ import { Pagination } from "components/Pagination"; import { Breadcrumbs } from "components/Breadcrumbs"; import { ModalFiltersMobile } from "views/ModalFiltersMobile"; import { SortAndFiltersToolbarMobile } from "views/SortAndFiltersToolbarMobile"; +import { useSearchParams } from "next/navigation"; interface ProductsPageProps { variants: PLPVariant[]; @@ -43,7 +45,7 @@ const ProductsPage: NextPage = ({ styleFilters, }) => { const productNames = pluralize(variants.map((prod) => prod.name)); - const { query } = useRouter(); + const query = useSearchParams(); const [isModalOpen, setIsModalOpen] = React.useState(false); const { isSm } = useDeviceSize(); diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 6fe79c8d..667aed92 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -24,9 +24,5 @@ async function getData() { export default async function Page() { const data = await getData(); - return ( - <> - ) - - ); + return ; } diff --git a/packages/nextjs/pages/_app.tsx b/packages/nextjs/pages/_app.tsx deleted file mode 100644 index 7111e5f9..00000000 --- a/packages/nextjs/pages/_app.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { AppProps } from "next/app"; -import * as React from "react"; -import Head from "next/head"; -import { AnimatePresence, MotionConfig } from "framer-motion"; - -import { FadeInOut } from "shared-ui"; -import { CartProvider } from "components/CartContext"; -import { Layout } from "components/Layout"; -import "styles/global.css"; - -(async () => { - if (process.env.NEXT_PUBLIC_API_MOCKING === "enabled") { - await import("mocks/msw/msw-enable"); - } -})(); - -function MyApp({ Component, pageProps, router }: AppProps) { - return ( - <> - - - - - - - - - - - - - - - - ); -} - -export default MyApp; diff --git a/packages/nextjs/pages/_document.tsx b/packages/nextjs/pages/_document.tsx deleted file mode 100644 index d828bb32..00000000 --- a/packages/nextjs/pages/_document.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Html, Head, Main, NextScript } from "next/document"; - -export default function Document() { - return ( - - - - - -
- - - - ); -} From 1c22a3fe3a7a32555a13964789ab7dc17bdcb58c Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 10:42:55 -0700 Subject: [PATCH 08/21] wip --- packages/nextjs/app/categories/page.tsx | 19 ++++++ packages/nextjs/app/migration/categories.tsx | 16 +---- .../app/migration/getOrderingFromQuery.ts | 22 +++++++ .../nextjs/app/migration/products/index.tsx | 56 +----------------- packages/nextjs/app/migration/sorting.ts | 52 +++++++++++++++++ packages/nextjs/app/products/page.tsx | 58 +++++++++++++++++++ packages/nextjs/components/Pagination.tsx | 11 ++-- packages/nextjs/utils/getFiltersCount.ts | 12 ++-- packages/nextjs/utils/useRouterQueryParams.ts | 53 ++++++----------- 9 files changed, 183 insertions(+), 116 deletions(-) create mode 100644 packages/nextjs/app/categories/page.tsx create mode 100644 packages/nextjs/app/migration/getOrderingFromQuery.ts create mode 100644 packages/nextjs/app/migration/sorting.ts create mode 100644 packages/nextjs/app/products/page.tsx diff --git a/packages/nextjs/app/categories/page.tsx b/packages/nextjs/app/categories/page.tsx new file mode 100644 index 00000000..78b9ab2a --- /dev/null +++ b/packages/nextjs/app/categories/page.tsx @@ -0,0 +1,19 @@ +import CategoriesPage from "app/migration/categories"; +import { getAllCategories } from "utils/getAllCategoriesQuery"; +import { isString, pluralize } from "utils/pluralize"; + +const getData = async () => { + const categories = await getAllCategories(); + const categoryNames = pluralize((categories || []).map((cat) => cat.name).filter(isString)); + + return { + categories, + categoryNames, + }; +}; + +export default async function Page() { + const data = await getData(); + + return ; +} diff --git a/packages/nextjs/app/migration/categories.tsx b/packages/nextjs/app/migration/categories.tsx index 391e8b15..2694bae5 100644 --- a/packages/nextjs/app/migration/categories.tsx +++ b/packages/nextjs/app/migration/categories.tsx @@ -1,10 +1,8 @@ "use client"; -import { GetServerSideProps, NextPage } from "next"; +import { NextPage } from "next"; import { WeDontSellBreadBanner } from "shared-ui"; -import { isString, pluralize } from "utils/pluralize"; -import { getAllCategories } from "utils/getAllCategoriesQuery"; import { CategoryList } from "components/CategoryList"; import { PageHead } from "components/PageHead"; @@ -34,16 +32,4 @@ const CategoriesPage: NextPage = ({ categories, categoryNames }) => { ); }; -export const getServerSideProps = (async () => { - const categories = await getAllCategories(); - const categoryNames = pluralize((categories || []).map((cat) => cat.name).filter(isString)); - - return { - props: { - categories, - categoryNames, - }, - }; -}) satisfies GetServerSideProps; - export default CategoriesPage; diff --git a/packages/nextjs/app/migration/getOrderingFromQuery.ts b/packages/nextjs/app/migration/getOrderingFromQuery.ts new file mode 100644 index 00000000..a37dfee7 --- /dev/null +++ b/packages/nextjs/app/migration/getOrderingFromQuery.ts @@ -0,0 +1,22 @@ +// TODO: This file can be imported from shared-ui once the server/client split is resolved + +import { ParsedUrlQuery } from "querystring"; +import { SORT_OPTIONS, SORT_QUERY_PARAM } from "./sorting"; + +export const getOrderingFromQuery = (query: ParsedUrlQuery) => { + const { [SORT_QUERY_PARAM]: sortValue } = query; + + // Sort/ordering + let ordering = SORT_OPTIONS.default.ordering; + if (sortValue) { + // If sort is string[], use first item + // (e.g. User modified url, wouldn't happen normally) + const sortType = Array.isArray(sortValue) ? sortValue[0] : sortValue; + const sortOption = SORT_OPTIONS[sortType]; + if (sortOption?.ordering) { + ordering = sortOption.ordering; + } + } + + return ordering; +}; diff --git a/packages/nextjs/app/migration/products/index.tsx b/packages/nextjs/app/migration/products/index.tsx index aa3b42bf..d21a3925 100644 --- a/packages/nextjs/app/migration/products/index.tsx +++ b/packages/nextjs/app/migration/products/index.tsx @@ -1,16 +1,11 @@ "use client"; -import { GetServerSideProps, GetServerSidePropsResult, NextPage } from "next"; +import { NextPage } from "next"; import * as React from "react"; import { AnimatePresence } from "framer-motion"; import classNames from "classnames"; import { H6, WeDontSellBreadBanner, FadeInOut } from "shared-ui"; -import { getAllFilteredVariants } from "utils/getFilteredPaginatedQuery"; -import { getCategoryFilters, getFlavourFilters, getStyleFilters } from "utils/getFilters"; -import { getPaginationFromQuery } from "utils/getPaginationFromQuery"; -import { getFiltersFromQuery } from "utils/getFiltersFromQuery"; -import { getOrderingFromQuery } from "shared-ui"; import { pluralize } from "utils/pluralize"; import { CategoryFilterItem, FlavourFilterItem, PLPVariant, StyleFilterItem } from "utils/groqTypes/ProductList"; import { useDeviceSize } from "utils/useDeviceSize"; @@ -142,53 +137,4 @@ const ProductsPage: NextPage = ({ ); }; -export const getServerSideProps = (async ({ query, res, resolvedUrl }) => { - // Sort/ordering. - const order = getOrderingFromQuery(query); - - // Fetch size filters from sanity - const categoryFilters = await getCategoryFilters(); - const flavourFilters = await getFlavourFilters(); - const styleFilters = await getStyleFilters(); - - // Filters. - const filters = getFiltersFromQuery(query, { flavourFilters, styleFilters, categoryFilters }); - // Pagination data. - const pagination = getPaginationFromQuery(query); - const result = await getAllFilteredVariants(filters, order, pagination); - - const { variants, itemCount } = result; - const { currentPage, pageSize } = pagination; - const pageCount = Math.ceil(itemCount / pageSize); - - /** - * Scenario: If user is on the third page and then enables - * a filter that only returns two pages worth of products, - * redirect them to the last page/pageCount - */ - if (pageCount > 0 && currentPage > pageCount) { - const destination = resolvedUrl.replace(`page=${currentPage}`, `page=${pageCount}`); - const redirect: GetServerSidePropsResult = { - redirect: { - destination, - permanent: false, - }, - }; - return redirect as never; // Exclude this return type from the return signature - } - - return { - props: { - categoryFilters, - flavourFilters, - styleFilters, - variants, - itemCount, - pageCount, - pageSize, - currentPage, - }, - }; -}) satisfies GetServerSideProps; - export default ProductsPage; diff --git a/packages/nextjs/app/migration/sorting.ts b/packages/nextjs/app/migration/sorting.ts new file mode 100644 index 00000000..f9bcdbeb --- /dev/null +++ b/packages/nextjs/app/migration/sorting.ts @@ -0,0 +1,52 @@ +// See: ./getOrderingFromQuery + +export enum SortType { + Default = "DEFAULT", // no sort + Natural = "NATURAL", // sorts alphanumerical +} + +export type SortOption = { + value: string; + label: string; + type: SortType; + ordering: `${string} ${"asc" | "desc"}`; +}; + +export const SORT_QUERY_PARAM = "sort"; + +export const PAGE_QUERY_PARAM = "page"; + +export const SORT_OPTIONS: Record = { + default: { + value: "default", + label: "Default", + type: SortType.Default, + ordering: "_id asc", // Default query sorting + }, + "a-z": { + value: "a-z", + label: "A - Z", + type: SortType.Natural, + ordering: "name asc", + }, + "z-a": { + value: "z-a", + label: "Z - A", + type: SortType.Natural, + ordering: "name desc", + }, + lowest: { + value: "lowest", + label: "Lowest Price", + type: SortType.Natural, + ordering: "price asc", + }, + highest: { + value: "highest", + label: "Highest Price", + type: SortType.Natural, + ordering: "price desc", + }, +}; + +export const SORT_OPTIONS_ARRAY = Object.values(SORT_OPTIONS); diff --git a/packages/nextjs/app/products/page.tsx b/packages/nextjs/app/products/page.tsx new file mode 100644 index 00000000..fdb8ceea --- /dev/null +++ b/packages/nextjs/app/products/page.tsx @@ -0,0 +1,58 @@ +import { redirect } from "next/navigation"; +import { getOrderingFromQuery } from "../migration/getOrderingFromQuery"; +import { getAllFilteredVariants } from "utils/getFilteredPaginatedQuery"; +import { getCategoryFilters, getFlavourFilters, getStyleFilters } from "utils/getFilters"; +import { getFiltersFromQuery } from "utils/getFiltersFromQuery"; +import { getPaginationFromQuery } from "utils/getPaginationFromQuery"; +import ProductsPage from "app/migration/products"; + +// See: https://nextjs.org/docs/app/api-reference/file-conventions/page +type RouteSearchParams = { [key: string]: string | string[] | undefined }; + +const getData = async ({ searchParams }: { searchParams: RouteSearchParams }) => { + // Sort/ordering. + const order = getOrderingFromQuery(searchParams); + + // Fetch size filters from sanity + const categoryFilters = await getCategoryFilters(); + const flavourFilters = await getFlavourFilters(); + const styleFilters = await getStyleFilters(); + + // Filters. + const filters = getFiltersFromQuery(searchParams, { flavourFilters, styleFilters, categoryFilters }); + // Pagination data. + const pagination = getPaginationFromQuery(searchParams); + const result = await getAllFilteredVariants(filters, order, pagination); + + const { variants, itemCount } = result; + const { currentPage, pageSize } = pagination; + const pageCount = Math.ceil(itemCount / pageSize); + + /** + * Scenario: If user is on the third page and then enables + * a filter that only returns two pages worth of products, + * redirect them to the last page/pageCount + */ + + return { + categoryFilters, + flavourFilters, + styleFilters, + variants, + itemCount, + pageCount, + pageSize, + currentPage, + }; +}; + +export default async function Page({ searchParams }: { searchParams: RouteSearchParams }) { + const data = await getData({ searchParams }); + + if (data.pageCount > 0 && data.currentPage > data.pageCount) { + const newParams = new URLSearchParams({ ...searchParams, page: data.pageCount.toString() }); + return redirect(`/products?${newParams.toString()}`); + } + + return ; +} diff --git a/packages/nextjs/components/Pagination.tsx b/packages/nextjs/components/Pagination.tsx index 58331d79..62377f0d 100644 --- a/packages/nextjs/components/Pagination.tsx +++ b/packages/nextjs/components/Pagination.tsx @@ -2,8 +2,7 @@ import * as React from "react"; import { Pagination as BasePagination } from "shared-ui"; import Link from "next/link"; import classNames from "classnames"; -import { useRouter } from "next/router"; -import { stringify } from "querystring"; +import { usePathname, useSearchParams } from "next/navigation"; type PaginationProps = { pageCount: number; @@ -12,7 +11,9 @@ type PaginationProps = { }; export const Pagination = ({ onPageChange, pageCount = 1, currentPage = 1 }: PaginationProps) => { - const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + const handlePageChanged = (e: React.MouseEvent, page: number) => { if (onPageChange) { e.preventDefault(); @@ -25,8 +26,8 @@ export const Pagination = ({ onPageChange, pageCount = 1, currentPage = 1 }: Pag pageCount={pageCount} NextPreviousLink={Link} currentPage={currentPage} - currentHref={router.pathname} - search={stringify(router.query)} + currentHref={pathname ?? ""} + search={searchParams?.toString() ?? ""} renderPaginationLink={({ page, href }) => ( { +export const getFiltersCount = (query: ReadonlyURLSearchParams | null) => { const filters = getFilterGroups(); const total = filters.reduce((acc: number, { label, value }: FilterGroup) => { - const selectedFilters = query[value]; + const selectedFilters = query?.get(value); const elements = selectedFilters?.length ?? 0; // if a single element is selected, type would be a string instead of an array. @@ -20,7 +19,6 @@ export const getFiltersCount = (query: ParsedUrlQuery) => { }; export const useGetFiltersCount = () => { - const router = useRouter(); - - return getFiltersCount(router.query); + const query = useSearchParams(); + return getFiltersCount(query); }; diff --git a/packages/nextjs/utils/useRouterQueryParams.ts b/packages/nextjs/utils/useRouterQueryParams.ts index 524630a1..de637e79 100644 --- a/packages/nextjs/utils/useRouterQueryParams.ts +++ b/packages/nextjs/utils/useRouterQueryParams.ts @@ -1,51 +1,36 @@ -import { useRouter } from "next/router"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; export const useRouterQueryParams = () => { const router = useRouter(); + const pathname = usePathname(); + const query = useSearchParams(); + const current = new URLSearchParams(query ?? ""); const add = (key: string, value: string) => { - const query = { ...router.query }; - const currentValue = query[key]; - if (currentValue) { - if (typeof currentValue === "string" || currentValue instanceof String) { - // Single existing, create and append to array - query[key] = [currentValue as string, value]; - } else { - // Multiple options, append to array - (query[key] as string[]).push(value); - } - } else { - // No existing options, sdd single option - query[key] = value; - } - router.replace({ query }); + current.append(key, value); + router.replace(`${pathname}?${current.toString()}`); }; const replace = (args: Record) => { - const query = { ...router.query, ...args }; - router.replace({ query }); + const currentQuery = Object.fromEntries(current); + const updatedEntries = { + ...currentQuery, + ...args, + }; + const newQuery = new URLSearchParams(updatedEntries).toString(); + + router.replace(`${pathname}?${newQuery}`); }; const clear = (key: string) => { - const query = { ...router.query }; - delete query[key]; - router.replace({ query }); + current.delete(key); + router.replace(`${pathname}?${current.toString()}`); }; const remove = (key: string, value: string) => { - const query = { ...router.query }; - const currentValue = query[key]; - if (currentValue) { - if (typeof currentValue === "string" || currentValue instanceof String) { - // Single option, remove group and option - delete query[key]; - } else { - // Multiple options, only remove option - query[key] = (query[key] as string[]).filter((curr) => curr !== value); - } - } - router.replace({ query }); + current.delete(key, value); + router.replace(`${pathname}?${current.toString()}`); }; - return { add, replace, clear, remove, query: router.query }; + return { add, replace, clear, remove, query }; }; From 77623992c54d77714a48fbfd35c45563aeadd45b Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 10:54:40 -0700 Subject: [PATCH 09/21] fix link --- packages/nextjs/components/Breadcrumbs.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/components/Breadcrumbs.tsx b/packages/nextjs/components/Breadcrumbs.tsx index 20db3228..d7f666dd 100644 --- a/packages/nextjs/components/Breadcrumbs.tsx +++ b/packages/nextjs/components/Breadcrumbs.tsx @@ -1,8 +1,8 @@ import React from "react"; -import { useRouter } from "next/router"; import { MdOutlineHome } from "react-icons/md"; import { BreadcrumbItem, BreadcrumbsContainer, capitalizeWords } from "shared-ui"; import Link from "next/link"; +import { usePathname, useSearchParams } from "next/navigation"; type LinkElement = { title: string; @@ -10,9 +10,9 @@ type LinkElement = { }; export const Breadcrumbs = () => { - const { query, asPath } = useRouter(); - // Remove query string. - const urlPath = asPath.split("?")[0]; + const searchParams = useSearchParams(); + const query = searchParams ? Object.fromEntries(searchParams?.entries()) : {}; + const urlPath = usePathname() ?? ""; // Normal route. let pathElements = urlPath.split("/"); @@ -48,7 +48,7 @@ export const Breadcrumbs = () => { return ( - + Home {elements.map(({ title, href }, index) => ( From 822bf3d43f3f9b9f8b2f7dc7f6685d7905b0e6a3 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 10:54:48 -0700 Subject: [PATCH 10/21] slug server page --- packages/nextjs/app/products/[slug]/page.tsx | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/nextjs/app/products/[slug]/page.tsx diff --git a/packages/nextjs/app/products/[slug]/page.tsx b/packages/nextjs/app/products/[slug]/page.tsx new file mode 100644 index 00000000..f89f8d0a --- /dev/null +++ b/packages/nextjs/app/products/[slug]/page.tsx @@ -0,0 +1,25 @@ +import ProductsPage from "app/migration/products/[slug]"; +import { getProductBySlug } from "utils/getProductBySlug"; +import { getRecommendations } from "utils/getRecommendationsQuery"; +import { isSlug } from "utils/isSlug"; + +// See: https://nextjs.org/docs/app/api-reference/file-conventions/page +type RouteSearchParams = { [key: string]: string | string[] | undefined }; + +const getData = async ({ searchParams }: { searchParams: RouteSearchParams }) => { + const { slug } = searchParams; + + const products = await getProductBySlug(isSlug(slug) ? slug : ""); + const recommendations = await getRecommendations(); + + return { + products, + recommendations, + }; +}; + +export default async function Page({ searchParams }: { searchParams: RouteSearchParams }) { + const data = await getData({ searchParams }); + + return ; +} From 87c5c30f3a32fcb1ce9d703828dbc9d1e8481f8b Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 11:34:07 -0700 Subject: [PATCH 11/21] fix --- packages/nextjs/app/migration/products/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/app/migration/products/index.tsx b/packages/nextjs/app/migration/products/index.tsx index d21a3925..b8ea69aa 100644 --- a/packages/nextjs/app/migration/products/index.tsx +++ b/packages/nextjs/app/migration/products/index.tsx @@ -98,7 +98,7 @@ const ProductsPage: NextPage = ({ 1 && "grid-rows-2" + +(query?.get("page") || 1) > 1 && "grid-rows-2" )} key={productNames} > @@ -106,7 +106,7 @@ const ProductsPage: NextPage = ({ ))} {/* Add padder items when on page > 1 so pagination bar isn't moving around */} - {+(query?.page || 1) > 1 && + {+(query?.get("page") || 1) > 1 && Array.from({ length: 6 - variants.length }) .fill(undefined) .map((_, i) =>
)} From 05b4c1dbf8617d2e91f3bea0d8a66fce3dc5ffa4 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 11:46:01 -0700 Subject: [PATCH 12/21] ts --- .../nextjs/app/migration/products/[slug].tsx | 34 +++------ .../components/ProductFilters/FilterGroup.tsx | 7 +- .../ProductPage/ProductVariantSelector.tsx | 6 +- .../components/ProductPage/StyleOptions.tsx | 2 +- packages/nextjs/components/ProductSort.tsx | 4 +- packages/nextjs/utils/getProductBySlug.ts | 70 +++++++++---------- .../nextjs/utils/groqTypes/ProductDetail.ts | 7 ++ .../nextjs/utils/groqTypes/ProductList.ts | 2 +- 8 files changed, 60 insertions(+), 72 deletions(-) create mode 100644 packages/nextjs/utils/groqTypes/ProductDetail.ts diff --git a/packages/nextjs/app/migration/products/[slug].tsx b/packages/nextjs/app/migration/products/[slug].tsx index f85e89dc..7e515843 100644 --- a/packages/nextjs/app/migration/products/[slug].tsx +++ b/packages/nextjs/app/migration/products/[slug].tsx @@ -1,15 +1,12 @@ "use client"; -import type { GetProductsAndCategoriesQuery, Product as ProductType, Variant } from "utils/groqTypes/ProductList"; import * as React from "react"; import { useState } from "react"; -import { GetServerSideProps, NextPage } from "next"; +import { NextPage } from "next"; import { AnimatePresence } from "framer-motion"; import { H6, FadeInOut, BlockContent, Price, QuantityInput, useCart } from "shared-ui"; -import { isSlug } from "utils/isSlug"; import { getRecommendations } from "utils/getRecommendationsQuery"; -import { getProductBySlug } from "utils/getProductBySlug"; import { ImageCarousel } from "components/ImageCarousel"; import { PageHead } from "components/PageHead"; @@ -18,20 +15,21 @@ import { ProductVariantSelector } from "components/ProductPage/ProductVariantSel import { Product } from "components/Product"; import { Breadcrumbs } from "components/Breadcrumbs"; import { useSearchParams, useRouter } from "next/navigation"; +import { ProductDetail, ProductDetailVariants } from "utils/groqTypes/ProductDetail"; interface PageProps { data?: { - products: GetProductsAndCategoriesQuery["products"]; - recommendations: GetProductsAndCategoriesQuery["products"]; + product: ProductDetail; + recommendations: Awaited>; }; } const ProductPage: NextPage = ({ data }) => { const query = useSearchParams(); + const product = data?.product; - const product = data?.products[0]; const selectedVariant = - (product?.variants || []).find((v) => v?.slug && v.slug === query.variant) || product?.variants?.[0]; + (product?.variants || []).find((v) => v?.slug && v.slug === query?.get("variant")) || product?.variants?.[0]; return ( @@ -41,7 +39,7 @@ const ProductPage: NextPage = ({ data }) => {
- + @@ -75,7 +73,7 @@ const ProductPage: NextPage = ({ data }) => { ); }; -const PageBody = ({ variant, product }: { product?: ProductType; variant?: Variant }) => { +const PageBody = ({ variant, product }: { product?: ProductDetail; variant?: ProductDetailVariants[number] }) => { const { replace } = useRouter(); const { updateCart, cartItems } = useCart(); @@ -144,20 +142,4 @@ const PageBody = ({ variant, product }: { product?: ProductType; variant?: Varia ); }; -export const getServerSideProps = (async ({ query }) => { - const { slug } = query; - - const products = await getProductBySlug(isSlug(slug) ? slug : ""); - const recommendations = await getRecommendations(); - - return { - props: { - data: { - products, - recommendations, - }, - }, - }; -}) satisfies GetServerSideProps; - export default ProductPage; diff --git a/packages/nextjs/components/ProductFilters/FilterGroup.tsx b/packages/nextjs/components/ProductFilters/FilterGroup.tsx index eb2e6b31..eadbb91b 100644 --- a/packages/nextjs/components/ProductFilters/FilterGroup.tsx +++ b/packages/nextjs/components/ProductFilters/FilterGroup.tsx @@ -13,8 +13,7 @@ export const FilterGroup: React.FC = ({ group }) => { const { query, add, remove } = useRouterQueryParams(); - const queryValue = query[groupValue]; - const queryValueIsString = typeof queryValue === "string" || queryValue instanceof String; + const queryValue = query?.get(groupValue); const handleChange = (e: ChangeEvent) => { const { checked, value: optionValue } = e.target; @@ -33,8 +32,8 @@ export const FilterGroup: React.FC = ({ group }) => { {options.map(({ value: optionValue, label: optionLabel }) => { const isChecked = !!queryValue && // Value exists - ((queryValueIsString && queryValue === optionValue) || // Single value matches option - (!queryValueIsString && queryValue.includes(optionValue))); // Multiple values includes option + (queryValue === optionValue || // Single value matches option + queryValue.includes(optionValue)); // Multiple values includes option return (
  • diff --git a/packages/nextjs/components/ProductPage/ProductVariantSelector.tsx b/packages/nextjs/components/ProductPage/ProductVariantSelector.tsx index d8064bc1..069afeda 100644 --- a/packages/nextjs/components/ProductPage/ProductVariantSelector.tsx +++ b/packages/nextjs/components/ProductPage/ProductVariantSelector.tsx @@ -1,11 +1,11 @@ import * as React from "react"; import { useMemo } from "react"; import { H6, Select } from "shared-ui"; -import { Variant } from "utils/groqTypes/ProductList"; +import { ProductDetailVariants } from "utils/groqTypes/ProductDetail"; interface Props { - variants: Variant[]; - selectedVariant?: Variant; + variants: ProductDetailVariants; + selectedVariant?: ProductDetailVariants[number]; onVariantChange: (slug?: string) => void; } diff --git a/packages/nextjs/components/ProductPage/StyleOptions.tsx b/packages/nextjs/components/ProductPage/StyleOptions.tsx index 4c92158d..d255eb70 100644 --- a/packages/nextjs/components/ProductPage/StyleOptions.tsx +++ b/packages/nextjs/components/ProductPage/StyleOptions.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { H6, Pill } from "shared-ui"; -import { Style } from "utils/groqTypes/ProductList"; +import { Style } from "utils/groqTypes/ProductDetail"; interface Props { options: Style[]; diff --git a/packages/nextjs/components/ProductSort.tsx b/packages/nextjs/components/ProductSort.tsx index e0469ff7..0960db9f 100644 --- a/packages/nextjs/components/ProductSort.tsx +++ b/packages/nextjs/components/ProductSort.tsx @@ -6,5 +6,7 @@ type ProductSortProps = Pick = (props) => { const { replace, clear, query } = useRouterQueryParams(); - return ; + const searchParams = new URLSearchParams(query ?? ""); + + return ; }; diff --git a/packages/nextjs/utils/getProductBySlug.ts b/packages/nextjs/utils/getProductBySlug.ts index af9f4386..a217fde8 100644 --- a/packages/nextjs/utils/getProductBySlug.ts +++ b/packages/nextjs/utils/getProductBySlug.ts @@ -1,40 +1,38 @@ import { q, sanityImage } from "groqd"; import { runQuery } from "./sanityClient"; -export const getProductBySlug = (slug = "") => - runQuery( - q("*") - .filterByType("product") - .filter("slug.current == $slug") - .grab({ - _id: q.string(), - name: q.string(), - categories: q("categories").filter().deref().grab$({ - name: q.string(), - }), - slug: q.slug("slug"), - variants: q("variants") - .filter() - .deref() - .grab$({ - _id: q.string(), - name: q.string(), - description: q.contentBlocks(), - msrp: q.number(), - price: q.number(), - slug: q.slug("slug"), - images: sanityImage("images", { - isList: true, - }), - style: q("style") - .filter() - .deref() - .grab$({ - _id: q.string(), - name: q.string(), - }) - .nullable(), - }), +export const productBySlugSelection = { + _id: q.string(), + name: q.string(), + categories: q("categories").filter().deref().grab$({ + name: q.string(), + }), + slug: q.slug("slug"), + variants: q("variants") + .filter() + .deref() + .grab$({ + _id: q.string(), + name: q.string(), + description: q.contentBlocks(), + msrp: q.number(), + price: q.number(), + slug: q.slug("slug"), + images: sanityImage("images", { + isList: true, + withCrop: true, + withHotspot: true, }), - { slug } - ); + style: q("style") + .filter() + .deref() + .grab$({ + _id: q.string(), + name: q.string(), + }) + .nullable(), + }), +}; + +export const getProductBySlug = (slug = "") => + runQuery(q("*").filterByType("product").filter("slug.current == $slug").grab(productBySlugSelection), { slug }); diff --git a/packages/nextjs/utils/groqTypes/ProductDetail.ts b/packages/nextjs/utils/groqTypes/ProductDetail.ts new file mode 100644 index 00000000..f1cace67 --- /dev/null +++ b/packages/nextjs/utils/groqTypes/ProductDetail.ts @@ -0,0 +1,7 @@ +import { TypeFromSelection } from "groqd"; +import { productBySlugSelection } from "utils/getProductBySlug"; +import { NullableArrayType } from "./ProductList"; + +export type ProductDetail = TypeFromSelection; +export type ProductDetailVariants = ProductDetail["variants"]; +export type Style = NullableArrayType[number]; diff --git a/packages/nextjs/utils/groqTypes/ProductList.ts b/packages/nextjs/utils/groqTypes/ProductList.ts index 654fe7d3..09469f06 100644 --- a/packages/nextjs/utils/groqTypes/ProductList.ts +++ b/packages/nextjs/utils/groqTypes/ProductList.ts @@ -32,7 +32,7 @@ export interface CategoryFilterItem { slug: string; } -type NullableArrayType = Exclude; +export type NullableArrayType = Exclude; export type Flavour = NullableArrayType[number]; export type Style = NullableArrayType[number]; From 5473d33a5257174990601ab00a35cfb5db1ffda3 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 11:46:42 -0700 Subject: [PATCH 13/21] single --- packages/nextjs/app/products/[slug]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/app/products/[slug]/page.tsx b/packages/nextjs/app/products/[slug]/page.tsx index f89f8d0a..e729997c 100644 --- a/packages/nextjs/app/products/[slug]/page.tsx +++ b/packages/nextjs/app/products/[slug]/page.tsx @@ -13,7 +13,7 @@ const getData = async ({ searchParams }: { searchParams: RouteSearchParams }) => const recommendations = await getRecommendations(); return { - products, + product: products[0], recommendations, }; }; From 3e03837a91ad5ba37d719b93421c2ea15f67d857 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 12:04:54 -0700 Subject: [PATCH 14/21] slug from params --- packages/nextjs/app/products/[slug]/page.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/nextjs/app/products/[slug]/page.tsx b/packages/nextjs/app/products/[slug]/page.tsx index e729997c..1a0a28ce 100644 --- a/packages/nextjs/app/products/[slug]/page.tsx +++ b/packages/nextjs/app/products/[slug]/page.tsx @@ -3,12 +3,7 @@ import { getProductBySlug } from "utils/getProductBySlug"; import { getRecommendations } from "utils/getRecommendationsQuery"; import { isSlug } from "utils/isSlug"; -// See: https://nextjs.org/docs/app/api-reference/file-conventions/page -type RouteSearchParams = { [key: string]: string | string[] | undefined }; - -const getData = async ({ searchParams }: { searchParams: RouteSearchParams }) => { - const { slug } = searchParams; - +const getData = async (slug: string) => { const products = await getProductBySlug(isSlug(slug) ? slug : ""); const recommendations = await getRecommendations(); @@ -18,8 +13,8 @@ const getData = async ({ searchParams }: { searchParams: RouteSearchParams }) => }; }; -export default async function Page({ searchParams }: { searchParams: RouteSearchParams }) { - const data = await getData({ searchParams }); +export default async function Page({ params }: { params: { slug: string } }) { + const data = await getData(params.slug); return ; } From e6156598cfa8236ecac4be4812f5cce7a4aa311b Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 13:37:44 -0700 Subject: [PATCH 15/21] e2e test updates w/o serversideprops --- README.md | 2 +- packages/nextjs/app/migration/about.tsx | 4 +- .../support/commands/getServerSideProps.ts | 50 ------------------- .../cypress/support/commands/index.ts | 1 - packages/tests-e2e/e2e-tests/categories.cy.ts | 14 ++---- packages/tests-e2e/e2e-tests/home.cy.ts | 23 ++++----- packages/tests-e2e/e2e-tests/product.cy.ts | 28 ++++------- packages/tests-e2e/e2e-tests/products.cy.ts | 34 ++++--------- 8 files changed, 36 insertions(+), 120 deletions(-) delete mode 100644 packages/tests-e2e/cypress/support/commands/getServerSideProps.ts diff --git a/README.md b/README.md index ee4a5ac2..e743b11c 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ To cache our server-rendered pages at the Fastly layer, we use response headers 1. `Surrogate-Control` response header needs to be added to pages where caching is desired ([reference](https://docs.fastly.com/en/guides/working-with-surrogate-keys)), 2. `Surrogate-Key` response header needs to be added to enable appropriate cache invalidation ([reference](https://developer.fastly.com/reference/api/purging/)). -On the Next.js side we'll need to include a few primary response headers to then control caching (in our case, we're setting these headers from `getServerSideProps` on server-rendered pages that we'd like to cache). +On the Next.js side we'll need to include a few primary response headers to then control caching (in our case, we're setting these headers from `middleware` on server-rendered pages that we'd like to cache). - `surrogate-control` – Fastly-specific header used to set the cache policies. (`max-age`, `stale-while-revalidate`, `stale-while-error`). - `surrogate-key` – Fastly-specific header that allows purging by key. Note: this header is removed by Fastly before sending the response to the client. To see the value of this header, you must include the [`Fastly-Debug`](https://developer.fastly.com/reference/http/http-headers/Fastly-Debug/) header in your request. diff --git a/packages/nextjs/app/migration/about.tsx b/packages/nextjs/app/migration/about.tsx index f436da54..7a37ef49 100644 --- a/packages/nextjs/app/migration/about.tsx +++ b/packages/nextjs/app/migration/about.tsx @@ -217,8 +217,8 @@ const AboutPage: NextPage = () => {
  • On the Next.js side we’ll need to include a few primary response headers to then control caching (in - our case, we’re setting these headers from getServerSideProps on server-rendered pages - that we’d like to cache). + our case, we’re setting these headers from middleware on server-rendered pages that + we’d like to cache).
    • diff --git a/packages/tests-e2e/cypress/support/commands/getServerSideProps.ts b/packages/tests-e2e/cypress/support/commands/getServerSideProps.ts deleted file mode 100644 index 466039e2..00000000 --- a/packages/tests-e2e/cypress/support/commands/getServerSideProps.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type * as home from " ../../../../nextjs/pages/index"; -import type * as categories from " ../../../../nextjs/pages/categories"; -import type * as products from " ../../../../nextjs/pages/products/index"; -import type * as product from " ../../../../nextjs/pages/products/[slug]"; - -type AsyncReturnType any> = UnwrapPromise>; -type UnwrapPromise = TPromiseMaybe extends Promise ? TValue : TPromiseMaybe; - -type PageProps = { - "/home": AsyncReturnType["props"]; - "/categories": AsyncReturnType["props"]; - "/products": AsyncReturnType["props"]; - "/products/[slug]": AsyncReturnType["props"]; -}; -const getServerSidePropsForPage = { - "/home": (props) => props, - "/categories": (props) => props, - "/products": (props) => props, - "/products/[slug]": (props) => props, -} satisfies { [P in keyof PageProps]: (props: PageProps[P]) => unknown }; - -export type PageDataTypes = { - [P in keyof typeof getServerSidePropsForPage]: ReturnType<(typeof getServerSidePropsForPage)[P]>; -}; - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Cypress { - export interface Chainable { - /** - * Retrieves the page's server-side props from __NEXT_DATA__. - */ - getServerSideProps(page: TPage): Chainable; - } - } -} - -Cypress.Commands.add( - "getServerSideProps", - ( - // We only use this parameter for strong types: - page: TPage // eslint-disable-line @typescript-eslint/no-unused-vars - ): Cypress.Chainable => { - return cy.window().then((win) => { - const props = win.__NEXT_DATA__.props.pageProps as any; - const mapped = getServerSidePropsForPage[page](props as any) as PageDataTypes[TPage]; - return mapped; - }); - } -); diff --git a/packages/tests-e2e/cypress/support/commands/index.ts b/packages/tests-e2e/cypress/support/commands/index.ts index 6a0003f1..e76eb340 100644 --- a/packages/tests-e2e/cypress/support/commands/index.ts +++ b/packages/tests-e2e/cypress/support/commands/index.ts @@ -1,6 +1,5 @@ import "@testing-library/cypress/add-commands"; -import "./getServerSideProps"; import "./nextClosest"; import "./setMockData"; diff --git a/packages/tests-e2e/e2e-tests/categories.cy.ts b/packages/tests-e2e/e2e-tests/categories.cy.ts index 4e1de982..e2af74a6 100644 --- a/packages/tests-e2e/e2e-tests/categories.cy.ts +++ b/packages/tests-e2e/e2e-tests/categories.cy.ts @@ -1,5 +1,4 @@ import { mock } from "mocks/factory"; -import { Category } from "utils/groqTypes/ProductList"; import { mockOnly } from "../utils/real-or-mock"; describe(`when I visit the "Categories" page`, () => { @@ -20,14 +19,11 @@ describe(`when I visit the "Categories" page`, () => { }); it("I see at least 3 Categories on the page", () => { - cy.getServerSideProps("/categories").then((data) => { - cy.get("main").within(() => { - const categories = data.categories; - expect(categories).to.have.length.at.least(3); - categories.forEach((cat: Category) => { - expect(cat.name).to.not.be.empty; - cy.findAllByText(cat.name!).should("exist"); - }); + cy.get("main").within(() => { + const categories = ["Delectable Donuts", "Palatable Pastries", "Love Loaves"]; + + categories.forEach((category) => { + cy.findAllByText(category).should("exist"); }); }); }); diff --git a/packages/tests-e2e/e2e-tests/home.cy.ts b/packages/tests-e2e/e2e-tests/home.cy.ts index 958ed6a7..d46b3d82 100644 --- a/packages/tests-e2e/e2e-tests/home.cy.ts +++ b/packages/tests-e2e/e2e-tests/home.cy.ts @@ -13,13 +13,10 @@ describe("when I visit the home page", () => { .nextClosest("section") .within(() => { cy.findByText("Show all breads").should("exist"); - cy.getServerSideProps("/home").then((props) => { - const bestSellers = props.data.products; - expect(bestSellers).to.have.length(3); - for (const product of bestSellers) { - cy.findByText(product.name!).should("exist"); - } - }); + const bestSellers = ["Kouign Amann", "Sourdough Loaf", "Pain au Chocolat"]; + for (const product of bestSellers) { + cy.findByText(product).should("exist"); + } }); }); it('I see the "Top Categories" section with 3 categories', () => { @@ -27,13 +24,11 @@ describe("when I visit the home page", () => { .should("exist") .nextClosest("section") .within(() => { - cy.getServerSideProps("/home").then((props) => { - const categories = props.data.categories.slice(0, 3); - cy.get('[data-testid="featured-list-item"]').should("have.length", 3); - for (const category of categories) { - cy.findByText(category.name!).should("exist"); - } - }); + const categories = ["Delectable Donuts", "Palatable Pastries", "Love Loaves"]; + cy.get('[data-testid="featured-list-item"]').should("have.length", 3); + for (const category of categories) { + cy.findByText(category).should("exist"); + } }); }); }); diff --git a/packages/tests-e2e/e2e-tests/product.cy.ts b/packages/tests-e2e/e2e-tests/product.cy.ts index d787cd1d..0d587d13 100644 --- a/packages/tests-e2e/e2e-tests/product.cy.ts +++ b/packages/tests-e2e/e2e-tests/product.cy.ts @@ -5,10 +5,7 @@ describe("when I visit the Product Details Page", () => { realOnly.before(() => { // Find a product to test: cy.visit("/products"); - cy.getServerSideProps("/products").then((props) => { - const product = props.variants[0]; - cy.visit(`/products/${product.productSlug}`); - }); + cy.visit("/products/sourdough-loaf?variant=seeded-sourdough-loaf"); }); mockOnly.before(() => { @@ -18,23 +15,16 @@ describe("when I visit the Product Details Page", () => { }); it(`I see the product's title`, () => { - cy.getServerSideProps("/products/[slug]").then((props) => { - const product = props.data.products[0]; - cy.findAllByText(product.name!).should("exist"); - }); + cy.findAllByText("Sourdough Loaf").should("exist"); }); + it("I see the item's price", () => { - cy.getServerSideProps("/products/[slug]").then((props) => { - const product = props.data.products[0]; - const variant = product.variants![0]!; - cy.findAllByText(/\$\d+\.\d\d/) - .should("exist") - .then((price) => { - const renderedPrice = price.first().text(); - const expectedPrice = variant.price!; - expect(renderedPrice).to.equal(`$${expectedPrice.toFixed(2)}`); - }); - }); + cy.findAllByText(/\$/i) + .should("exist") + .then((price) => { + const renderedPrice = price.first().text(); + expect(renderedPrice).to.equal(`$7.98`); + }); }); ["Type", "Style", "Quantity"].forEach((option) => { diff --git a/packages/tests-e2e/e2e-tests/products.cy.ts b/packages/tests-e2e/e2e-tests/products.cy.ts index 9632db35..985558fd 100644 --- a/packages/tests-e2e/e2e-tests/products.cy.ts +++ b/packages/tests-e2e/e2e-tests/products.cy.ts @@ -1,5 +1,4 @@ import { generateMockData } from "mocks/msw/db/mock-data"; -import type { PageDataTypes } from "../cypress/support/commands/getServerSideProps"; import { mockOnly } from "../utils/real-or-mock"; describe("when I visit the products page", () => { @@ -44,29 +43,16 @@ describe("when I visit the products page", () => { }); describe("on the default page", () => { - let pageProps: PageDataTypes["/products"]; - - before(async () => { - cy.getServerSideProps("/products").then((props) => { - pageProps = props; - }); - }); - - const EXPECTED_ITEMS_PER_PAGE = 6; - const EXPECTED_ITEMS_MINIMUM = 10; - it("I see 6 products", () => { - expect(pageProps.variants.length).to.equal( - EXPECTED_ITEMS_PER_PAGE, - `there should be ${EXPECTED_ITEMS_PER_PAGE} items on this page` - ); - expect(pageProps.itemCount).to.gte( - EXPECTED_ITEMS_MINIMUM, - `there should be at least ${EXPECTED_ITEMS_MINIMUM} items` - ); - - pageProps.variants.forEach((variant) => { - cy.findByText(variant.name).should("exist"); + [ + "Seeded Sourdough Loaf", + "Pain au Chocolat", + "Sourdough Loaf", + "Chocolate Croissant", + "Whole Grain Potato Rosemary", + "Lemon Tart", + ].forEach((variant) => { + cy.findByText(variant).should("exist"); }); }); @@ -74,7 +60,7 @@ describe("when I visit the products page", () => { cy.findByText("Previous").should("exist"); cy.findByText("Next").should("exist"); - const pageCount = pageProps.pageCount; + const pageCount = 4; for (let i = 0; i < pageCount; i++) { cy.findByText(`${i + 1}`).should("exist"); } From aeb962531be7d763d2df715c1c553d010de123a1 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 13:47:09 -0700 Subject: [PATCH 16/21] warnings --- packages/nextjs/app/migration/components.tsx | 1 + .../nextjs/components/ProductPage/ProductVariantSelector.tsx | 1 + packages/shared-ui/components/ProductSort/ProductSort.tsx | 1 + packages/shared-ui/components/Select.tsx | 4 +++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/app/migration/components.tsx b/packages/nextjs/app/migration/components.tsx index ae835b31..d86ad762 100644 --- a/packages/nextjs/app/migration/components.tsx +++ b/packages/nextjs/app/migration/components.tsx @@ -38,6 +38,7 @@ export default function ComponentsPage() {

      Select

      void; @@ -18,11 +19,12 @@ export interface Props { const itemToString = (item: Option | null) => (item ? item.title : ""); -export function Select({ label, placeholder, options, className, selectedItem, onChange }: Props) { +export function Select({ label, placeholder, options, className, selectedItem, onChange, id }: Props) { const { isOpen, getToggleButtonProps, getLabelProps, getMenuProps, highlightedIndex, getItemProps } = useSelect({ items: options, itemToString, selectedItem, + id, onSelectedItemChange({ selectedItem }) { onChange && onChange(selectedItem); }, From 4ebc04b76017f4a505174cefb945c47f7752942f Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 13:57:03 -0700 Subject: [PATCH 17/21] cleanup --- packages/nextjs/styles/global.css | 42 ------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 packages/nextjs/styles/global.css diff --git a/packages/nextjs/styles/global.css b/packages/nextjs/styles/global.css deleted file mode 100644 index af546bf1..00000000 --- a/packages/nextjs/styles/global.css +++ /dev/null @@ -1,42 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - @apply bg-secondary; -} - - -@font-face { - font-family: "JeanLuc"; - src: url('../assets/fonts/jeanluc/jeanlucweb-bold.woff'); - font-weight: bold; - font-style: normal; -} - -@font-face { - font-family: "JeanLuc"; - src: url('../assets/fonts/jeanluc/jeanlucweb-thin.woff'); - font-weight: auto; - font-style: normal; -} - -@font-face { - font-family: "JetBrains Mono"; - src: url('../assets/fonts/jetbrainsmono/JetBrainsMono-Bold.woff2'); - font-weight: bold; - font-style: normal; -} - -@font-face { - font-family: "JetBrains Mono"; - src: url('../assets/fonts/jetbrainsmono/JetBrainsMono-Regular.woff2'); - font-weight: auto; - font-style: normal; -} - -code { - @apply rounded-md bg-black/5 px-1.5 py-0.5; - font-family: 'JetBrains Mono', monospace; - font-size: 0.9rem; -} \ No newline at end of file From b2e06d38f3a65bb8f926b92833fa6cb9b2dd4b3c Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 14:12:18 -0700 Subject: [PATCH 18/21] e2e node version --- .github/workflows/e2e-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 1793cab7..92a89982 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: 16.x + node-version: 18.x - uses: pnpm/action-setup@v2.2.2 with: From 98f0adb11c1427513a506350e0b681cd9c8f2c68 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Mon, 4 Dec 2023 14:44:15 -0700 Subject: [PATCH 19/21] wait --- packages/shared-ui/components/cart/stories/Cart.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared-ui/components/cart/stories/Cart.stories.tsx b/packages/shared-ui/components/cart/stories/Cart.stories.tsx index 435490f6..f43ed803 100644 --- a/packages/shared-ui/components/cart/stories/Cart.stories.tsx +++ b/packages/shared-ui/components/cart/stories/Cart.stories.tsx @@ -124,6 +124,7 @@ export const API: StoryObj = { play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); await step("add to cart", async () => { + await waitFor(() => expect(canvas.getByTestId("cart")).toHaveTextContent("Cart1")); await userEvent.click(canvas.getByRole("button", { name: /product1/i })); await userEvent.click(canvas.getByRole("button", { name: /product2/i })); await waitFor(() => expect(canvas.getByTestId("cart")).toHaveTextContent("Cart3")); From 6938414cb17058ef490ad3f489f7442493d3f620 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 5 Dec 2023 08:40:47 -0700 Subject: [PATCH 20/21] shared var --- packages/nextjs/app/migration/products/[slug].tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/app/migration/products/[slug].tsx b/packages/nextjs/app/migration/products/[slug].tsx index 7e515843..389c7bd7 100644 --- a/packages/nextjs/app/migration/products/[slug].tsx +++ b/packages/nextjs/app/migration/products/[slug].tsx @@ -27,9 +27,10 @@ interface PageProps { const ProductPage: NextPage = ({ data }) => { const query = useSearchParams(); const product = data?.product; + const variant = query?.get("variant"); const selectedVariant = - (product?.variants || []).find((v) => v?.slug && v.slug === query?.get("variant")) || product?.variants?.[0]; + (product?.variants || []).find((v) => v?.slug && v.slug === variant) || product?.variants?.[0]; return ( @@ -39,7 +40,7 @@ const ProductPage: NextPage = ({ data }) => {
      - + From cb7c8625257066a184dd2ffe9fe3d9c77c009ad2 Mon Sep 17 00:00:00 2001 From: Nathan Kluth Date: Tue, 5 Dec 2023 09:51:57 -0700 Subject: [PATCH 21/21] remove old config val --- packages/nextjs/next.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/nextjs/next.config.js b/packages/nextjs/next.config.js index d9f2edbe..04543589 100644 --- a/packages/nextjs/next.config.js +++ b/packages/nextjs/next.config.js @@ -8,9 +8,6 @@ const nextConfig = { eslint: { ignoreDuringBuilds: true, }, - experimental: { - appDir: true, - }, }; module.exports = nextConfig;