Skip to content

feat(admin): rework product admin UI for marketplace module#887

Open
vholik wants to merge 17 commits into
feat/vendor-store-api-routesfrom
feat/admin-product-module-ui
Open

feat(admin): rework product admin UI for marketplace module#887
vholik wants to merge 17 commits into
feat/vendor-store-api-routesfrom
feat/admin-product-module-ui

Conversation

@vholik
Copy link
Copy Markdown
Collaborator

@vholik vholik commented Apr 20, 2026

Summary

  • Rewrite product hooks to match new admin API routes (accept, reject, request-changes, activate, deactivate) and add product-attributes/product-rejection-reasons hooks
  • Update product list table to match Figma: add Category column, remove Sales Channels/Seller, fix filters and status options to marketplace statuses (pending/accepted/changes_required/rejected)
  • Simplify product create form: remove seller, sales channels, shipping profile; make category required with single-select CategoryCombobox; remove variants from Details tab

Test plan

  • Verify product list table shows correct columns: Product, Category, Collection, Variants, Status
  • Verify product list filters work: Category, Collection, Type, Tag, Status, Created, Updated
  • Verify product status badges show correct marketplace statuses
  • Verify product create flow: Details → Organize → Variants tabs
  • Verify category field in Organize tab uses tree-based single-select combobox
  • Verify attribute pages still work with renamed hooks (product-attributes)

🤖 Generated with Claude Code

vholik and others added 17 commits April 20, 2026 14:57
- Rewrite product hooks to match new admin API routes (accept, reject,
  request-changes, activate, deactivate)
- Rename attributes hooks from attributes.ts to product-attributes.tsx
  with proper SDK route (sdk.admin.productAttributes)
- Add product-rejection-reasons hooks
- Update product list table columns to match Figma design (add Category,
  remove Sales Channels and Seller columns)
- Add CategoryCell table component with +N overflow badge
- Update product table filters: add Category/Collection, remove
  sales_channel_id/seller_id, fix status options to marketplace statuses
- Update ProductStatusCell to use marketplace ProductStatus enum
  (pending/accepted/changes_required/rejected)
- Convert CategoryCombobox from multi-select to single-select
- Simplify product create form: remove seller, sales channels, shipping
  profile; make category required; remove variants from Details tab
- Update product organization edit form for single category
- Update all consumer imports from old attribute hook names to new ones

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace individual create/update calls with a single upsert endpoint
at POST /admin/product-attributes/:id/values that accepts a values
array. Items with id are updated, items without are created. This
eliminates N+1 API calls from the ranking and possible values pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d system

Remove ProductOption/ProductOptionValue models and replace with direct
Product ↔ ProductAttribute/ProductAttributeValue M2M relationships.

- Add product_id FK on ProductAttribute for custom product-scoped attributes
- Add variant_attributes, custom_attributes, attribute_values M2M on Product
- Add attribute_values M2M on ProductVariant
- Rewrite service normalizeAttributes_ to handle global refs and inline custom attrs
- Update all API validators, query configs across admin/vendor/store
- Add delete validation for attributes and attribute values in use
- Rework admin product create UI: attributes tab with global/custom attrs,
  variant generation from attribute axes, Combobox multi-select for values
- Remove old options field from form schema, rename to attribute_values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…stom attribute linking

- Add ProductAttributeSection to product detail page with Variations and
  Product Information groups, dotted divider between groups, and bordered rows
- Fix custom attribute creation: defer product_id assignment until after
  product creation so hasMany FK is properly set
- Restrict variant axis to multi_select only (remove single_select)
- Derive informational attributes from both custom_attributes and
  attribute_values so existing global attributes appear in detail view
- Re-fetch created attributes with values relation to ensure attribute
  value IDs are correctly linked to products

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ns, and add-existing flow

- Add /admin/products/:id/attributes endpoints (GET, POST) and
  /admin/products/:id/attributes/:attribute_id (GET, POST, DELETE)
- Add removeAttributeFromProduct service method and workflow to properly
  unlink attributes from variant_attributes M2M, attribute_values M2M,
  and delete product-scoped attributes
- createProductAttributes now links variant_attributes M2M when
  is_variant_axis is true, and links attribute_values to the product
- Add product attribute sub-resource hooks (list, create, update, delete)
  using fetchQuery
- Add shared AttributeValueInput component for type-aware value inputs
- Add product-create-attribute route page (RouteDrawer with create form)
- Add product-add-existing-attributes route page (RouteFocusModal with
  two-step flow: DataTable selection → zod-validated value form)
- Add delete action per attribute row in detail section with confirmation
- Remove ProductOptionSection from product detail page
- Fix Switch component in create attributes form (shrink-0, ref-only spread)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… service

Add batch endpoint for product attributes (create/update/delete in one call),
refactor product service for improved attribute handling, and update integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… include product_attributes in update check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…and integration tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…apes

Update the composite unique index on product_attribute from (handle) to
(product_id, handle) so product-scoped attributes can reuse global handles.
Fix integration tests to use multi_select for variant axis attributes and
wrap value create payloads in { values: [...] } to match the upsert validator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…factor admin UI

Override listProductAttributes, listAndCountProductAttributes, and retrieveProductAttribute
to resolve values only for single_select/multi_select types (empty for others). Migrate
admin hooks to SDK route pattern, rename for clarity, add edit attribute route, and simplify
product attribute section to use unified attributes array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…te validation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t create/update

Resolve map-form `attribute_values` ({ Color: "Red" }) to value IDs in the
product service for both create and update paths, and accept either form in
the variant DTOs. Refactor admin variant create/edit forms to use the shared
attribute-value-input and drop the unused `prices` field from the variant
create validator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s enum

- Introduce product brands: admin pages, hooks, route, settings nav,
  organization form/section integration, and i18n.
- Rename product status enum to draft/proposed/published (default
  proposed), update workflow validators, status cell, store filter,
  i18n, and ship migrations.
- Drop product seller section from variant detail and seller_id link
  filter from /admin/products.
- Simplify variant detail to use default fields and render variant
  attribute_values grouped by attribute.
- Allow status on product create/update validators; show JSON on
  attribute detail; switch product media upload to new SDK; block
  product_id filter on /admin/product-attributes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ace store column

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dmin UI

Add POST /admin/products/batch with validator, workflow, and event names
(draft/published/proposed). Wire up an admin bulk-edit page reachable from
the product list table command, with row selection and a hook calling the
new SDK route. Drop `is_restricted` from product brand create/edit forms
and use the existing product status when emitting submit-seller status
changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the marketplace-specific is_active column on Product along with
its index, activate/deactivate workflows, admin routes, and admin
hooks. Product visibility is now driven entirely by status.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…equired status

Combine update/delete into a single batchProducts workflow and add a
bulk delete action to the product list. Add the changes_required option
to the edit form and color it blue on the detail page to match
ProductStatusCell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vholik vholik requested a review from Si3r4dz April 28, 2026 13:52
) => {
return useMutation({
mutationFn: (payload: HttpTypes.AdminUpdateProductVariant) =>
mutationFn: (payload: any) =>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why type was removed and changed to any?

Comment on lines +440 to +452
export const useBatchProductAttributes = (
productId: string,
options?: UseMutationOptions<
HttpTypes.AdminExportProductResponse,
any,
ClientError,
HttpTypes.AdminExportProductRequest
{
create?: {
attribute_id: string;
attribute_value_ids?: string[];
values?: string[];
}[];
delete?: string[];
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have type somewhere for this object passed as third param in options?


export const useUpdateProductVariantsBatch = (
productId: string,
options?: UseMutationOptions<any, ClientError, any>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any

Comment on lines +501 to +509
options?: UseMutationOptions<any, ClientError, any>,
) => {
return useMutation({
mutationFn: (transactionId) =>
sdk.admin.products.import.$transactionId.confirm.mutate({
$transactionId: transactionId,
mutationFn: (payload: any) =>
sdk.admin.products.$id.variants.inventoryItems.batch.mutate({
$id: productId,
...payload,
}),
onSuccess: (data, variables, context) => {
onSuccess: (data: any, variables: any, context: any) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Comment on lines +48 to +51
// const { variants = [], count } = useVariants({
// ...searchParams,
// fields: "*inventory_items.inventory.location_levels,+inventory_quantity",
// });
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to leave it here? if so please add TODO.

Comment on lines +139 to +140
// allow_backorder,
// manage_inventory,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to leave it?

Comment on lines +40 to +53
// mutateAsync(values, {
// onSuccess: () => {
// toast.success(
// t("products.options.create.successToast", {
// title: values.title,
// })
// )
// handleSuccess()
// },
// onError: async (err) => {
// toast.error(err.message)
// },
// })
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Comment on lines +114 to +116
{/* <Button type="submit" size="small" isLoading={isPending}>
{t("actions.save")}
</Button>
</Button> */}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that disabled?


await mutateAsync()
}
// await mutateAsync();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with comments in this file

Comment on lines +37 to +54
// const { mutateAsync, isPending } = useUpdateProductOption(
// option.product_id!,
// option.id
// )

const handleSubmit = form.handleSubmit(async (values) => {
mutateAsync(
{
id: option.id,
...values,
},
{
onSuccess: () => {
handleSuccess()
},
}
)
})
// mutateAsync(
// {
// id: option.id,
// ...values,
// },
// {
// onSuccess: () => {
// handleSuccess()
// },
// }
// )
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants