@mercurjs/docs 2.2.0-canary.40
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/README.md +24 -0
- package/content/blocks.mdx +209 -0
- package/content/learn/architecture.mdx +215 -0
- package/content/learn/attributes.mdx +89 -0
- package/content/learn/blocks.mdx +99 -0
- package/content/learn/commissions.mdx +175 -0
- package/content/learn/concepts.mdx +84 -0
- package/content/learn/installation.mdx +117 -0
- package/content/learn/introduction.mdx +71 -0
- package/content/learn/offers.mdx +78 -0
- package/content/learn/order-groups.mdx +114 -0
- package/content/learn/payouts.mdx +175 -0
- package/content/learn/product-requests.mdx +82 -0
- package/content/learn/products.mdx +79 -0
- package/content/learn/registry.mdx +155 -0
- package/content/learn/seller-members.mdx +62 -0
- package/content/learn/sellers.mdx +118 -0
- package/content/migration/from-1-x-to-2-0.mdx +152 -0
- package/content/migration/from-2-0-to-2-1.mdx +105 -0
- package/content/migration/overview.mdx +58 -0
- package/content/references/api/admin/commission-rates/batch-commission-rules.mdx +70 -0
- package/content/references/api/admin/commission-rates/create-commission-rate.mdx +95 -0
- package/content/references/api/admin/commission-rates/delete-commission-rate.mdx +40 -0
- package/content/references/api/admin/commission-rates/list-commission-rates.mdx +92 -0
- package/content/references/api/admin/commission-rates/retrieve-commission-rate.mdx +70 -0
- package/content/references/api/admin/commission-rates/update-commission-rate.mdx +84 -0
- package/content/references/api/admin/members/list-members.mdx +61 -0
- package/content/references/api/admin/offers/batch-create-offers.mdx +130 -0
- package/content/references/api/admin/offers/delete-offer.mdx +40 -0
- package/content/references/api/admin/offers/list-offers.mdx +106 -0
- package/content/references/api/admin/offers/retrieve-offer.mdx +82 -0
- package/content/references/api/admin/order-groups/list-order-groups.mdx +77 -0
- package/content/references/api/admin/order-groups/retrieve-order-group.mdx +56 -0
- package/content/references/api/admin/orders/get-order-commission-lines.mdx +65 -0
- package/content/references/api/admin/orders/get-order-order-group.mdx +56 -0
- package/content/references/api/admin/orders/list-orders.mdx +79 -0
- package/content/references/api/admin/payouts/list-payouts.mdx +75 -0
- package/content/references/api/admin/payouts/retrieve-payout.mdx +62 -0
- package/content/references/api/admin/product-attributes/create-attribute-value.mdx +76 -0
- package/content/references/api/admin/product-attributes/create-product-attribute.mdx +99 -0
- package/content/references/api/admin/product-attributes/delete-attribute-value.mdx +52 -0
- package/content/references/api/admin/product-attributes/delete-product-attribute.mdx +40 -0
- package/content/references/api/admin/product-attributes/list-product-attributes.mdx +91 -0
- package/content/references/api/admin/product-attributes/retrieve-product-attribute.mdx +81 -0
- package/content/references/api/admin/product-attributes/update-attribute-value.mdx +66 -0
- package/content/references/api/admin/product-attributes/update-product-attribute.mdx +76 -0
- package/content/references/api/admin/product-changes/cancel-product-change.mdx +68 -0
- package/content/references/api/admin/product-changes/confirm-product-change.mdx +47 -0
- package/content/references/api/admin/products/batch-product-attributes.mdx +96 -0
- package/content/references/api/admin/products/confirm-product.mdx +59 -0
- package/content/references/api/admin/products/create-product-variant.mdx +78 -0
- package/content/references/api/admin/products/create-product.mdx +169 -0
- package/content/references/api/admin/products/delete-product-variant.mdx +48 -0
- package/content/references/api/admin/products/delete-product.mdx +40 -0
- package/content/references/api/admin/products/list-product-variants.mdx +91 -0
- package/content/references/api/admin/products/list-products.mdx +109 -0
- package/content/references/api/admin/products/preview-product.mdx +78 -0
- package/content/references/api/admin/products/reject-product.mdx +59 -0
- package/content/references/api/admin/products/request-product-changes.mdx +59 -0
- package/content/references/api/admin/products/retrieve-product-variant.mdx +73 -0
- package/content/references/api/admin/products/retrieve-product.mdx +96 -0
- package/content/references/api/admin/products/set-product-sellers.mdx +47 -0
- package/content/references/api/admin/products/update-product-variant.mdx +89 -0
- package/content/references/api/admin/products/update-product.mdx +133 -0
- package/content/references/api/admin/sellers/add-seller-member.mdx +63 -0
- package/content/references/api/admin/sellers/approve-seller.mdx +54 -0
- package/content/references/api/admin/sellers/create-seller.mdx +100 -0
- package/content/references/api/admin/sellers/delete-member-invite.mdx +42 -0
- package/content/references/api/admin/sellers/delete-professional-details.mdx +52 -0
- package/content/references/api/admin/sellers/invite-seller-member.mdx +63 -0
- package/content/references/api/admin/sellers/list-member-invites.mdx +71 -0
- package/content/references/api/admin/sellers/list-seller-members.mdx +71 -0
- package/content/references/api/admin/sellers/list-seller-products.mdx +85 -0
- package/content/references/api/admin/sellers/list-sellers.mdx +96 -0
- package/content/references/api/admin/sellers/remove-seller-member.mdx +42 -0
- package/content/references/api/admin/sellers/resend-member-invite.mdx +55 -0
- package/content/references/api/admin/sellers/retrieve-seller.mdx +81 -0
- package/content/references/api/admin/sellers/suspend-seller.mdx +60 -0
- package/content/references/api/admin/sellers/terminate-seller.mdx +60 -0
- package/content/references/api/admin/sellers/unsuspend-seller.mdx +52 -0
- package/content/references/api/admin/sellers/unterminate-seller.mdx +52 -0
- package/content/references/api/admin/sellers/update-seller.mdx +83 -0
- package/content/references/api/admin/sellers/upsert-payment-details.mdx +88 -0
- package/content/references/api/admin/sellers/upsert-professional-details.mdx +78 -0
- package/content/references/api/admin/sellers/upsert-seller-address.mdx +91 -0
- package/content/references/api/admin.mdx +134 -0
- package/content/references/api/conventions.mdx +109 -0
- package/content/references/api/store/carts/add-line-item.mdx +115 -0
- package/content/references/api/store/carts/add-shipping-methods.mdx +84 -0
- package/content/references/api/store/carts/apply-promotions.mdx +83 -0
- package/content/references/api/store/carts/complete-cart.mdx +98 -0
- package/content/references/api/store/offers/list-offers.mdx +161 -0
- package/content/references/api/store/offers/retrieve-offer.mdx +112 -0
- package/content/references/api/store/order-groups/list-order-groups.mdx +105 -0
- package/content/references/api/store/order-groups/retrieve-order-group.mdx +78 -0
- package/content/references/api/store/product-attributes/list-product-attributes.mdx +134 -0
- package/content/references/api/store/product-attributes/retrieve-product-attribute.mdx +78 -0
- package/content/references/api/store/product-categories/list-product-categories.mdx +126 -0
- package/content/references/api/store/product-categories/retrieve-product-category.mdx +79 -0
- package/content/references/api/store/products/list-products.mdx +184 -0
- package/content/references/api/store/products/retrieve-product.mdx +107 -0
- package/content/references/api/store/search/search.mdx +136 -0
- package/content/references/api/store/sellers/list-sellers.mdx +105 -0
- package/content/references/api/store/sellers/retrieve-seller.mdx +62 -0
- package/content/references/api/store/shipping-options/list-shipping-options.mdx +74 -0
- package/content/references/api/store.mdx +67 -0
- package/content/references/api/vendor/members/accept-member-invite.mdx +65 -0
- package/content/references/api/vendor/members/retrieve-current-member.mdx +51 -0
- package/content/references/api/vendor/members/update-current-member.mdx +70 -0
- package/content/references/api/vendor/offers/batch-create-offers.mdx +123 -0
- package/content/references/api/vendor/offers/batch-offer-inventory-items.mdx +103 -0
- package/content/references/api/vendor/offers/create-offer.mdx +141 -0
- package/content/references/api/vendor/offers/delete-offer.mdx +51 -0
- package/content/references/api/vendor/offers/list-offers.mdx +125 -0
- package/content/references/api/vendor/offers/retrieve-offer.mdx +74 -0
- package/content/references/api/vendor/offers/update-offer.mdx +82 -0
- package/content/references/api/vendor/orders/cancel-fulfillment.mdx +63 -0
- package/content/references/api/vendor/orders/cancel-order.mdx +56 -0
- package/content/references/api/vendor/orders/complete-order.mdx +55 -0
- package/content/references/api/vendor/orders/create-fulfillment.mdx +76 -0
- package/content/references/api/vendor/orders/create-shipment.mdx +99 -0
- package/content/references/api/vendor/orders/get-order-commission-lines.mdx +66 -0
- package/content/references/api/vendor/orders/list-order-changes.mdx +75 -0
- package/content/references/api/vendor/orders/list-orders.mdx +103 -0
- package/content/references/api/vendor/orders/mark-fulfillment-delivered.mdx +62 -0
- package/content/references/api/vendor/orders/preview-order.mdx +65 -0
- package/content/references/api/vendor/orders/retrieve-order.mdx +84 -0
- package/content/references/api/vendor/payout-accounts/create-onboarding.mdx +68 -0
- package/content/references/api/vendor/payout-accounts/create-payout-account.mdx +59 -0
- package/content/references/api/vendor/payout-accounts/retrieve-payout-account-by-id.mdx +60 -0
- package/content/references/api/vendor/payout-accounts/retrieve-payout-account.mdx +77 -0
- package/content/references/api/vendor/payouts/list-payouts.mdx +90 -0
- package/content/references/api/vendor/payouts/retrieve-payout.mdx +63 -0
- package/content/references/api/vendor/product-attributes/list-product-attributes.mdx +135 -0
- package/content/references/api/vendor/product-attributes/retrieve-product-attribute.mdx +71 -0
- package/content/references/api/vendor/product-variants/list-variants.mdx +136 -0
- package/content/references/api/vendor/products/batch-product-attributes.mdx +104 -0
- package/content/references/api/vendor/products/cancel-product-change.mdx +62 -0
- package/content/references/api/vendor/products/create-product-variant.mdx +92 -0
- package/content/references/api/vendor/products/create-product.mdx +197 -0
- package/content/references/api/vendor/products/delete-product-variant.mdx +68 -0
- package/content/references/api/vendor/products/delete-product.mdx +62 -0
- package/content/references/api/vendor/products/list-product-variants.mdx +114 -0
- package/content/references/api/vendor/products/list-products.mdx +163 -0
- package/content/references/api/vendor/products/preview-product.mdx +61 -0
- package/content/references/api/vendor/products/retrieve-product-variant.mdx +76 -0
- package/content/references/api/vendor/products/retrieve-product.mdx +69 -0
- package/content/references/api/vendor/products/update-product-variant.mdx +104 -0
- package/content/references/api/vendor/products/update-product.mdx +148 -0
- package/content/references/api/vendor/sellers/create-seller.mdx +150 -0
- package/content/references/api/vendor/sellers/delete-professional-details.mdx +54 -0
- package/content/references/api/vendor/sellers/invite-seller-member.mdx +67 -0
- package/content/references/api/vendor/sellers/list-member-invites.mdx +82 -0
- package/content/references/api/vendor/sellers/list-seller-members.mdx +80 -0
- package/content/references/api/vendor/sellers/list-sellers.mdx +71 -0
- package/content/references/api/vendor/sellers/remove-seller-member.mdx +48 -0
- package/content/references/api/vendor/sellers/retrieve-current-seller-member.mdx +59 -0
- package/content/references/api/vendor/sellers/retrieve-current-seller.mdx +69 -0
- package/content/references/api/vendor/sellers/select-seller.mdx +44 -0
- package/content/references/api/vendor/sellers/update-current-seller.mdx +108 -0
- package/content/references/api/vendor/sellers/update-member-role.mdx +51 -0
- package/content/references/api/vendor/sellers/upsert-payment-details.mdx +74 -0
- package/content/references/api/vendor/sellers/upsert-professional-details.mdx +70 -0
- package/content/references/api/vendor/sellers/upsert-seller-address.mdx +81 -0
- package/content/references/api/vendor.mdx +150 -0
- package/content/references/configuration.mdx +155 -0
- package/content/references/modules/commission.mdx +106 -0
- package/content/references/modules/custom-fields.mdx +45 -0
- package/content/references/modules/media.mdx +55 -0
- package/content/references/modules/offer.mdx +64 -0
- package/content/references/modules/payout.mdx +121 -0
- package/content/references/modules/product-attribute.mdx +111 -0
- package/content/references/modules/product-edit.mdx +80 -0
- package/content/references/modules/search.mdx +112 -0
- package/content/references/modules/seller.mdx +175 -0
- package/content/references/overview.mdx +70 -0
- package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +44 -0
- package/content/references/workflows/cart/complete-cart-with-split-orders.mdx +36 -0
- package/content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx +39 -0
- package/content/references/workflows/cart/update-cart-seller-promotions.mdx +42 -0
- package/content/references/workflows/commission/batch-commission-rules.mdx +45 -0
- package/content/references/workflows/commission/create-commission-rates.mdx +41 -0
- package/content/references/workflows/commission/delete-commission-rates.mdx +34 -0
- package/content/references/workflows/commission/refresh-order-commission-lines.mdx +30 -0
- package/content/references/workflows/commission/update-commission-rates.mdx +35 -0
- package/content/references/workflows/media/set-category-images.mdx +41 -0
- package/content/references/workflows/media/set-collection-images.mdx +41 -0
- package/content/references/workflows/member/accept-member-invite.mdx +33 -0
- package/content/references/workflows/member/add-seller-member.mdx +30 -0
- package/content/references/workflows/member/create-member-invites.mdx +34 -0
- package/content/references/workflows/member/delete-member-invite.mdx +24 -0
- package/content/references/workflows/member/remove-seller-member.mdx +28 -0
- package/content/references/workflows/member/resend-member-invite.mdx +28 -0
- package/content/references/workflows/member/update-member-role.mdx +28 -0
- package/content/references/workflows/member/update-member.mdx +35 -0
- package/content/references/workflows/offer/batch-offer-inventory-items.mdx +58 -0
- package/content/references/workflows/offer/create-offers.mdx +63 -0
- package/content/references/workflows/offer/delete-offers.mdx +35 -0
- package/content/references/workflows/offer/update-offers.mdx +51 -0
- package/content/references/workflows/order/cancel-order-fulfillment.mdx +34 -0
- package/content/references/workflows/order/confirm-claim-request.mdx +25 -0
- package/content/references/workflows/order/confirm-exchange-request.mdx +25 -0
- package/content/references/workflows/order/confirm-order-edit-request.mdx +25 -0
- package/content/references/workflows/order/confirm-return-receive.mdx +25 -0
- package/content/references/workflows/order/create-order-fulfillment.mdx +45 -0
- package/content/references/workflows/order-group/get-order-group-detail.mdx +29 -0
- package/content/references/workflows/order-group/get-order-groups-list.mdx +36 -0
- package/content/references/workflows/overview.mdx +72 -0
- package/content/references/workflows/payout/create-onboarding.mdx +36 -0
- package/content/references/workflows/payout/create-payout-account.mdx +33 -0
- package/content/references/workflows/payout/create-payout.mdx +30 -0
- package/content/references/workflows/payout/process-payout-for-webhook.mdx +32 -0
- package/content/references/workflows/product/confirm-products.mdx +46 -0
- package/content/references/workflows/product/create-products.mdx +63 -0
- package/content/references/workflows/product/link-sellers-to-product-category.mdx +40 -0
- package/content/references/workflows/product/link-sellers-to-product.mdx +40 -0
- package/content/references/workflows/product/reject-product.mdx +46 -0
- package/content/references/workflows/product/request-product-change.mdx +46 -0
- package/content/references/workflows/product-attribute/add-product-attributes-to-product.mdx +49 -0
- package/content/references/workflows/product-attribute/create-and-link-product-attributes-to-product.mdx +46 -0
- package/content/references/workflows/product-attribute/create-product-attribute-values.mdx +43 -0
- package/content/references/workflows/product-attribute/create-product-attributes.mdx +56 -0
- package/content/references/workflows/product-attribute/delete-product-attribute-values.mdx +30 -0
- package/content/references/workflows/product-attribute/delete-product-attributes.mdx +30 -0
- package/content/references/workflows/product-attribute/remove-product-attributes-from-product.mdx +29 -0
- package/content/references/workflows/product-attribute/update-product-attribute-values.mdx +44 -0
- package/content/references/workflows/product-attribute/update-product-attributes-on-product.mdx +41 -0
- package/content/references/workflows/product-attribute/update-product-attributes.mdx +48 -0
- package/content/references/workflows/product-attribute/upsert-product-attribute-values.mdx +43 -0
- package/content/references/workflows/product-edit/auto-confirm-product-change.mdx +39 -0
- package/content/references/workflows/product-edit/cancel-product-change.mdx +49 -0
- package/content/references/workflows/product-edit/confirm-product-change.mdx +57 -0
- package/content/references/workflows/product-edit/create-product-change.mdx +72 -0
- package/content/references/workflows/product-edit/reject-product-change.mdx +54 -0
- package/content/references/workflows/product-edit/stage-product-change.mdx +75 -0
- package/content/references/workflows/seller/approve-seller.mdx +34 -0
- package/content/references/workflows/seller/create-seller-account.mdx +59 -0
- package/content/references/workflows/seller/create-seller-defaults.mdx +22 -0
- package/content/references/workflows/seller/create-sellers.mdx +65 -0
- package/content/references/workflows/seller/delete-seller-professional-details.mdx +37 -0
- package/content/references/workflows/seller/delete-sellers.mdx +24 -0
- package/content/references/workflows/seller/invite-seller.mdx +28 -0
- package/content/references/workflows/seller/suspend-seller.mdx +35 -0
- package/content/references/workflows/seller/terminate-seller.mdx +35 -0
- package/content/references/workflows/seller/unsuspend-seller.mdx +34 -0
- package/content/references/workflows/seller/unterminate-seller.mdx +34 -0
- package/content/references/workflows/seller/update-seller-address.mdx +55 -0
- package/content/references/workflows/seller/update-seller-payment-details.mdx +52 -0
- package/content/references/workflows/seller/update-seller-professional-details.mdx +48 -0
- package/content/references/workflows/seller/update-sellers.mdx +57 -0
- package/content/resources/ai/llms.mdx +74 -0
- package/content/resources/ai/mcp.mdx +152 -0
- package/content/resources/ai/overview.mdx +97 -0
- package/content/resources/customization/custom-fields.mdx +182 -0
- package/content/resources/customization/extend-a-workflow.mdx +39 -0
- package/content/resources/customization/extending-panels.mdx +309 -0
- package/content/resources/deployment/medusa-cloud.mdx +167 -0
- package/content/resources/integrations/notifications.mdx +39 -0
- package/content/resources/integrations/search.mdx +122 -0
- package/content/resources/integrations/stripe-connect.mdx +395 -0
- package/content/resources/tutorials/add-a-block.mdx +74 -0
- package/content/resources/tutorials/attributes-and-variant-axes.mdx +111 -0
- package/content/resources/tutorials/build-a-block.mdx +134 -0
- package/content/resources/tutorials/configure-commissions.mdx +127 -0
- package/content/resources/tutorials/custom-api-route.mdx +142 -0
- package/content/resources/tutorials/custom-panel-page.mdx +78 -0
- package/content/resources/tutorials/first-marketplace.mdx +45 -0
- package/content/resources/tutorials/handle-product-requests.mdx +80 -0
- package/content/resources/tutorials/import-export-products.mdx +96 -0
- package/content/resources/tutorials/master-products-and-offers.mdx +89 -0
- package/content/resources/tutorials/recompose-a-page.mdx +129 -0
- package/content/resources/tutorials/replace-panel-components.mdx +116 -0
- package/content/resources/tutorials/seller-payouts-stripe.mdx +89 -0
- package/content/telemetry.mdx +67 -0
- package/content/tools/api-client.mdx +155 -0
- package/content/tools/cli.mdx +196 -0
- package/content/tools/dashboard-sdk.mdx +35 -0
- package/content/user-guide/admin/overview.mdx +23 -0
- package/content/user-guide/admin/stores.mdx +193 -0
- package/content/user-guide/vendor/overview.mdx +23 -0
- package/content/user-guide/vendor/stores.mdx +209 -0
- package/llms.txt +307 -0
- package/package.json +33 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Build your own block"
|
|
3
|
+
description: "Author a reusable feature as a block — backend, panel UI, and docs — build it into a registry, and install it into any Mercur project."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Blocks are how features travel between Mercur projects: not as npm packages you depend on, but as **source code copied into the target project**. Anything you've built once — a module, workflows, routes, panel pages — can be packaged as a block, published through a registry, and installed with `mercurjs add`. This tutorial builds a minimal "announcements" block and ships it through your own registry.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**Blocks are source, not dependencies.** When someone installs your block, they get the files — editable, diffable, theirs. Updates are opt-in via `mercurjs diff` and `add --overwrite`, never forced through a lockfile. That's the trade: you give up automatic upgrades, users gain full ownership. Design blocks so they're readable after install.
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## What you'll build
|
|
13
|
+
|
|
14
|
+
An `announcements` block containing a module (data model + service), a vendor API route, and a vendor portal page — built into registry JSON and installed into a Mercur project.
|
|
15
|
+
|
|
16
|
+
## File types and where they land
|
|
17
|
+
|
|
18
|
+
Each file in a block carries a `type` that maps to an alias in the consumer's `blocks.json`:
|
|
19
|
+
|
|
20
|
+
| Type | Lands at (default aliases) |
|
|
21
|
+
|------|---------------------------|
|
|
22
|
+
| `registry:module` | the API package's modules directory |
|
|
23
|
+
| `registry:workflow` | the API package's workflows directory |
|
|
24
|
+
| `registry:api` | `packages/api/src` |
|
|
25
|
+
| `registry:link` | the API package's links directory |
|
|
26
|
+
| `registry:vendor` | `apps/vendor/src` |
|
|
27
|
+
| `registry:admin` | `apps/admin/src` |
|
|
28
|
+
| `registry:lib` | shared lib directory |
|
|
29
|
+
|
|
30
|
+
## Author and ship the block
|
|
31
|
+
|
|
32
|
+
<Steps>
|
|
33
|
+
<Step title="Lay out the block source">
|
|
34
|
+
A registry is a project with a `registry.json` and block sources under `src/`. Each block follows the standard directory convention, one folder per concern:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
my-registry/
|
|
38
|
+
├── registry.json
|
|
39
|
+
└── src/
|
|
40
|
+
└── announcements/
|
|
41
|
+
├── modules/announcements/ # data model, service, index
|
|
42
|
+
├── api/vendor/announcements/ # route.ts, validators.ts
|
|
43
|
+
└── vendor/routes/announcements/
|
|
44
|
+
└── page.tsx # vendor portal page
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Write the files exactly as they should land in a consumer's project — real imports, real Medusa module definitions. The build step resolves imports and rewrites them to the consumer's path aliases at install time. For the panel page, use the same conventions as any [custom panel page](/rc/resources/tutorials/custom-panel-page): a default export plus a `config` for the sidebar entry.
|
|
48
|
+
</Step>
|
|
49
|
+
<Step title="Declare it in registry.json">
|
|
50
|
+
```json registry.json
|
|
51
|
+
{
|
|
52
|
+
"$schema": "https://registry.mercurjs.com/registry.json",
|
|
53
|
+
"name": "@my-org",
|
|
54
|
+
"homepage": "https://my-org.com",
|
|
55
|
+
"items": [
|
|
56
|
+
{
|
|
57
|
+
"name": "announcements",
|
|
58
|
+
"description": "Marketplace announcements with a vendor portal feed.",
|
|
59
|
+
"dependencies": [],
|
|
60
|
+
"registryDependencies": [],
|
|
61
|
+
"docs": "## Setup\n\nRegister the module in `medusa-config.ts`, then run `bunx medusa db:generate announcements && bunx medusa db:migrate`, and finally `bunx @mercurjs/cli@rc codegen`.",
|
|
62
|
+
"categories": ["module", "api", "vendor"],
|
|
63
|
+
"files": [
|
|
64
|
+
{ "path": "announcements/modules/announcements/index.ts", "type": "registry:module" },
|
|
65
|
+
{ "path": "announcements/api/vendor/announcements/route.ts", "type": "registry:api" },
|
|
66
|
+
{ "path": "announcements/vendor/routes/announcements/page.tsx", "type": "registry:vendor" }
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Two fields do the heavy lifting: **`type`** on each file decides where it lands, and **`docs`** is the markdown shown after install. Put every manual step in `docs` — module registration, migrations, middleware, codegen. It's the only instruction the installer sees.
|
|
74
|
+
</Step>
|
|
75
|
+
<Step title="Build the registry">
|
|
76
|
+
```bash
|
|
77
|
+
bunx @mercurjs/cli@rc build
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This reads `registry.json`, resolves each block's imports, embeds file contents, and writes one JSON per block into `r/` — `r/announcements.json`, plus an index `r/registry.json`.
|
|
81
|
+
</Step>
|
|
82
|
+
<Step title="Host it">
|
|
83
|
+
Serve the `r/` directory from any static host (GitHub Pages, Vercel, S3 — anything that makes `{name}.json` publicly reachable).
|
|
84
|
+
</Step>
|
|
85
|
+
<Step title="Install it into a project">
|
|
86
|
+
In a consumer project, register your registry in `blocks.json` and install:
|
|
87
|
+
|
|
88
|
+
```json blocks.json
|
|
89
|
+
{
|
|
90
|
+
"registries": {
|
|
91
|
+
"@my-org": "https://my-registry.example.com/r/{name}.json"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
bunx @mercurjs/cli@rc add @my-org/announcements
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The CLI fetches the JSON, maps each file's `type` to the consumer's aliases, rewrites imports, and prints your `docs` instructions.
|
|
101
|
+
</Step>
|
|
102
|
+
</Steps>
|
|
103
|
+
|
|
104
|
+
## Verify
|
|
105
|
+
|
|
106
|
+
1. `r/announcements.json` exists after the build and embeds every file's content.
|
|
107
|
+
2. In the consumer project, the files landed under the alias-mapped paths and imports resolve.
|
|
108
|
+
3. After following your own `docs` steps (module registration, migrations, codegen), `bun run build` passes and the vendor portal shows the Announcements page.
|
|
109
|
+
4. `bunx @mercurjs/cli@rc diff @my-org/announcements` reports no changes — the installed copy matches the registry.
|
|
110
|
+
|
|
111
|
+
## FAQ
|
|
112
|
+
|
|
113
|
+
<AccordionGroup>
|
|
114
|
+
<Accordion title="How do I depend on another block or an npm package?">
|
|
115
|
+
Other blocks go in `registryDependencies` (e.g. `@my-org/reviews`) — the CLI installs them in order automatically. NPM packages go in `dependencies`; the build also auto-detects them from your imports, so you rarely list transitive ones by hand.
|
|
116
|
+
</Accordion>
|
|
117
|
+
<Accordion title="Can I make the registry private?">
|
|
118
|
+
Yes — use the object form with headers in the consumer's `blocks.json`: `{ "url": "…/{name}.json", "headers": { "Authorization": "Bearer ${REGISTRY_TOKEN}" } }`. The env var is resolved from the installer's environment. See [Registry](/rc/learn/registry).
|
|
119
|
+
</Accordion>
|
|
120
|
+
<Accordion title="How do consumers get updates to my block?">
|
|
121
|
+
They run `mercurjs diff <block>` to compare their local copy against your registry, then `add --overwrite` to take the new version. Because blocks are source, consumers with local edits merge deliberately rather than being force-upgraded.
|
|
122
|
+
</Accordion>
|
|
123
|
+
</AccordionGroup>
|
|
124
|
+
|
|
125
|
+
## Next steps
|
|
126
|
+
|
|
127
|
+
<CardGroup cols={2}>
|
|
128
|
+
<Card title="Registry" href="/rc/learn/registry">
|
|
129
|
+
The full registry.json schema, auth, and block dependencies.
|
|
130
|
+
</Card>
|
|
131
|
+
<Card title="Blocks" href="/rc/learn/blocks">
|
|
132
|
+
What blocks can contain and how consumers manage them.
|
|
133
|
+
</Card>
|
|
134
|
+
</CardGroup>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Configure commissions"
|
|
3
|
+
description: "Set the global commission, add scoped rules for categories and sellers, and confirm the right rate lands on an order."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Commissions are how your marketplace earns. Mercur matches a **commission rate** to every order line item through rules, with one **global commission** as the catch-all. In this tutorial you'll set up a realistic three-tier configuration and verify which rate wins on a real order.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**The most specific rate wins.** Rules combine with AND across dimensions and OR within a dimension, and among matching rates the one scoping the most dimensions applies. You never order rates by priority — specificity is the priority. The full algorithm is in [Commission](/rc/learn/commissions).
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## What you'll build
|
|
13
|
+
|
|
14
|
+
- A global commission of 15% that also commissions shipping.
|
|
15
|
+
- A 12% rate for the Electronics category.
|
|
16
|
+
- An 8% rate for Electronics sold by one specific premium seller.
|
|
17
|
+
|
|
18
|
+
An electronics order from the premium seller should produce commission lines at 8% — not 12%, not 15%.
|
|
19
|
+
|
|
20
|
+
## Set up the rates
|
|
21
|
+
|
|
22
|
+
<Steps>
|
|
23
|
+
<Step title="Set the global commission">
|
|
24
|
+
Every marketplace needs exactly one default rate — the fallback when nothing more specific matches. Create it with `is_default`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
curl -X POST http://localhost:9000/admin/commission-rates \
|
|
28
|
+
-H "Authorization: Bearer <admin-token>" \
|
|
29
|
+
-H "Content-Type: application/json" \
|
|
30
|
+
-d '{
|
|
31
|
+
"name": "Global Commission",
|
|
32
|
+
"code": "global",
|
|
33
|
+
"type": "percentage",
|
|
34
|
+
"value": 15,
|
|
35
|
+
"is_default": true,
|
|
36
|
+
"include_shipping": true
|
|
37
|
+
}'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
<Note>
|
|
41
|
+
`include_shipping` only has an effect on the global commission — shipping methods are always commissioned from the global rate, never from scoped rates. In the Admin Panel this lives on the Global Commission card under **Settings → Commissions**.
|
|
42
|
+
</Note>
|
|
43
|
+
</Step>
|
|
44
|
+
<Step title="Add a category-scoped rate">
|
|
45
|
+
A rate becomes scoped by attaching **rules**. Each rule names a dimension (`reference`) and a value (`reference_id`):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl -X POST http://localhost:9000/admin/commission-rates \
|
|
49
|
+
-H "Authorization: Bearer <admin-token>" \
|
|
50
|
+
-H "Content-Type: application/json" \
|
|
51
|
+
-d '{
|
|
52
|
+
"name": "Electronics Commission",
|
|
53
|
+
"code": "electronics",
|
|
54
|
+
"type": "percentage",
|
|
55
|
+
"value": 12,
|
|
56
|
+
"rules": [
|
|
57
|
+
{ "reference": "product_category", "reference_id": "pcat_electronics" }
|
|
58
|
+
]
|
|
59
|
+
}'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Available dimensions: `product`, `product_type`, `product_collection`, `product_category`, and `seller`.
|
|
63
|
+
</Step>
|
|
64
|
+
<Step title="Add a two-dimension rate for the premium seller">
|
|
65
|
+
Combining a `seller` rule with a `product_category` rule means *both* must match — this rate applies only to that seller's electronics:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
curl -X POST http://localhost:9000/admin/commission-rates \
|
|
69
|
+
-H "Authorization: Bearer <admin-token>" \
|
|
70
|
+
-H "Content-Type: application/json" \
|
|
71
|
+
-d '{
|
|
72
|
+
"name": "Premium seller electronics",
|
|
73
|
+
"code": "premium-electronics",
|
|
74
|
+
"type": "percentage",
|
|
75
|
+
"value": 8,
|
|
76
|
+
"rules": [
|
|
77
|
+
{ "reference": "seller", "reference_id": "slr_premium" },
|
|
78
|
+
{ "reference": "product_category", "reference_id": "pcat_electronics" }
|
|
79
|
+
]
|
|
80
|
+
}'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Because it scopes two dimensions, it beats the one-dimension Electronics rate whenever both match — regardless of which rate was created first.
|
|
84
|
+
</Step>
|
|
85
|
+
<Step title="Place a test order">
|
|
86
|
+
Commission lines are calculated when an order is placed — rates are never applied retroactively. Place an order containing an electronics item from the premium seller (via your storefront or the Store API), then inspect the order's commission lines.
|
|
87
|
+
</Step>
|
|
88
|
+
</Steps>
|
|
89
|
+
|
|
90
|
+
## Verify
|
|
91
|
+
|
|
92
|
+
1. List the rates and confirm all three exist and are enabled:
|
|
93
|
+
```bash
|
|
94
|
+
curl http://localhost:9000/admin/commission-rates \
|
|
95
|
+
-H "Authorization: Bearer <admin-token>"
|
|
96
|
+
```
|
|
97
|
+
2. On the placed order, each electronics line item from the premium seller has a commission line with `code: "premium-electronics"` and `rate: 8`.
|
|
98
|
+
3. The order's shipping method has a commission line from the **global** rate (because `include_shipping` is on).
|
|
99
|
+
4. Change a rate now and confirm the existing order's commission lines are untouched — `CommissionLine` records are a permanent audit trail.
|
|
100
|
+
|
|
101
|
+
## FAQ
|
|
102
|
+
|
|
103
|
+
<AccordionGroup>
|
|
104
|
+
<Accordion title="How do I charge a flat fee instead of a percentage?">
|
|
105
|
+
Use `"type": "fixed"` with per-currency `values`: `[{ "currency_code": "usd", "amount": 2 }, { "currency_code": "eur", "amount": 1.8 }]`. The scalar `value` is the fallback for currencies without an entry.
|
|
106
|
+
</Accordion>
|
|
107
|
+
<Accordion title="What happens when two rates tie on specificity?">
|
|
108
|
+
Ties break deterministically to the **oldest rate**. But if you find yourself relying on tie-breaking, prefer making one rate more specific — it reads better in the Admin Panel and survives audits.
|
|
109
|
+
</Accordion>
|
|
110
|
+
<Accordion title="Do rate changes affect existing orders?">
|
|
111
|
+
No. Commission is snapshotted into `CommissionLine` records at order placement. Changing or disabling a rate only affects future orders — the lines on past orders are a permanent audit trail that payouts read from.
|
|
112
|
+
</Accordion>
|
|
113
|
+
<Accordion title="Can a rate match multiple values in one dimension?">
|
|
114
|
+
Yes — rules within the same dimension are OR'd. A rate with two `product_category` rules matches items in either category, and still counts as one dimension for specificity.
|
|
115
|
+
</Accordion>
|
|
116
|
+
</AccordionGroup>
|
|
117
|
+
|
|
118
|
+
## Next steps
|
|
119
|
+
|
|
120
|
+
<CardGroup cols={2}>
|
|
121
|
+
<Card title="Commission" href="/rc/learn/commissions">
|
|
122
|
+
The full matching algorithm, calculation pipeline, and data model.
|
|
123
|
+
</Card>
|
|
124
|
+
<Card title="Set up seller payouts" href="/rc/resources/tutorials/seller-payouts-stripe">
|
|
125
|
+
Where commission is deducted — the settlement side of the marketplace.
|
|
126
|
+
</Card>
|
|
127
|
+
</CardGroup>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Add a custom API route"
|
|
3
|
+
description: "Create a backend endpoint, regenerate the route map, and call it from a panel page with full type safety end to end."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Mercur's typed API client isn't hand-maintained — its types are **generated from your actual route files**. That means a custom endpoint you add to the API package becomes a first-class, fully typed client call after one codegen run. This tutorial walks the whole loop: route → codegen → typed call from a custom panel page.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**The contract is generated, not declared.** You never write an interface for your endpoint. `mercurjs codegen` reads the route's handler and validators and emits the `Routes` type the client consumes — so the panel call site breaks at compile time the moment the backend changes. This loop is also what makes Mercur projects reliable targets for AI agents: see [Building with AI](/rc/resources/ai/overview).
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## What you'll build
|
|
13
|
+
|
|
14
|
+
A `GET /vendor/sales-summary` endpoint returning the seller's order count, called from a custom vendor portal page via `client.vendor.salesSummary.query()` with inferred types.
|
|
15
|
+
|
|
16
|
+
## Build the loop
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Create the route">
|
|
20
|
+
API routes follow Medusa's file conventions inside your API package. The URL path mirrors the directory path:
|
|
21
|
+
|
|
22
|
+
```typescript packages/api/src/api/vendor/sales-summary/route.ts
|
|
23
|
+
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
24
|
+
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
|
25
|
+
|
|
26
|
+
export async function GET(
|
|
27
|
+
req: AuthenticatedMedusaRequest,
|
|
28
|
+
res: MedusaResponse
|
|
29
|
+
) {
|
|
30
|
+
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
|
31
|
+
|
|
32
|
+
const { data: orders } = await query.graph({
|
|
33
|
+
entity: "order",
|
|
34
|
+
fields: ["id"],
|
|
35
|
+
filters: { seller_id: req.auth_context.actor_id },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
res.json({ order_count: orders.length })
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Routes under `src/api/vendor/*` run behind the vendor authentication middleware, so `req.auth_context` identifies the calling seller. Use `src/api/admin/*` for operator endpoints and `src/api/store/*` for public storefront endpoints.
|
|
43
|
+
</Step>
|
|
44
|
+
<Step title="Regenerate the route map">
|
|
45
|
+
```bash
|
|
46
|
+
bunx @mercurjs/cli@rc codegen
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Codegen scans your route files and rewrites the generated `Routes` type that your panel apps already import:
|
|
50
|
+
|
|
51
|
+
```typescript apps/vendor/src/lib/client.ts
|
|
52
|
+
import { createClient, type InferClient } from "@mercurjs/client"
|
|
53
|
+
import type { Routes } from "@acme/api/_generated"
|
|
54
|
+
|
|
55
|
+
declare const __BACKEND_URL__: string
|
|
56
|
+
|
|
57
|
+
export const client: InferClient<Routes> = createClient({
|
|
58
|
+
baseUrl: __BACKEND_URL__,
|
|
59
|
+
fetchOptions: { credentials: "include" },
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This file ships with the starter template — you don't need to touch it. After codegen, `client.vendor.salesSummary` simply exists, typed.
|
|
64
|
+
|
|
65
|
+
<Tip>
|
|
66
|
+
Run `bunx @mercurjs/cli@rc codegen --watch` during development so the route map regenerates as you edit route files.
|
|
67
|
+
</Tip>
|
|
68
|
+
</Step>
|
|
69
|
+
<Step title="Call it from a panel page">
|
|
70
|
+
Drop a page into the vendor app and call the endpoint through the client. Route segments map to camelCase properties, and the HTTP method is chosen by the terminal call: `query` (GET), `mutate` (POST), `delete` (DELETE).
|
|
71
|
+
|
|
72
|
+
```tsx apps/vendor/src/routes/sales-summary/page.tsx
|
|
73
|
+
import { useQuery } from "@tanstack/react-query"
|
|
74
|
+
import { Container, Heading, Text } from "@medusajs/ui"
|
|
75
|
+
import { ChartBar } from "@medusajs/icons"
|
|
76
|
+
import type { RouteConfig } from "@mercurjs/dashboard-sdk"
|
|
77
|
+
import type { InferClientOutput } from "@mercurjs/client"
|
|
78
|
+
import { client } from "../../lib/client"
|
|
79
|
+
|
|
80
|
+
export const config: RouteConfig = {
|
|
81
|
+
label: "Sales summary",
|
|
82
|
+
icon: ChartBar,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type Summary = InferClientOutput<typeof client.vendor.salesSummary.query>
|
|
86
|
+
|
|
87
|
+
export default function SalesSummaryPage() {
|
|
88
|
+
const { data } = useQuery<Summary>({
|
|
89
|
+
queryKey: ["sales-summary"],
|
|
90
|
+
queryFn: () => client.vendor.salesSummary.query(),
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Container className="divide-y p-0">
|
|
95
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
96
|
+
<Heading>Sales summary</Heading>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="px-6 py-4">
|
|
99
|
+
<Text size="small" className="text-ui-fg-subtle">
|
|
100
|
+
Orders: {data?.order_count ?? "—"}
|
|
101
|
+
</Text>
|
|
102
|
+
</div>
|
|
103
|
+
</Container>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`InferClientOutput` extracts the response type straight from the client method — change the route's response shape, rerun codegen, and this component stops compiling until you update it.
|
|
109
|
+
</Step>
|
|
110
|
+
</Steps>
|
|
111
|
+
|
|
112
|
+
## Verify
|
|
113
|
+
|
|
114
|
+
1. Start the project (`bun run dev`) and log into the vendor portal.
|
|
115
|
+
2. **Sales summary** appears in the sidebar (the `config` export registered it); the page shows the order count.
|
|
116
|
+
3. `curl http://localhost:9000/vendor/sales-summary` without a token returns an authentication error — the vendor middleware guards your route.
|
|
117
|
+
4. Change the route to return `{ count: ... }` instead of `{ order_count: ... }`, rerun codegen, and confirm the page fails to type-check — that's the generated contract doing its job. Revert after.
|
|
118
|
+
|
|
119
|
+
## FAQ
|
|
120
|
+
|
|
121
|
+
<AccordionGroup>
|
|
122
|
+
<Accordion title="How do path parameters work in the client?">
|
|
123
|
+
Use `$`-prefixed segments: a route at `src/api/vendor/things/[id]/route.ts` is called as `client.vendor.things.$id.query({ $id: "thing_123" })`. The `$id` key is threaded into the URL path; everything else in the object becomes query params (GET) or the JSON body (POST).
|
|
124
|
+
</Accordion>
|
|
125
|
+
<Accordion title="How do I handle errors from the client?">
|
|
126
|
+
Failed requests throw `ClientError` from `@mercurjs/client`, carrying `status`, `statusText`, and the backend's `message`. Wrap calls in try/catch or let TanStack Query surface the error. Full reference: [API Client](/rc/tools/api-client).
|
|
127
|
+
</Accordion>
|
|
128
|
+
<Accordion title="Where do request validation schemas go?">
|
|
129
|
+
Follow Medusa conventions: a `validators.ts` next to the route with a Zod schema, wired through the route's middleware. Codegen reads validators too, so the client's input type reflects them.
|
|
130
|
+
</Accordion>
|
|
131
|
+
</AccordionGroup>
|
|
132
|
+
|
|
133
|
+
## Next steps
|
|
134
|
+
|
|
135
|
+
<CardGroup cols={2}>
|
|
136
|
+
<Card title="API Client" href="/rc/tools/api-client">
|
|
137
|
+
Everything the typed client can do — inputs, outputs, errors, React Query.
|
|
138
|
+
</Card>
|
|
139
|
+
<Card title="Extend a workflow" href="/rc/resources/customization/extend-a-workflow">
|
|
140
|
+
Put multi-step business logic behind your endpoint with rollback support.
|
|
141
|
+
</Card>
|
|
142
|
+
</CardGroup>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Add a custom panel page"
|
|
3
|
+
description: "Add a page to the vendor portal with file-based routing — the dashboard SDK wires it in automatically."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Adding a page to the admin panel or vendor portal takes one file. The dashboard SDK scans `src/routes/` at build time, registers the route, and — if you export a `config` — adds it to the sidebar with a label and icon. No route table, no registration call.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**Adding vs changing.** This tutorial *adds* a brand-new page — the right move for new features. To *change* an existing page, don't rebuild it: drop a route at the same path and [re-compose the built-in page's slots](/rc/resources/tutorials/recompose-a-page). The [decision guide](/rc/resources/customization/extending-panels#choosing-your-extension-mechanism) compares every extension mechanism.
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## What you'll build
|
|
13
|
+
|
|
14
|
+
A `/reviews` page in the vendor portal with a sidebar entry — from a single file.
|
|
15
|
+
|
|
16
|
+
## Add the page
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Create the route file">
|
|
20
|
+
Drop a `page.tsx` under `src/routes/` in your vendor app. The file path becomes the URL, and the default export is the page:
|
|
21
|
+
|
|
22
|
+
```tsx apps/vendor/src/routes/reviews/page.tsx
|
|
23
|
+
import { Container, Heading } from "@medusajs/ui"
|
|
24
|
+
import { Star } from "@medusajs/icons"
|
|
25
|
+
import type { RouteConfig } from "@mercurjs/dashboard-sdk"
|
|
26
|
+
|
|
27
|
+
export const config: RouteConfig = {
|
|
28
|
+
label: "Reviews",
|
|
29
|
+
icon: Star,
|
|
30
|
+
rank: 10,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default function ReviewsPage() {
|
|
34
|
+
return (
|
|
35
|
+
<Container className="divide-y p-0">
|
|
36
|
+
<div className="flex items-center justify-between px-6 py-4">
|
|
37
|
+
<Heading>Reviews</Heading>
|
|
38
|
+
</div>
|
|
39
|
+
</Container>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
</Step>
|
|
44
|
+
<Step title="Let the SDK wire it up">
|
|
45
|
+
At build time the SDK registers the `/reviews` route, generates the sidebar item from `config` (`label`, `icon`, `rank`), and hot-reloads the route tree when you add or remove page files. Dynamic segments use brackets — `src/routes/reviews/[id]/page.tsx` becomes `/reviews/:id`. The full path-to-route table is in [Extending Panels](/rc/resources/customization/extending-panels#routing-conventions).
|
|
46
|
+
</Step>
|
|
47
|
+
<Step title="Open it in the running panel">
|
|
48
|
+
Start the project (`bun run dev`) and open the vendor portal — **Reviews** appears in the sidebar at the position set by `rank`, and `/reviews` renders your component.
|
|
49
|
+
</Step>
|
|
50
|
+
</Steps>
|
|
51
|
+
|
|
52
|
+
## Verify
|
|
53
|
+
|
|
54
|
+
1. The sidebar shows **Reviews** with the star icon.
|
|
55
|
+
2. Navigating to `/reviews` renders the page inside the standard panel layout (sidebar and topbar intact).
|
|
56
|
+
3. Removing the `config` export keeps the route working but drops the sidebar item.
|
|
57
|
+
4. Deleting the file removes the route entirely.
|
|
58
|
+
|
|
59
|
+
## FAQ
|
|
60
|
+
|
|
61
|
+
<AccordionGroup>
|
|
62
|
+
<Accordion title="How do I fetch data on my page?">
|
|
63
|
+
Use the [typed API client](/rc/tools/api-client) with TanStack Query — the panels already ship both. For a full loop including a custom backend endpoint, follow [Add a custom API route](/rc/resources/tutorials/custom-api-route).
|
|
64
|
+
</Accordion>
|
|
65
|
+
<Accordion title="Can the page live under /settings?">
|
|
66
|
+
Yes — place the file under a `settings/` route segment and set `nested: "/settings"` in the config to group its sidebar item under Settings.
|
|
67
|
+
</Accordion>
|
|
68
|
+
<Accordion title="Does the same work for the admin panel?">
|
|
69
|
+
Identically — drop the file in the admin app's `src/routes/` instead. Both panels use the same SDK and conventions.
|
|
70
|
+
</Accordion>
|
|
71
|
+
</AccordionGroup>
|
|
72
|
+
|
|
73
|
+
## Next steps
|
|
74
|
+
|
|
75
|
+
<CardGroup cols={2}>
|
|
76
|
+
<Card title="Re-compose a built-in page" href="/rc/resources/tutorials/recompose-a-page" />
|
|
77
|
+
<Card title="Add a custom API route" href="/rc/resources/tutorials/custom-api-route" />
|
|
78
|
+
</CardGroup>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Build your first marketplace"
|
|
3
|
+
description: "Go end-to-end: create a project, approve a seller, list a product, place a split order, and watch a payout settle."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<Note>
|
|
7
|
+
This tutorial is being written. The outline below is the intended structure.
|
|
8
|
+
</Note>
|
|
9
|
+
|
|
10
|
+
<Info>
|
|
11
|
+
**One order, three perspectives.** The same purchase looks different on every surface: the customer sees one checkout, the operator sees an order group with per-seller child orders, and each seller sees only their slice. Keeping those three views straight is the core mental model of a Mercur marketplace — this tutorial walks all three.
|
|
12
|
+
</Info>
|
|
13
|
+
|
|
14
|
+
## What you'll build
|
|
15
|
+
|
|
16
|
+
A working marketplace with one approved seller, one product, and one split order that settles into a payout. Prerequisites listed up front.
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Create the project">
|
|
20
|
+
Condensed link back to [Installation](/rc/learn/installation).
|
|
21
|
+
</Step>
|
|
22
|
+
<Step title="Approve a seller">
|
|
23
|
+
From the Admin Panel.
|
|
24
|
+
</Step>
|
|
25
|
+
<Step title="List a product">
|
|
26
|
+
From the Vendor Panel.
|
|
27
|
+
</Step>
|
|
28
|
+
<Step title="Place a split order">
|
|
29
|
+
From the storefront or Store API.
|
|
30
|
+
</Step>
|
|
31
|
+
<Step title="See the payout">
|
|
32
|
+
From the Vendor Panel.
|
|
33
|
+
</Step>
|
|
34
|
+
</Steps>
|
|
35
|
+
|
|
36
|
+
## Verify
|
|
37
|
+
|
|
38
|
+
A checklist of what "done" looks like — the observable end state to confirm every step worked.
|
|
39
|
+
|
|
40
|
+
## Next steps
|
|
41
|
+
|
|
42
|
+
<CardGroup cols={2}>
|
|
43
|
+
<Card title="Add a feature with a block" href="/rc/resources/tutorials/add-a-block" />
|
|
44
|
+
<Card title="Configure commissions" href="/rc/resources/tutorials/configure-commissions" />
|
|
45
|
+
</CardGroup>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Handle product requests"
|
|
3
|
+
description: "Walk a vendor's product submission and a follow-up edit through the operator review pipeline — confirm, decline, or send back."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Because Mercur's catalog is [shared](/rc/learn/products), vendors never mutate products directly. New products and edits arrive as **product changes** — reviewable requests the operator confirms, declines, or sends back for revision. This tutorial plays both roles: the vendor submitting, and the operator reviewing.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**Nothing touches the live product while a change is pending.** A `ProductChange` bundles ordered actions (`UPDATE`, `VARIANT_ADD`, `ATTRIBUTE_UPDATE`, …) that are applied only on confirmation. Declining or canceling leaves the product exactly as it was — and confirmed changes are never deleted, so the same records double as the product's audit trail.
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## What you'll build
|
|
13
|
+
|
|
14
|
+
One complete review cycle: a vendor submits a new product, the operator requests a revision, the vendor resubmits, the operator publishes. Then a smaller edit request on the now-live product.
|
|
15
|
+
|
|
16
|
+
## Review outcomes
|
|
17
|
+
|
|
18
|
+
The review queue on the admin product detail offers three outcomes:
|
|
19
|
+
|
|
20
|
+
| Action | Effect |
|
|
21
|
+
|--------|--------|
|
|
22
|
+
| **Confirm** | The product is published into the shared catalog |
|
|
23
|
+
| **Reject** | Status becomes `rejected`; the product stays out of the catalog |
|
|
24
|
+
| **Request changes** | The submission returns to the vendor with a reason (`requires_action_by` / `requires_action_reason`) |
|
|
25
|
+
|
|
26
|
+
## Run the review cycle
|
|
27
|
+
|
|
28
|
+
<Steps>
|
|
29
|
+
<Step title="Submit a product as the vendor">
|
|
30
|
+
In the Vendor Portal, create a product. Vendor-created products enter the catalog with status **`proposed`** — visible only to their creator, not to other sellers or the storefront.
|
|
31
|
+
|
|
32
|
+
Behind the scenes the platform records a `ProductChange` with a `PRODUCT_ADD` action and `created_by` set to the submitting member. That attribution is what keeps the seller's unpublished submissions in their own product list.
|
|
33
|
+
</Step>
|
|
34
|
+
<Step title="Request changes as the operator">
|
|
35
|
+
In the Admin Panel, open the product and choose **Request changes** with a note — say, "add product dimensions". Use the external note for the vendor-visible message; internal notes stay operator-only.
|
|
36
|
+
</Step>
|
|
37
|
+
<Step title="Revise and resubmit as the vendor">
|
|
38
|
+
Back in the Vendor Portal, the product page shows the revision request and the operator's note. The vendor updates the product and resubmits. As the operator, **Confirm** this time — the product's status flips to `published` and it enters the shared catalog for every eligible seller.
|
|
39
|
+
</Step>
|
|
40
|
+
<Step title="Request an edit on the live product">
|
|
41
|
+
Now that the product is published, edits also flow through review. As the vendor, change the product's description. This creates a new `ProductChange` in `pending` with an `UPDATE` action — the live product is unchanged until the operator confirms. Confirm it and watch the description update.
|
|
42
|
+
</Step>
|
|
43
|
+
</Steps>
|
|
44
|
+
|
|
45
|
+
<Note>
|
|
46
|
+
Any seller may propose changes to any published product — the shared catalog is collaborative. Selling-eligibility restrictions limit who may *sell* a product, not who may *suggest improvements* to it.
|
|
47
|
+
</Note>
|
|
48
|
+
|
|
49
|
+
## Verify
|
|
50
|
+
|
|
51
|
+
1. After step 1, the product is `proposed`: visible in the submitting vendor's list, absent from other vendors' lists and the Store API.
|
|
52
|
+
2. After **Request changes**, the vendor sees the revision note; the change is flagged `requires action`.
|
|
53
|
+
3. After the final **Confirm** in step 3, the product is `published` and appears in the Store API.
|
|
54
|
+
4. During step 4, the storefront still shows the old description while the change is `pending` — and the new one after confirmation.
|
|
55
|
+
5. The product's change history lists every change with who requested it, who reviewed it, and when.
|
|
56
|
+
|
|
57
|
+
## FAQ
|
|
58
|
+
|
|
59
|
+
<AccordionGroup>
|
|
60
|
+
<Accordion title="Do all edits require operator review?">
|
|
61
|
+
No — some low-risk edits are **auto-confirmed**: they still create a change record for the audit trail but apply immediately without waiting. Structural changes (variants, attributes, status) go through review.
|
|
62
|
+
</Accordion>
|
|
63
|
+
<Accordion title="Can the vendor withdraw a pending request?">
|
|
64
|
+
Yes — a pending change can be **canceled** by its requester, recorded with the same audit fields (`canceled_by`, timestamp). Once confirmed or declined, the outcome is final; a new change is needed for further edits.
|
|
65
|
+
</Accordion>
|
|
66
|
+
<Accordion title="What's the difference between rejecting and requesting changes?">
|
|
67
|
+
**Reject** is terminal for a submission — the product is marked `rejected` and stays out of the catalog. **Request changes** keeps the change alive and hands it back to the vendor with a reason; it's the "revise and resubmit" path.
|
|
68
|
+
</Accordion>
|
|
69
|
+
</AccordionGroup>
|
|
70
|
+
|
|
71
|
+
## Next steps
|
|
72
|
+
|
|
73
|
+
<CardGroup cols={2}>
|
|
74
|
+
<Card title="Product Requests & Approvals" href="/rc/learn/product-requests">
|
|
75
|
+
The full ProductChange data model, actions, and lifecycle.
|
|
76
|
+
</Card>
|
|
77
|
+
<Card title="Master products & offers" href="/rc/resources/tutorials/master-products-and-offers">
|
|
78
|
+
What happens after publication — sellers listing against the shared record.
|
|
79
|
+
</Card>
|
|
80
|
+
</CardGroup>
|