vannut / statamic-weather-addon

🎏 A Statamic-Addon for the OpenWeathermap API
5 stars 0 forks source link

Forecast as widget on dashboard #10

Closed vannut closed 2 years ago

vannut commented 2 years ago

As we have the current weather, wouldn't it be cool to display a small widget with the forecast?

Just a simple table with date, temperature, wind & pressure?

Wibbmer commented 2 years ago

Since I couldn't find a way to get the existing antlers tag {{ forecast }} to work within the current_data blade view I created an Action to do the logic Tags\Forecast.php did. This is now used in Forecast.php as well as ControlPanelController.php.

In Forecast.php


use Vannut\StatamicWeather\Actions\CreateForecastAction;

public function index(): Collection
    {
        $locale = strtolower($this->params->get('locale'));

        $daily = (new CreateForecastAction())->make($locale);

        return $daily;
    }

In ControlPanelController.php


use Vannut\StatamicWeather\Actions\CreateForecastAction;

public function currentData()
    {

        if (Storage::exists('weather-forecast.json'))
        {

            $json = json_encode(
                json_decode(Storage::get('weather-forecast.json')),
                JSON_PRETTY_PRINT
            );

            $daily = (new CreateForecastAction())->make();

        } else
        {
            $json = false;
        }

        return view('weather::current_data', [
            'json' => $json,
            'forecast' => $daily
        ]);
    }

And of course in Actions\CreateForecastAction.php


<?php

namespace Vannut\StatamicWeather\Actions;

use Storage;
use Illuminate\Support\Collection;
use Vannut\StatamicWeather\Settings;
use Vannut\StatamicWeather\ConversionTrait;

class CreateForecastAction {

    use ConversionTrait;

    public function make($lang = null)
    {
        $config = (new Settings)->get();
        if ($lang)
        {
            $locale = $lang;
        } else
        {
            $locale = strtolower($config->get('lang', 'en'));
        }
        $units = $config->get('units', 'metric');

        $json = json_decode(Storage::get('weather-forecast.json'), true);

        return collect($json['daily'])
            ->map(function ($item) use ($locale, $units) {

                $item['icon'] = $this->makeIcon($item['weather']);
                $item['wind_compass'] = $this->degreeesToWindDirection($item['wind_deg'], $locale);

                $item['wind_bft'] = ($units === 'metric')
                    ? $this->msToBft($item['wind_speed'])
                    : $this->mphToBft($item['wind_speed']);

                $item['uvi_color'] = $this->UVIndexToColor($item['uvi']);
                $item['uvi_percentage'] = $this->UVIndexToPercentage($item['uvi']);
                $item['pop_per'] = $item['pop'] * 100;

                if ($units == 'metric')
                {
                    $item['temp_unit'] = '&deg;C';
                } else
                {
                    $item['temp_unit'] = '&deg;F';
                }                

                return $item;
            });
    }
}

With everything set up you can now use this data in current_weather.blade.php with a slightly modified version of your existing forecast template

@extends('statamic::layout')
@section('title', 'Weather settings')
@section('wrapper_class', 'max-w-full ml-0')

@section('content')

    <form
        title="Fetch current Weather"
        method="post"
        action="{{ cp_route('weather.data.fetchWeather') }}"
    >
        <button class="btn-primary" type="submit">Fetch/update Weather data</button>
        @csrf
    </form>

    <div class="flex flex-wrap">
        @if( ! $json )
            <h1 class="mt-4">Current Data</h1>

            <p><em>No data stored yet</em></p>
        @else
            <div class="lg:w-1/4 w-full">
                <h1 class="mt-4">Current Data</h1>

                <div name="textarea">
            <textarea
                id="field_textarea"
                class="input-text"
                style="width:100%; overflow-wrap: break-word; height: 450px;"
            >{{ $json }}</textarea>
                </div>
            </div>
            <div class=" px-2">
                <div>
                    <h1 class="mt-4">Current Forecast</h1>
                    <div class="">
                        <div class="flex justify-center py-2">
                            <div class="flex flex-wrap bg-neutral-100">
                                @foreach($forecast as $daily)
                                    <div class="p-1 w-full sm:w-1/2 lg:w-1/3 xl:w-auto">
                                        <div class="rounded-xl bg-white">
                                            <div class="lining-nums p-4 text-center">
                                                <span
                                                    class="text-primary text-lg">{{date('l',$daily['dt'])}}</span><br/>
                                                <span class="text-xs">{{date('Y-m-d',$daily['dt'])}}</span>
                                            </div>
                                            <div class="pb-4 text-5xl flex flex-col text-center justify-center">
                                                <i class="fal {{ $daily['icon'] }}"></i>
                                                <span class="text-xs">{{$daily['weather'][0]['description']}}</span>
                                            </div>
                                            <div class="pb-2 flex items-center justify-center">
                                                <div class="text-sm">
                                                    {{ round($daily['temp']['max']) }}<span
                                                        class="text-neutral-700">{!! $daily['temp_unit']!!}</span>
                                                </div>
                                                <div class="text-sm">
                                                    <span class="text-neutral-700">&nbsp;&nbsp; / </span>
                                                    {{ round($daily['temp']['min']) }}<span
                                                        class="text-neutral-700">{!! $daily['temp_unit']!!}</span>
                                                </div>
                                            </div>
                                            <div class="flex items-center justify-center pb-4">
                                                <div>
                                                    <i class="fal fa-wind"></i>
                                                    {{ $daily['wind_compass']}} {{ $daily['wind_bft'] }}<span
                                                        class="text-neutral-700">Bft</span>
                                                </div>
                                            </div>
                                            <div class="flex items-center justify-center pb-4">
                                                <div>
                                                    {{ $daily['pressure'] }}<span
                                                        class="text-neutral-700">hPa</span>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                @endforeach
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        @endif
    </div>

@endsection

of course it's quite bare and could use some additional styling, but it's working

(except for the icons, since I am using a different version of Fontawesome it's not rendering them for me. Maybe it would be better to pass the SVG directly instead of the classes)

Wibbmer commented 2 years ago

As a widget:

Create the widget src/Widgets/CurrentForecast.php


<?php

namespace Vannut\StatamicWeather\Widgets;

use Statamic\Widgets\Widget;
use Vannut\StatamicWeather\Actions\CreateForecastAction;

class CurrentForecast extends Widget
{

    /**
     * The HTML that should be shown in the widget.
     *
     * @return string|\Illuminate\View\View
     */
    public function html()
    {
        $forecast=(new CreateForecastAction())->make();

        return view('vannut::widgets.current_forecast',['forecast'=>$forecast]);
    }
}

In views/Widgets/current_forecast.blade.php (I just copy/pasted this part from above, could be turned into a component I guess)


<div class="card p-1 content">
    <h1 class="mt-4">Current Forecast</h1>
    <div class="">
        <div class="flex justify-center py-2">
            <div class="flex flex-wrap bg-neutral-100">
                @foreach($forecast as $daily)
                    <div class="p-1 w-full sm:w-1/2 lg:w-1/3 xl:w-auto">
                        <div class="rounded-xl bg-white">
                            <div class="lining-nums p-4 text-center">
                                                <span
                                                    class="text-primary text-lg">{{date('l',$daily['dt'])}}</span><br/>
                                <span class="text-xs">{{date('Y-m-d',$daily['dt'])}}</span>
                            </div>
                            <div class="pb-4 text-5xl flex flex-col text-center justify-center">
                                <i class="fal {{ $daily['icon'] }}"></i>
                                <span class="text-xs">{{$daily['weather'][0]['description']}}</span>
                            </div>
                            <div class="pb-2 flex items-center justify-center">
                                <div class="text-sm">
                                    {{ round($daily['temp']['max']) }}<span
                                        class="text-neutral-700">{!! $daily['temp_unit']!!}</span>
                                </div>
                                <div class="text-sm">
                                    <span class="text-neutral-700">&nbsp;&nbsp; / </span>
                                    <span
                                        style="color:{{$daily['uvi_color']}}">{{ round($daily['temp']['min']) }}</span>
                                    <span class="text-neutral-700">{!! $daily['temp_unit']!!}</span>
                                </div>
                            </div>
                            <div class="flex items-center justify-center pb-4">
                                <div>
                                    <i class="fal fa-wind"></i>
                                    {{ $daily['wind_compass']}} {{ $daily['wind_bft'] }}<span
                                        class="text-neutral-700">Bft</span>
                                </div>
                            </div>
                            <div class="flex items-center justify-center pb-4">
                                <div>
                                    {{ $daily['pressure'] }}<span
                                        class="text-neutral-700">hPa</span>
                                </div>
                            </div>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
    </div>
</div>

Register in ServiceProvider.php


use Vannut\StatamicWeather\Widgets\CurrentForecast;

protected $viewNamespace = 'vannut';

protected $widgets = [
        CurrentForecast::class
    ];

And finally register it in config/statamic/cp.php


'widgets' => [
        'current_forecast',
    ],

Now you have the forecast as a widget within the CP dashboard

vannut commented 2 years ago

Added this in the latest pr #11