diff --git a/app/dashboard/orders/[id]/page.tsx b/app/dashboard/orders/[id]/page.tsx index 3540c81..65a0220 100644 --- a/app/dashboard/orders/[id]/page.tsx +++ b/app/dashboard/orders/[id]/page.tsx @@ -22,7 +22,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy, Loader2, RefreshCw } from "lucide-react"; +import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy, Loader2, RefreshCw, Users, TrendingUp, Calendar, DollarSign } from "lucide-react"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { @@ -76,6 +76,26 @@ interface Order { subtotalBeforeDiscount?: number; } +interface CustomerInsights { + totalOrders: number; + completedOrders: number; + cancelledOrders: number; + paidOrders: number; + totalSpent: number; + averageOrderValue: number; + completionRate: number; + paymentSuccessRate: number; + cancellationRate: number; + firstOrder: string | null; + lastOrder: string | null; + customerSince: number; // days since first order +} + +interface OrderResponse { + order: Order; + customerInsights: CustomerInsights; +} + interface OrderInList extends Order { _id: string; } @@ -120,6 +140,7 @@ const getStatusStyle = (status: string) => { export default function OrderDetailsPage() { const [order, setOrder] = useState(null); + const [customerInsights, setCustomerInsights] = useState(null); const [trackingNumber, setTrackingNumber] = useState(""); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); @@ -358,11 +379,13 @@ export default function OrderDetailsPage() { if (!res) throw new Error("Failed to fetch order details"); - const data: Order = await res; - setOrder(data); - console.log("Fresh order data:", data); + const data: OrderResponse = await res; + setOrder(data.order); + setCustomerInsights(data.customerInsights); + console.log("Fresh order data:", data.order); + console.log("Customer insights:", data.customerInsights); - const productIds = data.products.map((product) => product.productId); + const productIds = data.order.products.map((product) => product.productId); const productNamesMap = await fetchProductNames(productIds, authToken); setProductNames(productNamesMap); @@ -378,7 +401,7 @@ export default function OrderDetailsPage() { }); }, 3000); - if (data.status === "paid") { + if (data.order.status === "paid") { setIsPaid(true); } } catch (err: any) { @@ -668,6 +691,78 @@ export default function OrderDetailsPage() { )} + {/* Customer Insights */} + {customerInsights && order?.telegramUsername && ( + + + + + Customer Insights - @{order.telegramUsername} + + + +
+
+
+ {customerInsights.totalOrders} +
+
Total Orders
+
+ {customerInsights.paidOrders} paid +
+
+ +
+
+ £{customerInsights.totalSpent.toFixed(2)} +
+
Total Spent
+
+ £{customerInsights.averageOrderValue.toFixed(2)} avg +
+
+ +
+
+ {customerInsights.paymentSuccessRate.toFixed(1)}% +
+
Success Rate
+
+ {customerInsights.completionRate.toFixed(1)}% completed +
+
+ +
+
+ {customerInsights.customerSince} +
+
Days as Customer
+
+ {customerInsights.firstOrder ? + new Date(customerInsights.firstOrder).toLocaleDateString('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric' + }) : 'N/A' + } +
+
+
+ + {customerInsights.cancellationRate > 20 && ( +
+
+ + + High cancellation rate: {customerInsights.cancellationRate.toFixed(1)}% + +
+
+ )} +
+
+ )} +
{/* Left Column - Order Details */}
diff --git a/components/tables/order-table.tsx b/components/tables/order-table.tsx index 862b796..56d1e41 100644 --- a/components/tables/order-table.tsx +++ b/components/tables/order-table.tsx @@ -30,18 +30,12 @@ import { MessageCircle, AlertTriangle, Tag, - Percent, - TrendingUp, - Users, - DollarSign, - ShoppingCart, - Calendar + Percent } from "lucide-react"; import Link from "next/link"; import { clientFetch } from '@/lib/api'; import { toast } from "sonner"; import { Checkbox } from "@/components/ui/checkbox"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { AlertDialog, AlertDialogAction, @@ -75,34 +69,7 @@ interface Order { subtotalBeforeDiscount?: number; } -interface CustomerInsights { - overview: { - totalOrders: number; - completedOrders: number; - cancelledOrders: number; - uniqueCustomers: number; - completionRate: number; - paymentSuccessRate: number; - cancellationRate: number; - }; - financial: { - totalRevenue: number; - averageOrderValue: number; - }; - recent30Days: { - orders: number; - revenue: number; - newCustomers: number; - }; -} -interface OrdersResponse { - orders: Order[]; - page: number; - totalPages: number; - totalOrders: number; - customerInsights: CustomerInsights; -} type SortableColumns = "orderId" | "totalPrice" | "status" | "orderDate" | "paidAt"; @@ -155,67 +122,7 @@ const PageSizeSelector = ({ currentSize, onChange, options }: { currentSize: num ); }; -// Customer Insights Display Component -const CustomerInsightsDisplay = ({ insights }: { insights: CustomerInsights }) => { - return ( -
- {/* Overview Stats */} - - - Total Orders - - - -
{insights.overview.totalOrders}
-

- {insights.overview.uniqueCustomers} unique customers -

-
-
- - - Success Rate - - - -
- {insights.overview.paymentSuccessRate.toFixed(1)}% -
-

- {insights.overview.completionRate.toFixed(1)}% completion rate -

-
-
- - - - Total Revenue - - - -
£{insights.financial.totalRevenue.toFixed(2)}
-

- £{insights.financial.averageOrderValue.toFixed(2)} avg order -

-
-
- - - - Last 30 Days - - - -
{insights.recent30Days.orders}
-

- £{insights.recent30Days.revenue.toFixed(2)} revenue -

-
-
-
- ); -}; export default function OrderTable() { const [orders, setOrders] = useState([]); @@ -224,7 +131,6 @@ export default function OrderTable() { const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [totalOrders, setTotalOrders] = useState(0); - const [customerInsights, setCustomerInsights] = useState(null); const [sortConfig, setSortConfig] = useState<{ column: SortableColumns; direction: "asc" | "desc"; @@ -257,13 +163,12 @@ export default function OrderTable() { ...(statusFilter !== "all" && { status: statusFilter }), }); - const data: OrdersResponse = await clientFetch(`/orders?${queryParams}`); + const data = await clientFetch(`/orders?${queryParams}`); console.log("Fetched orders with fresh data:", data.orders?.length || 0); setOrders(data.orders || []); setTotalPages(data.totalPages || 1); setTotalOrders(data.totalOrders || 0); - setCustomerInsights(data.customerInsights || null); } catch (error) { toast.error("Failed to fetch orders"); console.error("Fetch error:", error); @@ -464,9 +369,6 @@ export default function OrderTable() { return (
- {/* Customer Insights */} - {customerInsights && } -
{/* Filters header */}
diff --git a/public/git-info.json b/public/git-info.json index 9027d99..563928d 100644 --- a/public/git-info.json +++ b/public/git-info.json @@ -1,4 +1,4 @@ { - "commitHash": "fabc8f2", - "buildTime": "2025-08-30T21:51:03.692Z" + "commitHash": "9cf2265", + "buildTime": "2025-08-31T17:30:00.225Z" } \ No newline at end of file