From 6713d76db36f8a35d3125698037daf150449e18a Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Wed, 3 Jul 2024 19:27:13 +0300 Subject: [PATCH] docs: prepare configuration (#7877) * update configuration * resolve todos + remove events guides * disable v2 docs in v1 navbar * remove v2 from v1 mobile sidebar * resolve build errors * fix build errors * fix lint errors * fix lint --- .../api-reference/components/Navbar/index.tsx | 1 + www/apps/api-reference/next.config.mjs | 1 + .../data-models/index/page.mdx | 20 +- .../basics/events-and-subscribers/page.mdx | 19 - www/apps/book/components/Navbar/index.tsx | 1 + www/apps/book/next.config.mjs | 9 +- .../page.mdx | 0 .../notification/send-notification/page.mdx | 4 +- .../_events-table/page.mdx | 0 .../api-key/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../auth/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../cart/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../currency/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../customer/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../fulfillment/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../inventory/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../order/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../payment/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../pricing/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../product/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../promotion/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../region/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../store/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../tax/{events => _events}/page.mdx | 0 .../_events-table/page.mdx | 0 .../user/{events => _events}/page.mdx | 0 www/apps/resources/app/recipes/b2b/page.mdx | 1125 +++++++------ .../app/recipes/commerce-automation/page.mdx | 1313 ++++++++------- .../app/recipes/digital-products/page.mdx | 1409 +++++++++-------- .../integrate-ecommerce-stack/page.mdx | 79 +- .../app/recipes/marketplace/page.mdx | 852 +++++----- .../resources/components/Navbar/index.tsx | 1 + www/apps/resources/generated/files-map.mjs | 232 +-- www/apps/resources/generated/sidebar.mjs | 133 -- www/apps/resources/sidebar.mjs | 152 +- www/apps/ui/.env.example | 3 +- www/apps/ui/src/config/docs.tsx | 13 +- www/packages/docs-ui/src/constants.tsx | 152 +- .../docs-ui/src/utils/get-navbar-items.ts | 13 +- 57 files changed, 2626 insertions(+), 2906 deletions(-) rename www/apps/resources/app/{events-reference => _events-reference}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/api-key/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/api-key/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/auth/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/auth/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/cart/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/cart/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/currency/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/currency/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/customer/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/customer/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/fulfillment/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/fulfillment/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/inventory/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/inventory/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/order/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/order/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/payment/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/payment/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/pricing/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/pricing/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/product/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/product/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/promotion/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/promotion/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/region/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/region/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/sales-channel/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/sales-channel/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/stock-location/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/stock-location/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/store/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/store/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/tax/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/tax/{events => _events}/page.mdx (100%) rename www/apps/resources/app/commerce-modules/user/{events => _events}/_events-table/page.mdx (100%) rename www/apps/resources/app/commerce-modules/user/{events => _events}/page.mdx (100%) diff --git a/www/apps/api-reference/components/Navbar/index.tsx b/www/apps/api-reference/components/Navbar/index.tsx index 4d23a33937..b07a28e905 100644 --- a/www/apps/api-reference/components/Navbar/index.tsx +++ b/www/apps/api-reference/components/Navbar/index.tsx @@ -41,6 +41,7 @@ const Navbar = () => { }} additionalActionsBefore={} additionalActionsAfter={} + showSearchOpener isLoading={isLoading} /> ) diff --git a/www/apps/api-reference/next.config.mjs b/www/apps/api-reference/next.config.mjs index 98453f84fe..0cd5c80730 100644 --- a/www/apps/api-reference/next.config.mjs +++ b/www/apps/api-reference/next.config.mjs @@ -12,6 +12,7 @@ const nextConfig = { destination: `${ process.env.NEXT_PUBLIC_DOCS_URL || "https://localhost:3001" }/:path*`, + basePath: false, }, ], } diff --git a/www/apps/book/app/advanced-development/data-models/index/page.mdx b/www/apps/book/app/advanced-development/data-models/index/page.mdx index 2f392f78a4..3fc7bf7c3a 100644 --- a/www/apps/book/app/advanced-development/data-models/index/page.mdx +++ b/www/apps/book/app/advanced-development/data-models/index/page.mdx @@ -53,7 +53,7 @@ import { model } from "@medusajs/utils" const MyCustom = model.define("my_custom", { id: model.id().primaryKey(), name: model.text(), - age: model.number() + age: model.number(), }).indexes([ { on: ["name", "age"], @@ -82,13 +82,13 @@ import { model } from "@medusajs/utils" const MyCustom = model.define("my_custom", { id: model.id().primaryKey(), name: model.text(), - age: model.number() + age: model.number(), }).indexes([ { on: ["name", "age"], where: { - age: 30 - } + age: 30, + }, }, ]) @@ -111,15 +111,15 @@ import { model } from "@medusajs/utils" const MyCustom = model.define("my_custom", { id: model.id().primaryKey(), name: model.text(), - age: model.number().nullable() + age: model.number().nullable(), }).indexes([ { on: ["name", "age"], where: { age: { - $ne: null - } - } + $ne: null, + }, + }, }, ]) @@ -146,11 +146,11 @@ import { model } from "@medusajs/utils" const MyCustom = model.define("my_custom", { id: model.id().primaryKey(), name: model.text(), - age: model.number() + age: model.number(), }).indexes([ { on: ["name", "age"], - unique: true + unique: true, }, ]) diff --git a/www/apps/book/app/basics/events-and-subscribers/page.mdx b/www/apps/book/app/basics/events-and-subscribers/page.mdx index dd22178390..a9c8300ed9 100644 --- a/www/apps/book/app/basics/events-and-subscribers/page.mdx +++ b/www/apps/book/app/basics/events-and-subscribers/page.mdx @@ -41,25 +41,6 @@ A subscriber file must export: The above subscriber listens to the `product.created` event. Whenever the event is emitted, it logs in the terminal `A product is created`. -{/* TODO add when we have the admin dashboard to use with V2 for easy testing. */} - -{/\* ### Test Subscriber - -To test the subscriber, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, create a product using the Medusa Admin dashboard. Afterward, you’ll see the following messages logged in the terminal: - -```bash -info: Processing product.created which has 1 subscribers -A product is created -``` - -The first message indicates that the `product.created` event was emitted, and the second one is the message logged from the subscriber. \*/} - --- ## When to Use Subscribers diff --git a/www/apps/book/components/Navbar/index.tsx b/www/apps/book/components/Navbar/index.tsx index f6550b9dba..bd19bdbfc7 100644 --- a/www/apps/book/components/Navbar/index.tsx +++ b/www/apps/book/components/Navbar/index.tsx @@ -31,6 +31,7 @@ const Navbar = () => { mobileSidebarOpen, }} isLoading={false} + showSearchOpener /> ) } diff --git a/www/apps/book/next.config.mjs b/www/apps/book/next.config.mjs index b7f82a6899..f23fd45380 100644 --- a/www/apps/book/next.config.mjs +++ b/www/apps/book/next.config.mjs @@ -108,6 +108,13 @@ const nextConfig = { }/v2/resources/:path*`, basePath: false, }, + { + source: "/v2/api/:path*", + destination: `${ + process.env.NEXT_PUBLIC_API_URL || "https://localhost:3001" + }/v2/api/:path*`, + basePath: false, + }, // TODO comment out once we have the user guide published // { // source: "/user-guide", @@ -122,7 +129,7 @@ const nextConfig = { { source: "/:path*", destination: `${ - process.env.NEXT_PUBLIC_DOCS_V1_URL || "https://localhost:3000" + process.env.NEXT_PUBLIC_API_V1_URL || "https://localhost:3001" }/:path*`, basePath: false, }, diff --git a/www/apps/resources/app/events-reference/page.mdx b/www/apps/resources/app/_events-reference/page.mdx similarity index 100% rename from www/apps/resources/app/events-reference/page.mdx rename to www/apps/resources/app/_events-reference/page.mdx diff --git a/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx b/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx index 49a1f13a2f..b35b7f3d6b 100644 --- a/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx +++ b/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx @@ -91,6 +91,4 @@ The `create` method accepts an object or an array of objects having the followin sectionTitle="Use the Create Method" /> -{/* TODO add once reference for type once generated */} - -{/* For a full list of properties accepted, refer to [this guide](/references/notification-provider-module#create). */} +For a full list of properties accepted, refer to [this guide](/references/notification-provider-module#create). diff --git a/www/apps/resources/app/commerce-modules/api-key/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/api-key/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/api-key/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/api-key/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/api-key/events/page.mdx b/www/apps/resources/app/commerce-modules/api-key/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/api-key/events/page.mdx rename to www/apps/resources/app/commerce-modules/api-key/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/auth/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/auth/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/auth/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/auth/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/auth/events/page.mdx b/www/apps/resources/app/commerce-modules/auth/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/auth/events/page.mdx rename to www/apps/resources/app/commerce-modules/auth/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/cart/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/cart/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/cart/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/cart/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/cart/events/page.mdx b/www/apps/resources/app/commerce-modules/cart/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/cart/events/page.mdx rename to www/apps/resources/app/commerce-modules/cart/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/currency/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/currency/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/currency/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/currency/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/currency/events/page.mdx b/www/apps/resources/app/commerce-modules/currency/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/currency/events/page.mdx rename to www/apps/resources/app/commerce-modules/currency/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/customer/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/customer/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/customer/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/customer/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/customer/events/page.mdx b/www/apps/resources/app/commerce-modules/customer/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/customer/events/page.mdx rename to www/apps/resources/app/commerce-modules/customer/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/fulfillment/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/fulfillment/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/fulfillment/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/fulfillment/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx b/www/apps/resources/app/commerce-modules/fulfillment/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx rename to www/apps/resources/app/commerce-modules/fulfillment/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/inventory/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/inventory/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/inventory/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/inventory/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/inventory/events/page.mdx b/www/apps/resources/app/commerce-modules/inventory/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/inventory/events/page.mdx rename to www/apps/resources/app/commerce-modules/inventory/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/order/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/order/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/order/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/order/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/order/events/page.mdx b/www/apps/resources/app/commerce-modules/order/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/order/events/page.mdx rename to www/apps/resources/app/commerce-modules/order/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/payment/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/payment/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/payment/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/payment/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/payment/events/page.mdx b/www/apps/resources/app/commerce-modules/payment/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/payment/events/page.mdx rename to www/apps/resources/app/commerce-modules/payment/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/pricing/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/pricing/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/pricing/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/pricing/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/pricing/events/page.mdx b/www/apps/resources/app/commerce-modules/pricing/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/pricing/events/page.mdx rename to www/apps/resources/app/commerce-modules/pricing/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/product/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/product/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/product/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/product/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/product/events/page.mdx b/www/apps/resources/app/commerce-modules/product/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/product/events/page.mdx rename to www/apps/resources/app/commerce-modules/product/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/promotion/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/promotion/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/promotion/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/promotion/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/promotion/events/page.mdx b/www/apps/resources/app/commerce-modules/promotion/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/promotion/events/page.mdx rename to www/apps/resources/app/commerce-modules/promotion/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/region/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/region/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/region/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/region/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/region/events/page.mdx b/www/apps/resources/app/commerce-modules/region/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/region/events/page.mdx rename to www/apps/resources/app/commerce-modules/region/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/sales-channel/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/sales-channel/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/sales-channel/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/sales-channel/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx b/www/apps/resources/app/commerce-modules/sales-channel/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx rename to www/apps/resources/app/commerce-modules/sales-channel/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/stock-location/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/stock-location/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/stock-location/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/stock-location/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/stock-location/events/page.mdx b/www/apps/resources/app/commerce-modules/stock-location/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/stock-location/events/page.mdx rename to www/apps/resources/app/commerce-modules/stock-location/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/store/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/store/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/store/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/store/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/store/events/page.mdx b/www/apps/resources/app/commerce-modules/store/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/store/events/page.mdx rename to www/apps/resources/app/commerce-modules/store/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/tax/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/tax/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/tax/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/tax/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/tax/events/page.mdx b/www/apps/resources/app/commerce-modules/tax/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/tax/events/page.mdx rename to www/apps/resources/app/commerce-modules/tax/_events/page.mdx diff --git a/www/apps/resources/app/commerce-modules/user/events/_events-table/page.mdx b/www/apps/resources/app/commerce-modules/user/_events/_events-table/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/user/events/_events-table/page.mdx rename to www/apps/resources/app/commerce-modules/user/_events/_events-table/page.mdx diff --git a/www/apps/resources/app/commerce-modules/user/events/page.mdx b/www/apps/resources/app/commerce-modules/user/_events/page.mdx similarity index 100% rename from www/apps/resources/app/commerce-modules/user/events/page.mdx rename to www/apps/resources/app/commerce-modules/user/_events/page.mdx diff --git a/www/apps/resources/app/recipes/b2b/page.mdx b/www/apps/resources/app/recipes/b2b/page.mdx index ca0037ab7d..d958d4af94 100644 --- a/www/apps/resources/app/recipes/b2b/page.mdx +++ b/www/apps/resources/app/recipes/b2b/page.mdx @@ -36,28 +36,26 @@ You can create a sales channel through the Medusa Admin or Admin REST APIs. {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#sales-channels_postsaleschannels", - title: "Option 2: Using the REST APIs", - text: "Create the sales channel using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#sales-channels_postsaleschannels", + title: "Option 2: Using the REST APIs", + text: "Create the sales channel using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -76,55 +74,51 @@ You can create a publishable API key through the Medusa Admin or the Admin REST ### Create Key -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#api-keys_postapikeys", - title: "Option 2: Using the REST APIs", - text: "Create the publishable API key using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#api-keys_postapikeys", + title: "Option 2: Using the REST APIs", + text: "Create the publishable API key using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> ### Associate Key with Sales Channel {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "#", - title: "Option 2: Using the REST APIs", - text: "Associate the publishable API key with the B2B sales channel using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "#", + title: "Option 2: Using the REST APIs", + text: "Associate the publishable API key with the B2B sales channel using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -136,55 +130,51 @@ You can create new products or add existing ones to the B2B sales channel using {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#products_postproducts", - title: "Using REST APIs", - text: "Create products using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#products_postproducts", + title: "Using REST APIs", + text: "Create products using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> ### Add Products to Sales Channel {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#sales-channels_postsaleschannelsidproductsbatchadd", - title: "Using REST APIs", - text: "Add the products to the B2B sales channel using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#sales-channels_postsaleschannelsidproductsbatchadd", + title: "Using REST APIs", + text: "Add the products to the B2B sales channel using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -202,308 +192,316 @@ Module Relationships is coming soon. -, - showLinkIcon: false, - }, - { - href: "!docs!/basics/data-models", - title: "Create Data Models", - text: "Learn how to create data models.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/basics/data-models", + title: "Create Data Models", + text: "Learn how to create data models.", + startIcon: , + showLinkIcon: false + }, +]} /> -{/_ } -showLinkIcon={false} -className="mt-1" -/> _/} +{/* } + showLinkIcon={false} + className="mt-1" +/> */} -{/\*
-In this section, you'll create a B2B module that has a `Company` data model. The `Company` data model has a relationship to the `CustomerGroup` data model of the Customer Module. +{/*
+ In this section, you'll create a B2B module that has a `Company` data model. The `Company` data model has a relationship to the `CustomerGroup` data model of the Customer Module. -Start by creating the `src/modules/b2b` directory. + Start by creating the `src/modules/b2b` directory. -Then, create the file `src/modules/b2b/models/company.ts` with the following content: + Then, create the file `src/modules/b2b/models/company.ts` with the following content: -```ts title="src/modules/b2b/models/company.ts" highlights={[["8", "", "The property will be used to create a relationship to customer groups."]]} -import { model } from "@medusajs/utils" + ```ts title="src/modules/b2b/models/company.ts" highlights={[["8", "", "The property will be used to create a relationship to customer groups."]]} + import { model } from "@medusajs/utils" -const Company = model.define("company", { - id: model.id().primaryKey(), - name: model.text(), - city: model.text(), - country_code: model.text(), - customer_group_id: model.text().nullable(), -}) + const Company = model.define("company", { + id: model.id().primaryKey(), + name: model.text(), + city: model.text(), + country_code: model.text(), + customer_group_id: model.text().nullable(), + }) -export default Company -``` + export default Company + ``` -This creates a `Company` data model with some relevant properties. Most importantly, it has a `customer_group_id` property. It'll later be used when creating the relationship to the `CustomerGroup` data model in the Customer Module. + This creates a `Company` data model with some relevant properties. Most importantly, it has a `customer_group_id` property. It'll later be used when creating the relationship to the `CustomerGroup` data model in the Customer Module. -Next, create the migration in the file `src/modules/b2b/migrations/Migration20240516081502.ts` with the following content: + Next, create the migration in the file `src/modules/b2b/migrations/Migration20240516081502.ts` with the following content: -```ts title="src/modules/b2b/migrations/Migration20240516081502.ts" -import { Migration } from "@mikro-orm/migrations" + ```ts title="src/modules/b2b/migrations/Migration20240516081502.ts" + import { Migration } from "@mikro-orm/migrations" + + export class Migration20240516081502 extends Migration { -export class Migration20240516081502 extends Migration { - async up(): Promise { - this.addSql( - 'create table if not exists "company" ("id" text not null, "name" text not null, "city" text not null, "country_code" text not null, "customer_group_id" text not null, constraint "company_pkey" primary key ("id"));' - ) + async up(): Promise { + this.addSql("create table if not exists \"company\" (\"id\" text not null, \"name\" text not null, \"city\" text not null, \"country_code\" text not null, \"customer_group_id\" text not null, constraint \"company_pkey\" primary key (\"id\"));") + } + + async down(): Promise { + this.addSql("drop table if exists \"company\" cascade;") + } + } + ``` + + You'll run the migration to reflect the data model in the database after finishing the module definition. + + Then, create the module's main service at `src/modules/b2b/service.ts` with the following content: + + ```ts title="src/modules/b2b/service.ts" + import { MedusaService } from "@medusajs/utils" + import Company from "./models/company" + + class B2bModuleService extends MedusaService({ + Company, + }){ + // TODO add custom methods } - async down(): Promise { - this.addSql('drop table if exists "company" cascade;') - } -} -``` + export default B2bModuleService + ``` -You'll run the migration to reflect the data model in the database after finishing the module definition. + This creates a `B2bModuleService` that extends the service factory, which generates data-management functionalities for the `Company` data model. -Then, create the module's main service at `src/modules/b2b/service.ts` with the following content: + Next, create the module definition at `src/modules/b2b/index.ts` with the following content: -```ts title="src/modules/b2b/service.ts" -import { MedusaService } from "@medusajs/utils" -import Company from "./models/company" + ```ts title="src/modules/b2b/index.ts" + import B2bModuleService from "./service" + import { Module } from "@medusajs/utils" -class B2bModuleService extends MedusaService({ - Company, -}) { - // TODO add custom methods -} + export default Module("b2b", { + service: B2bModuleService, + }) + ``` -export default B2bModuleService -``` + Finally, add the module to the `modules` object in `medusa-config.js`: -This creates a `B2bModuleService` that extends the service factory, which generates data-management functionalities for the `Company` data model. - -Next, create the module definition at `src/modules/b2b/index.ts` with the following content: - -```ts title="src/modules/b2b/index.ts" -import B2bModuleService from "./service" - -export default { - service: B2bModuleService, -} -``` - -Finally, add the module to the `modules` object in `medusa-config.js`: - -```js title="medusa-config.js" -module.exports = defineConfig({ - // ... - modules: { - b2bModuleService: { - resolve: "./modules/b2b", - definition: { - isQueryable: true, + ```js title="medusa-config.js" + module.exports = defineConfig({ + // ... + modules: { + b2bModuleService: { + resolve: "./modules/b2b", + definition: { + isQueryable: true, + }, }, }, - }, -}) -``` + }) + ``` -You can now run migrations with the following commands: + You can now run migrations with the following commands: -```bash npm2yarn -npx medusa migrations run -``` + ```bash npm2yarn + npx medusa migrations run + ``` -### Add Create Company API Route + ### Add Create Company API Route -To test out using the B2B Module, you'll add an API route to create a company. + To test out using the B2B Module, you'll add an API route to create a company. -Start by creating the file `src/types/b2b/index.ts` with some helper types: + Start by creating the file `src/types/b2b/index.ts` with some helper types: -```ts title="src/types/b2b/index.ts" -import { CustomerGroupDTO } from "@medusajs/types" + ```ts title="src/types/b2b/index.ts" + import { CustomerGroupDTO } from "@medusajs/types" -export type CompanyDTO = { - id: string - name: string - city: string - country_code: string - customer_group_id?: string - customer_group?: CustomerGroupDTO -} + export type CompanyDTO = { + id: string + name: string + city: string + country_code: string + customer_group_id?: string + customer_group?: CustomerGroupDTO + } -export type CreateCompanyDTO = { - name: string - city: string - country_code: string - customer_group_id?: string -} -``` + export type CreateCompanyDTO = { + name: string + city: string + country_code: string + customer_group_id?: string + } -Then, create the file `src/workflows/create-company.ts` with the following content: + ``` + + Then, create the file `src/workflows/create-company.ts` with the following content: export const workflowHighlights = [ - [ - "23", - "tryToCreateCustomerGroupStep", - "This step creates the customer group if its data is passed in the `customer_group` property.", - ], - [ - "36", - "createCustomerGroupsWorkflow", - "Use the `createCustomerGroupsWorkflow` defined by Medusa to create the customer group.", - ], - [ - "44", - "", - "Set the ID of the new customer group in the `customer_group_id` property so that it's added to the created company.", - ], + ["23", "tryToCreateCustomerGroupStep", "This step creates the customer group if its data is passed in the `customer_group` property."], + ["36", "createCustomerGroupsWorkflow", "Use the `createCustomerGroupsWorkflow` defined by Medusa to create the customer group."], + ["44", "", "Set the ID of the new customer group in the `customer_group_id` property so that it's added to the created company."], ["54", "createCompanyStep", "This step creates the company."], ] -```ts title="src/workflows/create-company.ts" highlights={workflowHighlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" -import { - StepResponse, - createStep, - createWorkflow, -} from "@medusajs/workflows-sdk" -import { createCustomerGroupsWorkflow } from "@medusajs/core-flows" -import { CreateCustomerGroupDTO } from "@medusajs/types" -import { CompanyDTO, CreateCompanyDTO } from "../types/b2b" -import B2bModuleService from "../modules/b2b/service" + ```ts title="src/workflows/create-company.ts" highlights={workflowHighlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" + import { + StepResponse, + createStep, + createWorkflow, + } from "@medusajs/workflows-sdk" + import { + createCustomerGroupsWorkflow, + } from "@medusajs/core-flows" + import { CreateCustomerGroupDTO } from "@medusajs/types" + import { CompanyDTO, CreateCompanyDTO } from "../types/b2b" + import B2bModuleService from "../modules/b2b/service" -export type CreateCompanyWorkflowInput = CreateCompanyDTO & { - customer_group?: CreateCustomerGroupDTO -} + export type CreateCompanyWorkflowInput = CreateCompanyDTO & { + customer_group?: CreateCustomerGroupDTO + } -type CreateCompanyWorkflowOutput = { - company: CompanyDTO -} + type CreateCompanyWorkflowOutput = { + company: CompanyDTO + } -type CreateCustomerGroupStepInput = CreateCompanyWorkflowInput + type CreateCustomerGroupStepInput = CreateCompanyWorkflowInput + + const tryToCreateCustomerGroupStep = createStep( + "try-to-create-customer-group-step", + async ( + { + customer_group, + ...company + }: CreateCustomerGroupStepInput, + { container }) => { + if (!customer_group) { + return new StepResponse({ company }) + } + + // create customer group + const { result } = await createCustomerGroupsWorkflow( + container + ).run({ + input: { + customersData: [customer_group], + }, + }) + + company.customer_group_id = result[0].id + + return new StepResponse({ company }) + } + ) + + export type CreateCompanyStep = { + companyData: CreateCompanyDTO + } + + const createCompanyStep = createStep( + "create-company-step", + async ( + { companyData }: CreateCompanyStep, + { container }) => { + const b2bModuleService: B2bModuleService = container + .resolve( + "b2bModuleService" + ) + + const company = await b2bModuleService.createCompany( + companyData + ) -const tryToCreateCustomerGroupStep = createStep( - "try-to-create-customer-group-step", - async ( - { customer_group, ...company }: CreateCustomerGroupStepInput, - { container } - ) => { - if (!customer_group) { return new StepResponse({ company }) } + ) - // create customer group - const { result } = await createCustomerGroupsWorkflow(container).run({ - input: { - customersData: [customer_group], - }, - }) + export const createCompanyWorkflow = createWorkflow< + CreateCompanyWorkflowInput, + CreateCompanyWorkflowOutput + >( + "create-company", + function (input) { + const { + company: companyData, + } = tryToCreateCustomerGroupStep(input) - company.customer_group_id = result[0].id + const company = createCompanyStep({ companyData }) - return new StepResponse({ company }) - } -) - -export type CreateCompanyStep = { - companyData: CreateCompanyDTO -} - -const createCompanyStep = createStep( - "create-company-step", - async ({ companyData }: CreateCompanyStep, { container }) => { - const b2bModuleService: B2bModuleService = - container.resolve("b2bModuleService") - - const company = await b2bModuleService.createCompany(companyData) - - return new StepResponse({ company }) - } -) - -export const createCompanyWorkflow = createWorkflow< - CreateCompanyWorkflowInput, - CreateCompanyWorkflowOutput ->("create-company", function (input) { - const { company: companyData } = tryToCreateCustomerGroupStep(input) - - const company = createCompanyStep({ companyData }) - - return company -}) -``` - -You create a workflow with two steps: - -1. The first one tries to create a customer group if its data is provided in the `customer_group` property and sets its value in the `customer_group_id` property. -2. The second one creates the company. - -Finally, create the file `src/api/admin/b2b/company/route.ts` with the following content: - -```ts title="src/api/admin/b2b/company/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" -import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa" -import { - CreateCompanyWorkflowInput, - createCompanyWorkflow, -} from "../../../../workflows/create-company" - -type CreateCompanyReq = CreateCompanyWorkflowInput - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createCompanyWorkflow(req.scope).run({ - input: req.body, - }) - - res.json({ - company: result.company, - }) -} -``` - -The API route uses the workflow to create the company. It passes the request body as the workflow's input. - -### Test API Route - -To test the API route, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Next, make sure you authenticate as an admin user as explained in [this Authentication guide](!api!/api/admin#authentication). - -Then, send a `POST` request to the `/admin/b2b/company` API route: - -```bash -curl -X POST 'localhost:9000/admin/b2b/company' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: Bearer {jwt_token}' \ ---data '{ - "name": "Acme", - "city": "London", - "country_code": "gb", - "customer_group": { - "name": "B2B" + return company } -}' -``` + ) + ``` -This creates a company and its associated customer group. + You create a workflow with two steps: + + 1. The first one tries to create a customer group if its data is provided in the `customer_group` property and sets its value in the `customer_group_id` property. + 2. The second one creates the company. + + Finally, create the file `src/api/admin/b2b/company/route.ts` with the following content: + + ```ts title="src/api/admin/b2b/company/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" + import type { + MedusaRequest, + MedusaResponse, + } from "@medusajs/medusa" + import { + CreateCompanyWorkflowInput, + createCompanyWorkflow, + } from "../../../../workflows/create-company" + + type CreateCompanyReq = CreateCompanyWorkflowInput + + export async function POST( + req: MedusaRequest, + res: MedusaResponse + ) { + const { result } = await createCompanyWorkflow(req.scope) + .run({ + input: req.body, + }) + + res.json({ + company: result.company, + }) + } + ``` + + The API route uses the workflow to create the company. It passes the request body as the workflow's input. + + ### Test API Route + + To test the API route, start the Medusa application: + + ```bash npm2yarn + npm run dev + ``` + + Next, make sure you authenticate as an admin user as explained in [this Authentication guide](!api!/api/admin#authentication). + + Then, send a `POST` request to the `/admin/b2b/company` API route: + + ```bash + curl -X POST 'localhost:9000/admin/b2b/company' \ + --header 'Content-Type: application/json' \ + --header 'Authorization: Bearer {jwt_token}' \ + --data '{ + "name": "Acme", + "city": "London", + "country_code": "gb", + "customer_group": { + "name": "B2B" + } + }' + ``` + + This creates a company and its associated customer group. -You can alternatively pass a `customer_group_id` to use an existing customer group. + You can alternatively pass a `customer_group_id` to use an existing customer group. @@ -519,55 +517,51 @@ You can do that through the Medusa Admin or Admin REST APIs. {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#customers_postcustomers", - title: "Option 2: Using the REST APIs", - text: "Create the customers using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#customers_postcustomers", + title: "Option 2: Using the REST APIs", + text: "Create the customers using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> ### Assign Customers to Groups {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#customer-groups_postcustomergroupsidcustomersbatch", - title: "Option 2: Using the REST APIs", - text: "Assign the customers to the B2B customer group using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#customer-groups_postcustomergroupsidcustomersbatch", + title: "Option 2: Using the REST APIs", + text: "Assign the customers to the B2B customer group using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -579,28 +573,26 @@ You can create a price list using the Medusa Admin or the Admin REST APIs. Make {/* TODO add links */} -, - showLinkIcon: false, - badge: { - variant: "blue", - children: "Guide Soon", - }, - }, - { - href: "!api!/api/admin#price-lists_postpricelists", - title: "Using REST APIs", - text: "Create the price list using the REST APIs.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false, + badge: { + variant: "blue", + children: "Guide Soon" + } + }, + { + href: "!api!/api/admin#price-lists_postpricelists", + title: "Using REST APIs", + text: "Create the price list using the REST APIs.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -608,7 +600,7 @@ You can create a price list using the Medusa Admin or the Admin REST APIs. Make To implement a more advanced B2B sales flow, add custom data models such as `Company`, `Employee`, `Admin`, and `Buyer` to your B2B module. -This provides more granular control of your B2B sales and allows you to build features like privileges, limits, and more. +This provides more granular control of your B2B sales and allows you to build features like privileges, limits, and more. -{/\*
+{/*
-For example, create the API route `src/api/store/b2b/check-customer/route.ts` with the following content: + For example, create the API route `src/api/store/b2b/check-customer/route.ts` with the following content: export const checkCustomerHighlights = [ ["19", "retrieveCustomer", "Retrieve the customer along with its groups."], - [ - "26", - "listCompanies", - "List the companies that have a customer group ID matching any of the customer's group IDs.", - ], - [ - "31", - "", - "Return whether there are any companies associated with the customer's groups.", - ], + ["26", "listCompanies", "List the companies that have a customer group ID matching any of the customer's group IDs."], + ["31", "", "Return whether there are any companies associated with the customer's groups."] ] -```ts title="src/api/store/b2b/check-customer/route.ts" highlights={checkCustomerHighlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" -import type { - AuthenticatedMedusaRequest, - MedusaResponse, -} from "@medusajs/medusa" -import { ModuleRegistrationName } from "@medusajs/utils" -import { ICustomerModuleService } from "@medusajs/types" -import B2bModuleService from "../../../../modules/b2b/service" + ```ts title="src/api/store/b2b/check-customer/route.ts" highlights={checkCustomerHighlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" + import type { + AuthenticatedMedusaRequest, + MedusaResponse, + } from "@medusajs/medusa" + import { ModuleRegistrationName } from "@medusajs/utils" + import { ICustomerModuleService } from "@medusajs/types" + import B2bModuleService from "../../../../modules/b2b/service" + + export async function GET( + req: AuthenticatedMedusaRequest, + res: MedusaResponse + ) { + const customerModuleService: ICustomerModuleService = req + .scope.resolve(ModuleRegistrationName.CUSTOMER) + const b2bModuleService: B2bModuleService = req.scope.resolve( + "b2bModuleService" + ) -export async function GET( - req: AuthenticatedMedusaRequest, - res: MedusaResponse -) { - const customerModuleService: ICustomerModuleService = req.scope.resolve( - ModuleRegistrationName.CUSTOMER - ) - const b2bModuleService: B2bModuleService = - req.scope.resolve("b2bModuleService") + const customer = await customerModuleService.retrieveCustomer( + req.auth_context.actor_id, + { + relations: ["groups"], + } + ) - const customer = await customerModuleService.retrieveCustomer( - req.auth_context.actor_id, - { - relations: ["groups"], - } - ) + const companies = await b2bModuleService.listCompanies({ + customer_group_id: customer.groups.map((group) => group.id), + }) - const companies = await b2bModuleService.listCompanies({ - customer_group_id: customer.groups.map((group) => group.id), - }) + res.json({ + is_b2b: companies.length > 0, + }) + } + ``` - res.json({ - is_b2b: companies.length > 0, - }) -} -``` + This creates a `GET` API Route at `/store/b2b/check-customer` that: -This creates a `GET` API Route at `/store/b2b/check-customer` that: + 1. Retrieves the customer along with its groups using the Customer Module's main service. + 2. Lists the companies that have a customer group ID matching any of the customer's group IDs. + 3. Return an `is_b2b` field whose value is `true` if there are any companies associated with the customer's groups. -1. Retrieves the customer along with its groups using the Customer Module's main service. -2. Lists the companies that have a customer group ID matching any of the customer's group IDs. -3. Return an `is_b2b` field whose value is `true` if there are any companies associated with the customer's groups. + Before using the API route, create the file `src/api/middlewares.ts` with the following content: -Before using the API route, create the file `src/api/middlewares.ts` with the following content: + ```ts title="src/api/middlewares.ts" + import { + MiddlewaresConfig, + authenticate, + } from "@medusajs/medusa" -```ts title="src/api/middlewares.ts" -import { MiddlewaresConfig, authenticate } from "@medusajs/medusa" + export const config: MiddlewaresConfig = { + routes: [ + { + matcher: "/store/b2b*", + middlewares: [ + authenticate("store", ["bearer", "session"]), + ], + }, + ], + } + ``` -export const config: MiddlewaresConfig = { - routes: [ - { - matcher: "/store/b2b*", - middlewares: [authenticate("store", ["bearer", "session"])], - }, - ], -} -``` + This ensures that only logged-in customers can use the API route. -This ensures that only logged-in customers can use the API route. + ### Test API Route -### Test API Route + To test out the API route: -To test out the API route: + 1. Start the Medusa application. + 2. Obtain an authentication JWT token for a new customer. Do that by sending a `POST` request to the `/auth/store/emailpass` API Route: -1. Start the Medusa application. -2. Obtain an authentication JWT token for a new customer. Do that by sending a `POST` request to the `/auth/store/emailpass` API Route: + ```bash + curl -X POST 'http://localhost:9000/auth/store/emailpass' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "email": "test@medusajs.com", + "password": "supersecret" + }' + ``` + + 3. Send a `POST` request to the `/store/customers` API route that registers the customer. Make sure to pass the authentication JWT token from the previous token in the header: -```bash -curl -X POST 'http://localhost:9000/auth/store/emailpass' \ --H 'Content-Type: application/json' \ ---data-raw '{ - "email": "test@medusajs.com", - "password": "supersecret" -}' -``` + ```bash + curl -X POST 'http://localhost:9000/store/customers' \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer {jwt_token}' \ + --data-raw '{ + "email": "test@medusajs.com", + "password": "supersecret" + }' + ``` -3. Send a `POST` request to the `/store/customers` API route that registers the customer. Make sure to pass the authentication JWT token from the previous token in the header: + 4. Add the customer to the B2B group as explained in a [previous section](#add-b2b-customers). + 5. Send a `GET` request to the `/store/b2b/check-customer` API route you created in this section: -```bash -curl -X POST 'http://localhost:9000/store/customers' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {jwt_token}' \ ---data-raw '{ - "email": "test@medusajs.com", - "password": "supersecret" -}' -``` + ```bash + curl 'http://localhost:9000/store/b2b/check-customer' \ + --header 'Authorization: Bearer {jwt_token}' + ``` -4. Add the customer to the B2B group as explained in a [previous section](#add-b2b-customers). -5. Send a `GET` request to the `/store/b2b/check-customer` API route you created in this section: + You'll receive a JSON response as the following: -```bash -curl 'http://localhost:9000/store/b2b/check-customer' \ ---header 'Authorization: Bearer {jwt_token}' -``` - -You'll receive a JSON response as the following: - -```json -{ - "is_b2b": true -} -``` + ```json + { + "is_b2b": true + } + ```
*/} @@ -765,31 +754,29 @@ Based on your use case, you may need to customize the Medusa Admin to add new wi The Medusa Admin plugin can be extended to add widgets, new pages, and setting pages. -, - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/admin/ui-routes", - title: "Create Admin UI Routes", - text: "Learn how to add new pages to your Medusa Admin.", - startIcon: , - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/admin/setting-pages", - title: "Create Admin Setting Page", - text: "Learn how to add new page to the Medusa Admin settings.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/admin/ui-routes", + title: "Create Admin UI Routes", + text: "Learn how to add new pages to your Medusa Admin.", + startIcon: , + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/admin/setting-pages", + title: "Create Admin Setting Page", + text: "Learn how to add new page to the Medusa Admin settings.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -799,24 +786,22 @@ Medusa provides a Next.js storefront to use with your application. You can eithe Use the publishable API key you associated with your B2B sales channel in the storefront to ensure only B2B products are retrieved. -, - showLinkIcon: false, - }, - { - href: "/storefront-development", - title: "Storefront Development", - text: "Find guides for your storefront development.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "/storefront-development", + title: "Storefront Development", + text: "Find guides for your storefront development.", + startIcon: , + showLinkIcon: false + }, +]} /> } showLinkIcon={false} className="mt-1" -/> +/> \ No newline at end of file diff --git a/www/apps/resources/app/recipes/commerce-automation/page.mdx b/www/apps/resources/app/recipes/commerce-automation/page.mdx index 0e1fc58fe0..432f3f8061 100644 --- a/www/apps/resources/app/recipes/commerce-automation/page.mdx +++ b/www/apps/resources/app/recipes/commerce-automation/page.mdx @@ -36,323 +36,271 @@ To implement sending restock notifications, you can: -The `inventory-item.updated` event is currently not emitted. +The `inventory-item.updated` event is currently not emitted. -, - showLinkIcon: false, - }, - { - href: "!docs!/basics/data-models", - title: "Create a Data Model", - text: "Learn how to create a data model.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/basics/data-models", + title: "Create a Data Model", + text: "Learn how to create a data model.", + startIcon: , + showLinkIcon: false + }, +]} /> -, - showLinkIcon: false, - }, - { - href: "!docs!/basics/events-and-subscribers", - title: "Create a Subscriber", - text: "Learn how to create a subscriber in Medusa.", - startIcon: , - showLinkIcon: false, - }, - ]} - className="mt-1" -/> +, + showLinkIcon: false + }, + { + href: "!docs!/basics/events-and-subscribers", + title: "Create a Subscriber", + text: "Learn how to create a subscriber in Medusa.", + startIcon: , + showLinkIcon: false + }, +]} className="mt-1" />
-In this example, you'll create a Restock Notification Module with the features explained above. + In this example, you'll create a Restock Notification Module with the features explained above. -### Create Restock Notification Module + ### Create Restock Notification Module -Start by creating the `src/modules/restock-notification` directory. + Start by creating the `src/modules/restock-notification` directory. -Then, create the file `src/modules/restock-notification/models/restock-notification.ts` with the following content: + Then, create the file `src/modules/restock-notification/models/restock-notification.ts` with the following content: export const restockModelHighlights = [ - [ - "5", - "email", - "The email of the customer to send the notification to when the item is restocked.", - ], + ["5", "email", "The email of the customer to send the notification to when the item is restocked."], ["6", "variant_id", "The ID of the variant the customer is subscribed to."], - [ - "7", - "sales_channel_id", - "The ID of the sales channel the customer is viewing the product variant from.", - ], + ["7", "sales_channel_id", "The ID of the sales channel the customer is viewing the product variant from."] ] -```ts title="src/modules/restock-notification/models/restock-notification.ts" highlights={restockModelHighlights} -import { model } from "@medusajs/utils" + ```ts title="src/modules/restock-notification/models/restock-notification.ts" highlights={restockModelHighlights} + import { model } from "@medusajs/utils" -const RestockNotification = model.define("restock_notification", { - id: model.id().primaryKey(), - email: model.text(), - variant_id: model.text(), - sales_channel_id: model.text(), -}) + const RestockNotification = model.define("restock_notification", { + id: model.id().primaryKey(), + email: model.text(), + variant_id: model.text(), + sales_channel_id: model.text(), + }) -export default RestockNotification -``` + export default RestockNotification + ``` -This creates a `RestockNotification` data model with the following properties: + This creates a `RestockNotification` data model with the following properties: -- `id`: An automatically generated ID. -- `email`: The email of the customer to send the notification to when the item is restocked. -- `variant_id`: The ID of the variant the customer is subscribed to. This will later be used to form a relationship with the `ProductVariant` data model of the Product Module. -- `sales_channel_id`: The ID of the sales channel the customer is viewing the product variant from. This will later be used to form a relationship with the `SalesChannel` data model of the Sales Channel Module. + - `id`: An automatically generated ID. + - `email`: The email of the customer to send the notification to when the item is restocked. + - `variant_id`: The ID of the variant the customer is subscribed to. This will later be used to form a relationship with the `ProductVariant` data model of the Product Module. + - `sales_channel_id`: The ID of the sales channel the customer is viewing the product variant from. This will later be used to form a relationship with the `SalesChannel` data model of the Sales Channel Module. -Since a variant's inventory is managed based on the locations of each sales channel, you have to specify which sales channel to check stock quantity in. + Since a variant's inventory is managed based on the locations of each sales channel, you have to specify which sales channel to check stock quantity in. -Next, create the file `src/modules/restock-notification/migrations/Migration20240516140616.ts` with the following content: + Next, create the file `src/modules/restock-notification/migrations/Migration20240516140616.ts` with the following content: -```ts title="src/modules/restock-notification/migrations/Migration20240516140616.ts" -import { Migration } from "@mikro-orm/migrations" + ```ts title="src/modules/restock-notification/migrations/Migration20240516140616.ts" + import { Migration } from "@mikro-orm/migrations" + + export class Migration20240516140616 extends Migration { -export class Migration20240516140616 extends Migration { - async up(): Promise { - this.addSql( - 'create table if not exists "restock_notification" ("id" text not null, "email" text not null, "variant_id" text not null, "sales_channel_id" text not null, constraint "restock_notification_pkey" primary key ("id"));' - ) + async up(): Promise { + this.addSql("create table if not exists \"restock_notification\" (\"id\" text not null, \"email\" text not null, \"variant_id\" text not null, \"sales_channel_id\" text not null, constraint \"restock_notification_pkey\" primary key (\"id\"));") + } + + async down(): Promise { + this.addSql("drop table if exists \"restock_notification\" cascade;") + } + + } + ``` + + You'll run the migration to reflect the changes on the database after finishing the module's definition. + + Then, create the module's main service at `src/modules/restock-notification/service.ts` with the following content: + + ```ts title="src/modules/restock-notification/service.ts" + import { MedusaService } from "@medusajs/utils" + import RestockNotification from "./models/restock-notification" + + class RestockNotificationModuleService extends MedusaService({ + RestockNotification, + }){ + // TODO add custom methods } - async down(): Promise { - this.addSql('drop table if exists "restock_notification" cascade;') - } -} -``` + export default RestockNotificationModuleService + ``` -You'll run the migration to reflect the changes on the database after finishing the module's definition. + The module's main service extends the service factory which generates basic management features for the `RestockNotification` data model. -Then, create the module's main service at `src/modules/restock-notification/service.ts` with the following content: + Next, create the module's definition file `src/modules/restock-notification/index.ts` with the following content: -```ts title="src/modules/restock-notification/service.ts" -import { MedusaService } from "@medusajs/utils" -import RestockNotification from "./models/restock-notification" + ```ts title="src/modules/restock-notification/index.ts" + import RestockNotificationModuleService from "./service" + import { Module } from "@medusajs/utils" -class RestockNotificationModuleService extends MedusaService({ - RestockNotification, -}) { - // TODO add custom methods -} + export default Module("restock-notification", { + service: RestockNotificationModuleService, + }) + ``` -export default RestockNotificationModuleService -``` + Finally, add the module to the `modules` object in `medusa-config.js`: -The module's main service extends the service factory which generates basic management features for the `RestockNotification` data model. - -Next, create the module's definition file `src/modules/restock-notification/index.ts` with the following content: - -```ts title="src/modules/restock-notification/index.ts" -import RestockNotificationModuleService from "./service" - -export default { - service: RestockNotificationModuleService, -} -``` - -Finally, add the module to the `modules` object in `medusa-config.js`: - -```js title="medusa-config.js" -module.exports = defineConfig({ - // ... - modules: { - restockNotificationModuleService: { - resolve: "./modules/restock-notification", - definition: { - isQueryable: true, + ```js title="medusa-config.js" + module.exports = defineConfig({ + // ... + modules: { + "restockNotificationModuleService": { + resolve: "./modules/restock-notification", + definition: { + isQueryable: true, + }, }, }, - }, -}) -``` - -You can now run the module's migrations with the following command: - -```bash npm2yarn -npx medusa migrations run -``` - -### Create Restock Notification API Route - -Create the file `src/api/store/restock-notification/route.ts` with the following content: - -```ts title="src/api/store/restock-notification/route.ts" collapsibleLines="1-13" expandButtonLabel="Show Imports" -import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa" -import RestockNotificationModuleService from "../../../modules/restock-notification/service" - -type RestockNotificationReq = { - email: string - variant_id: string - sales_channel_id: string -} - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - const restockNotificationModuleService: RestockNotificationModuleService = - req.scope.resolve("restockNotificationModuleService") - - await restockNotificationModuleService.createRestockNotifications(req.body) - - res.json({ - success: true, }) -} -``` + ``` -This creates a `POST` API route at `/store/restock-notification`. It accepts the `email`, `variant_id`, and `sales_channel_id` request body parameters and creates a restock notification. + You can now run the module's migrations with the following command: -### Create Inventory Item Updated Subscriber + ```bash npm2yarn + npx medusa migrations run + ``` -To handle the sending of the restock notifications, create a subscriber that listens to the `inventory-item.updated` event, then sends a notification using the Notification Module to subscribed emails. + ### Create Restock Notification API Route + + Create the file `src/api/store/restock-notification/route.ts` with the following content: + + ```ts title="src/api/store/restock-notification/route.ts" collapsibleLines="1-13" expandButtonLabel="Show Imports" + import type { + MedusaRequest, + MedusaResponse, + } from "@medusajs/medusa" + import RestockNotificationModuleService + from "../../../modules/restock-notification/service" + + type RestockNotificationReq = { + email: string + variant_id: string + sales_channel_id: string + } + + export async function POST( + req: MedusaRequest, + res: MedusaResponse + ) { + const restockNotificationModuleService: + RestockNotificationModuleService = req.scope.resolve( + "restockNotificationModuleService" + ) + + await restockNotificationModuleService.createRestockNotifications( + req.body + ) + + res.json({ + success: true, + }) + } + ``` + + This creates a `POST` API route at `/store/restock-notification`. It accepts the `email`, `variant_id`, and `sales_channel_id` request body parameters and creates a restock notification. + + ### Create Inventory Item Updated Subscriber + + To handle the sending of the restock notifications, create a subscriber that listens to the `inventory-item.updated` event, then sends a notification using the Notification Module to subscribed emails. -The `inventory-item.updated` event is currently not emitted. + The `inventory-item.updated` event is currently not emitted. -{/* To handle the sending of the restock notifications, create the file `src/subscribers/inventory-item-update.ts` with the following content: */} + {/* To handle the sending of the restock notifications, create the file `src/subscribers/inventory-item-update.ts` with the following content: */} export const subscriberHighlights = [ - [ - "48", - "inventoryVariantLinkService", - "Retrieve an instance of the link service for the product-variant-inventory-item link module.", - ], - [ - "55", - "inventoryVariantItems", - "Retrieve the variants linked to the updated inventory item.", - ], - [ - "68", - "restockQuery", - "Assemble the query to retrieve the restock notifications with their associated variants.", - ], - [ - "81", - "restockNotifications", - "Retrieve the restock notifications using the query.", - ], - [ - "84", - "salesChannelLocationService", - "Retrieve an instance of the link service for the sales-channel-stock-location link module.", - ], - [ - "93", - "salesChannelLocations", - "Retrieve the stock locations linked to the restock notification's sales channel.", - ], - [ - "107", - "availableQuantity", - "Retrieve the available quantity of the variant in the retrieved stock locations.", - ], - [ - "116", - "continue", - "Only send the notification if the available quantity is greater than `0`", - ], - [ - "119", - "createNotifications", - "Send the notification to the customer using the Notification Module.", - ], - [ - "122", - '"test_template"', - "Replace with the actual template used for sending the email.", - ], - [ - "123", - "data", - "The data to send along to the third-party service sending the notification.", - ], - [ - "131", - "deleteRestockNotifications", - "Delete the restock notification to not send the notification again.", - ], + ["48", "inventoryVariantLinkService", "Retrieve an instance of the link service for the product-variant-inventory-item link module."], + ["55", "inventoryVariantItems", "Retrieve the variants linked to the updated inventory item."], + ["68", "restockQuery", "Assemble the query to retrieve the restock notifications with their associated variants."], + ["81", "restockNotifications", "Retrieve the restock notifications using the query."], + ["84", "salesChannelLocationService", "Retrieve an instance of the link service for the sales-channel-stock-location link module."], + ["93", "salesChannelLocations", "Retrieve the stock locations linked to the restock notification's sales channel."], + ["107", "availableQuantity", "Retrieve the available quantity of the variant in the retrieved stock locations."], + ["116", "continue", "Only send the notification if the available quantity is greater than `0`"], + ["119", "createNotifications", "Send the notification to the customer using the Notification Module."], + ["122", '"test_template"', "Replace with the actual template used for sending the email."], + ["123", "data", "The data to send along to the third-party service sending the notification."], + ["131", "deleteRestockNotifications", "Delete the restock notification to not send the notification again."] ] -{/\* ```ts title="src/subscribers/inventory-item-update.ts" highlights={subscriberHighlights} collapsibleLines="1-20" expandButtonLabel="Show Imports" -import type { -SubscriberArgs, -SubscriberConfig, -} from "@medusajs/medusa" -import { -IInventoryService, -INotificationModuleService, -RemoteQueryFunction, -} from "@medusajs/types" -import { -ContainerRegistrationKeys, -Modules, -remoteQueryObjectFromString, -} from "@medusajs/utils" -import { -RemoteLink, -} from "@medusajs/modules-sdk" -import RestockNotificationModuleService -from "../modules/restock-notification/service" + {/* ```ts title="src/subscribers/inventory-item-update.ts" highlights={subscriberHighlights} collapsibleLines="1-20" expandButtonLabel="Show Imports" + import type { + SubscriberArgs, + SubscriberConfig, + } from "@medusajs/medusa" + import { + IInventoryService, + INotificationModuleService, + RemoteQueryFunction, + } from "@medusajs/types" + import { + ContainerRegistrationKeys, + Modules, + remoteQueryObjectFromString, + } from "@medusajs/utils" + import { + RemoteLink, + } from "@medusajs/modules-sdk" + import RestockNotificationModuleService + from "../modules/restock-notification/service" -// subscriber function -export default async function inventoryItemUpdateHandler({ -data, -container, -}: SubscriberArgs<{ id: string }>) { -const remoteQuery: RemoteQueryFunction = container.resolve( -ContainerRegistrationKeys.REMOTE_QUERY -) -const remoteLink: RemoteLink = container.resolve( -ContainerRegistrationKeys.REMOTE_LINK -) -const restockNotificationModuleService: -RestockNotificationModuleService = container.resolve( -"restockNotificationModuleService" -) -const inventoryModuleService: IInventoryService = -container.resolve(Modules.INVENTORY) -const notificationModuleService: INotificationModuleService = -container.resolve( -Modules.NOTIFICATION -) + // subscriber function + export default async function inventoryItemUpdateHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const remoteQuery: RemoteQueryFunction = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + const remoteLink: RemoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + const restockNotificationModuleService: + RestockNotificationModuleService = container.resolve( + "restockNotificationModuleService" + ) + const inventoryModuleService: IInventoryService = + container.resolve(Modules.INVENTORY) + const notificationModuleService: INotificationModuleService = + container.resolve( + Modules.NOTIFICATION + ) const inventoryItemId = "data" in data ? data.data.id : data.id const inventoryVariantLinkService = remoteLink.getLinkModule( - Modules.PRODUCT, - "variant_id", - Modules.INVENTORY, + Modules.PRODUCT, + "variant_id", + Modules.INVENTORY, "inventory_item_id" ) - const inventoryVariantItems = + const inventoryVariantItems = await inventoryVariantLinkService.list({ inventory_item_id: [inventoryItemId], }) as { @@ -378,7 +326,7 @@ Modules.NOTIFICATION }, }) - const restockNotifications = + const restockNotifications = await remoteQuery(restockQuery) const salesChannelLocationService = remoteLink.getLinkModule( @@ -389,7 +337,7 @@ Modules.NOTIFICATION ) for (const restockNotification of restockNotifications) { - const salesChannelLocations = + const salesChannelLocations = await salesChannelLocationService.list({ sales_channel_id: [ restockNotification.sales_channel_id, @@ -407,7 +355,7 @@ Modules.NOTIFICATION .retrieveAvailableQuantity( inventoryItemId, salesChannelLocations.map( - (salesChannelLocation) => + (salesChannelLocation) => salesChannelLocation.stock_location_id ) ) @@ -415,7 +363,7 @@ Modules.NOTIFICATION if (availableQuantity === 0) { continue } - + notificationModuleService.createNotifications({ to: restockNotification.email, channel: "email", @@ -431,24 +379,22 @@ Modules.NOTIFICATION await restockNotificationModuleService .deleteRestockNotifications(restockNotification.id) } + } -} + // subscriber config + export const config: SubscriberConfig = { + event: "inventory-item.updated", + } + ``` -// subscriber config -export const config: SubscriberConfig = { -event: "inventory-item.updated", -} + This adds a subscriber to the `inventory-item.updated` event. In the subscriber handler function, you: -```` - -This adds a subscriber to the `inventory-item.updated` event. In the subscriber handler function, you: - -- Retrieve an instance of the link service for the product-variant-inventory-item link module. -- Retrieve the variants linked to the updated inventory item. -- Retrieve the restock notifications of those variants. -- For each restock notification, you: - - Retrieve its quantity based on the stock location associated with the restock notification's sales channel. - - If the quantity is greater than `0`, you send a notification using the Notification Module and delete the restock notification. */} + - Retrieve an instance of the link service for the product-variant-inventory-item link module. + - Retrieve the variants linked to the updated inventory item. + - Retrieve the restock notifications of those variants. + - For each restock notification, you: + - Retrieve its quantity based on the stock location associated with the restock notification's sales channel. + - If the quantity is greater than `0`, you send a notification using the Notification Module and delete the restock notification. */}
@@ -469,20 +415,20 @@ The [Events reference](../../events-reference/page.mdx) shows an extensive list Medusa also provides Notification Module Providers that integrate with third-party services, such as SendGrid. , - showLinkIcon: false -}, -{ - href: "!docs!/basics/events-and-subscribers", - title: "Create Subscriber", - text: "Learn how to create a subscriber to handle events.", - startIcon: , - showLinkIcon: false -}, + { + href: "/architectural-modules/notification", + title: "Notification Module", + text: "Learn about the Notification Module.", + startIcon: , + showLinkIcon: false + }, + { + href: "!docs!/basics/events-and-subscribers", + title: "Create Subscriber", + text: "Learn how to create a subscriber to handle events.", + startIcon: , + showLinkIcon: false + }, ]} /> --- @@ -497,212 +443,217 @@ To implement that: - Create a scheduled job that executes the workflow automatically at the specified time pattern. } -showLinkIcon={false} + href="!docs!/basics/scheduled-jobs" + title="Create a Scheduled Job" + text="Learn how to create a scheduled job in Medusa." + startIcon={} + showLinkIcon={false} />
-For example, create the file `src/workflows/sync-products.ts` with the following content: + For example, create the file `src/workflows/sync-products.ts` with the following content: export const syncProductsWorkflowHighlight = [ -["20", "retrieveStoreStep", "A step that retrieves the store by its ID."], -["36", "retrieveProductsToUpdateStep", "A step that retrieves the products to update based on a last update date."], -["56", "syncProductsStep", "A step to sync the product with a third-party service."], -["59", "productSyncModuleService", "Assuming this is a custom module's main service that provides connection to the third-party service."], -["63", "productsBeforeSync", "Retrieve old product data from third-party service for compensation function."], -["68", "sync", "Sync the product data in the third-party service."], -["72", "", "Pass products data before sync to compensation function."], -["75", "", "A compensation function to revert the sync when an error occurs."], -["81", "sync", "Revert the product's data in the third-party service to its old data before the synchronization."], -["90", "updateStoreLastSyncStep", "A step to update the `last_sync_data` of the store."], -["96", "prevLastSyncDate", "Retrieve the previous value of `last_sync_date` to pass it to compensation function."], -["98", "update", "Update the `last_sync_date` of the store."], -["106", "", "Pass previous last sync date to compensation function."], -["109", "", "A compensation function to revert the update of `last_sync_data` if an error occurs."], -["125", "syncProductsWorkflow", "Define the workflow that uses the above steps."] + ["20", "retrieveStoreStep", "A step that retrieves the store by its ID."], + ["36", "retrieveProductsToUpdateStep", "A step that retrieves the products to update based on a last update date."], + ["56", "syncProductsStep", "A step to sync the product with a third-party service."], + ["59", "productSyncModuleService", "Assuming this is a custom module's main service that provides connection to the third-party service."], + ["63", "productsBeforeSync", "Retrieve old product data from third-party service for compensation function."], + ["68", "sync", "Sync the product data in the third-party service."], + ["72", "", "Pass products data before sync to compensation function."], + ["75", "", "A compensation function to revert the sync when an error occurs."], + ["81", "sync", "Revert the product's data in the third-party service to its old data before the synchronization."], + ["90", "updateStoreLastSyncStep", "A step to update the `last_sync_data` of the store."], + ["96", "prevLastSyncDate", "Retrieve the previous value of `last_sync_date` to pass it to compensation function."], + ["98", "update", "Update the `last_sync_date` of the store."], + ["106", "", "Pass previous last sync date to compensation function."], + ["109", "", "A compensation function to revert the update of `last_sync_data` if an error occurs."], + ["125", "syncProductsWorkflow", "Define the workflow that uses the above steps."] ] -```ts title="src/workflows/sync-products.ts" highlights={syncProductsWorkflowHighlight} collapsibleLines="1-16" expandButtonLabel="Show Imports" -import { - ModuleRegistrationName -} from "@medusajs/modules-sdk" -import { - IProductModuleService, - IStoreModuleService, - ProductDTO, - StoreDTO -} from "@medusajs/types" -import { - StepResponse, - createStep, - createWorkflow -} from "@medusajs/workflows-sdk" + ```ts title="src/workflows/sync-products.ts" highlights={syncProductsWorkflowHighlight} collapsibleLines="1-16" expandButtonLabel="Show Imports" + import { + ModuleRegistrationName + } from "@medusajs/modules-sdk" + import { + IProductModuleService, + IStoreModuleService, + ProductDTO, + StoreDTO + } from "@medusajs/types" + import { + StepResponse, + createStep, + createWorkflow + } from "@medusajs/workflows-sdk" -type RetrieveStoreStepInput = { - id: string -} - -const retrieveStoreStep = createStep( - "retrieve-store-step", - async ({ id }: RetrieveStoreStepInput, { container }) => { - const storeModuleService: IStoreModuleService = - container.resolve(ModuleRegistrationName.STORE) - - const store = await storeModuleService.retrieveStore(id) - - return new StepResponse({ store }) + type RetrieveStoreStepInput = { + id: string } -) -type RetrieveProductsToUpdateStepInput = { - last_sync_date?: string -} + const retrieveStoreStep = createStep( + "retrieve-store-step", + async ({ id }: RetrieveStoreStepInput, { container }) => { + const storeModuleService: IStoreModuleService = + container.resolve(ModuleRegistrationName.STORE) -const retrieveProductsToUpdateStep = createStep( - "retrieve-products-to-update-step", - async ({ last_sync_date }: RetrieveProductsToUpdateStepInput, { container }) => { - const productModuleService: IProductModuleService = - container.resolve(ModuleRegistrationName.PRODUCT) + const store = await storeModuleService.retrieveStore(id) - const products = await productModuleService.listProducts({ - updated_at: { - $gt: last_sync_date, - }, - }) - - return new StepResponse({ products }) - } -) - -type SyncProductsStepInput = { - products: ProductDTO[] -} - -const syncProductsStep = createStep( - "sync-products-step", - async ({ products }: SyncProductsStepInput, { container }) => { - const productSyncModuleService = container.resolve( - "productSyncModuleService" - ) - - const productsBeforeSync = await productSyncModuleService.listProductSyncs({ - id: products.map((product) => product.id), - }) - - for (const product of products) { - await productSyncModuleService.sync(product) + return new StepResponse({ store }) } + ) - return new StepResponse({}, { - products: productsBeforeSync, - }) - }, - async ({ products }, { container }) => { - const productSyncModuleService = container.resolve( - "productSyncModuleService" - ) + type RetrieveProductsToUpdateStepInput = { + last_sync_date?: string + } - for (const product of products) { - await productSyncModuleService.sync(product) + const retrieveProductsToUpdateStep = createStep( + "retrieve-products-to-update-step", + async ({ last_sync_date }: RetrieveProductsToUpdateStepInput, { container }) => { + const productModuleService: IProductModuleService = + container.resolve(ModuleRegistrationName.PRODUCT) + + const products = await productModuleService.listProducts({ + updated_at: { + $gt: last_sync_date, + }, + }) + + return new StepResponse({ products }) } + ) + + type SyncProductsStepInput = { + products: ProductDTO[] } -) -type UpdateStoreLastSyncStepInput = { - store: StoreDTO -} + const syncProductsStep = createStep( + "sync-products-step", + async ({ products }: SyncProductsStepInput, { container }) => { + const productSyncModuleService = container.resolve( + "productSyncModuleService" + ) -const updateStoreLastSyncStep = createStep( - "update-store-last-sync-step", - async ({ store }: UpdateStoreLastSyncStepInput, { container }) => { - const storeModuleService: IStoreModuleService = - container.resolve(ModuleRegistrationName.STORE) + const productsBeforeSync = await productSyncModuleService.listProductSyncs({ + id: products.map((product) => product.id), + }) - const prevLastSyncDate = store.metadata.last_sync_date + for (const product of products) { + await productSyncModuleService.sync(product) + } - await storeModuleService.updateStores(store.id, { - metadata: { - last_sync_date: (new Date()).toString(), - }, - }) + return new StepResponse({}, { + products: productsBeforeSync, + }) + }, + async ({ products }, { container }) => { + const productSyncModuleService = container.resolve( + "productSyncModuleService" + ) - return new StepResponse({}, { - id: store.id, - last_sync_date: prevLastSyncDate, - }) - }, - async ({ id, last_sync_date }, { container }) => { - const storeModuleService: IStoreModuleService = - container.resolve(ModuleRegistrationName.STORE) + for (const product of products) { + await productSyncModuleService.sync(product) + } + } + ) - await storeModuleService.updateStores(id, { - metadata: { - last_sync_date, - }, - }) + type UpdateStoreLastSyncStepInput = { + store: StoreDTO } -) -type SyncProductsWorkflowInput = { - store_id: string -} + const updateStoreLastSyncStep = createStep( + "update-store-last-sync-step", + async ({ store }: UpdateStoreLastSyncStepInput, { container }) => { + const storeModuleService: IStoreModuleService = + container.resolve(ModuleRegistrationName.STORE) -export const syncProductsWorkflow = createWorkflow< - SyncProductsWorkflowInput, {} - >( - "sync-products-workflow", - function ({ store_id }: SyncProductsWorkflowInput) { - const { store } = retrieveStoreStep({ - id: store_id, + const prevLastSyncDate = store.metadata.last_sync_date + + await storeModuleService.updateStores(store.id, { + metadata: { + last_sync_date: (new Date()).toString(), + }, }) - const { products } = retrieveProductsToUpdateStep({ - last_sync_date: store.metadata.last_sync_date, + return new StepResponse({}, { + id: store.id, + last_sync_date: prevLastSyncDate, }) + }, + async ({ id, last_sync_date }, { container }) => { + const storeModuleService: IStoreModuleService = + container.resolve(ModuleRegistrationName.STORE) - syncProductsStep({ - products, - }) - - updateStoreLastSyncStep({ - store, + await storeModuleService.updateStores(id, { + metadata: { + last_sync_date, + }, }) } ) -```` -This creates a workflow with the following steps: + type SyncProductsWorkflowInput = { + store_id: string + } -1. Retrieve the store by its ID. -2. Retrieve products to update based on the last date and time the products were synced. The last sync date is retrieved from the store's `metadata` property. -3. Sync the retrieved products with a third-party service. It's assumed that the connection to the third-party service is implemented within a custom module's main service. -4. Update the last sync date of the store to the current date. + export const syncProductsWorkflow = createWorkflow< + SyncProductsWorkflowInput, {} + >( + "sync-products-workflow", + function ({ store_id }: SyncProductsWorkflowInput) { + const { store } = retrieveStoreStep({ + id: store_id, + }) -Then, create a scheduled job at `src/jobs/sync-products.ts` that executes the workflow at the specified interval: + const { products } = retrieveProductsToUpdateStep({ + last_sync_date: store.metadata.last_sync_date, + }) -```ts -import { MedusaContainer } from "@medusajs/types" -import { syncProductsWorkflow } from "../workflows/sync-products" + syncProductsStep({ + products, + }) -export default async function syncProductsJob(container: MedusaContainer) { - await syncProductsWorkflow(container).run({ - input: { - name: "John", - }, - }) -} + updateStoreLastSyncStep({ + store, + }) + } + ) + ``` -export const config = { - name: "sync-products", - // execute every minute - schedule: "0 0 * * *", - numberOfExecutions: 3, -} -``` + This creates a workflow with the following steps: + + 1. Retrieve the store by its ID. + 2. Retrieve products to update based on the last date and time the products were synced. The last sync date is retrieved from the store's `metadata` property. + 3. Sync the retrieved products with a third-party service. It's assumed that the connection to the third-party service is implemented within a custom module's main service. + 4. Update the last sync date of the store to the current date. + + Then, create a scheduled job at `src/jobs/sync-products.ts` that executes the workflow at the specified interval: + + ```ts + import { MedusaContainer } from "@medusajs/types" + import { + syncProductsWorkflow, + } from "../workflows/sync-products" + + export default async function syncProductsJob( + container: MedusaContainer + ) { + await syncProductsWorkflow(container) + .run({ + input: { + name: "John", + }, + }) + } + + export const config = { + name: "sync-products", + // execute every minute + schedule: "0 0 * * *", + numberOfExecutions: 3, + } + ```
@@ -728,22 +679,22 @@ The `order.placed` event is currently not emitted. showLinkIcon={false} /> -{/_ , -showLinkIcon: false -}, -{ -href: "/events-reference", -title: "Events Reference", -text: "Check out triggered events by each commerce module.", -startIcon: , -showLinkIcon: false -}, -]} /> _/} +{/* , + showLinkIcon: false + }, + { + href: "/events-reference", + title: "Events Reference", + text: "Check out triggered events by each commerce module.", + startIcon: , + showLinkIcon: false + }, +]} /> */} --- @@ -757,24 +708,22 @@ Medusa's commerce features are geared towards automating RMA flows and ensuring - Merchants can make order changes and request the customer's approval for them. The customer can also send any additional payment if necessary. - Every order-related action triggers an event, which you can listen to with a subscriber. This allows you to handle order events to automate actions. -, - showLinkIcon: false, - }, - { - href: "!docs!/basics/events-and-subscribers", - title: "Create a Subscriber", - text: "Learn how to create a subscriber in Medusa.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/basics/events-and-subscribers", + title: "Create a Subscriber", + text: "Learn how to create a subscriber in Medusa.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -798,110 +747,112 @@ The `order.placed` event is currently not emitted. -, - showLinkIcon: false, - }, - { - href: "/commerce-modules/pricing", - title: "Pricing Module", - text: "Learn about the Pricing Module and its features.", - startIcon: , - showLinkIcon: false, - }, - { - href: "!docs!/basics/events-and-subscribers", - title: "Create a Subscriber", - text: "Learn how to create a subscriber in Medusa.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "/commerce-modules/pricing", + title: "Pricing Module", + text: "Learn about the Pricing Module and its features.", + startIcon: , + showLinkIcon: false + }, + { + href: "!docs!/basics/events-and-subscribers", + title: "Create a Subscriber", + text: "Learn how to create a subscriber in Medusa.", + startIcon: , + showLinkIcon: false + }, +]} />
-Here’s an example of a subscriber that listens to the `order.placed` event and checks whether the customer should be added to the VIP customer group based on their number of orders: + Here’s an example of a subscriber that listens to the `order.placed` event and checks whether the customer should be added to the VIP customer group based on their number of orders: -The `order.placed` event is currently not emitted. + The `order.placed` event is currently not emitted. -```ts title="src/subscribers/add-custom-to-vip.ts" collapsibleLines="1-12" expandButtonLabel="Show Imports" -import type { SubscriberArgs, SubscriberConfig } from "@medusajs/medusa" -import { ModuleRegistrationName } from "@medusajs/utils" -import { ICustomerModuleService, IOrderModuleService } from "@medusajs/types" + ```ts title="src/subscribers/add-custom-to-vip.ts" collapsibleLines="1-12" expandButtonLabel="Show Imports" + import type { + SubscriberArgs, + SubscriberConfig, + } from "@medusajs/medusa" + import { + ModuleRegistrationName, + } from "@medusajs/modules-sdk" + import { + ICustomerModuleService, + IOrderModuleService, + } from "@medusajs/types" -export default async function orderCreatedHandler({ - data, - container, -}: SubscriberArgs<{ id: string }>) { - const orderId = "data" in data ? data.data.id : data.id + export default async function orderCreatedHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const orderId = "data" in data ? data.data.id : data.id - const orderModuleService: IOrderModuleService = container.resolve( - ModuleRegistrationName.ORDER - ) + const orderModuleService: IOrderModuleService = container + .resolve(ModuleRegistrationName.ORDER) - const customerModuleService: ICustomerModuleService = container.resolve( - ModuleRegistrationName.CUSTOMER - ) + const customerModuleService: + ICustomerModuleService = container.resolve( + ModuleRegistrationName.CUSTOMER + ) - // check if VIP group exists - const vipGroup = await customerModuleService.listCustomerGroups( - { - name: "VIP", - }, - { - relations: ["customers"], + // check if VIP group exists + const vipGroup = await customerModuleService + .listCustomerGroups({ + name: "VIP", + }, { + relations: ["customers"], + }) + + if (!vipGroup.length) { + return } - ) - if (!vipGroup.length) { - return + // retrieve the order + const order = await orderModuleService.retrieveOrder(orderId) + + if (!order || + !order.customer_id || + vipGroup[0].customers.find( + (customer) => customer.id === order.customer_id + ) !== undefined) { + return + } + + const [, count] = await orderModuleService.listAndCountOrders({ + customer_id: order.customer_id, + }) + + if (count < 20) { + return + } + + // add customer to VIP group + await customerModuleService.addCustomerToGroup({ + customer_group_id: vipGroup[0].id, + customer_id: order.customer_id, + }) } - // retrieve the order - const order = await orderModuleService.retrieveOrder(orderId) - - if ( - !order || - !order.customer_id || - vipGroup[0].customers.find( - (customer) => customer.id === order.customer_id - ) !== undefined - ) { - return + export const config: SubscriberConfig = { + event: "order.placed", } - - const [, count] = await orderModuleService.listAndCountOrders({ - customer_id: order.customer_id, - }) - - if (count < 20) { - return - } - - // add customer to VIP group - await customerModuleService.addCustomerToGroup({ - customer_group_id: vipGroup[0].id, - customer_id: order.customer_id, - }) -} - -export const config: SubscriberConfig = { - event: "order.placed", -} -``` + ```
- + --- @@ -913,138 +864,116 @@ To do that, create a subscriber that listens to the `product.created`, and send You can also create a scheduled job that checks whether the number of new products has exceeded a set threshold, then sends out the newsletter. -, - showLinkIcon: false, - }, - { - href: "!docs!/basics/scheduled-jobs", - title: "Scheduled Jobs", - text: "Learn how to create a scheduled job in Medusa.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/basics/scheduled-jobs", + title: "Scheduled Jobs", + text: "Learn how to create a scheduled job in Medusa.", + startIcon: , + showLinkIcon: false + }, +]} />
-For example, create the file `src/subscribers/send-products-newsletter.ts` with the following content: + For example, create the file `src/subscribers/send-products-newsletter.ts` with the following content: export const newsletterHighlights = [ ["33", "store", "Retrieve the first store in the application."], - [ - "35", - "products", - "Retrieve the products created since the last newsletter send date.", - ], - [ - "41", - "", - "Check whether more than 10 products have been created before proceeding.", - ], - [ - "45", - "customers", - "Retrieve all customers, assuming they're considered subscribed.", - ], - [ - "47", - "createNotifications", - "Send a notification (newsletter) to each customer using the Notification Module.", - ], + ["35", "products", "Retrieve the products created since the last newsletter send date."], + ["41", "", "Check whether more than 10 products have been created before proceeding."], + ["45", "customers", "Retrieve all customers, assuming they're considered subscribed."], + ["47", "createNotifications", "Send a notification (newsletter) to each customer using the Notification Module."], ["50", '"email"', "Send the notification through the email channel."], - [ - "51", - '"newsletter_template"', - "Specify the template name in the third-party service (for example, SendGrid).", - ], + ["51", '"newsletter_template"', "Specify the template name in the third-party service (for example, SendGrid)."], ["53", "products", "Pass the created products to the template."], - [ - "58", - "updateStores", - "Update the store's `last_newsletter_send_date` property with the current date.", - ], + ["58", "updateStores", "Update the store's `last_newsletter_send_date` property with the current date."] ] -```ts title="src/subscribers/send-products-newsletter.ts" highlights={newsletterHighlights} collapsibleLines="1-14" expandButtonLabel="Show Imports" -import type { SubscriberArgs, SubscriberConfig } from "@medusajs/medusa" -import { ModuleRegistrationName } from "@medusajs/modules-sdk" -import { - ICustomerModuleService, - IProductModuleService, - IStoreModuleService, - INotificationModuleService, -} from "@medusajs/types" + ```ts title="src/subscribers/send-products-newsletter.ts" highlights={newsletterHighlights} collapsibleLines="1-14" expandButtonLabel="Show Imports" + import type { + SubscriberArgs, + SubscriberConfig, + } from "@medusajs/medusa" + import { + ModuleRegistrationName, + } from "@medusajs/modules-sdk" + import { + ICustomerModuleService, + IProductModuleService, + IStoreModuleService, + INotificationModuleService, + } from "@medusajs/types" -export default async function productCreateHandler({ - data, - container, -}: SubscriberArgs<{ id: string }>) { - const productModuleService: IProductModuleService = container.resolve( - ModuleRegistrationName.PRODUCT - ) + export default async function productCreateHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const productModuleService: IProductModuleService = + container.resolve(ModuleRegistrationName.PRODUCT) - const storeModuleService: IStoreModuleService = container.resolve( - ModuleRegistrationName.STORE - ) + const storeModuleService: IStoreModuleService = + container.resolve(ModuleRegistrationName.STORE) - const customerModuleService: ICustomerModuleService = container.resolve( - ModuleRegistrationName.CUSTOMER - ) + const customerModuleService: ICustomerModuleService = + container.resolve(ModuleRegistrationName.CUSTOMER) - const notificationModuleService: INotificationModuleService = - container.resolve(ModuleRegistrationName.NOTIFICATION) + const notificationModuleService: + INotificationModuleService = container.resolve( + ModuleRegistrationName.NOTIFICATION + ) - const store = (await storeModuleService.listStores())[0] + const store = (await storeModuleService.listStores())[0] - const products = await productModuleService.listProducts({ - created_at: { - $gt: store.metadata.last_newsletter_send_date, - }, - }) + const products = await productModuleService.listProducts({ + created_at: { + $gt: store.metadata.last_newsletter_send_date, + }, + }) - if (products.length < 10) { - return + if (products.length < 10) { + return + } + + const customers = await customerModuleService.listCustomers() + + await notificationModuleService.createNotifications( + customers.map((customer) => ({ + to: customer.email, + channel: "email", + template: "newsletter_template", + data: { + products, + }, + })) + ) + + await storeModuleService.updateStores(store.id, { + metadata: { + last_newsletter_send_date: (new Date()).toString(), + }, + }) } - const customers = await customerModuleService.listCustomers() + export const config: SubscriberConfig = { + event: "product.created", + } + ``` - await notificationModuleService.createNotifications( - customers.map((customer) => ({ - to: customer.email, - channel: "email", - template: "newsletter_template", - data: { - products, - }, - })) - ) + In the subscriber function, you: - await storeModuleService.updateStores(store.id, { - metadata: { - last_newsletter_send_date: new Date().toString(), - }, - }) -} + 1. Retrieve the first store in our application. + 2. Retrieve products created since the last time a newsletter is sent. The last send date is stored in the store's `metadata` property. + 3. If the count of last created products is less than 10, stop execution. + 4. Retrieve all customers. Here, it's assumed that all customers are considered subscribed for simplicity. + 5. Use the Notification Module to send a notification to the customer. This uses the Notification Module Provider configured for the `email` channel. + 6. Update the store's `last_newsletter_send_date` with the current date. -export const config: SubscriberConfig = { - event: "product.created", -} -``` - -In the subscriber function, you: - -1. Retrieve the first store in our application. -2. Retrieve products created since the last time a newsletter is sent. The last send date is stored in the store's `metadata` property. -3. If the count of last created products is less than 10, stop execution. -4. Retrieve all customers. Here, it's assumed that all customers are considered subscribed for simplicity. -5. Use the Notification Module to send a notification to the customer. This uses the Notification Module Provider configured for the `email` channel. -6. Update the store's `last_newsletter_send_date` with the current date. - -
+
\ No newline at end of file diff --git a/www/apps/resources/app/recipes/digital-products/page.mdx b/www/apps/resources/app/recipes/digital-products/page.mdx index 9d6268db5d..f0055d932a 100644 --- a/www/apps/resources/app/recipes/digital-products/page.mdx +++ b/www/apps/resources/app/recipes/digital-products/page.mdx @@ -33,24 +33,22 @@ Use a file module provider to manage your stored digital products. During development, you can use the Local File Module Provider, which is installed by default in your store. For production, check out available file module providers or create your own. -, - showLinkIcon: false, - }, - { - href: "/references/file-provider-module", - title: "Create a File Module Provider", - text: "Learn how to create a file module provider.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "/references/file-provider-module", + title: "Create a File Module Provider", + text: "Learn how to create a file module provider.", + startIcon: , + showLinkIcon: false + }, +]} /> --- @@ -64,24 +62,22 @@ You can use one of Medusa’s notification module providers or create your own. {/* TODO add links */} -, - showLinkIcon: false, - }, - { - href: "/references/notification-provider-module", - title: "Create a Notification Module Provider", - text: "Learn how to create a custom notification service.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "/references/notification-provider-module", + title: "Create a Notification Module Provider", + text: "Learn how to create a custom notification service.", + startIcon: , + showLinkIcon: false, + }, +]} /> --- @@ -101,46 +97,47 @@ The module will hold your custom data models and the service implementing digita
-In this section, you’ll create the skeleton of the Digital Product Module. In later sections, you’ll add more resources to it. + In this section, you’ll create the skeleton of the Digital Product Module. In later sections, you’ll add more resources to it. + + Start by creating the directory `src/modules/digital-product`. + + Then, create the file `src/modules/digital-product/service.ts` with the following content: + + ```ts title="src/modules/digital-product/service.ts" + class DigitalProductModuleService { + // TODO + } + + export default DigitalProductModuleService + ``` + + A module must export a service. So, you implement a dummy service for now. + + Next, create the file `src/modules/digital-product/index.ts` with the following content: + + ```ts title="src/modules/digital-product/index.ts" + import DigitalProductModuleService from "./service" + import { Module } from "@medusajs/utils" -Start by creating the directory `src/modules/digital-product`. - -Then, create the file `src/modules/digital-product/service.ts` with the following content: - -```ts title="src/modules/digital-product/service.ts" -class DigitalProductModuleService { - // TODO -} - -export default DigitalProductModuleService -``` - -A module must export a service. So, you implement a dummy service for now. - -Next, create the file `src/modules/digital-product/index.ts` with the following content: - -```ts title="src/modules/digital-product/index.ts" -import DigitalProductModuleService from "./service" - -export default { - service: DigitalProductModuleService, -} -``` - -This file holds the definition of the module. - -Finally, add the module to `medusa-config.js` into the `modules` object: - -```js title="medusa-config.js" -module.exports = defineConfig({ - // ... - modules: { - digitalProductModuleService: { - resolve: "./modules/digital-product", + export default Module("digital-product", { + service: DigitalProductModuleService, + }) + ``` + + This file holds the definition of the module. + + Finally, add the module to `medusa-config.js` into the `modules` object: + + ```js title="medusa-config.js" + module.exports = defineConfig({ + // ... + modules: { + digitalProductModuleService: { + resolve: "./modules/digital-product", + }, }, - }, -}) -``` + }) + ```
@@ -166,41 +163,41 @@ Module Relationships is coming soon. showLinkIcon={false} /> -{/\*
+{/*
-In this section, you’ll create a `ProductMedia` data model that represents your digital products. + In this section, you’ll create a `ProductMedia` data model that represents your digital products. + + Before creating the data model, create the file `src/types/digital-product/product-media.ts` that holds common types: + + ```ts title="src/types/digital-product/product-media.ts" + export enum MediaType { + MAIN = "main", + PREVIEW = "preview" + } + ``` + + Then, create the file `src/modules/digital-product/models/product-media.ts` with the following content: + + ```ts title="src/modules/digital-product/models/product-media.ts" + import { model } from "@medusajs/utils" + import { MediaType } from "../../../types/digital-product/product-media" -Before creating the data model, create the file `src/types/digital-product/product-media.ts` that holds common types: - -```ts title="src/types/digital-product/product-media.ts" -export enum MediaType { - MAIN = "main", - PREVIEW = "preview", -} -``` - -Then, create the file `src/modules/digital-product/models/product-media.ts` with the following content: - -```ts title="src/modules/digital-product/models/product-media.ts" -import { model } from "@medusajs/utils" -import { MediaType } from "../../../types/digital-product/product-media" - -const ProductMedia = model.define("product_media", { - id: model.id().primaryKey(), - name: model.text(), - type: model.enum(Object.values(MediaType)), - fileKey: model.text(), - mimeType: model.text(), - variant_id: model.text().index("IDX_product_media_variant_id"), -}) - -export default ProductMedia -``` - -The `ProductMedia` data model has properties relevant to digital products. Most importantly, it has a `variant_id` property that will later be used for its relationship with the Product Module. - -To reflect the data model in the database, you must create a migration. + const ProductMedia = model.define("product_media", { + id: model.id().primaryKey(), + name: model.text(), + type: model.enum(Object.values(MediaType)), + fileKey: model.text(), + mimeType: model.text(), + variant_id: model.text().index("IDX_product_media_variant_id"), + }) + export default ProductMedia + ``` + + The `ProductMedia` data model has properties relevant to digital products. Most importantly, it has a `variant_id` property that will later be used for its relationship with the Product Module. + + To reflect the data model in the database, you must create a migration. + Learn how to generate a migration in [this guide](!docs!/basics/data-models#create-a-migration). @@ -250,32 +247,32 @@ Medusa facilitates implementing data-management features by providing a service showLinkIcon={false} /> -{/\*
+{/*
+ + In this section, you’ll modify the `DigitalProductModuleService` you created earlier to provide data-management functionalities of the `ProductMedia` data model. + + Change the content of `src/modules/digital-product/service.ts` to the following: + + ```ts title="src/modules/digital-product/service.ts" + import { MedusaService } from "@medusajs/utils" + import ProductMedia from "./models/product-media" -In this section, you’ll modify the `DigitalProductModuleService` you created earlier to provide data-management functionalities of the `ProductMedia` data model. - -Change the content of `src/modules/digital-product/service.ts` to the following: - -```ts title="src/modules/digital-product/service.ts" -import { MedusaService } from "@medusajs/utils" -import ProductMedia from "./models/product-media" - -class DigitalProductModuleService extends MedusaService({ - ProductMedia, -}) { - // TODO add custom methods -} - -export default DigitalProductModuleService -``` - -The `DigitalProductModuleService` now extends the service factory which generates data-management methods for the `ProductMedia` data model. + class DigitalProductModuleService extends MedusaService({ + ProductMedia, + }){ + // TODO add custom methods + } + export default DigitalProductModuleService + ``` + + The `DigitalProductModuleService` now extends the service factory which generates data-management methods for the `ProductMedia` data model. +
*/} {/* --- */} -{/\* ## Add Relationship to Product Variants +{/* ## Add Relationship to Product Variants As mentioned in a previous section, the product media has a `variant_id` that points to the saleable product variant. @@ -344,8 +341,8 @@ The Medusa application resolves module relationships without creating an actual -Learn more about the data returned in the `__joinerConfig` method here. - + Learn more about the data returned in the `__joinerConfig` method here. + Next, change the module’s entry in the `modules` object in `medusa-config.js` to the following: @@ -382,479 +379,530 @@ Fetching data across modules using the remote query is coming soon. -, - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/modules/remote-query", - title: "Remote Query", - text: "Learn about what the remote query is and how to use it.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/modules/remote-query", + title: "Remote Query", + text: "Learn about what the remote query is and how to use it.", + startIcon: , + showLinkIcon: false + }, +]} /> -{/\*
+{/*
-In this section, you’ll create a List and Create API routes to retrieve and create digital products. + In this section, you’ll create a List and Create API routes to retrieve and create digital products. + + ### Create API Route + + In the Create API route, you want to create not only the product media but also the associated product variant if no ID is specified. + + You’ll implement this logic in a workflow, then use the workflow in the API route. + + Start by changing the content of `src/types/digital-product/product-media.ts` to include more types: + + ```ts title="src/types/digital-product/product-media.ts" + import { + ProductVariantDTO, + CreateProductWorkflowInputDTO, + } from "@medusajs/types" + + export enum MediaType { + MAIN = "main", + PREVIEW = "preview" + } + + export type ProductMediaDTO = { + id: string + name: string + type: MediaType + file_key: string + mime_type: string + variant_id: string + variant?: ProductVariantDTO + } + + export type CreateProductMediaDTO = { + name: string + file_key: string + type: MediaType + mime_type: string + variant_id?: string + } + + export type CreateProductMediaWorkflowInput = + CreateProductMediaDTO & { + product?: CreateProductWorkflowInputDTO + } -### Create API Route + ``` + + Then, create the file `src/workflows/digital-product/create.ts` with the following content: + + ```ts title="src/workflows/digital-product/create.ts" collapsibleLines="1-19" expandButtonLabel="Show Imports" + import { + createWorkflow, + WorkflowData, + createStep, + StepResponse, + } from "@medusajs/workflows-sdk" + import { createProductsWorkflow } from "@medusajs/core-flows" + import { + CreateProductMediaDTO, + CreateProductMediaWorkflowInput, + ProductMediaDTO, + } from "../../types/digital-product/product-media" + import DigitalProductModuleService from + "../../modules/digital-product/service" + import { RemoteQueryFunction } from "@medusajs/modules-sdk" + import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, + } from "@medusajs/utils" -In the Create API route, you want to create not only the product media but also the associated product variant if no ID is specified. + const tryToCreateProductVariantStep = createStep( + "try-to-create-product-variant-step", + async (input: CreateProductMediaWorkflowInput, { container }) => { + if (input.product && !input.variant_id) { + const { result, errors } = await createProductsWorkflow(container) + .run({ + input: { + products: [input.product], + }, + throwOnError: false, + }) -You’ll implement this logic in a workflow, then use the workflow in the API route. + if (errors.length) { + throw errors[0].error + } -Start by changing the content of `src/types/digital-product/product-media.ts` to include more types: + input.variant_id = result[0].variants[0].id -```ts title="src/types/digital-product/product-media.ts" -import { - ProductVariantDTO, - CreateProductWorkflowInputDTO, -} from "@medusajs/types" + delete input.product + } -export enum MediaType { - MAIN = "main", - PREVIEW = "preview", -} + return new StepResponse(input) + } + ) -export type ProductMediaDTO = { - id: string - name: string - type: MediaType - file_key: string - mime_type: string - variant_id: string - variant?: ProductVariantDTO -} + const createProductMediaStep = createStep( + "create-product-media-step", + async (input: CreateProductMediaDTO, { container }) => { + const digitalProductModuleService: + DigitalProductModuleService = container.resolve( + "digitalProductModuleService" + ) -export type CreateProductMediaDTO = { - name: string - file_key: string - type: MediaType - mime_type: string - variant_id?: string -} + const productMedia = await digitalProductModuleService + .createProductMedias( + input + ) -export type CreateProductMediaWorkflowInput = CreateProductMediaDTO & { - product?: CreateProductWorkflowInputDTO -} -``` + return new StepResponse(productMedia) + } + ) -Then, create the file `src/workflows/digital-product/create.ts` with the following content: + const retrieveProductMediaWithVariant = createStep( + "retrieve-product-media-with-variant-step", + async (input: ProductMediaDTO, { container }) => { + const remoteQuery: RemoteQueryFunction = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) -```ts title="src/workflows/digital-product/create.ts" collapsibleLines="1-19" expandButtonLabel="Show Imports" -import { - createWorkflow, - WorkflowData, - createStep, - StepResponse, -} from "@medusajs/workflows-sdk" -import { createProductsWorkflow } from "@medusajs/core-flows" -import { - CreateProductMediaDTO, - CreateProductMediaWorkflowInput, - ProductMediaDTO, -} from "../../types/digital-product/product-media" -import DigitalProductModuleService from "../../modules/digital-product/service" -import { RemoteQueryFunction } from "@medusajs/modules-sdk" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" + const query = remoteQueryObjectFromString({ + entryPoint: "product_media", + fields: [ + "id", + "name", + "type", + "file_key", + "mime_type", + "variant.*", + ], + variables: { + filters: { + id: input.id, + }, + }, + }) -const tryToCreateProductVariantStep = createStep( - "try-to-create-product-variant-step", - async (input: CreateProductMediaWorkflowInput, { container }) => { - if (input.product && !input.variant_id) { - const { result, errors } = await createProductsWorkflow(container).run({ + const result = await remoteQuery(query) + + return new StepResponse(result[0]) + } + ) + + type WorkflowInput = { + data: CreateProductMediaWorkflowInput + } + + export const createProductMediaWorkflow = createWorkflow( + "create-product-media-workflow", + function (input: WorkflowData) { + // create the product variant before creating the media + // if variant_id isn't passed + const normalizedInput = tryToCreateProductVariantStep(input.data) + + const productMedia = createProductMediaStep(normalizedInput) + + return retrieveProductMediaWithVariant(productMedia) + } + ) + ``` + + This workflow has three steps: + + 1. If a `variant_id` field isn’t passed and a `product` field is passed, create the product using Medusa’s `createProductsWorkflow` and set the ID of the variant in the `variant_id` property. + 2. Use the `DigitalProductModuleService` to create the product media. + 3. Use the remote query to retrieve the product media along with the variant it references. + + Finally, create the `src/api/admin/digital-products/route.ts` file with the following content: + + ```ts title="src/api/admin/digital-products/route.ts" collapsibleLines="1-12" expandButtonLabel="Show Imports" + import { + MedusaRequest, + MedusaResponse, + } from "@medusajs/medusa" + import { MedusaError } from "@medusajs/utils" + import { + CreateProductMediaWorkflowInput, + } from "../../../types/digital-product/product-media" + import { + createProductMediaWorkflow, + } from "../../../workflows/digital-product/create" + + type CreateProductMediaReq = CreateProductMediaWorkflowInput + + export async function POST( + req: MedusaRequest, + res: MedusaResponse + ) { + // validation omitted for simplicity + const { + result, + errors, + } = await createProductMediaWorkflow(req.scope) + .run({ input: { - products: [input.product], + data: { + ...req.body, + }, }, throwOnError: false, }) - - if (errors.length) { - throw errors[0].error - } - - input.variant_id = result[0].variants[0].id - - delete input.product + + if (errors.length) { + throw new MedusaError( + MedusaError.Types.DB_ERROR, + errors[0].error + ) } - - return new StepResponse(input) + + res.json({ + product_media: result, + }) } -) - -const createProductMediaStep = createStep( - "create-product-media-step", - async (input: CreateProductMediaDTO, { container }) => { - const digitalProductModuleService: DigitalProductModuleService = - container.resolve("digitalProductModuleService") - - const productMedia = await digitalProductModuleService.createProductMedias( - input - ) - - return new StepResponse(productMedia) + ``` + + This adds a `POST` API route at `/admin/digital-products` that executes the `createProductMediaWorkflow` workflow. + + To test it out, start the Medusa application: + + ```bash npm2yarn + npm run dev + ``` + + Next, authenticate as an admin user as explained in the [API Reference] + + Then, upload a file using the Upload API route: + + ```bash + curl -X POST 'http://localhost:9000/admin/uploads' \ + -H 'Authorization: Bearer {bearer_token}' \ + --form 'files=@"/path/to/file"' + ``` + + Make sure to replace `/path/to/file` with the path to the file to upload. Copy the `id` field’s value as you’ll use it as the `file_key`'s value when creating the digital product. + + Finally, send a request to the API route you created: + + ```bash + curl -X POST 'localhost:9000/admin/digital-products' \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer {bearer_token}' \ + --data '{ + "name": "Harry Potter", + "file_key": "file.png", + "type": "main", + "mime_type": "image/png", + "product": { + "title": "Harry Potter Books", + "variants": [ + { + "title": "Harry Potter 1" + } + ] + } + }' + ``` + + This creates a product and a variant, and a product media that references the created variant. + + You’ll receive a response similar to the following: + + ```json + { + "product_media": { + "id": "promed_01HXEFRMS79293ASYVN8YY9Y0J", + "name": "Harry Potter", + "type": "main", + "file_key": "file.png", + "mime_type": "image/png", + "variant_id": "variant_01HXEFRMR3B09EZJX23P1DMYFQ", + "variant": { + "id": "variant_01HXEFRMR3B09EZJX23P1DMYFQ", + "title": "Harry Potter 1", + // ... + } + } } -) - -const retrieveProductMediaWithVariant = createStep( - "retrieve-product-media-with-variant-step", - async (input: ProductMediaDTO, { container }) => { - const remoteQuery: RemoteQueryFunction = container.resolve( + ``` + + ### List API Route + + Next, you’ll create the List API route that returns a list of digital products. + + To do that, add the following to `src/api/admin/digital-products/route.ts`: + + ```ts title="src/api/admin/digital-products/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" + // other imports... + import { RemoteQueryFunction } from "@medusajs/modules-sdk" + import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, + } from "@medusajs/utils" + + // ... + + export async function GET( + req: MedusaRequest, + res: MedusaResponse + ) { + const remoteQuery: RemoteQueryFunction = req.scope.resolve( ContainerRegistrationKeys.REMOTE_QUERY ) - + const query = remoteQueryObjectFromString({ entryPoint: "product_media", - fields: ["id", "name", "type", "file_key", "mime_type", "variant.*"], - variables: { - filters: { - id: input.id, - }, - }, + fields: [ + "id", + "name", + "type", + "file_key", + "mime_type", + "variant.*", + "variant.product.*", + ], }) - + const result = await remoteQuery(query) - - return new StepResponse(result[0]) + + res.json({ + product_medias: result, + }) } -) - -type WorkflowInput = { - data: CreateProductMediaWorkflowInput -} - -export const createProductMediaWorkflow = createWorkflow( - "create-product-media-workflow", - function (input: WorkflowData) { - // create the product variant before creating the media - // if variant_id isn't passed - const normalizedInput = tryToCreateProductVariantStep(input.data) - - const productMedia = createProductMediaStep(normalizedInput) - - return retrieveProductMediaWithVariant(productMedia) - } -) -``` - -This workflow has three steps: - -1. If a `variant_id` field isn’t passed and a `product` field is passed, create the product using Medusa’s `createProductsWorkflow` and set the ID of the variant in the `variant_id` property. -2. Use the `DigitalProductModuleService` to create the product media. -3. Use the remote query to retrieve the product media along with the variant it references. - -Finally, create the `src/api/admin/digital-products/route.ts` file with the following content: - -```ts title="src/api/admin/digital-products/route.ts" collapsibleLines="1-12" expandButtonLabel="Show Imports" -import { MedusaRequest, MedusaResponse } from "@medusajs/medusa" -import { MedusaError } from "@medusajs/utils" -import { CreateProductMediaWorkflowInput } from "../../../types/digital-product/product-media" -import { createProductMediaWorkflow } from "../../../workflows/digital-product/create" - -type CreateProductMediaReq = CreateProductMediaWorkflowInput - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -) { - // validation omitted for simplicity - const { result, errors } = await createProductMediaWorkflow(req.scope).run({ - input: { - data: { - ...req.body, - }, - }, - throwOnError: false, - }) - - if (errors.length) { - throw new MedusaError(MedusaError.Types.DB_ERROR, errors[0].error) - } - - res.json({ - product_media: result, - }) -} -``` - -This adds a `POST` API route at `/admin/digital-products` that executes the `createProductMediaWorkflow` workflow. - -To test it out, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Next, authenticate as an admin user as explained in the [API Reference] - -Then, upload a file using the Upload API route: - -```bash -curl -X POST 'http://localhost:9000/admin/uploads' \ --H 'Authorization: Bearer {bearer_token}' \ ---form 'files=@"/path/to/file"' -``` - -Make sure to replace `/path/to/file` with the path to the file to upload. Copy the `id` field’s value as you’ll use it as the `file_key`'s value when creating the digital product. - -Finally, send a request to the API route you created: - -```bash -curl -X POST 'localhost:9000/admin/digital-products' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {bearer_token}' \ ---data '{ - "name": "Harry Potter", - "file_key": "file.png", - "type": "main", - "mime_type": "image/png", - "product": { - "title": "Harry Potter Books", - "variants": [ - { - "title": "Harry Potter 1" - } - ] - } -}' -``` - -This creates a product and a variant, and a product media that references the created variant. - -You’ll receive a response similar to the following: - -```json -{ - "product_media": { - "id": "promed_01HXEFRMS79293ASYVN8YY9Y0J", - "name": "Harry Potter", - "type": "main", - "file_key": "file.png", - "mime_type": "image/png", - "variant_id": "variant_01HXEFRMR3B09EZJX23P1DMYFQ", - "variant": { - "id": "variant_01HXEFRMR3B09EZJX23P1DMYFQ", - "title": "Harry Potter 1" - // ... - } - } -} -``` - -### List API Route - -Next, you’ll create the List API route that returns a list of digital products. - -To do that, add the following to `src/api/admin/digital-products/route.ts`: - -```ts title="src/api/admin/digital-products/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports" -// other imports... -import { RemoteQueryFunction } from "@medusajs/modules-sdk" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" - -// ... - -export async function GET(req: MedusaRequest, res: MedusaResponse) { - const remoteQuery: RemoteQueryFunction = req.scope.resolve( - ContainerRegistrationKeys.REMOTE_QUERY - ) - - const query = remoteQueryObjectFromString({ - entryPoint: "product_media", - fields: [ - "id", - "name", - "type", - "file_key", - "mime_type", - "variant.*", - "variant.product.*", - ], - }) - - const result = await remoteQuery(query) - - res.json({ - product_medias: result, - }) -} -``` - -This creates a new `GET` API route at `/admin/digital-products` that retrieves the list of digital products and their associated variants. - -To test it out, send a request to the API route while your Medusa application is running: - -```bash apiTesting testApiUrl="http://localhost:9000/admin/digital-products" testApiMethod="GET" -curl 'localhost:9000/admin/digital-products' \ --H 'Authorization: Bearer {bearer_token}' \ -``` - -You’ll receive a list of digital products. + ``` + + This creates a new `GET` API route at `/admin/digital-products` that retrieves the list of digital products and their associated variants. + + To test it out, send a request to the API route while your Medusa application is running: + + ```bash apiTesting testApiUrl="http://localhost:9000/admin/digital-products" testApiMethod="GET" + curl 'localhost:9000/admin/digital-products' \ + -H 'Authorization: Bearer {bearer_token}' \ + ``` + + You’ll receive a list of digital products.
*/} - + --- ## Customize Admin Dashboard -You can extend the Medusa Admin to provide merchants with an interface to manage digital products. You can inject widgets into existing pages or create new pages. +You can extend the Medusa Admin to provide merchants with an interface to manage digital products. You can inject widgets into existing pages or create new pages. In your customizations, you send requests to the API routes you created to create and list digital products. -, - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/admin/ui-routes", - title: "Create UI Route", - text: "Learn how to create a UI route in the Medusa Admin.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/admin/ui-routes", + title: "Create UI Route", + text: "Learn how to create a UI route in the Medusa Admin.", + startIcon: , + showLinkIcon: false + }, +]} /> -{/\*
+{/*
-In this example, you’ll add a single page that lists the digital products and allows you to create a new one. The implementation will be minimal for the purpose of simplicity, so you can elaborate on it based on your use case. + In this example, you’ll add a single page that lists the digital products and allows you to create a new one. The implementation will be minimal for the purpose of simplicity, so you can elaborate on it based on your use case. -To create the UI route, create the file `src/admin/routes/product-media/page.tsx` with the following content: + To create the UI route, create the file `src/admin/routes/product-media/page.tsx` with the following content: -```tsx title="src/admin/routes/product-media/page.tsx" badgeLabel="Medusa Application" collapsibleLines="1-13" expandButtonLabel="Show Imports" -import { defineRouteConfig } from "@medusajs/admin-shared" -import { useEffect, useState } from "react" -import { Container, Heading, Table } from "@medusajs/ui" -import { PhotoSolid } from "@medusajs/icons" -import { ProductMediaDTO } from "../../../types/digital-product/product-media" -import { Link } from "react-router-dom" -import ProductMediaCreateForm from "../../components/product-media/CreateForm" + ```tsx title="src/admin/routes/product-media/page.tsx" badgeLabel="Medusa Application" collapsibleLines="1-13" expandButtonLabel="Show Imports" + import { defineRouteConfig } from "@medusajs/admin-shared" + import { useEffect, useState } from "react" + import { + Container, + Heading, + Table, + } from "@medusajs/ui" + import { PhotoSolid } from "@medusajs/icons" + import { ProductMediaDTO } from "../../../types/digital-product/product-media" + import { Link } from "react-router-dom" + import ProductMediaCreateForm + from "../../components/product-media/CreateForm" -const ProductMediaListPage = () => { - const [loading, setLoading] = useState(true) - const [productMedias, setProductMedias] = useState([]) + const ProductMediaListPage = () => { + const [loading, setLoading] = useState(true) + const [productMedias, setProductMedias] = useState([]) - useEffect(() => { - if (!loading) { - return - } + useEffect(() => { + if (!loading) { + return + } - fetch(`/admin/digital-products`, { - credentials: "include", - }) + fetch(`/admin/digital-products`, { + credentials: "include", + }) .then((response) => response.json()) .then(({ product_medias }) => { setProductMedias(product_medias) setLoading(false) }) - }, [loading]) + }, [loading]) - return ( - -
- Digital Products - setLoading(true)} /> -
- {loading &&
Loading...
} - {!loading && !productMedias.length &&
No Digital Products
} - {!loading && productMedias.length > 0 && ( - - - - Product - Product Variant - File Key - Action - - - - {productMedias.map((product_media) => ( - - {product_media.variant?.product.title} - {product_media.variant?.title} - {product_media.file_key} - - {product_media.variant && ( - - View Product - - )} - + return ( + +
+ Digital Products + setLoading(true)} /> +
+ {loading &&
Loading...
} + {!loading && !productMedias.length && ( +
No Digital Products
+ )} + {!loading && productMedias.length > 0 && ( +
+ + + Product + + Product Variant + + File Key + Action - ))} - -
- )} -
- ) -} + + + {productMedias.map((product_media) => ( + + + {product_media.variant?.product.title} + + + {product_media.variant?.title} + + + {product_media.file_key} + + + {product_media.variant && + View Product + } + + + ))} + + + )} + + ) + } -export const config = defineRouteConfig({ - label: "Digital Products", - icon: PhotoSolid, -}) - -export default ProductMediaListPage -``` - -This UI route will show under the sidebar with the label “Digital Products”. In the page, you use the `/admin/digital-products` API route to retrieve and display the product medias. - -You also render a `ProductMediaCreateForm` component that implements the Create Digital Product form. - -To create this form, create the file `src/admin/components/product-media/CreateForm/index.tsx` with the following content: - -```tsx title="src/admin/components/product-media/CreateForm/index.tsx" badgeLabel="Medusa Application" collapsibleLines="1-11" expandButtonLabel="Show Imports" -import { useState } from "react" -import { redirect } from "react-router-dom" -import { Button, Container, Input, Label, Select, Drawer } from "@medusajs/ui" - -type Props = { - onCreate: () => void -} - -const ProductMediaCreateForm = ({ onCreate }: Props) => { - const [open, setOpen] = useState(false) - const [productName, setProductName] = useState("") - const [productVariantName, setProductVariantName] = useState("") - const [name, setName] = useState("") - const [type, setType] = useState("main") - const [file, setFile] = useState() - const [loading, setLoading] = useState(false) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - setLoading(true) - - const formData = new FormData() - formData.append("files", file) - - // upload file - fetch(`/admin/uploads`, { - method: "POST", - credentials: "include", - body: formData, + export const config = defineRouteConfig({ + label: "Digital Products", + icon: PhotoSolid, }) + + export default ProductMediaListPage + ``` + + This UI route will show under the sidebar with the label “Digital Products”. In the page, you use the `/admin/digital-products` API route to retrieve and display the product medias. + + You also render a `ProductMediaCreateForm` component that implements the Create Digital Product form. + + To create this form, create the file `src/admin/components/product-media/CreateForm/index.tsx` with the following content: + + ```tsx title="src/admin/components/product-media/CreateForm/index.tsx" badgeLabel="Medusa Application" collapsibleLines="1-11" expandButtonLabel="Show Imports" + import { useState } from "react" + import { redirect } from "react-router-dom" + import { + Button, + Container, + Input, + Label, + Select, + Drawer, + } from "@medusajs/ui" + + type Props = { + onCreate: () => void + } + + const ProductMediaCreateForm = ({ onCreate }: Props) => { + const [open, setOpen] = useState(false) + const [productName, setProductName] = useState("") + const [ + productVariantName, + setProductVariantName, + ] = useState("") + const [name, setName] = useState("") + const [type, setType] = useState("main") + const [file, setFile] = useState() + const [loading, setLoading] = useState(false) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + setLoading(true) + + const formData = new FormData() + formData.append("files", file) + + // upload file + fetch(`/admin/uploads`, { + method: "POST", + credentials: "include", + body: formData, + }) .then((res) => res.json()) .then(({ files }) => { // create digital product @@ -879,82 +927,96 @@ const ProductMediaCreateForm = ({ onCreate }: Props) => { }, }), }) - .then((res) => res.json()) - .then(() => { - setOpen(false) - setLoading(false) - onCreate() - }) - .catch((e) => { - console.error(e) - setLoading(false) - }) + .then((res) => res.json()) + .then(() => { + setOpen(false) + setLoading(false) + onCreate() + }) + .catch((e) => { + console.error(e) + setLoading(false) + }) }) .catch((e) => { console.error(e) setLoading(false) }) - } + } - return ( - - - - - - - Create Digital Product - - + return ( + + + + + + + + Create Digital Product + + + -
-
+ +
- setProductName(e.target.value)} />
-
+
- setProductVariantName(e.target.value)} + + setProductVariantName(e.target.value) + } />
-
+
- setName(e.target.value)} />
-
+
-
+
- setFile(e.target.files[0])} />
- @@ -962,31 +1024,31 @@ const ProductMediaCreateForm = ({ onCreate }: Props) => { - ) -} + ) + } -export default ProductMediaCreateForm -``` + export default ProductMediaCreateForm + ``` -In this component, you create a form that accepts basic information needed to create the digital product. This form only accepts one file for one variant for simplicity purposes. You can expand on this based on your use case. + In this component, you create a form that accepts basic information needed to create the digital product. This form only accepts one file for one variant for simplicity purposes. You can expand on this based on your use case. -An alternative approach would be to inject a widget to the Product Details page and allow users to upload the files from there. It depends on whether you’re only supporting Digital Products or you want the distinction between them, as done here. + An alternative approach would be to inject a widget to the Product Details page and allow users to upload the files from there. It depends on whether you’re only supporting Digital Products or you want the distinction between them, as done here. -When the user submits the form, you first upload the file using the [Upload Protected File API Route](!api!/admin#uploads_postuploads). Then, you create the digital product using the custom API Route you created. + When the user submits the form, you first upload the file using the [Upload Protected File API Route](!api!/admin#uploads_postuploads). Then, you create the digital product using the custom API Route you created. -The product’s details can still be edited from the product's page, similar to regular products. You can edit its price, add more variants, and more. + The product’s details can still be edited from the product's page, similar to regular products. You can edit its price, add more variants, and more. -To test it out, run the `dev` command: + To test it out, run the `dev` command: -```bash npm2yarn -npm run dev -``` + ```bash npm2yarn + npm run dev + ``` -If you open the admin now, you’ll find a new Digital Products item in the sidebar. You can try adding Digital Products and viewing them. + If you open the admin now, you’ll find a new Digital Products item in the sidebar. You can try adding Digital Products and viewing them.
*/} @@ -1020,81 +1082,94 @@ In the subscriber, you can send a notification, such as an email, to the custome showLinkIcon={false} /> -{/\*
+{/*
-An alternative solution is to create a store download API Route that allows authenticated customers to download products they've purchased, then add the link to the API Route or a storefront page that calls the API Route in the email. Learn how to implement the download API Route [here](#download-product-after-purchase). + An alternative solution is to create a store download API Route that allows authenticated customers to download products they've purchased, then add the link to the API Route or a storefront page that calls the API Route in the email. Learn how to implement the download API Route [here](#download-product-after-purchase). -Here’s an example of a subscriber that retrieves the download links and sends them to the customer using the installed notification module provider: + Here’s an example of a subscriber that retrieves the download links and sends them to the customer using the installed notification module provider: + + ```ts title="src/subscribers/handle-order.ts" badgeLabel="Medusa Application" collapsibleLines="1-15" expandButtonLabel="Show Imports" + import { + type SubscriberConfig, + type SubscriberArgs, + } from "@medusajs/medusa" + import { + IOrderModuleService, + IFileModuleService, + INotificationModuleService, + } from "@medusajs/types" + import { + ModuleRegistrationName, + } from "@medusajs/modules-sdk" + import DigitalProductModuleService + from "../modules/digital-product/service" -```ts title="src/subscribers/handle-order.ts" badgeLabel="Medusa Application" collapsibleLines="1-15" expandButtonLabel="Show Imports" -import { type SubscriberConfig, type SubscriberArgs } from "@medusajs/medusa" -import { - IOrderModuleService, - IFileModuleService, - INotificationModuleService, -} from "@medusajs/types" -import { ModuleRegistrationName } from "@medusajs/utils" -import DigitalProductModuleService from "../modules/digital-product/service" + export default async function handleOrderPlaced({ + data, container, + }: SubscriberArgs<{ id: string }>) { + const orderModuleService: IOrderModuleService = + container.resolve( + ModuleRegistrationName.ORDER + ) + const fileModuleService: IFileModuleService = + container.resolve( + ModuleRegistrationName.FILE + ) + const notificationModuleService: INotificationModuleService = + container.resolve(ModuleRegistrationName.NOTIFICATION) + const digitalProductModuleService: + DigitalProductModuleService = container.resolve( + "digitalProductModuleService" + ) -export default async function handleOrderPlaced({ - data, - container, -}: SubscriberArgs<{ id: string }>) { - const orderModuleService: IOrderModuleService = container.resolve( - ModuleRegistrationName.ORDER - ) - const fileModuleService: IFileModuleService = container.resolve( - ModuleRegistrationName.FILE - ) - const notificationModuleService: INotificationModuleService = - container.resolve(ModuleRegistrationName.NOTIFICATION) - const digitalProductModuleService: DigitalProductModuleService = - container.resolve("digitalProductModuleService") + const orderId = "data" in data ? data.data.id : data.id - const orderId = "data" in data ? data.data.id : data.id - - const order = await orderModuleService.retrieveOrder(orderId, { - relations: ["items"], - }) - - // find product medias in the order - const urls = [] - for (const item of order.items) { - const productMedias = await digitalProductModuleService.listProductMedias({ - variant_id: [item.variant_id], + const order = await orderModuleService.retrieveOrder(orderId, { + relations: ["items"], }) - const downloadUrls = await Promise.all( - productMedias.map(async (productMedia) => { - // get the download URL from the file service - return (await fileModuleService.retrieveFile(productMedia.file_key)).url - }) - ) + // find product medias in the order + const urls = [] + for (const item of order.items) { + const productMedias = await digitalProductModuleService + .listProductMedias({ + variant_id: [item.variant_id], + }) + + const downloadUrls = await Promise.all( + productMedias.map(async (productMedia) => { + + // get the download URL from the file service + return (await fileModuleService.retrieveFile( + productMedia.file_key + )).url + }) + ) - urls.push(...downloadUrls) + urls.push(...downloadUrls) + } + + notificationModuleService.createNotifications({ + to: order.email, + template: "digital-download", + channel: "email", + data: { + // any data necessary for your template... + digital_download_urls: urls, + }, + }) } - notificationModuleService.createNotifications({ - to: order.email, - template: "digital-download", - channel: "email", - data: { - // any data necessary for your template... - digital_download_urls: urls, - }, - }) -} - -export const config: SubscriberConfig = { - event: "order.placed", -} -``` - -The `handleOrderPlaced` subscriber function retrieves the order, loops over its items to find digital products and retrieve their download links, then uses the installed notification module provider to send the email, passing the URLs as a data payload. You can customize the sent data based on your template and your use case. + export const config: SubscriberConfig = { + event: "order.placed", + } + ``` + + The `handleOrderPlaced` subscriber function retrieves the order, loops over its items to find digital products and retrieve their download links, then uses the installed notification module provider to send the email, passing the URLs as a data payload. You can customize the sent data based on your template and your use case.
*/} @@ -1114,4 +1189,4 @@ Alternatively, you can build the storefront with your preferred tech stack. text="Learn how to build a storefront for your Medusa application." startIcon={} showLinkIcon={false} -/> +/> \ No newline at end of file diff --git a/www/apps/resources/app/recipes/integrate-ecommerce-stack/page.mdx b/www/apps/resources/app/recipes/integrate-ecommerce-stack/page.mdx index 8ed61e9f3e..122de33607 100644 --- a/www/apps/resources/app/recipes/integrate-ecommerce-stack/page.mdx +++ b/www/apps/resources/app/recipes/integrate-ecommerce-stack/page.mdx @@ -37,11 +37,11 @@ Then, resolve the module's main service in other resources, such as API routes o
-This example showcases how to create a module that integrates to a dummy ERP system. + This example showcases how to create a module that integrates to a dummy ERP system. -Start by creating the directory `src/modules/erp` for your module. + Start by creating the directory `src/modules/erp` for your module. -Then, create the file `src/modules/erp/service.ts` with the following content: + Then, create the file `src/modules/erp/service.ts` with the following content: export const serviceHighlights = [ ["4", "ErpModuleOptions", "The module's expected options."], @@ -56,12 +56,12 @@ export const serviceHighlights = [ import axios, { AxiosInstance } from "axios" import { ProductDTO } from "@medusajs/types" -type ErpModuleOptions = { -apiKey: string -} + type ErpModuleOptions = { + apiKey: string + } -class ErpModuleService { -private client\_: AxiosInstance + class ErpModuleService { + private client_: AxiosInstance constructor({}, { apiKey }: ErpModuleOptions) { this.client_ = axios.create({ @@ -87,52 +87,51 @@ private client\_: AxiosInstance async deleteProduct(id: string) { await this.client_.delete(`/product/${id}`) } + } -} + export default ErpModuleService -export default ErpModuleService + ``` -```` + This creates the module's main service. Few things to note: -This creates the module's main service. Few things to note: + - The module accepts an `apiKey` option, used to authenticate to the dummy ERP system. The module's main service accesses this option in the second parameter of the constructor. + - The module uses axios to create a client in the constructor. The client is used in the service's methods when connecting to the ERP system. If the system you're integrating has an SDK, you can initialize it in the constructor, instead. + - The `getProductData` method retrieves a product's details from the ERP system by sending a `GET` request using the client. + - The `createProduct` method creates a product in the ERP system by sending a `POST` request using the client. + - The `deleteProduct` method deletes a product in the ERP system by sending a `DELETE` request using the client. -- The module accepts an `apiKey` option, used to authenticate to the dummy ERP system. The module's main service accesses this option in the second parameter of the constructor. -- The module uses axios to create a client in the constructor. The client is used in the service's methods when connecting to the ERP system. If the system you're integrating has an SDK, you can initialize it in the constructor, instead. -- The `getProductData` method retrieves a product's details from the ERP system by sending a `GET` request using the client. -- The `createProduct` method creates a product in the ERP system by sending a `POST` request using the client. -- The `deleteProduct` method deletes a product in the ERP system by sending a `DELETE` request using the client. + - + You can store the product's ID in the external system using the `metadata` property of the `Product` data model in the Product Module. Alternatively, you can create a [data model](!docs!/basics/data-models) in your module to store data related to the external system. -You can store the product's ID in the external system using the `metadata` property of the `Product` data model in the Product Module. Alternatively, you can create a [data model](!docs!/basics/data-models) in your module to store data related to the external system. + - + Then, create the module's definition file at `src/modules/erp/index.ts` with the following content: -Then, create the module's definition file at `src/modules/erp/index.ts` with the following content: + ```ts title="src/modules/erp/index.ts" + import ErpModuleService from "./service" -```ts title="src/modules/erp/index.ts" -import ErpModuleService from "./service" + export default { + service: ErpModuleService, + } + ```` -export default { - service: ErpModuleService, -} -```` + Finally, add the module to the `modules` object in `medusa-config.js`: -Finally, add the module to the `modules` object in `medusa-config.js`: - -```js title="medusa-config.js" highlights={[["7", "ERP_API_KEY", "The environment variable holding the API key of the ERP system."]]} -module.exports = defineConfig({ - // ... - modules: { - erpModuleService: { - resolve: "./modules/erp", - options: { - apiKey: process.env.ERP_API_KEY, + ```js title="medusa-config.js" highlights={[["7", "ERP_API_KEY", "The environment variable holding the API key of the ERP system."]]} + module.exports = defineConfig({ + // ... + modules: { + erpModuleService: { + resolve: "./modules/erp", + options: { + apiKey: process.env.ERP_API_KEY, + }, }, }, - }, -}) -``` + }) + ```
diff --git a/www/apps/resources/app/recipes/marketplace/page.mdx b/www/apps/resources/app/recipes/marketplace/page.mdx index 56f46f9bb9..2e13cd6fdf 100644 --- a/www/apps/resources/app/recipes/marketplace/page.mdx +++ b/www/apps/resources/app/recipes/marketplace/page.mdx @@ -40,119 +40,113 @@ Module Relationships is coming soon. -, - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/modules/module-relationships", - title: "Module Relationships", - text: "Create relationships between modules.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/modules/module-relationships", + title: "Module Relationships", + text: "Create relationships between modules.", + startIcon: , + showLinkIcon: false + }, +]} />
-In this section, you’ll create a Marketplace Module with the necessary relationships and functionalities in the main service. + In this section, you’ll create a Marketplace Module with the necessary relationships and functionalities in the main service. + + Start by creating the directory `src/modules/marketplace`. + + Then, create the file `src/modules/marketplace/models/store-user.ts` with the following content: + + ```ts title="src/modules/marketplace/models/store-user.ts" + import { model } from "@medusajs/utils" -Start by creating the directory `src/modules/marketplace`. + const StoreUser = model.define("store_user", { + id: model.id().primaryKey(), + store_id: model.text(), + user_id: model.text(), + }) -Then, create the file `src/modules/marketplace/models/store-user.ts` with the following content: + export default StoreUser + ``` + + This creates a `StoreUser` data model with the `store_id` and `user_id` properties. + + {/* These properties will be used later to establish relationships to the Store and User modules. */} + + Next, create the file `src/modules/marketplace/models/store-product.ts` with the following content: + + ```ts title="src/modules/marketplace/models/store-product.ts" + import { model } from "@medusajs/utils" -```ts title="src/modules/marketplace/models/store-user.ts" -import { model } from "@medusajs/utils" + const StoreProduct = model.define("store_user", { + id: model.id().primaryKey(), + store_id: model.text(), + product_id: model.text(), + }) -const StoreUser = model.define("store_user", { - id: model.id().primaryKey(), - store_id: model.text(), - user_id: model.text(), -}) + export default StoreProduct + ``` + + This creates a `StoreProduct` data model with the `store_id` and `product_id` properties. + + {/* These properties will be used later to establish relationships to the Store and Product modules. */} + + Finally, create the file `src/modules/marketplace/models/store-order.ts` with the following content: + + ```ts title="src/modules/marketplace/models/store-order.ts" + import { model } from "@medusajs/utils" -export default StoreUser -``` + const StoreOrder = model.define("store_user", { + id: model.id().primaryKey(), + store_id: model.text(), + order_id: model.text(), + parent_order_id: model.text(), + }) -This creates a `StoreUser` data model with the `store_id` and `user_id` properties. - -{/* These properties will be used later to establish relationships to the Store and User modules. */} - -Next, create the file `src/modules/marketplace/models/store-product.ts` with the following content: - -```ts title="src/modules/marketplace/models/store-product.ts" -import { model } from "@medusajs/utils" - -const StoreProduct = model.define("store_user", { - id: model.id().primaryKey(), - store_id: model.text(), - product_id: model.text(), -}) - -export default StoreProduct -``` - -This creates a `StoreProduct` data model with the `store_id` and `product_id` properties. - -{/* These properties will be used later to establish relationships to the Store and Product modules. */} - -Finally, create the file `src/modules/marketplace/models/store-order.ts` with the following content: - -```ts title="src/modules/marketplace/models/store-order.ts" -import { model } from "@medusajs/utils" - -const StoreOrder = model.define("store_user", { - id: model.id().primaryKey(), - store_id: model.text(), - order_id: model.text(), - parent_order_id: model.text(), -}) - -export default StoreOrder -``` - -This creates a `StoreOrder` data model with the `store_id`, `order_id`, and `parent_order_id` properties. - -{/* The `store_id` and `order_id` properties will be used to establish relationships to the Store and Order modules. You’ll learn about the use of `parent_order_id` in a later section. */} - -To reflect these changes on the database, create the migration `src/modules/marketplace/migrations/Migration20240514143248.ts` with the following content: - -```ts title="src/modules/marketplace/migrations/Migration20240514143248.ts" -import { Migration } from "@mikro-orm/migrations" - -export class Migration20240514143248 extends Migration { - async up(): Promise { - this.addSql( - 'create table if not exists "store_order" ("id" varchar(255) not null, "store_id" text not null, "order_id" text not null, "parent_order_id" text not null, constraint "store_order_pkey" primary key ("id"));' - ) - - this.addSql( - 'create table if not exists "store_product" ("id" varchar(255) not null, "store_id" text not null, "product_id" text not null, constraint "store_product_pkey" primary key ("id"));' - ) - - this.addSql( - 'create table if not exists "store_user" ("id" varchar(255) not null, "store_id" text not null, "user_id" text not null, constraint "store_user_pkey" primary key ("id"));' - ) + export default StoreOrder + ``` + + This creates a `StoreOrder` data model with the `store_id`, `order_id`, and `parent_order_id` properties. + + {/* The `store_id` and `order_id` properties will be used to establish relationships to the Store and Order modules. You’ll learn about the use of `parent_order_id` in a later section. */} + + To reflect these changes on the database, create the migration `src/modules/marketplace/migrations/Migration20240514143248.ts` with the following content: + + ```ts title="src/modules/marketplace/migrations/Migration20240514143248.ts" + import { Migration } from "@mikro-orm/migrations" + + export class Migration20240514143248 extends Migration { + + async up(): Promise { + this.addSql("create table if not exists \"store_order\" (\"id\" varchar(255) not null, \"store_id\" text not null, \"order_id\" text not null, \"parent_order_id\" text not null, constraint \"store_order_pkey\" primary key (\"id\"));") + + this.addSql("create table if not exists \"store_product\" (\"id\" varchar(255) not null, \"store_id\" text not null, \"product_id\" text not null, constraint \"store_product_pkey\" primary key (\"id\"));") + + this.addSql("create table if not exists \"store_user\" (\"id\" varchar(255) not null, \"store_id\" text not null, \"user_id\" text not null, constraint \"store_user_pkey\" primary key (\"id\"));") + } + + async down(): Promise { + this.addSql("drop table if exists \"store_order\" cascade;") + + this.addSql("drop table if exists \"store_product\" cascade;") + + this.addSql("drop table if exists \"store_user\" cascade;") + } + } - - async down(): Promise { - this.addSql('drop table if exists "store_order" cascade;') - - this.addSql('drop table if exists "store_product" cascade;') - - this.addSql('drop table if exists "store_user" cascade;') - } -} -``` - -You’ll run the migration after registering the module in the Medusa configurations. - -Then, create the module’s main service at `src/modules/marketplace/service.ts` with the following content: + ``` + + You’ll run the migration after registering the module in the Medusa configurations. + + Then, create the module’s main service at `src/modules/marketplace/service.ts` with the following content: export const mainServiceHighlights = [ ["6", "MedusaService", "Extends the service factory to generate data management features."] @@ -164,54 +158,54 @@ export const mainServiceHighlights = [ import StoreProduct from "./models/store-product" import StoreOrder from "./models/store-order" -class MarketplaceModuleService extends MedusaService({ -StoreUser, -StoreProduct, -StoreOrder, -}){ -// TODO add custom methods -} + class MarketplaceModuleService extends MedusaService({ + StoreUser, + StoreProduct, + StoreOrder, + }){ + // TODO add custom methods + } -export default MarketplaceModuleService + export default MarketplaceModuleService + ``` + + The module’s main service extends the service factory to generate data management features for the `StoreUser`, `StoreProduct`, and `StoreOrder` data models. + + Finally, create the module definition at `src/modules/marketplace/index.ts` with the following content: + + ```ts title="src/modules/marketplace/index.ts" + import MarketplaceModuleService from "./service" + import { Module } from "@medusajs/utils" -```` - -The module’s main service extends the service factory to generate data management features for the `StoreUser`, `StoreProduct`, and `StoreOrder` data models. - -Finally, create the module definition at `src/modules/marketplace/index.ts` with the following content: - -```ts title="src/modules/marketplace/index.ts" -import MarketplaceModuleService from "./service" - -export default { - service: MarketplaceModuleService, -} -```` - -To use the module, add it to the `modules` object in `medusa-config.js`: - -```js title="medusa-config.js" -module.exports = defineConfig({ - // ... - modules: { - marketplaceModuleService: { - resolve: "./modules/marketplace", - definition: { - isQueryable: true, + export default Module("marketplace", { + service: MarketplaceModuleService, + }) + ``` + + To use the module, add it to the `modules` object in `medusa-config.js`: + + ```js title="medusa-config.js" + module.exports = defineConfig({ + // ... + modules: { + marketplaceModuleService: { + resolve: "./modules/marketplace", + definition: { + isQueryable: true, + }, }, }, - }, -}) -``` - -Then, run the migrations of the module: - -```bash npm2yarn -npx medusa migrations run -``` + }) + ``` + + Then, run the migrations of the module: + + ```bash npm2yarn + npx medusa migrations run + ```
- + --- @@ -234,10 +228,10 @@ To attach admin users to their own stores, create a subscriber that listens to t showLinkIcon={false} /> -{/\*
- -Create the file `src/subscribers/user-created.ts` with the following content: +{/*
+ Create the file `src/subscribers/user-created.ts` with the following content: + export const userSubscriberHighlights = [ ["13", "data", "The event data payload with the created user's ID."], ["27", "retrieveUser", "Retrieve the created user."], @@ -257,55 +251,53 @@ export const userSubscriberHighlights = [ } from "@medusajs/types" import MarketplaceModuleService from "../modules/marketplace/service" -export default async function userCreatedHandler({ -data, -container, -}: SubscriberArgs<{ id: string }>) { -const { id } = "data" in data ? data.data : data -const userModuleService: IUserModuleService = container.resolve( -ModuleRegistrationName.USER -) -const storeModuleService: IStoreModuleService = container.resolve( -ModuleRegistrationName.STORE -) -const marketplaceModuleService: MarketplaceModuleService = container.resolve( -"marketplaceModuleService" -) + export default async function userCreatedHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const { id } = "data" in data ? data.data : data + const userModuleService: IUserModuleService = container.resolve( + ModuleRegistrationName.USER + ) + const storeModuleService: IStoreModuleService = container.resolve( + ModuleRegistrationName.STORE + ) + const marketplaceModuleService: MarketplaceModuleService = container.resolve( + "marketplaceModuleService" + ) const user = await userModuleService.retrieveUser(id) const store = await storeModuleService.createStores({ name: `${user.first_name}'s Store`, }) - + const storeUser = await marketplaceModuleService.createStoreUsers({ store_id: store.id, user_id: user.id, }) console.log(`Created StoreUser ${storeUser.id}`) + } -} - -export const config: SubscriberConfig = { -event: "user.created", -} - -```` - -This adds a subscriber to the `user.created` event. In the subscriber, you: - -- Retrieve the created user. The created user’s ID is passed in the event’s data payload. -- Create a store for that user using the Store Module. -- Create a relationship between the user and the store by creating a `StoreUser` record. - -To test it out, use the `medusa user` command to create a user: - -```bash npm2yarn -npx medusa user -e my-admin@medusa-test.com -p supersecret -```` - -At the end of the output, you should see the message `Created StoreUser {store_user_id}` where the `{store_user_id}` is the ID of the created `StoreUser`. + export const config: SubscriberConfig = { + event: "user.created", + } + ``` + + This adds a subscriber to the `user.created` event. In the subscriber, you: + + - Retrieve the created user. The created user’s ID is passed in the event’s data payload. + - Create a store for that user using the Store Module. + - Create a relationship between the user and the store by creating a `StoreUser` record. + + To test it out, use the `medusa user` command to create a user: + + ```bash npm2yarn + npx medusa user -e my-admin@medusa-test.com -p supersecret + ``` + + At the end of the output, you should see the message `Created StoreUser {store_user_id}` where the `{store_user_id}` is the ID of the created `StoreUser`.
*/} @@ -330,9 +322,9 @@ Similar to the previous section, to attach products to stores, create a subscrib showLinkIcon={false} /> -{/\*
+{/*
-Create the file `src/subscribers/product-created.ts` with the following content: + Create the file `src/subscribers/product-created.ts` with the following content: export const productSubscriberHighlights = [ ["24", "retrieveProduct", "Retrieve the created product."], @@ -351,15 +343,15 @@ export const productSubscriberHighlights = [ } from "@medusajs/types" import MarketplaceModuleService from "../modules/marketplace/service" -export default async function productCreateHandler({ -data, -container, -}: SubscriberArgs<{ id: string }>) { -const { id } = "data" in data ? data.data : data -const productModuleService: IProductModuleService = container.resolve( -ModuleRegistrationName.PRODUCT -) - + export default async function productCreateHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const { id } = "data" in data ? data.data : data + const productModuleService: IProductModuleService = container.resolve( + ModuleRegistrationName.PRODUCT + ) + const marketplaceModuleService: MarketplaceModuleService = container.resolve( "marketplaceModuleService" ) @@ -374,45 +366,43 @@ ModuleRegistrationName.PRODUCT store_id: product.metadata.store_id as string, product_id: id, }) + } -} - -export const config: SubscriberConfig = { -event: "product.created", -} - -```` - -This adds a subscriber to the `product.created` event. In the subscriber, you: - -- Retrieve the created product. -- This subscriber requires the store ID to be set in `product.metadata.store_id`. If not, the subscriber ends execution. -- If the store ID is found, create a relationship between the product and the store by creating a `StoreProduct` record. - -To test it out, start the Medusa application: - -```bash npm2yarn -npm run dev -```` - -Then, send an authenticated `POST` request to `/admin/products`: - -```bash -curl -X POST 'http://localhost:9000/admin/products' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {jwt_token}' \ ---data '{ - "title": "My Shirt 13", - "metadata": { - "store_id": "store_01HXV74JCCAHA3F2X3EWZTZG5R" - } -}' -``` - -This returns the created product. In the next section, you’ll implement the API route to fetch the products of a store. + export const config: SubscriberConfig = { + event: "product.created", + } + ``` + + This adds a subscriber to the `product.created` event. In the subscriber, you: + + - Retrieve the created product. + - This subscriber requires the store ID to be set in `product.metadata.store_id`. If not, the subscriber ends execution. + - If the store ID is found, create a relationship between the product and the store by creating a `StoreProduct` record. + + To test it out, start the Medusa application: + + ```bash npm2yarn + npm run dev + ``` + + Then, send an authenticated `POST` request to `/admin/products`: + + ```bash + curl -X POST 'http://localhost:9000/admin/products' \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer {jwt_token}' \ + --data '{ + "title": "My Shirt 13", + "metadata": { + "store_id": "store_01HXV74JCCAHA3F2X3EWZTZG5R" + } + }' + ``` + + This returns the created product. In the next section, you’ll implement the API route to fetch the products of a store.
*/} - + --- @@ -426,28 +416,26 @@ Retrieving module relationship data using the remote query is coming soon. -, - showLinkIcon: false, - }, - { - href: "!docs!/advanced-development/modules/remote-query", - title: "Remote Query", - text: "Use the remote query to fetch data across modules.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/modules/remote-query", + title: "Remote Query", + text: "Use the remote query to fetch data across modules.", + startIcon: , + showLinkIcon: false + }, +]} /> -{/\*
+{/*
-Create the file `src/api/admin/marketplace/products/route.ts` with the following content: + Create the file `src/api/admin/marketplace/products/route.ts` with the following content: export const productRoutesHighlights = [ ["26", "storeUsers", "Retrieve the store of the logged-in user."], @@ -469,17 +457,17 @@ export const productRoutesHighlights = [ import MarketplaceModuleService from "../../../../modules/marketplace/service" -export async function GET( -req: AuthenticatedMedusaRequest, -res: MedusaResponse -): Promise { -const marketplaceModuleService: MarketplaceModuleService = -req.scope.resolve( -"marketplaceModuleService" -) -const remoteQuery: RemoteQueryFunction = req.scope.resolve( -ContainerRegistrationKeys.REMOTE_QUERY -) + export async function GET( + req: AuthenticatedMedusaRequest, + res: MedusaResponse + ): Promise { + const marketplaceModuleService: MarketplaceModuleService = + req.scope.resolve( + "marketplaceModuleService" + ) + const remoteQuery: RemoteQueryFunction = req.scope.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) const storeUsers = await marketplaceModuleService .listStoreUsers({ @@ -511,53 +499,51 @@ ContainerRegistrationKeys.REMOTE_QUERY store_id: storeUsers[0].store_id, products: result.map((data) => data.product), }) - -} - -```` - -This creates a `GET` API route at `/admin/marketplace/products`. In the API route, you: - -- Retrieve the store of the logged-in user. -- Build a query that retrieves the products of that store. -- Retrieve the products using remote query and return them. - -Next, create the file `src/api/middlewares.ts` with the following content: - -```ts title="src/api/middlewares.ts" -import { - MiddlewaresConfig, - authenticate, -} from "@medusajs/medusa" - -export const config: MiddlewaresConfig = { - routes: [ - { - matcher: "/admin/marketplace*", - middlewares: [ - authenticate( - "admin", - ["session", "bearer", "api-key"] - ), - ], - }, - ], -} -```` - -This ensures that only authenticated admin users can access your API route. - -To test it out, start the Medusa application. Then, send a `GET` request to `/admin/marketplace/products`: - -```bash -curl 'localhost:9000/admin/marketplace/products' \ --H 'Authorization: Bearer {jwt_token}' -``` - -This will return the product you created in the previous section, if the `{jwt_token}` belongs to the user of the same store ID. + } + ``` + + This creates a `GET` API route at `/admin/marketplace/products`. In the API route, you: + + - Retrieve the store of the logged-in user. + - Build a query that retrieves the products of that store. + - Retrieve the products using remote query and return them. + + Next, create the file `src/api/middlewares.ts` with the following content: + + ```ts title="src/api/middlewares.ts" + import { + MiddlewaresConfig, + authenticate, + } from "@medusajs/medusa" + + export const config: MiddlewaresConfig = { + routes: [ + { + matcher: "/admin/marketplace*", + middlewares: [ + authenticate( + "admin", + ["session", "bearer", "api-key"] + ), + ], + }, + ], + } + ``` + + This ensures that only authenticated admin users can access your API route. + + To test it out, start the Medusa application. Then, send a `GET` request to `/admin/marketplace/products`: + + ```bash + curl 'localhost:9000/admin/marketplace/products' \ + -H 'Authorization: Bearer {jwt_token}' + ``` + + This will return the product you created in the previous section, if the `{jwt_token}` belongs to the user of the same store ID.
*/} - + --- @@ -579,9 +565,9 @@ The `order.placed` event is still not emitted in Medusa V2. showLinkIcon={false} /> -{/\*
+{/*
-Create the file `src/subscribers/order-created.ts` with the following content: + Create the file `src/subscribers/order-created.ts` with the following content: export const orderSubscriberHighlights = [ ["35", "", "Loop over the created order’s items."], @@ -605,17 +591,17 @@ export const orderSubscriberHighlights = [ from "../modules/marketplace/service" import { createOrdersWorkflow } from "@medusajs/core-flows" -export default async function orderCreatedHandler({ -data, -container, -}: SubscriberArgs<{ id: string }>) { -const { id } = "data" in data ? data.data : data -const orderModuleService: IOrderModuleService = -container.resolve( -ModuleRegistrationName.ORDER -) - - const marketplaceModuleService: MarketplaceModuleService = + export default async function orderCreatedHandler({ + data, + container, + }: SubscriberArgs<{ id: string }>) { + const { id } = "data" in data ? data.data : data + const orderModuleService: IOrderModuleService = + container.resolve( + ModuleRegistrationName.ORDER + ) + + const marketplaceModuleService: MarketplaceModuleService = container.resolve( "marketplaceModuleService" ) @@ -637,7 +623,7 @@ ModuleRegistrationName.ORDER } const storeId = storeProduct[0].store_id - + if (!storeToOrders[storeId]) { const { id, ...orderDetails } = order storeToOrders[storeId] = { @@ -658,10 +644,10 @@ ModuleRegistrationName.ORDER } if ( - storeToOrdersKeys.length === 1 && + storeToOrdersKeys.length === 1 && storeToOrders[0].items.length === order.items.length ) { - // The order is composed of items from one store, so + // The order is composed of items from one store, so // associate the order as-is with the store. await marketplaceModuleService.createStoreOrders({ store_id: storeToOrdersKeys[0], @@ -686,28 +672,26 @@ ModuleRegistrationName.ORDER }) }) ) + } -} - -export const config: SubscriberConfig = { -event: "order.placed", -} - -```` - -This adds a subscriber to the `order.placed` event. In the subscriber, you: - -- Loop over the created order’s items. -- Group the items by their store ID. -- If the items have the same store ID, then associate the created order with the store. -- If there are items from more than one store in the order, create child orders and associate each of them with the store. Here, you use the `parent_order_id` property in the `StoreOrder` data model to point to the original order. - -To test this out, create an order in your store. That will run the subscriber and create the child orders. - -The next section covers how to retrieve the store’s orders. + export const config: SubscriberConfig = { + event: "order.placed", + } + ``` + + This adds a subscriber to the `order.placed` event. In the subscriber, you: + + - Loop over the created order’s items. + - Group the items by their store ID. + - If the items have the same store ID, then associate the created order with the store. + - If there are items from more than one store in the order, create child orders and associate each of them with the store. Here, you use the `parent_order_id` property in the `StoreOrder` data model to point to the original order. + + To test this out, create an order in your store. That will run the subscriber and create the child orders. + + The next section covers how to retrieve the store’s orders.
*/} - + --- @@ -722,95 +706,95 @@ Retrieving module relationship data using the remote query is coming soon. , - showLinkIcon: false -}, -{ - href: "!docs!/advanced-development/modules/remote-query", - title: "Remote Query", - text: "Use the remote query to fetch data across modules.", - startIcon: , - showLinkIcon: false -}, + { + href: "!docs!/basics/api-routes", + title: "API Routes", + text: "Learn how to create an API Route in Medusa.", + startIcon: , + showLinkIcon: false + }, + { + href: "!docs!/advanced-development/modules/remote-query", + title: "Remote Query", + text: "Use the remote query to fetch data across modules.", + startIcon: , + showLinkIcon: false + }, ]} /> {/*
-Create the file `src/api/admin/marketplace/orders/route.ts` with the following content: + Create the file `src/api/admin/marketplace/orders/route.ts` with the following content: export const orderRoutesHighlights = [ -["25", "", "Retrieve the store of the logged-in user."], -["29", "", "Build a query that retrieves the orders of that store."], -["41", "", "Retrieve the orders using remote query."] + ["25", "", "Retrieve the store of the logged-in user."], + ["29", "", "Build a query that retrieves the orders of that store."], + ["41", "", "Retrieve the orders using remote query."] ] + + ```ts title="src/api/admin/marketplace/orders/route.ts" highlights={orderRoutesHighlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" + import { + AuthenticatedMedusaRequest, + MedusaResponse, + } from "@medusajs/medusa" + import { RemoteQueryFunction } from "@medusajs/modules-sdk" + import { + remoteQueryObjectFromString, + ContainerRegistrationKeys, + } from "@medusajs/utils" + import MarketplaceModuleService + from "../../../../modules/marketplace/service" -```ts title="src/api/admin/marketplace/orders/route.ts" highlights={orderRoutesHighlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" -import { - AuthenticatedMedusaRequest, - MedusaResponse, -} from "@medusajs/medusa" -import { RemoteQueryFunction } from "@medusajs/modules-sdk" -import { - remoteQueryObjectFromString, - ContainerRegistrationKeys, -} from "@medusajs/utils" -import MarketplaceModuleService - from "../../../../modules/marketplace/service" - -export async function GET( - req: AuthenticatedMedusaRequest, - res: MedusaResponse -): Promise { - const marketplaceModuleService: MarketplaceModuleService = - req.scope.resolve( - "marketplaceModuleService" + export async function GET( + req: AuthenticatedMedusaRequest, + res: MedusaResponse + ): Promise { + const marketplaceModuleService: MarketplaceModuleService = + req.scope.resolve( + "marketplaceModuleService" + ) + const remoteQuery: RemoteQueryFunction = req.scope.resolve( + ContainerRegistrationKeys.REMOTE_QUERY ) - const remoteQuery: RemoteQueryFunction = req.scope.resolve( - ContainerRegistrationKeys.REMOTE_QUERY - ) - const storeUsers = await marketplaceModuleService.listStoreUsers({ - user_id: req.auth_context.actor_id, - }) + const storeUsers = await marketplaceModuleService.listStoreUsers({ + user_id: req.auth_context.actor_id, + }) - const query = remoteQueryObjectFromString({ - entryPoint: "store_order", - fields: [ - "order.*", - ], - variables: { - filters: { - store_id: storeUsers[0].store_id, + const query = remoteQueryObjectFromString({ + entryPoint: "store_order", + fields: [ + "order.*", + ], + variables: { + filters: { + store_id: storeUsers[0].store_id, + }, }, - }, - }) + }) - const result = await remoteQuery(query) + const result = await remoteQuery(query) - res.json({ - orders: result.map((data) => data.order), - }) -} -```` - -This creates a `GET` API route at `/admin/marketplace/orders`. In the API route, you: - -- Retrieve the store of the logged-in user. -- Build a query that retrieves the orders of that store. -- Retrieve the orders using remote query and return them. - -To test it out, start the Medusa application. Then, send a `GET` request to `/admin/marketplace/orders`: - -```bash -curl 'localhost:9000/admin/marketplace/orders' \ --H 'Authorization: Bearer {jwt_token}' -``` - -This will return the orders you created in the previous section if the `{jwt_token}` belongs to the user of the same store ID. + res.json({ + orders: result.map((data) => data.order), + }) + } + ``` + + This creates a `GET` API route at `/admin/marketplace/orders`. In the API route, you: + + - Retrieve the store of the logged-in user. + - Build a query that retrieves the orders of that store. + - Retrieve the orders using remote query and return them. + + To test it out, start the Medusa application. Then, send a `GET` request to `/admin/marketplace/orders`: + + ```bash + curl 'localhost:9000/admin/marketplace/orders' \ + -H 'Authorization: Bearer {jwt_token}' + ``` + + This will return the orders you created in the previous section if the `{jwt_token}` belongs to the user of the same store ID.
*/} @@ -822,21 +806,19 @@ Medusa provides a Next.js storefront to use with your application. You can eithe For example, you can create an API route that retrieves available stores and another API route that retrieves the products of each store using the remote query as done in previous sections. -, - showLinkIcon: false, - }, - { - href: "!docs!/storefront-development/tips", - title: "Storefront Tips", - text: "Find tips on developing a custom storefront.", - startIcon: , - showLinkIcon: false, - }, - ]} -/> +, + showLinkIcon: false + }, + { + href: "!docs!/storefront-development/tips", + title: "Storefront Tips", + text: "Find tips on developing a custom storefront.", + startIcon: , + showLinkIcon: false + }, +]} /> \ No newline at end of file diff --git a/www/apps/resources/components/Navbar/index.tsx b/www/apps/resources/components/Navbar/index.tsx index 8602b9707b..d25830efab 100644 --- a/www/apps/resources/components/Navbar/index.tsx +++ b/www/apps/resources/components/Navbar/index.tsx @@ -30,6 +30,7 @@ const Navbar = () => { mobileSidebarOpen, }} isLoading={false} + showSearchOpener /> ) } diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs index 1df136231b..d74cbf8987 100644 --- a/www/apps/resources/generated/files-map.mjs +++ b/www/apps/resources/generated/files-map.mjs @@ -1,4 +1,8 @@ export const filesMap = [ + { + "filePath": "/www/apps/resources/app/_events-reference/page.mdx", + "pathname": "/_events-reference" + }, { "filePath": "/www/apps/resources/app/admin-widget-injection-zones/page.mdx", "pathname": "/admin-widget-injection-zones" @@ -79,18 +83,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx", "pathname": "/architectural-modules/workflow-engine/redis" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/api-key/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/api-key/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/api-key/_events/page.mdx", + "pathname": "/commerce-modules/api-key/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/api-key/concepts/page.mdx", "pathname": "/commerce-modules/api-key/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/api-key/events/_events-table/page.mdx", - "pathname": "/commerce-modules/api-key/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/api-key/events/page.mdx", - "pathname": "/commerce-modules/api-key/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/api-key/examples/page.mdx", "pathname": "/commerce-modules/api-key/examples" @@ -103,6 +107,14 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/api-key/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/api-key/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/auth/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/auth/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/auth/_events/page.mdx", + "pathname": "/commerce-modules/auth/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/auth/auth-flows/page.mdx", "pathname": "/commerce-modules/auth/auth-flows" @@ -131,14 +143,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx", "pathname": "/commerce-modules/auth/create-actor-type" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/auth/events/_events-table/page.mdx", - "pathname": "/commerce-modules/auth/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/auth/events/page.mdx", - "pathname": "/commerce-modules/auth/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/auth/examples/page.mdx", "pathname": "/commerce-modules/auth/examples" @@ -151,18 +155,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/auth/page.mdx", "pathname": "/commerce-modules/auth" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/cart/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/cart/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/cart/_events/page.mdx", + "pathname": "/commerce-modules/cart/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/cart/concepts/page.mdx", "pathname": "/commerce-modules/cart/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/cart/events/_events-table/page.mdx", - "pathname": "/commerce-modules/cart/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/cart/events/page.mdx", - "pathname": "/commerce-modules/cart/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/cart/examples/page.mdx", "pathname": "/commerce-modules/cart/examples" @@ -184,12 +188,12 @@ export const filesMap = [ "pathname": "/commerce-modules/cart/tax-lines" }, { - "filePath": "/www/apps/resources/app/commerce-modules/currency/events/_events-table/page.mdx", - "pathname": "/commerce-modules/currency/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/currency/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/currency/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/currency/events/page.mdx", - "pathname": "/commerce-modules/currency/events" + "filePath": "/www/apps/resources/app/commerce-modules/currency/_events/page.mdx", + "pathname": "/commerce-modules/currency/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/currency/examples/page.mdx", @@ -203,18 +207,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/currency/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/currency/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/customer/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/customer/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/customer/_events/page.mdx", + "pathname": "/commerce-modules/customer/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/customer/customer-accounts/page.mdx", "pathname": "/commerce-modules/customer/customer-accounts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/customer/events/_events-table/page.mdx", - "pathname": "/commerce-modules/customer/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/customer/events/page.mdx", - "pathname": "/commerce-modules/customer/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/customer/examples/page.mdx", "pathname": "/commerce-modules/customer/examples" @@ -227,18 +231,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/customer/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/customer/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/fulfillment/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/_events/page.mdx", + "pathname": "/commerce-modules/fulfillment/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/concepts/page.mdx", "pathname": "/commerce-modules/fulfillment/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/events/_events-table/page.mdx", - "pathname": "/commerce-modules/fulfillment/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/events/page.mdx", - "pathname": "/commerce-modules/fulfillment/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/fulfillment-provider/page.mdx", "pathname": "/commerce-modules/fulfillment/fulfillment-provider" @@ -263,18 +267,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/fulfillment/shipping-option/page.mdx", "pathname": "/commerce-modules/fulfillment/shipping-option" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/inventory/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/inventory/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/inventory/_events/page.mdx", + "pathname": "/commerce-modules/inventory/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/inventory/concepts/page.mdx", "pathname": "/commerce-modules/inventory/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/inventory/events/_events-table/page.mdx", - "pathname": "/commerce-modules/inventory/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/inventory/events/page.mdx", - "pathname": "/commerce-modules/inventory/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/inventory/examples/page.mdx", "pathname": "/commerce-modules/inventory/examples" @@ -291,18 +295,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/inventory/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/inventory/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/order/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/order/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/order/_events/page.mdx", + "pathname": "/commerce-modules/order/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/order/concepts/page.mdx", "pathname": "/commerce-modules/order/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/order/events/_events-table/page.mdx", - "pathname": "/commerce-modules/order/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/order/events/page.mdx", - "pathname": "/commerce-modules/order/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/order/order-change/page.mdx", "pathname": "/commerce-modules/order/order-change" @@ -336,12 +340,12 @@ export const filesMap = [ "pathname": "/commerce-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/payment/events/_events-table/page.mdx", - "pathname": "/commerce-modules/payment/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/payment/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/payment/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/payment/events/page.mdx", - "pathname": "/commerce-modules/payment/events" + "filePath": "/www/apps/resources/app/commerce-modules/payment/_events/page.mdx", + "pathname": "/commerce-modules/payment/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/payment/examples/page.mdx", @@ -387,18 +391,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/payment/webhook-events/page.mdx", "pathname": "/commerce-modules/payment/webhook-events" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/pricing/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/pricing/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/pricing/_events/page.mdx", + "pathname": "/commerce-modules/pricing/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/pricing/concepts/page.mdx", "pathname": "/commerce-modules/pricing/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/pricing/events/_events-table/page.mdx", - "pathname": "/commerce-modules/pricing/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/pricing/events/page.mdx", - "pathname": "/commerce-modules/pricing/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/pricing/examples/page.mdx", "pathname": "/commerce-modules/pricing/examples" @@ -420,12 +424,12 @@ export const filesMap = [ "pathname": "/commerce-modules/pricing/relations-to-other-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/product/events/_events-table/page.mdx", - "pathname": "/commerce-modules/product/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/product/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/product/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/product/events/page.mdx", - "pathname": "/commerce-modules/product/events" + "filePath": "/www/apps/resources/app/commerce-modules/product/_events/page.mdx", + "pathname": "/commerce-modules/product/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/product/examples/page.mdx", @@ -439,6 +443,14 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/product/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/product/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/promotion/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/promotion/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/promotion/_events/page.mdx", + "pathname": "/commerce-modules/promotion/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/promotion/actions/page.mdx", "pathname": "/commerce-modules/promotion/actions" @@ -455,14 +467,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/promotion/concepts/page.mdx", "pathname": "/commerce-modules/promotion/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/promotion/events/_events-table/page.mdx", - "pathname": "/commerce-modules/promotion/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/promotion/events/page.mdx", - "pathname": "/commerce-modules/promotion/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/promotion/examples/page.mdx", "pathname": "/commerce-modules/promotion/examples" @@ -476,12 +480,12 @@ export const filesMap = [ "pathname": "/commerce-modules/promotion/relations-to-other-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/region/events/_events-table/page.mdx", - "pathname": "/commerce-modules/region/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/region/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/region/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/region/events/page.mdx", - "pathname": "/commerce-modules/region/events" + "filePath": "/www/apps/resources/app/commerce-modules/region/_events/page.mdx", + "pathname": "/commerce-modules/region/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/region/examples/page.mdx", @@ -496,12 +500,12 @@ export const filesMap = [ "pathname": "/commerce-modules/region/relations-to-other-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/events/_events-table/page.mdx", - "pathname": "/commerce-modules/sales-channel/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/sales-channel/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx", - "pathname": "/commerce-modules/sales-channel/events" + "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/_events/page.mdx", + "pathname": "/commerce-modules/sales-channel/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/examples/page.mdx", @@ -519,18 +523,18 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/commerce-modules/sales-channel/relations-to-other-modules/page.mdx", "pathname": "/commerce-modules/sales-channel/relations-to-other-modules" }, + { + "filePath": "/www/apps/resources/app/commerce-modules/stock-location/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/stock-location/_events/_events-table" + }, + { + "filePath": "/www/apps/resources/app/commerce-modules/stock-location/_events/page.mdx", + "pathname": "/commerce-modules/stock-location/_events" + }, { "filePath": "/www/apps/resources/app/commerce-modules/stock-location/concepts/page.mdx", "pathname": "/commerce-modules/stock-location/concepts" }, - { - "filePath": "/www/apps/resources/app/commerce-modules/stock-location/events/_events-table/page.mdx", - "pathname": "/commerce-modules/stock-location/events/_events-table" - }, - { - "filePath": "/www/apps/resources/app/commerce-modules/stock-location/events/page.mdx", - "pathname": "/commerce-modules/stock-location/events" - }, { "filePath": "/www/apps/resources/app/commerce-modules/stock-location/examples/page.mdx", "pathname": "/commerce-modules/stock-location/examples" @@ -544,12 +548,12 @@ export const filesMap = [ "pathname": "/commerce-modules/stock-location/relations-to-other-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/store/events/_events-table/page.mdx", - "pathname": "/commerce-modules/store/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/store/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/store/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/store/events/page.mdx", - "pathname": "/commerce-modules/store/events" + "filePath": "/www/apps/resources/app/commerce-modules/store/_events/page.mdx", + "pathname": "/commerce-modules/store/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/store/examples/page.mdx", @@ -564,12 +568,12 @@ export const filesMap = [ "pathname": "/commerce-modules/store/relations-to-other-modules" }, { - "filePath": "/www/apps/resources/app/commerce-modules/tax/events/_events-table/page.mdx", - "pathname": "/commerce-modules/tax/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/tax/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/tax/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/tax/events/page.mdx", - "pathname": "/commerce-modules/tax/events" + "filePath": "/www/apps/resources/app/commerce-modules/tax/_events/page.mdx", + "pathname": "/commerce-modules/tax/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/tax/examples/page.mdx", @@ -596,12 +600,12 @@ export const filesMap = [ "pathname": "/commerce-modules/tax/tax-region" }, { - "filePath": "/www/apps/resources/app/commerce-modules/user/events/_events-table/page.mdx", - "pathname": "/commerce-modules/user/events/_events-table" + "filePath": "/www/apps/resources/app/commerce-modules/user/_events/_events-table/page.mdx", + "pathname": "/commerce-modules/user/_events/_events-table" }, { - "filePath": "/www/apps/resources/app/commerce-modules/user/events/page.mdx", - "pathname": "/commerce-modules/user/events" + "filePath": "/www/apps/resources/app/commerce-modules/user/_events/page.mdx", + "pathname": "/commerce-modules/user/_events" }, { "filePath": "/www/apps/resources/app/commerce-modules/user/examples/page.mdx", @@ -671,10 +675,6 @@ export const filesMap = [ "filePath": "/www/apps/resources/app/deployment/storefront/vercel/page.mdx", "pathname": "/deployment/storefront/vercel" }, - { - "filePath": "/www/apps/resources/app/events-reference/page.mdx", - "pathname": "/events-reference" - }, { "filePath": "/www/apps/resources/app/favicon.ico", "pathname": "/" diff --git a/www/apps/resources/generated/sidebar.mjs b/www/apps/resources/generated/sidebar.mjs index 411575bac4..fd1b4ad6d9 100644 --- a/www/apps/resources/generated/sidebar.mjs +++ b/www/apps/resources/generated/sidebar.mjs @@ -162,13 +162,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/api-key/events", - "title": "Events Reference", - "children": [] } ] } @@ -371,13 +364,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/auth/events", - "title": "Events Reference", - "children": [] } ] } @@ -880,13 +866,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/cart/events", - "title": "Events Reference", - "children": [] } ] } @@ -990,13 +969,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/currency/events", - "title": "Events Reference", - "children": [] } ] } @@ -1275,13 +1247,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/customer/events", - "title": "Events Reference", - "children": [] } ] } @@ -1931,13 +1896,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/fulfillment/events", - "title": "Events Reference", - "children": [] } ] } @@ -2293,13 +2251,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/inventory/events", - "title": "Events Reference", - "children": [] } ] } @@ -3187,13 +3138,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/order/events", - "title": "Events Reference", - "children": [] } ] } @@ -3557,13 +3501,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/payment/events", - "title": "Events Reference", - "children": [] } ] } @@ -3947,13 +3884,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/pricing/events", - "title": "Events Reference", - "children": [] } ] } @@ -4533,13 +4463,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/product/events", - "title": "Events Reference", - "children": [] } ] } @@ -4881,13 +4804,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/promotion/events", - "title": "Events Reference", - "children": [] } ] } @@ -5061,13 +4977,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/region/events", - "title": "Events Reference", - "children": [] } ] } @@ -5220,13 +5129,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/sales-channel/events", - "title": "Events Reference", - "children": [] } ] } @@ -5386,13 +5288,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/stock-location/events", - "title": "Events Reference", - "children": [] } ] } @@ -5545,13 +5440,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/store/events", - "title": "Events Reference", - "children": [] } ] } @@ -5823,13 +5711,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/tax/events", - "title": "Events Reference", - "children": [] } ] } @@ -6052,13 +5933,6 @@ export const generatedSidebar = [ ] } ] - }, - { - "loaded": true, - "isPathHref": true, - "path": "/commerce-modules/user/events", - "title": "Events Reference", - "children": [] } ] } @@ -7917,13 +7791,6 @@ export const generatedSidebar = [ "title": "Medusa Container Resources", "children": [] }, - { - "loaded": true, - "isPathHref": true, - "path": "/events-reference", - "title": "Events", - "children": [] - }, { "loaded": true, "isPathHref": true, diff --git a/www/apps/resources/sidebar.mjs b/www/apps/resources/sidebar.mjs index 7e01b294e2..48520d3e53 100644 --- a/www/apps/resources/sidebar.mjs +++ b/www/apps/resources/sidebar.mjs @@ -65,10 +65,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/api-key/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/api-key/events", + // title: "Events Reference", + // }, ], }, ], @@ -156,10 +156,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/auth/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/auth/events", + // title: "Events Reference", + // }, ], }, ], @@ -224,10 +224,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/cart/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/cart/events", + // title: "Events Reference", + // }, ], }, ], @@ -280,10 +280,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/currency/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/currency/events", + // title: "Events Reference", + // }, ], }, ], @@ -340,10 +340,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/customer/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/customer/events", + // title: "Events Reference", + // }, ], }, ], @@ -417,10 +417,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/fulfillment/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/fulfillment/events", + // title: "Events Reference", + // }, ], }, ], @@ -482,10 +482,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/inventory/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/inventory/events", + // title: "Events Reference", + // }, ], }, ], @@ -558,10 +558,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/order/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/order/events", + // title: "Events Reference", + // }, ], }, ], @@ -657,10 +657,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/payment/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/payment/events", + // title: "Events Reference", + // }, ], }, ], @@ -725,10 +725,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/pricing/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/pricing/events", + // title: "Events Reference", + // }, ], }, ], @@ -781,10 +781,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/product/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/product/events", + // title: "Events Reference", + // }, ], }, ], @@ -853,10 +853,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/promotion/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/promotion/events", + // title: "Events Reference", + // }, ], }, ], @@ -909,10 +909,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/region/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/region/events", + // title: "Events Reference", + // }, ], }, ], @@ -971,10 +971,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/sales-channel/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/sales-channel/events", + // title: "Events Reference", + // }, ], }, ], @@ -1034,10 +1034,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/stock-location/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/stock-location/events", + // title: "Events Reference", + // }, ], }, ], @@ -1090,10 +1090,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/store/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/store/events", + // title: "Events Reference", + // }, ], }, ], @@ -1162,10 +1162,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/tax/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/tax/events", + // title: "Events Reference", + // }, ], }, ], @@ -1222,10 +1222,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ }, ], }, - { - path: "/commerce-modules/user/events", - title: "Events Reference", - }, + // { + // path: "/commerce-modules/user/events", + // title: "Events Reference", + // }, ], }, ], @@ -2104,10 +2104,10 @@ export const sidebar = sidebarAttachHrefCommonOptions([ path: "/medusa-container-resources", title: "Medusa Container Resources", }, - { - path: "/events-reference", - title: "Events", - }, + // { + // path: "/events-reference", + // title: "Events", + // }, { path: "/admin-widget-injection-zones", title: "Admin Widget Injection Zones", diff --git a/www/apps/ui/.env.example b/www/apps/ui/.env.example index 520fe20af5..e7cbf786fc 100644 --- a/www/apps/ui/.env.example +++ b/www/apps/ui/.env.example @@ -8,5 +8,4 @@ NEXT_PUBLIC_ALGOLIA_APP_ID= NEXT_PUBLIC_SEGMENT_API_KEY= NEXT_PUBLIC_AI_ASSISTANT_URL= NEXT_PUBLIC_AI_WEBSITE_ID= -NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= -NEXT_PUBLIC_SHOW_V2= \ No newline at end of file +NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= \ No newline at end of file diff --git a/www/apps/ui/src/config/docs.tsx b/www/apps/ui/src/config/docs.tsx index dbe092aa86..2cc08d708a 100644 --- a/www/apps/ui/src/config/docs.tsx +++ b/www/apps/ui/src/config/docs.tsx @@ -1,10 +1,5 @@ import { ArrowUpRightOnBox } from "@medusajs/icons" -import { - NavbarItem, - getNavbarItems, - legacyMobileSidebarItems, - mobileSidebarItemsV1, -} from "docs-ui" +import { NavbarItem, getNavbarItems, mobileSidebarItemsV1 } from "docs-ui" import { SidebarSectionItemsType } from "types" import { siteConfig } from "./site" @@ -17,7 +12,7 @@ export const docsConfig: DocsConfig = { mainNav: getNavbarItems({ basePath: siteConfig.baseUrl, activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/ui", - version: process.env.NEXT_PUBLIC_SHOW_V2 ? "v1" : "legacy", + version: "v1", }), sidebar: { top: [ @@ -285,8 +280,6 @@ export const docsConfig: DocsConfig = { ], }, ], - mobile: process.env.NEXT_PUBLIC_SHOW_V2 - ? mobileSidebarItemsV1 - : legacyMobileSidebarItems, + mobile: mobileSidebarItemsV1, }, } diff --git a/www/packages/docs-ui/src/constants.tsx b/www/packages/docs-ui/src/constants.tsx index 27d749fe34..abbbba3436 100644 --- a/www/packages/docs-ui/src/constants.tsx +++ b/www/packages/docs-ui/src/constants.tsx @@ -52,22 +52,23 @@ export const navbarItemsV1: NavbarItem[] = [ href: `/ui`, }, }, - { - type: "divider", - }, - { - type: "link", - props: { - label: "Learn Medusa v2", - target: "_blank", - rel: "noreferrer", - href: `/v2`, - badge: { - variant: "blue", - children: "New", - }, - }, - }, + // TODO enable them later + // { + // type: "divider", + // }, + // { + // type: "link", + // props: { + // label: "Learn Medusa v2", + // target: "_blank", + // rel: "noreferrer", + // href: `/v2`, + // badge: { + // variant: "blue", + // children: "New", + // }, + // }, + // }, ] export const navbarItemsV2: NavbarItem[] = [ @@ -122,71 +123,10 @@ export const navbarItemsV2: NavbarItem[] = [ { type: "link", props: { - label: "Medusa V1", + label: "Medusa v1", target: "_blank", rel: "noreferrer", href: `/`, - badge: { - variant: "neutral", - children: "v1", - }, - }, - }, -] - -export const legacyNavbarItems: NavbarItem[] = [ - { - type: "link", - props: { - label: "Docs", - target: "_blank", - rel: "noreferrer", - href: `/`, - }, - }, - { - type: "link", - props: { - label: "Resources", - target: "_blank", - rel: "noreferrer", - href: `/resources`, - }, - }, - { - type: "link", - props: { - label: "User Guide", - target: "_blank", - rel: "noreferrer", - href: `/user-guide`, - }, - }, - { - type: "link", - props: { - label: "Store API", - target: "_blank", - rel: "noreferrer", - href: `/api/store`, - }, - }, - { - type: "link", - props: { - label: "Admin API", - target: "_blank", - rel: "noreferrer", - href: `/api/admin`, - }, - }, - { - type: "link", - props: { - label: "UI", - target: "_blank", - rel: "noreferrer", - href: `/ui`, }, }, ] @@ -222,13 +162,13 @@ export const mobileSidebarItemsV1: SidebarItemType[] = [ loaded: true, isPathHref: true, }, - { - title: "Learn Medusa V2", - path: `/v2`, - loaded: true, - isPathHref: true, - additionalElms: v2, - }, + // { + // title: "Learn Medusa V2", + // path: `/v2`, + // loaded: true, + // isPathHref: true, + // additionalElms: v2, + // }, ] export const mobileSidebarItemsV2: SidebarItemType[] = [ @@ -263,51 +203,17 @@ export const mobileSidebarItemsV2: SidebarItemType[] = [ isPathHref: true, }, { - title: "Docs", + title: "Medusa v1", path: `/`, loaded: true, isPathHref: true, - additionalElms: v1, - }, -] - -export const legacyMobileSidebarItems: SidebarItemType[] = [ - { - title: "Docs", - path: `/`, - loaded: true, - isPathHref: true, - }, - { - title: "User Guide", - path: `/user-guide`, - loaded: true, - isPathHref: true, - }, - { - title: "Store API", - path: `/api/store`, - loaded: true, - isPathHref: true, - }, - { - title: "Admin API", - path: `/api/admin`, - loaded: true, - isPathHref: true, - }, - { - title: "UI", - path: `/ui`, - loaded: true, - isPathHref: true, }, ] export const searchFiltersV2: OptionType[] = [ { value: "book", - label: "Docs V2", + label: "Docs v2", }, { value: "resources", @@ -315,11 +221,11 @@ export const searchFiltersV2: OptionType[] = [ }, { value: "admin-v2", - label: "Admin API", + label: "Admin API (v2)", }, { value: "store-v2", - label: "Store API", + label: "Store API (v2)", }, // TODO add more filters ] diff --git a/www/packages/docs-ui/src/utils/get-navbar-items.ts b/www/packages/docs-ui/src/utils/get-navbar-items.ts index 34acc0a014..670e572c27 100644 --- a/www/packages/docs-ui/src/utils/get-navbar-items.ts +++ b/www/packages/docs-ui/src/utils/get-navbar-items.ts @@ -1,22 +1,17 @@ -import { NavbarItem, legacyNavbarItems, navbarItemsV1, navbarItemsV2 } from ".." +import { NavbarItem, navbarItemsV1, navbarItemsV2 } from ".." type Options = { basePath: string activePath: string - version?: "v1" | "v2" | "legacy" + version?: "v1" | "v2" } export function getNavbarItems({ basePath, activePath, - version = "legacy", + version = "v1", }: Options): NavbarItem[] { - const navbarItems = - version === "v2" - ? navbarItemsV2 - : version === "v1" - ? navbarItemsV1 - : legacyNavbarItems + const navbarItems = version === "v2" ? navbarItemsV2 : navbarItemsV1 return navbarItems.map((item) => { if (item.type === "divider") { return item