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

361 lines
12 KiB
TypeScript

"use client";
import {
ChevronRight,
Package,
RefreshCcw,
Search,
ShoppingBag,
Truck,
} from "lucide-react";
import { useState } from "react";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
type OrderStatus =
| "processing"
| "shipped"
| "delivered"
| "cancelled"
| "returned";
interface OrderHistoryItem {
id: string;
orderNumber: string;
/** ISO 8601 date (YYYY-MM-DD) */
orderDate: string;
status: OrderStatus;
total: number;
itemCount: number;
thumbnails: string[];
trackingNumber?: string;
}
const DEFAULT_ORDERS: OrderHistoryItem[] = [
{
id: "1",
orderNumber: "ORD-2024-78432",
orderDate: "2024-12-14",
status: "processing",
total: 289000.0,
itemCount: 2,
thumbnails: [
"/block-media/shadcnblocks/block/ecommerce/clothes/stylish-maroon-sneaker.png",
"/block-media/shadcnblocks/block/ecommerce/clothes/black-hoodie-against-light-background.png",
],
},
{
id: "2",
orderNumber: "ORD-2024-78401",
orderDate: "2024-12-10",
status: "shipped",
total: 549990.0,
itemCount: 3,
thumbnails: [
"/block-media/shadcnblocks/block/ecommerce/electronics/smart-watch-1.png",
"/block-media/shadcnblocks/block/ecommerce/electronics/Sleek-Modern-Laptop-1.png",
"/block-media/shadcnblocks/block/ecommerce/electronics/Sleek-Smartphone-Close-Up-1.png",
],
trackingNumber: "1Z999AA10123456784",
},
{
id: "3",
orderNumber: "ORD-2024-78355",
orderDate: "2024-11-28",
status: "delivered",
total: 1249000.0,
itemCount: 2,
thumbnails: [
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Leather-Sofa-1.png",
"/block-media/shadcnblocks/block/ecommerce/furniture/Modern-Artistic-Lamp-1.png",
],
},
{
id: "4",
orderNumber: "ORD-2024-78290",
orderDate: "2024-11-15",
status: "cancelled",
total: 185000.0,
itemCount: 3,
thumbnails: [
"/block-media/shadcnblocks/block/ecommerce/accessories/Stylish-Round-Sunglasses-1.png",
"/block-media/shadcnblocks/block/ecommerce/accessories/Sunflower-Pendant-Necklace-1.png",
"/block-media/shadcnblocks/block/ecommerce/accessories/Stylish-Pink-Eyeglasses-1.png",
],
},
];
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 formatItemCount = (count: number) =>
`${count.toLocaleString("fa-IR")} قلم`;
const statusConfig: Record<
OrderStatus,
{ label: string; className: string; icon: typeof Package }
> = {
processing: {
label: "در حال پردازش",
className: "bg-amber-500/10 text-amber-600 hover:bg-amber-500/10",
icon: Package,
},
shipped: {
label: "ارسال‌شده",
className: "bg-blue-500/10 text-blue-600 hover:bg-blue-500/10",
icon: Truck,
},
delivered: {
label: "تحویل‌شده",
className: "bg-emerald-500/10 text-emerald-600 hover:bg-emerald-500/10",
icon: Package,
},
cancelled: {
label: "لغوشده",
className: "bg-red-500/10 text-red-600 hover:bg-red-500/10",
icon: Package,
},
returned: {
label: "مرجوع‌شده",
className: "bg-slate-500/10 text-slate-600 hover:bg-slate-500/10",
icon: RefreshCcw,
},
};
const tabLabels: Record<string, string> = {
all: "همه",
active: "فعال",
delivered: "تحویل‌شده",
cancelled: "لغوشده",
};
interface OrderHistory1Props {
orders?: OrderHistoryItem[];
className?: string;
}
const OrderHistory1 = ({
orders = DEFAULT_ORDERS,
className,
}: OrderHistory1Props) => {
const [activeTab, setActiveTab] = useState("all");
const [searchQuery, setSearchQuery] = useState("");
const filteredOrders = orders.filter((order) => {
const matchesTab =
activeTab === "all" ||
(activeTab === "active" &&
(order.status === "processing" || order.status === "shipped")) ||
order.status === activeTab;
const matchesSearch =
searchQuery === "" ||
order.orderNumber.toLowerCase().includes(searchQuery.toLowerCase());
return matchesTab && matchesSearch;
});
return (
<section
dir="rtl"
lang="fa"
className={cn("py-16 md:py-24", className)}
>
<div className="container max-w-4xl">
<div className="mb-8">
<h1 className="text-xl font-bold tracking-tight md:text-3xl">
تاریخچه سفارش‌ها
</h1>
<p className="mt-1 text-muted-foreground">
مشاهده و مدیریت سفارش‌های گذشته خود
</p>
</div>
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
<TabsTrigger value="all">{tabLabels.all}</TabsTrigger>
<TabsTrigger value="active">{tabLabels.active}</TabsTrigger>
<TabsTrigger value="delivered">
{tabLabels.delivered}
</TabsTrigger>
<TabsTrigger value="cancelled">
{tabLabels.cancelled}
</TabsTrigger>
</TabsList>
</Tabs>
<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-64"
/>
</div>
</div>
{filteredOrders.length > 0 ? (
<div className="space-y-4">
{filteredOrders.map((order) => {
const status = statusConfig[order.status];
return (
<Card
key={order.id}
className="shadow-none transition-shadow hover:shadow-md"
>
<CardContent className="p-4 md:p-6">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex gap-4">
<div className="relative flex shrink-0">
{order.thumbnails.slice(0, 3).map((thumb, index) => (
<div
key={index}
className={cn(
"relative size-14 overflow-hidden rounded-lg border-2 border-background bg-muted",
index > 0 && "-ms-4",
)}
style={{ zIndex: 3 - index }}
>
<AspectRatio ratio={1}>
<img
src={thumb}
alt=""
className="size-full object-cover"
/>
</AspectRatio>
</div>
))}
{order.itemCount > 3 && (
<div
className="relative -ms-4 flex size-14 items-center justify-center rounded-lg border-2 border-background bg-muted text-sm font-medium text-muted-foreground"
style={{ zIndex: 0 }}
>
+
{(order.itemCount - 3).toLocaleString("fa-IR")}
</div>
)}
</div>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<p className="font-semibold" dir="rtl">
{order.orderNumber}
</p>
<Badge className={status.className}>
{status.label}
</Badge>
</div>
<p className="mt-1 text-sm text-muted-foreground">
{formatOrderDate(order.orderDate)} ·{" "}
{formatItemCount(order.itemCount)}
</p>
{order.trackingNumber && (
<p className="mt-1 text-xs text-muted-foreground">
رهگیری:{" "}
<span dir="rtl" className="tabular-nums">
{order.trackingNumber}
</span>
</p>
)}
</div>
</div>
<div className="flex items-center justify-between gap-4 sm:flex-col sm:items-end sm:justify-center">
<p className="text-lg font-bold tabular-nums">
{formatCurrency(order.total)}
</p>
<div className="flex gap-2">
{order.status === "shipped" && (
<Button variant="outline" size="sm">
<Truck
className="me-1.5 size-4"
aria-hidden="true"
/>
پیگیری مرسوله
</Button>
)}
{order.status === "delivered" && (
<Button variant="outline" size="sm">
<RefreshCcw
className="me-1.5 size-4"
aria-hidden="true"
/>
سفارش مجدد
</Button>
)}
<Button variant="ghost" size="sm">
مشاهده
<ChevronRight
className="ms-1 size-4 rtl:rotate-180"
aria-hidden="true"
/>
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
);
})}
</div>
) : (
<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">
<ShoppingBag
className="size-8 text-muted-foreground"
aria-hidden="true"
/>
</div>
<h2 className="mb-2 text-lg font-semibold">سفارشی یافت نشد</h2>
<p className="mb-6 max-w-sm text-sm text-muted-foreground">
{searchQuery
? `سفارشی با عبارت «${searchQuery}» یافت نشد.`
: "هنوز سفارشی ثبت نکرده‌اید. با شروع خرید، تاریخچه سفارش‌ها اینجا نمایش داده می‌شود."}
</p>
<Button>
<ShoppingBag className="me-2 size-4" aria-hidden="true" />
شروع خرید
</Button>
</CardContent>
</Card>
)}
{filteredOrders.length > 0 && (
<p className="mt-6 border-t pt-6 text-sm text-muted-foreground">
نمایش {filteredOrders.length.toLocaleString("fa-IR")} از{" "}
{orders.length.toLocaleString("fa-IR")} سفارش
</p>
)}
</div>
</section>
);
};
export { OrderHistory1 };