mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(roadtrip): a check-in is a time somebody chose, so print it as one
The rail prints an hour in ink when a person picked it and in grey when the chain worked it out. Every scheduler anchors on `time ?? checkInTime`, so a booked night starts its day on the check-in exactly the way a pinned stop does — but two of the three then asked only about `time` when labelling the result, and printed that chosen hour grey. The answer lived in three places and two of them disagreed with the anchor they had just used, so it is one function now. Nothing but the rail reads the flag. Only visible since a booked night became a stop of its own; before that no stop carried a check-in.
This commit is contained in:
@@ -3,7 +3,7 @@ import type { RoadtripDayBoundary } from './day-boundary.schema';
|
||||
import type { SpillChain } from './nightSpill';
|
||||
import type { RoadtripDay, RoadtripStop, RoutedLeg } from './planning-types';
|
||||
import type { DistanceUnit } from './planning-types';
|
||||
import { formatClock, parseClock } from './roadtripModel';
|
||||
import { formatClock, hasChosenArrival, parseClock } from './roadtripModel';
|
||||
import { formatDurationShort } from './roadtripModel';
|
||||
import { formatDistance } from './units';
|
||||
|
||||
@@ -168,7 +168,7 @@ export function planDayWindow(
|
||||
chain.schedule.entries.push({
|
||||
arrival: formatClock(Math.round(arrival)),
|
||||
departure: formatClock(Math.round(departure)),
|
||||
anchored: !stop.automaticNight && stop.time !== null,
|
||||
anchored: hasChosenArrival(stop),
|
||||
dayOffset: Math.floor(arrival / 1440),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { QuietDay, RoadtripDay, RoadtripStop } from './planning-types';
|
||||
import type { RouteSegment } from './planning-types';
|
||||
import { computeSchedule, parseClock, type Schedule } from './roadtripModel';
|
||||
import { computeSchedule, parseClock, type Schedule, hasChosenArrival } from './roadtripModel';
|
||||
|
||||
export interface SpillMark {
|
||||
automatic?: boolean;
|
||||
@@ -147,7 +147,7 @@ export function spillChains(plan: PlanDay[], quietDays: QuietDay[], legFor: LegL
|
||||
entries: placed.map((p) => ({
|
||||
arrival: p.arrival,
|
||||
departure: p.departure,
|
||||
anchored: p.stop.time !== null && p.stop.time !== undefined,
|
||||
anchored: hasChosenArrival(p.stop),
|
||||
dayOffset: p.dayOffset,
|
||||
})),
|
||||
warnings: placed.flatMap((p, i) =>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
computeSchedule,
|
||||
formatClock,
|
||||
hasChosenArrival,
|
||||
formatDurationShort,
|
||||
insertIndexForAlong,
|
||||
deriveDriveWarnings,
|
||||
@@ -822,3 +823,27 @@ it('uses checkout as departure even without an arrival or daily start', () => {
|
||||
expect(schedule.entries[2]!.arrival).toBe('10:00');
|
||||
expect(schedule.entries[2]!.dayOffset).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
describe('hasChosenArrival', () => {
|
||||
// The rail prints a chosen hour in ink and a computed one in grey. Every scheduler
|
||||
// anchors on `time ?? checkInTime`, so a booked night's check-in is as chosen as a
|
||||
// pinned stop; two of the three used to ask only about `time` and printed it grey.
|
||||
it('counts a pinned time', () => {
|
||||
expect(hasChosenArrival({ time: '09:00' })).toBe(true);
|
||||
});
|
||||
|
||||
it('counts a check-in, which is the hour a booked night starts its day on', () => {
|
||||
expect(hasChosenArrival({ time: null, checkInTime: '08:00' })).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves a stop the chain worked out alone', () => {
|
||||
expect(hasChosenArrival({ time: null, checkInTime: null })).toBe(false);
|
||||
expect(hasChosenArrival({})).toBe(false);
|
||||
});
|
||||
|
||||
it('never counts a night the planner inserted itself', () => {
|
||||
// Its clock comes from the driving limits, not from anybody's decision.
|
||||
expect(hasChosenArrival({ time: '22:00', automaticNight: { phase: 'end' } })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,22 @@ export function formatDurationShort(seconds: number): string {
|
||||
return `${m} min`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this stop's arrival is a time somebody chose, rather than one the chain
|
||||
* worked out from the stop before it.
|
||||
*
|
||||
* A check-in counts. Every schedule already anchors on `time ?? checkInTime`, so a
|
||||
* booked night starts its day exactly like a pinned stop does; asking only about
|
||||
* `time` here made the rail print that chosen hour in the grey it reserves for
|
||||
* computed ones. One function because the answer was spelled out separately in each
|
||||
* of the three schedulers, and two of them spelled it differently from the anchor
|
||||
* they had just used.
|
||||
*/
|
||||
export function hasChosenArrival(stop: { time?: string | null; checkInTime?: string | null; automaticNight?: unknown }): boolean {
|
||||
if (stop.automaticNight) return false;
|
||||
return (stop.time ?? null) !== null || (stop.checkInTime ?? null) !== null;
|
||||
}
|
||||
|
||||
export function parseClock(value: string | null | undefined): number | null {
|
||||
if (!value) return null;
|
||||
const m = /^(\d{1,2}):(\d{2})/.exec(value.trim());
|
||||
|
||||
Reference in New Issue
Block a user