mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
Reservation end time, route perf overhaul, assignment search fix
- Add reservation_end_time field (DB migration, API, UI) - Split reservation form: separate date, start time, end time, status fields - Fix DateTimePicker forcing 00:00 when no time selected - Show end time across all reservation displays - Link-to-assignment and date on same row (50/50 layout) - Assignment search now shows day headers for filtered results - Auto-fill date when selecting a day assignment - Route segments: single OSRM request instead of N separate calls (~6s → ~1s) - Route labels visible from zoom level 12 (was 16) - Fix stale route labels after place deletion (useEffect triggers recalc) - AbortController cancels outdated route calculations
This commit is contained in:
@@ -162,11 +162,11 @@ function MapClickHandler({ onClick }) {
|
||||
// ── Route travel time label ──
|
||||
function RouteLabel({ midpoint, walkingText, drivingText }) {
|
||||
const map = useMap()
|
||||
const [visible, setVisible] = useState(map ? map.getZoom() >= 16 : false)
|
||||
const [visible, setVisible] = useState(map ? map.getZoom() >= 12 : false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return
|
||||
const check = () => setVisible(map.getZoom() >= 16)
|
||||
const check = () => setVisible(map.getZoom() >= 12)
|
||||
check()
|
||||
map.on('zoomend', check)
|
||||
return () => map.off('zoomend', check)
|
||||
|
||||
@@ -7,7 +7,7 @@ const OSRM_BASE = 'https://router.project-osrm.org/route/v1'
|
||||
* @param {string} profile - 'driving' | 'walking' | 'cycling'
|
||||
* @returns {Promise<{coordinates: Array<[number,number]>, distance: number, duration: number, distanceText: string, durationText: string}>}
|
||||
*/
|
||||
export async function calculateRoute(waypoints, profile = 'driving') {
|
||||
export async function calculateRoute(waypoints, profile = 'driving', { signal } = {}) {
|
||||
if (!waypoints || waypoints.length < 2) {
|
||||
throw new Error('At least 2 waypoints required')
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export async function calculateRoute(waypoints, profile = 'driving') {
|
||||
// OSRM public API only supports driving; we override duration for other modes
|
||||
const url = `${OSRM_BASE}/driving/${coords}?overview=full&geometries=geojson&steps=false`
|
||||
|
||||
const response = await fetch(url)
|
||||
const response = await fetch(url, { signal })
|
||||
if (!response.ok) {
|
||||
throw new Error('Route could not be calculated')
|
||||
}
|
||||
@@ -100,6 +100,36 @@ export function optimizeRoute(places) {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate per-leg travel times in a single OSRM request
|
||||
* Returns array of { mid, walkingText, drivingText } for each leg
|
||||
*/
|
||||
export async function calculateSegments(waypoints, { signal } = {}) {
|
||||
if (!waypoints || waypoints.length < 2) return []
|
||||
|
||||
const coords = waypoints.map(p => `${p.lng},${p.lat}`).join(';')
|
||||
const url = `${OSRM_BASE}/driving/${coords}?overview=false&geometries=geojson&steps=false&annotations=distance,duration`
|
||||
|
||||
const response = await fetch(url, { signal })
|
||||
if (!response.ok) throw new Error('Route could not be calculated')
|
||||
|
||||
const data = await response.json()
|
||||
if (data.code !== 'Ok' || !data.routes?.[0]) throw new Error('No route found')
|
||||
|
||||
const legs = data.routes[0].legs
|
||||
return legs.map((leg, i) => {
|
||||
const from = [waypoints[i].lat, waypoints[i].lng]
|
||||
const to = [waypoints[i + 1].lat, waypoints[i + 1].lng]
|
||||
const mid = [(from[0] + to[0]) / 2, (from[1] + to[1]) / 2]
|
||||
const walkingDuration = leg.distance / (5000 / 3600) // 5 km/h
|
||||
return {
|
||||
mid, from, to,
|
||||
walkingText: formatDuration(walkingDuration),
|
||||
drivingText: formatDuration(leg.duration),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function formatDistance(meters) {
|
||||
if (meters < 1000) {
|
||||
return `${Math.round(meters)} m`
|
||||
|
||||
Reference in New Issue
Block a user