This commit is contained in:
NotII
2025-03-18 18:19:24 +01:00
parent 740fa0a2dc
commit 2715d3a0e7
2 changed files with 185 additions and 13 deletions

View File

@@ -0,0 +1,167 @@
"use client";
import React, { useState, useEffect } from "react";
import { Package, ShoppingBag, Info, ExternalLink } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getCookie } from "@/lib/client-utils";
import axios from "axios";
import { useRouter } from "next/navigation";
interface Order {
_id: string;
orderId: number;
status: string;
totalPrice: number;
orderDate: string;
products: Array<{
productId: string;
quantity: number;
pricePerUnit: number;
totalItemPrice: number;
}>;
}
interface BuyerOrderInfoProps {
buyerId: string;
}
export default function BuyerOrderInfo({ buyerId }: BuyerOrderInfoProps) {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [orders, setOrders] = useState<Order[]>([]);
useEffect(() => {
// Only load if we have a buyerId
if (!buyerId) return;
const fetchBuyerOrders = async () => {
try {
const authToken = getCookie("Authorization");
if (!authToken) return;
const authAxios = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
headers: {
Authorization: `Bearer ${authToken}`
}
});
// Fetch orders for this specific buyer
const response = await authAxios.get(`/orders/buyer/${buyerId}?limit=5`);
if (response.data && response.data.orders) {
setOrders(response.data.orders);
}
setLoading(false);
} catch (error) {
console.error("Error fetching buyer orders:", error);
setLoading(false);
}
};
fetchBuyerOrders();
}, [buyerId]);
const handleViewOrder = (orderId: string) => {
router.push(`/dashboard/orders/${orderId}`);
};
// Format the price as currency
const formatPrice = (price: number) => {
return `£${price.toFixed(2)}`;
};
// Don't show anything while loading or if no orders
if (loading || orders.length === 0) {
return null;
}
// Count products across all orders
const productCount = orders.reduce((total, order) => {
return total + order.products.reduce((sum, product) => sum + product.quantity, 0);
}, 0);
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-2 flex items-center gap-1.5"
>
<ShoppingBag className="h-4 w-4" />
<span className="text-xs font-medium">{orders.length} Orders</span>
{productCount > 0 && (
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 ml-1">
{productCount} items
</Badge>
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" align="start" className="w-80 p-0" sideOffset={10}>
<div className="py-2">
<div className="px-3 py-2 text-xs font-medium border-b flex justify-between items-center">
<span>Recent Orders from this Customer</span>
<Info className="h-3.5 w-3.5 text-muted-foreground" />
</div>
<div className="max-h-64 overflow-y-auto divide-y divide-border">
{orders.map((order) => (
<div
key={order._id}
className="px-3 py-2 hover:bg-accent/50 cursor-pointer"
onClick={() => handleViewOrder(order._id)}
>
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-2">
<Package className="h-3.5 w-3.5" />
<span className="text-xs font-medium">Order #{order.orderId}</span>
</div>
<Badge variant={
order.status === "paid" ? "paid" :
order.status === "unpaid" ? "unpaid" :
order.status === "shipped" ? "shipped" :
order.status === "completed" ? "completed" :
"secondary"
} className="text-[10px] h-5 px-1.5">
{order.status.toUpperCase()}
</Badge>
</div>
<div className="flex justify-between items-center text-xs text-muted-foreground pl-5">
<div className="flex gap-1">
<span>{order.products.length} {order.products.length === 1 ? 'product' : 'products'}</span>
<span>·</span>
<span>{formatPrice(order.totalPrice)}</span>
</div>
<span className="text-[10px]">{new Date(order.orderDate).toLocaleDateString()}</span>
</div>
</div>
))}
</div>
<div className="border-t px-3 py-2">
<Button
variant="ghost"
size="sm"
className="w-full h-7 text-xs flex items-center gap-1.5"
onClick={() => router.push(`/dashboard/orders?buyer=${buyerId}`)}
>
<ExternalLink className="h-3.5 w-3.5" />
View All Customer Orders
</Button>
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

View File

@@ -13,6 +13,7 @@ import { toast } from "sonner";
import { ArrowLeft, Send, RefreshCw, File, FileText, Image as ImageIcon, Download } from "lucide-react";
import { getCookie } from "@/lib/client-utils";
import { ImageViewerModal } from "@/components/modals/image-viewer-modal";
import BuyerOrderInfo from "./BuyerOrderInfo";
interface Message {
_id: string;
@@ -381,20 +382,24 @@ export default function ChatDetail({ chatId }: { chatId: string }) {
return (
<div className="flex flex-col h-screen w-full relative">
<div className="border-b h-16 px-4 flex items-center space-x-2 bg-card z-10">
<Button variant="ghost" size="icon" onClick={handleBackClick}>
<ArrowLeft className="h-5 w-5" />
</Button>
<div className="flex flex-col">
<h3 className="text-lg font-semibold">
Chat with Customer {chat.buyerId.slice(-4)}
</h3>
{chat.telegramUsername && (
<span className="text-sm text-muted-foreground">
@{chat.telegramUsername}
</span>
)}
<div className="border-b h-16 px-4 flex items-center justify-between bg-card z-10">
<div className="flex items-center space-x-2">
<Button variant="ghost" size="icon" onClick={handleBackClick}>
<ArrowLeft className="h-5 w-5" />
</Button>
<div className="flex flex-col">
<h3 className="text-lg font-semibold">
Chat with Customer {chat.buyerId.slice(-4)}
</h3>
{chat.telegramUsername && (
<span className="text-sm text-muted-foreground">
@{chat.telegramUsername}
</span>
)}
</div>
</div>
<BuyerOrderInfo buyerId={chat.buyerId} />
</div>
<div className="flex-1 overflow-y-auto p-2 space-y-2 pb-[80px]">