mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
fix(dayplan): a moving day with a flight draws no drive from one stay to the next
A day that checks out of one hotel and into another, with nothing else planned, falls back to a drive from the first hotel to the second (#1297). A flight, train, ferry or coach booked on that day is the move itself, but one saved without its stations leaves no waypoint behind, so the day map, the trip overview and the phone plan all drew the full road between the two hotels, and the phone showed it above and below the flight (#2476). With its stations the day was already right. - The fallback drive between the two hotels is no longer drawn when any such booking is on the day, located or not. A day without a booking keeps it. No line beats a wrong one. - The phone plan takes the evening hotel leg from the last drive that reaches the hotel, not the first, so an earlier drive to the same spot no longer stands in for the one that closes the day. - Google Maps and CoMaps exports of such a day, which could only ever describe that same drive, return nothing, and on the desktop and the phone the two buttons are left out whenever fewer than two stops remain, instead of doing nothing on a tap. A hotel stop planned by hand ahead of the flight already sorts behind it through the ride seating of #2474; a test now pins that for the days view as well.
This commit is contained in:
@@ -145,4 +145,58 @@ describe('buildDayRouteRuns', () => {
|
||||
it('FE-MAP-DRP-008: a day that is not in the trip has no route', () => {
|
||||
expect(buildDayRouteRuns(99, inputs({ assignments: { '1': [at(48.86, 2.35, 0)] } }))).toEqual([])
|
||||
})
|
||||
|
||||
describe('a moving day from one stay to the next (#2476)', () => {
|
||||
// Day 3 checks out of a hotel in Munich and into one in Hamburg, with a flight
|
||||
// in between. The booked Hamburg night wrote its own (hidden) stop onto day 3.
|
||||
const HOTEL_A = { lat: 48.137, lng: 11.575 }
|
||||
const HOTEL_B = { lat: 53.551, lng: 9.993 }
|
||||
const MUC = { lat: 48.353, lng: 11.786 }
|
||||
const HAM = { lat: 53.63, lng: 9.988 }
|
||||
const days = [1, 2, 3, 4, 5].map(n => buildDay({ id: n, day_number: n }))
|
||||
const stays = [
|
||||
{ id: 1, trip_id: 1, place_lat: HOTEL_A.lat, place_lng: HOTEL_A.lng, start_day_id: 1, end_day_id: 3, check_in: '15:00', check_out: '11:00' },
|
||||
{ id: 2, trip_id: 1, place_lat: HOTEL_B.lat, place_lng: HOTEL_B.lng, start_day_id: 3, end_day_id: 5, check_in: '15:00', check_out: '11:00' },
|
||||
] as unknown as Accommodation[]
|
||||
const bookedStop = buildAssignment({ day_id: 3, order_index: 0, accommodation_id: 2, place: buildPlace({ ...HOTEL_B }) })
|
||||
const flight = (located: boolean) => ({
|
||||
id: 7, trip_id: 1, type: 'flight', title: 'LH 2078', status: 'confirmed', day_id: 3, end_day_id: 3,
|
||||
reservation_time: '2026-11-04T15:15', reservation_end_time: '2026-11-04T17:20',
|
||||
endpoints: located
|
||||
? [{ role: 'from', sequence: 0, name: 'MUC', ...MUC }, { role: 'to', sequence: 1, name: 'HAM', ...HAM }]
|
||||
: [],
|
||||
}) as unknown as Reservation
|
||||
const movingDay = (reservations: Reservation[], dayStops = [bookedStop]) =>
|
||||
buildDayRouteRuns(3, inputs({
|
||||
days, assignments: { '3': dayStops }, reservations, accommodations: stays, optimizeFromAccommodation: true,
|
||||
})).map(run => run.map(p => [p.lat, p.lng]))
|
||||
|
||||
it('FE-MAP-DRP-011: a flight saved without airports draws no drive from one hotel to the other', () => {
|
||||
// Nothing located is left on the day, and the flight is still the move: a road
|
||||
// from Munich to Hamburg is exactly the trip that did not happen.
|
||||
expect(movingDay([flight(false)])).toEqual([])
|
||||
})
|
||||
|
||||
it('FE-MAP-DRP-012: with its airports the flight splits the day at them, as reported', () => {
|
||||
expect(movingDay([flight(true)])).toEqual([
|
||||
[[HOTEL_A.lat, HOTEL_A.lng], [MUC.lat, MUC.lng]],
|
||||
[[HAM.lat, HAM.lng], [HOTEL_B.lat, HOTEL_B.lng]],
|
||||
])
|
||||
})
|
||||
|
||||
it('FE-MAP-DRP-013: without any booking the move is still the drive between the two hotels (#1297)', () => {
|
||||
expect(movingDay([])).toEqual([[[HOTEL_A.lat, HOTEL_A.lng], [HOTEL_B.lat, HOTEL_B.lng]]])
|
||||
})
|
||||
|
||||
it('FE-MAP-DRP-014: a hotel stop planned by hand ahead of the flight is reached after landing', () => {
|
||||
// The traveller put the Hamburg hotel on the day before booking it, so the stop
|
||||
// stays visible and carries no time. The flight still seats itself in front of
|
||||
// the place it lands next to, and no road runs from Munich to Hamburg.
|
||||
const handPlaced = at(HOTEL_B.lat, HOTEL_B.lng, 0, { day_id: 3 })
|
||||
expect(movingDay([flight(true)], [handPlaced])).toEqual([
|
||||
[[HOTEL_A.lat, HOTEL_A.lng], [MUC.lat, MUC.lng]],
|
||||
[[HAM.lat, HAM.lng], [HOTEL_B.lat, HOTEL_B.lng]],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -170,7 +170,12 @@ export function buildDayRouteRuns(dayId: number, input: DayRouteInputs): DayRout
|
||||
|
||||
// Transfer day with no activities: you check out of one accommodation and into
|
||||
// another, so there are no waypoints for withHotelBookends to attach a leg to.
|
||||
if (runsWithHotel.length === 0 && drawMorning && drawEvening) {
|
||||
// Not when a flight, train, ferry or coach is booked on the day, located or not:
|
||||
// that booking IS the move, and the road from one hotel to the other is exactly
|
||||
// the stretch nobody drove (#2476). One saved without its stations leaves no
|
||||
// waypoint behind, so the gates above never see it; no line beats a wrong one.
|
||||
const dayHasCarrierBooking = dayTransports.some(r => isCarrierTransport(r))
|
||||
if (runsWithHotel.length === 0 && drawMorning && drawEvening && !dayHasCarrierBooking) {
|
||||
const m = hotelPt(bookends?.morning)
|
||||
const e = hotelPt(bookends?.evening)
|
||||
if (m && e && (m.lat !== e.lat || m.lng !== e.lng)) runsWithHotel.push([m, e])
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { dayColor } from '../Roadtrip/dayColors'
|
||||
import { dayRouteColor, planTripRoute, routeTrip, summariseTripRoute } from './tripRouteGeometry'
|
||||
import { buildAssignment, buildDay, buildPlace } from '../../../tests/helpers/factories'
|
||||
import type { AssignmentsMap, RouteSegment } from '../../types'
|
||||
import type { Accommodation, AssignmentsMap, Reservation, RouteSegment } from '../../types'
|
||||
|
||||
vi.mock('./RouteCalculator', async (importActual) => {
|
||||
const actual = await importActual<typeof import('./RouteCalculator')>()
|
||||
@@ -212,4 +212,24 @@ describe('tripRouteGeometry', () => {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('FE-MAP-TRG-012: the overview draws no drive between two stays on the day a flight connects them (#2476)', () => {
|
||||
// Day 2 moves from a Munich hotel to a Hamburg one. The overview reads the same
|
||||
// day plan as the map, so the flight saved without airports keeps the road off it.
|
||||
const days = [1, 2, 3].map(n => buildDay({ id: n, day_number: n }))
|
||||
const accommodations = [
|
||||
{ id: 1, trip_id: 1, place_lat: 48.137, place_lng: 11.575, start_day_id: 1, end_day_id: 2 },
|
||||
{ id: 2, trip_id: 1, place_lat: 53.551, place_lng: 9.993, start_day_id: 2, end_day_id: 3 },
|
||||
] as unknown as Accommodation[]
|
||||
const flight = {
|
||||
id: 80, trip_id: 1, type: 'flight', title: 'LH 2078', day_id: 2, end_day_id: 2,
|
||||
reservation_time: '2026-11-04T15:15', reservation_end_time: '2026-11-04T17:20', endpoints: [],
|
||||
} as unknown as Reservation
|
||||
const moving = { days, assignments: {}, accommodations, optimizeFromAccommodation: true }
|
||||
|
||||
expect(planTripRoute({ ...moving, reservations: [flight] }, 'driving')).toEqual([])
|
||||
// Without the booking the day is a drive from one hotel to the other, as before (#1297).
|
||||
const byRoad = planTripRoute({ ...moving, reservations: [] }, 'driving')
|
||||
expect(byRoad.map(d => d.day.id)).toEqual([2])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user