diff --git a/client/scripts/eslint-baseline.json b/client/scripts/eslint-baseline.json index 1788f48cd..6b322f5f2 100644 --- a/client/scripts/eslint-baseline.json +++ b/client/scripts/eslint-baseline.json @@ -1,10 +1,10 @@ { "src": { - "@typescript-eslint/no-non-null-assertion": 407, + "@typescript-eslint/no-non-null-assertion": 406, "@typescript-eslint/no-misused-promises": 876, "no-console": 88, "complexity": 231, - "@typescript-eslint/no-explicit-any": 154, + "@typescript-eslint/no-explicit-any": 149, "trek/no-swallowed-catch": 135, "max-params": 35, "trek/store-without-selector": 69, diff --git a/client/scripts/size-baseline.json b/client/scripts/size-baseline.json index 40fb14cc5..0686ec8b8 100644 --- a/client/scripts/size-baseline.json +++ b/client/scripts/size-baseline.json @@ -12,7 +12,7 @@ "src/components/PDF/TripPDF.tsx": 1097, "src/components/Planner/DayDetailPanel.test.tsx": 2333, "src/components/Planner/DayPlanSidebar.test.tsx": 5381, - "src/components/Planner/DayPlanSidebar.tsx": 3052, + "src/components/Planner/DayPlanSidebar.tsx": 3001, "src/components/Planner/PlaceFormModal.test.tsx": 2864, "src/components/Planner/PlaceFormModal.tsx": 1250, "src/components/Planner/PlaceInspector.tsx": 1308, diff --git a/client/scripts/theme-disable-baseline.json b/client/scripts/theme-disable-baseline.json index de8d283c1..0a39028fa 100644 --- a/client/scripts/theme-disable-baseline.json +++ b/client/scripts/theme-disable-baseline.json @@ -30,7 +30,6 @@ "components/Roadtrip/RoadtripCategoryPicker.tsx": 2, "components/Roadtrip/RoadtripCorridorPanel.tsx": 3, "components/Roadtrip/RoadtripSidebarStopRows.tsx": 4, - "components/Roadtrip/RoadtripStopPopup.tsx": 1, "components/Roadtrip/RoadtripTrackModal.tsx": 1, "components/Roadtrip/StopKindPicker.tsx": 2, "components/Roadtrip/dayColors.ts": 8, diff --git a/client/src/components/Planner/DayHeadPills.tsx b/client/src/components/Planner/DayHeadPills.tsx new file mode 100644 index 000000000..07663902f --- /dev/null +++ b/client/src/components/Planner/DayHeadPills.tsx @@ -0,0 +1,112 @@ +import { Car, Hotel, Ticket } from 'lucide-react'; +import React from 'react'; +import { useTranslation } from '../../i18n'; +import type { Accommodation, Reservation } from '../../types'; +import { fs } from '../shared/DialogShell'; +import { Tooltip } from '../shared/Tooltip'; +import { SoftPill } from './planParts'; + +/** The second line of a day head: its stays, its hire cars and a plugin's name for the day. */ +export function DayHeadPills({ + dayId, + stays, + rentals, + pendingStays, + reservations, + label, + onPlaceClick, + onOpenBooking, + onOpenRental, +}: { + dayId: number; + stays: Accommodation[]; + rentals: Reservation[]; + pendingStays: Set; + reservations: Reservation[]; + label?: string | null; + onPlaceClick: (placeId: number) => void; + onOpenBooking?: (reservation: Reservation) => void; + onOpenRental: (reservation: Reservation) => void; +}): React.ReactElement { + const { t } = useTranslation(); + return ( +
+ {stays.map((acc) => { + const isCheckIn = acc.start_day_id === dayId; + const isCheckOut = acc.end_day_id === dayId; + const accName = acc.place_name || acc.reservation_title || ''; + // The state is said in words as well as colour: check-in green, + // check-out red, a stay in between quiet. + const state = isCheckOut && !isCheckIn ? t('day.checkOut') : isCheckIn ? t('day.checkIn') : null; + const iconTone = isCheckOut && !isCheckIn ? 'text-danger' : isCheckIn ? 'text-success' : 'text-content-faint'; + // Not confirmed yet: a dashed ring says so at a glance, the tooltip in words (#2281). + const pending = pendingStays.has(acc.id); + const named = state ? `${state}: ${accName}` : accName; + const tip = pending ? `${named} (${t('reservations.pending')})` : named; + // The name opens the place, as before; a stay with a booking gets a + // second target in the same pill that opens the booking (#2363). + const stayBooking = onOpenBooking + ? reservations.find((r) => r.accommodation_id != null && Number(r.accommodation_id) === acc.id) + : undefined; + return ( + + + + + {stayBooking && onOpenBooking && ( + + + + )} + + ); + })} + {/* Active rental car badges */} + {rentals.map((r) => ( + + ))} + {/* A plugin's name for this day (which leg of the trip it belongs to). */} + {label && {label}} +
+ ); +} diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx index c48f0d0c5..07db1adbe 100644 --- a/client/src/components/Planner/DayPlanSidebar.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tsx @@ -70,6 +70,7 @@ import { projectDayItinerary } from '../Map/dayTourProjection' import { usePluginDaySchedule, usePluginDayTints, dayTintBackground, dayTinted, PluginDayScheduleRow, formatScheduleMinutes } from '../Plugins/PluginDaySchedule' import { MobileAddPlaceButton } from './DayPlanSidebarMobileAddPlaceButton' import { DayPlanSidebarToolbar } from './DayPlanSidebarToolbar' +import { DayHeadPills } from './DayHeadPills' import { DayPlanSidebarNoteModal } from './DayPlanSidebarNoteModal' import { DayPlanSidebarTimeConfirmModal } from './DayPlanSidebarTimeConfirmModal' import { DayPlanSidebarTransportDetailModal } from './DayPlanSidebarTransportDetailModal' @@ -1735,59 +1736,9 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP {hasDayPills && ( -
- {dayAccs.map(acc => { - const isCheckIn = acc.start_day_id === day.id - const isCheckOut = acc.end_day_id === day.id - const accName = (acc as any).place_name || (acc as any).reservation_title - // The state is said in words as well as colour: check-in green, - // check-out red, a stay in between quiet. - const state = isCheckOut && !isCheckIn ? t('day.checkOut') : isCheckIn ? t('day.checkIn') : null - const iconTone = isCheckOut && !isCheckIn ? 'text-danger' : isCheckIn ? 'text-success' : 'text-content-faint' - // Not confirmed yet: a dashed ring says so at a glance, the tooltip in words (#2281). - const pending = pendingStays.has(acc.id) - const named = state ? `${state}: ${accName}` : accName - const tip = pending ? `${named} (${t('reservations.pending')})` : named - // The name opens the place, as before; a stay with a booking gets a - // second target in the same pill that opens the booking (#2363). - const stayBooking = onOpenBooking - ? reservations.find(r => r.accommodation_id != null && Number(r.accommodation_id) === acc.id) - : undefined - return ( - - - - - {stayBooking && ( - - - - )} - - ) - })} - {/* Active rental car badges */} - {activeRentals.map(r => ( - - ))} - {/* A plugin's name for this day (which leg of the trip it belongs to). */} - {dayTint?.label && {dayTint.label}} -
+ { if (onOpenBooking) onOpenBooking(r); else setTransportDetail(r) }} /> )}