feat(dashboard): add price dashboard with Zonneplan integration

This commit is contained in:
2026-08-01 19:53:18 +02:00
parent 49919fef32
commit 1cd905ca4a
29 changed files with 1701 additions and 28 deletions
@@ -0,0 +1,60 @@
<?php
namespace App\Features\Dashboard\Clients;
use App\Features\Dashboard\Contracts\PriceClient;
use Exception;
use Illuminate\Http\Client\Factory;
use Illuminate\Support\Facades\Log;
use Throwable;
class ZonneplanPriceClient implements PriceClient
{
public function __construct(
private readonly Factory $http,
) {}
public function getElectricityPrices(): array
{
return $this->get('/energy-prices/electricity/upcoming');
}
public function getGasPrices(): array
{
return $this->get('/energy-prices/gas/upcoming');
}
/** @return array<int, array<string, mixed>> */
private function get(string $path): array
{
try {
$response = $this->http
->baseUrl(config('api.baseurl'))
->withQueryParameters([
'secret' => config('api.key'),
])
->timeout(10)
->retry(2, 100)
->get($path);
$response->throw();
Log::info('Zonneplan API opgehaald', [
'path' => $path,
'status' => $response->status(),
]);
return $response->json('data', []);
} catch (Throwable $e) {
Log::error('Zonneplan API aanroep mislukt', [
'path' => $path,
'message' => $e->getMessage(),
]);
throw new Exception(
"Kon energieprijzen niet ophalen voor {$path}",
previous: $e,
);
}
}
}
@@ -0,0 +1,10 @@
<?php
namespace App\Features\Dashboard\Contracts;
interface PriceClient
{
public function getElectricityPrices(): array;
public function getGasPrices(): array;
}
@@ -0,0 +1,12 @@
<?php
namespace App\Features\Dashboard\Contracts;
use Illuminate\Support\Collection;
interface PriceRepository
{
public function retrieveElectricity(): Collection;
public function retrieveGas(): Collection;
}
@@ -0,0 +1,15 @@
<?php
namespace App\Features\Dashboard\Controllers;
use App\Features\Dashboard\Data\DashboardData;
use App\Features\Dashboard\Services\DashboardService;
use App\Http\Controllers\Controller;
class DashboardController extends Controller
{
public function index(DashboardService $dashboardService)
{
return inertia('dashboard::index', DashboardData::from($dashboardService->getDashboardData()));
}
}
@@ -0,0 +1,17 @@
<?php
namespace App\Features\Dashboard\Data;
use Spatie\LaravelData\Data;
class DashboardData extends Data
{
/**
* @property array<ElectricityData> $electricity
* @property array<GasData> $gas
*/
public function __construct(
public array $electricity,
public array $gas
) {}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Features\Dashboard\Data;
use Carbon\Carbon;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
#[MapInputName(SnakeCaseMapper::class)]
class ElectricityData extends Data
{
public function __construct(
public Carbon $startDate,
public Carbon $endDate,
public string $period,
public ?int $marketPrice,
public ?float $totalPriceTaxIncluded,
public ?int $priceInclHandlingVat,
public ?int $priceTaxWithVat,
public ?string $pricingProfile,
public ?int $carbonFootprintInGram,
public ?int $sustainabilityScore,
public Carbon $startDateDatetime,
) {}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Features\Dashboard\Data;
use Carbon\Carbon;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
#[MapInputName(SnakeCaseMapper::class)]
class GasData extends Data
{
public function __construct(
public Carbon $startDate,
public Carbon $endDate,
public string $period,
public int $marketPrice,
public float $totalPriceTaxIncluded,
public int $priceInclHandlingVat,
public int $priceTaxWithVat,
public Carbon $startDateDatetime,
) {}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Features\Dashboard\Repositories;
use App\Features\Dashboard\Contracts\PriceClient;
use App\Features\Dashboard\Contracts\PriceRepository as ContractsPriceRepository;
use App\Features\Dashboard\Data\ElectricityData;
use App\Features\Dashboard\Data\GasData;
use Illuminate\Support\Collection;
use Override;
class ZonneplanPriceRepository implements ContractsPriceRepository
{
public function __construct(
public PriceClient $client
) {}
#[Override]
public function retrieveElectricity(): Collection
{
$data = $this->client->getElectricityPrices();
return collect(ElectricityData::collect($data));
}
#[Override]
public function retrieveGas(): Collection
{
$data = $this->client->getGasPrices();
return collect(GasData::collect($data));
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Features\Dashboard\Services;
use App\Features\Dashboard\Contracts\PriceRepository;
class DashboardService
{
public function __construct(
public PriceRepository $repository
) {}
public function getDashboardData(): array
{
return [
'electricity' => $this->repository->retrieveElectricity(),
'gas' => $this->repository->retrieveGas(),
];
}
}
+6 -1
View File
@@ -2,6 +2,10 @@
namespace App\Providers;
use App\Features\Dashboard\Clients\ZonneplanPriceClient;
use App\Features\Dashboard\Contracts\PriceClient;
use App\Features\Dashboard\Contracts\PriceRepository;
use App\Features\Dashboard\Repositories\ZonneplanPriceRepository;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\DB;
@@ -15,7 +19,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
//
$this->app->bind(PriceRepository::class, ZonneplanPriceRepository::class);
$this->app->bind(PriceClient::class, ZonneplanPriceClient::class);
}
/**
@@ -0,0 +1,27 @@
<?php
namespace App\Providers;
use Carbon\Carbon;
use Spatie\LaravelTypeScriptTransformer\LaravelData\Transformers\DataClassTransformer;
use Spatie\LaravelTypeScriptTransformer\TypeScriptTransformerApplicationServiceProvider as BaseTypeScriptTransformerServiceProvider;
use Spatie\TypeScriptTransformer\Formatters\PrettierFormatter;
use Spatie\TypeScriptTransformer\Transformers\AttributedClassTransformer;
use Spatie\TypeScriptTransformer\Transformers\EnumTransformer;
use Spatie\TypeScriptTransformer\TypeScriptTransformerConfigFactory;
use Spatie\TypeScriptTransformer\Writers\GlobalNamespaceWriter;
class TypeScriptTransformerServiceProvider extends BaseTypeScriptTransformerServiceProvider
{
protected function configure(TypeScriptTransformerConfigFactory $config): void
{
$config
->transformer(AttributedClassTransformer::class)
->transformer(EnumTransformer::class)
->transformer(DataClassTransformer::class)
->transformDirectories(app_path())
->writer(new GlobalNamespaceWriter('generated.d.ts'))
->replaceType(Carbon::class, 'string')
->formatter(PrettierFormatter::class);
}
}