Skip to content

Commit

Permalink
fix: pagination for PDP reviews
Browse files Browse the repository at this point in the history
  • Loading branch information
migueloller committed Feb 11, 2025
1 parent 8ad9332 commit 40b142e
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 21 deletions.
76 changes: 57 additions & 19 deletions core/app/[locale]/(default)/product/[slug]/_components/reviews.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client';
import { useTranslations } from 'next-intl';
import { getFormatter, getTranslations } from 'next-intl/server';
import { cache } from 'react';

Expand All @@ -13,15 +12,25 @@ import { revalidate } from '~/client/revalidate-target';
import { ProductReviewSchemaFragment } from './product-review-schema/fragment';
import { ProductReviewSchema } from './product-review-schema/product-review-schema';

export const PaginationSearchParamNames = {
BEFORE: 'reviews_before',
AFTER: 'reviews_after',
} as const;

interface SearchParams {
[PaginationSearchParamNames.BEFORE]?: string | null;
[PaginationSearchParamNames.AFTER]?: string | null;
}

const ReviewsQuery = graphql(
`
query ReviewsQuery($entityId: Int!) {
query ReviewsQuery($entityId: Int!, $first: Int, $after: String, $before: String, $last: Int) {
site {
product(entityId: $entityId) {
reviewSummary {
averageRating
}
reviews(first: 5) {
reviews(first: $first, after: $after, before: $before, last: $last) {
pageInfo {
...PaginationFragment
}
Expand All @@ -48,18 +57,37 @@ const ReviewsQuery = graphql(
[ProductReviewSchemaFragment, PaginationFragment],
);

const getReviewsData = cache(async (productId: number) => {
const getReviewsData = cache(async (productId: number, searchParams: Promise<SearchParams>) => {
const pageSize = 5;
const { [PaginationSearchParamNames.AFTER]: after, [PaginationSearchParamNames.BEFORE]: before } =
await searchParams;

const variables = { entityId: productId };

if (after != null) {
variables.first = pageSize;
variables.after = after;
}

if (after == null && before != null) {
variables.last = pageSize;
variables.before = before;
}

const { data } = await client.fetch({
document: ReviewsQuery,
variables: { entityId: productId },
variables,
fetchOptions: { next: { revalidate } },
});

return data.site.product;
});

const getReviews = async (productId: number) => {
const product = await getReviewsData(productId);
const getReviews = async (
productId: number,
searchParams: Promise<{ before?: string; after?: string }>,
) => {
const product = await getReviewsData(productId, searchParams);

if (!product) {
return [];
Expand All @@ -68,8 +96,11 @@ const getReviews = async (productId: number) => {
return removeEdgesAndNodes(product.reviews);
};

const getFormattedReviews = async (productId: number) => {
const reviews = await getReviews(productId);
const getFormattedReviews = async (
productId: number,
searchParams: Promise<{ before?: string; after?: string }>,
) => {
const reviews = await getReviews(productId, searchParams);
const format = await getFormatter();

return reviews.map((review) => ({
Expand All @@ -86,7 +117,7 @@ const getFormattedReviews = async (productId: number) => {
};

const getAverageRating = async (productId: number) => {
const product = await getReviewsData(productId);
const product = await getReviewsData(productId, Promise.resolve({}));

if (!product) {
return 0;
Expand All @@ -95,9 +126,12 @@ const getAverageRating = async (productId: number) => {
return product.reviewSummary.averageRating;
};

const getPaginationInfo = async (productId: number) => {
const getPaginationInfo = async (
productId: number,
searchParams: Promise<{ before?: string; after?: string }>,
) => {
const t = await getTranslations('Product.Reviews.Pagination');
const product = await getReviewsData(productId);
const product = await getReviewsData(productId, searchParams);

if (!product) {
return {};
Expand All @@ -107,8 +141,8 @@ const getPaginationInfo = async (productId: number) => {

return hasNextPage || hasPreviousPage
? {
startCursorParamName: 'before',
endCursorParamName: 'after',
startCursorParamName: PaginationSearchParamNames.BEFORE,
endCursorParamName: PaginationSearchParamNames.AFTER,
endCursor: hasNextPage ? endCursor : null,
startCursor: hasPreviousPage ? startCursor : null,
nextLabel: t('next'),
Expand All @@ -119,21 +153,25 @@ const getPaginationInfo = async (productId: number) => {

interface Props {
productId: number;
searchParams: Promise<{
[PaginationSearchParamNames.BEFORE]?: string;
[PaginationSearchParamNames.AFTER]?: string;
}>;
}

export const Reviews = ({ productId }: Props) => {
const t = useTranslations('Product.Reviews');
export const Reviews = async ({ productId, searchParams }: Props) => {
const t = await getTranslations('Product.Reviews');

return (
<>
<ReviewsSection
averageRating={getAverageRating(productId)}
emptyStateMessage={t('empty')}
paginationInfo={getPaginationInfo(productId)}
reviews={getFormattedReviews(productId)}
paginationInfo={getPaginationInfo(productId, searchParams)}
reviews={getFormattedReviews(productId, searchParams)}
reviewsLabel={t('title')}
/>
<Stream fallback={null} value={getReviews(productId)}>
<Stream fallback={null} value={getReviews(productId, searchParams)}>
{(reviews) =>
reviews.length > 0 && <ProductReviewSchema productId={productId} reviews={reviews} />
}
Expand Down
11 changes: 9 additions & 2 deletions core/app/[locale]/(default)/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client';
import { Metadata } from 'next';
import { getFormatter, getTranslations, setRequestLocale } from 'next-intl/server';
import { createSearchParamsCache, parseAsString } from 'nuqs/server';
import { cache } from 'react';

import { Stream } from '@/vibes/soul/lib/streamable';
Expand All @@ -14,7 +15,7 @@ import { getPreferredCurrencyCode } from '~/lib/currency';
import { addToCart } from './_actions/add-to-cart';
import { ProductSchema } from './_components/product-schema';
import { ProductViewed } from './_components/product-viewed';
import { Reviews } from './_components/reviews';
import { PaginationSearchParamNames, Reviews } from './_components/reviews';
import { getProductData } from './page-data';

const cachedProductDataVariables = cache(
Expand Down Expand Up @@ -220,6 +221,11 @@ export async function generateMetadata(props: Props): Promise<Metadata> {
};
}

const searchParamsCache = createSearchParamsCache({
[PaginationSearchParamNames.BEFORE]: parseAsString,
[PaginationSearchParamNames.AFTER]: parseAsString,
});

export default async function Product(props: Props) {
const { locale, slug } = await props.params;

Expand All @@ -229,6 +235,7 @@ export default async function Product(props: Props) {

const productId = Number(slug);
const variables = await cachedProductDataVariables(slug, props.searchParams);
const parsedSearchParams = searchParamsCache.parse(props.searchParams);

return (
<>
Expand Down Expand Up @@ -257,7 +264,7 @@ export default async function Product(props: Props) {
title={t('RelatedProducts.title')}
/>

<Reviews productId={productId} />
<Reviews productId={productId} searchParams={parsedSearchParams} />

<Stream fallback={null} value={getProductData(variables)}>
{(product) => (
Expand Down

0 comments on commit 40b142e

Please sign in to comment.