add monthly rainfalls

This commit is contained in:
Romulus21
2024-03-09 15:33:23 +01:00
parent 7244cd7be4
commit 77412774c8
9 changed files with 100 additions and 130 deletions

View File

@@ -1,116 +0,0 @@
import * as d3 from "d3"
import React, {FC, useEffect, useRef} from "react"
import {rainfallGraphData} from "../../types"
const RainfallGraph: FC<RainfallGraphProps> = ({width, height, data, start_date, end_date}) => {
const svgRef = useRef<SVGSVGElement|null>(null)
useEffect(() => {
renderSVG()
}, [width, height, data])
const renderSVG = () => {
const margin = {top: 10, right: 30, bottom: 30, left: 20}
const gWidth = width - margin.left - margin.right
const gHeight = height - margin.top - margin.bottom
d3.select(svgRef.current).selectAll("*").remove()
const svg = d3.select(svgRef.current)
.attr('id', 'rain-graph')
.attr('width', gWidth + margin.left + margin.right)
.attr('height', gHeight + margin.top + margin.bottom)
.attr('class', 'relative')
.append('g')
.attr('transform', "translate(" + margin.left + "," + margin.top + ")")
const yMax = data.reduce((result, item) => item.value > result ? item.value : result, 0)
const y = d3.scaleLinear()
.domain([0, yMax + 10])
.range([gHeight, 0])
const x = d3.scaleUtc()
.domain([(new Date(start_date)), (new Date()).setDate((new Date(end_date)).getDate())])
.range([margin.left, width - margin.right])
const yAxis = svg.append('g')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(height / 80))
.call(g => g.select('.domain').remove())
.call(g => g.append('text')
.attr('x', -margin.left)
.attr('y', 10)
.attr('fill', 'currentColor')
.attr('text-anchor', 'start')
)
svg.append("g")
.attr("transform", "translate(0," + gHeight + ")")
.call(d3.axisBottom(x)
.ticks(8)
.tickFormat(
// @ts-expect-error change time format
d3.timeFormat("%d/%m/%Y")
)
, 0)
if (data.length === 0) {
// no values text
const titleBox = svg.append("g")
.attr("x", (width))
.attr("y", height)
titleBox.append("text")
.attr("x", (width / 2))
.attr("y", height / 2)
.attr("text-anchor", "middle")
.style("font-size", "20px")
.text('Aucune Donnée')
} else {
yAxis.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margin.left - margin.right)
.attr("stroke-opacity", 0.1))
}
const showDetails = (event: Event, data: rainfallGraphData) => {
const toolTip = document.getElementById('tooltip')
if (toolTip) {
toolTip.style.opacity = '1'
toolTip.innerText = `${data.label} : ${data.value}`
}
}
const diffDays = Math.round(Math.abs(((new Date(start_date)).getTime() - (new Date(end_date)).getTime()) / (24 * 60 * 60 * 1000))) + 1
const dayWidth = (width - 44) / diffDays
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("fill", "steelblue")
.attr("x", d => x(new Date(d.start)) + 1.5)
.attr("y", d => y(d.value))
.attr("width", d => (dayWidth * d.days - 3))
.attr("height", d => height - margin.bottom - 10 - y(d.value))
.on('click', showDetails)
.append('title')
.text(d => `${d.label} : ${d.value}`)
}
return <div className="relative">
<svg ref={svgRef} />
<div id="tooltip" className="absolute left-10 top-3 rounded border p-2" style={{opacity: 0}}></div>
</div>
}
export default RainfallGraph
interface RainfallGraphProps {
width: number,
height: number,
data: rainfallGraphData[],
start_date: string,
end_date: string,
}

View File

@@ -0,0 +1,52 @@
import React, {useState, useEffect} from "react"
import Card from "../Card"
import useAxiosTools from "../../hooks/AxiosTools"
import {AxiosError} from "axios"
import {monthlyRainfall} from "../../types"
const YearRainfall = () => {
const {errorCatch, errorLabel, setError, axiosGet} = useAxiosTools()
const [data, setData] = useState<monthlyRainfall[]>([])
const months = Array(13)
.reduce((result, item, index) => {
const date = new Date()
console.log(item, index, date)
return item
}, [])
console.log(months)
useEffect(() => {
fetchData()
}, [])
const fetchData = async () => {
try {
const res = await axiosGet('/api/rainfalls/last-months')
setData(res.data)
} catch (e) {
if (e instanceof AxiosError) {
setError(e.message)
} else {
errorCatch(e)
}
}
}
return <div>
<Card className="w-full min-w-[300px] self-start overflow-hidden md:w-auto">
<h1 className="-mx-2 -mt-1 bg-blue-500 px-2 py-1 text-center text-lg font-bold text-white">Précipitations des derniers mois</h1>
{errorLabel()}
<table className="w-full text-center">
<tbody>
{data.map(line => <tr key={line.year + '-' + line.month} className="">
<td>{line.label}</td>
<td className="px-2 text-right">{line.values}</td>
</tr>)}
</tbody>
</table>
</Card>
</div>
}
export default YearRainfall

View File

@@ -4,7 +4,7 @@ const useDimension = () => {
const RESET_TIMEOUT = 300
let movement_timer: number|undefined = undefined
const targetRef = useRef<HTMLDivElement>()
const targetRef = useRef<HTMLDivElement|undefined>()
const [dimensions, setDimensions] = useState({ width:0, height: 0 })
useEffect(() => {

View File

@@ -1,6 +1,7 @@
import React from "react"
import useAuthUser from "../hooks/AuthUser"
import Rainfall from "./Rainfall"
import YearRainfall from "../components/rainfall/YearRainfaill"
import PageLayout from "../components/PageLayout"
const Home = () => {
@@ -8,7 +9,9 @@ const Home = () => {
return <div>
{authUser
? <Rainfall />
? <PageLayout>
<YearRainfall />
</PageLayout>
: <div className="px-5 pt-10">
<h1 className="text-lg font-bold">Application pour enregistrer sa pluviométrie</h1>
<p className="mt-5">Un compte est nécessaire mais les inscriptions ne sont pas ouvertes.</p>

View File

@@ -1,14 +1,14 @@
import React, {useEffect, useState} from "react"
import PageLayout from "../components/PageLayout"
import LastFiveMesure from "../components/rainfall/LastFiveMesure"
import AddRainfall from "../components/rainfall/AddRainfall"
import useAxiosTools from "../hooks/AxiosTools"
import {rainfallGraphData} from "../types"
import Field from "../components/Field"
import useDimension from "../hooks/DimensionHook"
import RainFallEcharts from "../components/rainfall/RainFallEcharts"
import PageLayout from "../../components/PageLayout"
import LastFiveMesure from "../../components/rainfall/LastFiveMesure"
import AddRainfall from "../../components/rainfall/AddRainfall"
import useAxiosTools from "../../hooks/AxiosTools"
import {rainfallGraphData} from "../../types"
import Field from "../../components/Field"
import useDimension from "../../hooks/DimensionHook"
import RainFallEcharts from "../../components/rainfall/RainFallEcharts"
const Rainfall = () => {
const RainfallGraph = () => {
const [loadedAt, reload] = useState(new Date)
const [graphData, setGraphData] = useState<rainfallGraphData[]>([])
@@ -77,4 +77,4 @@ const Rainfall = () => {
</PageLayout>
}
export default Rainfall
export default RainfallGraph

View File

@@ -9,7 +9,7 @@ const Home = lazy(() => import('./Home'))
const Login = lazy(() => import('./Auth/Login'))
const Profile = lazy(() => import('./Auth/Profile'))
const Reset = lazy(() => import('./Auth/Reset'))
const Rainfall = lazy(() => import('./Rainfall'))
const Rainfall = lazy(() => import('./Rainfall/RainfallGraph'))
const RainfallIndex = lazy(() => import('./Rainfall/RainfallIndex'))
const Router = () => {

View File

@@ -14,6 +14,13 @@ export interface rainfallGraphData {
value: number,
}
export interface monthlyRainfall {
year: number,
month: number,
label: string,
values: number,
}
export interface WeatherRequest {
list: WeatherValue[],
}