275 lines
9.1 KiB
TypeScript
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 };
|