214 lines
5.4 KiB
Vue
214 lines
5.4 KiB
Vue
<script setup>
|
|
import { ref, computed, onActivated } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useWeatherStore } from '../stores/weather'
|
|
import { useLocationStore } from '../stores/location'
|
|
|
|
|
|
const router = useRouter()
|
|
const locationStore = useLocationStore()
|
|
const weatherStore = useWeatherStore()
|
|
|
|
const coordinates = ref(null)
|
|
const refreshing = ref(false)
|
|
const timestamp = ref(new Date().getTime())
|
|
|
|
|
|
const panelHeadingTitle = computed(() => {
|
|
if (!weatherStore.forecast) {
|
|
return "Forecast"
|
|
}
|
|
|
|
let generated = new Date(weatherStore.forecast.properties.generatedAt)
|
|
generated = generated.toLocaleTimeString('en-US', {timeStyle: 'short'})
|
|
return `Forecast @ ${generated}`
|
|
})
|
|
|
|
|
|
const radarLatestURL = computed(() => {
|
|
if (weatherStore.weather) {
|
|
return `${weatherStore.radarLatestURL}?t=${timestamp.value}`
|
|
}
|
|
})
|
|
|
|
|
|
const radarLoopURL = computed(() => {
|
|
if (weatherStore.weather) {
|
|
return `${weatherStore.radarLoopURL}?t=${timestamp.value}`
|
|
}
|
|
})
|
|
|
|
|
|
onActivated(() => {
|
|
|
|
if (!weatherStore.coordinates) {
|
|
router.push('/')
|
|
return
|
|
}
|
|
|
|
refreshing.value = true
|
|
fetchWeather().then(() => {
|
|
refreshing.value = false
|
|
})
|
|
})
|
|
|
|
|
|
async function coordinatesUpdated(coords) {
|
|
refreshing.value = true
|
|
weatherStore.clearWeather()
|
|
weatherStore.setCoordinates(coords)
|
|
await fetchWeather()
|
|
refreshing.value = false
|
|
}
|
|
|
|
|
|
async function fetchWeather() {
|
|
await weatherStore.getForecast()
|
|
coordinates.value = weatherStore.coordinates
|
|
}
|
|
|
|
|
|
async function refreshWeather() {
|
|
refreshing.value = true
|
|
weatherStore.clearWeather(true)
|
|
timestamp.value = new Date().getTime()
|
|
await fetchWeather()
|
|
refreshing.value = false
|
|
}
|
|
|
|
|
|
function getShortForecast(period) {
|
|
return period.shortForecast.replace(/Showers And Thunderstorms/g, 'T-storms')
|
|
}
|
|
|
|
|
|
function showHourly(period) {
|
|
router.push('/hourly')
|
|
}
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<main>
|
|
<div style="display: flex; flex-direction: column;">
|
|
|
|
<div class="block"
|
|
style="display: flex; justify-content: space-between; align-items: center;">
|
|
<o-select v-if="weatherStore.weather"
|
|
v-model="coordinates"
|
|
@update:model-value="coordinatesUpdated">
|
|
<option v-for="location in locationStore.locations"
|
|
:key="location.coordinates"
|
|
:value="location.coordinates">
|
|
{{ location.cityState }}
|
|
</option>
|
|
</o-select>
|
|
<o-button variant="primary"
|
|
icon-left="refresh"
|
|
@click="refreshWeather()"
|
|
:disabled="refreshing">
|
|
{{ refreshing ? "Refreshing" : "Refresh" }}
|
|
</o-button>
|
|
</div>
|
|
|
|
<div v-if="weatherStore.alerts?.features?.length"
|
|
class="block">
|
|
<o-notification variant="warning"
|
|
icon="warning"
|
|
@click="$router.push('/alerts')"
|
|
style="cursor: pointer;">
|
|
<p class="has-text-weight-bold">Watches In Effect</p>
|
|
<p>Updated {{ new Date(weatherStore.alerts.updated).toLocaleTimeString('en-US', {timeStyle: 'short'}) }}</p>
|
|
</o-notification>
|
|
</div>
|
|
|
|
<nav class="panel weather-panel">
|
|
|
|
<p class="panel-heading">
|
|
{{ panelHeadingTitle }}
|
|
</p>
|
|
|
|
<div class="panel-block">
|
|
|
|
<div v-if="weatherStore.forecast"
|
|
style="display: flex; overflow-x: scroll;">
|
|
|
|
<div v-for="period in weatherStore.forecast.properties.periods"
|
|
:key="period.number"
|
|
class="weather-period is-size-7 has-text-weight-bold"
|
|
:class="{daytime: period.isDaytime}"
|
|
style="cursor: pointer;"
|
|
@click="showHourly(period)">
|
|
|
|
<p>{{ period.name }}</p>
|
|
|
|
<div>
|
|
<img :src="period.icon" />
|
|
</div>
|
|
|
|
<p>{{ getShortForecast(period) }}</p>
|
|
|
|
<p>
|
|
<span v-if="period.isDaytime">Hi</span>
|
|
<span v-else>Lo</span>
|
|
{{ period.temperature }} ° {{ period.temperatureUnit }}
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="!weatherStore.forecast">
|
|
fetching weather data...
|
|
</div>
|
|
|
|
</div>
|
|
</nav>
|
|
|
|
<nav class="panel weather-panel">
|
|
<div class="panel-heading"
|
|
style="display: flex; align-items: center; justify-content: space-between;">
|
|
<p>Radar</p>
|
|
<o-button tag="a"
|
|
href="https://radar.weather.gov/ridge/standard/CONUS-LARGE_loop.gif"
|
|
target="_blank"
|
|
icon-left="external-link">
|
|
National Radar
|
|
</o-button>
|
|
</div>
|
|
<div class="panel-block">
|
|
<div class="columns">
|
|
<div class="column">
|
|
<img :src="radarLatestURL" />
|
|
</div>
|
|
<div class="column">
|
|
<img :src="radarLoopURL" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
</div>
|
|
</main>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.weather-period {
|
|
width: 100px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
background-color: gray;
|
|
color: white;
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.weather-period.daytime {
|
|
background-color: white;
|
|
color: black;
|
|
}
|
|
|
|
</style>
|