Files
lara-bermite/resources/js/pages/Weather.tsx
2024-03-06 09:42:12 +01:00

162 lines
6.4 KiB
TypeScript

import React, {FC, useEffect, useState} from "react"
import PageLayout from "../components/PageLayout"
import useAxiosTools from "../hooks/AxiosTools"
import {WeatherValue} from "../types"
import Card from "../components/Card"
import Img from "../components/Img"
const Weather = () => {
const [currentWeather, setCurrentWeather] = useState<WeatherValue|null>(null)
const [fetchTime, setFetchTime] = useState(0)
const [weatherDays, setWeatherDays] = useState<[string, WeatherValue[]][]|null>(null)
const {loading, setLoading, errorLabel, errorCatch, cleanErrors, axiosGet} = useAxiosTools()
useEffect(() => {
const timer = setInterval(() => {
if (!loading && (fetchTime + 5 * 60) < getCurrentTime()) {
fetchWeather()
}
}, 1000)
return () => clearInterval(timer)
}, [fetchTime])
const getCurrentTime = () => Number(((new Date).getTime() /1000).toFixed())
const fetchWeather = async () => {
try {
setLoading(true)
cleanErrors()
const res = await axiosGet(`/api/weather`)
const currentWeather = res.data.list[0]
const weatherDays: [string, WeatherValue[]][] = []
let objectEntries = {index: -1, date: ''}
res.data.list.forEach((item: WeatherValue) => {
const date = item.dt_txt.split(' ')[0]
if (date === (new Date).toSQLDate()) {
if (currentWeather.main.temp_min > item.main.temp_min) {
currentWeather.main.temp_min = item.main.temp_min
}
if (currentWeather.main.temp_max < item.main.temp_max) {
currentWeather.main.temp_max = item.main.temp_max
}
}
if (date !== objectEntries.date) {
objectEntries = {index: objectEntries.index + 1, date}
}
if (!weatherDays[objectEntries.index]) {
weatherDays[objectEntries.index] = [date, []]
}
weatherDays[objectEntries.index][1] = [...weatherDays[objectEntries.index][1], item]
})
setWeatherDays(weatherDays)
setCurrentWeather(currentWeather)
setFetchTime(getCurrentTime())
} catch (e) {
errorCatch(e)
} finally {
setLoading(false)
}
}
return <PageLayout>
{errorLabel()}
<Card className="flex justify-between">
<div className="m-2 flex flex-col justify-between">
<span className="text-6xl">{currentWeather?.main.temp.toFixed()} °C</span>
<span className="text-secondary dark:text-secondary-ligth">{currentWeather?.weather[0].description}</span>
</div>
<div className="flex items-stretch">
<div>
{/*{currentWeather && <img src={`http://openweathermap.org/img/wn/${currentWeather?.weather[0].icon}@2x.png`}*/}
{/* alt={currentWeather?.weather[0].main} width="120px" />}*/}
{currentWeather && <Img src={`images/icons/${currentWeather?.weather[0].icon}.svg`}
alt={currentWeather?.weather[0].main} width="120px" />}
</div>
<div className="flex flex-col gap-1">
<span className="pt-5 text-4xl">{currentWeather?.main.temp_max.toFixed()} <span className="text-2xl">°C</span></span>
<span className="mt-2 text-2xl text-secondary dark:text-secondary-ligth">{currentWeather?.main.temp_min.toFixed()} °C</span>
</div>
</div>
</Card>
<div className="m-3">
{weatherDays?.filter(([date]) => date !== (new Date).toSQLDate())
.map(([date, values]) => <WeatherCard key={date} date={date} values={values}/>)}
</div>
</PageLayout>
}
export default Weather
const WeatherCard: FC<{date: string, values: WeatherValue[]}> = ({date, values= []}) => {
const [weatherState, setWeatherState] = useState<{main: string, description: string, icon: string, min: number, max: number}|null>(null)
useEffect(() => {
const weatherState = {
min: 100,
max: -100,
main: '',
icon: '',
description: '',
}
const result: Record<string, number> = {}
values.forEach(value => {
if (value.main.temp_min < weatherState.min) {
weatherState.min = value.main.temp_min
}
if (value.main.temp_max > weatherState.max) {
weatherState.max = value.main.temp_max
}
const tag = value.weather[0].main
if (! result[tag]) {
result[tag] = 0
}
result[tag]++
})
let itemToReturn: {name:string, value: number}|null = null
for (const item in result) {
if (! itemToReturn || itemToReturn.value < result[item]) {
itemToReturn = {name: item, value: result[item]}
}
}
if (itemToReturn && itemToReturn.name) {
const nameToSearch = itemToReturn.name
const value = values.find(item => item.weather[0].main === nameToSearch)
if (value) {
weatherState.main = itemToReturn.name
weatherState.description = value.weather[0].description
weatherState.icon = value.weather[0].icon.replace('n', 'd')
setWeatherState(weatherState)
}
}
}, [])
return <div className="flex gap-5">
<div className="flex h-full flex-1 flex-col gap-2">
<span className="text-lg font-bold" title={(new Date(date)).toLocaleDateString()}>{(new Date(date)).getWeekDay()}</span>
<span className="text-secondary dark:text-secondary-ligth">{weatherState?.description}</span>
</div>
<div className="-mt-1.5 flex items-center">
<Img src={`images/icons/${weatherState?.icon}.svg`}
alt={weatherState?.main + ' ' + weatherState?.icon}
width="80px" />
</div>
<div className="flex flex-col gap-1">
<span className="text-lg">{weatherState?.max.toFixed()} °C</span>
<span className="text-secondary dark:text-secondary-ligth">{weatherState?.min.toFixed()} °C</span>
</div>
</div>
}