@mercurjs/docs 2.2.1 → 2.3.0-canary.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.
Files changed (316) hide show
  1. package/content/home.mdx +107 -0
  2. package/content/learn/architecture.mdx +168 -83
  3. package/content/learn/introduction.mdx +161 -56
  4. package/content/learn/migration-to-2-0.mdx +187 -0
  5. package/content/learn/why-mercur.mdx +91 -0
  6. package/content/platform/attribute/concepts/attribute-types.mdx +59 -0
  7. package/content/platform/attribute/concepts/global-vs-inline.mdx +50 -0
  8. package/content/platform/attribute/concepts/variant-axes.mdx +61 -0
  9. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +69 -0
  10. package/content/platform/attribute/guides/create-a-variant-axis.mdx +83 -0
  11. package/content/platform/attribute/guides/create-an-attribute.mdx +81 -0
  12. package/content/platform/attribute/overview.mdx +87 -0
  13. package/content/platform/attribute/reference/data-models.mdx +56 -0
  14. package/content/platform/attribute/reference/events.mdx +41 -0
  15. package/content/platform/attribute/reference/links.mdx +45 -0
  16. package/content/platform/attribute/reference/service.mdx +49 -0
  17. package/content/platform/attribute/reference/workflows.mdx +45 -0
  18. package/content/platform/catalog/concepts/master-products.mdx +54 -0
  19. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +48 -0
  20. package/content/platform/catalog/concepts/status-lifecycle.mdx +58 -0
  21. package/content/platform/catalog/concepts/variants-categories-collections.mdx +49 -0
  22. package/content/platform/catalog/guides/allowlist-stores.mdx +59 -0
  23. package/content/platform/catalog/guides/create-a-master-product.mdx +70 -0
  24. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +79 -0
  25. package/content/platform/catalog/overview.mdx +90 -0
  26. package/content/platform/catalog/reference/data-models.mdx +66 -0
  27. package/content/platform/catalog/reference/events.mdx +41 -0
  28. package/content/platform/catalog/reference/links.mdx +44 -0
  29. package/content/platform/catalog/reference/service.mdx +52 -0
  30. package/content/platform/catalog/reference/workflows.mdx +40 -0
  31. package/content/platform/commission/concepts/order-commission-lines.mdx +65 -0
  32. package/content/platform/commission/concepts/rule-matching.mdx +86 -0
  33. package/content/platform/commission/concepts/rules-and-rates.mdx +84 -0
  34. package/content/platform/commission/guides/batch-update-rules.mdx +58 -0
  35. package/content/platform/commission/guides/create-a-rate.mdx +72 -0
  36. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +55 -0
  37. package/content/platform/commission/overview.mdx +86 -0
  38. package/content/platform/commission/reference/data-models.mdx +75 -0
  39. package/content/platform/commission/reference/events.mdx +58 -0
  40. package/content/platform/commission/reference/links.mdx +45 -0
  41. package/content/platform/commission/reference/service.mdx +51 -0
  42. package/content/platform/commission/reference/workflows.mdx +40 -0
  43. package/content/platform/offer/concepts/pricing-and-inventory.mdx +72 -0
  44. package/content/platform/offer/concepts/shipping.mdx +48 -0
  45. package/content/platform/offer/concepts/what-is-an-offer.mdx +65 -0
  46. package/content/platform/offer/guides/bulk-create-offers.mdx +84 -0
  47. package/content/platform/offer/guides/create-an-offer.mdx +83 -0
  48. package/content/platform/offer/guides/manage-offer-inventory.mdx +53 -0
  49. package/content/platform/offer/overview.mdx +84 -0
  50. package/content/platform/offer/reference/data-models.mdx +56 -0
  51. package/content/platform/offer/reference/events.mdx +39 -0
  52. package/content/platform/offer/reference/links.mdx +60 -0
  53. package/content/platform/offer/reference/service.mdx +53 -0
  54. package/content/platform/offer/reference/workflows.mdx +38 -0
  55. package/content/platform/order-group/concepts/computed-totals.mdx +59 -0
  56. package/content/platform/order-group/concepts/order-splitting.mdx +61 -0
  57. package/content/platform/order-group/concepts/the-order-group.mdx +64 -0
  58. package/content/platform/order-group/guides/list-order-groups.mdx +67 -0
  59. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +63 -0
  60. package/content/platform/order-group/guides/split-a-cart.mdx +58 -0
  61. package/content/platform/order-group/overview.mdx +83 -0
  62. package/content/platform/order-group/reference/data-models.mdx +40 -0
  63. package/content/platform/order-group/reference/events.mdx +37 -0
  64. package/content/platform/order-group/reference/links.mdx +45 -0
  65. package/content/platform/order-group/reference/service.mdx +50 -0
  66. package/content/platform/order-group/reference/workflows.mdx +39 -0
  67. package/content/platform/payout/concepts/account-lifecycle.mdx +75 -0
  68. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +70 -0
  69. package/content/platform/payout/concepts/payout-pipeline.mdx +99 -0
  70. package/content/platform/payout/guides/create-a-payout-account.mdx +59 -0
  71. package/content/platform/payout/guides/process-a-provider-webhook.mdx +74 -0
  72. package/content/platform/payout/guides/start-provider-onboarding.mdx +50 -0
  73. package/content/platform/payout/overview.mdx +86 -0
  74. package/content/platform/payout/reference/data-models.mdx +61 -0
  75. package/content/platform/payout/reference/events.mdx +48 -0
  76. package/content/platform/payout/reference/links.mdx +36 -0
  77. package/content/platform/payout/reference/service.mdx +53 -0
  78. package/content/platform/payout/reference/workflows.mdx +32 -0
  79. package/content/platform/product-edit/concepts/change-actions.mdx +66 -0
  80. package/content/platform/product-edit/concepts/change-pipeline.mdx +69 -0
  81. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +66 -0
  82. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +76 -0
  83. package/content/platform/product-edit/guides/edit-a-product.mdx +74 -0
  84. package/content/platform/product-edit/guides/request-a-revision.mdx +54 -0
  85. package/content/platform/product-edit/overview.mdx +85 -0
  86. package/content/platform/product-edit/reference/data-models.mdx +65 -0
  87. package/content/platform/product-edit/reference/events.mdx +47 -0
  88. package/content/platform/product-edit/reference/links.mdx +39 -0
  89. package/content/platform/product-edit/reference/service.mdx +43 -0
  90. package/content/platform/product-edit/reference/workflows.mdx +49 -0
  91. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +63 -0
  92. package/content/platform/review/concepts/ratings-and-moderation.mdx +64 -0
  93. package/content/platform/review/concepts/the-review-model.mdx +56 -0
  94. package/content/platform/review/guides/compute-aggregate-ratings.mdx +57 -0
  95. package/content/platform/review/guides/create-a-review.mdx +55 -0
  96. package/content/platform/review/guides/moderate-a-review.mdx +58 -0
  97. package/content/platform/review/guides/respond-to-a-review.mdx +61 -0
  98. package/content/platform/review/overview.mdx +87 -0
  99. package/content/platform/review/reference/data-models.mdx +36 -0
  100. package/content/platform/review/reference/events.mdx +61 -0
  101. package/content/platform/review/reference/links.mdx +43 -0
  102. package/content/platform/review/reference/service.mdx +54 -0
  103. package/content/platform/review/reference/workflows.mdx +31 -0
  104. package/content/platform/store/concepts/lifecycle.mdx +62 -0
  105. package/content/platform/store/concepts/store-entity.mdx +53 -0
  106. package/content/platform/store/concepts/team.mdx +50 -0
  107. package/content/platform/store/guides/create-a-store.mdx +55 -0
  108. package/content/platform/store/guides/manage-the-team.mdx +55 -0
  109. package/content/platform/store/guides/moderate-a-store.mdx +59 -0
  110. package/content/platform/store/overview.mdx +86 -0
  111. package/content/platform/store/reference/data-models.mdx +89 -0
  112. package/content/platform/store/reference/events.mdx +43 -0
  113. package/content/platform/store/reference/links.mdx +71 -0
  114. package/content/platform/store/reference/service.mdx +51 -0
  115. package/content/platform/store/reference/workflows.mdx +35 -0
  116. package/content/references/api/admin/commission-rates/create-commission-rate.mdx +1 -1
  117. package/content/references/api/admin/commission-rates/list-commission-rates.mdx +2 -2
  118. package/content/references/api/admin/commission-rates/update-commission-rate.mdx +1 -1
  119. package/content/references/api/admin/offers/batch-create-offers.mdx +3 -3
  120. package/content/references/api/admin/order-groups/list-order-groups.mdx +1 -1
  121. package/content/references/api/admin/product-attributes/create-attribute-value.mdx +2 -2
  122. package/content/references/api/admin/product-attributes/create-product-attribute.mdx +1 -1
  123. package/content/references/api/admin/product-attributes/update-product-attribute.mdx +1 -1
  124. package/content/references/api/admin/product-changes/confirm-product-change.mdx +1 -1
  125. package/content/references/api/admin/products/batch-product-attributes.mdx +1 -1
  126. package/content/references/api/admin/products/create-product.mdx +1 -1
  127. package/content/references/api/admin/products/preview-product.mdx +1 -1
  128. package/content/references/api/admin.mdx +4 -5
  129. package/content/references/api/conventions.mdx +9 -7
  130. package/content/references/api/store/carts/add-line-item.mdx +1 -1
  131. package/content/references/api/store/offers/list-offers.mdx +1 -1
  132. package/content/references/api/store/order-groups/list-order-groups.mdx +1 -1
  133. package/content/references/api/store.mdx +4 -13
  134. package/content/references/api/vendor/members/accept-member-invite.mdx +1 -1
  135. package/content/references/api/vendor/offers/batch-create-offers.mdx +4 -0
  136. package/content/references/api/vendor/offers/batch-offer-inventory-items.mdx +2 -0
  137. package/content/references/api/vendor/offers/create-offer.mdx +13 -1
  138. package/content/references/api/vendor/offers/list-offers.mdx +4 -0
  139. package/content/references/api/vendor/offers/retrieve-offer.mdx +4 -0
  140. package/content/references/api/vendor/offers/update-offer.mdx +12 -0
  141. package/content/references/api/vendor/payout-accounts/create-onboarding.mdx +1 -1
  142. package/content/references/api/vendor/products/batch-product-attributes.mdx +1 -1
  143. package/content/references/api/vendor/products/create-product-variant.mdx +1 -1
  144. package/content/references/api/vendor/products/create-product.mdx +1 -1
  145. package/content/references/api/vendor/products/delete-product.mdx +1 -1
  146. package/content/references/api/vendor/products/update-product.mdx +1 -1
  147. package/content/references/api/vendor/sellers/create-seller.mdx +2 -2
  148. package/content/references/api/vendor/sellers/list-sellers.mdx +1 -1
  149. package/content/references/api/vendor.mdx +6 -5
  150. package/content/references/configuration.mdx +16 -33
  151. package/content/references/overview.mdx +34 -52
  152. package/content/references/panel-extensions/create-page.mdx +194 -0
  153. package/content/references/panel-extensions/custom-fields.mdx +256 -0
  154. package/content/references/panel-extensions/overview.mdx +102 -0
  155. package/content/references/panel-extensions/widgets.mdx +212 -0
  156. package/content/resources/ai/mcp.mdx +2 -2
  157. package/content/resources/ai/overview.mdx +21 -16
  158. package/content/resources/ai/skills.mdx +67 -0
  159. package/content/resources/best-practices/api-routes.mdx +55 -43
  160. package/content/resources/best-practices/custom-fields.mdx +116 -92
  161. package/content/resources/best-practices/frontend.mdx +62 -50
  162. package/content/resources/best-practices/module-links.mdx +48 -34
  163. package/content/resources/best-practices/modules.mdx +53 -27
  164. package/content/resources/best-practices/overview.mdx +45 -17
  165. package/content/resources/best-practices/subscribers-and-jobs.mdx +37 -24
  166. package/content/resources/best-practices/types.mdx +38 -23
  167. package/content/resources/best-practices/workflows.mdx +33 -21
  168. package/content/resources/customization/custom-fields.mdx +15 -15
  169. package/content/resources/customization/extend-a-workflow.mdx +7 -4
  170. package/content/resources/customization/extending-panels.mdx +55 -52
  171. package/content/resources/deployment/medusa-cloud.mdx +21 -20
  172. package/content/resources/deployment/self-host.mdx +123 -0
  173. package/content/resources/integrations/overview.mdx +38 -0
  174. package/content/resources/integrations/stripe-connect.mdx +39 -38
  175. package/content/resources/tutorials/add-a-block.mdx +25 -18
  176. package/content/resources/tutorials/add-a-widget.mdx +32 -23
  177. package/content/resources/tutorials/add-order-detail-button.mdx +33 -20
  178. package/content/resources/tutorials/attributes-and-variant-axes.mdx +28 -27
  179. package/content/resources/tutorials/build-a-block.mdx +26 -15
  180. package/content/resources/tutorials/custom-api-route.mdx +32 -20
  181. package/content/resources/tutorials/custom-panel-page.mdx +21 -12
  182. package/content/resources/tutorials/customize-navigation.mdx +30 -23
  183. package/content/resources/tutorials/extend-forms-and-tables.mdx +36 -28
  184. package/content/resources/tutorials/extend-onboarding.mdx +38 -35
  185. package/content/resources/tutorials/master-products-and-offers.mdx +28 -23
  186. package/content/telemetry.mdx +3 -3
  187. package/content/user-guide/admin/attributes/how-tos/create-an-attribute.mdx +64 -0
  188. package/content/user-guide/admin/attributes/how-tos/manage-possible-values.mdx +40 -0
  189. package/content/user-guide/admin/attributes/overview.mdx +22 -0
  190. package/content/user-guide/admin/commissions/how-tos/create-a-commission-rule.mdx +63 -0
  191. package/content/user-guide/admin/commissions/how-tos/edit-the-global-commission.mdx +48 -0
  192. package/content/user-guide/admin/commissions/how-tos/manage-a-commission-rule.mdx +45 -0
  193. package/content/user-guide/admin/commissions/overview.mdx +25 -0
  194. package/content/user-guide/admin/overview.mdx +20 -12
  195. package/content/user-guide/admin/product-requests/how-tos/review-a-new-product.mdx +58 -0
  196. package/content/user-guide/admin/product-requests/how-tos/review-a-product-edit.mdx +48 -0
  197. package/content/user-guide/admin/product-requests/overview.mdx +25 -0
  198. package/content/user-guide/vendor/offers/how-tos/create-an-offer.mdx +59 -0
  199. package/content/user-guide/vendor/offers/how-tos/update-prices-and-stock.mdx +40 -0
  200. package/content/user-guide/vendor/offers/overview.mdx +22 -0
  201. package/content/user-guide/vendor/onboarding.mdx +79 -0
  202. package/content/user-guide/vendor/orders/how-tos/fulfill-an-order.mdx +49 -0
  203. package/content/user-guide/vendor/orders/how-tos/mark-an-order-as-delivered.mdx +33 -0
  204. package/content/user-guide/vendor/orders/how-tos/process-a-return.mdx +42 -0
  205. package/content/user-guide/vendor/orders/how-tos/refund-an-order.mdx +38 -0
  206. package/content/user-guide/vendor/orders/how-tos/ship-an-order.mdx +40 -0
  207. package/content/user-guide/vendor/orders/overview.mdx +31 -0
  208. package/content/user-guide/vendor/overview.mdx +23 -12
  209. package/content/user-guide/vendor/products/how-tos/edit-a-product.mdx +44 -0
  210. package/content/user-guide/vendor/products/how-tos/submit-a-product.mdx +63 -0
  211. package/content/user-guide/vendor/products/overview.mdx +22 -0
  212. package/llms.txt +176 -142
  213. package/package.json +1 -1
  214. package/content/learn/concepts.mdx +0 -84
  215. package/content/learn/installation.mdx +0 -117
  216. package/content/learn/mirakl-alternative.mdx +0 -86
  217. package/content/migration/from-1-x-to-2-0.mdx +0 -152
  218. package/content/migration/from-2-0-to-2-1.mdx +0 -105
  219. package/content/migration/overview.mdx +0 -58
  220. package/content/references/api/store/search/search.mdx +0 -136
  221. package/content/references/modules/commission.mdx +0 -106
  222. package/content/references/modules/custom-fields.mdx +0 -45
  223. package/content/references/modules/media.mdx +0 -55
  224. package/content/references/modules/offer.mdx +0 -64
  225. package/content/references/modules/payout.mdx +0 -121
  226. package/content/references/modules/product-attribute.mdx +0 -111
  227. package/content/references/modules/product-edit.mdx +0 -80
  228. package/content/references/modules/search.mdx +0 -112
  229. package/content/references/modules/seller.mdx +0 -175
  230. package/content/references/panel-extension-api.mdx +0 -337
  231. package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +0 -48
  232. package/content/references/workflows/cart/complete-cart-with-split-orders.mdx +0 -36
  233. package/content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx +0 -39
  234. package/content/references/workflows/cart/update-cart-seller-promotions.mdx +0 -42
  235. package/content/references/workflows/commission/batch-commission-rules.mdx +0 -49
  236. package/content/references/workflows/commission/create-commission-rates.mdx +0 -41
  237. package/content/references/workflows/commission/delete-commission-rates.mdx +0 -34
  238. package/content/references/workflows/commission/refresh-order-commission-lines.mdx +0 -30
  239. package/content/references/workflows/commission/update-commission-rates.mdx +0 -35
  240. package/content/references/workflows/media/set-category-images.mdx +0 -41
  241. package/content/references/workflows/media/set-collection-images.mdx +0 -41
  242. package/content/references/workflows/member/accept-member-invite.mdx +0 -33
  243. package/content/references/workflows/member/add-seller-member.mdx +0 -30
  244. package/content/references/workflows/member/create-member-invites.mdx +0 -34
  245. package/content/references/workflows/member/delete-member-invite.mdx +0 -24
  246. package/content/references/workflows/member/remove-seller-member.mdx +0 -28
  247. package/content/references/workflows/member/resend-member-invite.mdx +0 -28
  248. package/content/references/workflows/member/update-member-role.mdx +0 -28
  249. package/content/references/workflows/member/update-member.mdx +0 -35
  250. package/content/references/workflows/offer/batch-offer-inventory-items.mdx +0 -62
  251. package/content/references/workflows/offer/create-offers.mdx +0 -65
  252. package/content/references/workflows/offer/delete-offers.mdx +0 -35
  253. package/content/references/workflows/offer/update-offers.mdx +0 -53
  254. package/content/references/workflows/order/cancel-order-fulfillment.mdx +0 -34
  255. package/content/references/workflows/order/confirm-claim-request.mdx +0 -25
  256. package/content/references/workflows/order/confirm-exchange-request.mdx +0 -25
  257. package/content/references/workflows/order/confirm-order-edit-request.mdx +0 -25
  258. package/content/references/workflows/order/confirm-return-receive.mdx +0 -25
  259. package/content/references/workflows/order/create-order-fulfillment.mdx +0 -45
  260. package/content/references/workflows/order-group/get-order-group-detail.mdx +0 -29
  261. package/content/references/workflows/order-group/get-order-groups-list.mdx +0 -38
  262. package/content/references/workflows/overview.mdx +0 -72
  263. package/content/references/workflows/payout/create-onboarding.mdx +0 -36
  264. package/content/references/workflows/payout/create-payout-account.mdx +0 -33
  265. package/content/references/workflows/payout/create-payout.mdx +0 -30
  266. package/content/references/workflows/payout/process-payout-for-webhook.mdx +0 -34
  267. package/content/references/workflows/product/confirm-products.mdx +0 -48
  268. package/content/references/workflows/product/create-products.mdx +0 -63
  269. package/content/references/workflows/product/link-sellers-to-product-category.mdx +0 -40
  270. package/content/references/workflows/product/link-sellers-to-product.mdx +0 -40
  271. package/content/references/workflows/product/reject-product.mdx +0 -48
  272. package/content/references/workflows/product/request-product-change.mdx +0 -48
  273. package/content/references/workflows/product-attribute/add-product-attributes-to-product.mdx +0 -49
  274. package/content/references/workflows/product-attribute/create-and-link-product-attributes-to-product.mdx +0 -46
  275. package/content/references/workflows/product-attribute/create-product-attribute-values.mdx +0 -43
  276. package/content/references/workflows/product-attribute/create-product-attributes.mdx +0 -56
  277. package/content/references/workflows/product-attribute/delete-product-attribute-values.mdx +0 -30
  278. package/content/references/workflows/product-attribute/delete-product-attributes.mdx +0 -30
  279. package/content/references/workflows/product-attribute/remove-product-attributes-from-product.mdx +0 -29
  280. package/content/references/workflows/product-attribute/update-product-attribute-values.mdx +0 -44
  281. package/content/references/workflows/product-attribute/update-product-attributes-on-product.mdx +0 -41
  282. package/content/references/workflows/product-attribute/update-product-attributes.mdx +0 -48
  283. package/content/references/workflows/product-attribute/upsert-product-attribute-values.mdx +0 -43
  284. package/content/references/workflows/product-edit/auto-confirm-product-change.mdx +0 -39
  285. package/content/references/workflows/product-edit/cancel-product-change.mdx +0 -49
  286. package/content/references/workflows/product-edit/confirm-product-change.mdx +0 -57
  287. package/content/references/workflows/product-edit/create-product-change.mdx +0 -72
  288. package/content/references/workflows/product-edit/reject-product-change.mdx +0 -54
  289. package/content/references/workflows/product-edit/stage-product-change.mdx +0 -75
  290. package/content/references/workflows/seller/approve-seller.mdx +0 -36
  291. package/content/references/workflows/seller/create-seller-account.mdx +0 -59
  292. package/content/references/workflows/seller/create-seller-defaults.mdx +0 -22
  293. package/content/references/workflows/seller/create-sellers.mdx +0 -65
  294. package/content/references/workflows/seller/delete-seller-professional-details.mdx +0 -37
  295. package/content/references/workflows/seller/delete-sellers.mdx +0 -24
  296. package/content/references/workflows/seller/invite-seller.mdx +0 -28
  297. package/content/references/workflows/seller/suspend-seller.mdx +0 -37
  298. package/content/references/workflows/seller/terminate-seller.mdx +0 -37
  299. package/content/references/workflows/seller/unsuspend-seller.mdx +0 -36
  300. package/content/references/workflows/seller/unterminate-seller.mdx +0 -36
  301. package/content/references/workflows/seller/update-seller-address.mdx +0 -55
  302. package/content/references/workflows/seller/update-seller-payment-details.mdx +0 -52
  303. package/content/references/workflows/seller/update-seller-professional-details.mdx +0 -48
  304. package/content/references/workflows/seller/update-sellers.mdx +0 -57
  305. package/content/resources/ai/llms.mdx +0 -74
  306. package/content/resources/integrations/notifications.mdx +0 -39
  307. package/content/resources/integrations/search.mdx +0 -122
  308. package/content/resources/tutorials/configure-commissions.mdx +0 -127
  309. package/content/resources/tutorials/first-marketplace.mdx +0 -45
  310. package/content/resources/tutorials/handle-product-requests.mdx +0 -80
  311. package/content/resources/tutorials/import-export-products.mdx +0 -96
  312. package/content/resources/tutorials/seller-payouts-stripe.mdx +0 -89
  313. package/content/resources/tutorials/store-setup-checklist.mdx +0 -214
  314. package/content/tools/api-client.mdx +0 -155
  315. package/content/tools/cli.mdx +0 -196
  316. package/content/tools/dashboard-sdk.mdx +0 -35
@@ -1,21 +1,22 @@
1
1
  ---
2
- title: "Add a button to order details"
3
- description: "Drop a 'Copy link' button onto the vendor order detail page with a widget — no forking, no page override."
2
+ title: "How to Add an Action Button"
3
+ sidebarTitle: "Add an Action Button"
4
+ description: "Add a Copy link button to the vendor order detail page with a widget, without forking or overriding the page."
4
5
  ---
5
6
 
6
- The order detail page is a built-in panel screen you don't own. To add a small piece of UI to it — a button, a badge, a note — you don't copy the page. You drop a **widget** at one of its zones, and the SDK renders your component there while the rest of the page keeps working exactly as shipped.
7
+ Add a small piece of UI to a built-in panel screen without copying it.
7
8
 
8
- This tutorial adds a **"Copy link"** button to the order summary section that copies a link to the order.
9
+ The order detail page is a panel screen you don't own. To add a button, a badge, or a note to it, you drop a **widget** at one of its zones. The SDK renders your component there, and the rest of the page keeps working exactly as shipped. This tutorial adds a **Copy link** button to the order summary section that copies a link to the order.
9
10
 
10
11
  <Info>
11
- **Additive, not a replacement.** A widget layers your component onto a built-in page at a documented zone. Reach for it first whenever you just want to *add* something to an existing screen.
12
+ A widget is additive, not a replacement. It layers your component onto a built-in page at a documented zone. Reach for it first whenever you just want to add something to an existing screen.
12
13
  </Info>
13
14
 
14
15
  ## Add the button
15
16
 
16
17
  <Steps>
17
18
  <Step title="Create the widget file">
18
- Drop a file under `src/widgets/`. Export the component as the **default** and a `config` built with `defineWidgetConfig`. Target `orders.detail.summary.after` your component renders in the order summary section footer and receives the loaded order as `data`.
19
+ Drop a file under `src/widgets/`. Export the component as the **default** and a `config` built with `defineWidgetConfig`. Target `orders.detail.summary.after`. Your component renders in the order summary section footer and receives the loaded order as `data`.
19
20
 
20
21
  ```tsx apps/vendor/src/widgets/order-copy-link.tsx
21
22
  import { defineWidgetConfig } from "@mercurjs/dashboard-sdk"
@@ -52,24 +53,30 @@ This tutorial adds a **"Copy link"** button to the order summary section that co
52
53
  export default OrderCopyLink
53
54
  ```
54
55
  </Step>
55
- <Step title="Understand the zone id">
56
- A zone id is `<domain>.<view>.<slot>.<placement>`. The last segment is the placement:
56
+
57
+ <Step title="Read the zone id">
58
+ A zone id is `<domain>.<view>.<slot>.<placement>`. The last segment is the placement.
57
59
 
58
60
  | Placement | Effect |
59
61
  |-----------|--------|
60
62
  | `before` | Renders before the built-in content of the zone |
61
63
  | `after` | Renders after the built-in content |
62
64
 
63
- Multiple `before` / `after` widgets on the same zone stack in registration order.
65
+ Multiple `before` or `after` widgets on the same zone stack in registration order.
64
66
  </Step>
67
+
65
68
  <Step title="Reload the panel">
66
- Start the project (`bun run dev`) and open any order in the vendor portal. Widget files hot-reload the "Copy link" button appears in the summary section footer, and the rest of the page is untouched.
69
+ Start the project and open any order in the vendor portal. Widget files hot-reload. The Copy link button appears in the summary section footer, and the rest of the page is untouched.
70
+
71
+ ```bash Terminal
72
+ bun run dev
73
+ ```
67
74
  </Step>
68
75
  </Steps>
69
76
 
70
77
  ## Order detail zones
71
78
 
72
- Zones mounted on the vendor order detail page:
79
+ These zones are mounted on the vendor order detail page.
73
80
 
74
81
  | Zone | Where it renders |
75
82
  |------|------------------|
@@ -77,27 +84,33 @@ Zones mounted on the vendor order detail page:
77
84
  | `orders.detail.main.before` / `.after` | Around the main column (summary, payment, fulfillment) |
78
85
  | `orders.detail.side.before` / `.after` | Around the sidebar (customer, activity) |
79
86
 
80
- Each is passed the loaded `order` as `data`. The full, valid set is typed as `WidgetZoneId` and generated from the panel's own zone hosts let your editor autocomplete `zone:` to see every option. A zone no page renders can't be targeted and won't type-check.
87
+ Each zone is passed the loaded `order` as `data`. The full, valid set is typed as `WidgetZoneId` and generated from the panel's own zone hosts. Let your editor autocomplete `zone:` to see every option.
88
+
89
+ <Tip>
90
+ A zone that no page renders can't be targeted and won't type-check. Set `zone: "not.a.zone"` and `tsc` (`bun run lint`) fails with a "not assignable to `WidgetZoneId`" error.
91
+ </Tip>
81
92
 
82
93
  ## Verify
83
94
 
84
- 1. Open an order — the "Copy link" button renders in the summary section footer.
85
- 2. Click it — the link is copied and a toast appears.
86
- 3. Change the zone to `orders.detail.side.before` and reload the button moves to the top of the sidebar.
87
- 4. Set `zone: "not.a.zone"` `tsc` (`bun run lint`) fails with a "not assignable to `WidgetZoneId`" error.
88
- 5. Delete the file the button disappears; nothing else changed.
95
+ Confirm the widget works end to end.
96
+
97
+ 1. Open an order. The Copy link button renders in the summary section footer.
98
+ 2. Click it. The link is copied and a toast appears.
99
+ 3. Change the zone to `orders.detail.side.before` and reload. The button moves to the top of the sidebar.
100
+ 4. Set `zone: "not.a.zone"`. `tsc` (`bun run lint`) fails with a "not assignable to `WidgetZoneId`" error.
101
+ 5. Delete the file. The button disappears, and nothing else changed.
89
102
 
90
103
  ## FAQ
91
104
 
92
105
  <AccordionGroup>
93
106
  <Accordion title="What can I read from the order?">
94
- The zone passes the loaded order as `data` (`HttpTypes.AdminOrder`) id, display id, totals, items, `payment_collections`, customer, and more. Build the link (or any UI) from it.
107
+ The zone passes the loaded order as `data` (`HttpTypes.AdminOrder`): id, display id, totals, items, `payment_collections`, customer, and more. Build the link (or any UI) from it.
95
108
  </Accordion>
96
109
  <Accordion title="Can a block ship this instead of the host app?">
97
- Yes. Put the same file in a [block](/rc/learn/blocks)'s `vendor_ui` entry under `src/widgets/`; installing the block adds the button with no wiring.
110
+ Yes. Put the same file in a [block](/rc/learn/blocks)'s `vendor_ui` entry under `src/widgets/`. Installing the block adds the button with no wiring.
98
111
  </Accordion>
99
112
  <Accordion title="Can I render more than a button?">
100
- The zone renders any React component a badge, an action menu, a whole section. You have the full order in `data`.
113
+ The zone renders any React component, such as a badge, an action menu, or a whole section. You have the full order in `data`.
101
114
  </Accordion>
102
115
  </AccordionGroup>
103
116
 
@@ -1,48 +1,49 @@
1
1
  ---
2
- title: "Create attributes and variant axes"
3
- description: "Build the attribute catalog: a filterable attribute, a variant axis backed by a native product option, and an inline product-scoped axis."
2
+ title: "How to Create Variant-Axis Attributes"
3
+ sidebarTitle: "Create Variant-Axis Attributes"
4
+ description: "Build an attribute catalog with a filterable attribute, a global variant axis, and an inline product-scoped axis."
4
5
  ---
5
6
 
6
- Attributes give the shared catalog structured, typed data and for `multi_select` attributes, they can drive variant generation. This tutorial sets up the two kinds that matter most: a plain filterable attribute and a **variant axis**, which under the hood is a native Medusa global product option.
7
+ Attributes give the shared catalog structured, typed data. For `multi_select` attributes, they can also drive variant generation.
7
8
 
8
- <Warning>
9
- This is a release-candidate feature built on Medusa's global product options (Medusa 2.16 preview). Details may still shift before the stable release.
10
- </Warning>
9
+ This tutorial sets up the two kinds that matter most: a plain filterable attribute and a variant axis. A variant axis is a native Medusa global product option under the hood.
11
10
 
12
11
  <Info>
13
- **A variant axis IS a product option.** Attributes marked `is_variant_axis` aren't a parallel system bolted onto products each one mirrors one-to-one onto a Medusa `ProductOption` and its values. Variants are then built with Medusa's standard machinery (`variants[].options`), exactly as in a plain Medusa project. Non-axis attributes never become options; they attach as plain value links. This is why only `multi_select` attributes can be axes an axis needs an enumerable set of values to combine into variants.
12
+ **A variant axis IS a product option.** Attributes marked `is_variant_axis` are not a parallel system bolted onto products. Each one mirrors one-to-one onto a Medusa `ProductOption` and its values. Variants are then built with Medusa's standard machinery (`variants[].options`), exactly as in a plain Medusa project. Non-axis attributes never become options. They attach as plain value links. This is why only `multi_select` attributes can be axes: an axis needs an enumerable set of values to combine into variants.
14
13
  </Info>
15
14
 
16
15
  ## What you'll build
17
16
 
18
- - A global `Material` attribute (multi-select, filterable) used for storefront filtering.
19
- - A global `Color` variant axis shared across the catalog.
20
- - An inline, product-scoped `Fit` axis created on the fly from a product form.
17
+ - **Material:** a global `multi_select`, filterable attribute used for storefront filtering.
18
+ - **Color:** a global variant axis shared across the catalog.
19
+ - **Fit:** an inline, product-scoped axis created on the fly from a product form.
21
20
 
22
21
  ## Global vs product-scoped
23
22
 
24
23
  | Kind | Backed by | Appears in the global catalog? | Use for |
25
24
  |------|-----------|-------------------------------|---------|
26
- | Global attribute | Shared `ProductOption` (when axis) or value links | Yes | Data every product can use Material, Color, Condition |
25
+ | Global attribute | Shared `ProductOption` (when axis) or value links | Yes | Data every product can use, such as Material, Color, or Condition |
27
26
  | Product-scoped attribute | Exclusive, product-owned option (when axis) | No | One-off fields or axes for a single product |
28
27
 
29
28
  ## Build the attribute catalog
30
29
 
31
30
  <Steps>
32
31
  <Step title="Create a filterable attribute">
33
- In the Admin Panel, the operator owns the attribute catalog. Create **Material** as a global `multi_select` attribute with values like Cotton, Wool, Linen, and turn on `is_filterable`. Global attributes (`product_id = null`) can be attached to any product and linked to categories so the right attributes surface for the right product types.
32
+ In the Admin Panel, the operator owns the attribute catalog. Create **Material** as a global `multi_select` attribute with values like Cotton, Wool, or Linen, and turn on `is_filterable`.
34
33
 
35
- Since it's not a variant axis, Material describes the product it will never generate variants.
34
+ Global attributes (`product_id = null`) can be attached to any product and linked to categories, so the right attributes surface for the right product types.
35
+
36
+ Since it is not a variant axis, Material describes the product. It never generates variants.
36
37
  </Step>
37
38
  <Step title="Create a variant axis">
38
- Create **Color** the same way, but enable `is_variant_axis`. This is only allowed for `multi_select` attributes the platform rejects the flag on any other type.
39
+ Create **Color** the same way, but enable `is_variant_axis`. This is only allowed for `multi_select` attributes. The platform rejects the flag on any other type.
39
40
 
40
- Because Color is a **global** axis, it's backed by one shared product option. Every product that uses it links to that option, restricted to the subset of values the product actually offers so "Color" means the same thing across the whole catalog, while one product can offer only Red and Blue.
41
+ Because Color is a **global** axis, it is backed by one shared product option. Every product that uses it links to that option, restricted to the subset of values the product actually offers. So "Color" means the same thing across the whole catalog, while one product can offer only Red and Blue.
41
42
  </Step>
42
43
  <Step title="Attach attributes to a product">
43
- Products manage attributes through one **batch** endpoint that adds, removes, and updates in a single request:
44
+ Products manage attributes through one **batch** endpoint that adds, removes, and updates in a single request.
44
45
 
45
- ```bash
46
+ ```bash Terminal
46
47
  curl -X POST "http://localhost:9000/vendor/products/prod_123/attributes/batch" \
47
48
  -H "Authorization: Bearer <token>" \
48
49
  -H "Content-Type: application/json" \
@@ -54,12 +55,12 @@ Attributes give the shared catalog structured, typed data — and for `multi_sel
54
55
  }'
55
56
  ```
56
57
 
57
- At product create time, the same entry shape is passed as a unified `attributes[]` array. Because Color is an axis, selecting Red and Blue makes them available as variant options variants are then defined with standard Medusa `variants[].options` mapping `"Color"` to `"Red"` or `"Blue"`.
58
+ At product create time, the same entry shape is passed as a unified `attributes[]` array. Because Color is an axis, selecting Red and Blue makes them available as variant options. Variants are then defined with standard Medusa `variants[].options` mapping `"Color"` to `"Red"` or `"Blue"`.
58
59
  </Step>
59
60
  <Step title="Add an inline product-scoped axis">
60
- Sometimes one product needs an axis that doesn't belong in the shared catalog. Define it **inline** by `title` instead of referencing an `id`:
61
+ Sometimes one product needs an axis that does not belong in the shared catalog. Define it **inline** by `title` instead of referencing an `id`.
61
62
 
62
- ```bash
63
+ ```bash Terminal
63
64
  curl -X POST "http://localhost:9000/vendor/products/prod_123/attributes/batch" \
64
65
  -H "Authorization: Bearer <token>" \
65
66
  -H "Content-Type: application/json" \
@@ -70,18 +71,18 @@ Attributes give the shared catalog structured, typed data — and for `multi_sel
70
71
  }'
71
72
  ```
72
73
 
73
- This creates a product-scoped attribute on the fly, backed by an **exclusive**, product-owned option. It won't appear in the global attribute list it belongs to this product alone.
74
+ This creates a product-scoped attribute on the fly, backed by an **exclusive**, product-owned option. It does not appear in the global attribute list. It belongs to this product alone.
74
75
  </Step>
75
76
  </Steps>
76
77
 
77
78
  <Note>
78
- Attribute changes on products submitted by vendors flow through the same [change-request pipeline](/rc/learn/product-requests) as other product edits `ATTRIBUTE_ADD` / `ATTRIBUTE_UPDATE` / `ATTRIBUTE_REMOVE` actions the operator reviews.
79
+ Attribute changes on products submitted by vendors flow through the same [change-request pipeline](/rc/learn/product-requests) as other product edits: `ATTRIBUTE_ADD`, `ATTRIBUTE_UPDATE`, or `ATTRIBUTE_REMOVE` actions the operator reviews.
79
80
  </Note>
80
81
 
81
82
  ## Verify
82
83
 
83
- 1. **Material** and **Color** appear in the Admin Panel's attribute catalog; **Fit** does not (it's product-scoped).
84
- 2. The product detail shows Material as descriptive data and Color/Fit as variant axes.
84
+ 1. **Material** and **Color** appear in the Admin Panel's attribute catalog. **Fit** does not, because it is product-scoped.
85
+ 2. The product detail shows Material as descriptive data and Color and Fit as variant axes.
85
86
  3. The product's variants combine the selected Color and Fit values, built from real product options.
86
87
  4. The Store API exposes Material for filtering on product listings (`is_filterable`).
87
88
 
@@ -89,13 +90,13 @@ Attributes give the shared catalog structured, typed data — and for `multi_sel
89
90
 
90
91
  <AccordionGroup>
91
92
  <Accordion title="Why can't a text or toggle attribute be a variant axis?">
92
- An axis needs an enumerable, finite value set to combine into variants Red/Blue × Slim/Regular. Free text has no enumerable values, and a toggle's two fixed values rarely describe purchasable variations. Only `multi_select` qualifies, and the platform enforces it.
93
+ An axis needs an enumerable, finite value set to combine into variants, for example Red/Blue times Slim/Regular. Free text has no enumerable values, and a toggle's two fixed values rarely describe purchasable variations. Only `multi_select` qualifies, and the platform enforces it.
93
94
  </Accordion>
94
95
  <Accordion title="What's the difference between a value link and an option?">
95
- Non-axis attributes attach to a product as plain **value links** descriptive data for display and filtering. Axis attributes are mirrored onto real Medusa **product options**, which participate in variant generation. Same authoring UI, structurally different underneath.
96
+ Non-axis attributes attach to a product as plain **value links**: descriptive data for display and filtering. Axis attributes are mirrored onto real Medusa **product options**, which participate in variant generation. Same authoring UI, structurally different underneath.
96
97
  </Accordion>
97
98
  <Accordion title="Can I promote a product-scoped attribute to a global one later?">
98
- Not automatically a product-scoped axis is backed by an exclusive option owned by that product. Create the global attribute in the catalog and re-attach products to it; treat inline attributes as intentionally local.
99
+ Not automatically. A product-scoped axis is backed by an exclusive option owned by that product. Create the global attribute in the catalog and re-attach products to it. Treat inline attributes as intentionally local.
99
100
  </Accordion>
100
101
  </AccordionGroup>
101
102
 
@@ -1,17 +1,20 @@
1
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."
2
+ title: "How to Build a Block"
3
+ sidebarTitle: "Build a Block"
4
+ description: "Author a reusable feature as a block covering backend, panel UI, and docs, then build it into a registry and install it into any Mercur project."
4
5
  ---
5
6
 
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
+ Blocks are how features travel between Mercur projects. You ship them as source code copied into the target project, not as npm packages you depend on.
8
+
9
+ Anything you build once, such as a module, workflows, routes, or 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
10
 
8
11
  <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.
12
+ **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 is the trade: you give up automatic upgrades, and users gain full ownership. Design blocks so they read cleanly after install.
10
13
  </Info>
11
14
 
12
15
  ## What you'll build
13
16
 
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.
17
+ You build an `announcements` block containing a module (data model and service), a vendor API route, and a vendor portal page. Then you build it into registry JSON and install it into a Mercur project.
15
18
 
16
19
  ## File types and where they land
17
20
 
@@ -44,9 +47,11 @@ Each file in a block carries a `type` that maps to an alias in the consumer's `b
44
47
  └── page.tsx # vendor portal page
45
48
  ```
46
49
 
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.
50
+ 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
51
  </Step>
49
52
  <Step title="Declare it in registry.json">
53
+ Add one entry per block to the `items` array.
54
+
50
55
  ```json registry.json
51
56
  {
52
57
  "$schema": "https://registry.mercurjs.com/registry.json",
@@ -70,20 +75,22 @@ Each file in a block carries a `type` that maps to an alias in the consumer's `b
70
75
  }
71
76
  ```
72
77
 
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.
78
+ Two fields do the heavy lifting. **`type`** on each file decides where the file lands, and **`docs`** is the markdown shown after install. Put every manual step in `docs`: module registration, migrations, middleware, codegen. It is the only instruction the installer sees.
74
79
  </Step>
75
80
  <Step title="Build the registry">
76
- ```bash
81
+ Run the CLI build from the registry root.
82
+
83
+ ```bash Terminal
77
84
  bunx @mercurjs/cli@latest build
78
85
  ```
79
86
 
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`.
87
+ 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
88
  </Step>
82
89
  <Step title="Host it">
83
- Serve the `r/` directory from any static host (GitHub Pages, Vercel, S3 anything that makes `{name}.json` publicly reachable).
90
+ Serve the `r/` directory from any static host (GitHub Pages, Vercel, S3, or anything that makes `{name}.json` publicly reachable).
84
91
  </Step>
85
92
  <Step title="Install it into a project">
86
- In a consumer project, register your registry in `blocks.json` and install:
93
+ In a consumer project, register your registry in `blocks.json`.
87
94
 
88
95
  ```json blocks.json
89
96
  {
@@ -93,7 +100,9 @@ Each file in a block carries a `type` that maps to an alias in the consumer's `b
93
100
  }
94
101
  ```
95
102
 
96
- ```bash
103
+ Then install the block.
104
+
105
+ ```bash Terminal
97
106
  bunx @mercurjs/cli@latest add @my-org/announcements
98
107
  ```
99
108
 
@@ -103,19 +112,21 @@ Each file in a block carries a `type` that maps to an alias in the consumer's `b
103
112
 
104
113
  ## Verify
105
114
 
115
+ Confirm the block built and installed correctly:
116
+
106
117
  1. `r/announcements.json` exists after the build and embeds every file's content.
107
118
  2. In the consumer project, the files landed under the alias-mapped paths and imports resolve.
108
119
  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@latest diff @my-org/announcements` reports no changes the installed copy matches the registry.
120
+ 4. `bunx @mercurjs/cli@latest diff @my-org/announcements` reports no changes. The installed copy matches the registry.
110
121
 
111
122
  ## FAQ
112
123
 
113
124
  <AccordionGroup>
114
125
  <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.
126
+ Other blocks go in `registryDependencies` (for example `@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
127
  </Accordion>
117
128
  <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).
129
+ 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
130
  </Accordion>
120
131
  <Accordion title="How do consumers get updates to my block?">
121
132
  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.
@@ -1,23 +1,26 @@
1
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."
2
+ title: "How to Add a Custom API Route"
3
+ sidebarTitle: "Add a Custom Route"
4
+ description: "Add a backend endpoint, regenerate the route map, and call it from a panel page with full type safety."
4
5
  ---
5
6
 
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
+ Mercur's typed API client is generated from your actual route files, not hand-maintained. A custom endpoint you add to the API package becomes a first-class, fully typed client call after one codegen run.
8
+
9
+ This tutorial walks the whole loop: route, then codegen, then a typed call from a custom panel page.
7
10
 
8
11
  <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).
12
+ 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
13
  </Info>
11
14
 
12
15
  ## What you'll build
13
16
 
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.
17
+ A `GET /vendor/sales-summary` endpoint returns the seller's order count. You call it from a custom vendor portal page via `client.vendor.salesSummary.query()` with inferred types.
15
18
 
16
19
  ## Build the loop
17
20
 
18
21
  <Steps>
19
22
  <Step title="Create the route">
20
- API routes follow Medusa's file conventions inside your API package. The URL path mirrors the directory path:
23
+ API routes follow Medusa's file conventions inside your API package. The URL path mirrors the directory path.
21
24
 
22
25
  ```typescript packages/api/src/api/vendor/sales-summary/route.ts
23
26
  import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http"
@@ -42,11 +45,13 @@ A `GET /vendor/sales-summary` endpoint returning the seller's order count, calle
42
45
  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
46
  </Step>
44
47
  <Step title="Regenerate the route map">
45
- ```bash
48
+ Run codegen to scan your route files.
49
+
50
+ ```bash Terminal
46
51
  bunx @mercurjs/cli@latest codegen
47
52
  ```
48
53
 
49
- Codegen scans your route files and rewrites the generated `Routes` type that your panel apps already import:
54
+ Codegen rewrites the generated `Routes` type that your panel apps already import:
50
55
 
51
56
  ```typescript apps/vendor/src/lib/client.ts
52
57
  import { createClient, type InferClient } from "@mercurjs/client"
@@ -60,14 +65,14 @@ A `GET /vendor/sales-summary` endpoint returning the seller's order count, calle
60
65
  })
61
66
  ```
62
67
 
63
- This file ships with the starter template you don't need to touch it. After codegen, `client.vendor.salesSummary` simply exists, typed.
68
+ This file ships with the starter template, so you don't need to touch it. After codegen, `client.vendor.salesSummary` simply exists, typed.
64
69
 
65
70
  <Tip>
66
71
  Run `bunx @mercurjs/cli@latest codegen --watch` during development so the route map regenerates as you edit route files.
67
72
  </Tip>
68
73
  </Step>
69
74
  <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).
75
+ Drop a page into the vendor app and call the endpoint through the client. Route segments map to camelCase properties, and the terminal call chooses the HTTP method: `query` (GET), `mutate` (POST), or `delete` (DELETE).
71
76
 
72
77
  ```tsx apps/vendor/src/routes/sales-summary/page.tsx
73
78
  import { useQuery } from "@tanstack/react-query"
@@ -97,7 +102,7 @@ A `GET /vendor/sales-summary` endpoint returning the seller's order count, calle
97
102
  </div>
98
103
  <div className="px-6 py-4">
99
104
  <Text size="small" className="text-ui-fg-subtle">
100
- Orders: {data?.order_count ?? ""}
105
+ Orders: {data?.order_count ?? "-"}
101
106
  </Text>
102
107
  </div>
103
108
  </Container>
@@ -105,25 +110,32 @@ A `GET /vendor/sales-summary` endpoint returning the seller's order count, calle
105
110
  }
106
111
  ```
107
112
 
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.
113
+ `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
114
  </Step>
110
115
  </Steps>
111
116
 
112
117
  ## Verify
113
118
 
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.
119
+ Start the project and log into the vendor portal.
120
+
121
+ ```bash Terminal
122
+ bun run dev
123
+ ```
124
+
125
+ Confirm each of the following:
126
+
127
+ 1. **Sidebar entry:** Sales summary appears in the sidebar (the `config` export registered it), and the page shows the order count.
128
+ 2. **Auth guard:** `curl http://localhost:9000/vendor/sales-summary` without a token returns an authentication error. The vendor middleware guards your route.
129
+ 3. **Generated contract:** change the route to return `{ count: ... }` instead of `{ order_count: ... }`, rerun codegen, and confirm the page fails to type-check. That is the generated contract doing its job. Revert after.
118
130
 
119
131
  ## FAQ
120
132
 
121
133
  <AccordionGroup>
122
134
  <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).
135
+ 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, and everything else in the object becomes query params (GET) or the JSON body (POST).
124
136
  </Accordion>
125
137
  <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).
138
+ 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.
127
139
  </Accordion>
128
140
  <Accordion title="Where do request validation schemas go?">
129
141
  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.
@@ -133,8 +145,8 @@ A `GET /vendor/sales-summary` endpoint returning the seller's order count, calle
133
145
  ## Next steps
134
146
 
135
147
  <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.
148
+ <Card title="API conventions" href="/references/api/conventions">
149
+ Authentication, pagination, field selection, and error shapes.
138
150
  </Card>
139
151
  <Card title="Extend a workflow" href="/rc/resources/customization/extend-a-workflow">
140
152
  Put multi-step business logic behind your endpoint with rollback support.
@@ -1,23 +1,26 @@
1
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."
2
+ title: "How to Create a Custom Page"
3
+ sidebarTitle: "Create a Page"
4
+ description: "Add a page to the vendor portal or admin panel with file-based routing and let the dashboard SDK wire it into the sidebar."
4
5
  ---
5
6
 
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
+ Add a page to the vendor portal or admin panel from a single file.
8
+
9
+ 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. There is no route table and no registration call.
7
10
 
8
11
  <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: add a [widget](/rc/resources/tutorials/add-a-widget) or a [custom field](/rc/resources/tutorials/extend-forms-and-tables) to inject into it. The [decision guide](/rc/resources/customization/extending-panels#choosing-your-extension-mechanism) compares every extension mechanism.
12
+ **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. Add a [widget](/rc/resources/tutorials/add-a-widget) or a [custom field](/rc/resources/tutorials/extend-forms-and-tables) to inject into it. The [decision guide](/rc/resources/customization/extending-panels#choosing-your-extension-mechanism) compares every extension mechanism.
10
13
  </Info>
11
14
 
12
15
  ## What you'll build
13
16
 
14
- A `/reviews` page in the vendor portal with a sidebar entry from a single file.
17
+ A `/reviews` page in the vendor portal with a sidebar entry, from a single file.
15
18
 
16
19
  ## Add the page
17
20
 
18
21
  <Steps>
19
22
  <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:
23
+ 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
24
 
22
25
  ```tsx apps/vendor/src/routes/reviews/page.tsx
23
26
  import { Container, Heading } from "@medusajs/ui"
@@ -42,17 +45,23 @@ A `/reviews` page in the vendor portal with a sidebar entry — from a single fi
42
45
  ```
43
46
  </Step>
44
47
  <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).
48
+ 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
49
  </Step>
47
50
  <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.
51
+ Start the project, then open the vendor portal.
52
+
53
+ ```bash Terminal
54
+ bun run dev
55
+ ```
56
+
57
+ **Reviews** appears in the sidebar at the position set by `rank`, and `/reviews` renders your component.
49
58
  </Step>
50
59
  </Steps>
51
60
 
52
61
  ## Verify
53
62
 
54
63
  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).
64
+ 2. Navigating to `/reviews` renders the page inside the standard panel layout, with the sidebar and topbar intact.
56
65
  3. Removing the `config` export keeps the route working but drops the sidebar item.
57
66
  4. Deleting the file removes the route entirely.
58
67
 
@@ -60,13 +69,13 @@ A `/reviews` page in the vendor portal with a sidebar entry — from a single fi
60
69
 
61
70
  <AccordionGroup>
62
71
  <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).
72
+ Use the typed 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
73
  </Accordion>
65
74
  <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.
75
+ Yes. Place the file under a `settings/` route segment and set `nested: "/settings"` in the config to group its sidebar item under Settings.
67
76
  </Accordion>
68
77
  <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.
78
+ Identically. Drop the file in the admin app's `src/routes/` instead. Both panels use the same SDK and conventions.
70
79
  </Accordion>
71
80
  </AccordionGroup>
72
81