From a62ea370f450229b5f60439abdb5aa63eab4222a Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 29 Jan 2026 10:52:20 +0700 Subject: [PATCH] feat: Implement global date range filtering across all dashboard widgets and add corresponding UI controls to the dashboard page. --- app/Filament/Pages/Dashboard.php | 30 ++++++++++++++--- .../Widgets/CooperationStatusChart.php | 7 ++++ app/Filament/Widgets/IssueLocationChart.php | 7 ++++ .../Widgets/JournalistMetricsStats.php | 14 ++++++-- .../Widgets/JournalistRegistrationChart.php | 9 +++++- app/Filament/Widgets/JournalistUkwChart.php | 12 ++++++- .../Widgets/MediaClassificationChart.php | 7 ++++ .../Widgets/MediaCooperationRankingChart.php | 7 ++++ .../Widgets/MediaRegistrationChart.php | 9 +++++- app/Filament/Widgets/MediaTypeChart.php | 7 ++++ app/Filament/Widgets/OverviewStats.php | 32 +++++++++++++++---- app/Filament/Widgets/PopularNewsChart.php | 7 ++++ app/Filament/Widgets/TaskRealizationChart.php | 9 +++++- .../Widgets/TopMediaJournalistChart.php | 7 ++++ .../Widgets/VerificationPerformanceChart.php | 9 +++++- app/Filament/Widgets/VisitorTrafficChart.php | 11 +++++-- 16 files changed, 165 insertions(+), 19 deletions(-) diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index 594ffbc..3f7c71e 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -2,11 +2,13 @@ namespace App\Filament\Pages; +use Filament\Actions\Action; use Filament\Forms\Components\DatePicker; use Filament\Pages\Dashboard as BaseDashboard; use Filament\Pages\Dashboard\Concerns\HasFiltersForm; use Filament\Schemas\Components\Fieldset; use Filament\Schemas\Schema; +use Filament\Support\Icons\Heroicon; class Dashboard extends BaseDashboard { @@ -20,16 +22,36 @@ public function filtersForm(Schema $schema): Schema ->schema([ DatePicker::make('startDate') ->label('Tanggal Mulai') + ->placeholder('Pilih tanggal mulai') ->native(false) - ->placeholder('Pilih tanggal mulai'), + ->displayFormat('l, d F Y'), DatePicker::make('endDate') ->label('Tanggal Selesai') + ->placeholder('Pilih tanggal selesai') ->native(false) - ->placeholder('Pilih tanggal selesai'), + ->displayFormat('l, d F Y'), ]) + ->columns(2) ->columnSpanFull(), - ]) - ->columns(2); + ]); + } + + protected function getHeaderActions(): array + { + return [ + Action::make('reset') + ->label('Reset Filter') + ->icon(Heroicon::XMark) + ->color('danger') + ->action(fn () => $this->resetFilters()) + ->visible(fn () => ! empty($this->filters['startDate']) || ! empty($this->filters['endDate'])), + ]; + } + + public function resetFilters(): void + { + $this->filters['startDate'] = null; + $this->filters['endDate'] = null; } } diff --git a/app/Filament/Widgets/CooperationStatusChart.php b/app/Filament/Widgets/CooperationStatusChart.php index b2678f3..cde5e95 100644 --- a/app/Filament/Widgets/CooperationStatusChart.php +++ b/app/Filament/Widgets/CooperationStatusChart.php @@ -8,6 +8,8 @@ class CooperationStatusChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Status Kerjasama'; protected static ?int $sort = 9; @@ -16,7 +18,12 @@ class CooperationStatusChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = Cooperation::query() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->selectRaw('status, count(*) as total') ->groupBy('status') ->get(); diff --git a/app/Filament/Widgets/IssueLocationChart.php b/app/Filament/Widgets/IssueLocationChart.php index ffefa58..4c21ff8 100644 --- a/app/Filament/Widgets/IssueLocationChart.php +++ b/app/Filament/Widgets/IssueLocationChart.php @@ -9,6 +9,8 @@ class IssueLocationChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Sebaran Isu Berdasarkan Wilayah'; protected static ?int $sort = 15; @@ -17,11 +19,16 @@ class IssueLocationChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $issueTable = (new IssueManagement)->getTable(); $locationTable = (new Location)->getTable(); $data = IssueManagement::query() ->join($locationTable, "{$issueTable}.location_id", '=', "{$locationTable}.id") + ->when($startDate, fn ($q) => $q->whereDate("{$issueTable}.created_at", '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate("{$issueTable}.created_at", '<=', $endDate)) ->select("{$locationTable}.name", DB::raw('count(*) as total')) ->groupBy("{$locationTable}.id", "{$locationTable}.name") ->orderByDesc('total') diff --git a/app/Filament/Widgets/JournalistMetricsStats.php b/app/Filament/Widgets/JournalistMetricsStats.php index 9cee29a..3e4f864 100644 --- a/app/Filament/Widgets/JournalistMetricsStats.php +++ b/app/Filament/Widgets/JournalistMetricsStats.php @@ -10,14 +10,24 @@ class JournalistMetricsStats extends BaseWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected static ?int $sort = 7; protected int|string|array $columnSpan = 1; protected function getStats(): array { - $totalJournalists = Journalist::count(); - $totalMedia = PartnerMedia::count(); + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + + $totalJournalists = Journalist::when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count(); + + $totalMedia = PartnerMedia::when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count(); $average = $totalMedia > 0 ? round($totalJournalists / $totalMedia, 1) : 0; diff --git a/app/Filament/Widgets/JournalistRegistrationChart.php b/app/Filament/Widgets/JournalistRegistrationChart.php index 69ad1b3..34fb0f7 100644 --- a/app/Filament/Widgets/JournalistRegistrationChart.php +++ b/app/Filament/Widgets/JournalistRegistrationChart.php @@ -9,6 +9,8 @@ class JournalistRegistrationChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Tren Pertambahan Jurnalis'; protected static ?int $sort = 8; @@ -17,12 +19,17 @@ class JournalistRegistrationChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = Journalist::query() ->select( DB::raw('COUNT(*) as count'), DB::raw("DATE_FORMAT(created_at, '%Y-%m') as month") ) - ->where('created_at', '>=', now()->subMonths(6)) + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when(! $startDate, fn ($q) => $q->where('created_at', '>=', now()->subMonths(6))) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->groupBy('month') ->orderBy('month') ->get(); diff --git a/app/Filament/Widgets/JournalistUkwChart.php b/app/Filament/Widgets/JournalistUkwChart.php index 2c40614..b239bb9 100644 --- a/app/Filament/Widgets/JournalistUkwChart.php +++ b/app/Filament/Widgets/JournalistUkwChart.php @@ -7,6 +7,8 @@ class JournalistUkwChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Status Sertifikasi UKW Jurnalis'; protected static ?int $sort = 6; @@ -15,14 +17,22 @@ class JournalistUkwChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $certified = Journalist::whereNotNull('ukw_certificate') ->where('ukw_certificate', '!=', '') + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->count(); $notCertified = Journalist::where(function ($query) { $query->whereNull('ukw_certificate') ->orWhere('ukw_certificate', ''); - })->count(); + }) + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count(); return [ 'datasets' => [ diff --git a/app/Filament/Widgets/MediaClassificationChart.php b/app/Filament/Widgets/MediaClassificationChart.php index 960cd03..2a90070 100644 --- a/app/Filament/Widgets/MediaClassificationChart.php +++ b/app/Filament/Widgets/MediaClassificationChart.php @@ -8,6 +8,8 @@ class MediaClassificationChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Klasifikasi Media'; protected static ?int $sort = 3; @@ -16,7 +18,12 @@ class MediaClassificationChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = PartnerMedia::query() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->selectRaw('classification, count(*) as total') ->groupBy('classification') ->get(); diff --git a/app/Filament/Widgets/MediaCooperationRankingChart.php b/app/Filament/Widgets/MediaCooperationRankingChart.php index 3b69e47..e5a6ba3 100644 --- a/app/Filament/Widgets/MediaCooperationRankingChart.php +++ b/app/Filament/Widgets/MediaCooperationRankingChart.php @@ -9,6 +9,8 @@ class MediaCooperationRankingChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Ranking Media (Kerjasama Disetujui)'; protected static ?int $sort = 11; @@ -17,9 +19,14 @@ class MediaCooperationRankingChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = PartnerMedia::query() ->join('cooperation_media', 'partner_media.id', '=', 'cooperation_media.partner_media_id') ->where('cooperation_media.status', ApprovalStatus::ACCEPTED) + ->when($startDate, fn ($q) => $q->whereDate('cooperation_media.created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('cooperation_media.created_at', '<=', $endDate)) ->select('partner_media.name', DB::raw('count(*) as total')) ->groupBy('partner_media.id', 'partner_media.name') ->orderByDesc('total') diff --git a/app/Filament/Widgets/MediaRegistrationChart.php b/app/Filament/Widgets/MediaRegistrationChart.php index ca3a4ba..aa207b2 100644 --- a/app/Filament/Widgets/MediaRegistrationChart.php +++ b/app/Filament/Widgets/MediaRegistrationChart.php @@ -9,6 +9,8 @@ class MediaRegistrationChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Tren Pendaftaran Media'; protected static ?int $sort = 4; @@ -17,12 +19,17 @@ class MediaRegistrationChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = PartnerMedia::query() ->select( DB::raw('COUNT(*) as count'), DB::raw("DATE_FORMAT(created_at, '%Y-%m') as month") ) - ->where('created_at', '>=', now()->subMonths(6)) + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when(! $startDate, fn ($q) => $q->where('created_at', '>=', now()->subMonths(6))) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->groupBy('month') ->orderBy('month') ->get(); diff --git a/app/Filament/Widgets/MediaTypeChart.php b/app/Filament/Widgets/MediaTypeChart.php index 26d65f6..a55ceed 100644 --- a/app/Filament/Widgets/MediaTypeChart.php +++ b/app/Filament/Widgets/MediaTypeChart.php @@ -8,6 +8,8 @@ class MediaTypeChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Distribusi Jenis Media'; protected static ?int $sort = 2; @@ -16,7 +18,12 @@ class MediaTypeChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = PartnerMedia::query() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->selectRaw('type, count(*) as total') ->groupBy('type') ->get(); diff --git a/app/Filament/Widgets/OverviewStats.php b/app/Filament/Widgets/OverviewStats.php index 174ff08..fb0d8be 100644 --- a/app/Filament/Widgets/OverviewStats.php +++ b/app/Filament/Widgets/OverviewStats.php @@ -14,39 +14,59 @@ class OverviewStats extends BaseWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected static ?int $sort = 1; protected int|string|array $columnSpan = 'full'; protected function getStats(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + return [ - Stat::make('Total Perusahaan', Company::count()) + Stat::make('Total Perusahaan', Company::when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count()) ->description('Perusahaan terdaftar') ->descriptionIcon(Heroicon::BuildingOffice2) ->color('primary'), - Stat::make('Media Terverifikasi', PartnerMedia::approved()->count()) + Stat::make('Media Terverifikasi', PartnerMedia::approved() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count()) ->description('Media aktif') ->descriptionIcon(Heroicon::CheckBadge) ->color('success'), - Stat::make('Total Jurnalis', Journalist::count()) + Stat::make('Total Jurnalis', Journalist::when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count()) ->description('Jurnalis terdaftar') ->descriptionIcon(Heroicon::Identification) ->color('info'), - Stat::make('Antrean Verifikasi', VerificationRequest::pending()->count()) + Stat::make('Antrean Verifikasi', VerificationRequest::pending() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count()) ->description('Butuh tindakan') ->descriptionIcon(Heroicon::PencilSquare) ->color('warning'), - Stat::make('Antrean Perubahan', DataChangeRequest::pending()->count()) + Stat::make('Antrean Perubahan', DataChangeRequest::pending() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) + ->count()) ->description('Perubahan data') ->descriptionIcon(Heroicon::DocumentText) ->color('warning'), - Stat::make('Total Pengunjung', Visitor::count()) + Stat::make('Total Pengunjung', Visitor::when($startDate, fn ($q) => $q->whereDate('date', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('date', '<=', $endDate)) + ->count()) ->description('Total traffic') ->descriptionIcon(Heroicon::ChartBar) ->color('info'), diff --git a/app/Filament/Widgets/PopularNewsChart.php b/app/Filament/Widgets/PopularNewsChart.php index 1a7909f..53ea34e 100644 --- a/app/Filament/Widgets/PopularNewsChart.php +++ b/app/Filament/Widgets/PopularNewsChart.php @@ -7,6 +7,8 @@ class PopularNewsChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Berita Terpopuler'; protected static ?int $sort = 13; @@ -15,7 +17,12 @@ class PopularNewsChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = News::query() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->orderByDesc('views') ->limit(10) ->get(); diff --git a/app/Filament/Widgets/TaskRealizationChart.php b/app/Filament/Widgets/TaskRealizationChart.php index 855d5fc..f9b2a19 100644 --- a/app/Filament/Widgets/TaskRealizationChart.php +++ b/app/Filament/Widgets/TaskRealizationChart.php @@ -9,6 +9,8 @@ class TaskRealizationChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Realisasi Penugasan (Laporan)'; protected static ?int $sort = 10; @@ -17,13 +19,18 @@ class TaskRealizationChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = Report::query() ->select( DB::raw('COUNT(*) as count'), DB::raw('status'), DB::raw("DATE_FORMAT(created_at, '%Y-%m') as month") ) - ->where('created_at', '>=', now()->subMonths(6)) + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when(! $startDate, fn ($q) => $q->where('created_at', '>=', now()->subMonths(6))) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->groupBy('month', 'status') ->orderBy('month') ->get(); diff --git a/app/Filament/Widgets/TopMediaJournalistChart.php b/app/Filament/Widgets/TopMediaJournalistChart.php index c95bbed..e133627 100644 --- a/app/Filament/Widgets/TopMediaJournalistChart.php +++ b/app/Filament/Widgets/TopMediaJournalistChart.php @@ -7,6 +7,8 @@ class TopMediaJournalistChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Top Media Berdasarkan Jumlah Jurnalis'; protected static ?int $sort = 5; @@ -15,7 +17,12 @@ class TopMediaJournalistChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = PartnerMedia::query() + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->withCount('journalists') ->orderByDesc('journalists_count') ->limit(10) diff --git a/app/Filament/Widgets/VerificationPerformanceChart.php b/app/Filament/Widgets/VerificationPerformanceChart.php index a5fd920..4485f99 100644 --- a/app/Filament/Widgets/VerificationPerformanceChart.php +++ b/app/Filament/Widgets/VerificationPerformanceChart.php @@ -10,6 +10,8 @@ class VerificationPerformanceChart extends ChartWidget { + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + protected ?string $heading = 'Performa Verifikasi (Approved vs Rejected)'; protected static ?int $sort = 14; @@ -18,6 +20,9 @@ class VerificationPerformanceChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = VerificationRequest::query() ->select( DB::raw('COUNT(*) as count'), @@ -25,7 +30,9 @@ protected function getData(): array DB::raw("DATE_FORMAT(created_at, '%Y-%m') as month") ) ->whereIn('status', [VerificationStatus::APPROVED, VerificationStatus::REJECTED]) - ->where('created_at', '>=', now()->subMonths(6)) + ->when($startDate, fn ($q) => $q->whereDate('created_at', '>=', $startDate)) + ->when(! $startDate, fn ($q) => $q->where('created_at', '>=', now()->subMonths(6))) + ->when($endDate, fn ($q) => $q->whereDate('created_at', '<=', $endDate)) ->groupBy('month', 'status') ->orderBy('month') ->get(); diff --git a/app/Filament/Widgets/VisitorTrafficChart.php b/app/Filament/Widgets/VisitorTrafficChart.php index 4aa4e9c..823645c 100644 --- a/app/Filament/Widgets/VisitorTrafficChart.php +++ b/app/Filament/Widgets/VisitorTrafficChart.php @@ -9,7 +9,9 @@ class VisitorTrafficChart extends ChartWidget { - protected ?string $heading = 'Traffic Pengunjung (30 Hari Terakhir)'; + use \Filament\Widgets\Concerns\InteractsWithPageFilters; + + protected ?string $heading = 'Traffic Pengunjung'; protected static ?int $sort = 12; @@ -17,12 +19,17 @@ class VisitorTrafficChart extends ChartWidget protected function getData(): array { + $startDate = $this->filters['startDate'] ?? null; + $endDate = $this->filters['endDate'] ?? null; + $data = Visitor::query() ->select( DB::raw('SUM(counter) as total'), DB::raw("DATE_FORMAT(date, '%Y-%m-%d') as day") ) - ->where('date', '>=', now()->subDays(30)) + ->when($startDate, fn ($q) => $q->whereDate('date', '>=', $startDate)) + ->when(! $startDate, fn ($q) => $q->where('date', '>=', now()->subDays(30))) + ->when($endDate, fn ($q) => $q->whereDate('date', '<=', $endDate)) ->groupBy('day') ->orderBy('day') ->get();