119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
|
|
interface ProductCardProps {
|
|
product: {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
image_url: string;
|
|
brand: { name: string };
|
|
category: { name: string };
|
|
restrictions: string[];
|
|
offers: Array<{ price: number; vendor: { name: string } }>;
|
|
};
|
|
}
|
|
|
|
export default function ProductCard({ product }: ProductCardProps) {
|
|
const [imageError, setImageError] = useState(false);
|
|
const lowestPrice = Math.min(...product.offers.map(offer => offer.price));
|
|
|
|
// Restriction badge component
|
|
const RestrictionBadge = ({ restriction }: { restriction: string }) => {
|
|
const restrictionConfig = {
|
|
NFA: {
|
|
label: 'NFA',
|
|
color: 'badge-error',
|
|
icon: '🔒',
|
|
tooltip: 'National Firearms Act - Requires special registration'
|
|
},
|
|
SBR: {
|
|
label: 'SBR',
|
|
color: 'badge-warning',
|
|
icon: '📏',
|
|
tooltip: 'Short Barrel Rifle - Requires NFA registration'
|
|
},
|
|
SUPPRESSOR: {
|
|
label: 'Suppressor',
|
|
color: 'badge-secondary',
|
|
icon: '🔇',
|
|
tooltip: 'Sound Suppressor - Requires NFA registration'
|
|
},
|
|
FFL_REQUIRED: {
|
|
label: 'FFL',
|
|
color: 'badge-info',
|
|
icon: '🏪',
|
|
tooltip: 'Federal Firearms License required for purchase'
|
|
},
|
|
STATE_RESTRICTIONS: {
|
|
label: 'State',
|
|
color: 'badge-warning',
|
|
icon: '🗺️',
|
|
tooltip: 'State-specific restrictions may apply'
|
|
},
|
|
HIGH_CAPACITY: {
|
|
label: 'High Cap',
|
|
color: 'badge-accent',
|
|
icon: '🥁',
|
|
tooltip: 'High capacity magazine - check local laws'
|
|
},
|
|
SILENCERSHOP_PARTNER: {
|
|
label: 'SilencerShop',
|
|
color: 'badge-success',
|
|
icon: '🤝',
|
|
tooltip: 'Available through SilencerShop partnership'
|
|
}
|
|
};
|
|
|
|
const config = restrictionConfig[restriction as keyof typeof restrictionConfig];
|
|
if (!config) return null;
|
|
|
|
return (
|
|
<div
|
|
className={`badge ${config.color} gap-1 cursor-help`}
|
|
title={config.tooltip}
|
|
>
|
|
<span>{config.icon}</span>
|
|
<span>{config.label}</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="card bg-base-100 shadow-lg hover:shadow-xl transition-shadow duration-300 border border-base-300">
|
|
<figure className="relative">
|
|
<img
|
|
src={imageError ? 'https://placehold.co/300x200/6b7280/ffffff?text=No+Image' : product.image_url}
|
|
alt={product.name}
|
|
className="w-full h-48 object-cover"
|
|
onError={() => setImageError(true)}
|
|
/>
|
|
{product.restrictions && product.restrictions.length > 0 && (
|
|
<div className="absolute top-2 left-2 flex flex-wrap gap-1">
|
|
{product.restrictions.map((restriction) => (
|
|
<RestrictionBadge key={restriction} restriction={restriction} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</figure>
|
|
|
|
<div className="card-body">
|
|
<h3 className="card-title text-base-content line-clamp-2">{product.name}</h3>
|
|
<p className="text-base-content/70 text-sm line-clamp-2">{product.description}</p>
|
|
|
|
<div className="flex items-center justify-between mb-3">
|
|
<span className="text-sm text-base-content/60">{product.brand.name}</span>
|
|
<span className="text-lg font-bold text-primary">${lowestPrice.toFixed(2)}</span>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-xs text-base-content/50">{product.category.name}</span>
|
|
<button className="btn btn-primary btn-sm">
|
|
View Details
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|