Skip to content

Commit 78377c2

Browse files
committed
feat: migrate collections
1 parent dbed9b9 commit 78377c2

File tree

28 files changed

+356
-205
lines changed

28 files changed

+356
-205
lines changed

src/app/checkout/layout.tsx

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,9 @@
1-
"use client"
2-
3-
import { MEDUSA_BACKEND_URL, queryClient } from "@lib/config"
4-
import { AccountProvider } from "@lib/context/account-context"
5-
import { StoreProvider } from "@lib/context/store-context"
6-
import { CartProvider, MedusaProvider } from "medusa-react"
7-
import "styles/globals.css"
1+
import Providers from "@modules/providers"
82

93
export default function CheckoutLayout({
104
children,
115
}: {
126
children: React.ReactNode
137
}) {
14-
return (
15-
<MedusaProvider
16-
baseUrl={MEDUSA_BACKEND_URL}
17-
queryClientProviderProps={{
18-
client: queryClient,
19-
}}
20-
>
21-
<CartProvider>
22-
<StoreProvider>
23-
<AccountProvider>
24-
<main className="relative">{children}</main>
25-
</AccountProvider>
26-
</StoreProvider>
27-
</CartProvider>
28-
</MedusaProvider>
29-
)
8+
return <Providers>{children}</Providers>
309
}

src/app/checkout/page.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,5 @@ export const metadata: Metadata = {
66
}
77

88
export default function Checkout() {
9-
return (
10-
<>
11-
<CheckoutTemplate />
12-
</>
13-
)
9+
return <CheckoutTemplate />
1410
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"use client"
2+
3+
import usePreviews from "@lib/hooks/use-previews"
4+
import getNumberOfSkeletons from "@lib/util/get-number-of-skeletons"
5+
import repeat from "@lib/util/repeat"
6+
import ProductPreview from "@modules/products/components/product-preview"
7+
import SkeletonProductPreview from "@modules/skeletons/components/skeleton-product-preview"
8+
import { useInfiniteQuery } from "@tanstack/react-query"
9+
import { useCart } from "medusa-react"
10+
import React, { useEffect } from "react"
11+
import { useInView } from "react-intersection-observer"
12+
13+
type CollectionTemplateProps = {
14+
collection: {
15+
handle: string
16+
title: string
17+
}
18+
}
19+
20+
const BASEURL = process.env.NEXT_PUBLIC_BASE_URL
21+
22+
const fetchCollectionProducts = async ({
23+
pageParam,
24+
handle,
25+
cartId,
26+
}: {
27+
pageParam?: string
28+
handle: string
29+
cartId?: string
30+
}) => {
31+
const { products, count, nextPage } = await fetch(
32+
`${BASEURL}/collections?handle=${handle}&cart_id=${cartId}&page=${pageParam}`
33+
).then((res) => res.json())
34+
return {
35+
response: { products, count },
36+
nextPage,
37+
}
38+
}
39+
40+
const Collection: React.FC<CollectionTemplateProps> = ({ collection }) => {
41+
const { cart } = useCart()
42+
const { ref, inView } = useInView()
43+
44+
const {
45+
data: infiniteData,
46+
hasNextPage,
47+
fetchNextPage,
48+
isFetchingNextPage,
49+
isLoading,
50+
refetch,
51+
} = useInfiniteQuery(
52+
[`get_collection_products`, collection.handle, cart?.id],
53+
({ pageParam }) =>
54+
fetchCollectionProducts({
55+
pageParam,
56+
handle: collection.handle,
57+
cartId: cart?.id,
58+
}),
59+
{
60+
getNextPageParam: (lastPage) => lastPage.nextPage,
61+
}
62+
)
63+
64+
useEffect(() => {
65+
if (cart?.region_id) {
66+
refetch()
67+
}
68+
}, [cart?.region_id, refetch])
69+
70+
const previews = usePreviews({
71+
pages: infiniteData?.pages,
72+
region: cart?.region,
73+
})
74+
75+
useEffect(() => {
76+
if (inView && hasNextPage) {
77+
fetchNextPage()
78+
}
79+
// eslint-disable-next-line react-hooks/exhaustive-deps
80+
}, [inView, hasNextPage])
81+
82+
return (
83+
<div className="content-container py-6">
84+
<div className="mb-8 text-2xl-semi">
85+
<h1>{collection.title}</h1>
86+
</div>
87+
<ul className="grid grid-cols-2 small:grid-cols-3 medium:grid-cols-4 gap-x-4 gap-y-8">
88+
{previews.map((p) => (
89+
<li key={p.id}>
90+
<ProductPreview {...p} />
91+
</li>
92+
))}
93+
{isLoading &&
94+
!previews.length &&
95+
repeat(8).map((index) => (
96+
<li key={index}>
97+
<SkeletonProductPreview />
98+
</li>
99+
))}
100+
{isFetchingNextPage &&
101+
repeat(getNumberOfSkeletons(infiniteData?.pages)).map((index) => (
102+
<li key={index}>
103+
<SkeletonProductPreview />
104+
</li>
105+
))}
106+
</ul>
107+
<div
108+
className="py-16 flex justify-center items-center text-small-regular text-gray-700"
109+
ref={ref}
110+
>
111+
<span ref={ref}></span>
112+
</div>
113+
</div>
114+
)
115+
}
116+
117+
export default Collection
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Providers from "@modules/providers"
2+
3+
export default function CollectionLayout({
4+
children,
5+
}: {
6+
children: React.ReactNode
7+
}) {
8+
return <Providers>{children}</Providers>
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import SkeletonCollectionPage from "@modules/skeletons/templates/skeleton-collection-page"
2+
3+
export default function Loading() {
4+
return <SkeletonCollectionPage />
5+
}

src/app/collections/[handle]/page.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Collection from "./collection"
2+
3+
import { Metadata } from "next"
4+
5+
type Props = {
6+
params: { handle: string }
7+
searchParams: { [key: string]: string | string[] | undefined }
8+
}
9+
10+
const BASEURL = process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
11+
12+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
13+
const { collection } = await fetch(
14+
`${BASEURL}/collections?handle=${params.handle}`
15+
).then((res) => res.json())
16+
17+
return {
18+
title: `${collection.title} | Acme Store`,
19+
description: `${collection.title} collection`,
20+
}
21+
}
22+
23+
export default async function CollectionPage({ params }: Props) {
24+
const { collection } = await fetch(
25+
`${BASEURL}/collections?handle=${params.handle}`
26+
).then((res) => res.json())
27+
28+
return (
29+
<>
30+
<Collection collection={collection} />
31+
</>
32+
)
33+
}

src/app/collections/route.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import medusaRequest from "@lib/medusa-fetch"
2+
import { NextRequest, NextResponse } from "next/server"
3+
4+
export async function GET(request: NextRequest) {
5+
const searchParams = request.nextUrl.searchParams
6+
const handle = searchParams.get("handle") ?? ""
7+
const pageParam = searchParams.get("pageParam") ?? "0"
8+
9+
const collection = await fetchCollection(handle)
10+
.then((res) => res)
11+
.catch((error) => {
12+
throw new Error(JSON.stringify(error.error))
13+
})
14+
15+
const { response, nextPage } = await fetchCollectionProducts({
16+
pageParam,
17+
id: collection.id,
18+
}).then((res) => res)
19+
20+
return NextResponse.json({
21+
collection,
22+
products: response.products,
23+
count: response.count,
24+
nextPage,
25+
})
26+
}
27+
28+
async function fetchCollection(handle: string) {
29+
return await medusaRequest("GET", `/collections?handle[]=${handle}`).then(
30+
({ body }) => ({
31+
id: body.collections[0].id,
32+
handle: body.collections[0].handle,
33+
title: body.collections[0].title,
34+
})
35+
)
36+
}
37+
38+
async function fetchCollectionProducts({
39+
pageParam = "0",
40+
id,
41+
}: {
42+
pageParam?: string
43+
id: string
44+
}) {
45+
const { products, count, offset } = await medusaRequest(
46+
"GET",
47+
`/products?collection_id[]=${id}`,
48+
{
49+
limit: "12",
50+
offset: pageParam,
51+
}
52+
).then((res) => res.body)
53+
54+
return {
55+
response: { products, count },
56+
nextPage: count > offset + 12 ? offset + 12 : null,
57+
}
58+
}

src/app/layout.tsx

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
"use client"
2-
3-
import { MEDUSA_BACKEND_URL, queryClient } from "@lib/config"
4-
import { AccountProvider } from "@lib/context/account-context"
5-
import { CartDropdownProvider } from "@lib/context/cart-dropdown-context"
6-
import { MobileMenuProvider } from "@lib/context/mobile-menu-context"
7-
import { StoreProvider } from "@lib/context/store-context"
8-
import { CartProvider, MedusaProvider } from "medusa-react"
9-
import Footer from "@modules/layout/templates/footer"
10-
import Nav from "@modules/layout/templates/nav"
111
import "styles/globals.css"
122

133
export default function RootLayout({
@@ -18,26 +8,7 @@ export default function RootLayout({
188
return (
199
<html lang="en">
2010
<body>
21-
<MedusaProvider
22-
baseUrl={MEDUSA_BACKEND_URL}
23-
queryClientProviderProps={{
24-
client: queryClient,
25-
}}
26-
>
27-
<CartDropdownProvider>
28-
<MobileMenuProvider>
29-
<CartProvider>
30-
<StoreProvider>
31-
<AccountProvider>
32-
<Nav />
33-
<main className="relative">{children}</main>
34-
<Footer />
35-
</AccountProvider>
36-
</StoreProvider>
37-
</CartProvider>
38-
</MobileMenuProvider>
39-
</CartDropdownProvider>
40-
</MedusaProvider>
11+
<main className="relative">{children}</main>
4112
</body>
4213
</html>
4314
)

src/app/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import FeaturedProducts from "@modules/home/components/featured-products"
22
import Hero from "@modules/home/components/hero"
3+
import Footer from "@modules/layout/templates/footer"
4+
import Nav from "@modules/layout/templates/nav"
5+
import Providers from "@modules/providers"
36
import { Metadata } from "next"
47

58
export const metadata: Metadata = {
@@ -10,10 +13,12 @@ export const metadata: Metadata = {
1013

1114
const Home = () => {
1215
return (
13-
<>
16+
<Providers>
17+
<Nav />
1418
<Hero />
1519
<FeaturedProducts />
16-
</>
20+
<Footer />
21+
</Providers>
1722
)
1823
}
1924

src/lib/context/account-context.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client"
2+
13
import { medusaClient } from "@lib/config"
24
import { Customer } from "@medusajs/medusa"
35
import { useMutation } from "@tanstack/react-query"

0 commit comments

Comments
 (0)