small bug fixes
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
|
||||
import { ChartContainer } from '$lib/components/charts';
|
||||
|
||||
import { href } from '$lib/i18n';
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
import {
|
||||
type FriendlyWeatherError,
|
||||
type WeekForecastResult,
|
||||
@@ -30,7 +32,7 @@
|
||||
} from '$lib/services/weather';
|
||||
|
||||
import { useHeroActions } from '../../hero.svelte';
|
||||
import { defaultParameters } from '../../options';
|
||||
import { defaultParameters, inDomainCity } from '../../options';
|
||||
import { computeDayNightWeatherCodes } from '../../utils/weather-codes';
|
||||
import DailyStripSticky from './DailyStripSticky.svelte';
|
||||
import DaySummary from './DaySummary.svelte';
|
||||
@@ -114,6 +116,14 @@
|
||||
// for the same place once their range is exhausted.
|
||||
let locationRoute = $derived(buildLocationRoute(location));
|
||||
|
||||
// When a regional model has no data here, point at a place it does cover
|
||||
// rather than only offering to abandon the model.
|
||||
let suggestedCity = $derived.by(() => {
|
||||
const city = inDomainCity(params.models?.[0] ?? '');
|
||||
// pointless to offer the place we are already on
|
||||
return city && city.slug !== locationRoute ? city : null;
|
||||
});
|
||||
|
||||
let mounted = $state(false);
|
||||
let loading = $state(true);
|
||||
let loadError = $state<FriendlyWeatherError | null>(null);
|
||||
@@ -157,6 +167,36 @@
|
||||
return fd ? formatZoned(selectedDay, fd.timezone, 'yyyy-MM-dd') : '';
|
||||
});
|
||||
|
||||
// A model swap can leave the open day outside what the new model covers (a
|
||||
// short-range model after a 15-day one, say). Rather than showing an empty
|
||||
// day, fall back to the nearest day that does have data - searching forward
|
||||
// first, then back.
|
||||
$effect(() => {
|
||||
const fd = fetchedDaily;
|
||||
if (!fd || wantedDay) return;
|
||||
const days = fd.dailyDates;
|
||||
if (days.length === 0) return;
|
||||
|
||||
const covered = (i: number) => {
|
||||
const max = fd.daily.temperature_2m_max[i];
|
||||
return max != null && Number.isFinite(max);
|
||||
};
|
||||
const current = days.findIndex(
|
||||
(d) => formatZoned(d, fd.timezone, 'yyyy-MM-dd') === selectedDayKey
|
||||
);
|
||||
if (current >= 0 && covered(current)) return;
|
||||
|
||||
const from = current >= 0 ? current : 0;
|
||||
for (let step = 0; step < days.length; step++) {
|
||||
for (const i of [from + step, from - step]) {
|
||||
if (i >= 0 && i < days.length && covered(i)) {
|
||||
selectedDay.setTime(days[i].getTime());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Charts intentionally keep their current range: they show the full week
|
||||
// unless the user narrows it via the range presets or Ctrl+scroll.
|
||||
const switchDay = (date: Date) => {
|
||||
@@ -324,12 +364,20 @@
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
{#if suggestedCity}
|
||||
<a
|
||||
class="cursor-pointer rounded-md border border-border bg-background px-3 py-1 text-xs font-semibold text-foreground transition-colors hover:bg-muted"
|
||||
href={href('/weather/week/[location]', { location: suggestedCity.slug })}
|
||||
>
|
||||
{m.no_data_try_city({ city: suggestedCity.label })}
|
||||
</a>
|
||||
{/if}
|
||||
{#if params.models?.[0] !== 'best_match'}
|
||||
<button
|
||||
class="cursor-pointer rounded-md border border-border bg-background px-3 py-1 text-xs font-semibold text-foreground transition-colors hover:bg-muted"
|
||||
onclick={resetToBestMatch}
|
||||
>
|
||||
Switch to Best match
|
||||
{m.no_data_best_match()}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -362,18 +410,27 @@
|
||||
/>
|
||||
</svg>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="font-semibold">No forecast data for this model here</p>
|
||||
<p class="font-semibold">{m.no_data_title()}</p>
|
||||
<p class="text-[13px] opacity-90">
|
||||
The selected weather model doesn't cover {location.name} — regional models only provide data
|
||||
inside their own area.
|
||||
{m.no_data_body({ location: location.name ?? '' })}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="cursor-pointer rounded-md border border-amber-500/50 bg-background/60 px-3 py-1 text-xs font-semibold transition-colors hover:bg-background"
|
||||
onclick={resetToBestMatch}
|
||||
>
|
||||
Switch to Best match
|
||||
</button>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
{#if suggestedCity}
|
||||
<a
|
||||
class="cursor-pointer rounded-md border border-amber-500/50 bg-background/60 px-3 py-1 text-xs font-semibold transition-colors hover:bg-background"
|
||||
href={href('/weather/week/[location]', { location: suggestedCity.slug })}
|
||||
>
|
||||
{m.no_data_try_city({ city: suggestedCity.label })}
|
||||
</a>
|
||||
{/if}
|
||||
<button
|
||||
class="cursor-pointer rounded-md border border-amber-500/50 bg-background/60 px-3 py-1 text-xs font-semibold transition-colors hover:bg-background"
|
||||
onclick={resetToBestMatch}
|
||||
>
|
||||
{m.no_data_best_match()}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user