@mercurjs/docs 2.3.8-canary.2 → 2.3.8-canary.4

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 (114) hide show
  1. package/content/learn/architecture.mdx +166 -263
  2. package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
  3. package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
  4. package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
  5. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
  6. package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
  7. package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
  8. package/content/platform/attribute/overview.mdx +37 -33
  9. package/content/platform/attribute/reference/data-models.mdx +53 -38
  10. package/content/platform/attribute/reference/events.mdx +47 -24
  11. package/content/platform/attribute/reference/links.mdx +98 -27
  12. package/content/platform/attribute/reference/service.mdx +71 -32
  13. package/content/platform/attribute/reference/workflows.mdx +158 -28
  14. package/content/platform/catalog/concepts/master-products.mdx +39 -37
  15. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
  16. package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
  17. package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
  18. package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
  19. package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
  20. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
  21. package/content/platform/catalog/overview.mdx +39 -35
  22. package/content/platform/catalog/reference/data-models.mdx +154 -43
  23. package/content/platform/catalog/reference/events.mdx +41 -25
  24. package/content/platform/catalog/reference/links.mdx +196 -28
  25. package/content/platform/catalog/reference/service.mdx +57 -35
  26. package/content/platform/catalog/reference/workflows.mdx +124 -30
  27. package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
  28. package/content/platform/commission/concepts/rule-matching.mdx +53 -63
  29. package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
  30. package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
  31. package/content/platform/commission/guides/create-a-rate.mdx +101 -53
  32. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
  33. package/content/platform/commission/overview.mdx +43 -35
  34. package/content/platform/commission/reference/data-models.mdx +59 -45
  35. package/content/platform/commission/reference/events.mdx +28 -34
  36. package/content/platform/commission/reference/links.mdx +42 -33
  37. package/content/platform/commission/reference/service.mdx +103 -28
  38. package/content/platform/commission/reference/workflows.mdx +101 -27
  39. package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
  40. package/content/platform/offer/concepts/shipping.mdx +28 -29
  41. package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
  42. package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
  43. package/content/platform/offer/guides/create-an-offer.mdx +112 -60
  44. package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
  45. package/content/platform/offer/overview.mdx +39 -32
  46. package/content/platform/offer/reference/data-models.mdx +77 -47
  47. package/content/platform/offer/reference/events.mdx +44 -18
  48. package/content/platform/offer/reference/links.mdx +92 -40
  49. package/content/platform/offer/reference/service.mdx +66 -31
  50. package/content/platform/offer/reference/workflows.mdx +146 -37
  51. package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
  52. package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
  53. package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
  54. package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
  55. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
  56. package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
  57. package/content/platform/order-group/overview.mdx +42 -32
  58. package/content/platform/order-group/reference/data-models.mdx +48 -31
  59. package/content/platform/order-group/reference/events.mdx +29 -17
  60. package/content/platform/order-group/reference/links.mdx +90 -29
  61. package/content/platform/order-group/reference/service.mdx +55 -31
  62. package/content/platform/order-group/reference/workflows.mdx +83 -24
  63. package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
  64. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
  65. package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
  66. package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
  67. package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
  68. package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
  69. package/content/platform/payout/overview.mdx +43 -34
  70. package/content/platform/payout/reference/data-models.mdx +63 -36
  71. package/content/platform/payout/reference/events.mdx +56 -30
  72. package/content/platform/payout/reference/links.mdx +52 -22
  73. package/content/platform/payout/reference/service.mdx +72 -33
  74. package/content/platform/payout/reference/workflows.mdx +80 -19
  75. package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
  76. package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
  77. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
  78. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
  79. package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
  80. package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
  81. package/content/platform/product-edit/overview.mdx +41 -34
  82. package/content/platform/product-edit/reference/data-models.mdx +73 -47
  83. package/content/platform/product-edit/reference/events.mdx +39 -29
  84. package/content/platform/product-edit/reference/links.mdx +32 -24
  85. package/content/platform/product-edit/reference/service.mdx +45 -26
  86. package/content/platform/product-edit/reference/workflows.mdx +173 -31
  87. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
  88. package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
  89. package/content/platform/review/concepts/the-review-model.mdx +52 -45
  90. package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
  91. package/content/platform/review/guides/create-a-review.mdx +109 -39
  92. package/content/platform/review/guides/moderate-a-review.mdx +83 -37
  93. package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
  94. package/content/platform/review/overview.mdx +43 -33
  95. package/content/platform/review/reference/data-models.mdx +41 -27
  96. package/content/platform/review/reference/events.mdx +55 -37
  97. package/content/platform/review/reference/links.mdx +80 -22
  98. package/content/platform/review/reference/service.mdx +61 -33
  99. package/content/platform/review/reference/workflows.mdx +80 -24
  100. package/content/platform/store/concepts/lifecycle.mdx +68 -44
  101. package/content/platform/store/concepts/store-entity.mdx +45 -27
  102. package/content/platform/store/concepts/team.mdx +56 -35
  103. package/content/platform/store/guides/create-a-store.mdx +102 -35
  104. package/content/platform/store/guides/manage-the-team.mdx +122 -32
  105. package/content/platform/store/guides/moderate-a-store.mdx +107 -38
  106. package/content/platform/store/overview.mdx +41 -34
  107. package/content/platform/store/reference/data-models.mdx +132 -57
  108. package/content/platform/store/reference/events.mdx +42 -17
  109. package/content/platform/store/reference/links.mdx +67 -49
  110. package/content/platform/store/reference/service.mdx +105 -33
  111. package/content/platform/store/reference/workflows.mdx +304 -24
  112. package/content/references/panel-extensions/layouts.mdx +169 -0
  113. package/llms.txt +112 -111
  114. package/package.json +1 -1
@@ -1,35 +1,315 @@
1
1
  ---
2
- title: "Workflows"
2
+ title: "Seller module workflows"
3
3
  sidebarTitle: "Workflows"
4
- description: "Seller and member workflows, service methods, and events."
4
+ description: "Every store, lifecycle, business-detail, and team workflow in the Seller module, with inputs, hooks, and events."
5
5
  ---
6
6
 
7
- This reference lists the workflows, service methods, and events for the Store
8
- domain. Import workflows from `@mercurjs/core/workflows` and run them against the
9
- Medusa container.
7
+ This reference lists every workflow that writes Seller module data. Import them from `@mercurjs/core/workflows` and run them against the Medusa container: `await workflow(container).run({ input })`. Workflows roll back their completed steps when a later step fails.
10
8
 
11
- ## Seller workflows
9
+ ```ts
10
+ import { approveSellerWorkflow } from "@mercurjs/core/workflows"
12
11
 
13
- | Workflow | Input | Purpose |
12
+ await approveSellerWorkflow(container).run({
13
+ input: { seller_id: "sel_01JD8Q1234567" },
14
+ })
15
+ ```
16
+
17
+ Workflows that take `additional_data` pass it to their hooks. Register a hook handler with `workflow.hooks.<hookName>(handler)`.
18
+
19
+ ## createSellersWorkflow
20
+
21
+ Creates one or more stores, links each to the default region plus `region_ids`, and invites one member per store with `role_seller_administration`. Returns `SellerDTO[]`.
22
+
23
+ | Input | Type | Description |
24
+ | --- | --- | --- |
25
+ | `sellers` | `CreateSellerDTO & { member, region_ids? }[]` | Store fields; `status` defaults to `pending_approval` |
26
+ | `sellers[].member` | `{ email: string }` | Required, the address that gets the invite |
27
+ | `sellers[].region_ids` | `string[]` | Optional, regions besides the default one |
28
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
29
+
30
+ - **Hooks**: `validate` (`{ input }`), `sellersCreated` (`{ sellers, additional_data }`)
31
+ - **Events**: `seller.created` per store, `member_invite.created` per invite
32
+
33
+ ## createSellerAccountWorkflow
34
+
35
+ Creates one store in `pending_approval` and adds a member as its owner with `role_seller_administration`. Returns `SellerDTO`.
36
+
37
+ | Input | Type | Description |
38
+ | --- | --- | --- |
39
+ | `auth_identity_id` | `string` | The auth identity to attach the member to |
40
+ | `seller` | `CreateSellerDTO` | Store fields; `status` is forced to `pending_approval` |
41
+ | `member_id` | `string` | Optional, an existing member to make owner |
42
+ | `member_email` | `string` | Used when `member_id` is absent to upsert a member |
43
+ | `first_name`, `last_name` | `string` | Optional, set on a new member |
44
+ | `address` | `UpdateSellerAddressDTO` | Optional, written through `updateSellerAddressWorkflow` |
45
+ | `professional_details` | `UpdateProfessionalDetailsDTO` | Optional |
46
+ | `payment_details` | `UpdatePaymentDetailsDTO` | Optional |
47
+ | `region_ids` | `string[]` | Optional, regions besides the default one |
48
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
49
+
50
+ - **Hooks**: `validate` (`{ input }`), `sellerAccountCreated` (`{ seller, member_id, additional_data }`)
51
+ - **Events**: `seller.created`
52
+
53
+ ## updateSellersWorkflow
54
+
55
+ Updates every store that matches `selector` with the same `update`. Returns `SellerDTO[]`.
56
+
57
+ | Input | Type | Description |
58
+ | --- | --- | --- |
59
+ | `selector` | `Record<string, unknown>` | Filters, such as `{ id: "sel_01JD8Q1234567" }` |
60
+ | `update` | `UpdateSellerDTO` | Fields to set |
61
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
62
+
63
+ - **Hooks**: `sellersUpdated` (`{ sellers, additional_data }`)
64
+ - **Events**: `seller.updated` per store
65
+
66
+ ## deleteSellersWorkflow
67
+
68
+ Soft deletes stores by id. Compensation restores them.
69
+
70
+ | Input | Type | Description |
71
+ | --- | --- | --- |
72
+ | `ids` | `string[]` | Seller ids |
73
+
74
+ - **Hooks**: none
75
+ - **Events**: `seller.deleted` per store
76
+
77
+ ## approveSellerWorkflow
78
+
79
+ Moves a `pending_approval` store to `open`, clears `status_reason`, and sets `approved_at` if it’s empty.
80
+
81
+ | Input | Type | Description |
82
+ | --- | --- | --- |
83
+ | `seller_id` | `string` | The store to approve |
84
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
85
+
86
+ - **Hooks**: `sellerApproved` (`{ seller_id, additional_data }`)
87
+ - **Events**: `seller.approved`
88
+
89
+ ## suspendSellerWorkflow
90
+
91
+ Moves a `pending_approval` or `open` store to `suspended`, saves `reason` in `status_reason`, and sets `rejected_at` if it’s empty.
92
+
93
+ | Input | Type | Description |
94
+ | --- | --- | --- |
95
+ | `seller_id` | `string` | The store to suspend |
96
+ | `reason` | `string` | Optional |
97
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
98
+
99
+ - **Hooks**: `sellerSuspended` (`{ seller_id, additional_data }`)
100
+ - **Events**: `seller.suspended`
101
+
102
+ ## unsuspendSellerWorkflow
103
+
104
+ Moves a `suspended` store to `open` and clears `status_reason`.
105
+
106
+ | Input | Type | Description |
107
+ | --- | --- | --- |
108
+ | `seller_id` | `string` | The store to reopen |
109
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
110
+
111
+ - **Hooks**: `sellerUnsuspended` (`{ seller_id, additional_data }`)
112
+ - **Events**: `seller.unsuspended`
113
+
114
+ ## terminateSellerWorkflow
115
+
116
+ Moves a `pending_approval` or `suspended` store to `terminated` and saves `reason` in `status_reason`. Fails for an `open` store.
117
+
118
+ | Input | Type | Description |
119
+ | --- | --- | --- |
120
+ | `seller_id` | `string` | The store to terminate |
121
+ | `reason` | `string` | Optional |
122
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
123
+
124
+ - **Hooks**: `sellerTerminated` (`{ seller_id, additional_data }`)
125
+ - **Events**: `seller.terminated`
126
+
127
+ ## unterminateSellerWorkflow
128
+
129
+ Moves a `terminated` store to `suspended` and clears `status_reason`.
130
+
131
+ | Input | Type | Description |
132
+ | --- | --- | --- |
133
+ | `seller_id` | `string` | The store to restore |
134
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
135
+
136
+ - **Hooks**: `sellerUnterminated` (`{ seller_id, additional_data }`)
137
+ - **Events**: `seller.unterminated`
138
+
139
+ ## linkSellerRegionsWorkflow
140
+
141
+ Adds and removes store region links in one run. Fails if a region or seller doesn’t exist, or if a store would be left with no region.
142
+
143
+ | Input | Type | Description |
144
+ | --- | --- | --- |
145
+ | `add` | `{ seller_id, region_id }[]` | Optional, links to create |
146
+ | `remove` | `{ seller_id, region_id }[]` | Optional, links to dismiss |
147
+
148
+ - **Hooks**: none
149
+ - **Events**: none
150
+
151
+ ## updateSellerAddressWorkflow
152
+
153
+ Creates or updates a store’s `SellerAddress`. Returns `SellerAddressDTO`.
154
+
155
+ | Input | Type | Description |
156
+ | --- | --- | --- |
157
+ | `seller_id` | `string` | The store |
158
+ | `data` | `UpdateSellerAddressDTO` | Address fields |
159
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
160
+
161
+ - **Hooks**: `addressUpdated` (`{ address, additional_data }`)
162
+ - **Events**: none
163
+
164
+ ## updateSellerProfessionalDetailsWorkflow
165
+
166
+ Creates or updates a store’s `ProfessionalDetails`. Returns `ProfessionalDetailsDTO`.
167
+
168
+ | Input | Type | Description |
169
+ | --- | --- | --- |
170
+ | `seller_id` | `string` | The store |
171
+ | `data` | `UpdateProfessionalDetailsDTO` | `corporate_name`, `registration_number`, `tax_id` |
172
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
173
+
174
+ - **Hooks**: `professionalDetailsUpdated` (`{ professional_details, additional_data }`)
175
+ - **Events**: none
176
+
177
+ ## deleteSellerProfessionalDetailsWorkflow
178
+
179
+ Deletes a store’s `ProfessionalDetails`, if it has any.
180
+
181
+ | Input | Type | Description |
182
+ | --- | --- | --- |
183
+ | `seller_id` | `string` | The store |
184
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
185
+
186
+ - **Hooks**: `professionalDetailsDeleted` (`{ seller_id, additional_data }`)
187
+ - **Events**: none
188
+
189
+ ## updateSellerPaymentDetailsWorkflow
190
+
191
+ Creates or updates a store’s `PaymentDetails`. Returns `PaymentDetailsDTO`.
192
+
193
+ | Input | Type | Description |
194
+ | --- | --- | --- |
195
+ | `seller_id` | `string` | The store |
196
+ | `data` | `UpdatePaymentDetailsDTO` | Bank account fields |
197
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
198
+
199
+ - **Hooks**: `paymentDetailsUpdated` (`{ payment_details, additional_data }`)
200
+ - **Events**: none
201
+
202
+ ## inviteSellerWorkflow
203
+
204
+ Sends an email that invites someone to register a new store. It creates a notification with the template `newSellerInvitation` on the `email` channel, and writes no Seller module records.
205
+
206
+ | Input | Type | Description |
207
+ | --- | --- | --- |
208
+ | `email` | `string` | The prospective seller |
209
+ | `registration_url` | `string` | Optional, the link in the email’s button |
210
+
211
+ - **Hooks**: none
212
+ - **Events**: none
213
+
214
+ ## createMemberInvitesWorkflow
215
+
216
+ Creates team invites with signed tokens. Fails if an email already belongs to the store. Returns `MemberInviteDTO[]`.
217
+
218
+ | Input | Type | Description |
219
+ | --- | --- | --- |
220
+ | `[].seller_id` | `string` | The store |
221
+ | `[].email` | `string` | The invitee |
222
+ | `[].role_id` | `string` | A `SellerRole` value |
223
+
224
+ The input is an array of invites, not an object.
225
+
226
+ - **Hooks**: none
227
+ - **Events**: `member_invite.created` per invite
228
+
229
+ ## resendMemberInviteWorkflow
230
+
231
+ Deletes an invite and creates a replacement with the same email and role, a new token, and a new expiry. Returns `MemberInviteDTO[]`.
232
+
233
+ | Input | Type | Description |
234
+ | --- | --- | --- |
235
+ | `invite_id` | `string` | The invite to replace |
236
+ | `seller_id` | `string` | The store that owns it |
237
+
238
+ - **Hooks**: none
239
+ - **Events**: `member_invite.created`
240
+
241
+ ## deleteMemberInviteWorkflow
242
+
243
+ Deletes an invite.
244
+
245
+ | Input | Type | Description |
246
+ | --- | --- | --- |
247
+ | `invite_id` | `string` | The invite to delete |
248
+
249
+ - **Hooks**: none
250
+ - **Events**: none
251
+
252
+ ## acceptMemberInviteWorkflow
253
+
254
+ Validates an invite token, upserts the member by email, creates the membership with the invite’s role, and deletes the invite. The member becomes owner when the store has no owner yet. Returns `MemberDTO`.
255
+
256
+ | Input | Type | Description |
257
+ | --- | --- | --- |
258
+ | `invite_token` | `string` | The token from the invite |
259
+ | `auth_identity_id` | `string` | The invitee’s auth identity |
260
+ | `member_id` | `string` | Optional; when set, the auth identity isn’t updated |
261
+ | `first_name`, `last_name` | `string` | Optional, set on a new member |
262
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
263
+
264
+ - **Hooks**: `validate` (`{ input, invite }`), `memberInviteAccepted` (`{ member, invite, additional_data }`)
265
+ - **Events**: `member_invite.accepted`
266
+
267
+ ## addSellerMemberWorkflow
268
+
269
+ Adds an existing member to a store. Returns the `SellerMember`.
270
+
271
+ | Input | Type | Description |
272
+ | --- | --- | --- |
273
+ | `seller_id` | `string` | The store |
274
+ | `member_id` | `string` | The existing member |
275
+ | `role_id` | `string` | A `SellerRole` value |
276
+
277
+ - **Hooks**: none
278
+ - **Events**: `seller_member.created`
279
+
280
+ ## updateMemberRoleWorkflow
281
+
282
+ Changes the role on a membership. Fails for the owner membership.
283
+
284
+ | Input | Type | Description |
14
285
  | --- | --- | --- |
15
- | `createSellersWorkflow` | `{ sellers[], additional_data? }` | Create stores with defaults |
16
- | `updateSellersWorkflow` | `{ selector, update }` | Update store fields |
17
- | `deleteSellersWorkflow` | `{ ids[] }` | Delete stores |
18
- | `approveSellerWorkflow` | `{ seller_id, additional_data? }` | `pending_approval` → `open` |
19
- | `suspendSellerWorkflow` | `{ seller_id }` | `open` → `suspended` |
20
- | `unsuspendSellerWorkflow` | `{ seller_id }` | `suspended` → `open` |
21
- | `terminateSellerWorkflow` | `{ seller_id }` | → `terminated` (irreversible) |
286
+ | `seller_member_id` | `string` | The membership id (`selmem_…`) |
287
+ | `role_id` | `string` | A `SellerRole` value |
288
+
289
+ - **Hooks**: none
290
+ - **Events**: `seller_member.updated`
291
+
292
+ ## removeSellerMemberWorkflow
293
+
294
+ Deletes a membership and keeps the `Member` record. Fails for the owner membership.
295
+
296
+ | Input | Type | Description |
297
+ | --- | --- | --- |
298
+ | `seller_member_id` | `string` | The membership id |
299
+ | `seller_id` | `string` | The store, included in the event payload |
300
+
301
+ - **Hooks**: none
302
+ - **Events**: `seller_member.deleted`
303
+
304
+ ## updateMemberWorkflow
22
305
 
23
- ## Member workflows
306
+ Updates the profile of every member that matches `selector`. Returns `MemberDTO[]`.
24
307
 
25
- | Workflow | Input | Purpose |
308
+ | Input | Type | Description |
26
309
  | --- | --- | --- |
27
- | `inviteSellerWorkflow` | `{ email, seller_id }` | Create + send a member invite |
28
- | `acceptMemberInviteWorkflow` | `{ token }` | Accept an invite, link the member |
29
- | `addSellerMemberWorkflow` | `{ seller_id, member }` | Add a member directly |
30
- | `removeSellerMemberWorkflow` | `{ seller_id, member_id }` | Revoke access |
31
- | `updateMemberRoleWorkflow` | `{ id, role_id }` | Change a member's role |
310
+ | `selector` | `Record<string, unknown>` | Filters, such as `{ id: "mem_01JD8Q1234567" }` |
311
+ | `update` | `{ first_name?, last_name?, locale? }` | Profile fields |
312
+ | `additional_data` | `Record<string, unknown>` | Optional, passed to hooks |
32
313
 
33
- To work with records directly instead of through a workflow, see the
34
- [Service reference](/platform/store/reference/service). To run side effects when
35
- a store changes, see the [Event reference](/platform/store/reference/events).
314
+ - **Hooks**: `membersUpdated` (`{ members, additional_data }`)
315
+ - **Events**: none
@@ -0,0 +1,169 @@
1
+ ---
2
+ title: "Layouts and layers"
3
+ sidebarTitle: "Layouts and layers"
4
+ description: "How extensions from plugins and your project stack, how a page exposes its sections, and how to hide, reorder, replace or wrap what a page renders."
5
+ ---
6
+
7
+ Every panel extension comes from an ordered list of **layers**:
8
+
9
+ 1. `@mercurjs/admin` or `@mercurjs/vendor` itself,
10
+ 2. each plugin in `medusa-config` `plugins` order, from its built `.medusa/server/src/<admin|vendor>/index.mjs`,
11
+ 3. your Medusa project's `src/admin` or `src/vendor` folder (next to `medusa-config.ts`),
12
+ 4. the panel app's own `src` folder.
13
+
14
+ A later layer wins. A route at a path an earlier layer declared replaces it, a widget with the same `id`
15
+ replaces it, a translation key overrides it, and a layout preference overrides it field by field. Every
16
+ layer uses the same folders: `routes/`, `widgets/`, `custom-fields/`, `layouts/`, `i18n/` and a
17
+ `_layouts.ts` file.
18
+
19
+ ## Pages expose named entries
20
+
21
+ A detail or list page renders a `LayoutComposer`. It names the zone widgets target, the layout it uses,
22
+ and gives each section a stable entry id:
23
+
24
+ ```tsx
25
+ import { CORE_LAYOUT_IDS } from "@mercurjs/dashboard-sdk"
26
+ import { LayoutComposer, detailPageDefaultEntries } from "@mercurjs/dashboard-shared"
27
+
28
+ <LayoutComposer
29
+ widgetsZonePrefix="product.detail"
30
+ preferredLayoutId={CORE_LAYOUT_IDS.TWO_COLUMN}
31
+ data={product}
32
+ sections={{
33
+ main: (
34
+ <>
35
+ <LayoutComposer.Entry id="ProductGeneralSection">
36
+ <ProductGeneralSection product={product} />
37
+ </LayoutComposer.Entry>
38
+ {detailPageDefaultEntries(product)}
39
+ </>
40
+ ),
41
+ side: (
42
+ <LayoutComposer.Entry id="ProductOrganizationSection">
43
+ <ProductOrganizationSection product={product} />
44
+ </LayoutComposer.Entry>
45
+ ),
46
+ }}
47
+ />
48
+ ```
49
+
50
+ The page's own sections become entries `core:ProductGeneralSection`, `core:MetadataSection` and so on;
51
+ widgets become `widget:<id>`. Use the same component on your own pages and the build emits their zones and
52
+ entry ids into `extension-targets.d.ts`, so the next layer gets autocompletion for them.
53
+
54
+ ## Place a widget
55
+
56
+ `zone` is the page prefix or one of its sections. `section` and `order` place the widget among the page's
57
+ entries; core entries sit at order `0`, so a negative order renders before them and no order renders after.
58
+
59
+ ```tsx
60
+ export const config = defineWidgetConfig({
61
+ zone: "product.detail",
62
+ id: "seller-offers",
63
+ section: "side",
64
+ order: -1,
65
+ })
66
+ ```
67
+
68
+ `replace` takes the slot of a page entry. The widget receives `renderDefault` to render what it replaced:
69
+
70
+ ```tsx
71
+ export const config = defineWidgetConfig({
72
+ zone: "product.detail",
73
+ id: "general-with-seller",
74
+ replace: "core:ProductGeneralSection",
75
+ })
76
+
77
+ export default function GeneralWithSeller({ data, renderDefault }) {
78
+ return (
79
+ <>
80
+ {renderDefault?.()}
81
+ <SellerBadge product={data} />
82
+ </>
83
+ )
84
+ }
85
+ ```
86
+
87
+ The legacy `product.detail.side.before` and `.after` zones still work and map to the first and last
88
+ position of that section.
89
+
90
+ ## Hide, reorder and move entries: `_layouts.ts`
91
+
92
+ A layer's `_layouts.ts` holds per-zone preferences that apply to everyone. The sidebars are zones too
93
+ (`nav.main`, `nav.settings`) with `nav:<path>` entries, which replaces `_navigation.ts`.
94
+
95
+ ```ts
96
+ import { defineLayoutPreferences } from "@mercurjs/dashboard-sdk"
97
+ import { Tag } from "@medusajs/icons"
98
+
99
+ export default defineLayoutPreferences({
100
+ "product.detail": {
101
+ entries: {
102
+ "core:ProductMediaSection": { hidden: true },
103
+ "widget:seller-offers": { section: "main", order: 1 },
104
+ },
105
+ },
106
+ "nav.main": {
107
+ groups: [{ id: "finance", label: "Finance", rank: 5 }],
108
+ entries: {
109
+ "nav:/products": { label: "Catalog", icon: Tag, order: -1 },
110
+ "nav:/payouts": { section: "finance" },
111
+ "nav:/price-lists": { parent: null },
112
+ "nav:/settings/commissions": { hidden: true },
113
+ },
114
+ },
115
+ })
116
+ ```
117
+
118
+ ## Override a page
119
+
120
+ A `page.tsx` at a path an earlier layer already serves replaces it. The component receives `renderDefault`,
121
+ which renders the page it replaced:
122
+
123
+ ```tsx
124
+ export default function ProductDetail({ renderDefault }) {
125
+ return (
126
+ <>
127
+ <ApprovalBanner />
128
+ {renderDefault?.()}
129
+ </>
130
+ )
131
+ }
132
+ ```
133
+
134
+ ## Block the panel behind a flow
135
+
136
+ There is no app-wrapper seam, as in Medusa. A flow every member must complete before using the panel
137
+ (terms acceptance, a verification step) is a page plus a redirect: a `routes/<flow>/page.tsx` with
138
+ `config.public = true` so it renders in the bare public layout, and a widget on the `topbar` zone that
139
+ checks the status with `useQuery` and `navigate`s to the page while the flow is outstanding. The API keeps
140
+ enforcing the rule with a 403 for everything else.
141
+
142
+ ## Register a layout
143
+
144
+ A file under `layouts/` registers a layout other pages can pick. Augment `LayoutSectionRegistry` so the
145
+ `sections` prop is typed.
146
+
147
+ ```tsx
148
+ import { defineLayoutConfig } from "@mercurjs/dashboard-sdk"
149
+
150
+ export default defineLayoutConfig({
151
+ id: "erp:three-column",
152
+ sections: [{ id: "main" }, { id: "left" }, { id: "right" }],
153
+ Component: ({ sections }) => (
154
+ <div className="grid grid-cols-3 gap-3">
155
+ {sections.left}
156
+ {sections.main}
157
+ {sections.right}
158
+ </div>
159
+ ),
160
+ })
161
+ ```
162
+
163
+ ```ts
164
+ declare module "@mercurjs/dashboard-sdk" {
165
+ interface LayoutSectionRegistry {
166
+ "erp:three-column": "main" | "left" | "right"
167
+ }
168
+ }
169
+ ```