From ed0a0274486bbf1a9d31473d810b9b93405ca0b3 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Fri, 10 Jul 2026 08:44:29 +0000 Subject: [PATCH] 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 --- backend/api/bookability.py | 33 ++++++++++++++++++------------ frontend/src/pages/Bookability.tsx | 11 +++++++++- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/backend/api/bookability.py b/backend/api/bookability.py index b240afb..27d2b0e 100644 --- a/backend/api/bookability.py +++ b/backend/api/bookability.py @@ -52,14 +52,16 @@ class DateRateInfo(BaseModel): tariffs: List[TariffInfo] tariff_count: int 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): categories: List[CategoryInfo] dates: List[str] 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, tariff_count=tariffs_data.get('tariff_count', len(tariffs_list)), 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) - # Use last_verified_at so the header reflects when data was last CHECKED, not when rates last changed. - # This makes all dates verified in the same daily run show a consistent timestamp. - date_last_updated: Dict[str, Optional[str]] = {} + # Per-date header timestamps + date_last_updated: Dict[str, Optional[str]] = {} # last CHECKED (verified_at) + date_last_changed: Dict[str, Optional[str]] = {} # last CHANGED (valid_from) for date_str in dates: - latest = None + latest_checked = None + latest_changed = None for cat in categories: - vf = matrix.get(cat.category_id, {}).get(date_str, DateRateInfo(tariffs=[], tariff_count=0)).valid_from - if vf and (latest is None or vf > latest): - latest = vf - date_last_updated[date_str] = latest + entry = matrix.get(cat.category_id, {}).get(date_str, DateRateInfo(tariffs=[], tariff_count=0)) + if entry.verified_at and (latest_checked is None or entry.verified_at > latest_checked): + latest_checked = entry.verified_at + 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( categories=categories, dates=dates, matrix=matrix, - date_last_updated=date_last_updated + date_last_updated=date_last_updated, + date_last_changed=date_last_changed ) diff --git a/frontend/src/pages/Bookability.tsx b/frontend/src/pages/Bookability.tsx index a3c3e6d..e9d5367 100644 --- a/frontend/src/pages/Bookability.tsx +++ b/frontend/src/pages/Bookability.tsx @@ -45,6 +45,7 @@ interface RateMatrixData { dates: string[] matrix: Record> date_last_updated?: Record + date_last_changed?: Record } // Helper functions @@ -432,7 +433,15 @@ const Bookability: React.FC = () => { {refreshingDate === dateStr ? '...' : '↻'} {data.date_last_updated?.[dateStr] && ( - + {formatLastUpdated(data.date_last_updated[dateStr])} )}