Files
vaishnavi-ecommerce-website/src/components/order/MyOrders.jsx
2026-03-10 14:47:34 +05:30

458 lines
15 KiB
JavaScript

// import React, { useState } from "react";
// import { useGetOrdersQuery } from "../../features/orders/ordersApi";
// const statusColor = {
// PENDING: "bg-yellow-100 text-yellow-800",
// PAID: "bg-blue-100 text-blue-800",
// SHIPPED: "bg-purple-100 text-purple-800",
// DELIVERED: "bg-green-100 text-green-800",
// CANCELLED: "bg-red-100 text-red-800",
// RETURNED: "bg-gray-100 text-gray-800",
// };
// // LEFT FILTERS
// const OrderFilters = ({ filters, setFilters }) => {
// const toggleStatus = (status) => {
// if (filters.status.includes(status)) {
// setFilters({
// ...filters,
// status: filters.status.filter((s) => s !== status),
// });
// } else {
// setFilters({ ...filters, status: [...filters.status, status] });
// }
// };
// return (
// <div className="sticky top-24 p-6 border rounded-3xl bg-white shadow-lg space-y-6">
// <h2 className="text-xl font-semibold text-gray-800">Filter Orders</h2>
// <div>
// <p className="font-medium text-gray-700 mb-2">Status</p>
// <div className="flex flex-col gap-2">
// {["PENDING", "ON_THE_WAY", "DELIVERED", "CANCELLED", "RETURNED"].map(
// (status) => (
// <label
// key={status}
// className="flex items-center gap-3 cursor-pointer hover:text-blue-600"
// >
// <input
// type="checkbox"
// checked={filters.status.includes(status)}
// onChange={() => toggleStatus(status)}
// className="h-4 w-4 accent-blue-500"
// />
// <span className="capitalize">{status.replace("_", " ")}</span>
// </label>
// )
// )}
// </div>
// </div>
// <div>
// <p className="font-medium text-gray-700 mb-2">Order Time</p>
// <select
// value={filters.time}
// onChange={(e) => setFilters({ ...filters, time: e.target.value })}
// className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none"
// >
// <option value="30_DAYS">Last 30 Days</option>
// <option value="2024">2024</option>
// <option value="2023">2023</option>
// </select>
// </div>
// </div>
// );
// };
// // ORDER CARD
// const OrderCard = ({ order }) => {
// return (
// <div className="bg-white border border-gray-200 rounded-3xl shadow hover:shadow-xl transition p-6 space-y-4">
// {/* Header */}
// <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-3">
// <div>
// <p className="text-sm text-gray-400">Order ID</p>
// <p className="font-semibold text-gray-800">{order.orderNumber}</p>
// <p className="text-xs text-gray-400">
// {new Date(order.createdAt).toLocaleString()}
// </p>
// </div>
// <div className="flex items-center gap-4 flex-wrap">
// <span
// className={`px-4 py-1 rounded-full text-sm font-medium ${
// statusColor[order.status] || "bg-gray-100 text-gray-800"
// }`}
// >
// {order.status.replace("_", " ")}
// </span>
// <span className="font-bold text-lg text-gray-900">
// ₹{Number(order.totalAmount).toFixed(2)}
// </span>
// </div>
// </div>
// {/* Items */}
// <div className="divide-y divide-gray-200">
// {order.items.map((item) => (
// <div
// key={item.id}
// className="flex items-center justify-between py-3"
// >
// <div>
// <p className="font-medium text-gray-800">{item.productName}</p>
// <p className="text-sm text-gray-500">Qty: {item.quantity}</p>
// </div>
// <p className="font-semibold text-gray-900">
// ₹{Number(item.price).toFixed(2)}
// </p>
// </div>
// ))}
// </div>
// {/* Footer */}
// <div className="grid md:grid-cols-2 gap-4 mt-4 text-sm text-gray-700">
// <div>
// <p className="font-medium text-gray-800">Shipping Address</p>
// <p className="mt-1">
// {order.address.firstName} {order.address.lastName},{" "}
// {order.address.addressLine1}, {order.address.city},{" "}
// {order.address.state} - {order.address.postalCode}
// </p>
// </div>
// <div className="md:text-right">
// <p>
// <span className="font-medium">Payment:</span>{" "}
// {order.paymentMethod}
// </p>
// <p>
// <span className="font-medium">Payment Status:</span>{" "}
// {order.paymentStatus}
// </p>
// </div>
// </div>
// </div>
// );
// };
// // MAIN COMPONENT
// const MyOrders = () => {
// const { data, isLoading, isError } = useGetOrdersQuery({
// page: 1,
// limit: 50,
// });
// const [filters, setFilters] = useState({
// status: [],
// time: "30_DAYS",
// });
// const orders = data?.orders || [];
// const filteredOrders = orders.filter((order) => {
// if (filters.status.length && !filters.status.includes(order.status)) return false;
// const orderDate = new Date(order.createdAt);
// const now = new Date();
// if (filters.time === "30_DAYS" && (now - orderDate) / (1000 * 60 * 60 * 24) > 30)
// return false;
// if (filters.time === "2024" && orderDate.getFullYear() !== 2024) return false;
// if (filters.time === "2023" && orderDate.getFullYear() !== 2023) return false;
// return true;
// });
// if (isLoading)
// return (
// <div className="min-h-[60vh] flex items-center justify-center text-lg">
// Loading orders...
// </div>
// );
// if (isError || !orders.length)
// return (
// <div className="min-h-[60vh] flex items-center justify-center text-gray-500">
// No orders found
// </div>
// );
// return (
// <div className="max-w-7xl mx-auto px-4 py-12 mt-24">
// <h1 className="text-4xl font-bold mb-10 text-gray-800">My Orders</h1>
// <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
// {/* Filters */}
// <OrderFilters filters={filters} setFilters={setFilters} />
// {/* Orders */}
// <div className="md:col-span-3 flex flex-col gap-6 max-h-[75vh] overflow-y-auto">
// {filteredOrders.length === 0 ? (
// <div className="text-gray-500 text-center py-20">
// No orders match your filters
// </div>
// ) : (
// filteredOrders.map((order) => <OrderCard key={order.id} order={order} />)
// )}
// </div>
// </div>
// </div>
// );
// };
// export default MyOrders;
import React, { useState } from "react";
import { useGetOrdersQuery } from "../../features/orders/ordersApi";
import { useNavigate } from "react-router-dom";
import { statusColor } from "../../constants/statusColor";
// const statusColor = {
// PENDING: "bg-yellow-100 text-yellow-800",
// PAID: "bg-blue-100 text-blue-800",
// SHIPPED: "bg-purple-100 text-purple-800",
// DELIVERED: "bg-green-100 text-green-800",
// CANCELLED: "bg-red-100 text-red-800",
// RETURNED: "bg-gray-100 text-gray-800",
// };
// LEFT FILTERS
const OrderFilters = ({ filters, setFilters }) => {
const toggleStatus = (status) => {
if (filters.status.includes(status)) {
setFilters({
...filters,
status: filters.status.filter((s) => s !== status),
});
} else {
setFilters({ ...filters, status: [...filters.status, status] });
}
};
return (
<div className="sticky top-24 p-6 border rounded-3xl bg-white shadow-lg space-y-6">
<h2 className="text-xl font-semibold text-gray-800">Filter Orders</h2>
<div>
<p className="font-medium text-gray-700 mb-2">Status</p>
<div className="flex flex-col gap-2">
{["PENDING", "ON_THE_WAY", "DELIVERED", "CANCELLED", "RETURNED"].map(
(status) => (
<label
key={status}
className="flex items-center gap-3 cursor-pointer hover:text-blue-600"
>
<input
type="checkbox"
checked={filters.status.includes(status)}
onChange={() => toggleStatus(status)}
className="h-4 w-4 accent-blue-500"
/>
<span className="capitalize">{status.replace("_", " ")}</span>
</label>
)
)}
</div>
</div>
<div>
<p className="font-medium text-gray-700 mb-2">Order Time</p>
<select
value={filters.time}
onChange={(e) => setFilters({ ...filters, time: e.target.value })}
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-400 focus:outline-none"
>
<option value="30_DAYS">Last 30 Days</option>
<option value="2024">2024</option>
<option value="2023">2023</option>
</select>
</div>
</div>
);
};
// ORDER CARD
const OrderCard = ({ order }) => {
const navigate = useNavigate();
return (
<div
className="bg-white border border-gray-200 rounded-3xl shadow hover:shadow-xl transition p-6 space-y-4 cursor-pointer"
onClick={() => navigate(`/my-orders/${order.id}`)}
>
{/* Header */}
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-3">
<div>
<p className="text-sm text-gray-400">Order ID</p>
<p className="font-semibold text-gray-800">{order.orderNumber}</p>
<p className="text-xs text-gray-400">
{new Date(order.createdAt).toLocaleString()}
</p>
</div>
<div className="flex items-center gap-4 flex-wrap">
<span
className={`px-4 py-1 rounded-full text-sm font-medium ${
statusColor[order.status] || "bg-gray-100 text-gray-800"
}`}
>
{order.status.replace("_", " ")}
</span>
<span className="font-bold text-lg text-gray-900">
{Number(order.totalAmount).toFixed(2)}
</span>
</div>
</div>
{/* Items */}
<div className="divide-y divide-gray-200">
{order.items.map((item) => (
<div key={item.id} className="flex items-center justify-between py-3">
<div>
<p className="font-medium text-gray-800">{item.productName}</p>
<p className="text-sm text-gray-500">Qty: {item.quantity}</p>
</div>
<p className="font-semibold text-gray-900">
{Number(item.price).toFixed(2)}
</p>
</div>
))}
</div>
{/* Footer */}
<div className="grid md:grid-cols-2 gap-4 mt-4 text-sm text-gray-700">
<div>
<p className="font-medium text-gray-800">Shipping Address</p>
<p className="mt-1">
{order.address.firstName} {order.address.lastName},{" "}
{order.address.addressLine1}, {order.address.city},{" "}
{order.address.state} - {order.address.postalCode}
</p>
</div>
<div className="md:text-right">
<p>
<span className="font-medium">Payment:</span> {order.paymentMethod}
</p>
<p>
<span className="font-medium">Payment Status:</span>{" "}
{order.paymentStatus}
</p>
</div>
</div>
</div>
);
};
// MAIN COMPONENT
const MyOrders = () => {
const { data, isLoading, isError } = useGetOrdersQuery({
page: 1,
limit: 50,
});
const [filters, setFilters] = useState({
status: [],
time: "30_DAYS",
});
const [searchQuery, setSearchQuery] = useState("");
const orders = data?.orders || [];
const filteredOrders = orders
.filter((order) => {
if (filters.status.length && !filters.status.includes(order.status))
return false;
const orderDate = new Date(order.createdAt);
const now = new Date();
if (
filters.time === "30_DAYS" &&
(now - orderDate) / (1000 * 60 * 60 * 24) > 30
)
return false;
if (filters.time === "2024" && orderDate.getFullYear() !== 2024)
return false;
if (filters.time === "2023" && orderDate.getFullYear() !== 2023)
return false;
return true;
})
.filter((order) => {
// SEARCH FILTER
if (!searchQuery) return true;
const q = searchQuery.toLowerCase();
const inOrderId = order.orderNumber.toLowerCase().includes(q);
const inCustomer = `${order.address.firstName} ${order.address.lastName}`
.toLowerCase()
.includes(q);
const inProduct = order.items.some((item) =>
item.productName.toLowerCase().includes(q)
);
return inOrderId || inCustomer || inProduct;
});
if (isLoading)
return (
<div className="min-h-[60vh] flex items-center justify-center text-lg">
Loading orders...
</div>
);
if (isError || !orders.length)
return (
<div className="min-h-[60vh] flex items-center justify-center text-gray-500">
No orders found
</div>
);
return (
<div className="max-w-7xl mx-auto px-4 py-12 mt-24">
<h1 className="text-2xl font-bold mb-6 text-primary-dark">My Orders</h1>
{/* SEARCH BAR */}
{/* SEARCH BAR */}
<div className="mb-8 flex justify-center">
<div className="w-full md:w-1/2 flex">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search by Order ID, Product, or Customer"
className="flex-1 border border-gray-300 rounded-l-xl px-4 py-3 shadow-sm focus:outline-none focus:ring-1 focus:ring-orange-700"
/>
<button
onClick={() => {}}
className="bg-primary-default text-white px-5 py-3 rounded-r-xl font-medium hover:bg-primary-dark transition"
>
Search
</button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{/* Filters */}
<OrderFilters filters={filters} setFilters={setFilters} />
{/* Orders */}
<div className="md:col-span-3 flex flex-col gap-6 max-h-[75vh] overflow-y-auto">
{filteredOrders.length === 0 ? (
<div className="text-gray-500 text-center py-20">
No orders match your filters
</div>
) : (
filteredOrders.map((order) => (
<OrderCard key={order.id} order={order} />
))
)}
</div>
</div>
</div>
);
};
export default MyOrders;