Files
rentall-app/frontend/src/components/ItemCard.tsx

104 lines
3.3 KiB
TypeScript

import React from 'react';
import { Link } from 'react-router-dom';
import { Item } from '../types';
import { getImageUrl } from '../services/uploadService';
interface ItemCardProps {
item: Item;
variant?: 'compact' | 'standard';
}
const ItemCard: React.FC<ItemCardProps> = ({
item,
variant = 'standard'
}) => {
const isCompact = variant === 'compact';
const getPriceDisplay = () => {
// Collect all available pricing tiers
const pricingTiers: string[] = [];
if (item.pricePerHour && Number(item.pricePerHour) > 0) {
pricingTiers.push(`$${Math.floor(Number(item.pricePerHour))}/hr`);
}
if (item.pricePerDay && Number(item.pricePerDay) > 0) {
pricingTiers.push(`$${Math.floor(Number(item.pricePerDay))}/day`);
}
if (item.pricePerWeek && Number(item.pricePerWeek) > 0) {
pricingTiers.push(`$${Math.floor(Number(item.pricePerWeek))}/wk`);
}
if (item.pricePerMonth && Number(item.pricePerMonth) > 0) {
pricingTiers.push(`$${Math.floor(Number(item.pricePerMonth))}/mo`);
}
if (pricingTiers.length === 0) {
return "Free to Borrow";
}
// Display up to 2 pricing tiers separated by bullet point
return pricingTiers.slice(0, 2).join(" • ");
};
const getLocationDisplay = () => {
return item.city && item.state
? `${item.city}, ${item.state}`
: '';
};
return (
<Link to={`/items/${item.id}`} className="text-decoration-none">
<div className="card h-100" style={{ cursor: 'pointer' }}>
{item.imageFilenames && item.imageFilenames[0] ? (
<img
src={getImageUrl(item.imageFilenames[0], 'thumbnail')}
className="card-img-top"
alt={item.name}
loading="lazy"
onError={(e) => {
// Fallback to original for images uploaded before resizing was added
const target = e.currentTarget;
if (!target.dataset.fallback) {
target.dataset.fallback = 'true';
target.src = getImageUrl(item.imageFilenames[0], 'original');
}
}}
style={{
height: isCompact ? '150px' : '200px',
objectFit: 'contain',
backgroundColor: '#f8f9fa'
}}
/>
) : (
<div
className="card-img-top bg-light d-flex align-items-center justify-content-center"
style={{ height: isCompact ? '150px' : '200px' }}
>
<i className="bi bi-image text-muted" style={{ fontSize: '2rem' }}></i>
</div>
)}
<div className={`card-body ${isCompact ? 'p-2' : ''}`}>
{isCompact ? (
<h6 className="card-title text-truncate mb-1 text-dark">{item.name}</h6>
) : (
<h5 className="card-title text-dark">{item.name}</h5>
)}
<div className={isCompact ? "mb-1" : "mb-3"}>
<div className="text-primary">
<strong className={isCompact ? "small" : ""}>
{getPriceDisplay()}
</strong>
</div>
</div>
<div className={`text-muted small ${isCompact ? 'mb-1' : 'mb-2'}`}>
<i className="bi bi-geo-alt"></i> {getLocationDisplay()}
</div>
</div>
</div>
</Link>
);
};
export default ItemCard;