Skip to content

Commit cc1cc2e

Browse files
committed
feat: pricing module in categories and collections
1 parent 584c189 commit cc1cc2e

File tree

20 files changed

+424
-572
lines changed

20 files changed

+424
-572
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
"dependencies": {
2323
"@headlessui/react": "^1.6.1",
2424
"@hookform/error-message": "^2.0.0",
25-
"@medusajs/link-modules": "^0.1.0",
2625
"@medusajs/medusa-js": "^6.0.3",
27-
"@medusajs/modules-sdk": "^1.10.0",
28-
"@medusajs/pricing": "^0.0.2",
29-
"@medusajs/product": "^0.2.0",
26+
"@medusajs/link-modules": "0.1.1-snapshot-20230913073011",
27+
"@medusajs/modules-sdk": "1.11.0-snapshot-20230913073011",
28+
"@medusajs/pricing": "0.0.3-snapshot-20230913073011",
29+
"@medusajs/product": "latest",
3030
"@meilisearch/instant-meilisearch": "^0.7.1",
3131
"@paypal/paypal-js": "^5.0.6",
3232
"@paypal/react-paypal-js": "^7.8.1",

src/app/(main)/[category]/page.tsx renamed to src/app/(main)/[...category]/page.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { Metadata } from "next"
44
import { notFound } from "next/navigation"
55

66
type Props = {
7-
params: { category: string }
7+
params: { category: string[] }
88
}
99

1010
export async function generateMetadata({ params }: Props): Promise<Metadata> {
11-
const { product_categories } = await getCategoryByHandle(
12-
params.category
13-
).catch((err) => {
14-
notFound()
15-
})
11+
const handle = params.category.join("/")
12+
13+
const { product_categories } = await getCategoryByHandle(handle).catch(
14+
(err) => {
15+
notFound()
16+
}
17+
)
1618

1719
const category = product_categories[0]
1820

@@ -23,13 +25,12 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
2325
}
2426

2527
export default async function CategoryPage({ params }: Props) {
26-
const { product_categories } = await getCategoryByHandle(
27-
params.category
28-
).catch((err) => {
29-
notFound()
30-
})
31-
32-
const category = product_categories[0]
28+
const handle = params.category.join("/")
29+
const { product_categories } = await getCategoryByHandle(handle).catch(
30+
(err) => {
31+
notFound()
32+
}
33+
)
3334

34-
return <CategoryTemplate category={category} />
35+
return <CategoryTemplate categories={product_categories} />
3536
}

src/app/(main)/[category]/[subcategory]/page.tsx

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/app/(main)/[category]/loading.tsx

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { NextRequest, NextResponse } from "next/server"
2+
import { initialize as initializeProductModule } from "@medusajs/product"
3+
import { ProductDTO } from "@medusajs/types/dist/product"
4+
import { notFound } from "next/navigation"
5+
import { MedusaApp, Modules } from "@medusajs/modules-sdk"
6+
7+
/**
8+
* This endpoint uses the serverless Product Module to retrieve a category and its products by handle.
9+
* The module connects directly to you Medusa database to retrieve and manipulate data, without the need for a dedicated server.
10+
* Read more about the Product Module here: https://docs.medusajs.com/modules/products/serverless-module
11+
*/
12+
export async function GET(
13+
request: NextRequest,
14+
{ params }: { params: Record<string, any> }
15+
) {
16+
const productService = await initializeProductModule()
17+
18+
const searchParams = Object.fromEntries(request.nextUrl.searchParams)
19+
const { page, limit } = searchParams
20+
21+
let { category: categoryHandle } = params
22+
23+
const handle = categoryHandle.map((handle: string, index: number) => {
24+
return categoryHandle.slice(0, index + 1).join("/")
25+
})
26+
27+
const product_categories = await productService
28+
.listCategories(
29+
{
30+
handle,
31+
},
32+
{
33+
select: ["id", "handle", "name", "description"],
34+
relations: ["category_children"],
35+
take: handle.length,
36+
}
37+
)
38+
.catch((e) => {
39+
return notFound()
40+
})
41+
42+
const category = product_categories[0]
43+
44+
if (!category) {
45+
return notFound()
46+
}
47+
48+
const {
49+
rows: products,
50+
metadata: { count },
51+
} = await getProductsByCategoryId(category.id, searchParams)
52+
53+
const publishedProducts: ProductDTO[] = products.filter(
54+
(product) => product.status === "published"
55+
)
56+
57+
const nextPage = parseInt(page) + parseInt(limit)
58+
59+
return NextResponse.json({
60+
product_categories: Object.values(product_categories),
61+
response: {
62+
products: publishedProducts,
63+
count,
64+
},
65+
nextPage: count > nextPage ? nextPage : null,
66+
})
67+
}
68+
69+
async function getProductsByCategoryId(
70+
category_id: string,
71+
params: Record<string, any>
72+
): Promise<{ rows: ProductDTO[]; metadata: Record<string, any> }> {
73+
// Extract the query parameters
74+
let { currency_code } = params
75+
76+
currency_code = currency_code && currency_code.toUpperCase()
77+
78+
const { query, modules } = await MedusaApp({
79+
modulesConfig: [
80+
{
81+
module: Modules.PRODUCT,
82+
path: "@medusajs/product",
83+
},
84+
{
85+
module: Modules.PRICING,
86+
path: "@medusajs/pricing",
87+
},
88+
],
89+
sharedResourcesConfig: {
90+
database: { clientUrl: process.env.POSTGRES_URL },
91+
},
92+
})
93+
94+
const filters = {
95+
take: parseInt(params.limit) || 100,
96+
skip: parseInt(params.offset) || 0,
97+
filters: {
98+
category_id: [category_id],
99+
},
100+
currency_code,
101+
}
102+
103+
const productsQuery = `#graphql
104+
query($filters: Record, $take: Int, $skip: Int) {
105+
products(filters: $filters, take: $take, skip: $skip) {
106+
id
107+
title
108+
handle
109+
tags
110+
status
111+
collection
112+
collection_id
113+
thumbnail
114+
images {
115+
url
116+
alt_text
117+
id
118+
}
119+
options {
120+
id
121+
value
122+
title
123+
}
124+
variants {
125+
id
126+
title
127+
created_at
128+
updated_at
129+
thumbnail
130+
inventory_quantity
131+
material
132+
weight
133+
length
134+
height
135+
width
136+
options {
137+
id
138+
value
139+
title
140+
}
141+
price {
142+
price_set {
143+
id
144+
}
145+
}
146+
}
147+
}
148+
}`
149+
150+
const { rows, metadata } = await query(productsQuery, filters)
151+
152+
for (const product of rows) {
153+
for (const variant of product.variants) {
154+
const priceSetIds = variant.price.map((price) => price.price_set.id)
155+
156+
const prices = await modules.pricingService.calculatePrices(
157+
{ id: priceSetIds },
158+
{
159+
context: { currency_code },
160+
}
161+
)
162+
163+
const price = prices.find(
164+
(price) => price.currency_code === currency_code
165+
)
166+
167+
if (!price) continue
168+
169+
delete variant.price
170+
variant.price = price
171+
variant.calculated_price = price.amount
172+
}
173+
}
174+
175+
return {
176+
rows,
177+
metadata,
178+
}
179+
}

src/app/api/categories/[category]/[subcategory]/route.ts

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)