From 91be785fbca0b70205ca6ec907ad1ecfd6d29a98 Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 29 Sep 2026 21:16:11 +0200 Subject: [PATCH] fix(planner): a leg's time zone sits behind a globe instead of beside the route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A departure or arrival row wrote its zone out ("Europe/Berlin") next to the phase label, and on the narrow day plan that ate the width the route needed, so "BER → HAM" ended in an ellipsis. The zone is now a small globe after the time that names it on hover, the time pill carries only the time, and the route gets its room back. --- .../Planner/DayPlanSidebar.test.tsx | 22 +++++++++++++++++++ .../src/components/Planner/DayPlanSidebar.tsx | 17 ++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/client/src/components/Planner/DayPlanSidebar.test.tsx b/client/src/components/Planner/DayPlanSidebar.test.tsx index 91e895ee2..c127d57dd 100644 --- a/client/src/components/Planner/DayPlanSidebar.test.tsx +++ b/client/src/components/Planner/DayPlanSidebar.test.tsx @@ -1040,6 +1040,28 @@ describe('DayPlanSidebar', () => { // ── Multi-day transport span phases ──────────────────────────────────── + it('FE-PLANNER-DAYPLAN-233: a leg names its time zone behind a globe, not as text beside the route', async () => { + const day1 = buildDay({ id: 10, date: '2025-06-01', title: 'Out' }) + const day2 = buildDay({ id: 11, date: '2025-06-02', title: 'In' }) + const flight = buildReservation({ + id: 202, type: 'flight', title: 'Overnight', + reservation_time: '2025-06-01T22:00:00', + reservation_end_time: '2025-06-02T06:00:00', + day_id: 10, + end_day_id: 11, + metadata: JSON.stringify({ departure_timezone: 'Europe/Berlin', arrival_timezone: 'America/New_York' }), + } as any) + const { container } = render() + + // Written out, the zone took the width the route needed. + expect(screen.queryByText(/Europe\/Berlin|America\/New_York/)).not.toBeInTheDocument() + const globes = container.querySelectorAll('[data-dp="transport-timezone"]') + expect([...globes].map(g => g.getAttribute('aria-label'))).toEqual(['Europe/Berlin', 'America/New_York']) + + fireEvent.mouseEnter(globes[0]) + expect(await screen.findByText('Europe/Berlin')).toBeInTheDocument() + }) + it('FE-PLANNER-DAYPLAN-045: multi-day flight shows departure label on first day', () => { const day1 = buildDay({ id: 10, date: '2025-06-01', title: 'Departure' }) const day2 = buildDay({ id: 11, date: '2025-06-02', title: 'Arrival' }) diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx index 7f359cf39..16ef1d8f7 100644 --- a/client/src/components/Planner/DayPlanSidebar.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tsx @@ -7,7 +7,7 @@ declare global { interface Window { __dragData: DragDataPayload | null } } import React, { useState, useEffect, useLayoutEffect, useRef, useMemo, useCallback } from 'react' import { avatarSrc } from '../../utils/avatarSrc' import { safeHttpUrl } from '../../utils/safeUrl' -import { ChevronDown, ChevronRight, ChevronUp, Compass, Navigation, RotateCcw, ExternalLink, Pencil, GripVertical, Ticket, Plus, FileText, Trash2, Car, Lock, Hotel, Footprints, Route as RouteIcon, Bookmark, StickyNote, TramFront, Zap, MapPin, Wallet } from 'lucide-react' +import { ChevronDown, ChevronRight, ChevronUp, Compass, Navigation, RotateCcw, ExternalLink, Pencil, GripVertical, Ticket, Plus, FileText, Trash2, Car, Lock, Hotel, Footprints, Route as RouteIcon, Bookmark, StickyNote, TramFront, Zap, MapPin, Wallet, Globe } from 'lucide-react' import { type PickedPlace } from './TransitSearchPanel' import { buildTransitLeg, buildTransitNameIndex } from './transitLeg' import { assignmentsApi, reservationsApi, daysApi } from '../../api/client' @@ -2559,15 +2559,24 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP const timeText = [ dispTime ? formatTime(dispTime, locale, timeFormat) : '', spanPhase === 'single' && endTime ? `– ${formatTime(endTime, locale, timeFormat)}` : '', - meta.departure_timezone && spanPhase === 'start' ? meta.departure_timezone : '', - meta.arrival_timezone && spanPhase === 'end' ? meta.arrival_timezone : '', ].filter(Boolean).join(' ') - if (!timeText && !spanLabel && !transitMeta && !subtitle) return null + // The zone a leg's time is read in. Written out it took the width + // the route needs, so it is a globe that names it on hover. + const timeZone = (spanPhase === 'start' && meta.departure_timezone) || (spanPhase === 'end' && meta.arrival_timezone) || '' + if (!timeText && !timeZone && !spanLabel && !transitMeta && !subtitle) return null return (
{/* Where in a multi-day span this day falls, said quietly beside its time. */} {spanLabel && {spanLabel}} {timeText && {timeText}} + {timeZone && ( + + + + + )} {transitMeta ? ( ) : subtitle && (