From 4fa5f71d166090b3f8df0c536329c606cc8c995f Mon Sep 17 00:00:00 2001 From: DanielS Date: Thu, 25 Jun 2026 09:19:28 +0200 Subject: [PATCH] feat: change products view sorting to category grouping with subheaders --- shop/components/admin/product-list.tsx | 162 ++++++++++++++----------- 1 file changed, 90 insertions(+), 72 deletions(-) diff --git a/shop/components/admin/product-list.tsx b/shop/components/admin/product-list.tsx index f627ff4..c4dd7a8 100644 --- a/shop/components/admin/product-list.tsx +++ b/shop/components/admin/product-list.tsx @@ -14,12 +14,12 @@ import { Badge } from '@/components/ui/badge' import { Edit2, Trash2, Layers, ArrowUpDown } from 'lucide-react' import { Button } from '@/components/ui/button' import { deleteProduct } from '@/lib/actions/products' -import { useState, useEffect, useMemo } from 'react' +import { useState, useEffect, useMemo, Fragment } from 'react' import { CreateProductDialog } from './create-product-dialog' export function ProductList({ initialProducts, categories }: { initialProducts: Product[], categories: Category[] }) { const [products, setProducts] = useState(initialProducts) - const [isSortedByCategory, setIsSortedByCategory] = useState(false) + const [isGroupedByCategory, setIsGroupedByCategory] = useState(false) useEffect(() => { setProducts(initialProducts) @@ -32,25 +32,81 @@ export function ProductList({ initialProducts, categories }: { initialProducts: } } - const toggleSortByCategory = () => { - setIsSortedByCategory(!isSortedByCategory) + const toggleGroupByCategory = () => { + setIsGroupedByCategory(!isGroupedByCategory) } - const displayedProducts = useMemo(() => { - if (!isSortedByCategory) return products - - return [...products].sort((a, b) => { + const groupedProducts = useMemo(() => { + const groups: Record = {} + products.forEach(p => { + const catId = p.category_id || 'no-category' + if (!groups[catId]) { + groups[catId] = { + category: p.category || null, + products: [] + } + } + groups[catId].products.push(p) + }) + return Object.values(groups).sort((a, b) => { const orderA = a.category?.sort_order ?? 9999 const orderB = b.category?.sort_order ?? 9999 - if (orderA !== orderB) return orderA - orderB - - const catA = a.category?.name || '' - const catB = b.category?.name || '' - if (catA !== catB) return catA.localeCompare(catB) - - return a.name.localeCompare(b.name) + return orderA - orderB }) - }, [products, isSortedByCategory]) + }, [products]) + + const renderProductRow = (product: Product) => ( + + +
+
+ {product.name} + {product.category && ( + + {product.category.name} + + )} + {product.billing_interval === 'monthly' && ( + Abo/Monat + )} + {product.billing_interval === 'one_time' && ( + Einmalig + )} +
+

{product.description}

+
+
+ {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)} + +
+ + {product.modules?.length || 0} Module +
+
+ + + {product.billing_interval === 'monthly' ? 'Abo/Monat' : 'Einmalig'} + + + +
+ + + + +
+
+
+ ) return ( @@ -64,13 +120,13 @@ export function ProductList({ initialProducts, categories }: { initialProducts: @@ -85,59 +141,21 @@ export function ProductList({ initialProducts, categories }: { initialProducts: - {displayedProducts.map((product) => ( - - -
-
- {product.name} - {product.category && ( - - {product.category.name} - - )} - {product.billing_interval === 'monthly' && ( - Abo/Monat - )} - {product.billing_interval === 'one_time' && ( - Einmalig - )} -
-

{product.description}

-
-
- {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)} - -
- - {product.modules?.length || 0} Module -
-
- - - {product.billing_interval === 'monthly' ? 'Abo/Monat' : 'Einmalig'} - - - -
- - - - -
-
-
- ))} - {displayedProducts.length === 0 && ( + {isGroupedByCategory ? ( + groupedProducts.map((group) => ( + + + + {group.category ? group.category.name : 'Keine Kategorie'} + + + {group.products.map((product) => renderProductRow(product))} + + )) + ) : ( + products.map((product) => renderProductRow(product)) + )} + {products.length === 0 && ( Keine Produkte gefunden. Erstellen Sie Ihr erstes Produkt!