From 8d9cdbdb97105383d3f6a8a35d6371714d022d12 Mon Sep 17 00:00:00 2001 From: Carl-Adrien Mercey Date: Tue, 22 Apr 2025 14:55:51 +0200 Subject: [PATCH] chore: add SSG and ISR on beer, brewery and user pages --- .../_components/header/search-bar/index.tsx | 4 + .../_components/brewery-beer-list/index.tsx | 1 + .../brewery-friend-review-card/index.tsx | 1 + .../brewery-your-review-card/index.tsx | 1 + .../_components/beer-card/index.tsx | 9 +- .../_components/beer-reviews/index.tsx | 84 ++++++++++++++----- .../beer-reviews/review-card/index.tsx | 1 + .../beer-reviews/review-card/skeleton.tsx | 31 +++++++ .../friend-requests/accept/page.tsx | 1 + .../_components/review-card/index.tsx | 1 + .../search/_components/tab/beer/index.tsx | 1 + .../search/_components/tab/brewery/index.tsx | 1 + .../search/_components/tab/user/index.tsx | 1 + .../[username]/reviews/[reviewSlug]/page.tsx | 3 + src/app/_components/ui/pagination/index.tsx | 2 + src/components/ui/skeleton.tsx | 18 ++++ src/domain/beers/index.ts | 12 ++- src/domain/breweries/index.ts | 9 +- src/lib/cache/index.ts | 15 ++++ 19 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/skeleton.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/lib/cache/index.ts diff --git a/src/app/[locale]/(business)/(with-header)/_components/header/search-bar/index.tsx b/src/app/[locale]/(business)/(with-header)/_components/header/search-bar/index.tsx index 01ab917..9746afb 100644 --- a/src/app/[locale]/(business)/(with-header)/_components/header/search-bar/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/_components/header/search-bar/index.tsx @@ -251,6 +251,7 @@ const HeaderSearchBar = ({ brewerySlug: beer.brewery.slug, beerSlug: beer.slug, })} + prefetch={true} > {t(`headerSearch.${searchKind}.seeMore`, { diff --git a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-beer-list/index.tsx b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-beer-list/index.tsx index 3c948ba..6920246 100644 --- a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-beer-list/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-beer-list/index.tsx @@ -34,6 +34,7 @@ const BreweryBeerList = async ({ brewerySlug, beerSlug: beer.slug, })} + prefetch={true} >
diff --git a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-reviews/brewery-your-review-card/index.tsx b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-reviews/brewery-your-review-card/index.tsx index 3c2c91e..6ba1c84 100644 --- a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-reviews/brewery-your-review-card/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/_components/brewery-reviews/brewery-your-review-card/index.tsx @@ -25,6 +25,7 @@ const BreweryYourReviewCard = async ({ username: review.user.username, reviewSlug: review.slug, })} + prefetch={true} className="col-span-2 grid grid-cols-subgrid" >
diff --git a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-card/index.tsx b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-card/index.tsx index 4e2bde5..ed9eb1c 100644 --- a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-card/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-card/index.tsx @@ -65,7 +65,7 @@ const BeerCard = async ({
) : null} -
+

{name}

@@ -101,7 +102,7 @@ const BeerCard = async ({
-
+
{hasDetails ? ( - + {description ? ( { ); } - const yourReviews = await getBeerReviewsByUser({ + const yourReviewsPromise = getBeerReviewsByUser({ userId: user.id, beerId, page, @@ -67,32 +68,69 @@ const BeerReviews = async ({ beerId, page }: BeerReviewsProps) => { -

- {t.rich("beerPage.reviews.tabs.myReviews.count", { - count: yourReviews.count, - muted: (chunks) => ( - {chunks} - ), - })} -

- -
- {yourReviews.results.map((review) => ( - - ))} - - -
+ +

{t("beerPage.reviews.tabs.friendReviews.loading")}

+ +
+ + + + + +
+ + } + > + + {(reviews) => ( + <> +

+ {t.rich("beerPage.reviews.tabs.myReviews.count", { + count: reviews.count, + muted: (chunks) => ( + + {chunks} + + ), + })} +

+ +
+ {reviews.results.map((review) => ( + + ))} + + +
+ + )} +
+
{t("beerPage.reviews.tabs.friendReviews.loading")}

} + fallback={ + <> +

{t("beerPage.reviews.tabs.friendReviews.loading")}

+ +
+ + + + + +
+ + } > {(reviews) => ( diff --git a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/index.tsx b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/index.tsx index 031a36a..b6cad08 100644 --- a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/index.tsx @@ -27,6 +27,7 @@ const BeerReviewCard = async ({ review }: BeerReviewCardProps) => { username: review.username, reviewSlug: review.slug, })} + prefetch={true} className="col-span-2 grid grid-cols-subgrid" >
diff --git a/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/skeleton.tsx b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/skeleton.tsx new file mode 100644 index 0000000..9b24a04 --- /dev/null +++ b/src/app/[locale]/(business)/(with-header)/breweries/[brewerySlug]/beers/[beerSlug]/_components/beer-reviews/review-card/skeleton.tsx @@ -0,0 +1,31 @@ +import { ChevronRightIcon } from "lucide-react"; + +import Skeleton from "@/components/ui/skeleton"; + +const BeerReviewCardSkeleton = () => { + return ( +
+
+ + +
+ + + +
+
+ +
+
+ + + +
+ + +
+
+ ); +}; + +export default BeerReviewCardSkeleton; diff --git a/src/app/[locale]/(business)/(with-header)/friend-requests/accept/page.tsx b/src/app/[locale]/(business)/(with-header)/friend-requests/accept/page.tsx index 052e917..68903d7 100644 --- a/src/app/[locale]/(business)/(with-header)/friend-requests/accept/page.tsx +++ b/src/app/[locale]/(business)/(with-header)/friend-requests/accept/page.tsx @@ -87,6 +87,7 @@ const AcceptFriendRequestPage = async ({ href={generatePath(Routes.PROFILE, { username: friendRequest.friend.username, })} + prefetch={true} className="text-primary-700 underline" > {t("friendRequestPage.cta.visitProfile", { diff --git a/src/app/[locale]/(business)/(with-header)/users/[username]/_components/review-card/index.tsx b/src/app/[locale]/(business)/(with-header)/users/[username]/_components/review-card/index.tsx index 8d16fef..29e82f6 100644 --- a/src/app/[locale]/(business)/(with-header)/users/[username]/_components/review-card/index.tsx +++ b/src/app/[locale]/(business)/(with-header)/users/[username]/_components/review-card/index.tsx @@ -23,6 +23,7 @@ const UserReviewCard = async ({ username, review }: UserReviewCardProps) => { username, reviewSlug: review.slug, })} + prefetch={true} className="col-span-2 grid grid-cols-subgrid" >
diff --git a/src/app/[locale]/(business)/(without-header)/search/_components/tab/beer/index.tsx b/src/app/[locale]/(business)/(without-header)/search/_components/tab/beer/index.tsx index 604ef34..205d5a0 100644 --- a/src/app/[locale]/(business)/(without-header)/search/_components/tab/beer/index.tsx +++ b/src/app/[locale]/(business)/(without-header)/search/_components/tab/beer/index.tsx @@ -48,6 +48,7 @@ const BeerTab = async ({ results, count, page }: BeerTabProps) => { brewerySlug: beer.brewery.slug, beerSlug: beer.slug, })} + prefetch={true} > { href={generatePath(Routes.BREWERY, { brewerySlug: brewery.slug, })} + prefetch={true} > { href={generatePath(Routes.PROFILE, { username: user.username, })} + prefetch={true} > { brewerySlug: review.beer.brewery.slug, beerSlug: review.beer.slug, })} + prefetch={true} className={cn( "text-primary font-title cursor-pointer", "text-2xl md:text-4xl", @@ -127,6 +128,7 @@ const UserReviewPage = async ({ params }: UserReviewPageProps) => { href={generatePath(Routes.BREWERY, { brewerySlug: review.beer.brewery.slug, })} + prefetch={true} className="text-primary cursor-pointer truncate" > {chunks} @@ -206,6 +208,7 @@ const UserReviewPage = async ({ params }: UserReviewPageProps) => { {
  • {
  • { + return ( +
    + ); +}; + +export default Skeleton; diff --git a/src/domain/beers/index.ts b/src/domain/beers/index.ts index 615d4ff..7ee0297 100644 --- a/src/domain/beers/index.ts +++ b/src/domain/beers/index.ts @@ -1,7 +1,6 @@ "server only"; import { nanoid } from "nanoid"; -import { cache } from "react"; import { InvalidBreweryError, @@ -17,6 +16,7 @@ import { transformRawStyleCategoryToStyleCategory, } from "@/domain/beers/transforms"; import { getCurrentUser } from "@/lib/auth"; +import { nextCache } from "@/lib/cache"; import { getPaginatedResults } from "@/lib/pagination"; import prisma, { getPrismaTransactionClient } from "@/lib/prisma"; import { slugify } from "@/lib/prisma/utils"; @@ -35,8 +35,12 @@ import type { } from "@/lib/pagination/types"; import type { Prisma } from "@prisma/client"; -export const getBeerBySlug = cache( - async (beerSlug: string, brewerySlug: string): Promise => { +export const getBeerBySlug = nextCache({ + tags: ([beerSlug, brewerySlug]) => [ + `brewery/slug:${brewerySlug}`, + `beer/slug:${beerSlug}`, + ], + callback: async (beerSlug: string, brewerySlug: string): Promise => { if (brewerySlug.length < 4 || beerSlug.length < 4) { throw new InvalidBeerSlugError(); } @@ -81,7 +85,7 @@ export const getBeerBySlug = cache( return transformRawBeerToBeer(beer); }, -); +}); export const getColors = async (): Promise => { const colors = await prisma.colors.findMany(); diff --git a/src/domain/breweries/index.ts b/src/domain/breweries/index.ts index a7a4347..f1b2841 100644 --- a/src/domain/breweries/index.ts +++ b/src/domain/breweries/index.ts @@ -1,7 +1,6 @@ "server only"; import { nanoid } from "nanoid"; -import { cache } from "react"; import { InvalidBrewerySlugError, @@ -13,6 +12,7 @@ import { transformRawBreweryToBrewery, } from "@/domain/breweries/transforms"; import { getCurrentUser } from "@/lib/auth"; +import { nextCache } from "@/lib/cache"; import { getPaginatedResults } from "@/lib/pagination"; import prisma, { getPrismaTransactionClient } from "@/lib/prisma"; import { slugify } from "@/lib/prisma/utils"; @@ -25,8 +25,9 @@ import type { } from "@/lib/pagination/types"; import type { Prisma } from "@prisma/client"; -export const getBreweryBySlug = cache( - async (brewerySlug: string): Promise => { +export const getBreweryBySlug = nextCache({ + tags: ([brewerySlug]) => [`brewery/slug:${brewerySlug}`], + callback: async (brewerySlug: string): Promise => { if (brewerySlug.length < 4) { throw new InvalidBrewerySlugError(); } @@ -65,7 +66,7 @@ export const getBreweryBySlug = cache( return transformRawBreweryToBrewery(brewery); }, -); +}); interface GetBreweryReviewsParams { userId: string; diff --git a/src/lib/cache/index.ts b/src/lib/cache/index.ts new file mode 100644 index 0000000..fb02ede --- /dev/null +++ b/src/lib/cache/index.ts @@ -0,0 +1,15 @@ +import { unstable_cache } from "next/cache"; + +type CacheOptions = { + callback: (...params: TArgs) => Promise; + tags?: (params: TArgs) => string[]; + expiresAfter?: number; +}; + +export const nextCache = + (config: CacheOptions) => + async (...args: TArgs): Promise => + unstable_cache(config.callback, [], { + tags: config.tags?.(args) ?? [], + revalidate: config.expiresAfter ?? false, + })(...args);