-
-
Notifications
You must be signed in to change notification settings - Fork 541
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #173 from medusajs/add-pricing-module
feat: pricing module + category improvements
- Loading branch information
Showing
25 changed files
with
2,044 additions
and
1,773 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Metadata } from "next" | ||
import Link from "next/link" | ||
|
||
export const metadata: Metadata = { | ||
title: "404", | ||
description: "Something went wrong", | ||
} | ||
|
||
export default function NotFound() { | ||
return ( | ||
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-64px)]"> | ||
<h1 className="text-2xl-semi text-gry-900">Page not found</h1> | ||
<p className="text-small-regular text-gray-700"> | ||
The page you tried to access does not exist. | ||
</p> | ||
<Link href="/" className="mt-4 underline text-base-regular text-gray-900"> | ||
Go to frontpage | ||
</Link> | ||
</div> | ||
) | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Metadata } from "next" | ||
import Link from "next/link" | ||
|
||
export const metadata: Metadata = { | ||
title: "404", | ||
description: "Something went wrong", | ||
} | ||
|
||
export default function NotFound() { | ||
return ( | ||
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-64px)]"> | ||
<h1 className="text-2xl-semi text-gry-900">Page not found</h1> | ||
<p className="text-small-regular text-gray-700"> | ||
The page you tried to access does not exist. | ||
</p> | ||
<Link href="/" className="mt-4 underline text-base-regular text-gray-900"> | ||
Go to frontpage | ||
</Link> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import { NextRequest, NextResponse } from "next/server" | ||
import { initialize as initializeProductModule } from "@medusajs/product" | ||
import { ProductDTO } from "@medusajs/types/dist/product" | ||
import { IPricingModuleService } from "@medusajs/types" | ||
import { notFound } from "next/navigation" | ||
import { MedusaApp, Modules } from "@medusajs/modules-sdk" | ||
import { getPricesByPriceSetId } from "@lib/util/get-prices-by-price-set-id" | ||
|
||
/** | ||
* This endpoint uses the serverless Product and Pricing Modules to retrieve a category and its products by handle. | ||
* The module connects directly to you Medusa database to retrieve and manipulate data, without the need for a dedicated server. | ||
* Read more about the Product Module here: https://docs.medusajs.com/modules/products/serverless-module | ||
*/ | ||
export async function GET( | ||
request: NextRequest, | ||
{ params }: { params: Record<string, any> } | ||
) { | ||
// Initialize the Product Module | ||
const productService = await initializeProductModule() | ||
|
||
// Extract the query parameters | ||
const searchParams = Object.fromEntries(request.nextUrl.searchParams) | ||
const { page, limit } = searchParams | ||
|
||
let { handle: categoryHandle } = params | ||
|
||
const handle = categoryHandle.map((handle: string, index: number) => | ||
categoryHandle.slice(0, index + 1).join("/") | ||
) | ||
|
||
// Fetch the category by handle | ||
const product_categories = await productService | ||
.listCategories( | ||
{ | ||
handle, | ||
}, | ||
{ | ||
select: ["id", "handle", "name", "description"], | ||
relations: ["category_children"], | ||
take: handle.length, | ||
} | ||
) | ||
.catch((e) => { | ||
return notFound() | ||
}) | ||
|
||
const category = product_categories[0] | ||
|
||
if (!category) { | ||
return notFound() | ||
} | ||
|
||
// Fetch the products by category id | ||
const { | ||
rows: products, | ||
metadata: { count }, | ||
} = await getProductsByCategoryId(category.id, searchParams) | ||
|
||
// Filter out unpublished products | ||
const publishedProducts: ProductDTO[] = products.filter( | ||
(product) => product.status === "published" | ||
) | ||
|
||
// Calculate the next page | ||
const nextPage = parseInt(page) + parseInt(limit) | ||
|
||
// Return the response | ||
return NextResponse.json({ | ||
product_categories: Object.values(product_categories), | ||
response: { | ||
products: publishedProducts, | ||
count, | ||
}, | ||
nextPage: count > nextPage ? nextPage : null, | ||
}) | ||
} | ||
|
||
/** | ||
* This function uses the serverless Product and Pricing Modules to retrieve products by category id. | ||
* @param category_id The category id | ||
* @param params The query parameters | ||
* @returns The products and metadata | ||
*/ | ||
async function getProductsByCategoryId( | ||
category_id: string, | ||
params: Record<string, any> | ||
): Promise<{ rows: ProductDTO[]; metadata: Record<string, any> }> { | ||
// Extract the query parameters | ||
let { currency_code } = params | ||
|
||
currency_code = currency_code && currency_code.toUpperCase() | ||
|
||
// Initialize Remote Query with the Product and Pricing Modules | ||
const { query, modules } = await MedusaApp({ | ||
modulesConfig: { | ||
[Modules.PRODUCT]: true, | ||
[Modules.PRICING]: true, | ||
}, | ||
sharedResourcesConfig: { | ||
database: { clientUrl: process.env.POSTGRES_URL }, | ||
}, | ||
}) | ||
|
||
// Set the filters for the query | ||
const filters = { | ||
take: parseInt(params.limit) || 100, | ||
skip: parseInt(params.offset) || 0, | ||
filters: { | ||
category_id: [category_id], | ||
}, | ||
currency_code, | ||
} | ||
|
||
// Set the GraphQL query | ||
const productsQuery = `#graphql | ||
query($filters: Record, $take: Int, $skip: Int) { | ||
products(filters: $filters, take: $take, skip: $skip) { | ||
id | ||
title | ||
handle | ||
tags | ||
status | ||
collection | ||
collection_id | ||
thumbnail | ||
images { | ||
url | ||
alt_text | ||
id | ||
} | ||
options { | ||
id | ||
value | ||
title | ||
} | ||
variants { | ||
id | ||
title | ||
created_at | ||
updated_at | ||
thumbnail | ||
inventory_quantity | ||
material | ||
weight | ||
length | ||
height | ||
width | ||
options { | ||
id | ||
value | ||
title | ||
} | ||
price { | ||
price_set { | ||
id | ||
} | ||
} | ||
} | ||
} | ||
}` | ||
|
||
// Run the query | ||
const { rows, metadata } = await query(productsQuery, filters) | ||
|
||
// Calculate prices | ||
const productsWithPrices = await getPricesByPriceSetId({ | ||
products: rows, | ||
currency_code, | ||
pricingService: modules.pricingService as unknown as IPricingModuleService, | ||
}) | ||
|
||
// Return the response | ||
return { | ||
rows: productsWithPrices, | ||
metadata, | ||
} | ||
} |
Oops, something went wrong.