From 0b4406bf371bed098a57271790a9d2168708d838 Mon Sep 17 00:00:00 2001 From: Daniel Almaguer Date: Wed, 1 May 2024 15:45:08 -0500 Subject: [PATCH] refactor(core): cache pdp page data and metadata request --- .../(default)/product/[slug]/page-data.ts | 61 ++++++++++++++ .../(default)/product/[slug]/page.tsx | 82 ++++++------------- 2 files changed, 88 insertions(+), 55 deletions(-) create mode 100644 apps/core/app/[locale]/(default)/product/[slug]/page-data.ts diff --git a/apps/core/app/[locale]/(default)/product/[slug]/page-data.ts b/apps/core/app/[locale]/(default)/product/[slug]/page-data.ts new file mode 100644 index 000000000..e92cdfc5d --- /dev/null +++ b/apps/core/app/[locale]/(default)/product/[slug]/page-data.ts @@ -0,0 +1,61 @@ +import { cache } from 'react'; + +import { getSessionCustomerId } from '~/auth'; +import { client } from '~/client'; +import { graphql, VariablesOf } from '~/client/graphql'; +import { revalidate } from '~/client/revalidate-target'; + +import { BreadcrumbsFragment } from './_components/breadcrumbs'; +import { DescriptionFragment } from './_components/description'; +import { DetailsFragment } from './_components/details'; +import { GalleryFragment } from './_components/gallery/fragment'; +import { WarrantyFragment } from './_components/warranty'; + +const ProductPageQuery = graphql( + ` + query ProductPageQuery($entityId: Int!, $optionValueIds: [OptionValueId!]) { + site { + product(entityId: $entityId, optionValueIds: $optionValueIds) { + ...GalleryFragment + ...DetailsFragment + ...DescriptionFragment + ...WarrantyFragment + entityId + name + defaultImage { + url: urlTemplate + altText + } + categories(first: 1) { + edges { + node { + ...BreadcrumbsFragment + } + } + } + seo { + pageTitle + metaDescription + metaKeywords + } + } + } + } + `, + [BreadcrumbsFragment, GalleryFragment, DetailsFragment, DescriptionFragment, WarrantyFragment], +); + +type ProductPageQueryVariables = VariablesOf; + +export const getProduct = cache(async (variables: ProductPageQueryVariables) => { + const customerId = await getSessionCustomerId(); + + const { data } = await client.fetch({ + document: ProductPageQuery, + variables, + customerId, + fetchOptions: customerId ? { cache: 'no-store' } : { next: { revalidate } }, + }); + + return data.site.product; +}); diff --git a/apps/core/app/[locale]/(default)/product/[slug]/page.tsx b/apps/core/app/[locale]/(default)/product/[slug]/page.tsx index 4cb20eadb..d01faf283 100644 --- a/apps/core/app/[locale]/(default)/product/[slug]/page.tsx +++ b/apps/core/app/[locale]/(default)/product/[slug]/page.tsx @@ -5,30 +5,30 @@ import { NextIntlClientProvider } from 'next-intl'; import { getMessages, getTranslations, unstable_setRequestLocale } from 'next-intl/server'; import { Suspense } from 'react'; -import { getSessionCustomerId } from '~/auth'; -import { client } from '~/client'; -import { graphql } from '~/client/graphql'; -import { getProduct } from '~/client/queries/get-product'; -import { revalidate } from '~/client/revalidate-target'; import { LocaleType } from '~/i18n'; -import { Breadcrumbs, BreadcrumbsFragment } from './_components/breadcrumbs'; -import { Description, DescriptionFragment } from './_components/description'; -import { Details, DetailsFragment } from './_components/details'; +import { Breadcrumbs } from './_components/breadcrumbs'; +import { Description } from './_components/description'; +import { Details } from './_components/details'; import { Gallery } from './_components/gallery'; -import { GalleryFragment } from './_components/gallery/fragment'; import { RelatedProducts } from './_components/related-products'; import { Reviews } from './_components/reviews'; -import { Warranty, WarrantyFragment } from './_components/warranty'; +import { Warranty } from './_components/warranty'; +import { getProduct } from './page-data'; interface ProductPageProps { params: { slug: string; locale: LocaleType }; searchParams: { [key: string]: string | string[] | undefined }; } -export async function generateMetadata({ params }: ProductPageProps): Promise { +export async function generateMetadata({ + params, + searchParams, +}: ProductPageProps): Promise { const productId = Number(params.slug); - const product = await getProduct(productId); + const optionValueIds = getOptionValueIds({ searchParams }); + + const product = await getProduct({ entityId: productId, optionValueIds }); if (!product) { return {}; @@ -54,33 +54,7 @@ export async function generateMetadata({ params }: ProductPageProps): Promise ({ - optionEntityId: Number(option), - valueEntityId: Number(searchParams[option]), - })) - .filter( - (option) => !Number.isNaN(option.optionEntityId) && !Number.isNaN(option.valueEntityId), - ); - const { data } = await client.fetch({ - document: ProductPageQuery, - variables: { entityId: productId, optionValueIds }, - customerId, - fetchOptions: customerId ? { cache: 'no-store' } : { next: { revalidate } }, - }); + const optionValueIds = getOptionValueIds({ searchParams }); - const product = data.site.product; + const product = await getProduct({ entityId: productId, optionValueIds }); if (!product) { return notFound(); @@ -140,4 +99,17 @@ export default async function Product({ params, searchParams }: ProductPageProps ); } +function getOptionValueIds({ searchParams }: { searchParams: ProductPageProps['searchParams'] }) { + const { slug, ...options } = searchParams; + + return Object.keys(options) + .map((option) => ({ + optionEntityId: Number(option), + valueEntityId: Number(searchParams[option]), + })) + .filter( + (option) => !Number.isNaN(option.optionEntityId) && !Number.isNaN(option.valueEntityId), + ); +} + export const runtime = 'edge';