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
|
|
@ -8,7 +8,7 @@ import "../assets/Map.css";
|
|||
import { Metric, type ParameterValues } from "./Parameters";
|
||||
import { PropertyCard } from "./PropertyCard";
|
||||
import { ScrollArea } from "./ui/scroll-area";
|
||||
import type { GeoJSONFeatureCollection, PropertyFeature, PropertyProperties } from "@/types";
|
||||
import type { GeoJSONFeatureCollection, PropertyFeature, PropertyProperties, POI } from "@/types";
|
||||
import { MAP_CONFIG, HEATMAP_CONFIG, PERCENTILE_CONFIG } from "@/constants";
|
||||
import { getColorSchemeForMetric, getMetricInterpretation } from "@/constants/colorSchemes";
|
||||
import { clone, percentile, calculateColorStops } from "@/utils/mapUtils";
|
||||
|
|
@ -40,6 +40,7 @@ interface MapProps {
|
|||
listingData: GeoJSONFeatureCollection;
|
||||
queryParameters: ParameterValues | null;
|
||||
onPropertyClick?: (property: PropertyProperties, coordinates: [number, number]) => void;
|
||||
pois?: POI[];
|
||||
}
|
||||
|
||||
interface FilterState {
|
||||
|
|
@ -58,6 +59,7 @@ export function Map(props: MapProps) {
|
|||
const updateTimeoutRef = useRef<number | null>(null);
|
||||
const isMapLoadedRef = useRef<boolean>(false);
|
||||
const lastDataLengthRef = useRef<number>(0);
|
||||
const poiMarkersRef = useRef<mapboxgl.Marker[]>([]);
|
||||
|
||||
const filter: FilterState = { city: 'London', country: null, mode: Metric.qmprice };
|
||||
if (props.queryParameters) {
|
||||
|
|
@ -237,6 +239,33 @@ export function Map(props: MapProps) {
|
|||
};
|
||||
}, [data, updateHeatmap]);
|
||||
|
||||
// Update POI markers when pois prop changes
|
||||
useEffect(() => {
|
||||
if (!mapRef.current || !isMapLoadedRef.current) return;
|
||||
|
||||
// Remove existing markers
|
||||
poiMarkersRef.current.forEach(m => m.remove());
|
||||
poiMarkersRef.current = [];
|
||||
|
||||
if (!props.pois) return;
|
||||
|
||||
for (const poi of props.pois) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'poi-marker';
|
||||
el.style.cssText = 'width:24px;height:24px;background:#ef4444;border:2px solid white;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,0.3);cursor:pointer;';
|
||||
el.title = poi.name;
|
||||
|
||||
const marker = new mapboxgl.Marker({ element: el })
|
||||
.setLngLat([poi.longitude, poi.latitude])
|
||||
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(
|
||||
`<div style="padding:4px 8px"><strong>${poi.name}</strong><br/><span style="color:#666;font-size:12px">${poi.address}</span></div>`
|
||||
))
|
||||
.addTo(mapRef.current);
|
||||
|
||||
poiMarkersRef.current.push(marker);
|
||||
}
|
||||
}, [props.pois]);
|
||||
|
||||
function makeLegend(colorstops: [number, string][], minValue: number, maxValue: number) {
|
||||
const svg_height = 280, svg_width = 80;
|
||||
d3.select('#svg').selectAll('*').remove();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue