Full website
This commit is contained in:
1 parent
807c43ece5
commit
ea9f33f60e
40 files changed
+4503
-1202
No files matched your search
@@ -1,63 +1,71 @@
|
||||
import Link from 'next/link'
|
||||
import type { Product } from '@homaroma/types'
|
||||
import { getCategoryImage, formatPrice } from '@homaroma/types'
|
||||
import { resolveLocalized } from '../lib/localization'
|
||||
import WishlistButton from './WishlistButton'
|
||||
|
||||
interface ProductCardProps {
|
||||
product: Product
|
||||
}
|
||||
|
||||
export default function ProductCard({ product }: ProductCardProps) {
|
||||
const categoryName = resolveLocalized(product.category.name)
|
||||
const productName = resolveLocalized(product.name) || product.scent.name
|
||||
const imageUrl = product.images?.[0]?.url || product.category.imageUrl || getCategoryImage(categoryName)
|
||||
const firstPrice = product.variants[0]?.price ?? product.price
|
||||
|
||||
return (
|
||||
<div className="glass glass-hover rounded-2xl overflow-hidden flex flex-col group p-1.5 md:p-2">
|
||||
<div className="h-40 md:h-64 overflow-hidden rounded-xl relative">
|
||||
<img
|
||||
src={product.images?.[0]?.url || product.category.imageUrl || getCategoryImage(typeof product.category.name === 'string' ? product.category.name : (product.category.name as any)?.th || (product.category.name as any)?.en || '')}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
||||
alt={product.scent.name}
|
||||
/>
|
||||
{/* Category badge */}
|
||||
<div className="absolute top-4 left-4 glass px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest text-cyan-glow">
|
||||
{typeof product.category.name === 'string' ? product.category.name : (product.category.name as any)?.th || (product.category.name as any)?.en || ''}
|
||||
</div>
|
||||
{/* Available badge */}
|
||||
<div className="hidden md:block absolute top-4 right-4 glass px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest">
|
||||
Available
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 md:p-6 space-y-2 md:space-y-4 flex flex-col flex-1">
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-start gap-1">
|
||||
<div>
|
||||
<h3 className="text-sm md:text-xl font-bold leading-tight line-clamp-1">
|
||||
{product.scent.name}
|
||||
</h3>
|
||||
<p className="text-[10px] md:text-xs text-slate-400 mt-0.5 md:mt-1 uppercase tracking-widest line-clamp-1">
|
||||
{typeof product.category.name === 'string' ? product.category.name : (product.category.name as any)?.th || (product.category.name as any)?.en || ''}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-cyan-glow font-bold text-xs md:text-lg">
|
||||
{formatPrice(product.variants[0]?.price)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Variants (sizes) */}
|
||||
<div className="hidden md:flex flex-wrap gap-1.5 md:gap-2">
|
||||
{product.variants.map((v) => (
|
||||
<div key={v.id} className="glass px-1.5 py-0.5 md:px-2.5 md:py-1 rounded-lg text-[8px] md:text-[10px] uppercase tracking-wider">
|
||||
{v.size}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<Link
|
||||
href={`/products/${product.id}`}
|
||||
className="block w-full py-2 md:py-3 text-center bg-white/10 hover:bg-white text-white hover:text-night-900 rounded-lg font-bold uppercase tracking-widest text-[10px] md:text-xs transition-all duration-300"
|
||||
>
|
||||
Discover
|
||||
<article className="group flex h-full flex-col overflow-hidden rounded-2xl bg-white shadow-brutal transition duration-300 hover:-translate-y-1 hover:shadow-brutal-hover">
|
||||
<div className="relative aspect-[4/3] overflow-hidden bg-pop-bg">
|
||||
<WishlistButton product={product} />
|
||||
<Link href={`/products/${product.id}`} className="block h-full w-full">
|
||||
<img
|
||||
src={imageUrl}
|
||||
className="h-full w-full object-cover transition duration-700 group-hover:scale-105"
|
||||
alt={productName}
|
||||
/>
|
||||
{categoryName && (
|
||||
<span className="absolute left-4 top-4 rounded-full bg-white/90 px-3 py-1 text-[10px] font-bold uppercase tracking-widest text-pop-black shadow-sm">
|
||||
{categoryName}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-col gap-4 p-5">
|
||||
<div className="space-y-1">
|
||||
<h3 className="line-clamp-2 text-lg font-semibold leading-snug text-pop-black">
|
||||
<Link href={`/products/${product.id}`} className="transition hover:text-pop-pink">
|
||||
{productName}
|
||||
</Link>
|
||||
</h3>
|
||||
{product.scent.name && (
|
||||
<p className="text-xs font-semibold uppercase tracking-widest text-gray-400">
|
||||
{product.scent.name}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{product.variants.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{product.variants.slice(0, 3).map((variant) => (
|
||||
<span key={variant.id} className="rounded-full bg-pop-bg px-3 py-1 text-xs font-semibold text-gray-600">
|
||||
{variant.size}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-auto flex items-center justify-between gap-4">
|
||||
<span className="text-lg font-bold text-pop-pink">{formatPrice(firstPrice)}</span>
|
||||
<Link
|
||||
href={`/products/${product.id}`}
|
||||
className="rounded-full bg-pop-black px-4 py-2 text-xs font-bold uppercase tracking-widest text-white transition hover:bg-pop-pink"
|
||||
>
|
||||
View
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user