Add frontend POI management and travel time display
POIManager component in FilterPanel for creating/deleting POIs and triggering distance calculations. PropertyCard shows travel time badges (walk/cycle/transit) per POI. Map renders POI locations as red markers. API client extended with POST body support for POI endpoints.
This commit is contained in:
parent
bb489c2032
commit
8509a0326f
9 changed files with 414 additions and 10 deletions
|
|
@ -6,6 +6,7 @@ import { ApiError } from '@/types';
|
|||
export interface RequestOptions {
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
params?: Record<string, string | number | boolean | Date | undefined>;
|
||||
body?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +36,7 @@ export async function apiRequest<T>(
|
|||
endpoint: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<T> {
|
||||
const { method = 'GET', params } = options;
|
||||
const { method = 'GET', params, body } = options;
|
||||
|
||||
let url = endpoint;
|
||||
if (params) {
|
||||
|
|
@ -45,13 +46,19 @@ export async function apiRequest<T>(
|
|||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${user.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (body !== undefined) {
|
||||
fetchOptions.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const response = await fetch(url, fetchOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ApiError(`Error: ${response.status}`, response.status);
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ 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';
|
||||
|
|
|
|||
64
frontend/src/services/poiService.ts
Normal file
64
frontend/src/services/poiService.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// POI API service for managing Points of Interest
|
||||
|
||||
import type { AuthUser } from '@/auth/types';
|
||||
import type { POI, POIDistanceInfo } from '@/types';
|
||||
import { apiRequest } from './apiClient';
|
||||
|
||||
export async function fetchUserPOIs(user: AuthUser): Promise<POI[]> {
|
||||
return apiRequest<POI[]>(user, '/api/poi');
|
||||
}
|
||||
|
||||
export async function createPOI(
|
||||
user: AuthUser,
|
||||
data: { name: string; address: string; latitude: number; longitude: number }
|
||||
): Promise<POI> {
|
||||
return apiRequest<POI>(user, '/api/poi', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updatePOI(
|
||||
user: AuthUser,
|
||||
poiId: number,
|
||||
data: { name?: string; address?: string; latitude?: number; longitude?: number }
|
||||
): Promise<POI> {
|
||||
return apiRequest<POI>(user, `/api/poi/${poiId}`, {
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deletePOI(user: AuthUser, poiId: number): Promise<void> {
|
||||
await apiRequest(user, `/api/poi/${poiId}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
export async function triggerPOICalculation(
|
||||
user: AuthUser,
|
||||
poiId: number,
|
||||
travelModes: string[],
|
||||
listingType: 'RENT' | 'BUY',
|
||||
listingIds?: number[]
|
||||
): Promise<{ task_id: string; message: string }> {
|
||||
return apiRequest(user, `/api/poi/${poiId}/calculate`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
travel_modes: travelModes,
|
||||
listing_type: listingType,
|
||||
listing_ids: listingIds,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchPOIDistances(
|
||||
user: AuthUser,
|
||||
listingId: number,
|
||||
listingType: 'RENT' | 'BUY' = 'RENT'
|
||||
): Promise<POIDistanceInfo[]> {
|
||||
return apiRequest<POIDistanceInfo[]>(user, '/api/poi/distances', {
|
||||
params: {
|
||||
listing_id: listingId,
|
||||
listing_type: listingType,
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue