refactor(weather): share the day weather anchor and icon map between desktop and phone

The day plan, the day panel, the phone timeline and the phone day sheet read
the day local weather anchor from dayWeatherAnchor, and every weather icon
comes from one map in weatherIcons.
This commit is contained in:
Maurice
2026-10-08 20:57:33 +00:00
parent 34127b99bb
commit 9f3e1f8c83
13 changed files with 164 additions and 71 deletions
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef, useId, type ReactNode } from 'react'
import { X, Sun, Cloud, CloudRain, CloudSnow, CloudDrizzle, CloudLightning, Wind, Droplets, Sunrise, Sunset, Hotel, Calendar, MapPin, LogIn, LogOut, Pencil, ChevronsDown, ChevronsUp, ArrowRight, Check, Moon, type LucideIcon } from 'lucide-react'
import { X, CloudRain, Wind, Droplets, Sunrise, Sunset, Hotel, Calendar, MapPin, LogIn, LogOut, Pencil, ChevronsDown, ChevronsUp, ArrowRight, Check, Moon, type LucideIcon } from 'lucide-react'
import type { WeatherResult } from '@trek/shared'
import { usePluginViewContributions, PluginCardFooter } from '../Plugins/PluginContributions'
import { usePluginStore } from '../../store/pluginStore'
@@ -24,14 +24,10 @@ import { INPUT, PANEL, EditorField, AddRowButton, PillSelect } from '../shared/d
import { BOX, Eyebrow, Field, RoundAction, TypeTile, toneOf, toneColor, toneTint } from './bookings/bookingParts'
import { SoftPill, TimePill, tintOf } from './planParts'
import { typeInfo } from './bookings/bookingsModel'
const WEATHER_ICON_MAP: Record<string, LucideIcon> = {
Clear: Sun, Clouds: Cloud, Rain: CloudRain, Drizzle: CloudDrizzle,
Thunderstorm: CloudLightning, Snow: CloudSnow, Mist: Wind, Fog: Wind, Haze: Wind,
}
import { weatherIconFor } from '../Weather/weatherIcons'
function WIcon({ main, size = 14 }: { main: string; size?: number }) {
const Icon = WEATHER_ICON_MAP[main] || Cloud
const Icon = weatherIconFor(main)
return <Icon size={size} strokeWidth={1.8} />
}
@@ -51,6 +51,7 @@ import { withinDriveRange } from '@trek/shared/roadtrip'
import { formatDate, formatTime, formatMoneySum, splitReservationDateTime } from '../../utils/formatters'
import { dayHeadingParts } from '../../utils/dayLabel'
import { pendingStayIds } from '../../utils/pendingStays'
import { dayWeatherAnchor } from '../../utils/dayWeather'
import { planCosts } from './planCosts'
import { useDayNotes } from '../../hooks/useDayNotes'
import { useExchangeRates } from '../../hooks/useExchangeRates'
@@ -1806,10 +1807,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP
// Day-local anchor only (#2167): the day's first located stop, else the
// hotel you wake up in. No trip-wide fallback — on a roadtrip that
// silently showed another city's weather with nothing naming the place.
const weatherHotel = loc == null ? dayBookends.morning : undefined
const wLat = loc?.place?.lat ?? weatherHotel?.place_lat
const wLng = loc?.place?.lng ?? weatherHotel?.place_lng
const weatherName = loc?.place?.name ?? weatherHotel?.place_name ?? null
const { lat: wLat, lng: wLng, name: weatherName } = dayWeatherAnchor(da, () => dayBookends.morning)
const hasWeather = !!(day.date && wLat != null && wLng != null)
const dayAccs = accommodations.filter(a => isDayInAccommodationRange(day, a.start_day_id, a.end_day_id, days))
// Sort: check-out first, then ongoing stays, then check-in last