Skip to content

Commit 6b684dd

Browse files
committed
feat: product module
1 parent 43e760b commit 6b684dd

File tree

22 files changed

+648
-240
lines changed

22 files changed

+648
-240
lines changed

.env.template

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ NEXT_PUBLIC_PAYPAL_CLIENT_ID=
1717
NEXT_PUBLIC_SEARCH_APP_ID=
1818
NEXT_PUBLIC_SEARCH_ENDPOINT=http://127.0.0.1:7700
1919
NEXT_PUBLIC_SEARCH_API_KEY=
20-
NEXT_PUBLIC_SEARCH_INDEX_NAME=products
20+
NEXT_PUBLIC_SEARCH_INDEX_NAME=products
21+
22+
# Your Next.js revalidation secret. See – https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation
23+
REVALIDATE_SECRET=supersecret

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ node_modules
4545

4646
.yarn
4747
.swc
48+
dump.rdb

next.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const store = require("./store.config.json")
44
module.exports = withStoreConfig({
55
experimental: {
66
serverActions: true,
7+
serverComponentsExternalPackages: ["@medusajs/product"],
78
},
89
features: store.features,
910
reactStrictMode: true,

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"dependencies": {
2323
"@headlessui/react": "^1.6.1",
2424
"@hookform/error-message": "^2.0.0",
25-
"@medusajs/medusa-js": "^6.0.0",
25+
"@medusajs/medusa-js": "^6.0.3",
26+
"@medusajs/product": "^0.1.7",
2627
"@meilisearch/instant-meilisearch": "^0.7.1",
2728
"@paypal/paypal-js": "^5.0.6",
2829
"@paypal/react-paypal-js": "^7.8.1",

src/app/(main)/collections/[handle]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Props = {
99
const BASEURL = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:8000"
1010

1111
async function getCollection(handle: string) {
12-
const res = await fetch(`${BASEURL}/collections?handle=${handle}`)
12+
const res = await fetch(`${BASEURL}/api/collections/${handle}`)
1313

1414
if (!res.ok) {
1515
notFound()

src/app/(main)/collections/route.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import medusaRequest from "@lib/medusa-fetch"
21
import ProductTemplate from "@modules/products/templates"
32
import { Metadata } from "next"
43
import { notFound } from "next/navigation"
@@ -8,27 +7,19 @@ type Props = {
87
}
98

109
async function getProducts(handle: string) {
11-
const res = await medusaRequest("GET", "/products", {
12-
query: {
13-
handle,
14-
},
15-
})
16-
17-
if (!res.ok) {
18-
notFound()
19-
}
20-
21-
return res.body
10+
const data = await fetch(
11+
`${process.env.NEXT_PUBLIC_BASE_URL}/api/products/${handle}`
12+
)
13+
.then((res) => res.json())
14+
.catch(() => {
15+
notFound()
16+
})
17+
18+
return data
2219
}
2320

2421
export async function generateMetadata({ params }: Props): Promise<Metadata> {
25-
const { products } = await getProducts(params.handle)
26-
27-
if (!products.length) {
28-
notFound()
29-
}
30-
31-
const product = products[0]
22+
const { product } = await getProducts(params.handle)
3223

3324
return {
3425
title: `${product.title} | Acme Store`,
@@ -42,7 +33,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
4233
}
4334

4435
export default async function CollectionPage({ params }: Props) {
45-
const { products } = await getProducts(params.handle)
36+
const { product } = await getProducts(params.handle)
4637

47-
return <ProductTemplate product={products[0]} />
38+
return <ProductTemplate product={product} />
4839
}

src/app/actions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use server"
2+
3+
import { revalidateTag } from "next/cache"
4+
5+
/**
6+
* Revalidates each cache tag in the passed array
7+
* @param {string[]} tags - array of tags to revalidate
8+
*/
9+
export async function revalidateTags(tags: string[]) {
10+
tags.forEach((tag) => {
11+
revalidateTag(tag)
12+
})
13+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { NextRequest, NextResponse } from "next/server"
2+
import { initialize as initializeProductModule } from "@medusajs/product"
3+
import { ProductCollectionDTO } from "@medusajs/types/dist/product"
4+
import { notFound } from "next/navigation"
5+
import getPrices from "@lib/util/get-product-prices"
6+
7+
export async function GET(
8+
request: NextRequest,
9+
{ params }: { params: Record<string, any> }
10+
) {
11+
const productService = await initializeProductModule()
12+
13+
const { handle } = params
14+
15+
const searchParams = Object.fromEntries(request.nextUrl.searchParams)
16+
const { page, limit, cart_id } = searchParams
17+
18+
const collections = await productService.listCollections()
19+
20+
const collectionsByHandle = new Map<string, ProductCollectionDTO>()
21+
22+
for (const collection of collections) {
23+
collectionsByHandle.set(collection.handle, collection)
24+
}
25+
26+
const collection = collectionsByHandle.get(handle)
27+
28+
if (!collection) {
29+
return notFound()
30+
}
31+
32+
const count = collection.products?.length || 0
33+
34+
const { products, ...collectionMeta } =
35+
await productService.retrieveCollection(collection.id, {
36+
relations: [
37+
"products",
38+
"products.variants",
39+
"products.variants.options",
40+
"products.tags",
41+
],
42+
take: limit || 100,
43+
skip: page || 0,
44+
})
45+
46+
if (!products) {
47+
return notFound()
48+
}
49+
50+
const productsWithPrices = await getPrices(products, cart_id)
51+
52+
const nextPage = parseInt(page) + parseInt(limit)
53+
54+
return NextResponse.json({
55+
collection: collectionMeta,
56+
response: {
57+
products: productsWithPrices,
58+
count,
59+
},
60+
nextPage: count > nextPage ? nextPage : null,
61+
})
62+
}

src/app/api/collections/route.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { NextRequest, NextResponse } from "next/server"
2+
import { initialize as initializeProductModule } from "@medusajs/product"
3+
import { notFound } from "next/navigation"
4+
5+
export async function GET(request: NextRequest) {
6+
const productService = await initializeProductModule()
7+
8+
const { offset } = Object.fromEntries(request.nextUrl.searchParams)
9+
10+
const [collections, count] = await productService.listAndCountCollections(
11+
{},
12+
{
13+
skip: offset || 0,
14+
take: 100,
15+
}
16+
)
17+
18+
if (!collections) {
19+
return notFound()
20+
}
21+
22+
return NextResponse.json({
23+
collections,
24+
count,
25+
})
26+
}

0 commit comments

Comments
 (0)