deckhearth/components/card-detail/CardDetailPurchaseTab.js

133 lines
4.5 KiB
JavaScript
Raw Normal View History

function formatUpdatedLabel(pricesUpdatedAt) {
if (!pricesUpdatedAt) return 'Price date unknown';
const date = new Date(pricesUpdatedAt);
if (Number.isNaN(date.getTime())) return 'Price date unknown';
return `Updated ${date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric',
})}`;
}
export function stalenessBadgeText({ stale, pricesUpdatedAt }) {
if (stale) return 'Prices may be outdated';
return formatUpdatedLabel(pricesUpdatedAt);
}
import CardDetailLocalStores from './CardDetailLocalStores';
export default function CardDetailPurchaseTab({
card,
pricing,
pricingLoading,
formatCurrency,
nearbyStores = [],
nearbyStoresLoading = false,
nearbyStoresError = null,
nearbyStoresNoHomeZip = false,
}) {
const displayPrice = pricing?.prices?.current ?? pricing?.prices?.market ?? card?.current_price ?? null;
const foilPrice = pricing?.prices?.market ?? card?.market_price ?? null;
const badgeText = pricing ? stalenessBadgeText(pricing) : null;
return (
<div className="space-y-6">
<div className="flex flex-wrap items-center justify-between gap-3">
<h3 className="text-xl font-semibold" style={{ color: 'var(--text-primary)' }}>
Where to Buy
</h3>
{badgeText && (
<span
className="px-3 py-1 rounded-full text-xs font-medium"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-secondary)',
}}
data-testid="pricing-staleness-badge"
>
{badgeText}
</span>
)}
</div>
<div className="glass-panel p-6 rounded-xl">
<h4 className="text-lg font-semibold mb-4" style={{ color: 'var(--text-primary)' }}>
Online Price Estimate
</h4>
{pricingLoading ? (
<p style={{ color: 'var(--text-secondary)' }} aria-live="polite">Loading prices…</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<p className="text-sm mb-1" style={{ color: 'var(--text-secondary)' }}>NM / Non-foil</p>
<p className="text-2xl font-bold gradient-text-gold">
{formatCurrency(displayPrice || 0)}
</p>
</div>
{foilPrice != null && foilPrice !== displayPrice && (
<div>
<p className="text-sm mb-1" style={{ color: 'var(--text-secondary)' }}>Foil / Market</p>
<p className="text-2xl font-bold gradient-text-gold">
{formatCurrency(foilPrice)}
</p>
</div>
)}
</div>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{pricing?.outbound?.tcgplayer && (
<a
href={pricing.outbound.tcgplayer}
target="_blank"
rel="noopener noreferrer"
className="glass-panel p-6 rounded-xl transition-all duration-200 hover:shadow-lg hover:scale-105"
>
<div className="flex items-center gap-3">
<span className="text-2xl" aria-hidden="true">🃏</span>
<div>
<h4 className="font-semibold" style={{ color: 'var(--text-primary)' }}>
TCGPlayer
</h4>
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
View on TCGPlayer
</p>
</div>
</div>
</a>
)}
{pricing?.outbound?.ebay && (
<a
href={pricing.outbound.ebay}
target="_blank"
rel="noopener noreferrer"
className="glass-panel p-6 rounded-xl transition-all duration-200 hover:shadow-lg hover:scale-105"
>
<div className="flex items-center gap-3">
<span className="text-2xl" aria-hidden="true">🛒</span>
<div>
<h4 className="font-semibold" style={{ color: 'var(--text-primary)' }}>
eBay
</h4>
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
View on eBay
</p>
</div>
</div>
</a>
)}
</div>
<div data-testid="card-detail-local-stores-slot">
<CardDetailLocalStores
stores={nearbyStores}
loading={nearbyStoresLoading}
error={nearbyStoresError}
noHomeZip={nearbyStoresNoHomeZip}
/>
</div>
</div>
);
}