diff --git a/shop/app/my-orders/MyOrdersClient.tsx b/shop/app/my-orders/MyOrdersClient.tsx new file mode 100644 index 0000000..196d8d4 --- /dev/null +++ b/shop/app/my-orders/MyOrdersClient.tsx @@ -0,0 +1,277 @@ +'use client' + +import React, { useState, useMemo } from 'react' +import Link from 'next/link' +import { Download, ExternalLink, ShoppingBag, ArrowLeft, Package, Search, ChevronLeft, ChevronRight } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card, CardContent } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Input } from '@/components/ui/input' +import type { Order } from '@/lib/types' + +const statusLabel: Record = { + pending: 'Eingegangen', + active: 'In Bearbeitung', + completed: 'Abgeschlossen', + cancelled: 'Storniert', + rejected: 'Abgelehnt', +} + +const statusClass: Record = { + pending: 'bg-amber-500/20 text-amber-400 border-amber-500/30', + active: 'bg-green-500/20 text-green-400 border-green-500/30', + completed: 'bg-blue-500/20 text-blue-400 border-blue-500/30', + cancelled: 'bg-red-500/20 text-red-400 border-red-500/30', + rejected: 'bg-rose-500/20 text-rose-400 border-rose-500/30', +} + +interface MyOrdersClientProps { + orders: Order[] + error: string | null +} + +export function MyOrdersClient({ orders, error }: MyOrdersClientProps) { + const [searchTerm, setSearchTerm] = useState('') + const [currentPage, setCurrentPage] = useState(1) + const pageSize = 10 + + // Volltextsuche filtern + const filteredOrders = useMemo(() => { + const term = searchTerm.toLowerCase().trim() + if (!term) return orders + + return orders.filter(o => { + // Order number + if (o.order_number?.toString().toLowerCase().includes(term)) return true + + // Status label or raw status + const label = statusLabel[o.status] || o.status + if (label.toLowerCase().includes(term) || o.status.toLowerCase().includes(term)) return true + + // Date + const dateStr = new Date(o.created_at).toLocaleDateString('de-DE') + if (dateStr.includes(term)) return true + + // Order items (device name, product name, module name) + const items = o.order_data?.items ?? [] + for (const item of items) { + if (item.device_name?.toLowerCase().includes(term)) return true + if (item.product_name?.toLowerCase().includes(term)) return true + if (item.license_number?.toLowerCase().includes(term)) return true + + for (const mod of item.selected_modules ?? []) { + if (mod.module_name?.toLowerCase().includes(term)) return true + } + } + + return false + }) + }, [orders, searchTerm]) + + // Paginierung berechnen + const totalPages = Math.ceil(filteredOrders.length / pageSize) || 1 + const paginatedOrders = useMemo(() => { + const start = (currentPage - 1) * pageSize + return filteredOrders.slice(start, start + pageSize) + }, [filteredOrders, currentPage, pageSize]) + + const handleSearchChange = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value) + setCurrentPage(1) + } + + return ( +
+
+ {/* Header */} +
+ + + +
+

+ + Meine Anfragen +

+

+ Alle Ihre Anfragen auf einen Blick – inkl. aktuellem Status. +

+
+
+ + {/* Suche & Statistik */} +
+
+ + +
+
+ Gefunden: {filteredOrders.length} von {orders.length} Anfragen +
+
+ + {/* Fehler */} + {error && ( +
+ Fehler beim Laden der Anfragen: {error} +
+ )} + + {/* Keine Anfragen (aus DB) */} + {!error && orders.length === 0 && ( + + + +

Sie haben noch keine Anfragen aufgegeben.

+ + + +
+
+ )} + + {/* Keine Treffer nach Suche */} + {!error && orders.length > 0 && filteredOrders.length === 0 && ( + + + +

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

+ +
+
+ )} + + {/* Anfragenliste */} + {paginatedOrders.length > 0 && ( +
+ {paginatedOrders.map((o) => { + const items = o.order_data?.items ?? [] + return ( + + + {/* Kopfzeile */} +
+
+

Anfragenummer

+

#{o.order_number}

+
+
+

Datum

+

+ {new Date(o.created_at).toLocaleDateString('de-DE', { + day: '2-digit', month: '2-digit', year: 'numeric', + })} +

+
+
+

Status

+ + {statusLabel[o.status] ?? o.status} + +
+
+

Gesamt

+

+ {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(o.total_price)} +

+
+
+ + {/* Produkte kurz */} + {items.length > 0 && ( +
+ {items.map((item, idx) => ( + + {item.device_name || 'Kasse'}: + {item.product_name} + {item.selected_modules.length > 0 && ( + + (+{item.selected_modules.length} {item.selected_modules.length === 1 ? 'Modul' : 'Module'}) + + )} + + ))} +
+ )} + + {/* Aktionen */} +
+ + + + {o.status !== 'completed' && ( + + + + )} + {o.pdf_url && ( + <> + + + + )} +
+
+
+ ) + })} +
+ )} + + {/* Paginierung (10 pro Seite) */} + {totalPages > 1 && ( +
+ + Seite {currentPage} von {totalPages} + +
+ + +
+
+ )} +
+
+ ) +} diff --git a/shop/app/my-orders/page.tsx b/shop/app/my-orders/page.tsx index cf1aaf2..073b620 100644 --- a/shop/app/my-orders/page.tsx +++ b/shop/app/my-orders/page.tsx @@ -1,27 +1,7 @@ import { createClient } from '@/lib/supabase/server' import { redirect } from 'next/navigation' -import Link from 'next/link' -import { Download, ExternalLink, ShoppingBag, ArrowLeft, Package } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { Card, CardContent } from '@/components/ui/card' -import { Badge } from '@/components/ui/badge' import type { Order } from '@/lib/types' - -const statusLabel: Record = { - pending: 'Eingegangen', - active: 'In Bearbeitung', - completed: 'Abgeschlossen', - cancelled: 'Storniert', - rejected: 'Abgelehnt', -} - -const statusClass: Record = { - pending: 'bg-amber-500/20 text-amber-400 border-amber-500/30', - active: 'bg-green-500/20 text-green-400 border-green-500/30', - completed: 'bg-blue-500/20 text-blue-400 border-blue-500/30', - cancelled: 'bg-red-500/20 text-red-400 border-red-500/30', - rejected: 'bg-rose-500/20 text-rose-400 border-rose-500/30', -} +import { MyOrdersClient } from './MyOrdersClient' export default async function MyOrdersPage() { const supabase = await createClient() @@ -47,137 +27,9 @@ export default async function MyOrdersPage() { const { data: orders, error } = await query.order('created_at', { ascending: false }) return ( -
-
- {/* Header */} -
- - - -
-

- - Meine Anfragen -

-

- Alle Ihre Anfragen auf einen Blick – inkl. aktuellem Status. -

-
-
- - {/* Fehler */} - {error && ( -
- Fehler beim Laden der Anfragen: {error.message} -
- )} - - {/* Keine Anfragen */} - {!error && (!orders || orders.length === 0) && ( - - - -

Sie haben noch keine Anfragen aufgegeben.

- - - -
-
- )} - - {/* Anfragenliste */} -
- {(orders ?? []).map((order) => { - const o = order as Order - const items = o.order_data?.items ?? [] - return ( - - - {/* Kopfzeile */} -
-
-

Anfragenummer

-

#{o.order_number}

-
-
-

Datum

-

- {new Date(o.created_at).toLocaleDateString('de-DE', { - day: '2-digit', month: '2-digit', year: 'numeric', - })} -

-
-
-

Status

- - {statusLabel[o.status] ?? o.status} - -
-
-

Gesamt

-

- {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(o.total_price)} -

-
-
- - {/* Produkte kurz */} - {items.length > 0 && ( -
- {items.map((item, idx) => ( - - {item.device_name || 'Kasse'}: - {item.product_name} - {item.selected_modules.length > 0 && ( - - (+{item.selected_modules.length} {item.selected_modules.length === 1 ? 'Modul' : 'Module'}) - - )} - - ))} -
- )} - - {/* Aktionen */} -
- - - - {o.status !== 'completed' && ( - - - - )} - {o.pdf_url && ( - <> - - - - )} -
-
-
- ) - })} -
-
-
+ ) }