feat(map): add reservation route-visibility util

Extracts the route-visibility filter that was hand-copied, identically,
in both MapView.tsx and MapViewGL.tsx into one pure, unit-tested
function: a reservation's route shows on the map when it's a transit
booking with the day-route toggle on, or its id is in the caller's
visible-ids set. isRoutableReservation (>= 2 endpoints) is exported
separately since callers besides the map filter need the same check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Dieter Blomme
2026-07-07 14:22:36 +02:00
committed by Maurice
parent 47b659873e
commit dfb93f21d4
2 changed files with 79 additions and 0 deletions
@@ -0,0 +1,56 @@
import { describe, it, expect } from 'vitest'
import { isRoutableReservation, visibleRouteReservations } from './reservationRoutes'
import type { Reservation, ReservationEndpoint } from '../types'
function endpoint(role: 'from' | 'to', lat: number, lng: number): ReservationEndpoint {
return { role, sequence: role === 'from' ? 0 : 1, name: role, code: null, lat, lng, timezone: null, local_time: null, local_date: null }
}
function reservation(overrides: Partial<Reservation> = {}): Reservation {
return {
id: 1, trip_id: 1, title: 'Flight', type: 'flight', status: 'confirmed',
reservation_time: null, reservation_end_time: null, location: null,
confirmation_number: null, notes: null, url: null,
...overrides,
} as Reservation
}
describe('isRoutableReservation', () => {
it('is false with no endpoints', () => {
expect(isRoutableReservation(reservation())).toBe(false)
})
it('is false with a single endpoint', () => {
expect(isRoutableReservation(reservation({ endpoints: [endpoint('from', 1, 2)] }))).toBe(false)
})
it('is true with 2+ endpoints', () => {
expect(isRoutableReservation(reservation({ endpoints: [endpoint('from', 1, 2), endpoint('to', 3, 4)] }))).toBe(true)
})
})
describe('visibleRouteReservations', () => {
const twoStop = [endpoint('from', 1, 2), endpoint('to', 3, 4)]
it('includes a transit reservation only when showTransitRoutes is on', () => {
const r = reservation({ id: 1, type: 'transit', endpoints: twoStop })
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: false })).toEqual([])
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: true })).toEqual([r])
})
it('includes a reservation whose id is in visibleConnectionIds regardless of type', () => {
const r = reservation({ id: 5, type: 'flight', endpoints: twoStop })
expect(visibleRouteReservations([r], { visibleConnectionIds: [5], showTransitRoutes: false })).toEqual([r])
})
it('excludes a routable reservation when neither rule applies', () => {
const r = reservation({ id: 7, type: 'flight', endpoints: twoStop })
expect(visibleRouteReservations([r], { visibleConnectionIds: [], showTransitRoutes: false })).toEqual([])
})
it('does not duplicate a reservation matched by both rules', () => {
const r = reservation({ id: 10, type: 'transit', endpoints: twoStop })
const result = visibleRouteReservations([r], { visibleConnectionIds: [10], showTransitRoutes: true })
expect(result).toEqual([r])
})
})
+23
View File
@@ -0,0 +1,23 @@
import type { Reservation } from '../types'
/** A reservation is routable on the map once it has at least two ordered endpoints (from/to/stop). */
export function isRoutableReservation(r: Pick<Reservation, 'endpoints'>): boolean {
return (r.endpoints || []).length >= 2
}
export interface RouteVisibilityOptions {
/** Reservation ids resolved as currently visible for this trip (per-item toggle, bulk toggle, or the account-wide default — see connectionsVisibility.ts). */
visibleConnectionIds: number[]
/** The separate manual day-route-calculator toggle — 'transit' reservations ride it (#1065). */
showTransitRoutes: boolean
}
/** Which reservations should draw a route on the map, combining the two independent toggles above. */
export function visibleRouteReservations(reservations: Reservation[], options: RouteVisibilityOptions): Reservation[] {
const { visibleConnectionIds, showTransitRoutes } = options
const set = new Set(visibleConnectionIds || [])
return reservations.filter(r =>
(r.type === 'transit' && showTransitRoutes) ||
set.has(r.id)
)
}