docs: editing and general fixes of medusa's learning resources (#7261)
* docs: editing and general fixes of medusa's learning resources * fix build script * update ui dependency * fix build * adjust next.js steps
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
export const metadata = {
|
||||
title: `Discount Generator Plugin`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
## Features
|
||||
|
||||
In Medusa, merchants can create dynamic discounts that act as a template for other discounts. With dynamic discounts, merchants don't have to repeat certain conditions every time they want to create a new discount.
|
||||
|
||||
The discount generator plugin allows merchants and developers to generate new discounts from a dynamic discount either using the `/discount-code` API Route or the `DiscountGeneratorService`.
|
||||
|
||||
---
|
||||
|
||||
## Install the Discount Generator Plugin
|
||||
|
||||
To install the Discount Generator plugin, run the following command in the directory of your Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install medusa-plugin-discount-generator
|
||||
```
|
||||
|
||||
Next, add the plugin into the `plugins` array in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
const plugins = [
|
||||
// ...
|
||||
{
|
||||
resolve: `medusa-plugin-discount-generator`,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test the Plugin
|
||||
|
||||
<Note type="check">
|
||||
|
||||
- Dynamic discount. Create it using either the [Medusa Admin](!user-guide!/discounts/create) or the [Admin API routes](https://docs.medusajs.com/api/admin#discounts_postdiscounts).
|
||||
|
||||
</Note>
|
||||
|
||||
To test the plugin, start the Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Then, send a `POST` request to the `/discount-code` API Route:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/discount-code/" testApiMethod="POST" testBodyParams={{"discount_code": "TEST"}}
|
||||
curl -X POST http://localhost:9000/discount-code/ \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"discount_code": "TEST"
|
||||
}'
|
||||
```
|
||||
|
||||
The API Route accepts in the request body the parameter `discount_code` which is a string indicating the code of the dynamic discount to generate a new discount from.
|
||||
|
||||
The API Route then creates the new discount from the dynamic discount and returns it in the response.
|
||||
|
||||
---
|
||||
|
||||
## Use DiscountGeneratorService
|
||||
|
||||
Use the `DiscountGeneratorService` to generate a discount in other resources.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/store/generate-discount-code/route.ts"
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
// skipping validation for simplicity
|
||||
const { dynamicCode } = req.body
|
||||
const discountGenerator = req.scope.resolve(
|
||||
"discountGeneratorService"
|
||||
)
|
||||
const code =
|
||||
await discountGenerator.generateDiscount(dynamicCode)
|
||||
|
||||
res.json({
|
||||
code,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The `DiscountGeneratorService` has the method `generateDiscount`. It accepts the code of a dynamic discount as a parameter and creates a new discount having the same attributes as the dynamic discount, but with a different, random code.
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Table } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `IP Lookup (ipstack) Plugin`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
## Features
|
||||
|
||||
Location detection in a commerce store is essential for multi-region support.
|
||||
|
||||
Medusa provides an IP Lookup plugin that integrates the application with [ipstack](https://ipstack.com/) to detect a customer’s location and region.
|
||||
|
||||
---
|
||||
|
||||
## Install the IP Lookup Plugin
|
||||
|
||||
<Note type="check">
|
||||
|
||||
- [ipstack account](https://ipstack.com/)
|
||||
|
||||
</Note>
|
||||
|
||||
To install the IP Lookup plugin, run the following command in the directory of your Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install medusa-plugin-ip-lookup
|
||||
```
|
||||
|
||||
Next, add the plugin into the `plugins` array in `medusa-config.js`:
|
||||
|
||||
export const highlights = [
|
||||
["6", "access_token", "The ipstack account’s access key"],
|
||||
]
|
||||
|
||||
```js title="medusa-config.js" highlights={highlights}
|
||||
const plugins = [
|
||||
// other plugins...
|
||||
{
|
||||
resolve: `medusa-plugin-ip-lookup`,
|
||||
options: {
|
||||
access_token: process.env.IPSTACK_ACCESS_KEY,
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### IP Lookup Plugin Options
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Option</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`access_token`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A string indicating the ipstack account’s access key.
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Make sure to add the necessary environment variables for the above options in `.env`:
|
||||
|
||||
```bash
|
||||
IPSTACK_ACCESS_KEY=<YOUR_ACCESS_KEY>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test the Plugin
|
||||
|
||||
The plugin provides two resources: the `IpLookupService` and the `preCartCreation` middleware.
|
||||
|
||||
<Note>
|
||||
|
||||
Due to how Express resolves the current IP when accessing your website from `localhost`, you won’t be able to test the plugin locally. You can either use tools like ngrok to expose the `9000` port to be accessed publicly, or you have to test it on a deployed Medusa application.
|
||||
|
||||
</Note>
|
||||
|
||||
### IpLookupService
|
||||
|
||||
The `IpLookupService` has a method `lookupIp` that accepts the IP address as a parameter, sends a request to ipstack’s API, and returns the retrieved result.
|
||||
|
||||
For example, you can use it in a custom API route:
|
||||
|
||||
```ts title="src/api/store/customer-region/route.ts"
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
RegionService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const ipLookupService = req.scope.resolve("ipLookupService")
|
||||
const regionService =
|
||||
req.scope.resolve<RegionService>("regionService")
|
||||
|
||||
const ip =
|
||||
req.headers["x-forwarded-for"] || req.socket.remoteAddress
|
||||
|
||||
const { data } = await ipLookupService.lookupIp(ip)
|
||||
|
||||
if (!data.country_code) {
|
||||
throw new Error("Couldn't detect country code.")
|
||||
}
|
||||
|
||||
const region = await regionService.retrieveByCountryCode(
|
||||
data.country_code
|
||||
)
|
||||
|
||||
res.json({
|
||||
region,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### preCartCreation
|
||||
|
||||
The `preCartCreation` middleware can be added as a middleware to any route to attach the region ID to that route based on the user’s location.
|
||||
|
||||
For example, you can attach it to all `/store` routes to ensure the customer’s region is always detected:
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
const { preCartCreation } = require(
|
||||
"medusa-plugin-ip-lookup/api/medusa-middleware"
|
||||
).default
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/*",
|
||||
middlewares: [preCartCreation],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,189 @@
|
||||
import { Table } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Restock Notifications Plugin`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
## Features
|
||||
|
||||
Customers browsing your products may find something that they need, but it's out of stock. In this scenario, you can keep them interested in your product by notifying them when the product is back in stock.
|
||||
|
||||
The Restock Notifications plugin provides new API Routes to subscribe customers to restock notifications of a specific product variant. It also triggers the `restock-notification.restocked` event whenever a product variant's stock quantity is above a specified threshold.
|
||||
|
||||
However, this plugin doesn't actually implement the sending of the notification, only the required implementation to trigger restock events and allow customers to subscribe to product variants' stock status. To send the notification, use a Notification plugin.
|
||||
|
||||
---
|
||||
|
||||
## Install the Restock Notifications Plugin
|
||||
|
||||
To install the Restock Notifications plugin, run the following command in the directory of your Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install medusa-plugin-restock-notification
|
||||
```
|
||||
|
||||
Next, add the plugin into the `plugins` array in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
const plugins = [
|
||||
// other plugins...
|
||||
{
|
||||
resolve: `medusa-plugin-restock-notification`,
|
||||
options: {
|
||||
// optional options
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### Restock Notifications Plugin Options
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Option</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
<Table.HeaderCell>Default</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`trigger_delay`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A number indicating the time in milliseconds to delay the triggering of the `restock-notification.restocked` event.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`0`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`inventory_required`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A number indicating the minimum inventory quantity to consider a product variant as restocked.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`0`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
||||
### Run Migrations
|
||||
|
||||
The plugin requires changes in the database. So, before using it, run the `migrations` command:
|
||||
|
||||
```bash
|
||||
npx medusa migrations run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test the Plugin
|
||||
|
||||
<Note type="check">
|
||||
|
||||
- An out-of-stock product variant. You can edit a variant's stock quantity for testing either using the [Medusa Admin](!user-guide!/products/manage) or the [Admin API Routes](https://docs.medusajs.com/api/admin#products_postproductsproductvariantsvariant).
|
||||
|
||||
</Note>
|
||||
|
||||
To test the plugin, start the Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Then, send a `POST` request to the API Route `/restock-notifications/variants/{variant_id}` to subscribe to restock notifications of a product variant ID:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/restock-notifications/variants/{variant_id}" testApiMethod="POST" testPathParams={{"variant_id": "variant_01G1G5V2MRX2V3PVSR2WXYPFB6"}} testBodyParams={{"email": "example@gmail.com"}}
|
||||
curl -X POST http://localhost:9000/restock-notifications/variants/variant_01G1G5V2MRX2V3PVSR2WXYPFB6 \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"email": "example@gmail.com"
|
||||
}'
|
||||
```
|
||||
|
||||
The API Route accepts the following request body parameters:
|
||||
|
||||
1. `email`: a string indicating the email that is subscribing to the product variant's restock notification.
|
||||
2. `sales_channel_id`: an optional string indicating the ID of the sales channel to check the stock quantity in when subscribing.
|
||||
|
||||
After subscribing to the out-of-stock variant, change its stock quantity to the minimum inventory required to test the event trigger. The new stock quantity should be any value above `0` if you didn't set the `inventory_required` option.
|
||||
|
||||
{/* [Medusa Admin](../../user-guide/products/manage.mdx#manage-product-variants) */}
|
||||
|
||||
You can use the Medusa Admin or the [Admin API Routes](https://docs.medusajs.com/api/admin#products_postproductsproductvariantsvariant) to update the quantity.
|
||||
|
||||
After you update the quantity, the `restock-notification.restocked` is emitted.
|
||||
|
||||
---
|
||||
|
||||
## Example: Implement Notification Sending with SendGrid
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
The SendGrid plugin already listens to and handles the `restock-notification.restocked` event. So, if you install it, you don't need to manually create a subscriber that handles this event as explained here. This example is only provided for reference on how to send a notification to the customer using a Notification plugin.
|
||||
|
||||
</Note>
|
||||
|
||||
Here's an example of a subscriber that listens to the `restock-notification.restocked` event and uses the [SendGrid plugin](../../notifications/sendgrid/page.mdx) to send the subscribed customers an email:
|
||||
|
||||
```ts title="src/subscribers/restock-notification.ts"
|
||||
import {
|
||||
type SubscriberConfig,
|
||||
type SubscriberArgs,
|
||||
ProductVariantService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export default async function handleRestockNotification({
|
||||
data,
|
||||
container,
|
||||
}: SubscriberArgs<Record<string, string>>) {
|
||||
const sendgridService = container.resolve("sendgridService")
|
||||
const productVariantService: ProductVariantService =
|
||||
container.resolve("productVariantService")
|
||||
|
||||
// retrieve variant
|
||||
const variant = await productVariantService.retrieve(
|
||||
data.variant_id
|
||||
)
|
||||
|
||||
sendgridService.sendEmail({
|
||||
templateId: "restock-notification",
|
||||
from: "hello@medusajs.com",
|
||||
to: data.emails,
|
||||
dynamic_template_data: {
|
||||
// any data necessary for your template...
|
||||
variant,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const config: SubscriberConfig = {
|
||||
event: "restock-notification.restocked",
|
||||
}
|
||||
```
|
||||
|
||||
The handler function receives in the `data` property of the first parameter the following properties:
|
||||
|
||||
- `variant_id`: The ID of the variant that has been restocked.
|
||||
- `emails`: An array of strings indicating the email addresses subscribed to the restocked variant.
|
||||
|
||||
In the handler function, you retrieve the variant by its ID using the `ProductVariantService`, then send the email using the `SendGridService`.
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Table } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Wishlist Plugin`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
## Features
|
||||
|
||||
A wishlist allows customers to save items they like so they can browse and purchase them later.
|
||||
|
||||
Medusa's Wishlist plugin provides the following features:
|
||||
|
||||
- Allow a customer to manage their wishlist, including adding or deleting items.
|
||||
- Allow a customer to share their wishlist with others using a token.
|
||||
|
||||
Items in the wishlist are added as line items. This allows you to implement functionalities like moving an item from the wishlist to the cart.
|
||||
|
||||
---
|
||||
|
||||
## Install the Wishlist Plugin
|
||||
|
||||
To install the Wishlist plugin, run the following command in the directory of your Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install medusa-plugin-wishlist
|
||||
```
|
||||
|
||||
Next, add the plugin into the `plugins` array in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
const plugins = [
|
||||
// ...
|
||||
{
|
||||
resolve: `medusa-plugin-wishlist`,
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test the Plugin
|
||||
|
||||
To test the plugin, start the Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The plugin exposes four API Routes.
|
||||
|
||||
### Add Item to Wishlist API Route
|
||||
|
||||
The `POST` API Route at `/store/customers/{customer_id}/wishlist` allows customers to add items to their existing or new wishlist:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/store/customers/{customer_id}/wishlist" testApiMethod="POST" testPathParams={{"customer_id": "cus_01G2SG30J8C85S4A5CHM2S1NS2"}} testBodyParams={{"variant_id": "variant_01G1G5V2MRX2V3PVSR2WXYPFB6"}}
|
||||
curl -X POST http://localhost:9000/store/customers/cus_01G2SG30J8C85S4A5CHM2S1NS2/wishlist \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"variant_id": "variant_01G1G5V2MRX2V3PVSR2WXYPFB6"
|
||||
}'
|
||||
```
|
||||
|
||||
It accepts the following body parameters:
|
||||
|
||||
- `variant_id`: a string indicating the ID of the product variant to add to the wishlist.
|
||||
- `quantity`: (optional) a number indicating the quantity of the product variant.
|
||||
- `metadata`: (optional) any metadata to attach to the wishlist item.
|
||||
|
||||
The request returns the full customer object. The wishlist is available in the `customer.metadata.wishlist` property, where its value is an array of items.
|
||||
|
||||
### Delete Item from Wishlist API Route
|
||||
|
||||
The `DELETE` API Route at `/store/customers/{customer_id}/wishlist` allows customers to delete items from their wishlist:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/store/customers/{customer_id}/wishlist" testApiMethod="DELETE" testPathParams={{"customer_id": "cus_01G2SG30J8C85S4A5CHM2S1NS2"}} testBodyParams={{"index": 1}}
|
||||
curl -X DELETE http://localhost:9000/store/customers/cus_01G2SG30J8C85S4A5CHM2S1NS2/wishlist \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"index": 1
|
||||
}'
|
||||
```
|
||||
|
||||
The API Route accepts one request body parameter `index`, which indicates the index of the item in the `customer.metadata.wishlist` array.
|
||||
|
||||
The request returns the full customer object. The wishlist is available in the `customer.metadata.wishlist` property, where its value is an array of items.
|
||||
|
||||
#### Generate Share Token API Route
|
||||
|
||||
The `POST` API Route at `/store/customers/{customer_id}/wishlist/share-token` allows customers to retrieve a token that can be used to share the wishlist:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/store/customers/{customer_id}/wishlist/share-token" testApiMethod="POST" testPathParams={{"customer_id": "cus_01G2SG30J8C85S4A5CHM2S1NS2"}}
|
||||
curl -X POST http://localhost:9000/store/customers/cus_01G2SG30J8C85S4A5CHM2S1NS2/wishlist/share-token
|
||||
```
|
||||
|
||||
The request returns an object in the response having the property `share_token`, being the token that can be used to access the wishlist.
|
||||
|
||||
#### Access Wishlist with Token API Route
|
||||
|
||||
The `GET` API Route at `/wishlists/{token}` allows anyone to access the wishlist using its token, where `{token}` is the token retrieved from the [Generate Share Token API Route](#generate-share-token-api-token):
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/wishlists/{token}" testApiMethod="GET" testPathParams={{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"}}
|
||||
curl http://localhost:9000/wishlists/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
|
||||
```
|
||||
|
||||
The request returns an object in the response having the following properties:
|
||||
|
||||
- `items`: an array of objects, each being an item in the wishlist.
|
||||
- `first_name`: a string indicating the first name of the customer that this wishlist belongs to.
|
||||
Reference in New Issue
Block a user