Files
gunbuilder-next-tailwind/src/components/ProductCard.tsx
T
2025-06-29 08:39:26 -04:00

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>
);
}