From 3885fd52fefc3167524aacd6268ed518cd3269df Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 22 Feb 2026 13:29:35 +0000 Subject: [PATCH] Add bulk POI distances endpoint for decoupled loading New GET /api/poi/distances/bulk returns all POI distances keyed by listing ID, allowing the frontend to fetch distances separately from the listing stream and keep the stream on the cached path. --- api/poi_routes.py | 36 +++++++++++++++++++++++++++++ frontend/src/services/index.ts | 2 +- frontend/src/services/poiService.ts | 9 ++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/api/poi_routes.py b/api/poi_routes.py index eb74841..8d2c256 100644 --- a/api/poi_routes.py +++ b/api/poi_routes.py @@ -172,6 +172,42 @@ async def calculate_distances( return {"task_id": result.task_id or "", "message": result.message} +@poi_router.get("/distances/bulk") +async def get_bulk_distances( + user: Annotated[User, Depends(get_current_user)], + listing_type: ListingType = ListingType.RENT, +) -> dict[int, list[POIDistanceResponse]]: + """Get all POI distances for the current user, keyed by listing ID.""" + user_id = _get_user_id(user) + repo = POIRepository(engine) + pois = {p.id: p for p in poi_service.get_user_pois(repo, user_id)} + if not pois: + return {} + + from repositories.listing_repository import ListingRepository + from database import engine as db_engine + listing_repo = ListingRepository(db_engine) + all_ids = list(listing_repo.get_listing_ids(listing_type)) + if not all_ids: + return {} + + distances = repo.get_distances_for_listings(all_ids, listing_type, user_id) + + result: dict[int, list[POIDistanceResponse]] = {} + for d in distances: + poi_name = pois[d.poi_id].name if d.poi_id in pois else "Unknown" + result.setdefault(d.listing_id, []).append( + POIDistanceResponse( + poi_id=d.poi_id, + poi_name=poi_name, + travel_mode=d.travel_mode, + duration_seconds=d.duration_seconds, + distance_meters=d.distance_meters, + ) + ) + return result + + @poi_router.get("/distances") async def get_distances( user: Annotated[User, Depends(get_current_user)], diff --git a/frontend/src/services/index.ts b/frontend/src/services/index.ts index a081b7d..e3b11e0 100644 --- a/frontend/src/services/index.ts +++ b/frontend/src/services/index.ts @@ -4,6 +4,6 @@ export { fetchListingGeoJSON, refreshListings } from './listingService'; export { streamListingGeoJSON, type StreamingProgress } from './streamingService'; export { fetchTasksForUser, fetchTaskStatus, cancelTask, clearAllTasks, type CancelTaskResponse, type ClearAllTasksResponse } from './taskService'; export { checkBackendHealth, type HealthStatus, type HealthCheckResult } from './healthService'; -export { fetchUserPOIs, createPOI, updatePOI, deletePOI, triggerPOICalculation, fetchPOIDistances } from './poiService'; +export { fetchUserPOIs, createPOI, updatePOI, deletePOI, triggerPOICalculation, fetchPOIDistances, fetchBulkPOIDistances } from './poiService'; export { fetchDecisions, setDecision, clearDecision } from './decisionService'; export { fetchListingDetail } from './listingDetailService'; diff --git a/frontend/src/services/poiService.ts b/frontend/src/services/poiService.ts index 7529735..187f244 100644 --- a/frontend/src/services/poiService.ts +++ b/frontend/src/services/poiService.ts @@ -62,3 +62,12 @@ export async function fetchPOIDistances( }, }); } + +export async function fetchBulkPOIDistances( + user: AuthUser, + listingType: 'RENT' | 'BUY' = 'RENT' +): Promise> { + return apiRequest>(user, '/api/poi/distances/bulk', { + params: { listing_type: listingType }, + }); +}