optimization

This commit is contained in:
g
2026-01-11 07:35:52 +00:00
parent 027cc49430
commit 0fba981bca
8 changed files with 339 additions and 289 deletions

View File

@@ -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 */}

View File

@@ -47,7 +47,8 @@ import { DateRange } from "react-day-picker";
import { addDays, startOfDay, endOfDay } from "date-fns";
import type { DateRange as ProfitDateRange } from "@/lib/services/profit-analytics-service";
// Lazy load chart components
// Lazy load chart components - already handled individually below
const RevenueChart = dynamic(() => import("./RevenueChart"), {
loading: () => <ChartSkeleton />,
});

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { useState, useEffect, memo, useMemo, useCallback } from "react";
import {
Card,
CardContent,