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

473 lines
16 KiB
TypeScript
Raw Normal View History

import {
CheckCircle,
CreditCard,
Download,
MapPin,
Package,
Printer,
Truck,
} from "lucide-react";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
interface OrderItem {
id: string;
name: string;
image: string;
price: number;
quantity: number;
details?: { label: string; value: string }[];
}
interface ShippingAddress {
name: string;
street: string;
city: string;
state: string;
zipCode: string;
country: string;
}
interface PaymentMethod {
type: "card" | "paypal" | "bank";
lastFour?: string;
cardBrand?: string;
email?: string;
}
interface OrderSummaryData {
orderNumber: string;
/** ISO 8601 date (YYYY-MM-DD) */
orderDate: string;
status: "confirmed" | "processing" | "shipped" | "delivered";
email: string;
items: OrderItem[];
subtotal: number;
shipping: number;
tax: number;
discount?: number;
total: number;
shippingAddress: ShippingAddress;
shippingMethod: string;
/** ISO 8601 date range for estimated delivery */
estimatedDeliveryFrom: string;
estimatedDeliveryTo?: string;
paymentMethod: PaymentMethod;
}
const currencyFormatter = new Intl.NumberFormat("fa-IR", {
style: "currency",
currency: "IRR",
});
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 formatDeliveryEstimate = (from: string, to?: string) => {
if (to && to !== from) {
return `${formatOrderDate(from)} تا ${formatOrderDate(to)}`;
}
return formatOrderDate(from);
};
const DEFAULT_ORDER: OrderSummaryData = {
orderNumber: "ORD-2024-78432",
orderDate: "2024-12-14",
status: "confirmed",
email: "customer@example.com",
items: [
{
id: "1",
name: "کفش بژ مینیمال",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/Minimalist-Beige-Sneakers-2.png",
price: 120.0,
quantity: 1,
details: [
{ label: "سایز", value: "42" },
{ label: "رنگ", value: "بژ" },
],
},
{
id: "2",
name: "تاپ آبی گلدوزی",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/Woman-in-Embroidered-Blue-Top-2.png",
price: 140.0,
quantity: 2,
details: [
{ label: "سایز", value: "M" },
{ label: "رنگ", value: "آبی" },
],
},
{
id: "3",
name: "کلاه فدورا کلاسیک",
image:
"/block-media/shadcnblocks/block/ecommerce/accessories/Classic-Fedora-Hat-2.png",
price: 84.0,
quantity: 1,
details: [{ label: "سایز", value: "یک سایز" }],
},
],
subtotal: 484.0,
shipping: 12.0,
tax: 38.72,
discount: 50.0,
total: 484.72,
shippingAddress: {
name: "علی محمدی",
street: "خیابان ولیعصر، پلاک ۱۲۳۴، واحد ۵",
city: "تهران",
state: "تهران",
zipCode: "1415783114",
country: "ایران",
},
shippingMethod: "ارسال سریع",
estimatedDeliveryFrom: "2024-12-18",
estimatedDeliveryTo: "2024-12-20",
paymentMethod: {
type: "card",
lastFour: "4242",
cardBrand: "Visa",
},
};
interface OrderSummary1Props {
order?: OrderSummaryData;
className?: string;
}
const OrderSummary1 = ({
order = DEFAULT_ORDER,
className,
}: OrderSummary1Props) => {
const getStatusBadge = (status: OrderSummaryData["status"]) => {
const variants: Record<
OrderSummaryData["status"],
{ label: string; className: string }
> = {
confirmed: {
label: "سفارش تأیید شد",
className: "bg-emerald-500/10 text-emerald-600 hover:bg-emerald-500/10",
},
processing: {
label: "در حال پردازش",
className: "bg-amber-500/10 text-amber-600 hover:bg-amber-500/10",
},
shipped: {
label: "ارسال‌شده",
className: "bg-blue-500/10 text-blue-600 hover:bg-blue-500/10",
},
delivered: {
label: "تحویل‌شده",
className: "bg-emerald-500/10 text-emerald-600 hover:bg-emerald-500/10",
},
};
return variants[status];
};
const statusBadge = getStatusBadge(order.status);
const formatItemDetails = (details: OrderItem["details"]) => {
if (!details?.length) return null;
return details
.map((d) => `${d.label} ${d.value}`)
.join(" · ");
};
return (
<section
dir="rtl"
lang="fa"
className={cn("py-16 md:py-24", className)}
>
<div className="container max-w-4xl">
<div className="mb-10 text-center">
<div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-emerald-500/10">
<CheckCircle
className="size-8 text-emerald-600"
aria-hidden="true"
/>
</div>
<h1 className="mb-2 text-xl font-bold tracking-tight md:text-3xl">
از خرید شما سپاسگزاریم!
</h1>
<p className="text-muted-foreground">
ایمیل تأیید به{" "}
<span className="font-medium text-foreground" dir="rtl">
{order.email}
</span>{" "}
ارسال شد.
</p>
</div>
<Card className="mb-6 shadow-none">
<CardContent className="flex flex-wrap items-center justify-between gap-4 p-4 md:p-6">
<div className="flex flex-wrap items-center gap-x-6 gap-y-2">
<div>
<p className="text-sm text-muted-foreground">شماره سفارش</p>
<p className="font-semibold" dir="rtl">
{order.orderNumber}
</p>
</div>
<Separator
orientation="vertical"
className="hidden h-10 md:block"
/>
<div>
<p className="text-sm text-muted-foreground">تاریخ سفارش</p>
<p className="font-medium">
{formatOrderDate(order.orderDate)}
</p>
</div>
</div>
<Badge className={statusBadge.className}>{statusBadge.label}</Badge>
</CardContent>
</Card>
<div className="grid gap-6 lg:grid-cols-3">
<div className="space-y-6 lg:col-span-2">
<Card className="shadow-none">
<CardHeader className="pb-4">
<CardTitle className="flex items-center gap-2 text-lg">
<Package className="size-5" aria-hidden="true" />
اقلام سفارش
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{order.items.map((item, index) => (
<div key={item.id}>
<div className="flex gap-4">
<div className="w-20 shrink-0">
<AspectRatio
ratio={1}
className="overflow-hidden rounded-lg bg-muted"
>
<img
src={item.image}
alt={item.name}
className="size-full object-cover"
/>
</AspectRatio>
</div>
<div className="min-w-0 flex-1">
<h3 className="font-medium">{item.name}</h3>
{item.details && (
<p className="mt-0.5 text-sm text-muted-foreground">
{formatItemDetails(item.details)}
</p>
)}
<p className="mt-1 text-sm text-muted-foreground">
تعداد: {item.quantity.toLocaleString("fa-IR")}
</p>
</div>
<div className="text-end">
<p className="font-semibold tabular-nums">
{currencyFormatter.format(
item.price * item.quantity,
)}
</p>
{item.quantity > 1 && (
<p className="text-sm text-muted-foreground tabular-nums">
{currencyFormatter.format(item.price)} برای هر قلم
</p>
)}
</div>
</div>
{index < order.items.length - 1 && (
<Separator className="mt-4" />
)}
</div>
))}
</CardContent>
</Card>
<Card className="shadow-none">
<CardContent className="p-6">
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">جمع جزء</span>
<span className="tabular-nums">
{currencyFormatter.format(order.subtotal)}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">هزینه ارسال</span>
<span className="tabular-nums">
{order.shipping === 0
? "رایگان"
: currencyFormatter.format(order.shipping)}
</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">مالیات</span>
<span className="tabular-nums">
{currencyFormatter.format(order.tax)}
</span>
</div>
{order.discount && order.discount > 0 && (
<div className="flex justify-between text-sm text-emerald-600">
<span>تخفیف</span>
<span className="tabular-nums">
−{currencyFormatter.format(order.discount)}
</span>
</div>
)}
<Separator />
<div className="flex justify-between text-lg font-semibold">
<span>مبلغ پرداخت‌شده</span>
<span className="tabular-nums">
{currencyFormatter.format(order.total)}
</span>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="space-y-6">
<Card className="shadow-none">
<CardHeader className="pb-4">
<CardTitle className="flex items-center gap-2 text-lg">
<MapPin className="size-5" aria-hidden="true" />
آدرس ارسال
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="font-medium">{order.shippingAddress.name}</p>
<p className="text-sm text-muted-foreground">
{order.shippingAddress.street}
</p>
<p className="text-sm text-muted-foreground">
{order.shippingAddress.city}،{" "}
{order.shippingAddress.state} کد پستی{" "}
<span dir="rtl" className="tabular-nums">
{order.shippingAddress.zipCode}
</span>
</p>
<p className="text-sm text-muted-foreground">
{order.shippingAddress.country}
</p>
</div>
<Separator />
<div className="flex items-start gap-3">
<Truck
className="mt-0.5 size-4 text-muted-foreground"
aria-hidden="true"
/>
<div>
<p className="text-sm font-medium">
{order.shippingMethod}
</p>
<p className="text-sm text-muted-foreground">
تحویل تقریبی:{" "}
{formatDeliveryEstimate(
order.estimatedDeliveryFrom,
order.estimatedDeliveryTo,
)}
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="shadow-none">
<CardHeader className="pb-4">
<CardTitle className="flex items-center gap-2 text-lg">
<CreditCard className="size-5" aria-hidden="true" />
روش پرداخت
</CardTitle>
</CardHeader>
<CardContent>
{order.paymentMethod.type === "card" && (
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/logos/visa-icon.svg"
alt="ویزا"
className="size-8"
/>
<div>
<p className="text-sm font-medium">
{order.paymentMethod.cardBrand} پایان:{" "}
<span dir="rtl" className="tabular-nums">
{order.paymentMethod.lastFour}
</span>
</p>
<p className="text-sm text-muted-foreground">
پرداخت انجام شد
</p>
</div>
</div>
)}
{order.paymentMethod.type === "paypal" && (
<div className="flex items-center gap-3">
<div className="flex size-10 items-center justify-center rounded-md bg-muted">
<img
src="/block-media/shadcnblocks/block/logos/paypal-icon.svg"
alt="پی‌پال"
className="size-5"
/>
</div>
<div>
<p className="text-sm font-medium">پی‌پال</p>
<p className="text-sm text-muted-foreground" dir="rtl">
{order.paymentMethod.email}
</p>
</div>
</div>
)}
</CardContent>
</Card>
<Card className="shadow-none">
<CardContent className="space-y-3 p-4">
<Button className="w-full" variant="default">
<Package className="me-2 size-4" aria-hidden="true" />
پیگیری سفارش
</Button>
<Button className="w-full" variant="outline">
<Download className="me-2 size-4" aria-hidden="true" />
دانلود رسید
</Button>
<Button className="w-full" variant="ghost">
<Printer className="me-2 size-4" aria-hidden="true" />
چاپ سفارش
</Button>
</CardContent>
</Card>
</div>
</div>
<div className="mt-10 text-center">
<p className="mb-4 text-muted-foreground">
درباره سفارش خود سؤالی دارید؟
</p>
<div className="flex flex-wrap justify-center gap-3">
<Button variant="outline" asChild>
<a href="#">تماس با پشتیبانی</a>
</Button>
<Button asChild>
<a href="#">ادامه خرید</a>
</Button>
</div>
</div>
</div>
</section>
);
};
export { OrderSummary1 };