diff --git a/client/public/images/portals/check24.png b/client/public/images/portals/check24.png
new file mode 100644
index 000000000..ef931c5da
Binary files /dev/null and b/client/public/images/portals/check24.png differ
diff --git a/client/public/images/portals/pincamp.png b/client/public/images/portals/pincamp.png
new file mode 100644
index 000000000..d878e863a
Binary files /dev/null and b/client/public/images/portals/pincamp.png differ
diff --git a/client/public/images/portals/pitchup.ico b/client/public/images/portals/pitchup.ico
new file mode 100644
index 000000000..24b7881cd
Binary files /dev/null and b/client/public/images/portals/pitchup.ico differ
diff --git a/client/public/images/portals/trivago.png b/client/public/images/portals/trivago.png
new file mode 100644
index 000000000..1eb2abb50
Binary files /dev/null and b/client/public/images/portals/trivago.png differ
diff --git a/client/src/components/Map/MapView.tsx b/client/src/components/Map/MapView.tsx
index 35fabe83d..0e222ef27 100644
--- a/client/src/components/Map/MapView.tsx
+++ b/client/src/components/Map/MapView.tsx
@@ -1196,7 +1196,7 @@ export const MapView = memo(function MapView({
{/* 20px off the sidebar, not 12: the pill is round and frosted, so at the
smaller gap its shadow ran into the sidebar edge and the two read as one
surface. */}
-
: null
const provider = useSettingsStore(s => s.settings.map_provider)
const token = useSettingsStore(s => s.settings.mapbox_access_token)
// Fall back to Leaflet when Mapbox is selected but no token is set,
@@ -61,12 +40,12 @@ export function MapViewAuto(props: any) {
// to Leaflet keeps a usable map instead of an error card.
// resetKeys: with two engine chunks, a failure under one provider must not
// keep showing Leaflet after the user switches to the other.
- <>}>
+ }>
}>
- {status}>
+
)
}
- return <>{status}>
+ return
}
diff --git a/client/src/components/Map/MapViewGL.tsx b/client/src/components/Map/MapViewGL.tsx
index e4dcf25bf..1295c4e22 100644
--- a/client/src/components/Map/MapViewGL.tsx
+++ b/client/src/components/Map/MapViewGL.tsx
@@ -2299,7 +2299,8 @@ export function MapViewGL({
does, which is why the lift is only there. */}
diff --git a/client/src/components/Roadtrip/RoadtripSidebar.tsx b/client/src/components/Roadtrip/RoadtripSidebar.tsx
index b8bdf0ef6..aee4d1067 100644
--- a/client/src/components/Roadtrip/RoadtripSidebar.tsx
+++ b/client/src/components/Roadtrip/RoadtripSidebar.tsx
@@ -837,7 +837,7 @@ function ServiceStop({ stop, entry, late, driveFindings, selected, onSelect, onE
className="flex min-w-0 items-center gap-2 font-semibold leading-6 tracking-[-0.012em] text-content-secondary"
style={{ fontSize: FS.name }}
>
- {stop.name}{stop.stopType === 'charging' && }
+ {stop.name}{stop.stopType === 'charging' && }
@@ -959,6 +959,17 @@ function DriveFindingBadge({ warning }: { warning: ScheduleWarning }): React.Rea
* the state the whole view gets opened for. It is also the only filled accent left in the
* rail: selection moved to the row's own background, so nothing else competes with it.
*/
+function CheckoutDeparture({ stop, entry }: { stop: RoadtripStop; entry?: ScheduleEntry }): React.ReactElement | null {
+ const { t } = useTranslation()
+ const is12h = useSettingsStore(s => s.settings.time_format) === '12h'
+ if (stop.checkoutAt === undefined || !entry?.departure) return null
+ return
+
+ {formatClockTime(entry.departure, is12h)}
+
+
+}
+
function Arrival({ entry }: { entry: ScheduleEntry }): React.ReactElement {
const { t } = useTranslation()
const is12h = useSettingsStore(s => s.settings.time_format) === '12h'
@@ -1094,7 +1105,7 @@ function Stop({ stop, number, entry, late, driveFindings, selected, continues, s
className="flex min-w-0 items-center gap-2 font-semibold leading-6 tracking-[-0.012em] text-content"
style={{ fontSize: FS.name }}
>
- {stop.name}{stop.stopType === 'charging' && }
+ {stop.name}{stop.stopType === 'charging' && }
{/* Two halves under one border: the word says what the number means, so the
number needs no unit of explanation beside it. */}
@@ -1257,7 +1268,7 @@ function SpillBlock({ spill, children }: {
The stop it leaves from is not named — it is drawn on yesterday's card
directly above, and a second row for it would read as a stop made
twice. */}
- {departure ? (
+ {departure && spill.fromStop?.checkoutAt === undefined ? (
!!s.roadtrip_day_colors)
const tint = dayColorsOn ? dayColor(day.dayNumber).line : null
const last = day.stops.length - 1
- /**
- * The findings a stop wears, minus the one the refuel band already says better.
- *
- * The range warning marks the stop somebody FINDS OUT at and counts the whole tank;
- * the band sits on the leg the fuel actually ends on and offers somewhere to stop. Both
- * at once says the same thing twice, in the wrong order — the answer above the problem
- * — so the warning stands down wherever the band is showing.
- */
- const showingBand = !!refuel && !loading
const findingsFor = (i: number) =>
- day.driveWarnings.filter(w => w.index === i && !(showingBand && w.code === 'range'))
+ day.driveWarnings.filter(w => w.index === i && w.code !== 'range')
// The running number a stop wears, with the service stops passed over — so a day with a
// charger halfway through still counts one, two, three the way its map pins do.
let counted = 0
@@ -1353,7 +1355,7 @@ function DaySection({ day, selectedAssignmentId, onSelectStop, onReorderStop, on
* `stop.ownerIndex` instead, because a stop driven onto this date through the night is
* still stored on the day it set off from (`nightSpill.ts`).
*/
- const renderStop = (stop: RoadtripStop, i: number): React.ReactElement => {
+ const renderStopContent = (stop: RoadtripStop, i: number): React.ReactElement => {
if (stop.automaticNight) return (
@@ -1482,6 +1484,18 @@ function DaySection({ day, selectedAssignmentId, onSelectStop, onReorderStop, on
* Built rather than mapped straight through, because a spill is drawn inside a block of
* its own and a block cannot be opened halfway down a `map`.
*/
+ const renderStop = (stop: RoadtripStop, i: number): React.ReactElement => (
+
+ {refuel && !loading ? (day.dryPoints ?? []).filter(dry => dry.legIndex === -i - 1).map(dry => (
+
- {/* What OSM knows about the house, which is the whole of what we offer: no
- price, no availability, no link into a booking portal. TREK links to maps
- and to the place's own site, never to a vendor. */}
{websiteHref || draft.poi.phone ? (
{websiteHref ? (
@@ -258,7 +251,7 @@ export default function RoadtripStopPopup({
type="button"
aria-pressed={on}
onClick={() => {
- setStopType(on ? null : key)
+ setStopType(key)
// Picking a kind is also picking how long it takes, until the user
// says otherwise — a charge is not a fuel stop.
if (!on) setDwell(STOP_KIND_BY_KEY[key].defaultMinutes)
@@ -303,6 +296,12 @@ export default function RoadtripStopPopup({
>
)}
+