add Meteo part
This commit is contained in:
@@ -128,7 +128,7 @@ class AuthController extends Controller
|
||||
|
||||
public function user(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$user = $request->user()->load('locations');
|
||||
|
||||
return response()->json(new AuthUserResource($user));
|
||||
}
|
||||
|
||||
22
app/Http/Controllers/LocationController.php
Normal file
22
app/Http/Controllers/LocationController.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Resources\AuthUserResource;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LocationController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'latitude' => ['required', 'decimal:3,5'],
|
||||
'longitude' => ['required', 'decimal:3,5'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$user->locations()->create($data);
|
||||
|
||||
return response()->json(new AuthUserResource($user->load('locations')));
|
||||
}
|
||||
}
|
||||
45
app/Http/Controllers/WeatherController.php
Normal file
45
app/Http/Controllers/WeatherController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class WeatherController extends Controller
|
||||
{
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
abort_if($request->user()->locations->count() === 0, 404, 'Coordonnées non renseignées dans le profile.');
|
||||
|
||||
$location = $request->user()->locations->first();
|
||||
$idCity = 6427013;
|
||||
$apiKey = config('weather.open_weather_map_api_key');
|
||||
|
||||
try {
|
||||
$response = Cache::remember('weather-'.$location->id, 5 * 60, function () use ($location, $apiKey) {
|
||||
// $url = 'https://api.openweathermap.org/data/2.5/forecast?id='.$idCity.'&appid='.$apiKey.$params;
|
||||
$params = '&units=metric&lang=fr';
|
||||
$url = 'https://api.openweathermap.org/data/2.5/forecast?lat='.$location->latitude.'&lon='.$location->longitude.'&appid='.$apiKey.$params;
|
||||
$client = new Client();
|
||||
$promise = $client->requestAsync('GET', $url);
|
||||
$response = $promise->wait();
|
||||
|
||||
return json_decode($response->getBody()->getContents());
|
||||
});
|
||||
|
||||
return response()->json($response);
|
||||
} catch (Exception $e) {
|
||||
Log::alert('unable to fetch data', [
|
||||
'ip' => $request->ip(),
|
||||
'code' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return response()->json('unable to fetch data', 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ class AuthUserResource extends JsonResource
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'locations' => LocationResource::collection($this->whenLoaded('locations')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
24
app/Http/Resources/LocationResource.php
Normal file
24
app/Http/Resources/LocationResource.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class LocationResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'user_id' => $this->user_id,
|
||||
'latitude' => $this->latitude,
|
||||
'longitude' => $this->longitude,
|
||||
];
|
||||
}
|
||||
}
|
||||
16
app/Models/Location.php
Normal file
16
app/Models/Location.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Location extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,11 @@ class User extends Authenticatable
|
||||
'password' => 'hashed',
|
||||
];
|
||||
|
||||
public function locations(): HasMany
|
||||
{
|
||||
return $this->hasMany(Location::class);
|
||||
}
|
||||
|
||||
public function rainfalls(): HasMany
|
||||
{
|
||||
return $this->hasMany(Rainfall::class);
|
||||
|
||||
11
app/Models/Weather.php
Normal file
11
app/Models/Weather.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Weather extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
@@ -29,6 +29,6 @@ return [
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => false,
|
||||
'supports_credentials' => true,
|
||||
|
||||
];
|
||||
|
||||
5
config/weather.php
Normal file
5
config/weather.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'open_weather_map_api_key' => env('OPEN_WEATHER_MAP_API_KEY'),
|
||||
];
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('locations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(\App\Models\User::class)->constrained();
|
||||
$table->double('longitude');
|
||||
$table->double('latitude');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('locations');
|
||||
}
|
||||
};
|
||||
@@ -3,14 +3,14 @@ import React, {
|
||||
ReactElement
|
||||
} from "react"
|
||||
|
||||
const Field: FC<FieldProps> = ({children, type = 'text', ...props}) => {
|
||||
const Field: FC<FieldProps> = ({children, type = 'text', className = '', ...props}) => {
|
||||
|
||||
return <div className="form-control">
|
||||
{children && <label className="block text-gray-900 dark:text-gray-200"
|
||||
htmlFor={props.id ?? undefined}>
|
||||
{children}
|
||||
</label>}
|
||||
<input className="w-full mt-2 rounded dark:bg-gray-700"
|
||||
<input className={`${className} w-full mt-2 rounded dark:bg-gray-700`}
|
||||
type={type}
|
||||
{...props}/>
|
||||
<div className={`error-message`} />
|
||||
@@ -27,5 +27,7 @@ interface FieldProps {
|
||||
value: any,
|
||||
placeholder?: string,
|
||||
autoFocus?: boolean,
|
||||
className?: string,
|
||||
step?: string,
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
||||
}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import React from "react"
|
||||
import {Link} from "react-router-dom"
|
||||
import {Link, useLocation} from "react-router-dom"
|
||||
import useAuthUser from "../hooks/AuthUser"
|
||||
|
||||
const Header = () => {
|
||||
|
||||
const {authUser, logout} = useAuthUser()
|
||||
const {authUser} = useAuthUser()
|
||||
const location = useLocation()
|
||||
|
||||
return <header className="flex justify-between py-3 px-5 bg-blue-700 text-white text-xl">
|
||||
<div>
|
||||
<Link to="/">Bermite</Link>
|
||||
</div>
|
||||
|
||||
{/*{authUser && <nav className="flex gap-2">*/}
|
||||
{/* <Link to="/pluviometrie">Pluviométrie</Link>*/}
|
||||
{/* <Link to="/meteo">Météo</Link>*/}
|
||||
{/*</nav>}*/}
|
||||
{authUser?.locations && authUser.locations.length > 0 && <nav className="flex gap-2">
|
||||
<Link to="/pluviometrie" className={location.pathname === '/pluviometrie' ? 'font-bold' : ''}>Pluviométrie</Link>
|
||||
<Link to="/meteo" className={location.pathname === '/meteo' ? 'font-bold' : ''}>Météo</Link>
|
||||
</nav>}
|
||||
|
||||
{authUser
|
||||
? <span className="flex gap-2">
|
||||
<Link to="/profile">{authUser.name}</Link>
|
||||
<Link to="/profile" className={location.pathname === '/profile' ? 'font-bold' : ''}>{authUser.name}</Link>
|
||||
</span>
|
||||
: <span className="flex gap-2">
|
||||
<Link to="/connexion">Connexion</Link>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
const weekDays = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche']
|
||||
|
||||
interface Date {
|
||||
getWeekDay(): string,
|
||||
toSQLDate(): string,
|
||||
}
|
||||
|
||||
Date.prototype.toSQLDate = function (): string {
|
||||
return (new Date(this)).toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
Date.prototype.getWeekDay = function () {
|
||||
const dayIndex = this.getDay() === 0 ? 6 : this.getDay() - 1
|
||||
return weekDays[dayIndex]
|
||||
}
|
||||
|
||||
@@ -70,4 +70,5 @@ interface User {
|
||||
id: number,
|
||||
name: string,
|
||||
email: string,
|
||||
locations: {id: number, latitude: number, longitude: number}[],
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ const useAxiosTools = () => {
|
||||
if (error.response && error.response.status === 422) {
|
||||
displayFormErrors(error)
|
||||
} else {
|
||||
setError(error.response.data.message || error.message)
|
||||
setError(error.response?.data.message || error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,76 @@
|
||||
import React from "react"
|
||||
import React, {FormEvent, useState} from "react"
|
||||
import useAuthUser from "../../hooks/AuthUser"
|
||||
import PageLayout from "../../components/PageLayout"
|
||||
import Card from "../../components/Card";
|
||||
import Field from "../../components/Field";
|
||||
import useAxiosTools from "../../hooks/AxiosTools";
|
||||
|
||||
const Profile = () => {
|
||||
|
||||
const {authUser, logout} = useAuthUser()
|
||||
const {authUser, setAuthUser, logout} = useAuthUser()
|
||||
const [latitude, setLatitude] = useState(0)
|
||||
const [longitude, setLongitude] = useState(0)
|
||||
const {errorCatch, axiosPost} = useAxiosTools()
|
||||
|
||||
const submitLocation = async (event: FormEvent) => {
|
||||
event.preventDefault()
|
||||
|
||||
try {
|
||||
const res = await axiosPost(`/api/locations`, {latitude, longitude})
|
||||
setAuthUser(res.data)
|
||||
} catch (e) {
|
||||
errorCatch(e)
|
||||
}
|
||||
}
|
||||
|
||||
return <PageLayout>
|
||||
<div className="flex justify-between">
|
||||
<h1 className="mb-5 text-lg font-bold">Profile de l'utilisateur</h1>
|
||||
<div className="m-1 my-5 flex justify-between">
|
||||
<h1 className="text-lg font-bold">Profile de l'utilisateur</h1>
|
||||
<div>
|
||||
<button type="button" onClick={logout} className="btn-primary text-lg font-bold">Se déconnecter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Card className="mb-5">
|
||||
<div>Nom: <strong>{authUser?.name}</strong></div>
|
||||
<div>Email: <strong>{authUser?.email}</strong></div>
|
||||
</div>
|
||||
</Card>
|
||||
{/*<div>Update name & email</div>*/}
|
||||
{/*<div>Change password</div>*/}
|
||||
{/*<div>Delete Account</div>*/}
|
||||
<Card>
|
||||
<h2>Météo</h2>
|
||||
|
||||
{authUser?.locations && authUser.locations.length > 0 ? <>
|
||||
<h3>Emplacements</h3>
|
||||
<ul>
|
||||
{authUser?.locations.map(location => <li key={location.id}>{location.latitude} - {location.longitude}</li>)}
|
||||
</ul>
|
||||
</> : <form onSubmit={submitLocation}>
|
||||
<h3>Ajouter un emplacement</h3>
|
||||
<div className="flex gap-2">
|
||||
<Field name="latitude"
|
||||
type="number"
|
||||
step="0.0001"
|
||||
value={latitude}
|
||||
className="h-10"
|
||||
onChange={event => setLatitude(Number(event.target.value))}>
|
||||
Latitude
|
||||
</Field>
|
||||
<Field name="longitude"
|
||||
type="number"
|
||||
step="0.0001"
|
||||
value={longitude}
|
||||
className="h-10"
|
||||
onChange={event => setLongitude(Number(event.target.value))}>
|
||||
Longitude
|
||||
</Field>
|
||||
<div className="self-end">
|
||||
<button type="submit" className="btn-primary w-24 h-10">Valider</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>}
|
||||
|
||||
</Card>
|
||||
</PageLayout>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from "react"
|
||||
import PageLayout from "../components/PageLayout"
|
||||
|
||||
const Meteo = () => {
|
||||
|
||||
return <PageLayout>
|
||||
Météo
|
||||
</PageLayout>
|
||||
}
|
||||
|
||||
export default Meteo
|
||||
@@ -2,7 +2,7 @@ import React, {lazy, Suspense} from "react"
|
||||
import {BrowserRouter, Route, Routes} from "react-router-dom"
|
||||
import useAuthUser from "../hooks/AuthUser"
|
||||
import Header from "../components/Header"
|
||||
import Meteo from "./Meteo"
|
||||
import Weather from "./Weather"
|
||||
|
||||
const ForgotPassword = lazy(() => import('./Auth/ForgotPassword'))
|
||||
const Home = lazy(() => import('./Home'))
|
||||
@@ -29,7 +29,7 @@ const Router = () => {
|
||||
{/*<Route path="/sinscrire" element={<Register />} />*/}
|
||||
<Route path="/mot-de-passe-oubliee" element={<ForgotPassword />} />
|
||||
<Route path="/changer-le-mot-de-passe/:token" element={<Reset />} />
|
||||
<Route path="/meteo" element={<Meteo />} />
|
||||
<Route path="/meteo" element={<Weather />} />
|
||||
<Route path="/pluviometrie" element={<Rainfall />} />
|
||||
<Route path="/pluviometrie/mesures" element={<RainfallIndex />} />
|
||||
</Routes>
|
||||
|
||||
146
resources/js/pages/Weather.tsx
Normal file
146
resources/js/pages/Weather.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
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"
|
||||
|
||||
const Weather = () => {
|
||||
|
||||
const [currentWeather, setCurrentWeather] = useState<WeatherValue|null>(null)
|
||||
const [weatherDays, setWeatherDays] = useState<[string, WeatherValue[]][]|null>(null)
|
||||
const {errorLabel, errorCatch, cleanErrors, axiosGet} = useAxiosTools()
|
||||
|
||||
useEffect(() => {
|
||||
(async () => fetchWeather())()
|
||||
}, [])
|
||||
|
||||
const fetchWeather = async () => {
|
||||
try {
|
||||
cleanErrors()
|
||||
const res = await axiosGet(`/api/weather`)
|
||||
const currentWeather = res.data.list[0]
|
||||
|
||||
let weatherDays: [string, WeatherValue[]][] = []
|
||||
let objectEntries = {index: -1, date: ''}
|
||||
res.data.list.forEach((item: WeatherValue, index: number) => {
|
||||
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)
|
||||
} catch (e) {
|
||||
errorCatch(e)
|
||||
}
|
||||
}
|
||||
|
||||
return <PageLayout>
|
||||
|
||||
{errorLabel()}
|
||||
|
||||
<Card className="flex justify-between">
|
||||
<div className="flex flex-col m-2 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" />}
|
||||
</div>
|
||||
<div className="flex gap-1 flex-col">
|
||||
<span className="text-4xl pt-5">{currentWeather?.main.temp_max.toFixed()} <span className="text-2xl">°C</span></span>
|
||||
<span className="text-secondary text-2xl mt-2 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: {[k: 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 flex-col gap-2 flex-1 h-full">
|
||||
<span className="font-bold text-lg" title={(new Date(date)).toLocaleDateString()}>{(new Date(date)).getWeekDay()}</span>
|
||||
<span className="text-secondary dark:text-secondary-ligth">{weatherState?.description}</span>
|
||||
</div>
|
||||
<div className="flex items-center -mt-1.5">
|
||||
<img src={`http://openweathermap.org/img/wn/${weatherState?.icon}@2x.png`}
|
||||
className=""
|
||||
alt={weatherState?.main + ' ' + weatherState?.icon} width="80px" />
|
||||
|
||||
</div>
|
||||
<div className="flex gap-1 flex-col">
|
||||
<span className="text-lg">{weatherState?.max.toFixed()} °C</span>
|
||||
<span className="text-secondary dark:text-secondary-ligth">{weatherState?.min.toFixed()} °C</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -3,3 +3,32 @@ export interface rainfall {
|
||||
date: string,
|
||||
value: number,
|
||||
}
|
||||
|
||||
export interface WeatherRequest {
|
||||
list: WeatherValue[],
|
||||
}
|
||||
|
||||
export interface WeatherValue {
|
||||
dt_txt: string,
|
||||
main: {
|
||||
temp: number,
|
||||
humidity: number,
|
||||
pressure: number,
|
||||
temp_max: number,
|
||||
temp_min: number,
|
||||
},
|
||||
rain?: {
|
||||
'3h': number,
|
||||
},
|
||||
weather: WeatherTime[],
|
||||
}
|
||||
|
||||
export interface WeatherTime {
|
||||
description: string,
|
||||
icon: string,
|
||||
main: 'Rain',
|
||||
}
|
||||
|
||||
export interface WeatherCompilation {
|
||||
[k: string]: WeatherValue[]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\AuthController;
|
||||
use App\Http\Controllers\LocationController;
|
||||
use App\Http\Controllers\RainfallController;
|
||||
use App\Http\Controllers\WeatherController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
@@ -24,8 +26,12 @@ Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::get('/user', [AuthController::class, 'user'])->name('user');
|
||||
Route::delete('/logout', [AuthController::class, 'logout'])->name('logout');
|
||||
|
||||
Route::post('/locations', [LocationController::class, 'store'])->name('location.store');
|
||||
|
||||
Route::get('/rainfalls/last', [RainfallController::class, 'lastRainfalls'])->name('rainfalls.last');
|
||||
Route::get('/rainfalls/graph', [RainfallController::class, 'graphValue'])->name('rainfalls.graph');
|
||||
|
||||
Route::apiResource('rainfalls', RainfallController::class);
|
||||
|
||||
Route::get('weather', [WeatherController::class, 'index'])->name('weather.index');
|
||||
});
|
||||
|
||||
@@ -14,6 +14,12 @@ export default {
|
||||
fontFamily: {
|
||||
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
colors: {
|
||||
secondary: {
|
||||
'ligth':'#c6c5c5',
|
||||
DEFAULT:'#666666',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user