feat(ui): storefront filters and admin category/tag management (#23)
Storefront gains a Filters drawer holding the category tree, colour-coded tag pills, and a price range, with applied filters shown as removable chips. Filter state lives in the URL query string, so a filtered view is shareable and the back button works. Item cards now show their category and tags. Admin gains Categories and Tags tabs, and the item form gains a category TreeSelect plus a tags Select that creates new tags on the fly. The admin category tree tracks expansion in state rather than using defaultExpandAll: that prop is evaluated once at mount, so a branch added afterwards rendered collapsed and its children were unreachable. Creating or moving a node now expands its parent. Caught by the new admin e2e spec. The chip row is marked as a named group so its "Clear all" stays distinguishable from the drawer's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,9 +169,12 @@ described in `.claude/project-context.md` with no nginx change.
|
||||
- `GET|POST /api/admin/categories`, `PUT|DELETE /api/admin/categories/:id`
|
||||
- `GET|POST /api/admin/tags`, `PUT|DELETE /api/admin/tags/:id`
|
||||
|
||||
`PUT /api/admin/categories/:id` accepts `name`, `parent_id`, and `sort_order`. Reparenting is
|
||||
validated against cycles server-side — a node may not become its own descendant — returning `400`.
|
||||
`DELETE` responds with the affected counts so the UI can confirm before committing.
|
||||
`GET /api/admin/categories` returns each node with an `item_count`, which is what lets the admin UI
|
||||
state the blast radius of a delete before calling it — the tree and the counts are already on the
|
||||
client, so no extra preview endpoint is needed. `PUT /api/admin/categories/:id` accepts `name`,
|
||||
`parent_id`, and `sort_order`; reparenting is validated against cycles server-side — a node may not
|
||||
become its own descendant — returning `400`. `DELETE` reports what it actually did, as
|
||||
`{ deleted_categories, uncategorized_items }`.
|
||||
|
||||
`POST`/`PUT /api/admin/items` gain two multipart fields:
|
||||
|
||||
@@ -209,6 +212,9 @@ own line on mobile. The drawer enters from the right at every screen size (`plac
|
||||
near-full-width below the `md` breakpoint), with a sticky footer holding "Clear all" and
|
||||
"Show N items".
|
||||
|
||||
The chip row is marked `role="group" aria-label="Active filters"`, which keeps its "Clear all"
|
||||
distinguishable from the identically-labelled control in the drawer.
|
||||
|
||||
The tag section is labelled **"Tags — must have all"** so that selecting a second tag and watching
|
||||
the grid shrink reads as intentional rather than broken.
|
||||
|
||||
@@ -219,7 +225,9 @@ the grid shrink reads as intentional rather than broken.
|
||||
Two tabs added beside Inventory, Customers, and Settings:
|
||||
|
||||
- **Categories** — antd `Tree` with drag-to-reparent, inline add/rename/delete, delete confirm
|
||||
naming the affected subcategory and item counts
|
||||
naming the affected subcategory and item counts. Expansion is controlled state rather than
|
||||
`defaultExpandAll`: that prop is evaluated once at mount, so a branch created afterwards would
|
||||
render collapsed and its children be unreachable. Creating or moving a node expands its parent.
|
||||
- **Tags** — list with rename, colour override from a palette, and delete showing the item count
|
||||
|
||||
The item modal gains a category `TreeSelect` (with an explicit Uncategorized option) and a tags
|
||||
@@ -249,8 +257,19 @@ user rule. Existing files keep their barrel imports — churning them is out of
|
||||
|
||||
**E2E** (`frontend/tests/e2e/filters.spec.ts`)
|
||||
- Open the drawer, filter by category, tags, and price; assert the grid narrows
|
||||
- Remove a chip and assert the grid widens
|
||||
- A nested category is selectable, not just the roots
|
||||
- Remove a chip and assert the grid widens; "Clear all" resets everything
|
||||
- Reload a filtered URL and assert the filters are restored
|
||||
- Tags render on the item card
|
||||
|
||||
**E2E** (`frontend/tests/e2e/admin-taxonomy.spec.ts`)
|
||||
- Create a category and a nested child, asserting the child stays visible
|
||||
- Create a tag and confirm a colour was assigned
|
||||
- The item form exposes category and tag fields
|
||||
|
||||
Both e2e specs run against a database that is never reset between runs, so fixture names carry a
|
||||
per-run suffix, and `filters.spec.ts` treats re-entry into its `beforeAll` as a no-op — a worker can
|
||||
be handed the same spec file in more than one batch, and seeding twice would duplicate every item.
|
||||
|
||||
## Out of scope
|
||||
|
||||
|
||||
Reference in New Issue
Block a user