- )}
- {res.reservation_time?.includes('T') && (
-
- )}
+ {(() => {
+ const { date, time: startTime } = splitReservationDateTime(res.reservation_time)
+ const { time: endTime } = splitReservationDateTime(res.reservation_end_time)
+ return (
+ <>
+ {date && (
+
+ )}
+ {(startTime || endTime) && (
+
+ )}
+ >
+ )
+ })()}
{res.confirmation_number && (
{t('reservations.confirmationCode')}
diff --git a/client/src/components/Planner/ReservationsPanel.test.tsx b/client/src/components/Planner/ReservationsPanel.test.tsx
index 2dcd9c86..0f9e01a6 100644
--- a/client/src/components/Planner/ReservationsPanel.test.tsx
+++ b/client/src/components/Planner/ReservationsPanel.test.tsx
@@ -389,4 +389,51 @@ describe('ReservationsPanel', () => {
expect(screen.getByText('Pending 2')).toBeInTheDocument();
expect(screen.getByText('Pending 3')).toBeInTheDocument();
});
+
+ it('FE-PLANNER-RESP-041: dateless transport with legacy T-prefix shows time without "Invalid Date"', () => {
+ const day = buildDay({ date: null, day_number: 25 } as any);
+ const r = buildReservation({
+ title: 'Cruise test',
+ type: 'cruise',
+ status: 'pending',
+ reservation_time: 'T10:00',
+ reservation_end_time: 'T18:00',
+ day_id: day.id,
+ end_day_id: day.id,
+ } as any);
+ render(
);
+ expect(screen.queryByText(/Invalid Date/)).not.toBeInTheDocument();
+ expect(screen.getByText(/10:00/)).toBeInTheDocument();
+ });
+
+ it('FE-PLANNER-RESP-042: dateless transport with bare time format shows time without "Invalid Date"', () => {
+ const day = buildDay({ date: null, day_number: 3 } as any);
+ const r = buildReservation({
+ title: 'Car rental',
+ type: 'car',
+ status: 'pending',
+ reservation_time: '09:00',
+ reservation_end_time: '17:00',
+ day_id: day.id,
+ end_day_id: day.id,
+ } as any);
+ render(
);
+ expect(screen.queryByText(/Invalid Date/)).not.toBeInTheDocument();
+ expect(screen.getByText(/09:00/)).toBeInTheDocument();
+ });
+
+ it('FE-PLANNER-RESP-043: dated transport still shows date and time correctly', () => {
+ const day = buildDay({ date: '2026-07-15', day_number: 1 });
+ const r = buildReservation({
+ title: 'Flight out',
+ type: 'flight',
+ status: 'confirmed',
+ reservation_time: '2026-07-15T08:30',
+ reservation_end_time: '2026-07-15T10:45',
+ day_id: day.id,
+ } as any);
+ render(
);
+ expect(screen.queryByText(/Invalid Date/)).not.toBeInTheDocument();
+ expect(screen.getByText(/08:30/)).toBeInTheDocument();
+ });
});
diff --git a/client/src/components/Planner/ReservationsPanel.tsx b/client/src/components/Planner/ReservationsPanel.tsx
index 7dc1a686..a341cc21 100644
--- a/client/src/components/Planner/ReservationsPanel.tsx
+++ b/client/src/components/Planner/ReservationsPanel.tsx
@@ -15,6 +15,7 @@ import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
import type { Reservation, Day, TripFile, AssignmentsMap } from '../../types'
+import { splitReservationDateTime, formatTime } from '../../utils/formatters'
interface AssignmentLookupEntry {
dayNumber: number
@@ -99,17 +100,13 @@ function ReservationCard({ r, tripId, onEdit, onDelete, files = [], onNavigateTo
}
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768
- const fmtDate = (str) => {
- const dateOnly = str.includes('T') ? str.split('T')[0] : str
- return new Date(dateOnly + 'T00:00:00Z').toLocaleDateString(locale, { ...(isMobile ? {} : { weekday: 'short' }), day: 'numeric', month: 'short', timeZone: 'UTC' })
- }
- const fmtTime = (str) => {
- const d = new Date(str)
- return d.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit', hour12: timeFormat === '12h' })
- }
+ const startDt = splitReservationDateTime(r.reservation_time)
+ const endDt = splitReservationDateTime(r.reservation_end_time)
+ const fmtDate = (date: string) =>
+ new Date(date + 'T00:00:00Z').toLocaleDateString(locale, { ...(isMobile ? {} : { weekday: 'short' }), day: 'numeric', month: 'short', timeZone: 'UTC' })
- const hasDate = !!r.reservation_time
- const hasTime = r.reservation_time?.includes('T')
+ const hasDate = !!startDt.date
+ const hasTime = !!(startDt.time || endDt.time)
const hasCode = !!r.confirmation_number
const dateCols = [hasDate, hasTime, hasCode].filter(Boolean).length
@@ -233,31 +230,25 @@ function ReservationCard({ r, tripId, onEdit, onDelete, files = [], onNavigateTo