feat(frontend): group products by category with ownership filter

Replace category filter dropdown with client-side grouping and a
3-way ownership toggle (All / Owned / Not owned). Products are grouped
by category with header rows as visual dividers, sorted brand → name
within each group. Category column removed (redundant with headings).

Backend: GET /products now returns ProductWithInventory so inventory
data is available for ownership filtering (bulk-loaded in one query).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Piotr Oleszczyk 2026-02-28 23:07:37 +01:00
parent 2691708304
commit 1b1566e6d7
3 changed files with 100 additions and 58 deletions

View file

@ -172,7 +172,7 @@ class InventoryUpdate(SQLModel):
# ---------------------------------------------------------------------------
@router.get("", response_model=list[ProductPublic])
@router.get("", response_model=list[ProductWithInventory])
def list_products(
category: Optional[ProductCategory] = None,
brand: Optional[str] = None,
@ -205,7 +205,25 @@ def list_products(
)
]
return products
# Bulk-load inventory for all products in one query
product_ids = [p.id for p in products]
inventory_rows = (
session.exec(
select(ProductInventory).where(ProductInventory.product_id.in_(product_ids))
).all()
if product_ids
else []
)
inv_by_product: dict = {}
for inv in inventory_rows:
inv_by_product.setdefault(inv.product_id, []).append(inv)
results = []
for p in products:
r = ProductWithInventory.model_validate(p, from_attributes=True)
r.inventory = inv_by_product.get(p.id, [])
results.append(r)
return results
@router.post("", response_model=ProductPublic, status_code=201)