Add weather

This commit is contained in:
2024-09-04 22:30:58 +02:00
parent 7ca64c61f5
commit 043757bffd
6 changed files with 125 additions and 14 deletions

View File

@@ -59,7 +59,7 @@ function visitLink(clickType: 'self' | 'extern') {
<UCard <UCard
:ui="{ :ui="{
body: { base: 'h-full relative z-20' }, body: { base: 'h-full relative z-20' },
background: `h-full duration-300 bg-white dark:bg-gray-900 ${editMode ? '' : 'hover:bg-gray-100 dark:hover:bg-gray-800'}`, background: `h-full duration-300 bg-white dark:bg-gray-900 ${editMode ? '' : 'hover:bg-zinc-100 dark:hover:bg-zinc-800'}`,
}" }"
:class="editMode ? 'animate-wiggle' : 'cursor-pointer'" :class="editMode ? 'animate-wiggle' : 'cursor-pointer'"
@click.left="visitLink('self')" @click.left="visitLink('self')"

View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
import type { WeatherType } from '~~/types/types'
const { coords, error } = useGeolocation()
const { data, status, refresh } = await useAsyncData<WeatherType>(async () => await useRequestFetch<WeatherType>()('/api/weather', {
method: 'GET',
query: {
lon: coords.value.longitude,
lat: coords.value.latitude,
},
}))
watchOnce(coords, async () => await refresh())
const getIcon = computed(() => {
if (!data.value)
return 'i-ph:cloud-fog-duotone'
switch (data.value.weather.type.toLowerCase()) {
case 'clouds':
return 'i-ph:cloud-duotone'
case 'rain':
return 'i-ph:cloud-rain-duotone'
case 'drizzle':
return 'i-ph:cloud-snow-duotone'
case 'clear':
return 'i-ph:sun-duotone'
case 'snow':
return 'i-ph:cloud-snow-duotone'
case 'thunderstorm':
return 'i-ph:cloud-lightning-duotone'
default:
return 'i-ph:cloud-fog-duotone'
}
})
</script>
<template>
<ClientOnly>
<UCard
v-if="status === 'success' && !error"
class="mt-12"
:ui="{
body: { base: 'h-full relative z-20' },
background: 'h-full duration-300 bg-white dark:bg-gray-900',
}"
>
<div class="flex gap-12 items-center h-full gap-4">
<div class="flex items-center gap-2">
<UBadge color="sky" class="p-2" variant="soft">
<UIcon :name="getIcon" size="32" />
</UBadge>
<p class="text-sky-400 text-xl font-medium truncate">
{{ data.weather.description.charAt(0).toUpperCase() + data.weather.description.slice(1) }} in {{ data.city.split(' ')[data.city.split(' ').length - 1] }}
</p>
</div>
<div class="flex gap-2">
<div class="flex gap-1">
min. <span class="text-green-400">{{ data.temp.min }}</span>°C
</div>
<div class="flex gap-1">
actual <span class="text-orange-400">{{ data.temp.feels_like }}</span> °C
</div>
<div class="flex gap-1">
max.
<span class="text-red-400">{{ data.temp.max }}</span>
°C
</div>
<div class="flex gap-1">
<span class="text-cyan-400">{{ data.wind }}</span>
km/h
</div>
<div class="flex gap-1">
rain
<span class="text-blue-400">{{ data.temp.humidity }}</span>
%
</div>
</div>
</div>
</UCard>
</ClientOnly>
</template>

View File

@@ -118,6 +118,7 @@ defineShortcuts({
</h3> </h3>
</div> </div>
</section> </section>
<AppWeather />
<div class="flex justify-end my-8 gap-4"> <div class="flex justify-end my-8 gap-4">
<UButton <UButton
v-if="canCreateCategory()" v-if="canCreateCategory()"

View File

@@ -85,5 +85,9 @@ export default defineNuxtConfig({
url: '', url: '',
dir: './server/db', dir: './server/db',
}, },
openWeather: {
apiKey: '',
units: '',
},
}, },
}) })

View File

@@ -1,23 +1,36 @@
import type { OpenWeatherType } from '~~/types/types' import type { OpenWeatherType, WeatherType } from '~~/types/types'
export default defineCachedEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event) const config = useRuntimeConfig(event)
const { user } = await requireUserSession(event)
const query = getQuery(event)
if (Number(query.lon) === Infinity || Number(query.lat) === Infinity) {
return createError('Invalid coordinates')
}
const openWeather = await $fetch<OpenWeatherType>('https://api.openweathermap.org/data/2.5/weather', { const openWeather = await $fetch<OpenWeatherType>('https://api.openweathermap.org/data/2.5/weather', {
params: { params: {
lat: config.openWeather.lat, lon: query.lon,
lon: config.openWeather.lon, lat: query.lat,
appid: config.openWeather.apiKey, appid: config.openWeather.apiKey,
lang: config.openWeather.lang, lang: user.language.split('-')[0] ?? 'en',
units: config.openWeather.units, units: config.openWeather.units,
}, },
}) })
return { return {
weather: openWeather.weather[0].description, weather: {
type: openWeather.weather[0].main,
description: openWeather.weather[0].description,
},
city: openWeather.name, city: openWeather.name,
temp: openWeather.main.feels_like, temp: {
} feels_like: openWeather.main.feels_like,
}, { min: openWeather.main.temp_min,
maxAge: 60 * 60, // 1 hour max: openWeather.main.temp_max,
name: 'weather', humidity: openWeather.main.humidity,
},
wind: openWeather.wind.speed,
} as WeatherType
}) })

View File

@@ -80,11 +80,16 @@ export const UpdateUserSchemaType = z.infer<typeof UpdateUserSchema>
export interface OpenWeatherType { export interface OpenWeatherType {
weather: Array<{ weather: Array<{
main: string
description: string description: string
}> }>
main: { temp: {
feels_like: number feels_like: number
temp_min: number
temp_max: number
humidity: number
} }
wind: { speed: number }
name: string name: string
} }
@@ -94,7 +99,13 @@ export interface WeatherType {
type: string type: string
description: string description: string
} }
temp: number temp: {
feels_like: number
min: number
max: number
humidity: number
}
wind: number
} }
export const locales = [ export const locales = [