first commit
This commit is contained in:
457
src/components/order/MyOrders.jsx
Normal file
457
src/components/order/MyOrders.jsx
Normal file
@@ -0,0 +1,457 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user