Seed: ecommerce template (24 files)
This commit is contained in:
+16
-16
@@ -80,7 +80,7 @@ export default function ProductsPage() {
|
|||||||
|
|
||||||
const renderStars = (count: number) =>
|
const renderStars = (count: number) =>
|
||||||
Array.from({ length: 5 }, (_, i) => (
|
Array.from({ length: 5 }, (_, i) => (
|
||||||
<Star key={i} className={`h-3 w-3 \${i < count ? "fill-amber-400 text-amber-400" : "text-stone-300"}`} />
|
<Star key={i} className={`h-3 w-3 ${i < count ? "fill-amber-400 text-amber-400" : "text-stone-300"}`} />
|
||||||
));
|
));
|
||||||
|
|
||||||
const Sidebar = () => (
|
const Sidebar = () => (
|
||||||
@@ -113,7 +113,7 @@ export default function ProductsPage() {
|
|||||||
<button
|
<button
|
||||||
key={c.value}
|
key={c.value}
|
||||||
onClick={() => toggleColor(c.value)}
|
onClick={() => toggleColor(c.value)}
|
||||||
className={`h-7 w-7 rounded-full \${c.class} \${selectedColors.includes(c.value) ? "ring-2 ring-offset-2 ring-rose-500" : ""} transition-all`}
|
className={`h-7 w-7 rounded-full ${c.class} ${selectedColors.includes(c.value) ? "ring-2 ring-offset-2 ring-rose-500" : ""} transition-all`}
|
||||||
title={c.name}
|
title={c.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -174,7 +174,7 @@ export default function ProductsPage() {
|
|||||||
|
|
||||||
<div className="flex gap-8">
|
<div className="flex gap-8">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className={`w-56 shrink-0 \${showFilters ? "block" : "hidden"} lg:block`}>
|
<aside className={`w-56 shrink-0 ${showFilters ? "block" : "hidden"} lg:block`}>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@@ -186,21 +186,21 @@ export default function ProductsPage() {
|
|||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button className="group text-left w-full">
|
<button className="group text-left w-full">
|
||||||
<Card className="overflow-hidden border-0 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300">
|
<Card className="overflow-hidden border-0 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300">
|
||||||
<div className={`relative aspect-[3/4] bg-gradient-to-br \${product.gradient} overflow-hidden`}>
|
<div className={`relative aspect-[3/4] bg-gradient-to-br ${product.gradient} overflow-hidden`}>
|
||||||
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-110 group-hover:brightness-110" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
|
<div className="absolute inset-0 transition-transform duration-300 group-hover:scale-110 group-hover:brightness-110" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
|
||||||
<div className="absolute top-1/4 -left-8 h-24 w-24 rounded-full bg-white/30 blur-2xl" />
|
<div className="absolute top-1/4 -left-8 h-24 w-24 rounded-full bg-white/30 blur-2xl" />
|
||||||
<div className="absolute bottom-1/4 -right-8 h-20 w-20 rounded-full bg-white/20 blur-2xl" />
|
<div className="absolute bottom-1/4 -right-8 h-20 w-20 rounded-full bg-white/20 blur-2xl" />
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-stone-400 text-xs transition-transform duration-300 group-hover:scale-110 group-hover:brightness-110">Image</div>
|
<div className="absolute inset-0 flex items-center justify-center text-stone-400 text-xs transition-transform duration-300 group-hover:scale-110 group-hover:brightness-110">Image</div>
|
||||||
{product.badge && (
|
{product.badge && (
|
||||||
<Badge className={`absolute top-2 left-2 border-0 text-white text-xs \${product.badge === "Sale" ? "bg-rose-500" : "bg-stone-900"}`}>
|
<Badge className={`absolute top-2 left-2 border-0 text-white text-xs ${product.badge === "Sale" ? "bg-rose-500" : "bg-stone-900"}`}>
|
||||||
{product.badge}
|
{product.badge}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<span
|
<span
|
||||||
onClick={(e) => { e.stopPropagation(); toggleWishlist(product.id); }}
|
onClick={(e) => { e.stopPropagation(); toggleWishlist(product.id); }}
|
||||||
className={`absolute top-2 right-2 h-7 w-7 rounded-full bg-white/80 backdrop-blur-sm flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all hover:scale-125 active:scale-90 \${wishlist.includes(product.id) ? "!opacity-100" : ""}`}
|
className={`absolute top-2 right-2 h-7 w-7 rounded-full bg-white/80 backdrop-blur-sm flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all hover:scale-125 active:scale-90 ${wishlist.includes(product.id) ? "!opacity-100" : ""}`}
|
||||||
>
|
>
|
||||||
<Heart className={`h-3.5 w-3.5 \${wishlist.includes(product.id) ? "fill-rose-500 text-rose-500" : "text-stone-600"}`} />
|
<Heart className={`h-3.5 w-3.5 ${wishlist.includes(product.id) ? "fill-rose-500 text-rose-500" : "text-stone-600"}`} />
|
||||||
</span>
|
</span>
|
||||||
<div className="absolute bottom-0 left-0 right-0 translate-y-full group-hover:translate-y-0 transition-transform duration-300 p-3"><Button className="w-full" size="sm">Quick Add</Button></div>
|
<div className="absolute bottom-0 left-0 right-0 translate-y-full group-hover:translate-y-0 transition-transform duration-300 p-3"><Button className="w-full" size="sm">Quick Add</Button></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -209,9 +209,9 @@ export default function ProductsPage() {
|
|||||||
<div className="flex gap-1 mt-1 opacity-0 group-hover:opacity-100 transition-opacity"><span className="w-3 h-3 rounded-full bg-stone-900" /><span className="w-3 h-3 rounded-full bg-rose-400" /><span className="w-3 h-3 rounded-full bg-amber-200" /></div>
|
<div className="flex gap-1 mt-1 opacity-0 group-hover:opacity-100 transition-opacity"><span className="w-3 h-3 rounded-full bg-stone-900" /><span className="w-3 h-3 rounded-full bg-rose-400" /><span className="w-3 h-3 rounded-full bg-amber-200" /></div>
|
||||||
<div className="flex items-center gap-0.5 mt-1">{renderStars(product.rating)}</div>
|
<div className="flex items-center gap-0.5 mt-1">{renderStars(product.rating)}</div>
|
||||||
<div className="mt-1.5 flex items-center gap-2">
|
<div className="mt-1.5 flex items-center gap-2">
|
||||||
<span className="font-semibold text-sm">\${product.price}</span>
|
<span className="font-semibold text-sm">${product.price}</span>
|
||||||
{product.originalPrice && (
|
{product.originalPrice && (
|
||||||
<span className="text-xs text-stone-400 line-through">\${product.originalPrice}</span>
|
<span className="text-xs text-stone-400 line-through">${product.originalPrice}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 mt-1"><div className="flex">{[...Array(5)].map((_, j) => <Star key={j} className="h-3 w-3 fill-amber-400 text-amber-400" />)}</div><span className="text-xs text-muted-foreground">(128)</span></div>
|
<div className="flex items-center gap-1 mt-1"><div className="flex">{[...Array(5)].map((_, j) => <Star key={j} className="h-3 w-3 fill-amber-400 text-amber-400" />)}</div><span className="text-xs text-muted-foreground">(128)</span></div>
|
||||||
@@ -223,13 +223,13 @@ export default function ProductsPage() {
|
|||||||
<DialogContent className="max-w-2xl p-0 overflow-hidden backdrop-blur-xl bg-white/80 dark:bg-slate-900/80 border-white/20">
|
<DialogContent className="max-w-2xl p-0 overflow-hidden backdrop-blur-xl bg-white/80 dark:bg-slate-900/80 border-white/20">
|
||||||
<div className="grid md:grid-cols-2 gap-0">
|
<div className="grid md:grid-cols-2 gap-0">
|
||||||
{/* Product Image */}
|
{/* Product Image */}
|
||||||
<div className={`aspect-square bg-gradient-to-br \${product.gradient} relative`}>
|
<div className={`aspect-square bg-gradient-to-br ${product.gradient} relative`}>
|
||||||
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
|
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
|
||||||
<div className="absolute top-1/4 -left-8 h-32 w-32 rounded-full bg-white/30 blur-2xl" />
|
<div className="absolute top-1/4 -left-8 h-32 w-32 rounded-full bg-white/30 blur-2xl" />
|
||||||
<div className="absolute bottom-1/4 -right-8 h-28 w-28 rounded-full bg-white/20 blur-2xl" />
|
<div className="absolute bottom-1/4 -right-8 h-28 w-28 rounded-full bg-white/20 blur-2xl" />
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-stone-400 text-sm">Product Image</div>
|
<div className="absolute inset-0 flex items-center justify-center text-stone-400 text-sm">Product Image</div>
|
||||||
{product.badge && (
|
{product.badge && (
|
||||||
<Badge className={`absolute top-4 left-4 border-0 text-white \${product.badge === "Sale" ? "bg-rose-500" : "bg-stone-900"}`}>
|
<Badge className={`absolute top-4 left-4 border-0 text-white ${product.badge === "Sale" ? "bg-rose-500" : "bg-stone-900"}`}>
|
||||||
{product.badge}
|
{product.badge}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
@@ -240,9 +240,9 @@ export default function ProductsPage() {
|
|||||||
<h2 className="text-xl font-bold text-stone-900">{product.name}</h2>
|
<h2 className="text-xl font-bold text-stone-900">{product.name}</h2>
|
||||||
<div className="flex items-center gap-0.5 mt-2">{renderStars(product.rating)}</div>
|
<div className="flex items-center gap-0.5 mt-2">{renderStars(product.rating)}</div>
|
||||||
<div className="mt-3 flex items-center gap-3">
|
<div className="mt-3 flex items-center gap-3">
|
||||||
<span className="text-2xl font-bold">\${product.price}</span>
|
<span className="text-2xl font-bold">${product.price}</span>
|
||||||
{product.originalPrice && (
|
{product.originalPrice && (
|
||||||
<span className="text-base text-stone-400 line-through">\${product.originalPrice}</span>
|
<span className="text-base text-stone-400 line-through">${product.originalPrice}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-3 text-sm text-stone-500 leading-relaxed">Premium quality crafted with the finest materials. Designed for comfort and style that lasts.</p>
|
<p className="mt-3 text-sm text-stone-500 leading-relaxed">Premium quality crafted with the finest materials. Designed for comfort and style that lasts.</p>
|
||||||
@@ -254,7 +254,7 @@ export default function ProductsPage() {
|
|||||||
<button
|
<button
|
||||||
key={s}
|
key={s}
|
||||||
onClick={() => setSelectedSize(s)}
|
onClick={() => setSelectedSize(s)}
|
||||||
className={`h-9 min-w-[2.5rem] px-3 rounded-md border text-sm font-medium transition-all \${selectedSize === s ? "border-stone-900 bg-stone-900 text-white" : "border-stone-200 hover:border-stone-400"}`}
|
className={`h-9 min-w-[2.5rem] px-3 rounded-md border text-sm font-medium transition-all ${selectedSize === s ? "border-stone-900 bg-stone-900 text-white" : "border-stone-200 hover:border-stone-400"}`}
|
||||||
>
|
>
|
||||||
{s}
|
{s}
|
||||||
</button>
|
</button>
|
||||||
@@ -264,7 +264,7 @@ export default function ProductsPage() {
|
|||||||
<div className="mt-auto pt-4 space-y-2">
|
<div className="mt-auto pt-4 space-y-2">
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-stone-900 hover:bg-stone-800 text-white rounded-full active:scale-[0.98] transition-all"
|
className="w-full bg-stone-900 hover:bg-stone-800 text-white rounded-full active:scale-[0.98] transition-all"
|
||||||
onClick={() => { if (!selectedSize) { toast.error("Please select a size"); return; } toast.success(`Added \${product.name} (size \${selectedSize}) to cart`); setQuickView(null); setSelectedSize(""); }}
|
onClick={() => { if (!selectedSize) { toast.error("Please select a size"); return; } toast.success(`Added ${product.name} (size ${selectedSize}) to cart`); setQuickView(null); setSelectedSize(""); }}
|
||||||
>
|
>
|
||||||
<ShoppingBag className="mr-2 h-4 w-4" /> Add to Cart
|
<ShoppingBag className="mr-2 h-4 w-4" /> Add to Cart
|
||||||
</Button>
|
</Button>
|
||||||
@@ -285,7 +285,7 @@ export default function ProductsPage() {
|
|||||||
<ChevronLeft className="h-4 w-4" />
|
<ChevronLeft className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
{[1, 2, 3].map((p) => (
|
{[1, 2, 3].map((p) => (
|
||||||
<Button key={p} variant={page === p ? "default" : "outline"} size="icon" className={`h-9 w-9 \${page === p ? "bg-stone-900" : ""}`} onClick={() => setPage(p)}>
|
<Button key={p} variant={page === p ? "default" : "outline"} size="icon" className={`h-9 w-9 ${page === p ? "bg-stone-900" : ""}`} onClick={() => setPage(p)}>
|
||||||
{p}
|
{p}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user