@mercurjs/docs 2.2.0-rc.0 → 2.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/concepts.mdx +1 -1
- package/content/references/overview.mdx +10 -0
- package/content/references/panel-extension-api.mdx +337 -0
- package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +5 -1
- package/content/references/workflows/commission/batch-commission-rules.mdx +6 -2
- package/content/references/workflows/offer/batch-offer-inventory-items.mdx +6 -2
- package/content/references/workflows/offer/create-offers.mdx +3 -1
- package/content/references/workflows/offer/update-offers.mdx +3 -1
- package/content/references/workflows/order-group/get-order-groups-list.mdx +3 -1
- package/content/references/workflows/payout/process-payout-for-webhook.mdx +3 -1
- package/content/references/workflows/product/confirm-products.mdx +2 -0
- package/content/references/workflows/product/reject-product.mdx +2 -0
- package/content/references/workflows/product/request-product-change.mdx +2 -0
- package/content/references/workflows/seller/approve-seller.mdx +2 -0
- package/content/references/workflows/seller/suspend-seller.mdx +2 -0
- package/content/references/workflows/seller/terminate-seller.mdx +2 -0
- package/content/references/workflows/seller/unsuspend-seller.mdx +2 -0
- package/content/references/workflows/seller/unterminate-seller.mdx +2 -0
- package/content/resources/customization/custom-fields.mdx +5 -1
- package/content/resources/customization/extending-panels.mdx +170 -113
- package/content/resources/tutorials/add-a-widget.mdx +110 -0
- package/content/resources/tutorials/add-order-detail-button.mdx +113 -0
- package/content/resources/tutorials/custom-panel-page.mdx +2 -2
- package/content/resources/tutorials/customize-navigation.mdx +110 -0
- package/content/resources/tutorials/extend-forms-and-tables.mdx +188 -0
- package/content/resources/tutorials/extend-onboarding.mdx +253 -0
- package/content/resources/tutorials/import-export-products.mdx +3 -3
- package/content/resources/tutorials/store-setup-checklist.mdx +214 -0
- package/llms.txt +8 -3
- package/package.json +1 -1
- package/content/resources/tutorials/recompose-a-page.mdx +0 -129
- package/content/resources/tutorials/replace-panel-components.mdx +0 -116
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Build a store-setup checklist"
|
|
3
|
+
description: "Render an onboarding progress checklist on the vendor store-setup surface with a seller.setup widget — the seller is handed to you as data, no fetching required."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The vendor **store-setup / onboarding** surface is a widget zone (`seller.setup`)
|
|
7
|
+
that renders the full `seller` object as its `data`. That makes it the perfect
|
|
8
|
+
seam for an onboarding **checklist**: derive completion from the seller you're
|
|
9
|
+
handed, show what's left, and link each step to the page that completes it — all
|
|
10
|
+
from a single widget file, with nothing fetched and nothing forked.
|
|
11
|
+
|
|
12
|
+
<Info>
|
|
13
|
+
**This is the pattern the built-in store-setup card uses.** The shipped
|
|
14
|
+
"Complete store profile" card is itself a `seller.setup` widget that reads the
|
|
15
|
+
`seller` and renders a progress list. This tutorial rebuilds it so you can
|
|
16
|
+
shape your own onboarding steps.
|
|
17
|
+
</Info>
|
|
18
|
+
|
|
19
|
+
## What you'll build
|
|
20
|
+
|
|
21
|
+
A collapsible progress card on the store-setup surface that checks four parts of
|
|
22
|
+
the seller profile (details, address, company, payment), draws a progress bar,
|
|
23
|
+
and routes the vendor to the settings page for each incomplete step. When every
|
|
24
|
+
step is done, the card removes itself.
|
|
25
|
+
|
|
26
|
+
## Register the typed targets (once)
|
|
27
|
+
|
|
28
|
+
Widget zones are typed ids the vendor panel generates from its own pages and
|
|
29
|
+
ships as `@mercurjs/vendor/extension-targets`. Reference them once so `seller.setup`
|
|
30
|
+
autocompletes and a wrong zone fails `tsc`:
|
|
31
|
+
|
|
32
|
+
```typescript apps/vendor/src/extension-targets.d.ts
|
|
33
|
+
/// <reference types="@mercurjs/vendor/extension-targets" />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Build the checklist widget
|
|
37
|
+
|
|
38
|
+
<Steps>
|
|
39
|
+
<Step title="Derive the steps from the seller">
|
|
40
|
+
The `data` prop is the full `SellerDTO`. Compute each step's `completed` flag
|
|
41
|
+
from it — no request needed. Give every step a `path` to the settings page
|
|
42
|
+
that finishes it:
|
|
43
|
+
|
|
44
|
+
```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
|
|
45
|
+
import "@mercurjs/vendor/extension-targets"
|
|
46
|
+
import { SellerDTO } from "@mercurjs/types"
|
|
47
|
+
|
|
48
|
+
type ProfileStep = {
|
|
49
|
+
key: string
|
|
50
|
+
label: string
|
|
51
|
+
completed: boolean
|
|
52
|
+
path: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getProfileSteps(seller: SellerDTO): ProfileStep[] {
|
|
56
|
+
const hasStoreDetails = !!(seller.name && seller.email && seller.description)
|
|
57
|
+
const hasAddress = !!(
|
|
58
|
+
seller.address?.address_1 &&
|
|
59
|
+
seller.address?.city &&
|
|
60
|
+
seller.address?.country_code
|
|
61
|
+
)
|
|
62
|
+
const hasCompanyDetails = !!seller.professional_details?.corporate_name
|
|
63
|
+
const hasPaymentDetails = !!(
|
|
64
|
+
seller.payment_details?.holder_name &&
|
|
65
|
+
seller.payment_details?.country_code
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return [
|
|
69
|
+
{ key: "store_details", label: "Add store details", completed: hasStoreDetails, path: "/settings/store/edit" },
|
|
70
|
+
{ key: "address", label: "Add address", completed: hasAddress, path: "/settings/store/address" },
|
|
71
|
+
{ key: "company_details", label: "Add company details", completed: hasCompanyDetails, path: "/settings/store/professional-details" },
|
|
72
|
+
{ key: "payment_details", label: "Add payment details", completed: hasPaymentDetails, path: "/settings/store/payment-details" },
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
</Step>
|
|
78
|
+
<Step title="Render the collapsible progress card">
|
|
79
|
+
Use `@medusajs/ui` primitives and `@medusajs/icons` only. Draw a progress bar
|
|
80
|
+
from the completed ratio, list each step with a status icon, and navigate on
|
|
81
|
+
click. Return `null` when everything is done so the card disappears:
|
|
82
|
+
|
|
83
|
+
```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
|
|
84
|
+
import { useMemo, useState } from "react"
|
|
85
|
+
import { useNavigate } from "react-router-dom"
|
|
86
|
+
import { Container, Text, clx } from "@medusajs/ui"
|
|
87
|
+
import { CheckCircleSolid, TriangleDownMini, CircleDottedLine } from "@medusajs/icons"
|
|
88
|
+
import { Collapsible as RadixCollapsible } from "radix-ui"
|
|
89
|
+
|
|
90
|
+
const StoreSetupChecklist = ({ data: seller }: { data?: SellerDTO }) => {
|
|
91
|
+
const navigate = useNavigate()
|
|
92
|
+
const [open, setOpen] = useState(true)
|
|
93
|
+
|
|
94
|
+
const steps = useMemo(
|
|
95
|
+
() => (seller ? getProfileSteps(seller) : []),
|
|
96
|
+
[seller]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if (!seller) {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const completedCount = steps.filter((s) => s.completed).length
|
|
104
|
+
const totalCount = steps.length
|
|
105
|
+
const progressPercent = (completedCount / totalCount) * 100
|
|
106
|
+
|
|
107
|
+
if (completedCount === totalCount) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<RadixCollapsible.Root open={open} onOpenChange={setOpen}>
|
|
113
|
+
<Container className="overflow-hidden p-0">
|
|
114
|
+
<div
|
|
115
|
+
className="h-1 bg-ui-tag-green-icon transition-all duration-500"
|
|
116
|
+
style={{ width: `${progressPercent}%` }}
|
|
117
|
+
/>
|
|
118
|
+
<div className="p-6">
|
|
119
|
+
<RadixCollapsible.Trigger asChild>
|
|
120
|
+
<button className="flex w-full items-center justify-between">
|
|
121
|
+
<Text size="large" weight="plus" leading="compact">
|
|
122
|
+
Complete store profile
|
|
123
|
+
</Text>
|
|
124
|
+
<TriangleDownMini
|
|
125
|
+
className={clx(
|
|
126
|
+
"text-ui-fg-muted transition-transform duration-200",
|
|
127
|
+
!open && "-rotate-90"
|
|
128
|
+
)}
|
|
129
|
+
/>
|
|
130
|
+
</button>
|
|
131
|
+
</RadixCollapsible.Trigger>
|
|
132
|
+
|
|
133
|
+
<RadixCollapsible.Content>
|
|
134
|
+
<div className="mt-4 flex flex-col gap-y-3">
|
|
135
|
+
{steps.map((step) => (
|
|
136
|
+
<button
|
|
137
|
+
key={step.key}
|
|
138
|
+
className="flex items-center gap-x-3 text-left"
|
|
139
|
+
onClick={() => !step.completed && navigate(step.path)}
|
|
140
|
+
disabled={step.completed}
|
|
141
|
+
>
|
|
142
|
+
{step.completed ? (
|
|
143
|
+
<CheckCircleSolid className="text-ui-tag-green-icon shrink-0" />
|
|
144
|
+
) : (
|
|
145
|
+
<CircleDottedLine className="text-ui-fg-muted shrink-0" />
|
|
146
|
+
)}
|
|
147
|
+
<Text size="small" leading="compact" className="text-ui-fg-base">
|
|
148
|
+
{step.label}
|
|
149
|
+
</Text>
|
|
150
|
+
</button>
|
|
151
|
+
))}
|
|
152
|
+
</div>
|
|
153
|
+
</RadixCollapsible.Content>
|
|
154
|
+
</div>
|
|
155
|
+
</Container>
|
|
156
|
+
</RadixCollapsible.Root>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
</Step>
|
|
162
|
+
<Step title="Attach it to the store-setup zone">
|
|
163
|
+
Export the component as the **default** and a `config` with a `seller.setup`
|
|
164
|
+
zone. `before` renders it above the built-in card; use `after` to place it
|
|
165
|
+
below:
|
|
166
|
+
|
|
167
|
+
```tsx apps/vendor/src/widgets/store-setup-checklist.tsx
|
|
168
|
+
import { defineWidgetConfig } from "@mercurjs/dashboard-sdk"
|
|
169
|
+
|
|
170
|
+
export const config = defineWidgetConfig({
|
|
171
|
+
zone: "seller.setup.before",
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
export default StoreSetupChecklist
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
</Step>
|
|
178
|
+
</Steps>
|
|
179
|
+
|
|
180
|
+
## Where it renders
|
|
181
|
+
|
|
182
|
+
`seller.setup` is hosted in two places, both passing the same `seller` as `data`:
|
|
183
|
+
|
|
184
|
+
| Host | When it shows |
|
|
185
|
+
| ---------------------------------------- | ------------------------------------------------------------ |
|
|
186
|
+
| The vendor shell (above the page outlet) | On top-level routes — the dashboard "home" onboarding banner |
|
|
187
|
+
| The store settings detail page | Always, above the store status banner |
|
|
188
|
+
|
|
189
|
+
A single widget file covers both. Multiple `seller.setup.before` / `.after`
|
|
190
|
+
widgets stack in registration order, so your checklist and the built-in card can
|
|
191
|
+
coexist — or hide the built-in one by rendering your own and letting theirs
|
|
192
|
+
complete.
|
|
193
|
+
|
|
194
|
+
<Note>
|
|
195
|
+
**No data fetching.** Because the zone hands you the resolved `seller`, the
|
|
196
|
+
widget is pure render — you never call the SDK. To *collect and persist* a new
|
|
197
|
+
onboarding value (e.g. a Tax ID) rather than just link to existing pages, see
|
|
198
|
+
[Extend the onboarding flow](/rc/resources/tutorials/extend-onboarding), which
|
|
199
|
+
carries a value through `additional_data` to a workflow hook.
|
|
200
|
+
</Note>
|
|
201
|
+
|
|
202
|
+
## Next steps
|
|
203
|
+
|
|
204
|
+
<CardGroup cols={2}>
|
|
205
|
+
<Card
|
|
206
|
+
title="Extend the onboarding flow"
|
|
207
|
+
href="/rc/resources/tutorials/extend-onboarding"
|
|
208
|
+
>
|
|
209
|
+
Collect a new value and persist it through a workflow hook.
|
|
210
|
+
</Card>
|
|
211
|
+
<Card title="Add a widget" href="/rc/resources/tutorials/add-a-widget">
|
|
212
|
+
The full widget model and the published zone registry.
|
|
213
|
+
</Card>
|
|
214
|
+
</CardGroup>
|
package/llms.txt
CHANGED
|
@@ -30,24 +30,28 @@ package (`node_modules/@mercurjs/docs/`).
|
|
|
30
30
|
- [Building with AI](content/resources/ai/overview.mdx) — Mercur ships version-matched docs inside your project so AI coding agents build from accurate APIs instead of stale training data.
|
|
31
31
|
- [Custom Fields](content/resources/customization/custom-fields.mdx) — Extend any Medusa entity with additional fields without modifying core code.
|
|
32
32
|
- [Extend a workflow](content/resources/customization/extend-a-workflow.mdx) — Inject custom logic into an existing Mercur workflow through hooks — without rewriting it.
|
|
33
|
-
- [Extending Panels](content/resources/customization/extending-panels.mdx) — Add pages, customize navigation,
|
|
33
|
+
- [Extending Panels](content/resources/customization/extending-panels.mdx) — Add pages, inject widgets, customize navigation, and extend forms and tables in the admin and vendor panels.
|
|
34
34
|
- [Medusa Cloud deployment](content/resources/deployment/medusa-cloud.mdx) — Deploy Mercur — backend, admin panel, and vendor panel — on Medusa Cloud.
|
|
35
35
|
- [Notifications](content/resources/integrations/notifications.mdx) — Send transactional emails and marketplace notifications with Resend.
|
|
36
36
|
- [Search](content/resources/integrations/search.mdx) — Mercur's provider-agnostic search module — zero-infrastructure Orama by default, swappable for Algolia, Meilisearch, or your own provider.
|
|
37
37
|
- [Stripe Connect Integration](content/resources/integrations/stripe-connect.mdx) — Set up Stripe Connect for marketplace payments and seller payouts — from Stripe Dashboard configuration to the full end-to-end payment lifecycle.
|
|
38
38
|
- [Add a feature with a block](content/resources/tutorials/add-a-block.mdx) — Install the reviews block end-to-end and see it live across the admin, vendor, and storefront surfaces.
|
|
39
|
+
- [Add a widget](content/resources/tutorials/add-a-widget.mdx) — Inject a React component at a named zone on a built-in panel page with defineWidgetConfig — no forking.
|
|
40
|
+
- [Add a button to order details](content/resources/tutorials/add-order-detail-button.mdx) — Drop a 'Copy link' button onto the vendor order detail page with a widget — no forking, no page override.
|
|
39
41
|
- [Create attributes and variant axes](content/resources/tutorials/attributes-and-variant-axes.mdx) — Build the attribute catalog: a filterable attribute, a variant axis backed by a native product option, and an inline product-scoped axis.
|
|
40
42
|
- [Build your own block](content/resources/tutorials/build-a-block.mdx) — Author a reusable feature as a block — backend, panel UI, and docs — build it into a registry, and install it into any Mercur project.
|
|
41
43
|
- [Configure commissions](content/resources/tutorials/configure-commissions.mdx) — Set the global commission, add scoped rules for categories and sellers, and confirm the right rate lands on an order.
|
|
42
44
|
- [Add a custom API route](content/resources/tutorials/custom-api-route.mdx) — Create a backend endpoint, regenerate the route map, and call it from a panel page with full type safety end to end.
|
|
43
45
|
- [Add a custom panel page](content/resources/tutorials/custom-panel-page.mdx) — Add a page to the vendor portal with file-based routing — the dashboard SDK wires it in automatically.
|
|
46
|
+
- [Customize navigation](content/resources/tutorials/customize-navigation.mdx) — Reorder, hide, relabel, and re-parent built-in sidebar items with a single _navigation.ts file and defineNavigationConfig.
|
|
47
|
+
- [Extend forms and tables](content/resources/tutorials/extend-forms-and-tables.mdx) — Add validated fields to built-in forms, replace fields in detail sections, and add list columns with defineCustomFieldsConfig and createFormHelper.
|
|
48
|
+
- [Extend the onboarding flow](content/resources/tutorials/extend-onboarding.mdx) — Add a field to the vendor store-setup surface, submit it through additional_data, and persist it durably from a workflow hook — end to end, without forking.
|
|
44
49
|
- [Build your first marketplace](content/resources/tutorials/first-marketplace.mdx) — Go end-to-end: create a project, approve a seller, list a product, place a split order, and watch a payout settle.
|
|
45
50
|
- [Handle product requests](content/resources/tutorials/handle-product-requests.mdx) — Walk a vendor's product submission and a follow-up edit through the operator review pipeline — confirm, decline, or send back.
|
|
46
51
|
- [Import and export products via CSV](content/resources/tutorials/import-export-products.mdx) — Install the product-import-export block and give vendors CSV import and export drawers on their product list.
|
|
47
52
|
- [Work with master products and offers](content/resources/tutorials/master-products-and-offers.mdx) — Two sellers compete on one catalog entry: publish a master product, create competing offers, and read per-offer prices from the Store API.
|
|
48
|
-
- [Re-compose a built-in page](content/resources/tutorials/recompose-a-page.mdx) — Override a built-in panel page by dropping a route file at the same path and re-composing its compound component slots.
|
|
49
|
-
- [Replace panel components](content/resources/tutorials/replace-panel-components.mdx) — Swap the sidebar, topbar actions, or store setup screen for your own components through dashboard SDK configuration.
|
|
50
53
|
- [Set up seller payouts](content/resources/tutorials/seller-payouts-stripe.mdx) — Take a seller from zero to paid: Stripe Connect onboarding, an order through fulfillment, capture, and the automated payout.
|
|
54
|
+
- [Build a store-setup checklist](content/resources/tutorials/store-setup-checklist.mdx) — Render an onboarding progress checklist on the vendor store-setup surface with a seller.setup widget — the seller is handed to you as data, no fetching required.
|
|
51
55
|
|
|
52
56
|
## Tools — CLI, API client, dashboard SDK
|
|
53
57
|
|
|
@@ -213,6 +217,7 @@ package (`node_modules/@mercurjs/docs/`).
|
|
|
213
217
|
- [Search module](content/references/modules/search.mdx) — The provider-agnostic search module, its document shapes, and the provider contract.
|
|
214
218
|
- [Seller module](content/references/modules/seller.mdx) — Data models, links, and service methods for the Seller module.
|
|
215
219
|
- [References](content/references/overview.mdx) — Technical references for Mercur's modules, HTTP APIs, workflows, and configuration.
|
|
220
|
+
- [Panel Extension API](content/references/panel-extension-api.mdx) — The file-based helpers for extending the admin and vendor panels — defineWidgetConfig, defineNavigationConfig, defineCustomFieldsConfig, createFormHelper — and the exact props passed to every component you supply.
|
|
216
221
|
- [addSellerShippingMethodToCartWorkflow](content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx) — Add shipping methods to a cart, replacing only the same seller's existing methods.
|
|
217
222
|
- [completeCartWithSplitOrdersWorkflow](content/references/workflows/cart/complete-cart-with-split-orders.mdx) — Complete a multi-seller cart by splitting it into per-seller orders under an order group.
|
|
218
223
|
- [listSellerShippingOptionsForCartWorkflow](content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx) — List available shipping options for a cart, grouped by seller.
|
package/package.json
CHANGED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Re-compose a built-in page"
|
|
3
|
-
description: "Override a built-in panel page by dropping a route file at the same path and re-composing its compound component slots."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Every page in the admin and vendor panels is exported as a **compound component** — a root plus named slots like `Header`, `HeaderActions`, and `DataTable`. That means you never fork a page to change one part of it: you drop a `page.tsx` at the same route path, render the original page, and swap only the slot you care about. Everything you don't touch keeps its default behavior — data fetching, filters, pagination, i18n, all of it.
|
|
7
|
-
|
|
8
|
-
<Info>
|
|
9
|
-
**This is not Medusa's widget system.** Medusa's admin lets you inject widgets into predefined zones around a page. Mercur takes a different approach: pages are React compound components you re-compose directly. There are no injection zones and no `defineWidgetConfig` — you get the actual page component and decide what renders inside it. See [Extending Panels](/rc/resources/customization/extending-panels) for the full comparison.
|
|
10
|
-
</Info>
|
|
11
|
-
|
|
12
|
-
## What you'll build
|
|
13
|
-
|
|
14
|
-
A vendor portal `/products` page with **Import** and **Export** buttons added next to the built-in Create button — while the table, search, filters, and everything else stay exactly as shipped. This is the same technique the official `product-import-export` block uses.
|
|
15
|
-
|
|
16
|
-
## Slot anatomy
|
|
17
|
-
|
|
18
|
-
Built-in pages are exported from the `/pages` subpath of each panel package:
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
import { ProductListPage } from "@mercurjs/vendor/pages" // vendor portal
|
|
22
|
-
import { CustomerListPage } from "@mercurjs/admin/pages" // admin panel
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Each list page exposes the same family of slots:
|
|
26
|
-
|
|
27
|
-
| Slot | Renders |
|
|
28
|
-
|------|---------|
|
|
29
|
-
| `Table` | The `Container` shell holding header + table |
|
|
30
|
-
| `Header` | The title/actions row |
|
|
31
|
-
| `HeaderTitle` | Heading and subtitle |
|
|
32
|
-
| `HeaderActions` | The action button cluster |
|
|
33
|
-
| `HeaderCreateButton` | The built-in Create button |
|
|
34
|
-
| `DataTable` | The wired data table — fetching, columns, filters, pagination |
|
|
35
|
-
|
|
36
|
-
Detail pages expose section slots instead (e.g. `CustomerDetailPage.Main`, `CustomerDetailPage.Sidebar`, `CustomerDetailPage.MainGeneralSection`).
|
|
37
|
-
|
|
38
|
-
## Override the page
|
|
39
|
-
|
|
40
|
-
<Steps>
|
|
41
|
-
<Step title="Drop a route file at the same path">
|
|
42
|
-
Create the file in your vendor app at the path that matches the built-in route. `/products` maps to `src/routes/products/page.tsx`:
|
|
43
|
-
|
|
44
|
-
```tsx apps/vendor/src/routes/products/page.tsx
|
|
45
|
-
import { Link } from "react-router-dom"
|
|
46
|
-
import { Button } from "@medusajs/ui"
|
|
47
|
-
import { ArrowDownTray, ArrowUpTray } from "@medusajs/icons"
|
|
48
|
-
import { ProductListPage } from "@mercurjs/vendor/pages"
|
|
49
|
-
|
|
50
|
-
export default function ProductsWithImportExport() {
|
|
51
|
-
return (
|
|
52
|
-
<ProductListPage>
|
|
53
|
-
<ProductListPage.Table>
|
|
54
|
-
<ProductListPage.Header>
|
|
55
|
-
<ProductListPage.HeaderTitle />
|
|
56
|
-
<ProductListPage.HeaderActions>
|
|
57
|
-
<Button size="small" variant="secondary" asChild>
|
|
58
|
-
<Link to="import">
|
|
59
|
-
<ArrowUpTray />
|
|
60
|
-
Import
|
|
61
|
-
</Link>
|
|
62
|
-
</Button>
|
|
63
|
-
<Button size="small" variant="secondary" asChild>
|
|
64
|
-
<Link to="export">
|
|
65
|
-
<ArrowDownTray />
|
|
66
|
-
Export
|
|
67
|
-
</Link>
|
|
68
|
-
</Button>
|
|
69
|
-
<ProductListPage.HeaderCreateButton />
|
|
70
|
-
</ProductListPage.HeaderActions>
|
|
71
|
-
</ProductListPage.Header>
|
|
72
|
-
<ProductListPage.DataTable />
|
|
73
|
-
</ProductListPage.Table>
|
|
74
|
-
</ProductListPage>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
</Step>
|
|
79
|
-
<Step title="Reuse the slots you don't change">
|
|
80
|
-
Note what happened in that file: `HeaderTitle`, `HeaderCreateButton`, and `DataTable` are the originals, rendered untouched. Only `HeaderActions` gained two buttons. You re-compose top-down — render the page, re-declare only the branch you're changing, and keep original slots for everything inside it.
|
|
81
|
-
</Step>
|
|
82
|
-
<Step title="Reload the panel">
|
|
83
|
-
The dashboard SDK picks the file up automatically (adding or removing a route file triggers a full reload in dev). Because the path matches a built-in route, **your page replaces the built-in one** — no configuration, no registration.
|
|
84
|
-
</Step>
|
|
85
|
-
</Steps>
|
|
86
|
-
|
|
87
|
-
<Note>
|
|
88
|
-
When a custom route's path matches a built-in route, your page replaces the built-in one. Routes at new paths are appended instead. That one rule is the entire override mechanism.
|
|
89
|
-
</Note>
|
|
90
|
-
|
|
91
|
-
## How defaults work
|
|
92
|
-
|
|
93
|
-
If you render a compound page with **no children**, it renders its full default composition — so `<ProductListPage />` alone is identical to the built-in page. Every page root follows the pattern:
|
|
94
|
-
|
|
95
|
-
```tsx
|
|
96
|
-
Children.count(children) > 0 ? children : <DefaultComposition />
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Verify
|
|
100
|
-
|
|
101
|
-
1. Run your project (`bun run dev`) and open the vendor portal.
|
|
102
|
-
2. Navigate to **Products** — the list should look identical to before, plus Import and Export buttons in the header.
|
|
103
|
-
3. Search, filter, and paginate the table — all built-in behavior must still work, since `DataTable` is untouched.
|
|
104
|
-
4. Delete your `src/routes/products/page.tsx` and reload — the built-in page comes back. Nothing was forked.
|
|
105
|
-
|
|
106
|
-
## FAQ
|
|
107
|
-
|
|
108
|
-
<AccordionGroup>
|
|
109
|
-
<Accordion title="Where do I find which slots a page exposes?">
|
|
110
|
-
Import the page from `@mercurjs/vendor/pages` or `@mercurjs/admin/pages` and let your editor's autocomplete list the attached members — every slot is a static property on the page component. The naming is consistent across pages: list pages expose `Table`/`Header`/`HeaderTitle`/`HeaderActions`/`HeaderCreateButton`/`DataTable`; detail pages expose `Main`/`Sidebar` plus one property per section.
|
|
111
|
-
</Accordion>
|
|
112
|
-
<Accordion title="Can I remove a built-in element, like the Create button?">
|
|
113
|
-
Yes — re-composition is declarative. Anything you don't render doesn't appear: re-declare `HeaderActions` with only your own buttons and omit `HeaderCreateButton`.
|
|
114
|
-
</Accordion>
|
|
115
|
-
<Accordion title="What about changing the table columns?">
|
|
116
|
-
`DataTable` is a single slot — you either keep it wholesale or replace it with your own table. For a different column set, replace the slot with your own component built on the shared `DataTable`/`useDataTable` primitives from the panel package.
|
|
117
|
-
</Accordion>
|
|
118
|
-
</AccordionGroup>
|
|
119
|
-
|
|
120
|
-
## Next steps
|
|
121
|
-
|
|
122
|
-
<CardGroup cols={2}>
|
|
123
|
-
<Card title="Replace panel components" href="/rc/resources/tutorials/replace-panel-components">
|
|
124
|
-
Swap global chrome like the sidebar or topbar instead of a single page.
|
|
125
|
-
</Card>
|
|
126
|
-
<Card title="Add a custom API route" href="/rc/resources/tutorials/custom-api-route">
|
|
127
|
-
Back your custom page with a typed endpoint of your own.
|
|
128
|
-
</Card>
|
|
129
|
-
</CardGroup>
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Replace panel components"
|
|
3
|
-
description: "Swap the sidebar, topbar actions, or store setup screen for your own components through dashboard SDK configuration."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Some customizations aren't about one page — they change the panel's global chrome. For those, the dashboard SDK exposes a small set of **named component overrides**: you point the plugin config at your own file, and the panel renders your component in place of the built-in one everywhere it appears.
|
|
7
|
-
|
|
8
|
-
<Info>
|
|
9
|
-
**Choose the right tool.** A component override replaces one of four global layout slots across the whole panel. If you want to change a single page, [re-compose it](/rc/resources/tutorials/recompose-a-page) instead — and if you want to add a new screen, just [drop in a route](/rc/resources/tutorials/custom-panel-page). The decision guide in [Extending Panels](/rc/resources/customization/extending-panels#choosing-your-extension-mechanism) compares all three.
|
|
10
|
-
</Info>
|
|
11
|
-
|
|
12
|
-
## What you'll build
|
|
13
|
-
|
|
14
|
-
A vendor portal with custom topbar actions (a help link and a status badge) and a custom main sidebar, configured entirely from `vite.config.ts`.
|
|
15
|
-
|
|
16
|
-
## The available slots
|
|
17
|
-
|
|
18
|
-
Exactly four layout components can be replaced:
|
|
19
|
-
|
|
20
|
-
| Component | Where it renders |
|
|
21
|
-
|-----------|------------------|
|
|
22
|
-
| `MainSidebar` | Primary navigation sidebar on every main page |
|
|
23
|
-
| `SettingsSidebar` | Sidebar of the `/settings` section |
|
|
24
|
-
| `TopbarActions` | Action cluster on the right side of the top bar |
|
|
25
|
-
| `StoreSetup` | The store setup screen shown to new sellers (vendor portal) |
|
|
26
|
-
|
|
27
|
-
Anything else — a page, a section, a table — is customized through routes and compound components, not through overrides.
|
|
28
|
-
|
|
29
|
-
## Set up the override
|
|
30
|
-
|
|
31
|
-
<Steps>
|
|
32
|
-
<Step title="Write the replacement component">
|
|
33
|
-
Create the component anywhere under `src/`. It must have a **default export**:
|
|
34
|
-
|
|
35
|
-
```tsx apps/vendor/src/components/topbar-actions.tsx
|
|
36
|
-
import { Badge, Button } from "@medusajs/ui"
|
|
37
|
-
|
|
38
|
-
export default function TopbarActions() {
|
|
39
|
-
return (
|
|
40
|
-
<div className="flex items-center gap-x-2">
|
|
41
|
-
<Badge size="2xsmall" color="green">
|
|
42
|
-
All systems operational
|
|
43
|
-
</Badge>
|
|
44
|
-
<Button size="small" variant="transparent" asChild>
|
|
45
|
-
<a href="https://help.example.com" target="_blank" rel="noreferrer">
|
|
46
|
-
Help
|
|
47
|
-
</a>
|
|
48
|
-
</Button>
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
</Step>
|
|
54
|
-
<Step title="Register it in the Vite config">
|
|
55
|
-
Component overrides live in the `components` option of `mercurDashboardPlugin`, in your panel app's `vite.config.ts`. Paths are **relative to `src/`**:
|
|
56
|
-
|
|
57
|
-
```typescript apps/vendor/vite.config.ts
|
|
58
|
-
import { defineConfig } from 'vite'
|
|
59
|
-
import react from '@vitejs/plugin-react'
|
|
60
|
-
import { mercurDashboardPlugin } from '@mercurjs/dashboard-sdk'
|
|
61
|
-
|
|
62
|
-
export default defineConfig({
|
|
63
|
-
plugins: [
|
|
64
|
-
react(),
|
|
65
|
-
mercurDashboardPlugin({
|
|
66
|
-
medusaConfigPath: '../../packages/api/medusa-config.ts',
|
|
67
|
-
components: {
|
|
68
|
-
TopbarActions: 'components/topbar-actions',
|
|
69
|
-
MainSidebar: 'components/main-sidebar',
|
|
70
|
-
},
|
|
71
|
-
}),
|
|
72
|
-
],
|
|
73
|
-
})
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
The plugin resolves each path at build time and swaps your component in through the `virtual:mercur/components` module — no runtime registration, no context providers to wire.
|
|
77
|
-
</Step>
|
|
78
|
-
<Step title="Restart the dev server">
|
|
79
|
-
Vite config changes require a restart. After it, your components render everywhere their slots appear.
|
|
80
|
-
</Step>
|
|
81
|
-
</Steps>
|
|
82
|
-
|
|
83
|
-
<Tip>
|
|
84
|
-
Build overrides with `@medusajs/ui` components and Medusa UI color tokens so they match the rest of the panel. The panels ship no other UI library.
|
|
85
|
-
</Tip>
|
|
86
|
-
|
|
87
|
-
## Verify
|
|
88
|
-
|
|
89
|
-
1. Open the vendor portal — the top bar shows your badge and Help link on every page.
|
|
90
|
-
2. Navigate between main pages and `/settings` — the override applies everywhere its slot renders.
|
|
91
|
-
3. Remove the `components` entry and restart — the built-in components return. The originals were never modified.
|
|
92
|
-
|
|
93
|
-
## FAQ
|
|
94
|
-
|
|
95
|
-
<AccordionGroup>
|
|
96
|
-
<Accordion title="If I replace MainSidebar, do my custom routes still show up in the menu?">
|
|
97
|
-
Only if your sidebar renders them. A `MainSidebar` override replaces the **entire** navigation sidebar, including the auto-generated menu items from route `config` exports — read them from `virtual:mercur/menu-items` if you want to keep automatic navigation. If you only want to add or reorder items, you don't need an override at all: the `config` export on route files (with `rank` and `nested`) covers that.
|
|
98
|
-
</Accordion>
|
|
99
|
-
<Accordion title="Can I override other components, like the login page or a form?">
|
|
100
|
-
No — these four slots are the complete list. The login flow, forms, and everything page-level are customized through drop-in routes and [compound re-composition](/rc/resources/tutorials/recompose-a-page).
|
|
101
|
-
</Accordion>
|
|
102
|
-
<Accordion title="Do overrides apply to both panels?">
|
|
103
|
-
Each panel app has its own `vite.config.ts`, so overrides are configured per panel. `StoreSetup` only exists in the vendor portal; the other three slots exist in both.
|
|
104
|
-
</Accordion>
|
|
105
|
-
</AccordionGroup>
|
|
106
|
-
|
|
107
|
-
## Next steps
|
|
108
|
-
|
|
109
|
-
<CardGroup cols={2}>
|
|
110
|
-
<Card title="Re-compose a built-in page" href="/rc/resources/tutorials/recompose-a-page">
|
|
111
|
-
Change one page's composition instead of global chrome.
|
|
112
|
-
</Card>
|
|
113
|
-
<Card title="Extending Panels" href="/rc/resources/customization/extending-panels">
|
|
114
|
-
The full reference for routes, navigation, branding, and overrides.
|
|
115
|
-
</Card>
|
|
116
|
-
</CardGroup>
|