dstpabuaran.com/resources/js/components/home/ProductCard.tsx
Yoga Pangestu 4733df5827 Add homepage types and update routing to use HomepageController
- Created a new TypeScript file for homepage-related types including Product, ProductVariant, ProductCategory, and HomepageData.
- Updated the index.ts to export the new homepage types.
- Refactored the web routes to replace the inline closure for the home route with the HomepageController.
2026-08-07 17:29:59 +07:00

118 lines
4.6 KiB
TypeScript

import { formatRupiah } from '@/lib/rupiah';
import type { Product } from '@/types/homepage';
type ProductCardProps = {
product: Product;
onQuickView: (product: Product) => void;
};
function getProductImage(product: Product): string {
for (const variant of product.product_variants) {
const prices = variant.product_prices;
if (prices.length > 0) {
return `https://images.unsplash.com/photo-1490481651871-ab68de25d43d?w=800&auto=format&fit=crop&q=80`;
}
}
const fallbacks: Record<string, string[]> = {
'daster': [
'https://images.unsplash.com/photo-1595777457583-95e059d581b8?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1572804013309-59a88b7e92f1?w=800&auto=format&fit=crop&q=80',
],
'setelan-celana': [
'https://images.unsplash.com/photo-1539109136881-3be0616acf4b?w=800&auto=format&fit=crop&q=80',
],
'atasan': [
'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?w=800&auto=format&fit=crop&q=80',
],
'bawahan': [
'https://images.unsplash.com/photo-1583496661160-fb4886b36ca7?w=800&auto=format&fit=crop&q=80',
],
};
const firstCat = product.categories?.[0]?.slug || '';
const categoryUrls = fallbacks[firstCat] || [
'https://images.unsplash.com/photo-1490481651871-ab68de25d43d?w=800&auto=format&fit=crop&q=80',
'https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=800&auto=format&fit=crop&q=80',
];
return categoryUrls[0];
}
function getProductPriceRange(product: Product): string {
const prices = product.product_variants.flatMap((v) =>
v.product_prices
.filter((p) => p.type === 'retail')
.map((p) => p.price),
);
if (prices.length === 0) {
const fallbackPrices = product.product_variants.flatMap((v) =>
v.product_prices.map((p) => p.price),
);
if (fallbackPrices.length === 0) {
return 'Rp 0';
}
const min = Math.min(...fallbackPrices);
const max = Math.max(...fallbackPrices);
return min === max
? `Rp ${formatRupiah(min)}`
: `Rp ${formatRupiah(min)} - Rp ${formatRupiah(max)}`;
}
const min = Math.min(...prices);
const max = Math.max(...prices);
return min === max
? `Rp ${formatRupiah(min)}`
: `Rp ${formatRupiah(min)} - Rp ${formatRupiah(max)}`;
}
export default function ProductCard({ product, onQuickView }: ProductCardProps) {
return (
<div
className="group cursor-pointer"
onClick={() => onQuickView(product)}
>
<div className="relative aspect-3/4 rounded-2xl overflow-hidden shadow-md border-2 border-white transition-all duration-300 hover:shadow-xl hover:-translate-y-1">
<img
src={getProductImage(product)}
alt={product.name}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/40 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="absolute bottom-0 left-0 right-0 p-4 translate-y-full group-hover:translate-y-0 transition-transform duration-300">
<span className="text-white text-xs font-semibold uppercase tracking-widest">
Lihat Detail
</span>
</div>
</div>
<div className="mt-4 space-y-1">
<div className="flex items-center gap-2">
{product.categories.slice(0, 2).map((cat) => (
<span
key={cat.id}
className="text-[10px] font-semibold uppercase tracking-wider text-amber-600 bg-amber-50 px-2 py-0.5 rounded-full"
>
{cat.name}
</span>
))}
</div>
<h4 className="text-sm font-semibold text-slate-800 line-clamp-1">
{product.name}
</h4>
<p className="text-xs text-slate-500 line-clamp-2 font-light">
{product.description || 'Koleksi fashion berkualitas'}
</p>
<p className="text-sm font-bold text-amber-600">
{getProductPriceRange(product)}
</p>
</div>
</div>
);
}