From 026637990eaf621850ff6e4fd1ad0f97db9e4a91 Mon Sep 17 00:00:00 2001 From: Piotr Oleszczyk Date: Sat, 28 Feb 2026 23:02:35 +0100 Subject: [PATCH] feat(frontend): group products by category with ownership filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace category filter dropdown with client-side grouping and a 3-way ownership toggle (All / Owned / Not owned). Products are now sorted brand → name within each category group, with category header rows serving as visual dividers inside a single table. The Category column is removed since it's redundant with the headings. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/routes/products/+page.server.ts | 7 +- frontend/src/routes/products/+page.svelte | 129 +++++++++++-------- 2 files changed, 80 insertions(+), 56 deletions(-) diff --git a/frontend/src/routes/products/+page.server.ts b/frontend/src/routes/products/+page.server.ts index 6bb8800..5248dcf 100644 --- a/frontend/src/routes/products/+page.server.ts +++ b/frontend/src/routes/products/+page.server.ts @@ -1,8 +1,7 @@ import { getProducts } from '$lib/api'; import type { PageServerLoad } from './$types'; -export const load: PageServerLoad = async ({ url }) => { - const category = url.searchParams.get('category') ?? undefined; - const products = await getProducts({ category }); - return { products, category }; +export const load: PageServerLoad = async () => { + const products = await getProducts(); + return { products }; }; diff --git a/frontend/src/routes/products/+page.svelte b/frontend/src/routes/products/+page.svelte index 6e1bd37..796f576 100644 --- a/frontend/src/routes/products/+page.svelte +++ b/frontend/src/routes/products/+page.svelte @@ -1,5 +1,6 @@ Products — innercontext @@ -33,28 +60,21 @@

Products

-

{data.products.length} products

+

{totalCount} products

-
- Filter by category: - +
+ {#each (['all', 'owned', 'unowned'] as OwnershipFilter[]) as f (f)} + + {/each}
@@ -63,42 +83,47 @@ Name Brand - Category Targets Time - {#each data.products as product (product.id)} - - - - {product.name} - - - {product.brand} - - {product.category.replace(/_/g, ' ')} - - -
- {#each product.targets.slice(0, 3) as t (t)} - {t.replace(/_/g, ' ')} - {/each} - {#if product.targets.length > 3} - +{product.targets.length - 3} - {/if} -
-
- {product.recommended_time} -
- {:else} + {#if totalCount === 0} - + No products found. - {/each} + {:else} + {#each groupedProducts as [category, products] (category)} + + + {category.replace(/_/g, ' ')} + + + {#each products as product (product.id)} + + + + {product.name} + + + {product.brand} + +
+ {#each product.targets.slice(0, 3) as t (t)} + {t.replace(/_/g, ' ')} + {/each} + {#if product.targets.length > 3} + +{product.targets.length - 3} + {/if} +
+
+ {product.recommended_time} +
+ {/each} + {/each} + {/if}