add monthly rainfalls
This commit is contained in:
@@ -110,4 +110,27 @@ class RainfallController extends Controller
|
||||
|
||||
return response()->json(array_values($results));
|
||||
}
|
||||
|
||||
public function lastMonths(Request $request)
|
||||
{
|
||||
$result = [];
|
||||
for ($i = 12; $i >= 0; $i--) {
|
||||
$date = now()->subMonths($i);
|
||||
$firstOfMonth = now()->subMonths($i)->firstOfMonth();
|
||||
$lastOfMonth = now()->subMonths($i)->lastOfMonth();
|
||||
$rainfalls = $request->user()
|
||||
->rainfalls()
|
||||
->whereBetween('date', [$firstOfMonth, $lastOfMonth])
|
||||
->sum('value');
|
||||
|
||||
$result[] = [
|
||||
'year' => $date->year,
|
||||
'month' => $date->month,
|
||||
'label' => $date->monthName.' '.$date->year,
|
||||
'values' => (int) $rainfalls,
|
||||
];
|
||||
}
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
52
resources/js/components/rainfall/YearRainfaill.tsx
Normal file
52
resources/js/components/rainfall/YearRainfaill.tsx
Normal 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
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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[],
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ Route::middleware('auth:sanctum')->group(function () {
|
||||
|
||||
Route::post('/locations', [LocationController::class, 'store'])->name('location.store');
|
||||
|
||||
Route::get('/rainfalls/last-months', [RainfallController::class, 'lastMonths'])->name('rainfalls.last');
|
||||
Route::get('/rainfalls/last', [RainfallController::class, 'lastRainfalls'])->name('rainfalls.last');
|
||||
Route::get('/rainfalls/graph', [RainfallController::class, 'graphValue'])->name('rainfalls.graph');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user