From 40b63b3fa9b1536e7c3eedfa9a4701cc5bac59d5 Mon Sep 17 00:00:00 2001 From: DanielS Date: Mon, 7 Sep 2026 23:34:41 +0200 Subject: [PATCH] feat(customers): add pagination with 5 items per page --- shop/components/customer-accordion-list.tsx | 376 ++++++++++++-------- 1 file changed, 235 insertions(+), 141 deletions(-) diff --git a/shop/components/customer-accordion-list.tsx b/shop/components/customer-accordion-list.tsx index 98f8235..c2b2297 100644 --- a/shop/components/customer-accordion-list.tsx +++ b/shop/components/customer-accordion-list.tsx @@ -1,6 +1,6 @@ 'use client' -import React, { useState } from 'react' +import React, { useState, useMemo } from 'react' import { motion, AnimatePresence } from 'framer-motion' import Link from 'next/link' import type { EndCustomerWithDevices, FlattenedDevice } from '@/lib/types' @@ -17,6 +17,10 @@ import { AlertTriangle, Search, Package, + ChevronLeft, + ChevronRight, + ChevronsLeft, + ChevronsRight, } from 'lucide-react' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' @@ -188,6 +192,8 @@ export function CustomerAccordionList({ customers }: CustomerAccordionListProps) }) const [searchTerm, setSearchTerm] = useState('') + const [currentPage, setCurrentPage] = useState(1) + const pageSize = 5 const toggleCustomer = (id: string) => { setOpenCustomerIds((prev) => ({ @@ -196,16 +202,32 @@ export function CustomerAccordionList({ customers }: CustomerAccordionListProps) })) } - const filteredCustomers = customers.filter((c) => { - const term = searchTerm.toLowerCase() - return ( - c.company_name.toLowerCase().includes(term) || - (c.first_name && c.first_name.toLowerCase().includes(term)) || - (c.last_name && c.last_name.toLowerCase().includes(term)) || - (c.city && c.city.toLowerCase().includes(term)) || - (c.email && c.email.toLowerCase().includes(term)) - ) - }) + const filteredCustomers = useMemo(() => { + const term = searchTerm.toLowerCase().trim() + if (!term) return customers + + return customers.filter((c) => { + return ( + c.company_name.toLowerCase().includes(term) || + (c.first_name && c.first_name.toLowerCase().includes(term)) || + (c.last_name && c.last_name.toLowerCase().includes(term)) || + (c.city && c.city.toLowerCase().includes(term)) || + (c.email && c.email.toLowerCase().includes(term)) + ) + }) + }, [customers, searchTerm]) + + const totalPages = Math.ceil(filteredCustomers.length / pageSize) || 1 + + const paginatedCustomers = useMemo(() => { + const from = (currentPage - 1) * pageSize + return filteredCustomers.slice(from, from + pageSize) + }, [filteredCustomers, currentPage, pageSize]) + + const handleSearchChange = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value) + setCurrentPage(1) + } if (customers.length === 0) { return ( @@ -225,151 +247,223 @@ export function CustomerAccordionList({ customers }: CustomerAccordionListProps) return (
- {/* Suchfeld */} -
- - setSearchTerm(e.target.value)} - className="pl-9 bg-slate-900/50 border-white/10 text-white placeholder:text-slate-500 focus:border-primary" - /> + {/* Suchfeld & Info */} +
+
+ + +
+
+ Kunden: + {filteredCustomers.length} + {searchTerm && (gefiltert aus {customers.length})} +
+ {/* Keine Suchergebnisse */} + {filteredCustomers.length === 0 && ( + + + +

Keine Kunden für "{searchTerm}" gefunden.

+ +
+
+ )} + {/* Akkordeon-Liste */} -
- {filteredCustomers.map((customer) => { - const isOpen = !!openCustomerIds[customer.id] - const devices = customer.devices || [] + {paginatedCustomers.length > 0 && ( +
+ {paginatedCustomers.map((customer) => { + const isOpen = !!openCustomerIds[customer.id] + const devices = customer.devices || [] - return ( - - {/* Akkordeon-Header */} -
toggleCustomer(customer.id)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - toggleCustomer(customer.id) - } - }} - tabIndex={0} - role="button" - aria-expanded={isOpen} - className="p-5 flex items-center justify-between cursor-pointer hover:bg-white/[0.02] transition-colors select-none gap-4 flex-wrap focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-inset" + return ( + -
-
- -
-
-
-

{customer.company_name}

- {customer.is_anonymized && ( - - Anonymisiert - - )} + {/* Akkordeon-Header */} +
toggleCustomer(customer.id)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + toggleCustomer(customer.id) + } + }} + tabIndex={0} + role="button" + aria-expanded={isOpen} + className="p-5 flex items-center justify-between cursor-pointer hover:bg-white/[0.02] transition-colors select-none gap-4 flex-wrap focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-inset" + > +
+
+ +
+
+
+

{customer.company_name}

+ {customer.is_anonymized && ( + + Anonymisiert + + )} +
+

+ {[customer.first_name, customer.last_name].filter(Boolean).join(' ') || + 'Kein Ansprechpartner'} + {customer.city ? ` • ${customer.city}` : ''} + {customer.email ? ` • ${customer.email}` : ''} +

-

- {[customer.first_name, customer.last_name].filter(Boolean).join(' ') || - 'Kein Ansprechpartner'} - {customer.city ? ` • ${customer.city}` : ''} - {customer.email ? ` • ${customer.email}` : ''} -

-
-
- {/* Kassen-Anzahl Badge */} - - - {devices.length} {devices.length === 1 ? 'Kasse' : 'Kassen'} - +
+ {/* Kassen-Anzahl Badge */} + + + {devices.length} {devices.length === 1 ? 'Kasse' : 'Kassen'} + - {!customer.is_anonymized && ( - <> - e.stopPropagation()} - > - - - e.stopPropagation()} - > - + + e.stopPropagation()} > - Bearbeiten - - - - )} - - + + )} - -
-
- {/* Ausgeklappter Bereich mit Kassen */} - - {isOpen && ( - -
- {devices.length === 0 ? ( -
- Keine Kassen oder Bestellungen für diesen Kunden vorhanden. -
+
-
- )} -
- - ) - })} -
+ +
+
+ + {/* Ausgeklappter Bereich mit Kassen */} + + {isOpen && ( + +
+ {devices.length === 0 ? ( +
+ Keine Kassen oder Bestellungen für diesen Kunden vorhanden. +
+ ) : ( +
+

+ Zugeordnete Kassen ({devices.length}) +

+ {devices.map((device, idx) => ( + + ))} +
+ )} +
+
+ )} +
+
+ ) + })} +
+ )} + + {/* Paginierung (5 Kunden pro Seite) */} + {totalPages > 1 && ( +
+ + Seite {currentPage} von{' '} + {totalPages} + +
+ + + + +
+
+ )}
) }