optimization
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Search, Filter, Eye, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
import OrderDetailsModal from "./OrderDetailsModal";
|
||||
|
||||
interface Order {
|
||||
@@ -57,30 +58,85 @@ const getStatusStyle = (status: string) => {
|
||||
* This component should only be used in admin contexts
|
||||
*/
|
||||
export default function OrdersTable({ orders, enableModal = true }: OrdersTableProps) {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("all");
|
||||
const [selectedOrderId, setSelectedOrderId] = useState<number | string | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [selectedOrderId, setSelectedOrderId] = useState<string | number | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const itemsPerPage = 10;
|
||||
|
||||
const itemsPerPage = 20;
|
||||
|
||||
// Filter orders based on search and status
|
||||
const filteredOrders = orders.filter(order => {
|
||||
const matchesSearch = order.orderId.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
order.userId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(order.vendorUsername && order.vendorUsername.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
|
||||
const matchesStatus = statusFilter === "all" || order.status === statusFilter;
|
||||
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
const filteredOrders = useMemo(() => {
|
||||
return orders.filter((order) => {
|
||||
const matchesSearch = searchTerm === "" ||
|
||||
order.orderId.toString().toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
order.userId.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(order.vendorUsername && order.vendorUsername.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
|
||||
// Calculate pagination
|
||||
const matchesStatus = statusFilter === "all" || order.status === statusFilter;
|
||||
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
}, [orders, searchTerm, statusFilter]);
|
||||
|
||||
// Pagination calculations
|
||||
const totalPages = Math.ceil(filteredOrders.length / itemsPerPage);
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = startIndex + itemsPerPage;
|
||||
const currentOrders = filteredOrders.slice(startIndex, endIndex);
|
||||
|
||||
// Virtual scrolling row renderer
|
||||
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => {
|
||||
const order = currentOrders[index];
|
||||
if (!order) return null;
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<TableRow>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Determine if we should use virtual scrolling (for performance with large datasets)
|
||||
const useVirtualScrolling = currentOrders.length > 50;
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page);
|
||||
};
|
||||
@@ -152,47 +208,58 @@ export default function OrdersTable({ orders, enableModal = true }: OrdersTableP
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{currentOrders.map((order) => (
|
||||
<TableRow key={order.orderId}>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
{useVirtualScrolling ? (
|
||||
<List
|
||||
height={400}
|
||||
itemCount={currentOrders.length}
|
||||
itemSize={60}
|
||||
className="border"
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
) : (
|
||||
<TableBody>
|
||||
{currentOrders.map((order) => (
|
||||
<TableRow key={order.orderId}>
|
||||
<TableCell className="font-medium">{order.orderId}</TableCell>
|
||||
<TableCell>{order.userId}</TableCell>
|
||||
<TableCell>{order.vendorUsername || 'N/A'}</TableCell>
|
||||
<TableCell className="max-w-[200px] truncate">
|
||||
{order.items.length > 0 ? order.items[0].name : 'No items'}
|
||||
</TableCell>
|
||||
<TableCell>£{order.total.toFixed(2)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order.status)}`}>
|
||||
{order.status.toUpperCase()}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>N/A</TableCell>
|
||||
<TableCell>{new Date(order.createdAt).toLocaleDateString()}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{enableModal ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleViewOrder(order.orderId)}
|
||||
title="View order details (Admin only)"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled
|
||||
title="Order details modal disabled"
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
|
||||
{/* Pagination */}
|
||||
|
||||
Reference in New Issue
Block a user