Files
reui-blocks/blocks/order-history3.tsx
T

356 lines
12 KiB
TypeScript

"use client";
import { CheckCircle, Clock, Search, Truck, XCircle } from "lucide-react";
import { useState } from "react";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
type OrderStatus = "pending" | "shipped" | "delivered" | "cancelled";
interface OrderItem {
name: string;
variant: string;
image: string;
price: number;
quantity: number;
status: OrderStatus;
/** ISO 8601 date (YYYY-MM-DD) */
statIRRate: string;
}
interface Order {
id: string;
orderNumber: string;
/** ISO 8601 date (YYYY-MM-DD) */
orderDate: string;
total: number;
items: OrderItem[];
}
const DEFAULT_ORDERS: Order[] = [
{
id: "1",
orderNumber: "47582",
orderDate: "2025-01-15",
total: 487000.0,
items: [
{
name: "صندلی راحتی مین‌سنتری",
variant: "زرد خردلی",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Leather-Sofa-1.png",
price: 289000.0,
quantity: 1,
status: "delivered",
statIRRate: "2025-01-22",
},
{
name: "کوسن طرح گل",
variant: "چاپ گل‌دار تیره",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Minimalist-Loveseat-1.png",
price: 490000.0,
quantity: 2,
status: "delivered",
statIRRate: "2025-01-22",
},
{
name: "گلدان گیاه آپارتمانی",
variant: "سبز متوسط",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Minimalist-Wooden-Sideboard-1.png",
price: 990000.0,
quantity: 1,
status: "delivered",
statIRRate: "2025-01-18",
},
],
},
{
id: "2",
orderNumber: "46321",
orderDate: "2025-01-10",
total: 329000.0,
items: [
{
name: "میز جلو مبلی چوبی",
variant: "بلوط طبیعی",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Wooden-Cabinet-1.png",
price: 249000.0,
quantity: 1,
status: "delivered",
statIRRate: "2025-01-13",
},
{
name: "ست تابلو دیواری بافته",
variant: "طرح هندسی",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Artistic-Lamp-1.png",
price: 800000.0,
quantity: 1,
status: "delivered",
statIRRate: "2025-01-13",
},
],
},
{
id: "3",
orderNumber: "45890",
orderDate: "2024-12-28",
total: 159000.0,
items: [
{
name: "چراغ رومیزی سرامیکی",
variant: "سفید مات",
image:
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Artistic-Lamp-2.png",
price: 159000.0,
quantity: 1,
status: "shipped",
statIRRate: "2025-01-05",
},
],
},
];
const formatCurrency = (value: number) =>
`${value.toLocaleString("fa-IR")} ریال`;
const dateFormatter = new Intl.DateTimeFormat("fa-IR", {
year: "numeric",
month: "long",
day: "numeric",
});
const formatOrderDate = (isoDate: string) =>
dateFormatter.format(new Date(isoDate + "T12:00:00"));
const statusConfig: Record<
OrderStatus,
{ label: string; className: string; icon: typeof CheckCircle }
> = {
pending: {
label: "در حال پردازش،",
className: "text-slate-600",
icon: Clock,
},
shipped: {
label: "ارسال‌شده در",
className: "text-blue-600",
icon: Truck,
},
delivered: {
label: "تحویل‌شده در",
className: "text-emerald-600",
icon: CheckCircle,
},
cancelled: {
label: "لغوشده در",
className: "text-red-600",
icon: XCircle,
},
};
interface OrderHistory3Props {
orders?: Order[];
className?: string;
}
const OrderHistory3 = ({
orders = DEFAULT_ORDERS,
className,
}: OrderHistory3Props) => {
const [searchQuery, setSearchQuery] = useState("");
const filteredOrders = orders.filter((order) => {
if (searchQuery === "") return true;
const query = searchQuery.toLowerCase();
return (
order.orderNumber.includes(query) ||
order.items.some((item) => item.name.toLowerCase().includes(query))
);
});
return (
<section
dir="rtl"
lang="fa"
className={cn("py-16 md:py-24", className)}
>
<div className="container max-w-4xl">
<div className="mb-8 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div>
<h1 className="text-xl font-bold tracking-tight md:text-3xl">
سفارش‌های من
</h1>
<p className="mt-1 text-muted-foreground">
مشاهده تاریخچه و جزئیات پیگیری سفارش‌ها
</p>
</div>
<div className="relative">
<Search
className="absolute top-1/2 start-3 size-4 -translate-y-1/2 text-muted-foreground"
aria-hidden="true"
/>
<Input
placeholder="جستجو…"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
name="order-history-search"
inputMode="search"
autoComplete="off"
aria-label="جستجوی سفارش‌ها و محصولات"
className="ps-9 sm:w-56"
/>
</div>
</div>
<div className="space-y-6">
{filteredOrders.map((order) => (
<Card
key={order.id}
className="gap-0 overflow-hidden p-0 shadow-none"
>
<CardHeader className="bg-muted/40 px-4 py-3 md:px-6 md:py-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="grid grid-cols-3 gap-4 sm:flex sm:gap-8">
<div>
<p className="text-xs text-muted-foreground">
شماره سفارش
</p>
<p className="font-semibold tabular-nums" dir="rtl">
{order.orderNumber}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">
تاریخ سفارش
</p>
<p className="font-medium">
{formatOrderDate(order.orderDate)}
</p>
</div>
<div>
<p className="text-xs text-muted-foreground">مبلغ کل</p>
<p className="font-semibold tabular-nums">
{formatCurrency(order.total)}
</p>
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" size="sm">
مشاهده فاکتور
</Button>
<Button variant="outline" size="sm">
بایگانی
</Button>
</div>
</div>
</CardHeader>
<CardContent className="p-0">
{order.items.map((item, index) => {
const status = statusConfig[item.status];
const StatusIcon = status.icon;
return (
<div key={index}>
{index > 0 && <Separator />}
<div className="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between md:p-6">
<div className="flex gap-4">
<div className="size-16 shrink-0 overflow-hidden rounded-lg bg-muted md:size-20">
<AspectRatio ratio={1}>
<img
src={item.image}
alt={item.name}
className="size-full object-cover"
/>
</AspectRatio>
</div>
<div>
<h3 className="font-medium">{item.name}</h3>
<p className="text-sm text-muted-foreground">
{item.variant}
{item.quantity > 1 && (
<>
{" "}
· تعداد:{" "}
{item.quantity.toLocaleString("fa-IR")}
</>
)}
</p>
<Button
variant="outline"
size="sm"
className="mt-2"
>
خرید مجدد
</Button>
</div>
</div>
<div className="flex items-center gap-2 sm:text-end">
<StatusIcon
className={cn(
"size-5 shrink-0",
item.status === "delivered" && "text-emerald-500",
item.status === "shipped" && "text-blue-500",
item.status === "pending" && "text-slate-400",
item.status === "cancelled" && "text-red-500",
)}
fill={
item.status === "delivered"
? "currentColor"
: "none"
}
strokeWidth={item.status === "delivered" ? 0 : 2}
aria-hidden="true"
/>
<p className="text-sm">
<span className="text-muted-foreground">
{status.label}{" "}
</span>
<span className="font-medium">
{formatOrderDate(item.statIRRate)}
</span>
</p>
</div>
</div>
</div>
);
})}
</CardContent>
</Card>
))}
{filteredOrders.length === 0 && (
<Card className="shadow-none">
<CardContent className="flex flex-col items-center justify-center py-16 text-center">
<div className="mb-4 flex size-16 items-center justify-center rounded-full bg-muted">
<Search
className="size-8 text-muted-foreground"
aria-hidden="true"
/>
</div>
<h2 className="mb-2 text-lg font-semibold">سفارشی یافت نشد</h2>
<p className="max-w-sm text-sm text-muted-foreground">
سفارشی با عبارت «{searchQuery}» یافت نشد.
</p>
</CardContent>
</Card>
)}
</div>
</div>
</section>
);
};
export { OrderHistory3 };