feat: Implement global date range filtering across all dashboard widgets and add corresponding UI controls to the dashboard page.

This commit is contained in:
Yoga Pangestu 2026-01-29 10:52:20 +07:00
parent 8f1cb16dc9
commit a62ea370f4
16 changed files with 165 additions and 19 deletions

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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')

View File

@ -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;

View File

@ -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();

View File

@ -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' => [

View File

@ -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();

View File

@ -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')

View File

@ -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();

View File

@ -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();

View File

@ -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'),

View File

@ -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();

View File

@ -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();

View File

@ -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)

View File

@ -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();

View File

@ -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();