openIncomingLegModeMenu(e, Number(nextPlaceId), day.id)} style={{ cursor: 'pointer' }}>
+
openIncomingLegModeMenu(e, Number(nextPlaceId), day.id, routeLegs[day.id]![res.id])} style={{ cursor: 'pointer' }}>
{connector}
) : connector
@@ -2385,7 +2438,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP
const targetId = hotelLegs[day.id]?.bottom?.targetId
const connector =
return canEditDays && targetId != null ? (
-
openLegModeMenu(e, targetId, day.id)} style={{ cursor: 'pointer' }}>
+
openLegModeMenu(e, targetId, day.id, hotelLegs[day.id]!.bottom!.seg)} style={{ cursor: 'pointer' }}>
{connector}
) : connector
diff --git a/client/src/components/Planner/TransitSearchPanel.test.tsx b/client/src/components/Planner/TransitSearchPanel.test.tsx
index 3f4bdde6c..6d0b66cb2 100644
--- a/client/src/components/Planner/TransitSearchPanel.test.tsx
+++ b/client/src/components/Planner/TransitSearchPanel.test.tsx
@@ -177,6 +177,20 @@ describe('TransitSearchPanel', () => {
expect(transitApiMock.plan.mock.calls[0][0]).toMatchObject({ arriveBy: true, time: '2025-06-01T08:00:00.000Z' })
})
+ it('FE-PLANNER-TRANSIT-008b: initialTime seeds the departure time', async () => {
+ const user = userEvent.setup()
+ transitApiMock.plan.mockResolvedValueOnce({ itineraries: [] })
+ render(
)
+ await user.click(screen.getByRole('button', { name: /^Search$/ }))
+ await waitFor(() => expect(transitApiMock.plan).toHaveBeenCalled())
+ // 11:00 in Berlin (UTC+2) anchors depart-by to the origin zone → 09:00Z.
+ expect(transitApiMock.plan.mock.calls[0][0]).toMatchObject({ time: '2025-06-01T09:00:00.000Z' })
+ })
+
it('FE-PLANNER-TRANSIT-009: arrive-by lists the latest-arriving itinerary first', async () => {
const user = userEvent.setup()
// MOTIS returns arrive-by results ascending, deadline-adjacent last (#1479).
diff --git a/client/src/components/Planner/TransitSearchPanel.tsx b/client/src/components/Planner/TransitSearchPanel.tsx
index 11faf09b8..dd8478b67 100644
--- a/client/src/components/Planner/TransitSearchPanel.tsx
+++ b/client/src/components/Planner/TransitSearchPanel.tsx
@@ -318,9 +318,11 @@ interface TransitSearchPanelProps {
/** Pre-seed from/to — used by "change route" on an existing journey. */
initialFrom?: PickedPlace | null
initialTo?: PickedPlace | null
+ /** Pre-seed the departure time ('HH:mm') — used when planning a specific leg. */
+ initialTime?: string | null
}
-export default function TransitSearchPanel({ day, days, places, accommodations = [], onAdd, initialFrom = null, initialTo = null }: TransitSearchPanelProps) {
+export default function TransitSearchPanel({ day, days, places, accommodations = [], onAdd, initialFrom = null, initialTo = null, initialTime = null }: TransitSearchPanelProps) {
const { t } = useTranslation()
const toast = useToast()
const is12h = useSettingsStore(s => s.settings.time_format) === '12h'
@@ -328,7 +330,7 @@ export default function TransitSearchPanel({ day, days, places, accommodations =
const [from, setFrom] = useState
(initialFrom)
const [to, setTo] = useState(initialTo)
- const [time, setTime] = useState('09:00')
+ const [time, setTime] = useState(initialTime || '09:00')
const [arriveBy, setArriveBy] = useState(false)
const [activeModes, setActiveModes] = useState>(() => new Set(MODE_GROUPS.map(m => m.key)))
const [pref, setPref] = useState<'best' | 'transfers' | 'walking'>('best')
diff --git a/client/src/components/Planner/TransportModal.tsx b/client/src/components/Planner/TransportModal.tsx
index 6ccc3b175..1d6656acf 100644
--- a/client/src/components/Planner/TransportModal.tsx
+++ b/client/src/components/Planner/TransportModal.tsx
@@ -160,8 +160,8 @@ interface TransportModalProps {
initialAutomated?: boolean
/** Transit search needs real dates to depart on, so the Automated mode is hidden on a dateless trip. */
tripHasDates?: boolean
- /** Pre-seed the transit search — used by "change route" on an existing journey. */
- transitPrefill?: { from?: PickedPlace | null; to?: PickedPlace | null } | null
+ /** Pre-seed the transit search — used by "change route" and by per-leg planning. */
+ transitPrefill?: { from?: PickedPlace | null; to?: PickedPlace | null; time?: string | null } | null
/** Trip members + guests, for the traveler picker (#1517). */
tripMembers?: TripMember[]
}
@@ -701,6 +701,7 @@ export function TransportModal({ isOpen, onClose, onSave, reservation, days, sel
onAdd={(payload) => onSave(payload as Record & { title: string })}
initialFrom={transitPrefill?.from ?? null}
initialTo={transitPrefill?.to ?? null}
+ initialTime={transitPrefill?.time ?? null}
/>
)
})()}
diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx
index 814213cce..5c95091d4 100644
--- a/client/src/pages/TripPlannerPage.tsx
+++ b/client/src/pages/TripPlannerPage.tsx
@@ -459,6 +459,7 @@ function TripPlannerPageDesktop(): React.ReactElement | null {
onAddTransport={can('day_edit', trip) ? (dayId) => { setTransportModalDayId(dayId); setEditingTransport(null); setTransitPrefill(null); setTransportModalAutomated(false); setShowTransportModal(true) } : undefined}
onOpenTransit={(r) => setTransitJourney(r)}
onPlanTransit={can('day_edit', trip) && tripHasDates ? (dayId) => { setTransportModalDayId(dayId); setEditingTransport(null); setTransitPrefill(null); setTransportModalAutomated(true); setShowTransportModal(true) } : undefined}
+ onPlanTransitLeg={can('day_edit', trip) && tripHasDates ? ({ dayId, from, to, time }) => { setTransportModalDayId(dayId); setEditingTransport(null); setTransitPrefill({ from, to, time }); setTransportModalAutomated(true); setShowTransportModal(true) } : undefined}
onEditTransport={can('day_edit', trip) ? (reservation) => { setEditingTransport(reservation); setTransportModalDayId(reservation.day_id ?? null); setShowTransportModal(true) } : undefined}
onEditReservation={can('reservation_edit', trip) ? (r) => { setEditingReservation(r); setShowReservationModal(true) } : undefined}
onDayDetail={(day) => { setShowDayDetail(day); setSelectedPlaceId(null); selectAssignment(null) }}
diff --git a/client/src/pages/tripPlanner/useTripPlanner.ts b/client/src/pages/tripPlanner/useTripPlanner.ts
index 4e920677d..ef59b9519 100644
--- a/client/src/pages/tripPlanner/useTripPlanner.ts
+++ b/client/src/pages/tripPlanner/useTripPlanner.ts
@@ -241,7 +241,7 @@ export function useTripPlanner() {
// Public transit (#1065): open the TransportModal in its Automated mode, seed
// the search (change-route), and show the journey view for a saved entry.
const [transportModalAutomated, setTransportModalAutomated] = useState(false)
- const [transitPrefill, setTransitPrefill] = useState<{ from?: { name: string; lat: number; lng: number } | null; to?: { name: string; lat: number; lng: number } | null } | null>(null)
+ const [transitPrefill, setTransitPrefill] = useState<{ from?: { name: string; lat: number; lng: number } | null; to?: { name: string; lat: number; lng: number } | null; time?: string | null } | null>(null)
const [transitJourney, setTransitJourney] = useState(null)
// The bottom-nav "+" is context-aware per tab: on the Bookings / Transports tabs