Add 'last changed' tooltip to bookability date headers
Separate verified_at (last checked) from valid_from (last rate change) in the matrix response. Column headers show when rates were last checked; hovering shows both "Checked: X" and "Changed: Y" so users can distinguish a manual refresh that verified unchanged rates from one that found new prices. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
418e0d2fcc
commit
ed0a027448
2 changed files with 30 additions and 14 deletions
|
|
@ -52,14 +52,16 @@ class DateRateInfo(BaseModel):
|
||||||
tariffs: List[TariffInfo]
|
tariffs: List[TariffInfo]
|
||||||
tariff_count: int
|
tariff_count: int
|
||||||
occupancy: Optional[OccupancyInfo] = None
|
occupancy: Optional[OccupancyInfo] = None
|
||||||
valid_from: Optional[str] = None
|
valid_from: Optional[str] = None # when rates last CHANGED
|
||||||
|
verified_at: Optional[str] = None # when rates last CHECKED
|
||||||
|
|
||||||
|
|
||||||
class RateMatrixResponse(BaseModel):
|
class RateMatrixResponse(BaseModel):
|
||||||
categories: List[CategoryInfo]
|
categories: List[CategoryInfo]
|
||||||
dates: List[str]
|
dates: List[str]
|
||||||
matrix: Dict[str, Dict[str, DateRateInfo]]
|
matrix: Dict[str, Dict[str, DateRateInfo]]
|
||||||
date_last_updated: Dict[str, Optional[str]] = {}
|
date_last_updated: Dict[str, Optional[str]] = {} # max verified_at per date
|
||||||
|
date_last_changed: Dict[str, Optional[str]] = {} # max valid_from per date
|
||||||
|
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
@ -346,26 +348,31 @@ async def get_rate_matrix(
|
||||||
tariffs=tariffs_list,
|
tariffs=tariffs_list,
|
||||||
tariff_count=tariffs_data.get('tariff_count', len(tariffs_list)),
|
tariff_count=tariffs_data.get('tariff_count', len(tariffs_list)),
|
||||||
occupancy=existing_occ,
|
occupancy=existing_occ,
|
||||||
valid_from=lv
|
valid_from=vf,
|
||||||
|
verified_at=lv
|
||||||
)
|
)
|
||||||
|
|
||||||
# Per-date latest check time (max last_verified_at across all categories for each date)
|
# Per-date header timestamps
|
||||||
# Use last_verified_at so the header reflects when data was last CHECKED, not when rates last changed.
|
date_last_updated: Dict[str, Optional[str]] = {} # last CHECKED (verified_at)
|
||||||
# This makes all dates verified in the same daily run show a consistent timestamp.
|
date_last_changed: Dict[str, Optional[str]] = {} # last CHANGED (valid_from)
|
||||||
date_last_updated: Dict[str, Optional[str]] = {}
|
|
||||||
for date_str in dates:
|
for date_str in dates:
|
||||||
latest = None
|
latest_checked = None
|
||||||
|
latest_changed = None
|
||||||
for cat in categories:
|
for cat in categories:
|
||||||
vf = matrix.get(cat.category_id, {}).get(date_str, DateRateInfo(tariffs=[], tariff_count=0)).valid_from
|
entry = matrix.get(cat.category_id, {}).get(date_str, DateRateInfo(tariffs=[], tariff_count=0))
|
||||||
if vf and (latest is None or vf > latest):
|
if entry.verified_at and (latest_checked is None or entry.verified_at > latest_checked):
|
||||||
latest = vf
|
latest_checked = entry.verified_at
|
||||||
date_last_updated[date_str] = latest
|
if entry.valid_from and (latest_changed is None or entry.valid_from > latest_changed):
|
||||||
|
latest_changed = entry.valid_from
|
||||||
|
date_last_updated[date_str] = latest_checked
|
||||||
|
date_last_changed[date_str] = latest_changed
|
||||||
|
|
||||||
return RateMatrixResponse(
|
return RateMatrixResponse(
|
||||||
categories=categories,
|
categories=categories,
|
||||||
dates=dates,
|
dates=dates,
|
||||||
matrix=matrix,
|
matrix=matrix,
|
||||||
date_last_updated=date_last_updated
|
date_last_updated=date_last_updated,
|
||||||
|
date_last_changed=date_last_changed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ interface RateMatrixData {
|
||||||
dates: string[]
|
dates: string[]
|
||||||
matrix: Record<string, Record<string, DateRateInfo>>
|
matrix: Record<string, Record<string, DateRateInfo>>
|
||||||
date_last_updated?: Record<string, string | null>
|
date_last_updated?: Record<string, string | null>
|
||||||
|
date_last_changed?: Record<string, string | null>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
@ -432,7 +433,15 @@ const Bookability: React.FC = () => {
|
||||||
{refreshingDate === dateStr ? '...' : '↻'}
|
{refreshingDate === dateStr ? '...' : '↻'}
|
||||||
</button>
|
</button>
|
||||||
{data.date_last_updated?.[dateStr] && (
|
{data.date_last_updated?.[dateStr] && (
|
||||||
<span style={styles.lastUpdated} title={`Rates last fetched: ${data.date_last_updated[dateStr]}`}>
|
<span
|
||||||
|
style={styles.lastUpdated}
|
||||||
|
title={[
|
||||||
|
`Checked: ${new Date(data.date_last_updated[dateStr]!).toLocaleString('en-GB')}`,
|
||||||
|
data.date_last_changed?.[dateStr]
|
||||||
|
? `Changed: ${new Date(data.date_last_changed[dateStr]!).toLocaleString('en-GB')}`
|
||||||
|
: null,
|
||||||
|
].filter(Boolean).join('\n')}
|
||||||
|
>
|
||||||
{formatLastUpdated(data.date_last_updated[dateStr])}
|
{formatLastUpdated(data.date_last_updated[dateStr])}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue