From c1fcf77deae2dd93f5b55724a7ebf17f55f7e50d Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Mon, 5 Aug 2024 23:01:12 +0200 Subject: [PATCH 01/17] first commit --- app/Filament/Widgets/StatsOverviewWidget.php | 64 ++++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/app/Filament/Widgets/StatsOverviewWidget.php b/app/Filament/Widgets/StatsOverviewWidget.php index 6a7721147..52936e65d 100644 --- a/app/Filament/Widgets/StatsOverviewWidget.php +++ b/app/Filament/Widgets/StatsOverviewWidget.php @@ -5,6 +5,7 @@ use App\Enums\ResultStatus; use App\Helpers\Number; use App\Models\Result; +use Carbon\Carbon; use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget\Stat; @@ -43,21 +44,40 @@ protected function getCards(): array ->latest() ->first(); - if (! $previous) { - return [ - Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->download_bits, precision: 2) : 'n/a') - ->icon('heroicon-o-arrow-down-tray'), - Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->upload_bits, precision: 2) : 'n/a') - ->icon('heroicon-o-arrow-up-tray'), - Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' ms' : 'n/a') - ->icon('heroicon-o-clock'), - ]; - } - $downloadChange = percentChange($this->result->download, $previous->download, 2); $uploadChange = percentChange($this->result->upload, $previous->upload, 2); $pingChange = percentChange($this->result->ping, $previous->ping, 2); + $last24hData = Result::query() + ->select(['download', 'upload', 'ping']) + ->where('status', '=', ResultStatus::Completed) + ->where('created_at', '>=', Carbon::now()->subDay()) + ->get(); + + $averageDownload24h = $last24hData->avg('download'); + $averageUpload24h = $last24hData->avg('upload'); + $averagePing24h = $last24hData->avg('ping'); + + $last7dData = Result::query() + ->select(['download', 'upload', 'ping']) + ->where('status', '=', ResultStatus::Completed) + ->where('created_at', '>=', Carbon::now()->subWeek()) + ->get(); + + $averageDownload7d = $last7dData->avg('download'); + $averageUpload7d = $last7dData->avg('upload'); + $averagePing7d = $last7dData->avg('ping'); + + $last1mData = Result::query() + ->select(['download', 'upload', 'ping']) + ->where('status', '=', ResultStatus::Completed) + ->where('created_at', '>=', Carbon::now()->subMonth()) + ->get(); + + $averageDownload1m = $last1mData->avg('download'); + $averageUpload1m = $last1mData->avg('upload'); + $averagePing1m = $last1mData->avg('ping'); + return [ Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->download_bits, precision: 2) : 'n/a') ->icon('heroicon-o-arrow-down-tray') @@ -74,6 +94,28 @@ protected function getCards(): array ->description($pingChange > 0 ? $pingChange.'% slower' : abs($pingChange).'% faster') ->descriptionIcon($pingChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down') ->color($pingChange > 0 ? 'danger' : 'success'), + + Stat::make('Average Download', '') + ->icon('heroicon-o-arrow-down-tray') + ->description( + '24 Hours: '.(blank($averageDownload24h) ? 'n/a' : Number::toBitRate(bits: $averageDownload24h * 8, precision: 2))."\n". + '7 Days: '.(blank($averageDownload7d) ? 'n/a' : Number::toBitRate(bits: $averageDownload7d * 8, precision: 2))."\n". + '1 Month: '.(blank($averageDownload1m) ? 'n/a' : Number::toBitRate(bits: $averageDownload1m * 8, precision: 2)) + ), + Stat::make('Average Upload', '') + ->icon('heroicon-o-arrow-down-tray') + ->description( + '24 Hours: '.(blank($averageUpload24h) ? 'n/a' : Number::toBitRate(bits: $averageUpload24h * 8, precision: 2))."\n". + '7 Days: '.(blank($averageUpload7d) ? 'n/a' : Number::toBitRate(bits: $averageUpload7d * 8, precision: 2))."\n". + '1 Month: '.(blank($averageUpload1m) ? 'n/a' : Number::toBitRate(bits: $averageUpload1m * 8, precision: 2)) + ), + Stat::make('Average Ping', '') + ->icon('heroicon-o-arrow-down-tray') + ->description( + '24 Hours: '.(blank($averagePing24h) ? 'n/a' : number_format($averagePing24h, 2).' ms')."\n". + '7 Days: '.(blank($averagePing7d) ? 'n/a' : number_format($averagePing7d, 2).' ms')."\n". + '1 Month: '.(blank($averagePing1m) ? 'n/a' : number_format($averagePing1m, 2).' ms') + ), ]; } } From 1fb06af461989dd5a1dad52c5c26a12961864781 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 8 Aug 2024 21:44:31 +0200 Subject: [PATCH 02/17] Commit it --- .../Widgets/RecentDownloadChartWidget.php | 21 +++++- app/Filament/Widgets/StatsOverviewWidget.php | 66 ++++--------------- 2 files changed, 30 insertions(+), 57 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 7aca1d1df..0625f7e5d 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -48,17 +48,32 @@ protected function getData(): array ->orderBy('created_at') ->get(); + $downloads = $results->map(fn ($item) => !blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0); + $averageDownload = $downloads->avg(); + return [ 'datasets' => [ [ 'label' => 'Download', - 'data' => $results->map(fn ($item) => ! blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0), + 'data' => $downloads, 'borderColor' => '#0ea5e9', 'backgroundColor' => '#0ea5e9', 'pointBackgroundColor' => '#0ea5e9', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, + ], + [ + 'label' => 'Average', + 'data' => array_fill(0, count($downloads), $averageDownload), + 'borderColor' => '#ff0000', + 'pointBackgroundColor' => '#ff0000', + 'fill' => false, + 'cubicInterpolationMode' => 'monotone', + 'tension' => 0.4, + 'borderDash' => [5, 5], + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -70,12 +85,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => false, + 'display' => true, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; diff --git a/app/Filament/Widgets/StatsOverviewWidget.php b/app/Filament/Widgets/StatsOverviewWidget.php index 52936e65d..d6e89f505 100644 --- a/app/Filament/Widgets/StatsOverviewWidget.php +++ b/app/Filament/Widgets/StatsOverviewWidget.php @@ -5,7 +5,6 @@ use App\Enums\ResultStatus; use App\Helpers\Number; use App\Models\Result; -use Carbon\Carbon; use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget\Stat; @@ -44,40 +43,21 @@ protected function getCards(): array ->latest() ->first(); + if (! $previous) { + return [ + Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->download_bits, precision: 2) : 'n/a') + ->icon('heroicon-o-arrow-down-tray'), + Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->upload_bits, precision: 2) : 'n/a') + ->icon('heroicon-o-arrow-up-tray'), + Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' ms' : 'n/a') + ->icon('heroicon-o-clock'), + ]; + } + $downloadChange = percentChange($this->result->download, $previous->download, 2); $uploadChange = percentChange($this->result->upload, $previous->upload, 2); $pingChange = percentChange($this->result->ping, $previous->ping, 2); - $last24hData = Result::query() - ->select(['download', 'upload', 'ping']) - ->where('status', '=', ResultStatus::Completed) - ->where('created_at', '>=', Carbon::now()->subDay()) - ->get(); - - $averageDownload24h = $last24hData->avg('download'); - $averageUpload24h = $last24hData->avg('upload'); - $averagePing24h = $last24hData->avg('ping'); - - $last7dData = Result::query() - ->select(['download', 'upload', 'ping']) - ->where('status', '=', ResultStatus::Completed) - ->where('created_at', '>=', Carbon::now()->subWeek()) - ->get(); - - $averageDownload7d = $last7dData->avg('download'); - $averageUpload7d = $last7dData->avg('upload'); - $averagePing7d = $last7dData->avg('ping'); - - $last1mData = Result::query() - ->select(['download', 'upload', 'ping']) - ->where('status', '=', ResultStatus::Completed) - ->where('created_at', '>=', Carbon::now()->subMonth()) - ->get(); - - $averageDownload1m = $last1mData->avg('download'); - $averageUpload1m = $last1mData->avg('upload'); - $averagePing1m = $last1mData->avg('ping'); - return [ Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::toBitRate(bits: $this->result->download_bits, precision: 2) : 'n/a') ->icon('heroicon-o-arrow-down-tray') @@ -94,28 +74,6 @@ protected function getCards(): array ->description($pingChange > 0 ? $pingChange.'% slower' : abs($pingChange).'% faster') ->descriptionIcon($pingChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down') ->color($pingChange > 0 ? 'danger' : 'success'), - - Stat::make('Average Download', '') - ->icon('heroicon-o-arrow-down-tray') - ->description( - '24 Hours: '.(blank($averageDownload24h) ? 'n/a' : Number::toBitRate(bits: $averageDownload24h * 8, precision: 2))."\n". - '7 Days: '.(blank($averageDownload7d) ? 'n/a' : Number::toBitRate(bits: $averageDownload7d * 8, precision: 2))."\n". - '1 Month: '.(blank($averageDownload1m) ? 'n/a' : Number::toBitRate(bits: $averageDownload1m * 8, precision: 2)) - ), - Stat::make('Average Upload', '') - ->icon('heroicon-o-arrow-down-tray') - ->description( - '24 Hours: '.(blank($averageUpload24h) ? 'n/a' : Number::toBitRate(bits: $averageUpload24h * 8, precision: 2))."\n". - '7 Days: '.(blank($averageUpload7d) ? 'n/a' : Number::toBitRate(bits: $averageUpload7d * 8, precision: 2))."\n". - '1 Month: '.(blank($averageUpload1m) ? 'n/a' : Number::toBitRate(bits: $averageUpload1m * 8, precision: 2)) - ), - Stat::make('Average Ping', '') - ->icon('heroicon-o-arrow-down-tray') - ->description( - '24 Hours: '.(blank($averagePing24h) ? 'n/a' : number_format($averagePing24h, 2).' ms')."\n". - '7 Days: '.(blank($averagePing7d) ? 'n/a' : number_format($averagePing7d, 2).' ms')."\n". - '1 Month: '.(blank($averagePing1m) ? 'n/a' : number_format($averagePing1m, 2).' ms') - ), ]; } -} +} \ No newline at end of file From 78252569233898afba8e7ae9d3ac2ddf027c2795 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 8 Aug 2024 21:47:02 +0200 Subject: [PATCH 03/17] lint --- app/Filament/Widgets/RecentDownloadChartWidget.php | 2 +- app/Filament/Widgets/StatsOverviewWidget.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 0625f7e5d..32ae8acb8 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -48,7 +48,7 @@ protected function getData(): array ->orderBy('created_at') ->get(); - $downloads = $results->map(fn ($item) => !blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0); + $downloads = $results->map(fn ($item) => ! blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0); $averageDownload = $downloads->avg(); return [ diff --git a/app/Filament/Widgets/StatsOverviewWidget.php b/app/Filament/Widgets/StatsOverviewWidget.php index d6e89f505..6a7721147 100644 --- a/app/Filament/Widgets/StatsOverviewWidget.php +++ b/app/Filament/Widgets/StatsOverviewWidget.php @@ -76,4 +76,4 @@ protected function getCards(): array ->color($pingChange > 0 ? 'danger' : 'success'), ]; } -} \ No newline at end of file +} From 7f1e903237aeac0b46070c50e548f8e0a33022e7 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Fri, 9 Aug 2024 10:55:58 +0200 Subject: [PATCH 04/17] update-all-charts --- .../RecentDownloadLatencyChartWidget.php | 5 +++- .../Widgets/RecentJitterChartWidget.php | 5 +++- .../Widgets/RecentPingChartWidget.php | 24 +++++++++++++++++-- .../Widgets/RecentUploadChartWidget.php | 21 +++++++++++++--- .../RecentUploadLatencyChartWidget.php | 5 +++- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index c7bb0e05a..fc2bfc05a 100644 --- a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php @@ -58,6 +58,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'High (ms)', @@ -68,6 +69,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'Low (ms)', @@ -78,6 +80,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +92,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index 2d2454862..0b4b2df3f 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -58,6 +58,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'Upload (ms)', @@ -68,6 +69,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'Ping (ms)', @@ -78,6 +80,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +92,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 57a10eee5..8dccdd286 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -47,17 +47,32 @@ protected function getData(): array ->orderBy('created_at') ->get(); + $ping = $results->map(fn ($item) => ! blank($item->ping) ? number_format($item->ping, 2) : 0); + $averagePing = $ping->avg(); + return [ 'datasets' => [ [ 'label' => 'Ping (ms)', - 'data' => $results->map(fn ($item) => ! blank($item->ping) ? number_format($item->ping, 2) : 0), + 'data' => $ping, 'borderColor' => '#10b981', 'backgroundColor' => '#10b981', 'pointBackgroundColor' => '#10b981', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, + ], + [ + 'label' => 'Average', + 'data' => array_fill(0, count($ping), $averagePing), + 'borderColor' => '#ff0000', + 'pointBackgroundColor' => '#ff0000', + 'fill' => false, + 'cubicInterpolationMode' => 'monotone', + 'tension' => 0.4, + 'borderDash' => [5, 5], + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -67,9 +82,14 @@ protected function getData(): array protected function getOptions(): array { return [ + 'plugins' => [ + 'legend' => [ + 'display' => true, + ], + ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index 5c9c85baf..d3086353a 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -48,17 +48,32 @@ protected function getData(): array ->orderBy('created_at') ->get(); + $upload = $results->map(fn ($item) => ! blank($item->upload) ? Number::bitsToMagnitude(bits: $item->upload_bits, precision: 2, magnitude: 'mbit') : 0); + $averageUpload = $upload->avg(); + return [ 'datasets' => [ [ 'label' => 'Upload', - 'data' => $results->map(fn ($item) => ! blank($item->upload) ? Number::bitsToMagnitude(bits: $item->upload_bits, precision: 2, magnitude: 'mbit') : 0), + 'data' => $upload, 'borderColor' => '#8b5cf6', 'backgroundColor' => '#8b5cf6', 'pointBackgroundColor' => '#8b5cf6', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, + ], + [ + 'label' => 'Average', + 'data' => array_fill(0, count($upload), $averageUpload), + 'borderColor' => '#ff0000', + 'pointBackgroundColor' => '#ff0000', + 'fill' => false, + 'cubicInterpolationMode' => 'monotone', + 'tension' => 0.4, + 'borderDash' => [5, 5], + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -70,12 +85,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => false, + 'display' => true, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 049055adc..94ccfd321 100644 --- a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php @@ -58,6 +58,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'High (ms)', @@ -68,6 +69,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], [ 'label' => 'Low (ms)', @@ -78,6 +80,7 @@ protected function getData(): array 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, + 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +92,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => false, ], ], ]; From 0073e6affe897cea814b8f9a96c7e3495cc20803 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Mon, 12 Aug 2024 18:21:36 +0200 Subject: [PATCH 05/17] push_local_git --- .../Widgets/RecentDownloadChartWidget.php | 10 +++--- .../RecentDownloadLatencyChartWidget.php | 36 +++++++++---------- .../Widgets/RecentJitterChartWidget.php | 36 +++++++++---------- .../Widgets/RecentPingChartWidget.php | 10 +++--- .../Widgets/RecentUploadChartWidget.php | 10 +++--- .../RecentUploadLatencyChartWidget.php | 36 +++++++++---------- 6 files changed, 69 insertions(+), 69 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 32ae8acb8..36b72df74 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -56,13 +56,13 @@ protected function getData(): array [ 'label' => 'Download', 'data' => $downloads, - 'borderColor' => '#0ea5e9', - 'backgroundColor' => '#0ea5e9', - 'pointBackgroundColor' => '#0ea5e9', - 'fill' => false, + 'borderColor' => 'rgba(14, 165, 233)', + 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', // 10% opacity + 'pointBackgroundColor' => 'rgba(14, 165, 233)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => count($downloads) <= 25 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index fc2bfc05a..80ce99ac0 100644 --- a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php @@ -51,36 +51,36 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Average (ms)', - 'data' => $results->map(fn ($item) => $item->download_latency_iqm ? number_format($item->download_latency_iqm, 2) : 0), - 'borderColor' => '#10b981', - 'backgroundColor' => '#10b981', - 'pointBackgroundColor' => '#10b981', - 'fill' => false, + 'data' => $averageData = $results->map(fn ($item) => $item->download_latency_iqm ? number_format($item->download_latency_iqm, 2) : 0), + 'borderColor' => 'rgba(16, 185, 129)', + 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', + 'pointBackgroundColor' => 'rgba(16, 185, 129)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $averageData->count() <= 25 ? 3 : 0, ], [ 'label' => 'High (ms)', - 'data' => $results->map(fn ($item) => $item->download_latency_high ? number_format($item->download_latency_high, 2) : 0), - 'borderColor' => '#0ea5e9', - 'backgroundColor' => '#0ea5e9', - 'pointBackgroundColor' => '#0ea5e9', - 'fill' => false, + 'data' => $highData = $results->map(fn ($item) => $item->download_latency_high ? number_format($item->download_latency_high, 2) : 0), + 'borderColor' => 'rgba(14, 165, 233)', + 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', + 'pointBackgroundColor' => 'rgba(14, 165, 233)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $highData->count() <= 25 ? 3 : 0, ], [ 'label' => 'Low (ms)', - 'data' => $results->map(fn ($item) => $item->download_latency_low ? number_format($item->download_latency_low, 2) : 0), - 'borderColor' => '#8b5cf6', - 'backgroundColor' => '#8b5cf6', - 'pointBackgroundColor' => '#8b5cf6', - 'fill' => false, + 'data' => $lowData = $results->map(fn ($item) => $item->download_latency_low ? number_format($item->download_latency_low, 2) : 0), + 'borderColor' => 'rgba(139, 92, 246)', + 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', + 'pointBackgroundColor' => 'rgba(139, 92, 246)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $lowData->count() <= 25 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index 0b4b2df3f..e18745bb5 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -51,36 +51,36 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Download (ms)', - 'data' => $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0), - 'borderColor' => '#0ea5e9', - 'backgroundColor' => '#0ea5e9', - 'pointBackgroundColor' => '#0ea5e9', - 'fill' => false, + 'data' => $downloadData = $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0), + 'borderColor' => 'rgba(14, 165, 233)', + 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', + 'pointBackgroundColor' => 'rgba(14, 165, 233)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $downloadData->count() <= 25 ? 3 : 0, ], [ 'label' => 'Upload (ms)', - 'data' => $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0), - 'borderColor' => '#8b5cf6', - 'backgroundColor' => '#8b5cf6', - 'pointBackgroundColor' => '#8b5cf6', - 'fill' => false, + 'data' => $uploadData = $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0), + 'borderColor' => 'rgba(139, 92, 246)', + 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', + 'pointBackgroundColor' => 'rgba(139, 92, 246)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $uploadData->count() <= 25 ? 3 : 0, ], [ 'label' => 'Ping (ms)', - 'data' => $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0), - 'borderColor' => '#10b981', - 'backgroundColor' => '#10b981', - 'pointBackgroundColor' => '#10b981', - 'fill' => false, + 'data' => $pingData = $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0), + 'borderColor' => 'rgba(16, 185, 129)', + 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', + 'pointBackgroundColor' => 'rgba(16, 185, 129)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $pingData->count() <= 25 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 8dccdd286..f8da5a180 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -55,13 +55,13 @@ protected function getData(): array [ 'label' => 'Ping (ms)', 'data' => $ping, - 'borderColor' => '#10b981', - 'backgroundColor' => '#10b981', - 'pointBackgroundColor' => '#10b981', - 'fill' => false, + 'borderColor' => 'rgba(16, 185, 129)', + 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', + 'pointBackgroundColor' => 'rgba(16, 185, 129)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => count($ping) <= 25 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index d3086353a..f96d1bb09 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -56,13 +56,13 @@ protected function getData(): array [ 'label' => 'Upload', 'data' => $upload, - 'borderColor' => '#8b5cf6', - 'backgroundColor' => '#8b5cf6', - 'pointBackgroundColor' => '#8b5cf6', - 'fill' => false, + 'borderColor' => 'rgba(139, 92, 246)', + 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', + 'pointBackgroundColor' => 'rgba(139, 92, 246)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => count($upload) <= 25 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 94ccfd321..0a2f676ef 100644 --- a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php @@ -51,36 +51,36 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Average (ms)', - 'data' => $results->map(fn ($item) => $item->upload_latency_iqm ? number_format($item->upload_latency_iqm, 2) : 0), - 'borderColor' => '#10b981', - 'backgroundColor' => '#10b981', - 'pointBackgroundColor' => '#10b981', - 'fill' => false, + 'data' => $averageData = $results->map(fn ($item) => $item->upload_latency_iqm ? number_format($item->upload_latency_iqm, 2) : 0), + 'borderColor' => 'rgba(16, 185, 129)', + 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', + 'pointBackgroundColor' => 'rgba(16, 185, 129)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $averageData->count() <= 25 ? 3 : 0, ], [ 'label' => 'High (ms)', - 'data' => $results->map(fn ($item) => $item->upload_latency_high ? number_format($item->upload_latency_high, 2) : 0), - 'borderColor' => '#0ea5e9', - 'backgroundColor' => '#0ea5e9', - 'pointBackgroundColor' => '#0ea5e9', - 'fill' => false, + 'data' => $highData = $results->map(fn ($item) => $item->upload_latency_high ? number_format($item->upload_latency_high, 2) : 0), + 'borderColor' => 'rgba(14, 165, 233)', + 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', + 'pointBackgroundColor' => 'rgba(14, 165, 233)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $highData->count() <= 25 ? 3 : 0, ], [ 'label' => 'Low (ms)', - 'data' => $results->map(fn ($item) => $item->upload_latency_low ? number_format($item->upload_latency_low, 2) : 0), - 'borderColor' => '#8b5cf6', - 'backgroundColor' => '#8b5cf6', - 'pointBackgroundColor' => '#8b5cf6', - 'fill' => false, + 'data' => $lowData = $results->map(fn ($item) => $item->upload_latency_low ? number_format($item->upload_latency_low, 2) : 0), + 'borderColor' => 'rgba(139, 92, 246)', + 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', + 'pointBackgroundColor' => 'rgba(139, 92, 246)', + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => 0, + 'pointRadius' => $lowData->count() <= 25 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), From 21221422a2ea931a726705dd6cdc5c0ddab1ea0e Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Mon, 12 Aug 2024 22:29:16 +0200 Subject: [PATCH 06/17] add-timepicker --- app/Filament/Pages/Dashboard.php | 43 +++++++++++++++++-- .../Widgets/RecentDownloadChartWidget.php | 32 +++++--------- .../RecentDownloadLatencyChartWidget.php | 36 ++++++---------- .../Widgets/RecentJitterChartWidget.php | 36 ++++++---------- .../Widgets/RecentPingChartWidget.php | 32 +++++--------- .../Widgets/RecentUploadChartWidget.php | 32 +++++--------- .../RecentUploadLatencyChartWidget.php | 36 ++++++---------- config/speedtest.php | 4 ++ .../views/filament/pages/dashboard.blade.php | 3 -- 9 files changed, 115 insertions(+), 139 deletions(-) delete mode 100644 resources/views/filament/pages/dashboard.blade.php diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 3737cf9bf..64fd6afdd 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -14,16 +14,20 @@ use Cron\CronExpression; use Filament\Actions\Action; use Filament\Actions\ActionGroup; +use Filament\Forms; +use Filament\Forms\Components\DatePicker; +use Filament\Forms\Form; use Filament\Notifications\Notification; -use Filament\Pages\Dashboard as BasePage; +use Filament\Pages\Dashboard as BaseDashboard; +use Filament\Pages\Dashboard\Concerns\HasFiltersForm; use Filament\Support\Enums\IconPosition; use Illuminate\Support\Arr; -class Dashboard extends BasePage +class Dashboard extends BaseDashboard { - protected static ?string $navigationIcon = 'heroicon-o-chart-bar'; + use HasFiltersForm; - protected static string $view = 'filament.pages.dashboard'; + protected static ?string $navigationIcon = 'heroicon-o-chart-bar'; public function getSubheading(): ?string { @@ -38,6 +42,31 @@ public function getSubheading(): ?string return 'Next speedtest at: '.$nextRunDate; } + public function filtersForm(Form $form): Form + { + // Retrieve the default number of days from the configuration + $defaultRangeDays = config('speedtest.chart_time_range'); + + // Calculate the start and end dates based on the configuration value + $endDate = now(); // Today + $startDate = now()->subDays($defaultRangeDays); // Start date for the range + + return $form + ->schema([ + Forms\Components\Section::make() + ->schema([ + DatePicker::make('startDate') + ->default($startDate), + DatePicker::make('endDate') + ->default($endDate), + ]) + ->columns([ + 'default' => 1, + 'sm' => 2, + ]), + ]); + } + protected function getHeaderActions(): array { return [ @@ -83,6 +112,12 @@ protected function getHeaderWidgets(): array { return [ StatsOverviewWidget::make(), + ]; + } + + public function getWidgets(): array + { + return [ RecentDownloadChartWidget::make(), RecentUploadChartWidget::make(), RecentPingChartWidget::make(), diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 36b72df74..e6a9d7b23 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -6,45 +6,35 @@ use App\Helpers\Number; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentDownloadChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Download (Mbps)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'download', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -62,7 +52,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => count($downloads) <= 25 ? 3 : 0, + 'pointRadius' => count($downloads) <= 5 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index 80ce99ac0..4ac6f416d 100644 --- a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php @@ -5,45 +5,35 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentDownloadLatencyChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Download Latency'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -58,7 +48,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $averageData->count() <= 25 ? 3 : 0, + 'pointRadius' => $averageData->count() <= 5 ? 3 : 0, ], [ 'label' => 'High (ms)', @@ -69,7 +59,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $highData->count() <= 25 ? 3 : 0, + 'pointRadius' => $highData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Low (ms)', @@ -80,7 +70,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $lowData->count() <= 25 ? 3 : 0, + 'pointRadius' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index e18745bb5..e1dae4dea 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -5,45 +5,35 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentJitterChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Jitter'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -58,7 +48,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $downloadData->count() <= 25 ? 3 : 0, + 'pointRadius' => $downloadData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Upload (ms)', @@ -69,7 +59,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $uploadData->count() <= 25 ? 3 : 0, + 'pointRadius' => $uploadData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Ping (ms)', @@ -80,7 +70,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $pingData->count() <= 25 ? 3 : 0, + 'pointRadius' => $pingData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index f8da5a180..7c142f59a 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -5,45 +5,35 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentPingChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Ping (ms)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'ping', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -61,7 +51,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => count($ping) <= 25 ? 3 : 0, + 'pointRadius' => count($ping) <= 5 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index f96d1bb09..ef24b76e1 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -6,45 +6,35 @@ use App\Helpers\Number; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentUploadChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Upload (Mbps)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'upload', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -62,7 +52,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => count($upload) <= 25 ? 3 : 0, + 'pointRadius' => count($upload) <= 5 ? 3 : 0, ], [ 'label' => 'Average', diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 0a2f676ef..5dcc830e9 100644 --- a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php @@ -5,45 +5,35 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; +use Filament\Widgets\Concerns\InteractsWithPageFilters; +use Illuminate\Database\Eloquent\Builder; class RecentUploadLatencyChartWidget extends ChartWidget { + use InteractsWithPageFilters; + protected static ?string $heading = 'Upload Latency'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; - public ?string $filter = '24h'; - protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getFilters(): ?array - { - return [ - '24h' => 'Last 24h', - 'week' => 'Last week', - 'month' => 'Last month', - ]; - } - protected function getData(): array { + + $startDate = $this->filters['startDate'] ?? now()->subWeek(); + $endDate = $this->filters['endDate'] ?? now(); + $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($this->filter == '24h', function ($query) { - $query->where('created_at', '>=', now()->subDay()); - }) - ->when($this->filter == 'week', function ($query) { - $query->where('created_at', '>=', now()->subWeek()); - }) - ->when($this->filter == 'month', function ($query) { - $query->where('created_at', '>=', now()->subMonth()); - }) + ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) ->orderBy('created_at') ->get(); @@ -58,7 +48,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $averageData->count() <= 25 ? 3 : 0, + 'pointRadius' => $averageData->count() <= 5 ? 3 : 0, ], [ 'label' => 'High (ms)', @@ -69,7 +59,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $highData->count() <= 25 ? 3 : 0, + 'pointRadius' => $highData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Low (ms)', @@ -80,7 +70,7 @@ protected function getData(): array 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $lowData->count() <= 25 ? 3 : 0, + 'pointRadius' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), diff --git a/config/speedtest.php b/config/speedtest.php index 3d532b5c8..0f88af691 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -17,6 +17,8 @@ 'public_dashboard' => env('PUBLIC_DASHBOARD', false), + 'chart_time_range' => env('CHART_TIME_RANGE', 7), + /** * Polling settings. */ @@ -35,4 +37,6 @@ 'servers' => env('SPEEDTEST_SERVERS', ''), + + ]; diff --git a/resources/views/filament/pages/dashboard.blade.php b/resources/views/filament/pages/dashboard.blade.php deleted file mode 100644 index f351a1c97..000000000 --- a/resources/views/filament/pages/dashboard.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - {{-- Silence is golden --}} - From 05b0091ee8c52bdf35123844c05306bbc650fbc8 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Tue, 13 Aug 2024 17:23:16 +0200 Subject: [PATCH 07/17] add-some-predefined-ranges --- app/Filament/Pages/Dashboard.php | 74 +++++++++++++++++++++++++++++--- config/app.php | 2 + config/speedtest.php | 2 - 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 64fd6afdd..1102b2896 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -16,6 +16,7 @@ use Filament\Actions\ActionGroup; use Filament\Forms; use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; use Filament\Forms\Form; use Filament\Notifications\Notification; use Filament\Pages\Dashboard as BaseDashboard; @@ -45,24 +46,85 @@ public function getSubheading(): ?string public function filtersForm(Form $form): Form { // Retrieve the default number of days from the configuration - $defaultRangeDays = config('speedtest.chart_time_range'); + $defaultRangeDays = config('app.chart_time_range'); // Calculate the start and end dates based on the configuration value - $endDate = now(); // Today - $startDate = now()->subDays($defaultRangeDays); // Start date for the range + $defaultEndDate = now(); // Today + $defaultStartDate = now()->subDays($defaultRangeDays); // Start date for the range return $form ->schema([ Forms\Components\Section::make() ->schema([ + Select::make('predefinedRange') + ->label('Time Range') + ->options([ + '3_hours' => 'Last 3 Hours', + '6_hours' => 'Last 6 Hours', + '12_hours' => 'Last 12 Hours', + '24_hours' => 'Last 24 Hours', + '1_week' => 'Last 1 Week', + '1_month' => 'Last 1 Month', + '3_months' => 'Last 3 Months', + '6_months' => 'Last 6 Months', + 'custom' => 'Custom Range', + ]) + ->reactive() + ->afterStateUpdated(function ($state, callable $set) { + switch ($state) { + case '3_hours': + $set('startDate', now()->subHours(3)->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '6_hours': + $set('startDate', now()->subHours(6)->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '12_hours': + $set('startDate', now()->subHours(12)->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '24_hours': + $set('startDate', now()->subDay()->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '1_week': + $set('startDate', now()->subWeek()->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '1_month': + $set('startDate', now()->subMonth()->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '3_months': + $set('startDate', now()->subMonths(3)->toDateString()); + $set('endDate', now()->toDateString()); + break; + case '6_months': + $set('startDate', now()->subMonths(6)->toDateString()); + $set('endDate', now()->toDateString()); + break; + case 'custom': + break; + } + }) + ->default('custom'), + DatePicker::make('startDate') - ->default($startDate), + ->label('Start Date') + ->default($defaultStartDate->toDateString()) + ->reactive() + ->hidden(fn ($get) => $get('predefinedRange') !== 'custom'), + DatePicker::make('endDate') - ->default($endDate), + ->label('End Date') + ->default($defaultEndDate->toDateString()) + ->reactive() + ->hidden(fn ($get) => $get('predefinedRange') !== 'custom'), ]) ->columns([ 'default' => 1, - 'sm' => 2, + 'sm' => 3, ]), ]); } diff --git a/config/app.php b/config/app.php index 60b38f700..2f757b84e 100644 --- a/config/app.php +++ b/config/app.php @@ -14,4 +14,6 @@ 'force_https' => env('FORCE_HTTPS', false), + 'chart_time_range' => env('CHART_TIME_RANGE', 7), + ]; diff --git a/config/speedtest.php b/config/speedtest.php index b43c785b5..6e3654552 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -17,8 +17,6 @@ 'public_dashboard' => env('PUBLIC_DASHBOARD', false), - 'chart_time_range' => env('CHART_TIME_RANGE', 7), - /** * Polling settings. */ From afcf6b3888b0ff239996701e896ed0a8c9340347 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Tue, 13 Aug 2024 17:30:42 +0200 Subject: [PATCH 08/17] remove whiteline --- config/speedtest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/speedtest.php b/config/speedtest.php index 6e3654552..8a55110c7 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -35,6 +35,4 @@ 'servers' => env('SPEEDTEST_SERVERS', ''), - - ]; From 756b324085fa32272e65393ca6aef746c274743c Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 15 Aug 2024 15:56:54 +0200 Subject: [PATCH 09/17] Add_env_for_chart_start --- app/Filament/Widgets/RecentDownloadChartWidget.php | 4 ++-- app/Filament/Widgets/RecentDownloadLatencyChartWidget.php | 2 +- app/Filament/Widgets/RecentJitterChartWidget.php | 2 +- app/Filament/Widgets/RecentPingChartWidget.php | 2 +- app/Filament/Widgets/RecentUploadChartWidget.php | 2 +- app/Filament/Widgets/RecentUploadLatencyChartWidget.php | 2 +- config/app.php | 2 ++ 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index e6a9d7b23..e50ada7ff 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -49,7 +49,7 @@ protected function getData(): array 'borderColor' => 'rgba(14, 165, 233)', 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', // 10% opacity 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => true, + 'fill' => config('app.chart_fill_background'), 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, 'pointRadius' => count($downloads) <= 5 ? 3 : 0, @@ -80,7 +80,7 @@ protected function getOptions(): array ], 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index 4ac6f416d..da69c1199 100644 --- a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php @@ -82,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index e1dae4dea..70ccd03c4 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -82,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 7c142f59a..5233c3abc 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -79,7 +79,7 @@ protected function getOptions(): array ], 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index ef24b76e1..3bf0d5ba0 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -80,7 +80,7 @@ protected function getOptions(): array ], 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 5dcc830e9..9c08663b4 100644 --- a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php @@ -82,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => false, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/config/app.php b/config/app.php index 2f757b84e..025fa8f90 100644 --- a/config/app.php +++ b/config/app.php @@ -16,4 +16,6 @@ 'chart_time_range' => env('CHART_TIME_RANGE', 7), + 'chart_begin_at_zero' => env('CHART_BEGIN_AT_ZERO', true), + ]; From 1c4a92c6fd64f218988eae0e0efac97d125d6661 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 15 Aug 2024 16:11:58 +0200 Subject: [PATCH 10/17] change-env-and_time-ranges --- app/Filament/Pages/Dashboard.php | 27 +------------------ .../Widgets/RecentDownloadChartWidget.php | 2 +- config/app.php | 2 +- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 1102b2896..da99d0a43 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -46,7 +46,7 @@ public function getSubheading(): ?string public function filtersForm(Form $form): Form { // Retrieve the default number of days from the configuration - $defaultRangeDays = config('app.chart_time_range'); + $defaultRangeDays = config('app.chart_default_date_range'); // Calculate the start and end dates based on the configuration value $defaultEndDate = now(); // Today @@ -59,31 +59,14 @@ public function filtersForm(Form $form): Form Select::make('predefinedRange') ->label('Time Range') ->options([ - '3_hours' => 'Last 3 Hours', - '6_hours' => 'Last 6 Hours', - '12_hours' => 'Last 12 Hours', '24_hours' => 'Last 24 Hours', '1_week' => 'Last 1 Week', '1_month' => 'Last 1 Month', - '3_months' => 'Last 3 Months', - '6_months' => 'Last 6 Months', 'custom' => 'Custom Range', ]) ->reactive() ->afterStateUpdated(function ($state, callable $set) { switch ($state) { - case '3_hours': - $set('startDate', now()->subHours(3)->toDateString()); - $set('endDate', now()->toDateString()); - break; - case '6_hours': - $set('startDate', now()->subHours(6)->toDateString()); - $set('endDate', now()->toDateString()); - break; - case '12_hours': - $set('startDate', now()->subHours(12)->toDateString()); - $set('endDate', now()->toDateString()); - break; case '24_hours': $set('startDate', now()->subDay()->toDateString()); $set('endDate', now()->toDateString()); @@ -96,14 +79,6 @@ public function filtersForm(Form $form): Form $set('startDate', now()->subMonth()->toDateString()); $set('endDate', now()->toDateString()); break; - case '3_months': - $set('startDate', now()->subMonths(3)->toDateString()); - $set('endDate', now()->toDateString()); - break; - case '6_months': - $set('startDate', now()->subMonths(6)->toDateString()); - $set('endDate', now()->toDateString()); - break; case 'custom': break; } diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index e50ada7ff..14ed3356b 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -49,7 +49,7 @@ protected function getData(): array 'borderColor' => 'rgba(14, 165, 233)', 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', // 10% opacity 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => config('app.chart_fill_background'), + 'fill' => true, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, 'pointRadius' => count($downloads) <= 5 ? 3 : 0, diff --git a/config/app.php b/config/app.php index 025fa8f90..1d98019cf 100644 --- a/config/app.php +++ b/config/app.php @@ -14,7 +14,7 @@ 'force_https' => env('FORCE_HTTPS', false), - 'chart_time_range' => env('CHART_TIME_RANGE', 7), + 'chart_default_date_range' => env('CHART_DEFAULT_DATE_RANGE', 7), 'chart_begin_at_zero' => env('CHART_BEGIN_AT_ZERO', true), From 249340e3198254feafe8bba242790d1f6c6c197b Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Wed, 21 Aug 2024 20:28:21 +0200 Subject: [PATCH 11/17] change_average_to_orange --- app/Filament/Widgets/RecentDownloadChartWidget.php | 4 ++-- app/Filament/Widgets/RecentPingChartWidget.php | 4 ++-- app/Filament/Widgets/RecentUploadChartWidget.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 14ed3356b..1a34fecb1 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -57,8 +57,8 @@ protected function getData(): array [ 'label' => 'Average', 'data' => array_fill(0, count($downloads), $averageDownload), - 'borderColor' => '#ff0000', - 'pointBackgroundColor' => '#ff0000', + 'borderColor' => 'rgb(255, 165, 0)', + 'pointBackgroundColor' => 'rgb(255, 165, 0)', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 5233c3abc..1135c8062 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -56,8 +56,8 @@ protected function getData(): array [ 'label' => 'Average', 'data' => array_fill(0, count($ping), $averagePing), - 'borderColor' => '#ff0000', - 'pointBackgroundColor' => '#ff0000', + 'borderColor' => 'rgb(255, 165, 0)', + 'pointBackgroundColor' => 'rgb(255, 165, 0)', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index 3bf0d5ba0..7cf40e16c 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -57,8 +57,8 @@ protected function getData(): array [ 'label' => 'Average', 'data' => array_fill(0, count($upload), $averageUpload), - 'borderColor' => '#ff0000', - 'pointBackgroundColor' => '#ff0000', + 'borderColor' => 'rgb(255, 165, 0)', + 'pointBackgroundColor' => 'rgb(255, 165, 0)', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, From 68389f1105d6770335eebbde53b40d4c4fb5462f Mon Sep 17 00:00:00 2001 From: Sven van Ginkel Date: Wed, 21 Aug 2024 21:14:18 +0200 Subject: [PATCH 12/17] Revert "Add failed and thresholds" --- app/Filament/Pages/Dashboard.php | 80 +------------------ .../Widgets/RecentDownloadChartWidget.php | 57 ++++++------- .../RecentDownloadLatencyChartWidget.php | 65 ++++++++------- .../Widgets/RecentJitterChartWidget.php | 65 ++++++++------- .../Widgets/RecentPingChartWidget.php | 60 ++++++-------- .../Widgets/RecentUploadChartWidget.php | 57 ++++++------- .../RecentUploadLatencyChartWidget.php | 65 ++++++++------- config/app.php | 4 - .../views/filament/pages/dashboard.blade.php | 3 + 9 files changed, 192 insertions(+), 264 deletions(-) create mode 100644 resources/views/filament/pages/dashboard.blade.php diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index da99d0a43..3737cf9bf 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -14,22 +14,17 @@ use Cron\CronExpression; use Filament\Actions\Action; use Filament\Actions\ActionGroup; -use Filament\Forms; -use Filament\Forms\Components\DatePicker; -use Filament\Forms\Components\Select; -use Filament\Forms\Form; use Filament\Notifications\Notification; -use Filament\Pages\Dashboard as BaseDashboard; -use Filament\Pages\Dashboard\Concerns\HasFiltersForm; +use Filament\Pages\Dashboard as BasePage; use Filament\Support\Enums\IconPosition; use Illuminate\Support\Arr; -class Dashboard extends BaseDashboard +class Dashboard extends BasePage { - use HasFiltersForm; - protected static ?string $navigationIcon = 'heroicon-o-chart-bar'; + protected static string $view = 'filament.pages.dashboard'; + public function getSubheading(): ?string { if (blank(config('speedtest.schedule'))) { @@ -43,67 +38,6 @@ public function getSubheading(): ?string return 'Next speedtest at: '.$nextRunDate; } - public function filtersForm(Form $form): Form - { - // Retrieve the default number of days from the configuration - $defaultRangeDays = config('app.chart_default_date_range'); - - // Calculate the start and end dates based on the configuration value - $defaultEndDate = now(); // Today - $defaultStartDate = now()->subDays($defaultRangeDays); // Start date for the range - - return $form - ->schema([ - Forms\Components\Section::make() - ->schema([ - Select::make('predefinedRange') - ->label('Time Range') - ->options([ - '24_hours' => 'Last 24 Hours', - '1_week' => 'Last 1 Week', - '1_month' => 'Last 1 Month', - 'custom' => 'Custom Range', - ]) - ->reactive() - ->afterStateUpdated(function ($state, callable $set) { - switch ($state) { - case '24_hours': - $set('startDate', now()->subDay()->toDateString()); - $set('endDate', now()->toDateString()); - break; - case '1_week': - $set('startDate', now()->subWeek()->toDateString()); - $set('endDate', now()->toDateString()); - break; - case '1_month': - $set('startDate', now()->subMonth()->toDateString()); - $set('endDate', now()->toDateString()); - break; - case 'custom': - break; - } - }) - ->default('custom'), - - DatePicker::make('startDate') - ->label('Start Date') - ->default($defaultStartDate->toDateString()) - ->reactive() - ->hidden(fn ($get) => $get('predefinedRange') !== 'custom'), - - DatePicker::make('endDate') - ->label('End Date') - ->default($defaultEndDate->toDateString()) - ->reactive() - ->hidden(fn ($get) => $get('predefinedRange') !== 'custom'), - ]) - ->columns([ - 'default' => 1, - 'sm' => 3, - ]), - ]); - } - protected function getHeaderActions(): array { return [ @@ -149,12 +83,6 @@ protected function getHeaderWidgets(): array { return [ StatsOverviewWidget::make(), - ]; - } - - public function getWidgets(): array - { - return [ RecentDownloadChartWidget::make(), RecentUploadChartWidget::make(), RecentPingChartWidget::make(), diff --git a/app/Filament/Widgets/RecentDownloadChartWidget.php b/app/Filament/Widgets/RecentDownloadChartWidget.php index 1a34fecb1..7aca1d1df 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -6,64 +6,59 @@ use App\Helpers\Number; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentDownloadChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Download (Mbps)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'download', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); - $downloads = $results->map(fn ($item) => ! blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0); - $averageDownload = $downloads->avg(); - return [ 'datasets' => [ [ 'label' => 'Download', - 'data' => $downloads, - 'borderColor' => 'rgba(14, 165, 233)', - 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', // 10% opacity - 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => true, - 'cubicInterpolationMode' => 'monotone', - 'tension' => 0.4, - 'pointRadius' => count($downloads) <= 5 ? 3 : 0, - ], - [ - 'label' => 'Average', - 'data' => array_fill(0, count($downloads), $averageDownload), - 'borderColor' => 'rgb(255, 165, 0)', - 'pointBackgroundColor' => 'rgb(255, 165, 0)', + 'data' => $results->map(fn ($item) => ! blank($item->download) ? Number::bitsToMagnitude(bits: $item->download_bits, precision: 2, magnitude: 'mbit') : 0), + 'borderColor' => '#0ea5e9', + 'backgroundColor' => '#0ea5e9', + 'pointBackgroundColor' => '#0ea5e9', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'borderDash' => [5, 5], - 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -75,12 +70,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => true, + 'display' => false, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index da69c1199..c7bb0e05a 100644 --- a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php @@ -5,35 +5,45 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentDownloadLatencyChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Download Latency'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); @@ -41,36 +51,33 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Average (ms)', - 'data' => $averageData = $results->map(fn ($item) => $item->download_latency_iqm ? number_format($item->download_latency_iqm, 2) : 0), - 'borderColor' => 'rgba(16, 185, 129)', - 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', - 'pointBackgroundColor' => 'rgba(16, 185, 129)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->download_latency_iqm ? number_format($item->download_latency_iqm, 2) : 0), + 'borderColor' => '#10b981', + 'backgroundColor' => '#10b981', + 'pointBackgroundColor' => '#10b981', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $averageData->count() <= 5 ? 3 : 0, ], [ 'label' => 'High (ms)', - 'data' => $highData = $results->map(fn ($item) => $item->download_latency_high ? number_format($item->download_latency_high, 2) : 0), - 'borderColor' => 'rgba(14, 165, 233)', - 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', - 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->download_latency_high ? number_format($item->download_latency_high, 2) : 0), + 'borderColor' => '#0ea5e9', + 'backgroundColor' => '#0ea5e9', + 'pointBackgroundColor' => '#0ea5e9', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $highData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Low (ms)', - 'data' => $lowData = $results->map(fn ($item) => $item->download_latency_low ? number_format($item->download_latency_low, 2) : 0), - 'borderColor' => 'rgba(139, 92, 246)', - 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', - 'pointBackgroundColor' => 'rgba(139, 92, 246)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->download_latency_low ? number_format($item->download_latency_low, 2) : 0), + 'borderColor' => '#8b5cf6', + 'backgroundColor' => '#8b5cf6', + 'pointBackgroundColor' => '#8b5cf6', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -82,7 +89,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index 70ccd03c4..2d2454862 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -5,35 +5,45 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentJitterChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Jitter'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); @@ -41,36 +51,33 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Download (ms)', - 'data' => $downloadData = $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0), - 'borderColor' => 'rgba(14, 165, 233)', - 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', - 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0), + 'borderColor' => '#0ea5e9', + 'backgroundColor' => '#0ea5e9', + 'pointBackgroundColor' => '#0ea5e9', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $downloadData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Upload (ms)', - 'data' => $uploadData = $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0), - 'borderColor' => 'rgba(139, 92, 246)', - 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', - 'pointBackgroundColor' => 'rgba(139, 92, 246)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0), + 'borderColor' => '#8b5cf6', + 'backgroundColor' => '#8b5cf6', + 'pointBackgroundColor' => '#8b5cf6', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $uploadData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Ping (ms)', - 'data' => $pingData = $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0), - 'borderColor' => 'rgba(16, 185, 129)', - 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', - 'pointBackgroundColor' => 'rgba(16, 185, 129)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0), + 'borderColor' => '#10b981', + 'backgroundColor' => '#10b981', + 'pointBackgroundColor' => '#10b981', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $pingData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -82,7 +89,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 1135c8062..57a10eee5 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -5,64 +5,59 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentPingChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Ping (ms)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'ping', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); - $ping = $results->map(fn ($item) => ! blank($item->ping) ? number_format($item->ping, 2) : 0); - $averagePing = $ping->avg(); - return [ 'datasets' => [ [ 'label' => 'Ping (ms)', - 'data' => $ping, - 'borderColor' => 'rgba(16, 185, 129)', - 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', - 'pointBackgroundColor' => 'rgba(16, 185, 129)', - 'fill' => true, - 'cubicInterpolationMode' => 'monotone', - 'tension' => 0.4, - 'pointRadius' => count($ping) <= 5 ? 3 : 0, - ], - [ - 'label' => 'Average', - 'data' => array_fill(0, count($ping), $averagePing), - 'borderColor' => 'rgb(255, 165, 0)', - 'pointBackgroundColor' => 'rgb(255, 165, 0)', + 'data' => $results->map(fn ($item) => ! blank($item->ping) ? number_format($item->ping, 2) : 0), + 'borderColor' => '#10b981', + 'backgroundColor' => '#10b981', + 'pointBackgroundColor' => '#10b981', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'borderDash' => [5, 5], - 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -72,14 +67,9 @@ protected function getData(): array protected function getOptions(): array { return [ - 'plugins' => [ - 'legend' => [ - 'display' => true, - ], - ], 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index 7cf40e16c..5c9c85baf 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -6,64 +6,59 @@ use App\Helpers\Number; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentUploadChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Upload (Mbps)'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'upload', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); - $upload = $results->map(fn ($item) => ! blank($item->upload) ? Number::bitsToMagnitude(bits: $item->upload_bits, precision: 2, magnitude: 'mbit') : 0); - $averageUpload = $upload->avg(); - return [ 'datasets' => [ [ 'label' => 'Upload', - 'data' => $upload, - 'borderColor' => 'rgba(139, 92, 246)', - 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', - 'pointBackgroundColor' => 'rgba(139, 92, 246)', - 'fill' => true, - 'cubicInterpolationMode' => 'monotone', - 'tension' => 0.4, - 'pointRadius' => count($upload) <= 5 ? 3 : 0, - ], - [ - 'label' => 'Average', - 'data' => array_fill(0, count($upload), $averageUpload), - 'borderColor' => 'rgb(255, 165, 0)', - 'pointBackgroundColor' => 'rgb(255, 165, 0)', + 'data' => $results->map(fn ($item) => ! blank($item->upload) ? Number::bitsToMagnitude(bits: $item->upload_bits, precision: 2, magnitude: 'mbit') : 0), + 'borderColor' => '#8b5cf6', + 'backgroundColor' => '#8b5cf6', + 'pointBackgroundColor' => '#8b5cf6', 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'borderDash' => [5, 5], - 'pointRadius' => 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -75,12 +70,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => true, + 'display' => false, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 9c08663b4..049055adc 100644 --- a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php +++ b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php @@ -5,35 +5,45 @@ use App\Enums\ResultStatus; use App\Models\Result; use Filament\Widgets\ChartWidget; -use Filament\Widgets\Concerns\InteractsWithPageFilters; -use Illuminate\Database\Eloquent\Builder; class RecentUploadLatencyChartWidget extends ChartWidget { - use InteractsWithPageFilters; - protected static ?string $heading = 'Upload Latency'; protected int|string|array $columnSpan = 'full'; protected static ?string $maxHeight = '250px'; + public ?string $filter = '24h'; + protected function getPollingInterval(): ?string { return config('speedtest.dashboard_polling'); } - protected function getData(): array + protected function getFilters(): ?array { + return [ + '24h' => 'Last 24h', + 'week' => 'Last week', + 'month' => 'Last month', + ]; + } - $startDate = $this->filters['startDate'] ?? now()->subWeek(); - $endDate = $this->filters['endDate'] ?? now(); - + protected function getData(): array + { $results = Result::query() ->select(['id', 'data', 'created_at']) ->where('status', '=', ResultStatus::Completed) - ->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate)) - ->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate)) + ->when($this->filter == '24h', function ($query) { + $query->where('created_at', '>=', now()->subDay()); + }) + ->when($this->filter == 'week', function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }) + ->when($this->filter == 'month', function ($query) { + $query->where('created_at', '>=', now()->subMonth()); + }) ->orderBy('created_at') ->get(); @@ -41,36 +51,33 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Average (ms)', - 'data' => $averageData = $results->map(fn ($item) => $item->upload_latency_iqm ? number_format($item->upload_latency_iqm, 2) : 0), - 'borderColor' => 'rgba(16, 185, 129)', - 'backgroundColor' => 'rgba(16, 185, 129, 0.1)', - 'pointBackgroundColor' => 'rgba(16, 185, 129)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->upload_latency_iqm ? number_format($item->upload_latency_iqm, 2) : 0), + 'borderColor' => '#10b981', + 'backgroundColor' => '#10b981', + 'pointBackgroundColor' => '#10b981', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $averageData->count() <= 5 ? 3 : 0, ], [ 'label' => 'High (ms)', - 'data' => $highData = $results->map(fn ($item) => $item->upload_latency_high ? number_format($item->upload_latency_high, 2) : 0), - 'borderColor' => 'rgba(14, 165, 233)', - 'backgroundColor' => 'rgba(14, 165, 233, 0.1)', - 'pointBackgroundColor' => 'rgba(14, 165, 233)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->upload_latency_high ? number_format($item->upload_latency_high, 2) : 0), + 'borderColor' => '#0ea5e9', + 'backgroundColor' => '#0ea5e9', + 'pointBackgroundColor' => '#0ea5e9', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $highData->count() <= 5 ? 3 : 0, ], [ 'label' => 'Low (ms)', - 'data' => $lowData = $results->map(fn ($item) => $item->upload_latency_low ? number_format($item->upload_latency_low, 2) : 0), - 'borderColor' => 'rgba(139, 92, 246)', - 'backgroundColor' => 'rgba(139, 92, 246, 0.1)', - 'pointBackgroundColor' => 'rgba(139, 92, 246)', - 'fill' => true, + 'data' => $results->map(fn ($item) => $item->upload_latency_low ? number_format($item->upload_latency_low, 2) : 0), + 'borderColor' => '#8b5cf6', + 'backgroundColor' => '#8b5cf6', + 'pointBackgroundColor' => '#8b5cf6', + 'fill' => false, 'cubicInterpolationMode' => 'monotone', 'tension' => 0.4, - 'pointRadius' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -82,7 +89,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => config('app.chart_begin_at_zero'), + 'beginAtZero' => true, ], ], ]; diff --git a/config/app.php b/config/app.php index 1d98019cf..60b38f700 100644 --- a/config/app.php +++ b/config/app.php @@ -14,8 +14,4 @@ 'force_https' => env('FORCE_HTTPS', false), - 'chart_default_date_range' => env('CHART_DEFAULT_DATE_RANGE', 7), - - 'chart_begin_at_zero' => env('CHART_BEGIN_AT_ZERO', true), - ]; diff --git a/resources/views/filament/pages/dashboard.blade.php b/resources/views/filament/pages/dashboard.blade.php new file mode 100644 index 000000000..f351a1c97 --- /dev/null +++ b/resources/views/filament/pages/dashboard.blade.php @@ -0,0 +1,3 @@ + + {{-- Silence is golden --}} + From ce2e0e8940de678d4108abe5d4f4bc5b501b5875 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Wed, 18 Sep 2024 22:19:26 +0200 Subject: [PATCH 13/17] first commit --- app/Enums/ResultStatus.php | 5 +- app/Events/SpeedtestSkipped.php | 20 +++ app/Filament/Resources/ResultResource.php | 4 + app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php | 137 ++++++++++++++++-- config/speedtest.php | 5 + 5 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 app/Events/SpeedtestSkipped.php diff --git a/app/Enums/ResultStatus.php b/app/Enums/ResultStatus.php index 9445e03b8..bad34ff44 100644 --- a/app/Enums/ResultStatus.php +++ b/app/Enums/ResultStatus.php @@ -9,7 +9,8 @@ enum ResultStatus: string implements HasColor, HasLabel { case Completed = 'completed'; // a speedtest that ran successfully. case Failed = 'failed'; // a speedtest that failed to run successfully. - case Started = 'started'; // a speedtest that has been started by a worker but has not finish running. + case Started = 'started'; // a speedtest that has been started by a worker but has not finished running. + case Skipped = 'skipped'; // a speedtest that was skipped. public function getColor(): ?string { @@ -17,6 +18,7 @@ public function getColor(): ?string self::Completed => 'success', self::Failed => 'danger', self::Started => 'warning', + self::Skipped => 'info', // Adding Skipped state with a color }; } @@ -26,6 +28,7 @@ public function getLabel(): ?string self::Completed => 'Completed', self::Failed => 'Failed', self::Started => 'Started', + self::Skipped => 'Skipped', }; } } diff --git a/app/Events/SpeedtestSkipped.php b/app/Events/SpeedtestSkipped.php new file mode 100644 index 000000000..550efa380 --- /dev/null +++ b/app/Events/SpeedtestSkipped.php @@ -0,0 +1,20 @@ +hint(new HtmlString('🔗Error Messages')) ->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Failed) ->columnSpanFull(), + Forms\Components\Textarea::make('data.message') + ->label('Skip Message') + ->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Skipped) + ->columnSpanFull(), ]) ->columnSpan(2), Forms\Components\Section::make() diff --git a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php index 3e3d591f9..593edb725 100644 --- a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php +++ b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php @@ -5,6 +5,7 @@ use App\Enums\ResultStatus; use App\Events\SpeedtestCompleted; use App\Events\SpeedtestFailed; +use App\Events\SpeedtestSkipped; use App\Models\Result; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; @@ -46,6 +47,52 @@ public function handle(): void return; } + // Fetch public IP data + $publicIpData = $this->getPublicIp(); + + $currentIp = $publicIpData['query'] ?? 'unknown'; + $currentIsp = $publicIpData['isp'] ?? 'unknown'; + + // Retrieve SPEEDTEST_SKIP_IP Settings + $skipSettings = array_filter(array_map('trim', explode(';', config('speedtest.skip_ip')))); + + // Check Each Skip Setting + foreach ($skipSettings as $setting) { + + if (filter_var($setting, FILTER_VALIDATE_IP)) { + + if ($currentIp === $setting) { + $this->markAsSkipped( + "Current public IP address ($currentIp) was listed to be skipped for testing.", + $currentIp, + $currentIsp + ); + + return; + } + } elseif (strpos($setting, '/') !== false) { + + if ($this->ipInSubnet($currentIp, $setting)) { + $this->markAsSkipped( + "Current public IP address ($currentIp) falls within the excluded subnet ($setting).", + $currentIp, + $currentIsp + ); + + return; + } + } elseif (strcasecmp($currentIsp, $setting) === 0) { + $this->markAsSkipped( + "Current ISP ($currentIsp) was listed to be skipped for testing.", + $currentIp, + $currentIsp + ); + + return; + } + } + + // Execute Speedtest $options = array_filter([ 'speedtest', '--accept-license', @@ -61,13 +108,42 @@ public function handle(): void } catch (ProcessFailedException $exception) { $messages = explode(PHP_EOL, $exception->getMessage()); - $message = collect(array_filter($messages, 'json_validate'))->last(); + // Extract only the "message" part from each JSON error message + $errorMessages = array_map(function ($message) { + $decoded = json_decode($message, true); + if (json_last_error() === JSON_ERROR_NONE && isset($decoded['message'])) { + return $decoded['message']; + } - $this->result->update([ - 'server_id' => $this->serverId, - 'data' => json_decode($message, true), - 'status' => ResultStatus::Failed, - ]); + return ''; // If it's not valid JSON or doesn't contain "message", return an empty string + }, $messages); + + // Filter out empty messages and concatenate + $errorMessage = implode(' | ', array_filter($errorMessages)); + + // Add server ID to the error message if it exists + if ($this->serverId !== null) { + $this->result->update([ + 'data' => [ + 'type' => 'log', + 'level' => 'error', + 'message' => $errorMessage, + 'server' => [ + 'id' => $this->serverId, + ], + ], + 'status' => ResultStatus::Failed, + ]); + } else { + $this->result->update([ + 'data' => [ + 'type' => 'log', + 'level' => 'error', + 'message' => $errorMessage, + ], + 'status' => ResultStatus::Failed, + ]); + } SpeedtestFailed::dispatch($this->result); @@ -87,18 +163,61 @@ public function handle(): void SpeedtestCompleted::dispatch($this->result); } + protected function getPublicIp(): array + { + // Implement method to fetch public IP data + $response = file_get_contents('http://ip-api.com/json/?fields=25088'); + + return json_decode($response, true) ?? []; + } + + protected function markAsSkipped(string $message, string $currentIp, string $currentIsp): void + { + $this->result->update([ + 'data' => ['type' => 'log', 'level' => 'info', 'message' => $message], + 'data' => [ + 'type' => 'log', + 'level' => 'info', + 'message' => $message, + 'isp' => $currentIsp, + 'interface' => [ + 'externalIp' => $currentIp, + ], + ], + 'status' => ResultStatus::Skipped, // Use Skipped status + ]); + SpeedtestSkipped::dispatch($this->result); // Dispatch skipped event + } + + protected function markAsFailed(string $message): void + { + $this->result->update([ + 'data' => ['type' => 'log', 'level' => 'error', 'message' => $message], + 'status' => ResultStatus::Failed, + ]); + SpeedtestFailed::dispatch($this->result); + } + + protected function ipInSubnet(string $ip, string $subnet): bool + { + [$subnet, $mask] = explode('/', $subnet) + [1 => '32']; + $subnetDecimal = ip2long($subnet); + $ipDecimal = ip2long($ip); + $maskDecimal = ~((1 << (32 - (int) $mask)) - 1); + + return ($subnetDecimal & $maskDecimal) === ($ipDecimal & $maskDecimal); + } + protected function checkForInternetConnection(): bool { $url = config('speedtest.ping_url'); - // Skip checking for internet connection if ping url isn't set (disabled) if (blank($url)) { return true; } if (! URL::isValidUrl($url)) { $this->result->update([ - 'server_id' => $this->serverId, 'data' => [ 'type' => 'log', 'level' => 'error', @@ -112,7 +231,6 @@ protected function checkForInternetConnection(): bool return false; } - // Remove http:// or https:// from the URL if present $url = preg_replace('/^https?:\/\//', '', $url); $ping = new Ping( @@ -122,7 +240,6 @@ protected function checkForInternetConnection(): bool if ($ping->ping() === false) { $this->result->update([ - 'server_id' => $this->serverId, 'data' => [ 'type' => 'log', 'level' => 'error', diff --git a/config/speedtest.php b/config/speedtest.php index 8a55110c7..2983dd2ce 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -35,4 +35,9 @@ 'servers' => env('SPEEDTEST_SERVERS', ''), + /** + * IP filtering settings. + */ + 'skip_ip' => env('SPEEDTEST_SKIP_IP', ''), // Comma-separated list of IPs, ISPs, ASNs, and subnets + ]; From baba35464ce9a44839ea0bc2df3cff91e4a62ad3 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Mon, 23 Sep 2024 15:37:35 +0200 Subject: [PATCH 14/17] change_api --- app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php index 593edb725..971024814 100644 --- a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php +++ b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php @@ -50,8 +50,8 @@ public function handle(): void // Fetch public IP data $publicIpData = $this->getPublicIp(); - $currentIp = $publicIpData['query'] ?? 'unknown'; - $currentIsp = $publicIpData['isp'] ?? 'unknown'; + $currentIp = $publicIpData['ip'] ?? 'unknown'; + $currentIsp = $publicIpData['org'] ?? 'unknown'; // Retrieve SPEEDTEST_SKIP_IP Settings $skipSettings = array_filter(array_map('trim', explode(';', config('speedtest.skip_ip')))); @@ -166,7 +166,7 @@ public function handle(): void protected function getPublicIp(): array { // Implement method to fetch public IP data - $response = file_get_contents('http://ip-api.com/json/?fields=25088'); + $response = file_get_contents('https://ipapi.co/json/'); return json_decode($response, true) ?? []; } From 46d8eaa2b3f8a5111857fa8b73a9a71b3b5be6e3 Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 26 Sep 2024 20:09:07 +0200 Subject: [PATCH 15/17] update comments --- config/speedtest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/speedtest.php b/config/speedtest.php index 2983dd2ce..d58d36011 100644 --- a/config/speedtest.php +++ b/config/speedtest.php @@ -38,6 +38,6 @@ /** * IP filtering settings. */ - 'skip_ip' => env('SPEEDTEST_SKIP_IP', ''), // Comma-separated list of IPs, ISPs, ASNs, and subnets + 'skip_ip' => env('SPEEDTEST_SKIP_IP', ''), // Comma-separated list of IPs, ISPs and subnets ]; From 37dc39c026c0acdecbf069d34aa82828bcb9d75c Mon Sep 17 00:00:00 2001 From: svenvg93 Date: Thu, 10 Oct 2024 10:03:45 +0200 Subject: [PATCH 16/17] Simplify --- app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php | 68 +++++++------------ 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php index 6523d28e3..11075936e 100644 --- a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php +++ b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php @@ -47,48 +47,32 @@ public function handle(): void return; } - // Fetch public IP data - $publicIpData = $this->getPublicIp(); - - $currentIp = $publicIpData['ip'] ?? 'unknown'; - $currentIsp = $publicIpData['org'] ?? 'unknown'; + // Fetch public IP data using icanhazip.com + $currentIp = $this->getPublicIp(); // Retrieve SPEEDTEST_SKIP_IP Settings $skipSettings = array_filter(array_map('trim', explode(';', config('speedtest.skip_ip')))); // Check Each Skip Setting foreach ($skipSettings as $setting) { - if (filter_var($setting, FILTER_VALIDATE_IP)) { - if ($currentIp === $setting) { $this->markAsSkipped( "Current public IP address ($currentIp) was listed to be skipped for testing.", - $currentIp, - $currentIsp + $currentIp ); return; } } elseif (strpos($setting, '/') !== false) { - if ($this->ipInSubnet($currentIp, $setting)) { $this->markAsSkipped( "Current public IP address ($currentIp) falls within the excluded subnet ($setting).", - $currentIp, - $currentIsp + $currentIp ); return; } - } elseif (strcasecmp($currentIsp, $setting) === 0) { - $this->markAsSkipped( - "Current ISP ($currentIsp) was listed to be skipped for testing.", - $currentIp, - $currentIsp - ); - - return; } } @@ -121,7 +105,6 @@ public function handle(): void // Filter out empty messages and concatenate $errorMessage = implode(' | ', array_filter($errorMessages)); - // Add server ID to the error message if it exists if ($this->serverId !== null) { $this->result->update([ @@ -164,7 +147,6 @@ public function handle(): void 'status' => ResultStatus::Failed, ]); - SpeedtestFailed::dispatch($this->result); return; @@ -183,42 +165,45 @@ public function handle(): void SpeedtestCompleted::dispatch($this->result); } - - protected function getPublicIp(): array + /** + * Get the public IP address using icanhazip.com. + */ + protected function getPublicIp(): string { - // Implement method to fetch public IP data - $response = file_get_contents('https://ipapi.co/json/'); + try { + $response = file_get_contents('https://icanhazip.com'); - return json_decode($response, true) ?? []; + return trim($response); + } catch (\Exception $e) { + \Log::error("Error fetching public IP data: {$e->getMessage()}"); + + return 'unknown'; + } } - protected function markAsSkipped(string $message, string $currentIp, string $currentIsp): void + /** + * Mark the test as skipped with a specific message. + */ + protected function markAsSkipped(string $message, string $currentIp): void { $this->result->update([ - 'data' => ['type' => 'log', 'level' => 'info', 'message' => $message], 'data' => [ 'type' => 'log', 'level' => 'info', 'message' => $message, - 'isp' => $currentIsp, 'interface' => [ 'externalIp' => $currentIp, ], ], - 'status' => ResultStatus::Skipped, // Use Skipped status + 'status' => ResultStatus::Skipped, ]); - SpeedtestSkipped::dispatch($this->result); // Dispatch skipped event - } - protected function markAsFailed(string $message): void - { - $this->result->update([ - 'data' => ['type' => 'log', 'level' => 'error', 'message' => $message], - 'status' => ResultStatus::Failed, - ]); - SpeedtestFailed::dispatch($this->result); + SpeedtestSkipped::dispatch($this->result); } + /** + * Check if IP is in a given subnet. + */ protected function ipInSubnet(string $ip, string $subnet): bool { [$subnet, $mask] = explode('/', $subnet) + [1 => '32']; @@ -229,16 +214,13 @@ protected function ipInSubnet(string $ip, string $subnet): bool return ($subnetDecimal & $maskDecimal) === ($ipDecimal & $maskDecimal); } - /** * Check for internet connection. */ - protected function checkForInternetConnection(): bool { $url = config('speedtest.ping_url'); - // TODO: skip checking for internet connection, current validation does not take into account different host formats and ip addresses. return true; From 605a9c27494da36ddd642b3958e78d6523e90beb Mon Sep 17 00:00:00 2001 From: Sven van Ginkel Date: Tue, 19 Nov 2024 19:42:53 +0100 Subject: [PATCH 17/17] Seperate the IP check --- app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php | 66 +++----------- app/Services/PublicIpService.php | 87 +++++++++++++++++++ 2 files changed, 101 insertions(+), 52 deletions(-) create mode 100644 app/Services/PublicIpService.php diff --git a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php index 829d7e1c8..f3a323dee 100644 --- a/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php +++ b/app/Jobs/Speedtests/ExecuteOoklaSpeedtest.php @@ -7,6 +7,7 @@ use App\Events\SpeedtestFailed; use App\Events\SpeedtestSkipped; use App\Models\Result; +use App\Services\PublicIpService; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -36,6 +37,7 @@ class ExecuteOoklaSpeedtest implements ShouldBeUnique, ShouldQueue public function __construct( public Result $result, public ?int $serverId = null, + protected PublicIpService $publicIpService = new PublicIpService ) {} /** @@ -47,33 +49,21 @@ public function handle(): void return; } - // Fetch public IP data using icanhazip.com - $currentIp = $this->getPublicIp(); + // Fetch public IP data using the PublicIpService + $ipData = $this->publicIpService->getPublicIp(); + $currentIp = $ipData['ip'] ?? 'unknown'; + $isp = $ipData['isp'] ?? 'unknown'; // Get the ISP value here // Retrieve SPEEDTEST_SKIP_IP Settings $skipSettings = array_filter(array_map('trim', explode(';', config('speedtest.skip_ip')))); // Check Each Skip Setting - foreach ($skipSettings as $setting) { - if (filter_var($setting, FILTER_VALIDATE_IP)) { - if ($currentIp === $setting) { - $this->markAsSkipped( - "Current public IP address ($currentIp) was listed to be skipped for testing.", - $currentIp - ); - - return; - } - } elseif (strpos($setting, '/') !== false) { - if ($this->ipInSubnet($currentIp, $setting)) { - $this->markAsSkipped( - "Current public IP address ($currentIp) falls within the excluded subnet ($setting).", - $currentIp - ); - - return; - } - } + $skipMessage = $this->publicIpService->shouldSkipIp($currentIp, $skipSettings); + if ($skipMessage) { + // Pass the $isp along with $currentIp and $skipMessage + $this->markAsSkipped($skipMessage, $currentIp, $isp); + + return; } // Execute Speedtest @@ -165,26 +155,10 @@ public function handle(): void SpeedtestCompleted::dispatch($this->result); } - /** - * Get the public IP address using icanhazip.com. - */ - protected function getPublicIp(): string - { - try { - $response = file_get_contents('https://icanhazip.com'); - - return trim($response); - } catch (\Exception $e) { - \Log::error("Error fetching public IP data: {$e->getMessage()}"); - - return 'unknown'; - } - } - /** * Mark the test as skipped with a specific message. */ - protected function markAsSkipped(string $message, string $currentIp): void + protected function markAsSkipped(string $message, string $currentIp, string $isp): void { $this->result->update([ 'data' => [ @@ -194,6 +168,7 @@ protected function markAsSkipped(string $message, string $currentIp): void 'interface' => [ 'externalIp' => $currentIp, ], + 'isp' => $isp, ], 'status' => ResultStatus::Skipped, ]); @@ -201,19 +176,6 @@ protected function markAsSkipped(string $message, string $currentIp): void SpeedtestSkipped::dispatch($this->result); } - /** - * Check if IP is in a given subnet. - */ - protected function ipInSubnet(string $ip, string $subnet): bool - { - [$subnet, $mask] = explode('/', $subnet) + [1 => '32']; - $subnetDecimal = ip2long($subnet); - $ipDecimal = ip2long($ip); - $maskDecimal = ~((1 << (32 - (int) $mask)) - 1); - - return ($subnetDecimal & $maskDecimal) === ($ipDecimal & $maskDecimal); - } - /** * Check for internet connection. * diff --git a/app/Services/PublicIpService.php b/app/Services/PublicIpService.php new file mode 100644 index 000000000..33c500d01 --- /dev/null +++ b/app/Services/PublicIpService.php @@ -0,0 +1,87 @@ + 'unknown', 'isp' => 'unknown']; + } + + // Decode the JSON response + $data = json_decode($response, true); + + // Validate the response format + if (json_last_error() === JSON_ERROR_NONE && isset($data['ip'])) { + return [ + 'ip' => $data['ip'], + 'isp' => $data['asn_org'] ?? 'unknown', + ]; + } + + // Log error if the response is invalid + \Log::error('Invalid response from ifconfig.co: '.$response); + + return ['ip' => 'unknown', 'isp' => 'unknown']; + } catch (\Exception $e) { + \Log::error("Error fetching public IP data from ifconfig.co: {$e->getMessage()}"); + + // Fallback response + return ['ip' => 'unknown', 'isp' => 'unknown']; + } + } + + /** + * Check if the current IP should be skipped. + */ + public function shouldSkipIp(string $currentIp, array $skipSettings): bool|string + { + foreach ($skipSettings as $setting) { + // Check for exact IP match + if (filter_var($setting, FILTER_VALIDATE_IP)) { + if ($currentIp === $setting) { + return "Current public IP address ($currentIp) is listed to be skipped for testing."; + } + } + + // Check for subnet match + if (strpos($setting, '/') !== false && $this->ipInSubnet($currentIp, $setting)) { + return "Current public IP address ($currentIp) falls within the excluded subnet ($setting)."; + } + } + + return false; + } + + /** + * Check if an IP is in a given subnet. + */ + protected function ipInSubnet(string $ip, string $subnet): bool + { + [$subnet, $mask] = explode('/', $subnet) + [1 => '32']; + $subnetDecimal = ip2long($subnet); + $ipDecimal = ip2long($ip); + $maskDecimal = ~((1 << (32 - (int) $mask)) - 1); + + return ($subnetDecimal & $maskDecimal) === ($ipDecimal & $maskDecimal); + } +}