docs: updates for 2.5.1 release (#11574)

* docs: updates for 2.5.1 release

* update
This commit is contained in:
Shahed Nasser
2025-02-24 13:02:26 +02:00
committed by GitHub
parent 148587edc0
commit 31f8b72129
11 changed files with 10596 additions and 10186 deletions

View File

@@ -71,11 +71,15 @@ export function register() {
}
```
In the `instrumentation.ts` file, you export a `register` function that uses Medusa's `registerOtel` utility function.
In the `instrumentation.ts` file, you export a `register` function that uses Medusa's `registerOtel` utility function. You also initialize an instance of the exporter, such as Zipkin, and pass it to the `registerOtel` function.
You also initialize an instance of the exporter, such as Zipkin, and pass it to the `registerOtel` function.
`registerOtel` accepts an object where you can pass any [NodeSDKConfiguration](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_node.NodeSDKConfiguration.html) property along with the following properties:
`registerOtel` accepts an object having the following properties:
<Note>
The `NodeSDKConfiguration` properties are accepted since Medusa v2.5.1.
</Note>
<TypeList
types={[

View File

@@ -0,0 +1,181 @@
export const metadata = {
title: `${pageNumber} Admin Routing Customizations`,
}
# {metadata.title}
The Medusa Admin dashboard uses [React Router](https://reactrouter.com) under the hood to manage routing. So, you can have more flexibility in routing-related customizations using some of React Router's utilities, hooks, and components.
In this chapter, you'll learn about routing-related customizations that you can use in your admin customizations using React Router.
<Note>
`react-router-dom` is available in your project by default through the Medusa packages. You don't need to install it separately.
</Note>
## Link to a Page
To link to a page in your admin customizations, you can use the `Link` component from `react-router-dom`. For example:
export const highlights = [
["9", "Link", "Add a link to the orders 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 to a product's details page with a link to the Orders page. The link's path must be without the `/app` prefix.
---
## Admin Route Loader
<Note>
Route loaders are available starting from Medusa v2.5.1.
</Note>
In your UI route or any other custom admin route, you may need to retrieve data to use it in your route component. For example, you may want to fetch a list of products to display on a custom page.
To do that, you can export a `loader` function in the route file, which is a [React Router loader](https://reactrouter.com/6.29.0/route/loader#loader). In this function, you can fetch and return data asynchronously. Then, in your route component, you can use the [useLoaderData](https://reactrouter.com/6.29.0/hooks/use-loader-data#useloaderdata) hook from React Router to access the data.
For example, consider the following UI route created at `src/admin/routes/custom/page.tsx`:
export const loaderHighlights = [
["6", "loader", "Export a loader function to fetch products."],
["15", "useLoaderData", "Access the data returned by the loader function in the route component."],
]
```tsx title="src/admin/routes/custom/page.tsx" highlights={loaderHighlights}
import { Container, Heading } from "@medusajs/ui"
import {
useLoaderData,
} from "react-router-dom"
export async function loader() {
// TODO fetch products
return {
products: [],
}
}
const CustomPage = () => {
const { products } = useLoaderData() as Awaited<ReturnType<typeof loader>>
return (
<div>
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">Products count: {products.length}</Heading>
</div>
</Container>
</div>
)
}
export default CustomPage
```
In this example, you first export a `loader` function that can be used to fetch data, such as products. The function returns an object with a `products` property.
Then, in the `CustomPage` route component, you use the `useLoaderData` hook from React Router to access the data returned by the `loader` function. You can then use the data in your component.
### Route Parameters
You can also access route params in the loader function. For example, consider the following UI route created at `src/admin/routes/custom/[id]/page.tsx`:
export const loaderParamHighlights = [
["7", "params", "Access route params in the loader."]
]
```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={loaderParamHighlights}
import { Container, Heading } from "@medusajs/ui"
import {
useLoaderData,
LoaderFunctionArgs,
} from "react-router-dom"
export async function loader({ params }: LoaderFunctionArgs) {
const { id } = params
// TODO fetch product by id
return {
id,
}
}
const CustomPage = () => {
const { id } = useLoaderData() as Awaited<ReturnType<typeof loader>>
return (
<div>
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading level="h2">Product ID: {id}</Heading>
</div>
</Container>
</div>
)
}
export default CustomPage
```
Because the UI route has a route parameter `[id]`, you can access the `id` parameter in the `loader` function. The loader function accepts as a parameter an object of type `LoaderFunctionArgs` from React Router. This object has a `params` property that contains the route parameters.
In the loader, you can fetch data asynchronously using the route parameter and return it. Then, in the route component, you can access the data using the `useLoaderData` hook.
### When to Use Route Loaders
A route loader is executed before the route is loaded. So, it will block navigation until the loader function is resolved.
Only use route loaders when the route component needs data essential before rendering. Otherwise, use the JS SDK with Tanstack (React) Query as explained in [this chapter](../tips/page.mdx#send-requests-to-api-routes). This way, you can fetch data asynchronously and update the UI when the data is available. You can also use a loader to prepare some initial data that's used in the route component before the data is retrieved.
---
## Other React Router Utilities
### Route Handles
<Note>
Route handles are available starting from Medusa v2.5.1.
</Note>
In your UI route or any route file, you can export a `handle` object to define [route handles](https://reactrouter.com/start/framework/route-module#handle). The object is passed to the loader and route contexts.
For example:
```tsx title="src/admin/routes/custom/page.tsx"
export const handle = {
sandbox: true,
}
```
### React Router Components and Hooks
Refer to [react-router-doms documentation](https://reactrouter.com/en/6.29.0) for components and hooks that you can use in your admin customizations.

View File

@@ -136,6 +136,10 @@ export default ProductWidget
You can also send requests to custom routes as explained in the [JS SDK reference](!resources!/js-sdk).
### Use Route Loaders for Initial Data
You may need to retrieve data before your component is rendered, or you may need to pass some initial data to your component to be used while data is being fetched. In those cases, you can use a [route loader](../routing/page.mdx).
---
## Global Variables in Admin Customizations
@@ -148,49 +152,6 @@ In your admin customizations, you can use the following global variables:
---
## 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-doms 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.

View File

@@ -244,3 +244,9 @@ If you run the Medusa application and go to `localhost:9000/app/custom/123`, you
## 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.
---
## More Routes Customizations
For more customizations related to routes, refer to the [Routing Customizations chapter](../routing/page.mdx).

View File

@@ -50,9 +50,9 @@ export const generatedEditDates = {
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/api-routes/parameters/page.mdx": "2025-02-14T08:34:03.184Z",
"app/learn/fundamentals/api-routes/http-methods/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/fundamentals/admin/tips/page.mdx": "2025-02-18T15:28:39.264Z",
"app/learn/fundamentals/admin/tips/page.mdx": "2025-02-24T09:52:06.901Z",
"app/learn/fundamentals/api-routes/cors/page.mdx": "2024-12-09T13:04:04.357Z",
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2024-12-09T16:44:40.198Z",
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2025-02-24T09:35:11.752Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2025-02-12T17:05:20.708Z",
"app/learn/fundamentals/modules/isolation/page.mdx": "2024-12-09T11:02:38.087Z",
"app/learn/fundamentals/data-models/configure-properties/page.mdx": "2024-10-21T13:30:21.368Z",
@@ -79,7 +79,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/modules/db-operations/page.mdx": "2024-12-09T14:40:50.581Z",
"app/learn/fundamentals/modules/multiple-services/page.mdx": "2024-10-21T13:30:21.370Z",
"app/learn/fundamentals/modules/page.mdx": "2025-01-16T08:34:28.947Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2024-12-09T15:33:05.121Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2025-02-24T08:12:53.132Z",
"app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-01-27T08:45:19.025Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/customization/custom-features/api-route/page.mdx": "2024-12-09T10:39:30.046Z",
@@ -117,5 +117,6 @@ export const generatedEditDates = {
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z",
"app/learn/fundamentals/module-links/query-context/page.mdx": "2025-02-12T16:59:20.963Z",
"app/learn/fundamentals/admin/environment-variables/page.mdx": "2025-02-18T14:48:17.731Z",
"app/learn/fundamentals/api-routes/parse-body/page.mdx": "2025-02-14T08:32:25.596Z"
"app/learn/fundamentals/api-routes/parse-body/page.mdx": "2025-02-14T08:32:25.596Z",
"app/learn/fundamentals/admin/routing/page.mdx": "2025-02-24T09:50:37.495Z"
}

View File

@@ -807,6 +807,15 @@ export const generatedSidebar = [
"children": [],
"chapterTitle": "3.9.3. Environment Variables"
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/admin/routing",
"title": "Routing Customizations",
"children": [],
"chapterTitle": "3.9.4. Routing Customizations"
},
{
"loaded": true,
"isPathHref": true,
@@ -814,7 +823,7 @@ export const generatedSidebar = [
"path": "/learn/fundamentals/admin/constraints",
"title": "Constraints",
"children": [],
"chapterTitle": "3.9.4. Constraints"
"chapterTitle": "3.9.5. Constraints"
},
{
"loaded": true,
@@ -823,7 +832,7 @@ export const generatedSidebar = [
"path": "/learn/fundamentals/admin/tips",
"title": "Tips",
"children": [],
"chapterTitle": "3.9.5. Tips"
"chapterTitle": "3.9.6. Tips"
}
],
"chapterTitle": "3.9. Admin Development"

File diff suppressed because it is too large Load Diff

View File

@@ -466,6 +466,11 @@ export const sidebar = sidebarAttachHrefCommonOptions([
path: "/learn/fundamentals/admin/environment-variables",
title: "Environment Variables",
},
{
type: "link",
path: "/learn/fundamentals/admin/routing",
title: "Routing Customizations",
},
{
type: "link",
path: "/learn/fundamentals/admin/constraints",

View File

@@ -185,6 +185,8 @@ The `Medusa` initializer accepts as a parameter an object with the following pro
- `local` for the Local Storage.
- `session` for the Session Storage.
- `memory` to store it within the SDK for the current application's runtime.
- `custom` to define custom storage using the `auth.storage` configuration. If `auth.storage` isn't defined, the SDK throws an error. This option is only available after Medusa v2.5.1.
- `nostore` to not store the token.
</Table.Cell>
<Table.Cell>
@@ -196,6 +198,29 @@ The `Medusa` initializer accepts as a parameter an object with the following pro
<Table.Row>
<Table.Cell>
`auth.storage`
</Table.Cell>
<Table.Cell>
This option is only available after Medusa v2.5.1. It's an object or class that's used when `auth.jwtTokenStorageMethod` is `custom` to store the JWT token. It's useful when using the JS SDK in an environment where Local or Session Storage isn't available. The object or class must have the following methods:
- `setItem`: A function that accepts a key and value to store the JWT token.
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.
Learn more in [this section](#use-custom-storage).
</Table.Cell>
<Table.Cell>
\-
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`auth.fetchCredentials`
</Table.Cell>
@@ -484,3 +509,49 @@ revalidateTag("products")
```
Learn more in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#fetch-optionsnexttags-and-revalidatetag).
### Use Custom Storage
<Note>
The `auth.storage` configuration is only available after Medusa v2.5.1.
</Note>
If you're using the JS SDK in an environment where Local Storage or Session Storage isn't available, such as in a React Native application, you can define custom logic to store the JWT token.
To do that, set the `auth.jwtTokenStorageMethod` configuration to `custom` and define the `auth.storage` configuration with the custom logic to store the JWT token.
For example, if you're using React Native's `AsyncStorage`:
```ts title="config.ts"
import AsyncStorage from "@react-native-async-storage/async-storage"
import Medusa from "@medusajs/js-sdk"
let MEDUSA_BACKEND_URL = "http://localhost:9000"
if (process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL) {
MEDUSA_BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
}
export const sdk = new Medusa({
baseUrl: MEDUSA_BACKEND_URL,
debug: process.env.NODE_ENV === "development",
publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
auth: {
type: "jwt",
jwtTokenStorageMethod: "custom",
storge: AsyncStorage,
},
})
```
In the `auth` configuration, you specify the `type` as `jwt`, the `jwtTokenStorageMethod` as `custom`, and the `storage` as `AsyncStorage`. So, the SDK uses `AsyncStorage` to store the JWT token.
#### Custom Storage Methods
The object or class passed to `auth.storage` configuration must have the following methods:
- `setItem`: A function that accepts a key and value to store the JWT token.
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.

View File

@@ -1207,7 +1207,7 @@ export const createWishlistItemWorkflow = createWorkflow(
})
validateWishlistExistsStep({
wishlists
wishlists,
})
validateWishlistSalesChannelStep({
@@ -1604,7 +1604,7 @@ export const deleteWishlistItemWorkflow = createWorkflow(
})
validateWishlistExistsStep({
wishlists
wishlists,
})
validateItemInWishlistStep({

View File

@@ -1403,7 +1403,7 @@ fetch(`/admin/digital-products`, {
// delegate setting the prices to the
// product's page.
prices: [],
shipping_profile_id: ""
shipping_profile_id: "",
}],
},
}),