fix(itinerary): fix Svelte 4 reactivity for temperature display and connector metrics

Temperature display always showed 'unavailable' because formatDayTemperature()
read dayTemperatures from closure, which Svelte 4's compiler doesn't track in
template expressions. Same issue affected getLocationConnector() reading
connectorMetricsMap from closure.

Fix: pass both state variables as explicit function parameters so they appear
in template expressions and trigger re-renders on async updates.

Also improve optimize button diagnostics with console logging and better
toast feedback when items lack coordinates.
This commit is contained in:
2026-03-08 18:18:53 +00:00
parent d4e0ef14b8
commit 6203d7ed87

View File

@@ -993,8 +993,8 @@
loadDayTemperatures(days, fetchVersion); loadDayTemperatures(days, fetchVersion);
} }
function formatDayTemperature(day: DayGroup): string { function formatDayTemperature(day: DayGroup, temps: Record<string, DayTemperature>): string {
const temperature = dayTemperatures[day.date]; const temperature = temps[day.date];
if (!temperature?.available || temperature.temperature_c === null) { if (!temperature?.available || temperature.temperature_c === null) {
return getI18nText('itinerary.temperature_unavailable', 'Temperature unavailable'); return getI18nText('itinerary.temperature_unavailable', 'Temperature unavailable');
} }
@@ -1099,6 +1099,7 @@
} }
function optimizeDayOrder(dayIndex: number) { function optimizeDayOrder(dayIndex: number) {
console.info('[optimize] optimizeDayOrder called for day', dayIndex);
if (!canModify || isSavingOrder) return; if (!canModify || isSavingOrder) return;
const day = days[dayIndex]; const day = days[dayIndex];
@@ -1135,7 +1136,21 @@
}); });
if (movableCoordinateItems.length < 2) { if (movableCoordinateItems.length < 2) {
addToast('info', getI18nText('itinerary.optimize_not_enough_items', 'Not enough stops to optimize')); console.warn('[optimize] Not enough movable coordinate items:', {
totalDayItems: nonShadowItems.length,
anchorCount: anchorEntries.length,
movableWithCoords: movableCoordinateItems.length,
movableWithoutCoords: nonShadowItems.filter(
(item, i) => !anchorIndexSet.has(i) && !getCoordinatesFromItineraryItem(item)
).length
});
addToast(
'warning',
getI18nText(
'itinerary.optimize_not_enough_items',
'Not enough items with location data to optimize'
)
);
return; return;
} }
@@ -1273,11 +1288,12 @@
function getLocationConnector( function getLocationConnector(
currentItem: ResolvedItineraryItem, currentItem: ResolvedItineraryItem,
nextItem: ResolvedItineraryItem | null nextItem: ResolvedItineraryItem | null,
metricsMap: Record<string, LocationConnector>
): LocationConnector | null { ): LocationConnector | null {
const key = getLocationConnectorKey(currentItem, nextItem); const key = getLocationConnectorKey(currentItem, nextItem);
if (key && connectorMetricsMap[key]) { if (key && metricsMap[key]) {
return connectorMetricsMap[key]; return metricsMap[key];
} }
return getFallbackLocationConnector(currentItem, nextItem); return getFallbackLocationConnector(currentItem, nextItem);
@@ -2808,7 +2824,7 @@
preTimelineLodging.id === postTimelineLodging.id} preTimelineLodging.id === postTimelineLodging.id}
{@const startBoundaryConnector = {@const startBoundaryConnector =
preTimelineLodging && firstConnectableItem preTimelineLodging && firstConnectableItem
? getLocationConnector(preTimelineLodging, firstConnectableItem) ? getLocationConnector(preTimelineLodging, firstConnectableItem, connectorMetricsMap)
: null} : null}
{@const startBoundaryDirectionsUrl = {@const startBoundaryDirectionsUrl =
preTimelineLodging && firstConnectableItem preTimelineLodging && firstConnectableItem
@@ -2820,7 +2836,7 @@
: null} : null}
{@const endBoundaryConnector = {@const endBoundaryConnector =
postTimelineLodging && lastConnectableItem postTimelineLodging && lastConnectableItem
? getLocationConnector(lastConnectableItem, postTimelineLodging) ? getLocationConnector(lastConnectableItem, postTimelineLodging, connectorMetricsMap)
: null} : null}
{@const endBoundaryDirectionsUrl = {@const endBoundaryDirectionsUrl =
postTimelineLodging && lastConnectableItem postTimelineLodging && lastConnectableItem
@@ -2842,7 +2858,7 @@
<div class="text-xs opacity-70">{weekday}</div> <div class="text-xs opacity-70">{weekday}</div>
<div class="text-2xl font-bold -mt-1">{dayOfMonth}</div> <div class="text-2xl font-bold -mt-1">{dayOfMonth}</div>
<div class="text-xs opacity-70">{monthAbbrev}</div> <div class="text-xs opacity-70">{monthAbbrev}</div>
<div class="text-[10px] opacity-80 mt-1">{formatDayTemperature(day)}</div> <div class="text-[10px] opacity-80 mt-1">{formatDayTemperature(day, dayTemperatures)}</div>
</div> </div>
</div> </div>
@@ -3075,7 +3091,7 @@
{@const resolvedObj = item.resolvedObject} {@const resolvedObj = item.resolvedObject}
{@const multiDay = isMultiDay(item)} {@const multiDay = isMultiDay(item)}
{@const nextConnectableItem = findNextConnectableItem(dayTimelineItems, index)} {@const nextConnectableItem = findNextConnectableItem(dayTimelineItems, index)}
{@const locationConnector = getLocationConnector(item, nextConnectableItem)} {@const locationConnector = getLocationConnector(item, nextConnectableItem, connectorMetricsMap)}
{@const directionsUrl = buildDirectionsUrl( {@const directionsUrl = buildDirectionsUrl(
item, item,
nextConnectableItem, nextConnectableItem,