{Array.from(byPoi.entries()).map(([poiName, dists]) => (
{poiName}:
{dists.map(d => (
{formatDuration(d.duration_seconds)}
))}
))}
);
}
interface PropertyCardProps {
property: PropertyProperties;
variant?: 'compact' | 'full';
isHighlighted?: boolean;
avgPricePerSqm?: number;
onClick?: () => void;
}
export function PropertyCard({
property,
variant = 'compact',
isHighlighted = false,
avgPricePerSqm,
onClick,
}: PropertyCardProps) {
const lastSeenDate = property.last_seen.split('T')[0];
const lastSeenDays = Math.round((Date.now() - new Date(lastSeenDate).getTime()) / (1000 * 60 * 60 * 24));
// Determine if this is a good deal
const isGoodDeal = avgPricePerSqm && property.qmprice > 0 && property.qmprice < avgPricePerSqm * 0.9;
const isExpensive = avgPricePerSqm && property.qmprice > avgPricePerSqm * 1.1;
const priceIndicator = isGoodDeal
? { color: 'text-green-600 bg-green-50', label: 'Good deal' }
: isExpensive
? { color: 'text-red-600 bg-red-50', label: 'Above avg' }
: null;
const handleClick = () => {
window.open(property.url, '_blank', 'noopener,noreferrer');
onClick?.();
};
if (variant === 'compact') {
return (
{/* Header with image and price */}
{property.photo_thumbnail && (
)}
£{property.total_price.toLocaleString()}
{property.listing_type !== 'BUY' && (
/mo
)}
{priceIndicator && (
{priceIndicator.label}
)}
{/* Stats grid */}
{property.rooms} bedrooms
{property.qm} m²
{property.listing_type !== 'BUY' && property.available_from && (
Available {property.available_from}
)}
{property.listing_type === 'BUY' && (
Seen {lastSeenDays}d ago
)}
{/* Agency and last seen */}
{property.agency}
•
Seen {lastSeenDays} days ago
{/* POI Distances */}
{property.poi_distances && property.poi_distances.length > 0 && (
)}
{/* Price history */}
{property.price_history.length > 1 && (
Price history
{property.price_history.slice(0, 5).map((entry) => (
{entry.last_seen.split('T')[0]}
£{entry.price.toLocaleString()}
))}
)}
{/* Actions */}
);
}