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

473 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 };