feat(weather): live current conditions endpoint + polling display in briefing view

This commit is contained in:
2026-04-08 21:33:27 -04:00
parent 304affb837
commit 8647f52fbc
3 changed files with 147 additions and 2 deletions
+61 -2
View File
@@ -60,6 +60,23 @@ const isToday = computed(() => selectedConvId.value === todayConvId.value)
const weatherData = ref<WeatherData[]>([])
const tempUnit = ref<string>('C')
interface CurrentConditions {
temperature: number | null;
windspeed: number | null;
description: string;
precip_next_3h: number[];
temp_unit: string;
location: string;
}
const currentConditions = ref<CurrentConditions | null>(null)
let currentWeatherTimer: ReturnType<typeof setInterval> | null = null
async function loadCurrentConditions() {
try {
currentConditions.value = await apiGet<CurrentConditions>('/api/briefing/weather/current')
} catch { /* silent — endpoint may not have locations configured */ }
}
async function loadWeather() {
try {
const data = await apiGet<{ locations: WeatherData[]; temp_unit: string }>('/api/briefing/weather')
@@ -90,6 +107,7 @@ async function loadAll() {
getBriefingToday().catch(() => null),
loadWeather(),
loadNews(),
loadCurrentConditions(),
])
conversations.value = convList
if (today) {
@@ -198,11 +216,18 @@ useBackgroundRefresh(
)
let _mounted = true
onUnmounted(() => { _mounted = false })
onUnmounted(() => {
_mounted = false
if (currentWeatherTimer) clearInterval(currentWeatherTimer)
})
onMounted(async () => {
await checkSetup()
if (!showWizard.value) await loadAll()
if (!showWizard.value) {
await loadAll()
// Poll current conditions every 30 minutes
currentWeatherTimer = setInterval(loadCurrentConditions, 30 * 60 * 1000)
}
})
</script>
@@ -235,6 +260,15 @@ onMounted(async () => {
<!-- Left column: Weather -->
<div class="briefing-left">
<div class="panel-label">Weather</div>
<!-- Current conditions (live) -->
<div v-if="currentConditions && currentConditions.temperature != null" class="current-conditions">
<div class="cc-temp">{{ currentConditions.temperature }}°{{ currentConditions.temp_unit }}</div>
<div class="cc-desc">{{ currentConditions.description }}</div>
<div v-if="currentConditions.precip_next_3h?.some((p: number) => p > 0)" class="cc-precip">
Rain next 3h: {{ currentConditions.precip_next_3h.map((p: number) => `${p}%`).join(', ') }}
</div>
</div>
<!-- Forecast card -->
<template v-if="weatherData.length">
<WeatherCard
v-for="loc in weatherData"
@@ -457,6 +491,31 @@ onMounted(async () => {
color: var(--color-text-muted);
}
/* ── Current conditions (live) ─────────────────────────── */
.current-conditions {
padding: 12px 16px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
margin-bottom: 10px;
}
.cc-temp {
font-size: 1.8rem;
font-weight: 600;
color: var(--color-text);
line-height: 1.1;
}
.cc-desc {
font-size: 0.85rem;
color: var(--color-text-secondary);
margin-top: 2px;
}
.cc-precip {
font-size: 0.78rem;
color: var(--color-accent-warm);
margin-top: 6px;
}
.panel-empty {
font-size: 0.82rem;
color: var(--color-text-muted);