docs: revise main docs outline (#10502)

This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-12-09 13:54:42 +02:00
1 parent c8cb9b5c1a
commit 0ae98c51eb
141 files changed
+814 -1181

No files matched your search

@@ -0,0 +1,58 @@
export const metadata = {
title: `${pageNumber} Admin Development Constraints`,
}
# {metadata.title}
This chapter lists some constraints of admin widgets and UI routes.
## Arrow Functions
Widget and UI route components must be created as arrow functions.
export const arrowHighlights = [
["2", "function", "Don't declare the widget / UI route as a function."],
["7", "() => ", "Use arrow functions when creating a widget / UI route."]
]
```ts highlights={arrowHighlights}
// Don't
function ProductWidget() {
// ...
}
// Do
const ProductWidget = () => {
// ...
}
```
---
## Widget Zone
A widget zone's value must be wrapped in double or single quotes. It can't be a template literal or a variable.
export const zoneHighlights = [
["3", "`product.details.before`", "Don't specify the value of `zone` as a template literal."],
["9", "ZONE", "Don't specify a variable as the value of `zone`."],
["14", `"product.details.before"`, "Wrap the value of `zone` in double or single quotes."]
]
```ts highlights={zoneHighlights}
// Don't
export const config = defineWidgetConfig({
zone: `product.details.before`,
})
// Don't
const ZONE = "product.details.after"
export const config = defineWidgetConfig({
zone: ZONE,
})
// Do
export const config = defineWidgetConfig({
zone: "product.details.before",
})
```
@@ -0,0 +1,26 @@
export const metadata = {
title: `${pageNumber} Admin Development`,
}
# {metadata.title}
In the next chapters, you'll learn more about possible admin customizations.
You can customize the admin dashboard by:
- Adding new sections to existing pages using Widgets.
- Adding new pages using UI Routes.
---
## Medusa UI Package
Medusa provides a Medusa UI package to facilitate your admin development through ready-made components and ensure a consistent design between your customizations and the dashboard’s design.
Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui) to learn how to install it and use its components.
---
## Admin Components List
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](!resources!/admin-components) to find common components.
@@ -0,0 +1,178 @@
import { CodeTabs, CodeTab } from "docs-ui"
export const metadata = {
title: `${pageNumber} Admin Development Tips`,
}
# {metadata.title}
In this chapter, you'll find some tips for your admin development.
## Send Requests to API Routes
To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](!resources!/js-sdk) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default.
First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations:
```ts
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
baseUrl: "http://localhost:9000",
debug: process.env.NODE_ENV === "development",
auth: {
type: "session",
},
})
```
<Note>
Learn more about the JS SDK's configurations [this documentation](!resources!/js-sdk#js-sdk-configurations).
</Note>
Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests.
For example:
<CodeTabs group="query-type">
<CodeTab label="Query" value="query">
export const queryHighlights = [
["8", "useQuery", "Use Tanstack Query's `useQuery` to send a `GET` request."],
["9", "sdk.admin.product.list", "Use the SDK to send the request."],
["10", "queryKey", "Specify the key used to cache data."]
]
```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Button, Container } from "@medusajs/ui"
import { useQuery } from "@tanstack/react-query"
import { sdk } from "../lib/config"
import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
const ProductWidget = () => {
const { data, isLoading } = useQuery({
queryFn: () => sdk.admin.product.list(),
queryKey: ["products"],
})
return (
<Container className="divide-y p-0">
{isLoading && <span>Loading...</span>}
{data?.products && (
<ul>
{data.products.map((product) => (
<li key={product.id}>{product.title}</li>
))}
</ul>
)}
</Container>
)
}
export const config = defineWidgetConfig({
zone: "product.list.before",
})
export default ProductWidget
```
</CodeTab>
<CodeTab label="Mutation" value="mutation">
export const mutationHighlights = [
["10", "useMutation", "Use Tanstack Query's `useMutation` to send `POST` or `DELETE` requests."],
["12", "sdk.admin.product.update", "Use the configured SDK to send the request."],
]
```tsx title="src/admin/widgets/product-widget.ts" highlights={mutationHighlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Button, Container } from "@medusajs/ui"
import { useMutation } from "@tanstack/react-query"
import { sdk } from "../lib/config"
import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
const ProductWidget = ({
data: productData,
}: DetailWidgetProps<HttpTypes.AdminProduct>) => {
const { mutateAsync } = useMutation({
mutationFn: (payload: HttpTypes.AdminUpdateProduct) =>
sdk.admin.product.update(productData.id, payload),
onSuccess: () => alert("updated product"),
})
const handleUpdate = () => {
mutateAsync({
title: "New Product Title",
})
}
return (
<Container className="divide-y p-0">
<Button onClick={handleUpdate}>Update Title</Button>
</Container>
)
}
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductWidget
```
</CodeTab>
</CodeTabs>
---
## Routing Functionalities
To navigate or link to other pages, or perform other routing functionalities, use the [react-router-dom](https://reactrouter.com/en/main) package. It's installed in your project through the Medusa Admin.
For example:
export const highlights = [
["9", "Link", "Add a link to another page"],
["9", '"/orders', "Add the path without the `/app` prefix."]
]
```tsx title="src/admin/widgets/product-widget.tsx" highlights={highlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Container } from "@medusajs/ui"
import { Link } from "react-router-dom"
// The widget
const ProductWidget = () => {
return (
<Container className="divide-y p-0">
<Link to={"/orders"}>View Orders</Link>
</Container>
)
}
// The widget's configurations
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductWidget
```
This adds a widget in a product's details page with a link to the Orders page. The link's path must be without the `/app` prefix.
<Note title="Learn more">
Refer to [react-router-dom’s documentation](https://reactrouter.com/en/main) for other available components and hooks.
</Note>
---
## Admin Translations
The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions.
Learn how to add a new language translation for the Medusa Admin in [this guide](!resources!/contribution-guidelines/admin-translations).
@@ -0,0 +1,218 @@
import { Prerequisites } from "docs-ui"
export const metadata = {
title: `${pageNumber} Admin UI Routes`,
}
# {metadata.title}
In this chapter, you’ll learn how to create a UI route in the admin dashboard.
## What is a UI Route?
The Medusa Admin dashboard is customizable, allowing you to add new pages, called UI routes. You create a UI route as a React component showing custom content that allow admin users to perform custom actions.
For example, you can add a new page to show and manage product reviews, which aren't available natively in Medusa.
---
## How to Create a UI Route?
<Prerequisites
items={[{
link: "/learn/installation",
text: "Medusa application installed"
}]}
/>
You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. The file’s default export must be the UI route’s React component.
For example, create the file `src/admin/routes/custom/page.tsx` with the following content:
![Example of UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg)
```tsx title="src/admin/routes/custom/page.tsx"
import { Container, Heading } from "@medusajs/ui"
const CustomPage = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">This is my custom route</Heading>
</div>
</Container>
)
}
export default CustomPage
```
You add a new route at `http://localhost:9000/app/custom`. The `CustomPage` component holds the page's content, which currently only shows a heading.
In the route, you use [Medusa UI](!ui!), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it.
<Note title="Important" type="warning">
The UI route component must be created as an arrow function.
</Note>
### Test the UI Route
To test the UI route, start the Medusa application:
```bash npm2yarn
npm run dev
```
Then, after logging into the admin dashboard, open the page `http://localhost:9000/app/custom` to see your custom page.
---
## Show UI Route in the Sidebar
To add a sidebar item for your custom UI route, export a configuration object in the UI route's file:
export const highlights = [
["16", "label", "The label of the UI route's sidebar item."],
["17", "icon", "The icon of the UI route's sidebar item."]
]
```tsx title="src/admin/routes/custom/page.tsx" highlights={highlights}
import { defineRouteConfig } from "@medusajs/admin-sdk"
import { ChatBubbleLeftRight } from "@medusajs/icons"
import { Container, Heading } from "@medusajs/ui"
const CustomPage = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">This is my custom route</Heading>
</div>
</Container>
)
}
export const config = defineRouteConfig({
label: "Custom Route",
icon: ChatBubbleLeftRight,
})
export default CustomPage
```
The configuration object is created using the `defineRouteConfig` function imported from `@medusajs/admin-sdk`. It accepts the following properties:
- `label`: the sidebar item’s label.
- `icon`: an optional React component used as an icon in the sidebar.
The above example adds a new sidebar item with the label `Custom Route` and an icon from the [Medusa UI Icons package](!ui!/icons/overview).
### Nested UI Routes
Consider that along the UI route above at `src/admin/routes/custom/page.tsx` you create a nested UI route at `src/admin/routes/custom/nested/page.tsx` that also exports route configurations:
![Example of nested UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg)
```tsx title="src/admin/routes/custom/nested/page.tsx"
import { defineRouteConfig } from "@medusajs/admin-sdk"
import { Container, Heading } from "@medusajs/ui"
const NestedCustomPage = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">This is my nested custom route</Heading>
</div>
</Container>
)
}
export const config = defineRouteConfig({
label: "Nested Route",
})
export default NestedCustomPage
```
This UI route is shown in the sidebar as an item nested in the parent "Custom Route" item. Nested items are only shown when the parent sidebar items (in this case, "Custom Route") are clicked.
#### Caveats
Some caveats for nested UI routes in the sidebar:
- Nested dynamic UI routes, such as one created at `src/admin/routes/custom/[id]/page.tsx` aren't added to the sidebar as it's not possible to link to a dynamic route. If the dynamic route exports route configurations, a warning is logged in the browser's console.
- Nested routes in setting pages aren't shown in the sidebar to follow the admin's design conventions.
- The `icon` configuration is ignored for the sidebar item of nested UI route to follow the admin's design conventions.
---
## Create Settings Page
To create a page under the settings section of the admin dashboard, create a UI route under the path `src/admin/routes/settings`.
For example, create a UI route at `src/admin/routes/settings/custom/page.tsx`:
![Example of settings UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867435/Medusa%20Book/setting-ui-route-dir-overview_kytbh8.jpg)
```tsx title="src/admin/routes/settings/custom/page.tsx"
import { defineRouteConfig } from "@medusajs/admin-sdk"
import { Container, Heading } from "@medusajs/ui"
const CustomSettingPage = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h1">Custom Setting Page</Heading>
</div>
</Container>
)
}
export const config = defineRouteConfig({
label: "Custom",
})
export default CustomSettingPage
```
This adds a page under the path `/app/settings/custom`. An item is also added to the settings sidebar with the label `Custom`.
---
## Path Parameters
A UI route can accept path parameters if the name of any of the directories in its path is of the format `[param]`.
For example, create the file `src/admin/routes/custom/[id]/page.tsx` with the following content:
![Example of UI route file with path parameters in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867748/Medusa%20Book/path-param-ui-route-dir-overview_kcfbev.jpg)
```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={[["5", "", "Retrieve the path parameter."], ["10", "{id}", "Show the path parameter."]]}
import { useParams } from "react-router-dom"
import { Container, Heading } from "@medusajs/ui"
const CustomPage = () => {
const { id } = useParams()
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h1">Passed ID: {id}</Heading>
</div>
</Container>
)
}
export default CustomPage
```
You access the passed parameter using `react-router-dom`'s [useParams hook](https://reactrouter.com/en/main/hooks/use-params).
If you run the Medusa application and go to `localhost:9000/app/custom/123`, you'll see `123` printed in the page.
---
## Admin Components List
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](!resources!/admin-components) to find common components.
@@ -0,0 +1,141 @@
import { Prerequisites } from "docs-ui"
export const metadata = {
title: `${pageNumber} Admin Widgets`,
}
# {metadata.title}
In this chapter, you’ll learn more about widgets and how to use them.
## What is an Admin Widget?
The Medusa Admin dashboard's pages are customizable to insert widgets of custom content in pre-defined injection zones. You create these widgets as React components that allow admin users to perform custom actions.
For example, you can add a widget on the product details page that allow admin users to sync products to a third-party service.
---
## How to Create a Widget?
<Prerequisites
items={[{
link: "/learn/installation",
text: "Medusa application installed"
}]}
/>
You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The file’s default export must be the widget, which is the React component that renders the custom content. The file must also export the widget’s configurations indicating where to insert the widget.
For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
![Example of widget file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867137/Medusa%20Book/widget-dir-overview_dqsbct.jpg)
export const widgetHighlights = [
["5", "ProductWidget", "The React component of the product widget."],
["17", "zone", "The zone to inject the widget to."]
]
```tsx title="src/admin/widgets/product-widget.tsx" highlights={widgetHighlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Container, Heading } from "@medusajs/ui"
// The widget
const ProductWidget = () => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">Product Widget</Heading>
</div>
</Container>
)
}
// The widget's configurations
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductWidget
```
You export the `ProductWidget` component, which shows the heading `Product Widget`. In the widget, you use [Medusa UI](!ui!), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it.
To export the widget's configurations, you use the `defineWidgetConfig` function imported from `@medusajs/admin-sdk`. It accepts as a parameter an object with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into.
In the example above, the widget is injected at the top of a product’s details.
<Note title="Important" type="warning">
The widget component must be created as an arrow function.
</Note>
### Test the Widget
To test out the widget, start the Medusa application:
```bash npm2yarn
npm run dev
```
Then, open a product’s details page. You’ll find your custom widget at the top of the page.
---
## Props Passed in Detail Pages
Widgets that are injected into a details page receive a `data` prop, which is the main data of the details page.
For example, a widget injected into the `product.details.before` zone receives the product's details in the `data` prop:
export const detailHighlights = [
["10", "data", "Receive the data as a prop."],
["11", "AdminProduct", "Pass the expected type of `data` as a type argument."],
["16", "data.title", "Show the product's title."]
]
```tsx title="src/admin/widgets/product-widget.tsx" highlights={detailHighlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Container, Heading } from "@medusajs/ui"
import {
DetailWidgetProps,
AdminProduct,
} from "@medusajs/framework/types"
// The widget
const ProductWidget = ({
data,
}: DetailWidgetProps<AdminProduct>) => {
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">
Product Widget {data.title}
</Heading>
</div>
</Container>
)
}
// The widget's configurations
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductWidget
```
The props type is `DetailWidgetProps`, and it accepts as a type argument the expected type of `data`. For the product details page, it's `AdminProduct`.
---
## Injection Zone
Refer to [this reference](!resources!/admin-widget-injection-zones) for the full list of injection zones and their props.
---
## Admin Components List
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](!resources!/admin-components) to find common components.