141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Link from 'next/link';
|
|
import { Product } from '@/mock/product';
|
|
|
|
interface ProductCardProps {
|
|
product: Product;
|
|
onAdd?: () => void;
|
|
added?: boolean;
|
|
}
|
|
|
|
function getRestrictionFlags(restrictions?: Product['restrictions']): string[] {
|
|
const flags: string[] = [];
|
|
if (restrictions?.nfa) flags.push('NFA');
|
|
if (restrictions?.sbr) flags.push('SBR');
|
|
if (restrictions?.suppressor) flags.push('SUPPRESSOR');
|
|
if (restrictions?.stateRestrictions && restrictions.stateRestrictions.length > 0) flags.push('STATE_RESTRICTIONS');
|
|
return flags;
|
|
}
|
|
|
|
export default function ProductCard({ product, onAdd, added }: 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 ? '/window.svg' : product.image_url}
|
|
alt={product.name}
|
|
className="w-full h-48 object-cover"
|
|
onError={() => setImageError(true)}
|
|
/>
|
|
{getRestrictionFlags(product.restrictions).length > 0 && (
|
|
<div className="absolute top-2 left-2 flex flex-wrap gap-1">
|
|
{getRestrictionFlags(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>
|
|
<Link href={`/products/${product.id}`} legacyBehavior>
|
|
<a className="btn btn-primary btn-sm">
|
|
View Details
|
|
</a>
|
|
</Link>
|
|
{onAdd && (
|
|
<button
|
|
className="btn btn-neutral btn-sm ml-2 flex items-center gap-1"
|
|
onClick={onAdd}
|
|
disabled={added}
|
|
>
|
|
{added ? (
|
|
'Added!'
|
|
) : (
|
|
<>
|
|
<span className="text-lg leading-none">+</span>
|
|
<span className="text-xs font-normal">to build</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|