diff --git a/shop/components/admin/orders-table.tsx b/shop/components/admin/orders-table.tsx index bdab8c1..ab4beb8 100644 --- a/shop/components/admin/orders-table.tsx +++ b/shop/components/admin/orders-table.tsx @@ -1,11 +1,18 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Download, ExternalLink, Search } from "lucide-react"; +import { Download, ExternalLink, Search, Loader2 } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { updateOrderStatus } from "@/lib/actions/orders"; interface OrdersTableProps { initialOrders: any[]; @@ -26,10 +33,29 @@ const statusClass: Record = { }; export function OrdersTable({ initialOrders }: OrdersTableProps) { + const [orders, setOrders] = useState(initialOrders); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); + const [updatingId, setUpdatingId] = useState(null); - const filteredOrders = initialOrders.filter((order) => { + useEffect(() => { + setOrders(initialOrders); + }, [initialOrders]); + + const handleStatusChange = async (orderId: string, newStatus: 'pending' | 'active' | 'completed' | 'cancelled') => { + setUpdatingId(orderId); + try { + await updateOrderStatus(orderId, newStatus); + setOrders(prev => prev.map(o => o.id === orderId ? { ...o, status: newStatus } : o)); + } catch (error) { + console.error(error); + alert("Fehler beim Ändern des Status."); + } finally { + setUpdatingId(null); + } + }; + + const filteredOrders = orders.filter((order) => { const orderNum = (order.order_number || "").toLowerCase(); const company = (order.customer_data?.company_name || "").toLowerCase(); const contact = `${order.customer_data?.first_name || ""} ${order.customer_data?.last_name || ""}`.toLowerCase(); @@ -163,9 +189,31 @@ export function OrdersTable({ initialOrders }: OrdersTableProps) { {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_price)} - - {statusLabel[order.status] ?? order.status} - + + + + + + {Object.entries(statusLabel).map(([key, label]) => ( + handleStatusChange(order.id, key as any)} + className={`text-xs cursor-pointer hover:bg-white/10 transition-colors focus:bg-white/10 focus:text-white ${order.status === key ? 'font-bold text-primary' : ''}`} + > + {label} + + ))} + + {order.pdf_url ? ( diff --git a/shop/lib/actions/orders.ts b/shop/lib/actions/orders.ts index dcb509d..a3aaf05 100644 --- a/shop/lib/actions/orders.ts +++ b/shop/lib/actions/orders.ts @@ -1,6 +1,7 @@ 'use server' import { createClient } from '@/lib/supabase/server' +import { createAdminClient } from '@/lib/supabase/admin' import { revalidatePath } from 'next/cache' import { sendMail } from '@/utils/mail' import { InvoicePDF } from '@/components/invoice-pdf' @@ -280,3 +281,96 @@ export async function submitOrder(params: { revalidatePath('/my-orders') return order as Order } + +export async function updateOrderStatus( + orderId: string, + newStatus: 'pending' | 'active' | 'completed' | 'cancelled' +) { + const admin = createAdminClient() + + // 1. Fetch current order + const { data: order, error: fetchError } = await admin + .from('orders') + .select('id, user_id, order_number, status') + .eq('id', orderId) + .single() + + if (fetchError || !order) { + throw new Error(`Bestellung nicht gefunden: ${fetchError?.message || 'Unbekannt'}`) + } + + const oldStatus = order.status + + if (oldStatus === newStatus) { + return order + } + + // 2. Update order status + const { data: updatedOrder, error: updateError } = await admin + .from('orders') + .update({ status: newStatus }) + .eq('id', orderId) + .select() + .single() + + if (updateError || !updatedOrder) { + throw new Error(`Fehler beim Aktualisieren des Status: ${updateError?.message || 'Unbekannt'}`) + } + + // 3. Send email to the retailer (händler) + if (order.user_id) { + try { + const { data: { user }, error: userError } = await admin.auth.admin.getUserById(order.user_id) + + if (!userError && user && user.email) { + const orderNumber = order.order_number || order.id.slice(0, 8) + const statusLabelMap: Record = { + pending: 'Eingegangen', + active: 'In Bearbeitung', + completed: 'Abgeschlossen', + cancelled: 'Storniert', + } + const oldLabel = statusLabelMap[oldStatus] || oldStatus + const newLabel = statusLabelMap[newStatus] || newStatus + + await sendMail({ + to: user.email, + subject: `Statusänderung Ihrer Bestellung ${orderNumber}`, + text: `Hallo,\n\nder Status Ihrer Bestellung mit der Nummer ${orderNumber} hat sich geändert.\n\nNeuer Status: ${newLabel} (vorher: ${oldLabel})\n\nViele Grüße,\nIhr CASPOS Shop-Team`, + html: ` +
+

Status Ihrer Bestellung hat sich geändert

+

Hallo,

+

der Status Ihrer Bestellung mit der Nummer ${orderNumber} wurde aktualisiert.

+
+ + + + + + + + + + + + + +
Bestellnummer:${orderNumber}
Alter Status:${oldLabel}
Neuer Status:${newLabel}
+
+

Viele Grüße,
Ihr CASPOS Shop-Team

+
+

Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht darauf.

+
+ ` + }) + } + } catch (mailError) { + console.error('Failed to send status update email:', mailError) + } + } + + revalidatePath('/admin/orders') + revalidatePath('/my-orders') + return updatedOrder as Order +}