feat: add Kaufprodukte and Mietprodukte filter checkboxes in products view
All checks were successful
Staging Build / build (push) Successful in 2m48s

This commit is contained in:
DanielS
2026-06-25 12:51:17 +02:00
parent fd7a53b954
commit fe05e13d3f

View File

@@ -13,6 +13,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
import { Badge } from '@/components/ui/badge'
import { Edit2, Trash2, Layers, ArrowUpDown } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { deleteProduct } from '@/lib/actions/products'
import { useState, useEffect, useMemo, Fragment } from 'react'
import { CreateProductDialog } from './create-product-dialog'
@@ -20,6 +21,8 @@ import { CreateProductDialog } from './create-product-dialog'
export function ProductList({ initialProducts, categories }: { initialProducts: Product[], categories: Category[] }) {
const [products, setProducts] = useState(initialProducts)
const [isGroupedByCategory, setIsGroupedByCategory] = useState(false)
const [showKauf, setShowKauf] = useState(true)
const [showAbo, setShowAbo] = useState(true)
useEffect(() => {
setProducts(initialProducts)
@@ -36,9 +39,17 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
setIsGroupedByCategory(!isGroupedByCategory)
}
const filteredProducts = useMemo(() => {
return products.filter(p => {
if (p.billing_interval === 'one_time') return showKauf
if (p.billing_interval === 'monthly') return showAbo
return true
})
}, [products, showKauf, showAbo])
const groupedProducts = useMemo(() => {
const groups: Record<string, { category: Category | null; products: Product[] }> = {}
products.forEach(p => {
filteredProducts.forEach(p => {
const catId = p.category_id || 'no-category'
if (!groups[catId]) {
groups[catId] = {
@@ -53,7 +64,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
const orderB = b.category?.sort_order ?? 9999
return orderA - orderB
})
}, [products])
}, [filteredProducts])
const renderProductRow = (product: Product) => (
<TableRow key={product.id} className="border-slate-100 dark:border-white/5 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors">
@@ -110,13 +121,28 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
return (
<Card className="bg-white dark:bg-slate-900/50 border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden text-slate-900 dark:text-white">
<CardHeader className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<CardHeader className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
<div>
<CardTitle>Produktübersicht</CardTitle>
<CardDescription className="text-slate-500 dark:text-slate-400">
Alle konfigurierten Software-Lösungen und Erweiterungen.
</CardDescription>
</div>
<div className="flex flex-wrap items-center gap-6">
<label className="flex items-center gap-2 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer select-none">
<Checkbox
checked={showKauf}
onCheckedChange={(checked) => setShowKauf(!!checked)}
/>
Kaufprodukte
</label>
<label className="flex items-center gap-2 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer select-none">
<Checkbox
checked={showAbo}
onCheckedChange={(checked) => setShowAbo(!!checked)}
/>
Mietprodukte
</label>
<Button
variant="outline"
size="sm"
@@ -128,6 +154,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
<ArrowUpDown className="w-4 h-4 mr-2" />
{isGroupedByCategory ? 'Kategoriengruppierung aktiv' : 'Nach Kategorien gruppieren'}
</Button>
</div>
</CardHeader>
<CardContent>
<Table>
@@ -153,12 +180,12 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
</Fragment>
))
) : (
products.map((product) => renderProductRow(product))
filteredProducts.map((product) => renderProductRow(product))
)}
{products.length === 0 && (
{filteredProducts.length === 0 && (
<TableRow>
<TableCell colSpan={5} className="text-center py-10 text-slate-500 dark:text-slate-400">
Keine Produkte gefunden. Erstellen Sie Ihr erstes Produkt!
Keine Produkte gefunden. Passen Sie Ihre Filter an.
</TableCell>
</TableRow>
)}