diff --git a/shop/components/wizard/step-customer.tsx b/shop/components/wizard/step-customer.tsx index dda4ad4..5aa41ac 100644 --- a/shop/components/wizard/step-customer.tsx +++ b/shop/components/wizard/step-customer.tsx @@ -5,6 +5,7 @@ import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } import { Label } from '@/components/ui/label' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' +import { Badge } from '@/components/ui/badge' import { User, Building2, @@ -16,6 +17,7 @@ import { ChevronLeft, ChevronsLeft, ChevronsRight, + Users, } from 'lucide-react' import { EndCustomer } from '@/lib/types' @@ -57,26 +59,51 @@ export function StepCustomer({ isCreatingCustomer, nextStep, }: StepCustomerProps) { - const [pageSize, setPageSize] = useState(10) - const [currentPage, setCurrentPage] = useState(1) - + // ─── PARTNER SELECTION STATE & PAGINATION (Admins only) ────────────────────── const [partnerSearchTerm, setPartnerSearchTerm] = useState('') + const [partnerPageSize, setPartnerPageSize] = useState(10) + const [partnerCurrentPage, setPartnerCurrentPage] = useState(1) + + useEffect(() => { + setPartnerCurrentPage(1) + }, [partnerSearchTerm, partnerPageSize]) + + // Count non-anonymized customers per partner company + const customerCountMap = useMemo(() => { + const counts: Record = {} + endCustomers.forEach((c) => { + if (!c.is_anonymized && c.partner_id) { + counts[c.partner_id] = (counts[c.partner_id] || 0) + 1 + } + }) + return counts + }, [endCustomers]) - // Filter partner companies by partnerSearchTerm const filteredCompanies = useMemo(() => { const term = partnerSearchTerm.toLowerCase().trim() if (!term) return companies - return companies.filter((c: any) => - c.name?.toLowerCase().includes(term) || c.id?.toLowerCase().includes(term) + return companies.filter( + (c: any) => c.name?.toLowerCase().includes(term) || c.id?.toLowerCase().includes(term) ) }, [companies, partnerSearchTerm]) - // Reset page to 1 when search or company filter or pageSize changes + const totalPartnerCount = filteredCompanies.length + const totalPartnerPages = Math.ceil(totalPartnerCount / partnerPageSize) || 1 + + const paginatedCompanies = useMemo(() => { + const from = (partnerCurrentPage - 1) * partnerPageSize + return filteredCompanies.slice(from, from + partnerPageSize) + }, [filteredCompanies, partnerCurrentPage, partnerPageSize]) + + // ─── CUSTOMER SELECTION STATE & PAGINATION ────────────────────────────────── + const [pageSize, setPageSize] = useState(10) + const [currentPage, setCurrentPage] = useState(1) + useEffect(() => { setCurrentPage(1) }, [searchTerm, selectedCompanyId, pageSize]) - // Filter customers by selected company (if admin and selectedCompanyId != 'all') + // Filter customers by selected partner (if admin and selectedCompanyId != 'all') const customersByCompany = useMemo(() => { let list = endCustomers.filter((c) => !c.is_anonymized) if (isAdmin && selectedCompanyId && selectedCompanyId !== 'all') { @@ -85,7 +112,7 @@ export function StepCustomer({ return list }, [endCustomers, isAdmin, selectedCompanyId]) - // Search filter + // Customer search filter const filteredCustomers = useMemo(() => { const term = searchTerm.toLowerCase().trim() if (!term) return customersByCompany @@ -99,14 +126,22 @@ export function StepCustomer({ ) }, [customersByCompany, searchTerm]) - const totalCount = filteredCustomers.length - const totalPages = Math.ceil(totalCount / pageSize) || 1 + const totalCustomerCount = filteredCustomers.length + const totalCustomerPages = Math.ceil(totalCustomerCount / pageSize) || 1 const paginatedCustomers = useMemo(() => { const from = (currentPage - 1) * pageSize return filteredCustomers.slice(from, from + pageSize) }, [filteredCustomers, currentPage, pageSize]) + // Click handler for customer selection + const handleSelectCustomer = (customer: EndCustomer) => { + setSelectedEndCustomerId(customer.id) + if (isAdmin && customer.partner_id && (selectedCompanyId === 'all' || !selectedCompanyId)) { + setSelectedCompanyId(customer.partner_id) + } + } + return ( @@ -115,45 +150,194 @@ export function StepCustomer({ Für wen bestellen Sie? - Wählen Sie einen Endkunden aus oder legen Sie einen neuen an. + Wählen Sie den Partner (für Admins) und den Endkunden aus oder legen Sie einen neuen an. - - {/* Admin Partner Dropdown with Search */} + + {/* ─── 1. ADMIN PARTNER SELECTION (Unified Card/List with Pagination) ─── */} {isAdmin && ( -
- -
-
+
+
+
+ +

+ Wählen Sie einen Partner aus, um dessen Kunden anzuzeigen, oder "Alle Partner". +

+
+ + Admin Modus + +
+ + {/* Partner Header Controls: Search & Page Size */} +
+
setPartnerSearchTerm(e.target.value)} - className="pl-9 bg-slate-900 border-white/10 text-white placeholder:text-slate-500 focus:border-primary text-sm h-9" + className="pl-9 bg-slate-950/80 border-white/10 text-white placeholder:text-slate-500 focus:border-primary text-sm h-9" />
- setPartnerPageSize(Number(e.target.value))} + className="h-9 rounded-md border border-white/10 bg-slate-950 px-2 py-1 text-xs text-white shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary" + > + + + + +
+
+ + {/* Partner Card List */} +
+ {/* Reserved Fixed Card: Alle Partner */} +
setSelectedCompanyId('all')} + className={`flex items-center justify-between p-3.5 rounded-xl border-2 cursor-pointer transition-all select-none ${ + selectedCompanyId === 'all' || !selectedCompanyId + ? 'border-primary bg-primary/10 shadow-lg shadow-primary/5' + : 'border-white/5 bg-slate-950/40 hover:bg-white/5' + }`} > - - {filteredCompanies.map((c: any) => ( - - ))} - +
+
+ +
+
+

+ Alle Partner (Firmenübergreifend) +

+

+ Zeigt Kunden aller registrierten Partnerunternehmen an +

+
+
+
+ + {endCustomers.filter((c) => !c.is_anonymized).length} Kunden gesamt + + {(selectedCompanyId === 'all' || !selectedCompanyId) && ( + + )} +
+
+ + {/* Dynamic Partner Cards */} + {paginatedCompanies.map((c: any) => { + const isSelected = selectedCompanyId === c.id + const customerCount = customerCountMap[c.id] || 0 + + return ( +
setSelectedCompanyId(c.id)} + className={`flex items-center justify-between p-3.5 rounded-xl border-2 cursor-pointer transition-all select-none ${ + isSelected + ? 'border-primary bg-primary/10 shadow-lg shadow-primary/5' + : 'border-white/5 bg-slate-950/40 hover:bg-white/5' + }`} + > +
+
+ +
+
+

{c.name}

+

ID: {c.id}

+
+
+ +
+ 0 + ? 'border-green-500/30 text-green-400 bg-green-500/10' + : 'border-white/10 text-slate-500' + }`} + > + {customerCount} {customerCount === 1 ? 'Kunde' : 'Kunden'} + + {isSelected && } +
+
+ ) + })} + + {filteredCompanies.length === 0 && ( +
+ Keine Partner mit dem Suchbegriff "{partnerSearchTerm}" gefunden. +
+ )} +
+ + {/* Partner Pagination Controls */} +
+
+ Zeige {totalPartnerCount === 0 ? 0 : (partnerCurrentPage - 1) * partnerPageSize + 1} bis{' '} + {Math.min(partnerCurrentPage * partnerPageSize, totalPartnerCount)} von {totalPartnerCount} Partnern +
+ +
+ + + + + Seite {partnerCurrentPage} von {totalPartnerPages} + + + + +
)} - {/* Modus-Toggle */} + {/* Modus-Toggle: Bestandskunde vs. Neuer Kunde */}
- {/* Modus A: Bestandskunde wählen */} + {/* ─── 2. CUSTOMER SELECTION (Unified Card/List with Pagination) ─── */} {customerMode === 'select' && (
{customersByCompany.length === 0 ? (
-

Keine Endkunden vorhanden.

+

+ {isAdmin && selectedCompanyId && selectedCompanyId !== 'all' + ? 'Keine Endkunden für den ausgewählten Partner vorhanden.' + : 'Noch keine Endkunden vorhanden.'} +

- Seite {currentPage} von {totalPages} + Seite {currentPage} von {totalCustomerPages}
)} - {/* Modus B: Neuen Kunden anlegen */} + {/* ─── MODUS B: NEUEN KUNDEN ANLEGEN ─── */} {customerMode === 'create' && (
{(