From 57e130a2479874d56a094866604cf1a0c3a7165b Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:06:58 +0100 Subject: [PATCH] i need a shit --- app/dashboard/orders/[id]/page.tsx | 109 ++++++++++++++++- components/analytics/OrderAnalyticsChart.tsx | 8 +- components/dashboard/BuyerOrderInfo.tsx | 118 ++++++++++++------- components/tables/order-table.tsx | 62 ++++++++-- lib/types/index.ts | 5 + 5 files changed, 242 insertions(+), 60 deletions(-) diff --git a/app/dashboard/orders/[id]/page.tsx b/app/dashboard/orders/[id]/page.tsx index 6fdc873..d76c475 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 } from "lucide-react"; +import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy } from "lucide-react"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { @@ -61,6 +61,11 @@ interface Order { stars: number; _id: string; }; + underpaid?: boolean; + underpaymentAmount?: number; + lastBalanceReceived?: number; + cryptoTotal?: number; + paymentAddress?: string; } interface OrderInList extends Order { @@ -74,6 +79,16 @@ interface OrdersResponse { totalOrders: number; } +type ProductNames = Record; +type NavigationInfo = { + nextOrderId: string | null; + prevOrderId: string | null; + totalOrders: number; + currentOrderNumber: number; + totalPages: number; + currentPage: number; +}; + const getStatusStyle = (status: string) => { switch (status) { case 'acknowledged': @@ -454,10 +469,38 @@ export default function OrderDetailsPage() { } }; - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); + const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text); + toast.success("Payment address copied to clipboard"); + } catch (error) { + toast.error("Failed to copy to clipboard"); + } }; + // Helper function to check if order is underpaid + const isOrderUnderpaid = (order: Order | null) => { + return order?.underpaid === true && order?.underpaymentAmount && order.underpaymentAmount > 0; + }; + + // Helper function to get underpaid information + const getUnderpaidInfo = (order: Order | null) => { + if (!isOrderUnderpaid(order)) return null; + + const received = order?.lastBalanceReceived || 0; + const required = order?.cryptoTotal || 0; + const missing = order?.underpaymentAmount || 0; + + return { + received, + required, + missing, + percentage: required > 0 ? ((received / required) * 100).toFixed(1) : 0 + }; + }; + + const underpaidInfo = getUnderpaidInfo(order); + if (loading) return ( @@ -481,8 +524,16 @@ export default function OrderDetailsPage() { Order {order?.orderId} -
- {order?.status?.toUpperCase()} +
+
+ {order?.status?.toUpperCase()} +
+ {isOrderUnderpaid(order) && ( +
+ + UNDERPAID ({underpaidInfo?.percentage}%) +
+ )}
@@ -507,6 +558,54 @@ export default function OrderDetailsPage() {
+ {/* Underpaid Alert Card */} + {isOrderUnderpaid(order) && underpaidInfo && ( + + + + + Payment Underpaid + + + +
+
+

Required Amount

+

{underpaidInfo.required}

+
+
+

Received Amount

+

{underpaidInfo.received}

+
+
+

Missing Amount

+

{underpaidInfo.missing}

+
+
+

Payment Progress

+

{underpaidInfo.percentage}% paid

+
+
+ + {order?.paymentAddress && ( +
+

Payment Address:

+
+ {order.paymentAddress} + +
+
+ )} +
+
+ )} +
{/* Left Column - Order Details */}
diff --git a/components/analytics/OrderAnalyticsChart.tsx b/components/analytics/OrderAnalyticsChart.tsx index 6dad5de..fa01263 100644 --- a/components/analytics/OrderAnalyticsChart.tsx +++ b/components/analytics/OrderAnalyticsChart.tsx @@ -5,7 +5,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Badge } from "@/components/ui/badge"; import { useToast } from "@/hooks/use-toast"; import { Skeleton } from "@/components/ui/skeleton"; -import { BarChart3, Clock, CheckCircle, XCircle, AlertCircle } from "lucide-react"; +import { BarChart3, Clock, CheckCircle, XCircle, AlertCircle, AlertTriangle } from "lucide-react"; import { getOrderAnalyticsWithStore, type OrderAnalytics } from "@/lib/services/analytics-service"; import { formatGBP } from "@/utils/format"; import { ChartSkeleton } from './SkeletonLoaders'; @@ -58,6 +58,8 @@ export default function OrderAnalyticsChart({ timeRange }: OrderAnalyticsChartPr return 'bg-red-100 text-red-800'; case 'disputed': return 'bg-orange-100 text-orange-800'; + case 'underpaid': + return 'bg-red-200 text-red-900'; default: return 'bg-gray-100 text-gray-800'; } @@ -76,6 +78,8 @@ export default function OrderAnalyticsChart({ timeRange }: OrderAnalyticsChartPr return ; case 'cancelled': return ; + case 'underpaid': + return ; default: return ; } @@ -99,6 +103,8 @@ export default function OrderAnalyticsChart({ timeRange }: OrderAnalyticsChartPr return 'Cancelled'; case 'disputed': return 'Disputed'; + case 'underpaid': + return 'Underpaid'; default: return status.charAt(0).toUpperCase() + status.slice(1); } diff --git a/components/dashboard/BuyerOrderInfo.tsx b/components/dashboard/BuyerOrderInfo.tsx index fc74d20..615837e 100644 --- a/components/dashboard/BuyerOrderInfo.tsx +++ b/components/dashboard/BuyerOrderInfo.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useState, useEffect, useCallback, useRef } from "react"; -import { Package, ShoppingBag, Info, ExternalLink, Loader2 } from "lucide-react"; +import { Package, ShoppingBag, Info, ExternalLink, Loader2, AlertTriangle } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { @@ -26,6 +26,10 @@ interface Order { pricePerUnit: number; totalItemPrice: number; }>; + underpaid?: boolean; + underpaymentAmount?: number; + lastBalanceReceived?: number; + cryptoTotal?: number; } interface BuyerOrderInfoProps { @@ -141,6 +145,21 @@ export default function BuyerOrderInfo({ buyerId, chatId }: BuyerOrderInfoProps) return `£${price.toFixed(2)}`; }; + // Helper function to check if order is underpaid + const isOrderUnderpaid = (order: Order) => { + return order.underpaid === true && order.underpaymentAmount && order.underpaymentAmount > 0; + }; + + // Helper function to get underpaid percentage + const getUnderpaidPercentage = (order: Order) => { + if (!isOrderUnderpaid(order)) return 0; + + const received = order.lastBalanceReceived || 0; + const required = order.cryptoTotal || 0; + + return required > 0 ? ((received / required) * 100) : 0; + }; + // If we know there are no orders, don't show the component at all if (hasOrders === false) { return null; @@ -172,33 +191,38 @@ export default function BuyerOrderInfo({ buyerId, chatId }: BuyerOrderInfoProps) {productCount} items )} + {orders.some(order => isOrderUnderpaid(order)) && ( + + )} -
-
- Recent Orders from this Customer - + {loading ? ( +
+ + Loading orders...
- - {loading ? ( - // Show loading state -
- - Loading orders... + ) : orders.length === 0 ? ( +
+ +

No orders found

+
+ ) : ( + <> +
+
+

Recent Orders

+
+ + {orders.length} {orders.length === 1 ? 'order' : 'orders'} +
+
- ) : orders.length === 0 ? ( - // Show no orders state -
- No orders found for this customer -
- ) : ( - // Show orders + + {/* Show orders */}
{orders.map((order) => (
Order #{order.orderId} + {isOrderUnderpaid(order) && ( + + )} +
+
+ + {order.status.toUpperCase()} + + {isOrderUnderpaid(order) && ( + + {getUnderpaidPercentage(order).toFixed(0)}% PAID + + )}
- - {order.status.toUpperCase()} -
@@ -227,26 +261,20 @@ export default function BuyerOrderInfo({ buyerId, chatId }: BuyerOrderInfoProps) {order.products.length} {order.products.length === 1 ? 'product' : 'products'} · {formatPrice(order.totalPrice)} + {isOrderUnderpaid(order) && ( + <> + · + Underpaid + + )}
{new Date(order.orderDate).toLocaleDateString()}
))}
- )} - -
- -
-
+ + )} diff --git a/components/tables/order-table.tsx b/components/tables/order-table.tsx index 9ca1aea..5063b79 100644 --- a/components/tables/order-table.tsx +++ b/components/tables/order-table.tsx @@ -27,7 +27,8 @@ import { ChevronRight, ArrowUpDown, Truck, - MessageCircle + MessageCircle, + AlertTriangle } from "lucide-react"; import Link from "next/link"; import { clientFetch } from '@/lib/api'; @@ -53,6 +54,10 @@ interface Order { orderDate: Date; telegramUsername?: string; telegramBuyerId?: string; + underpaid?: boolean; + underpaymentAmount?: number; + lastBalanceReceived?: number; + cryptoTotal?: number; } type SortableColumns = "orderId" | "totalPrice" | "status" | "orderDate"; @@ -281,6 +286,27 @@ export default function OrderTable() { } }; + // Helper function to determine if order is underpaid + const isOrderUnderpaid = (order: Order) => { + return order.underpaid === true && order.underpaymentAmount && order.underpaymentAmount > 0; + }; + + // Helper function to get underpaid display info + const getUnderpaidInfo = (order: Order) => { + if (!isOrderUnderpaid(order)) return null; + + const received = order.lastBalanceReceived || 0; + const required = order.cryptoTotal || 0; + const missing = order.underpaymentAmount || 0; + + return { + received, + required, + missing, + percentage: required > 0 ? ((received / required) * 100).toFixed(1) : 0 + }; + }; + return (
@@ -368,6 +394,7 @@ export default function OrderTable() { {paginatedOrders.map((order) => { const StatusIcon = statusConfig[order.status as keyof typeof statusConfig]?.icon || XCircle; + const underpaidInfo = getUnderpaidInfo(order); return ( @@ -379,15 +406,32 @@ export default function OrderTable() { /> #{order.orderId} - £{order.totalPrice.toFixed(2)} -
- {React.createElement(statusConfig[order.status as OrderStatus]?.icon || XCircle, { - className: `h-4 w-4 ${statusConfig[order.status as OrderStatus]?.animate || ""}` - })} - {order.status.charAt(0).toUpperCase() + order.status.slice(1)} +
+ £{order.totalPrice.toFixed(2)} + {underpaidInfo && ( + + Missing: £{(underpaidInfo.missing * 100).toFixed(2)} + + )} +
+ + +
+
+ {React.createElement(statusConfig[order.status as OrderStatus]?.icon || XCircle, { + className: `h-4 w-4 ${statusConfig[order.status as OrderStatus]?.animate || ""}` + })} + {order.status.charAt(0).toUpperCase() + order.status.slice(1)} +
+ {isOrderUnderpaid(order) && ( +
+ + {underpaidInfo?.percentage}% +
+ )}
diff --git a/lib/types/index.ts b/lib/types/index.ts index f9605c7..8d8dad7 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -77,6 +77,11 @@ export interface Order { totalPrice: number createdAt: string telegramUsername?: string + underpaid?: boolean + underpaymentAmount?: number + lastBalanceReceived?: number + underpaymentNotified?: boolean + cryptoTotal?: number } export type OrderStatus = "paid" | "unpaid" | "confirming" | "shipped" | "completed" | "disputed" | "cancelled"