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:
Shahed Nasser
2024-05-13 18:55:11 +03:00
committed by GitHub
parent 803e4aad02
commit 7cb90f8e82
79 changed files with 488 additions and 1707 deletions
@@ -1,585 +0,0 @@
import { AcademicCapSolid, BoltSolid } from "@medusajs/icons"
import { LearningPath } from "docs-ui"
export const metadata = {
title: `Commerce Automation Recipe`,
}
# {metadata.title}
In this recipe, you'll learn how to implement different forms of commerce automation with Medusa.
## Overview
Commerce automation is essential for businesses to save costs, provide a better user experience, and avoid manual, repetitive tasks that lead to human errors. Businesses utilize automation in different domains, including marketing, customer support, and order management.
Medusa provides the necessary architecture and tools to implement commerce automation for order management, customer service, and more. You can perform an asynchronous action when an event is triggered, schedule a job that runs at a specified interval, and more.
---
## Re-Stock Notifications
Customers may be interested in a product that is currently out of stock.
Medusa provides a Re-stock notifications plugin. You can also implement this by creating custom entities, triggering custom events, and handling these events with a subscriber.
<CardList itemsPerRow={2} items={[
{
href: "#",
title: "Restock Notifications Plugin",
text: "Install the Restock Notification plugin.",
startIcon: <BoltSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Create a Data Model",
text: "Learn how to create a custom data model.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Emit Events",
text: "Learn how to emit a custom event.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Create a Subscriber",
text: "Learn how to create a subscriber.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
---
## Automated Customer Support
Customer support is essential to build a store's brand and customer loyalty. This can include integrating with third-party services or automating notifications sent to customers when changes happen related to their orders, returns, exchanges, and more.
You can use Medusa's Notification Service to handle notifications triggered by actions on customer orders or profiles.
For example, when an order's status is updated, the `order.updated` event is triggered. You can create a Notification Service that handles this event by emailing the customer about the status update.
The Event reference includes an extensive list of events triggered by the Medusa core.
Medusa also provides official notification plugins that integrate with third-party services, such as SendGrid or TwilioSMS.
<CardList itemsPerRow={2} items={[
{
href: "#",
title: "Notification Plugins",
text: "Check out available notification plugins.",
startIcon: <BoltSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Create Notification Service",
text: "Learn how to create a notification service.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
<Details summaryContent="Example: Sending an email for new notes">
Heres an example of a subscriber that uses the SendGrid plugin to send an email to the customer when the order has a new note:
```ts title="src/subscribers/new-note.ts"
import {
type SubscriberConfig,
type SubscriberArgs,
ProductVariantService,
NoteService,
OrderService,
} from "@medusajs/medusa"
export default async function handleNoteCreated({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const noteService: NoteService = container.resolve(
"noteService"
)
const orderService: OrderService = container.resolve(
"orderService"
)
const sendgridService = container.resolve("sendgridService")
// retrieve note by id
const note = await noteService.retrieve(data.id, {
relations: ["author"],
})
if (!note || note.resource_type !== "order") {
return
}
// retrieve note's order
const order = await orderService.retrieve(
note.resource_id
)
if (!order) {
return
}
sendGridService.sendEmail({
templateId: "order-update",
from: "hello@medusajs.com",
to: order.email,
dynamic_template_data: {
// any data necessary for your template...
note_text: note.value,
note_author: note.author.first_name,
note_date: note.created_at,
order_id: order.display_id,
},
})
}
export const config: SubscriberConfig = {
event: NoteService.Events.CREATED,
context: {
subscriberId: "note-created-handler",
},
}
```
</Details>
---
## Automatic Data Synchronization
As your commerce store grows, you'll likely need to synchronize data across different systems. For example, you need to synchronize data with an ERP system or a data warehouse.
Use scheduled jobs to implement automatic synchronization. Within the scheduled job, you can synchronize your internal or external data.
<Card
href="#"
title="Create a Scheduled Job"
text="Learn how to create a scheduled job in Medusa."
startIcon={<AcademicCapSolid />}
showLinkIcon={false}
/>
<Details summaryContent="Example: Synchronizing products with a third-party service">
Heres an example of synchronizing products with a third party service using a loader:
```ts title="src/loaders/sync-products.ts"
import {
Logger,
ProductService,
StoreService,
MedusaContainer,
} from "@medusajs/medusa"
import {
ProductSelector,
} from "@medusajs/medusa/dist/types/product"
export default async (
container: MedusaContainer,
config: Record<string, unknown>
): Promise<void> => {
const logger = container.resolve<Logger>("logger")
logger.info("Synchronizing products...")
const productService = container.resolve<ProductService>(
"productService"
)
const storeService = container.resolve<StoreService>(
"storeService"
)
// retrieve store to get last sync date
const store = await storeService.retrieve()
const productFilters: ProductSelector = {}
if (store.metadata.last_sync_date) {
productFilters.updated_at = {
gt: new Date(
store.metadata.last_sync_date as string
),
}
}
const updatedProducts = await productService.list(
productFilters
)
updatedProducts.forEach((product) => {
// assuming client is an initialized connection
// with a third-party service
client.sync(product)
})
await storeService.update({
metadata: {
last_sync_date: new Date(),
},
})
logger.info("Finish synchronizing products")
}
```
Notice that here its assumed that:
1. The last update date is stored in the `Store`'s metadata object. You can instead use a custom data model to handle this.
2. The connection to the third-party service is assumed to be available and handled within the `client` variable.
</Details>
---
## Order Management Automation
Using Medusa's architecture and commerce features, you can automate a large amount of order management functionalities.
To handle events within an order flow and automate actions, create a subscriber. For example, create a subscriber that listens to the `order.placed` event and automatically creates a fulfillment if predefined conditions are met.
Another example is creating a subscriber that listens to the `order.shipment_created` event and automatically captures payment when an order is fully shipped.
<CardList items={[
{
href: "#",
title: "Event Bus",
text: "Learn about the event architecture system.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Create a Subscriber",
text: "Learn how to create a subscriber to listen to events.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Events Reference",
text: "Check out triggered events in Medusa and their expected payloads.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
---
## Automated RMA Flow
Businesses must optimize their Return Merchandise Authorization (RMA) flow to ensure a good customer experience and service. By automating the flow, customers request to return their received items, and businesses quickly support them.
Medusa's commerce features are geared towards automating RMA flows and ensuring a good customer experience.
For example, customers can create returns for their orders without direct involvement from the store operator. The store operator then receives a notification regarding the return and handles it accordingly. The same applies to order exchanges.
Medusa also provides features that allow a store operator to edit orders, receive customer approval for the edits, and authorize additional payment if necessary, all within a seamless flow.
Similar to the examples mentioned earlier, each action triggers events you can listen to and perform additional actions based on your use case.
<CardList items={[
{
href: "#",
title: "Event Bus",
text: "Learn about the event architecture system.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Create a Subscriber",
text: "Learn how to create a subscriber.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Events Reference",
text: "Check out triggered events in Medusa.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Order Returns",
text: "Learn about the Order Returns features.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Order Swaps",
text: "Learn about the Order Swaps features.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Order Edits",
text: "Learn about the Order Edits features.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
---
## Customer Segmentation
Businesses use customer segmentation to organize customers into different groups and then apply different price rules to these groups. For example, you may group customers by their product preferences, the number of orders they've placed, or geographical location.
Based on your use case and the segmentation logic, you can use different Medusa automation development tools to detect a customer's segment. Medusa also provides a customer groups feature that allows you to segment customers, whether you do it manually or automatically.
For example, to group customers with over twenty orders, create a subscriber that listens to the `order.placed` event and checks the number of orders the customer has so far. Then, add that customer to the VIP customer group. Finally, you can use the price lists feature to provide different prices or discounts for customers in the VIP group.
<CardList items={[
{
href: "#",
title: "Customer Groups",
text: "Learn about the Customer Groups architecture and features.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Subscribers and Events",
text: "Learn about events in Medusa and how to listen to events.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Price Lists",
text: "Learn about the Price Lists architecture and features.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
<Details summaryContent="Example: Add customer to VIP group">
Heres an example of a subscriber that listens to the `order.placed` event and checks if the customer should be added to the VIP customer group based on their number of orders:
```ts title="src/subscribers/add-custom-to-vip.ts"
import {
type SubscriberConfig,
type SubscriberArgs,
OrderService,
CustomerService,
CustomerGroupService,
} from "@medusajs/medusa"
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const orderService: OrderService = container.resolve(
"orderService"
)
const customerService: CustomerService = container.resolve(
"customerService"
)
const customerGroupService: CustomerGroupService =
container.resolve("customerGroupService")
// check if VIP group exists
const vipGroup = await customerGroupService.list({
name: "VIP",
}, {
relations: ["customers"],
})
if (!vipGroup.length) {
return
}
// retrieve order and its customer
const order = await orderService.retrieve(data.id)
if (!order || !order.customer_id ||
vipGroup[0].customers.find(
(customer) => customer.id === order.customer_id
) !== undefined) {
return
}
// retrieve orders of this customer
const [, count] = await orderService.listAndCount({
customer_id: order.customer_id,
})
if (count >= 20) {
// add customer to VIP group
customerGroupService.addCustomers(
vipGroup[0].id,
order.customer_id
)
}
}
export const config: SubscriberConfig = {
event: OrderService.Events.PLACED,
context: {
subscriberId: "order-placed-handler",
},
}
```
</Details>
---
## Marketing Automation
In your commerce store, you may utilize marketing strategies that encourage customers to make purchases. For example, you send a newsletter when new products are added to your store.
To do that, create a subscriber that listens to the `product.created`, and send an email to subscribed customers with tools like SendGrid or Mailchimp.
Alternatively, create a scheduled job that checks whether the number of new products has exceeded a set threshold, then sends out the newsletter.
<CardList items={[
{
href: "#",
title: "Events and Subscribers",
text: "Learn about events in Medusa and how to listen to events.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Scheduled Jobs",
text: "Learn about scheduled jobs and how you can create one.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Events Reference",
text: "Check out triggered events in Medusa and their expected payloads.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
]} />
<Details summaryContent="Example: Sending a newsletter email after adding ten products">
Heres an example of listening to the `product.created` event in a subscriber and send a newsletter if the condition is met:
```ts title="src/subscribers/send-products-newsletter.ts"
import {
type SubscriberConfig,
type SubscriberArgs,
ProductService,
StoreService,
CustomerService,
} from "@medusajs/medusa"
import {
ProductSelector,
} from "@medusajs/medusa/dist/types/product"
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const productService: ProductService = container.resolve(
"productService"
)
const storeService: StoreService = container.resolve(
"storeService"
)
const customerService: CustomerService = container.resolve(
"customerService"
)
const sendgridService = container.resolve("sendgridService")
// retrieve store to have access to last send date
const store = await storeService.retrieve()
const productFilters: ProductSelector = {}
if (store.metadata.last_send_date) {
productFilters.created_at = {
gt: new Date(store.metadata.last_send_date as string),
}
}
const products = await productService.list(
productFilters
)
if (products.length > 10) {
// get subscribed customers
const customers = await customerService.list({
metadata: {
is_subscribed: true,
},
})
sendgridService.sendEmail({
templateId: "product-newsletter",
from: "hello@medusajs.com",
to: customers.map((customer) => ({
name: customer.first_name,
email: customer.email,
})),
dynamic_template_data: {
// any data necessary for your template...
products,
},
})
await storeService.update({
metadata: {
last_send_date: new Date(),
},
})
}
}
export const config: SubscriberConfig = {
event: ProductService.Events.CREATED,
context: {
subscriberId: "product-create-handler",
},
}
```
</Details>
---
## Automation Development Toolkit
The use cases mentioned in this guide exemplify what commerce automation you can perform or implement with Medusa. Medusa provides the necessary development tools and mechanisms to automate tasks and manual work.
<CardList itemsPerRow={2} items={[
{
href: "#",
title: "Events and Subscribers",
text: "Learn about events in Medusa and how to listen to events.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Notification Service",
text: "Learn about notification services and how to create one.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Scheduled Jobs",
text: "Learn about scheduled jobs and how you can create one.",
startIcon: <AcademicCapSolid />,
showLinkIcon: false
},
{
href: "#",
title: "Notification Plugins",
text: "Check out available notification plugins.",
startIcon: <BoltSolid />,
showLinkIcon: false
},
]} />