mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
fix(planner): a place shows every booking linked to it, not just the first (#2201)
Four render sites resolved the day assignment's booking with find(), while the booking dialog happily links several to the same stop. A shared selector in dayMerge returns them all, time-sorted and stable, and the desktop plan tab, the place inspector, the mobile timeline and the mobile place sheet render the full list; the single-booking case is unchanged pixel for pixel.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { parseTimeToMinutes, getSpanPhase, hidesOnMiddleDay, getTransportRouteEndpoints, getDisplayTimeForDay, getTransportForDay, getMergedItems } from './dayMerge'
|
||||
import { parseTimeToMinutes, getSpanPhase, hidesOnMiddleDay, getTransportRouteEndpoints, getDisplayTimeForDay, getTransportForDay, getAssignmentReservations, getMergedItems } from './dayMerge'
|
||||
|
||||
describe('parseTimeToMinutes', () => {
|
||||
it('parses HH:MM string', () => {
|
||||
@@ -199,6 +199,41 @@ describe('getTransportForDay', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAssignmentReservations', () => {
|
||||
it('returns every booking pinned to the assignment, not just the first (#2201)', () => {
|
||||
const reservations = [
|
||||
{ id: 1, assignment_id: 42, reservation_time: '2025-06-01T10:00:00' },
|
||||
{ id: 2, assignment_id: 42, reservation_time: '2025-06-01T09:00:00' },
|
||||
{ id: 3, assignment_id: 7, reservation_time: '2025-06-01T08:00:00' },
|
||||
]
|
||||
expect(getAssignmentReservations(reservations, 42).map(r => r.id)).toEqual([2, 1])
|
||||
})
|
||||
|
||||
it('puts untimed bookings last and breaks ties on the id', () => {
|
||||
const reservations = [
|
||||
{ id: 5, assignment_id: 42, reservation_time: null },
|
||||
{ id: 4, assignment_id: 42, reservation_time: null },
|
||||
{ id: 6, assignment_id: 42, reservation_time: '2025-06-01T09:00:00' },
|
||||
]
|
||||
expect(getAssignmentReservations(reservations, 42).map(r => r.id)).toEqual([6, 4, 5])
|
||||
})
|
||||
|
||||
it('returns nothing without an assignment', () => {
|
||||
const reservations = [{ id: 1, assignment_id: 42, reservation_time: null }]
|
||||
expect(getAssignmentReservations(reservations, null)).toEqual([])
|
||||
expect(getAssignmentReservations(reservations, undefined)).toEqual([])
|
||||
})
|
||||
|
||||
it('leaves the caller array untouched', () => {
|
||||
const reservations = [
|
||||
{ id: 1, assignment_id: 42, reservation_time: '2025-06-01T10:00:00' },
|
||||
{ id: 2, assignment_id: 42, reservation_time: '2025-06-01T09:00:00' },
|
||||
]
|
||||
getAssignmentReservations(reservations, 42)
|
||||
expect(reservations.map(r => r.id)).toEqual([1, 2])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getMergedItems', () => {
|
||||
it('merges places and notes sorted by sortKey', () => {
|
||||
const dayAssignments = [
|
||||
|
||||
@@ -235,6 +235,36 @@ export function getTransportForDay(opts: {
|
||||
}).flatMap(r => expandFlightLegsForDay(r, dayId, getDayOrder, days))
|
||||
}
|
||||
|
||||
/**
|
||||
* Every booking pinned to one day assignment. A stop can carry several (the parking
|
||||
* pass and the tickets for the same zoo), and the exclusion above keeps all of them
|
||||
* out of the timeline, so the place row is the only surface that can show them, and
|
||||
* a `find()` there dropped the rest without trace (#2201).
|
||||
*
|
||||
* Earliest first, untimed last, id as the tiebreaker: the store's array order is
|
||||
* newest-first for locally created bookings and load order otherwise, which would
|
||||
* shuffle the same two bookings between a reload and a live update.
|
||||
*/
|
||||
export function getAssignmentReservations<T extends {
|
||||
id: number
|
||||
assignment_id?: number | null
|
||||
reservation_time?: string | null
|
||||
}>(reservations: T[], assignmentId: number | null | undefined): T[] {
|
||||
if (!assignmentId) return []
|
||||
return reservations
|
||||
.filter(r => r.assignment_id === assignmentId)
|
||||
.sort((a, b) => {
|
||||
const at = a.reservation_time || ''
|
||||
const bt = b.reservation_time || ''
|
||||
if (at !== bt) {
|
||||
if (!at) return 1
|
||||
if (!bt) return -1
|
||||
return at < bt ? -1 : 1
|
||||
}
|
||||
return a.id - b.id
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Order items chronologically: anything with a time (a place's place_time, a
|
||||
* transport/leg display time, a timed note) sorts by that time. An item WITHOUT a
|
||||
|
||||
Reference in New Issue
Block a user