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