diff --git a/frontend/src/pages/MarketView.tsx b/frontend/src/pages/MarketView.tsx index 0fb0093..d743c0c 100644 --- a/frontend/src/pages/MarketView.tsx +++ b/frontend/src/pages/MarketView.tsx @@ -1678,16 +1678,28 @@ const RateMatrixTab: React.FC = () => { ? formatCurrency(rate.last_available_rate) : null - // For sold-out / past: show last known available rate with strikethrough + // Rate text + strikethrough logic: + // Sold out + last price → "SOLD" label above + strikethrough price (no badge) + // Sold out, no history → "Sold" text only + // Past + last price → strikethrough price (grey/faded badge) + // Past, no history → — + // Available → live rate + coloured badge let rateText = '' let strikethrough = false + let showSoldLabel = false if (!rate) { rateText = '' - } else if (isSoldOrPast && lastAvailRate) { + } else if (isSoldOut && lastAvailRate) { rateText = lastAvailRate + staleMark strikethrough = true - } else if (isSoldOrPast) { - rateText = isSoldOut ? 'Sold' : '—' + showSoldLabel = true + } else if (isSoldOut) { + rateText = 'Sold' + } else if (isPast && lastAvailRate) { + rateText = lastAvailRate + staleMark + strikethrough = true + } else if (isPast) { + rateText = '—' } else if (isAvailable && rate.rate_gross) { rateText = formatCurrency(rate.rate_gross) + staleMark } else { @@ -1699,7 +1711,7 @@ const RateMatrixTab: React.FC = () => { rate.breakfast_included ? 'Breakfast incl.' : null, rate.free_cancellation ? 'Free cancel' : null, rate.rooms_left ? `${rate.rooms_left} left` : null, - lastAvailRate && isSoldOrPast ? `Last available: ${lastAvailRate}` : null, + lastAvailRate && (isSoldOut || isPast) ? `Last available: ${lastAvailRate}` : null, rate.scraped_at ? `Scraped: ${new Date(rate.scraped_at).toLocaleString('en-GB', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' })}` : null, isStale ? 'Not updated in latest scrape' : null, ].filter(Boolean).join(' | ') : '' @@ -1724,19 +1736,31 @@ const RateMatrixTab: React.FC = () => { } } - // Price index badge for competitor rows (use last_available_rate for sold/past) - const rateForIndex = isSoldOrPast ? rate?.last_available_rate : rate?.rate_gross + // Price index badge: + // Sold out → no badge (price no longer bookable, delta misleading) + // Past → grey/faded badge (historical context only) + // Future available → coloured badge + const rateForIndex = isPast ? rate?.last_available_rate : (!isSoldOut ? rate?.rate_gross : null) let priceIndexBadge: React.ReactNode = null if (hotel.tier === 'competitor' && rateForIndex && ownRateByDate[d]) { const delta = Math.round((rateForIndex / ownRateByDate[d]! - 1) * 100) - const bg = delta > 5 ? '#dcfce7' : delta < -15 ? '#fee2e2' : delta < -5 ? '#fef3c7' : '#f1f5f9' - const fg = delta > 5 ? '#16a34a' : delta < -15 ? '#dc2626' : delta < -5 ? '#d97706' : '#64748b' - priceIndexBadge = ( - - {delta > 0 ? '+' : ''}{delta}% - - ) + if (isPast) { + priceIndexBadge = ( + + {delta > 0 ? '+' : ''}{delta}% + + ) + } else { + const bg = delta > 5 ? '#dcfce7' : delta < -15 ? '#fee2e2' : delta < -5 ? '#fef3c7' : '#f1f5f9' + const fg = delta > 5 ? '#16a34a' : delta < -15 ? '#dc2626' : delta < -5 ? '#d97706' : '#64748b' + priceIndexBadge = ( + + {delta > 0 ? '+' : ''}{delta}% + + ) + } } const showEye = !isPast && !!bookingUrl && !!rate @@ -1762,6 +1786,9 @@ const RateMatrixTab: React.FC = () => { onMouseLeave={onCellLeave} >