diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 3737cf9bf..da99d0a43 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -14,16 +14,21 @@ 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 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 +43,67 @@ 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 [ @@ -83,6 +149,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 7aca1d1df..1a34fecb1 100644 --- a/app/Filament/Widgets/RecentDownloadChartWidget.php +++ b/app/Filament/Widgets/RecentDownloadChartWidget.php @@ -6,59 +6,64 @@ 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(); + $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), - 'borderColor' => '#0ea5e9', - 'backgroundColor' => '#0ea5e9', - 'pointBackgroundColor' => '#0ea5e9', + '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)', '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 +75,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => false, + 'display' => true, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php b/app/Filament/Widgets/RecentDownloadLatencyChartWidget.php index c7bb0e05a..da69c1199 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(); @@ -51,33 +41,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' => $averageData->count() <= 5 ? 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' => $highData->count() <= 5 ? 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' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index 2d2454862..70ccd03c4 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(); @@ -51,33 +41,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' => $downloadData->count() <= 5 ? 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' => $uploadData->count() <= 5 ? 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' => $pingData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 57a10eee5..1135c8062 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -5,59 +5,64 @@ 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(); + $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), - 'borderColor' => '#10b981', - 'backgroundColor' => '#10b981', - 'pointBackgroundColor' => '#10b981', + '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)', '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 +72,14 @@ protected function getData(): array protected function getOptions(): array { return [ + 'plugins' => [ + 'legend' => [ + 'display' => true, + ], + ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadChartWidget.php b/app/Filament/Widgets/RecentUploadChartWidget.php index 5c9c85baf..7cf40e16c 100644 --- a/app/Filament/Widgets/RecentUploadChartWidget.php +++ b/app/Filament/Widgets/RecentUploadChartWidget.php @@ -6,59 +6,64 @@ 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(); + $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), - 'borderColor' => '#8b5cf6', - 'backgroundColor' => '#8b5cf6', - 'pointBackgroundColor' => '#8b5cf6', + '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)', '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 +75,12 @@ protected function getOptions(): array return [ 'plugins' => [ 'legend' => [ - 'display' => false, + 'display' => true, ], ], 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/app/Filament/Widgets/RecentUploadLatencyChartWidget.php b/app/Filament/Widgets/RecentUploadLatencyChartWidget.php index 049055adc..9c08663b4 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(); @@ -51,33 +41,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' => $averageData->count() <= 5 ? 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' => $highData->count() <= 5 ? 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' => $lowData->count() <= 5 ? 3 : 0, ], ], 'labels' => $results->map(fn ($item) => $item->created_at->timezone(config('app.display_timezone'))->format(config('app.chart_datetime_format'))), @@ -89,7 +82,7 @@ protected function getOptions(): array return [ 'scales' => [ 'y' => [ - 'beginAtZero' => true, + 'beginAtZero' => config('app.chart_begin_at_zero'), ], ], ]; diff --git a/config/app.php b/config/app.php index 60b38f700..1d98019cf 100644 --- a/config/app.php +++ b/config/app.php @@ -14,4 +14,8 @@ '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 deleted file mode 100644 index f351a1c97..000000000 --- a/resources/views/filament/pages/dashboard.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - {{-- Silence is golden --}} -