Files
reui-blocks/blocks/wishlist2.tsx
T

275 lines
9.1 KiB
TypeScript

"use client";
import {
ArrowUpDown,
Check,
Heart,
Share2,
ShoppingCart,
X,
} 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 {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
interface WishlistItem {
id: string;
name: string;
image: string;
price: number;
originalPrice?: number;
inStock: boolean;
dateAdded: string;
category: string;
}
const DEFAULT_ITEMS: WishlistItem[] = [
{
id: "1",
name: "هدفون بی‌سیم نویزکنسل",
image:
"/block-media/shadcnblocks/block/ecommerce/electronics/Modern-White-Headphones-1.png",
price: 50000,
originalPrice: 60000,
inStock: true,
dateAdded: "2024-12-10",
category: "لوازم الکترونیکی",
},
{
id: "2",
name: "کیف چرمی کراس‌بادی",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/maroon-leather-handbag.png",
price: 180000,
inStock: true,
dateAdded: "2024-12-08",
category: "اکسسوری",
},
{
id: "3",
name: "پالتو پشمی کلاسیک",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/Young-Man-in-White-Hoodie-1.png",
price: 1200000,
inStock: false,
dateAdded: "2024-12-05",
category: "پوشاک",
},
{
id: "4",
name: "گوشواره حلقه‌ای طلا",
image:
"/block-media/shadcnblocks/block/ecommerce/accessories/Elegant-Gold-Earrings-1.png",
price: 8000000,
originalPrice: 9000000,
inStock: true,
dateAdded: "2024-12-02",
category: "زیورآلات",
},
{
id: "5",
name: "هودی پنبه‌ای اوورسایز",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/black-hoodie-against-light-background.png",
price: 6500000,
inStock: true,
dateAdded: "2024-11-28",
category: "پوشاک",
},
];
type SortOption = "date" | "price-low" | "price-high" | "name";
interface Wishlist2Props {
items?: WishlistItem[];
className?: string;
}
const Wishlist2 = ({ items = DEFAULT_ITEMS, className }: Wishlist2Props) => {
const [wishlistItems, setWishlistItems] = useState(items);
const [sortBy, setSortBy] = useState<SortOption>("date");
const removeItem = (id: string) => {
setWishlistItems((prev) => prev.filter((item) => item.id !== id));
};
const formatPrice = (price: number) => {
return new Intl.NumberFormat("fa-IR", {
style: "currency",
currency: "IRR",
}).format(price);
};
const formatDate = (isoDate: string) => {
return new Intl.DateTimeFormat("fa-IR", { dateStyle: "medium" }).format(
new Date(isoDate),
);
};
const sortedItems = [...wishlistItems].sort((a, b) => {
switch (sortBy) {
case "price-low":
return a.price - b.price;
case "price-high":
return b.price - a.price;
case "name":
return a.name.localeCompare(b.name, "fa");
default:
return (
new Date(b.dateAdded).getTime() - new Date(a.dateAdded).getTime()
);
}
});
const sortLabels: Record<SortOption, string> = {
date: "تاریخ افزودن",
"price-low": "قیمت: کم به زیاد",
"price-high": "قیمت: زیاد به کم",
name: "نام",
};
return (
<section
className={cn(
"-mx-4 w-[calc(100%+2rem)] max-w-none py-16 md:py-24",
className,
)}
>
<div className="w-full max-w-none px-0">
{/* Header */}
<div className="mb-6 flex w-full flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-xl font-semibold tracking-tight md:text-3xl">
موارد ذخیره‌شده
</h1>
<p className="mt-1 text-muted-foreground">
{wishlistItems.length.toLocaleString("fa-IR")} مورد در لیست علاقه‌مندی
</p>
</div>
<div className="flex flex-wrap gap-2 sm:justify-end">
<Button variant="outline" size="sm">
<Share2 className="me-2 size-4" />
اشتراک‌گذاری لیست
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<ArrowUpDown className="me-2 size-4" />
{sortLabels[sortBy]}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{(Object.keys(sortLabels) as SortOption[]).map((option) => (
<DropdownMenuItem
key={option}
onClick={() => setSortBy(option)}
>
{sortBy === option && <Check className="me-2 size-4" />}
<span className={sortBy !== option ? "ps-6" : ""}>
{sortLabels[option]}
</span>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
{/* List */}
{sortedItems.length > 0 ? (
<div className="box-border min-w-full w-full rounded-lg border">
{sortedItems.map((item, index) => (
<div key={item.id}>
{index > 0 && <Separator />}
<div className="flex w-full gap-4 p-4">
<div className="size-24 shrink-0 overflow-hidden rounded-lg bg-muted sm:size-28">
<AspectRatio ratio={1}>
<img
src={item.image}
alt={item.name}
className={cn(
"size-full object-cover",
!item.inStock && "opacity-50",
)}
/>
</AspectRatio>
</div>
<div className="flex min-w-0 flex-1 flex-col">
<div className="flex w-full items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<h3 className="leading-tight font-medium">{item.name}</h3>
{!item.inStock && (
<Badge variant="secondary" className="shrink-0">
ناموجود
</Badge>
)}
</div>
<p className="mt-0.5 text-sm text-muted-foreground">
{item.category}
</p>
</div>
<Button
variant="ghost"
size="icon"
className="shrink-0"
onClick={() => removeItem(item.id)}
aria-label={`حذف ${item.name}`}
>
<X className="size-4" />
</Button>
</div>
<div className="mt-2 flex flex-wrap items-center gap-2">
<span className="font-semibold">{formatPrice(item.price)}</span>
{item.originalPrice && (
<span className="text-sm text-muted-foreground line-through">
{formatPrice(item.originalPrice)}
</span>
)}
</div>
<div className="mt-auto flex w-full flex-wrap items-center justify-between gap-3 pt-3">
<span className="text-xs text-muted-foreground">
افزوده‌شده در {formatDate(item.dateAdded)}
</span>
<Button size="sm" disabled={!item.inStock} className="shrink-0">
<ShoppingCart className="me-2 size-4" />
{item.inStock ? "افزودن به سبد" : "ناموجود"}
</Button>
</div>
</div>
</div>
</div>
))}
</div>
) : (
<div className="flex w-full flex-col items-center justify-center rounded-lg border py-16 text-center">
<div className="mb-4 flex size-16 items-center justify-center rounded-full bg-muted">
<Heart className="size-8 text-muted-foreground" />
</div>
<h2 className="text-xl font-semibold">موردی ذخیره نشده است</h2>
<p className="mt-2 w-full max-w-none text-muted-foreground">
مجموعه را مرور کنید و محصولات مورد علاقه را ذخیره کنید
</p>
<Button className="mt-6">مشاهده محصولات</Button>
</div>
)}
</div>
</section>
);
};
export { Wishlist2 };