diff --git a/app/login/page.tsx b/app/login/page.tsx index df12d5d..4c41563 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -8,7 +8,6 @@ import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Github } from "lucide-react"; // ✅ Added missing GitHub icon export default function LoginPage() { const [username, setUsername] = useState(""); // ✅ Fixed incorrect state name diff --git a/components/kokonutui/sidebar.tsx b/components/kokonutui/sidebar.tsx index 8cafa97..c1e5b55 100644 --- a/components/kokonutui/sidebar.tsx +++ b/components/kokonutui/sidebar.tsx @@ -41,7 +41,7 @@ export default function Sidebar() { if (!res.ok) throw new Error("Logout failed") document.cookie = "authToken=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT;" // Clear cookie - router.push("/login") // Redirect to login page + router.push("/login") } catch (error) { console.error("Error logging out:", error) } diff --git a/components/order-table.tsx b/components/order-table.tsx index 0cb7411..2f4277c 100644 --- a/components/order-table.tsx +++ b/components/order-table.tsx @@ -24,12 +24,14 @@ import { XCircle, ChevronLeft, ChevronRight, + ArrowUpDown, + Truck, } from "lucide-react"; import Link from "next/link"; import { fetchWithAuthClient } from "@/lib/client-utils"; import { toast } from "sonner"; +import { Checkbox } from "@/components/ui/checkbox"; -// ✅ Define Order Type interface Order { _id: string; orderId: number; @@ -37,42 +39,32 @@ interface Order { totalPrice: number; escrowExpiresAt: string; telegramUsername: string; -} - -interface OrdersResponse { - orders: Order[]; - page: number; - totalPages: number; + createdAt: string; } export default function OrderTable() { - const [orders, setOrders] = useState(null); + const [orders, setOrders] = useState([]); + const [filteredOrders, setFilteredOrders] = useState([]); + const [selectedOrders, setSelectedOrders] = useState>(new Set()); const [loading, setLoading] = useState(true); - const [statusFilter, setStatusFilter] = useState("all"); // ✅ Status Filter + const [statusFilter, setStatusFilter] = useState("all"); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); + const [sortColumn, setSortColumn] = useState("createdAt"); + const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc"); + const ITEMS_PER_PAGE = 10; - // ✅ Fetch Orders with Pagination & Filtering + // Fetch Orders useEffect(() => { const fetchOrders = async () => { try { setLoading(true); - const query = new URLSearchParams({ - page: currentPage.toString(), - limit: "10", - }); - - if (statusFilter !== "all") { - query.append("status", statusFilter); - } - - const response: OrdersResponse = await fetchWithAuthClient( - `/orders?${query.toString()}` - ); + const response = await fetchWithAuthClient("/orders"); if (response.orders) { setOrders(response.orders); - setTotalPages(response.totalPages); + setFilteredOrders(response.orders); + setTotalPages(Math.ceil(response.orders.length / ITEMS_PER_PAGE)); } else { throw new Error("Invalid API response format"); } @@ -85,17 +77,99 @@ export default function OrderTable() { }; fetchOrders(); - }, [statusFilter, currentPage]); + }, []); + + // Filter Orders by Status + useEffect(() => { + const filtered = orders.filter( + (order) => statusFilter === "all" || order.status === statusFilter + ); + setFilteredOrders(filtered); + setTotalPages(Math.ceil(filtered.length / ITEMS_PER_PAGE)); + setCurrentPage(1); // Reset to the first page on filter change + }, [statusFilter, orders]); + + // Sort Orders + const sortedOrders = filteredOrders.sort((a, b) => { + const aValue = a[sortColumn as keyof Order]; + const bValue = b[sortColumn as keyof Order]; + + if (typeof aValue === "number" || typeof aValue === "string") { + if (aValue < bValue) return sortDirection === "asc" ? -1 : 1; + if (aValue > bValue) return sortDirection === "asc" ? 1 : -1; + } + + return 0; + }); + + // Paginated Orders + const paginatedOrders = sortedOrders.slice( + (currentPage - 1) * ITEMS_PER_PAGE, + currentPage * ITEMS_PER_PAGE + ); + + // Handle Sorting + const handleSort = (column: string) => { + if (column === sortColumn) { + setSortDirection(sortDirection === "asc" ? "desc" : "asc"); + } else { + setSortColumn(column); + setSortDirection("asc"); + } + }; + + const toggleOrderSelection = (orderId: string) => { + setSelectedOrders((prev) => { + const updated = new Set(prev); + if (updated.has(orderId)) { + updated.delete(orderId); + } else { + updated.add(orderId); + } + return updated; + }); + }; + + // Handle Bulk Actions + const markAsShipped = async () => { + try { + if (selectedOrders.size === 0) { + toast.warning("No orders selected."); + return; + } + + const orderIds = Array.from(selectedOrders); + const response = await fetchWithAuthClient( + "/orders/mark-shipped", + "POST", + { orderIds } + ); + + if (response.success) { + setOrders((prevOrders) => + prevOrders.map((order) => + selectedOrders.has(order._id) + ? { ...order, status: "shipped" } + : order + ) + ); + setSelectedOrders(new Set()); + toast.success("Orders marked as shipped successfully."); + } else { + throw new Error("Failed to update orders"); + } + } catch (error) { + toast.error("Failed to update orders."); + console.error("Error marking orders as shipped:", error); + } + }; return (
- {/* ✅ Filtering UI */} + {/* Filter and Bulk Action */}
-

- Orders -

+
+ {/* Orders Table */} - Order ID{" "} - {/* Center-aligned */} - Total (£) - Status + + 0 + } + onCheckedChange={(checked) => + setSelectedOrders( + checked + ? new Set(paginatedOrders.map((o) => o._id)) + : new Set() + ) + } + /> + + handleSort("orderId")} + > + Order ID + + handleSort("totalPrice")} + > + Total (£) + + handleSort("status")} + > + Status + + handleSort("createdAt")} + > + Date + Buyer Action @@ -122,32 +236,63 @@ export default function OrderTable() { {loading ? ( - + - ) : orders && orders.length > 0 ? ( - orders.map((order) => ( - - - #{order.orderId} + ) : paginatedOrders.length ? ( + paginatedOrders.map((order) => ( + + + toggleOrderSelection(order._id)} + /> + #{order.orderId} £{order.totalPrice.toFixed(2)} - {order.status === "paid" ? ( -
+ {order.status === "paid" && ( + Paid -
- ) : ( -
- {order.status} -
+ )} + {order.status === "unpaid" && ( + + Unpaid + + )} + {order.status === "confirming" && ( + + {" "} + Confirming + + )} + {order.status === "shipped" && ( + + Shipped + + )} + {order.status === "completed" && ( + + Completed + + )} + {order.status === "disputed" && ( + + Disputed + + )} + {order.status === "cancelled" && ( + + Cancelled + + )} +
+ + {new Date(order.createdAt).toLocaleDateString()} @{order.telegramUsername} @@ -163,7 +308,7 @@ export default function OrderTable() { )) ) : ( - + No orders found. @@ -171,6 +316,7 @@ export default function OrderTable() {
+ {/* Pagination */}
- + Page {currentPage} of {totalPages}