Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
3.3 KiB
PHP
107 lines
3.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Widgets;
|
|
|
|
use App\Models\Call;
|
|
use App\Models\Member;
|
|
use App\Models\Project;
|
|
use App\Models\RepositoryIssue;
|
|
use Filament\Widgets\StatsOverviewWidget;
|
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
|
|
|
class AdvancedAnalytics extends StatsOverviewWidget
|
|
{
|
|
protected function getStats(): array
|
|
{
|
|
return [
|
|
// Project metrics
|
|
Stat::make(__('panel.widgets.stats.projects'), Project::query()->published()->count())
|
|
->description(__('panel.widgets.stats.projects_description'))
|
|
->icon('heroicon-o-rocket-launch')
|
|
->color('primary')
|
|
->chart($this->projectsChart()),
|
|
|
|
// Active projects (seeking contributors)
|
|
Stat::make(__('panel.widgets.stats.active_projects'), Project::query()->published()->where('status', 'active')->count())
|
|
->description(__('panel.widgets.stats.active_projects_description'))
|
|
->icon('heroicon-o-fire')
|
|
->color('danger'),
|
|
|
|
// Members metrics
|
|
Stat::make(__('panel.widgets.stats.members'), Member::query()->public()->current()->count())
|
|
->description(__('panel.widgets.stats.members_description'))
|
|
->icon('heroicon-o-users')
|
|
->color('info')
|
|
->chart($this->membersChart()),
|
|
|
|
// Open calls
|
|
Stat::make(__('panel.widgets.stats.open_calls'), Call::query()->open()->count())
|
|
->description(__('panel.widgets.stats.open_calls_description'))
|
|
->icon('heroicon-o-megaphone')
|
|
->color('warning'),
|
|
|
|
// Good first issues
|
|
Stat::make(__('panel.widgets.stats.good_first_issues'), RepositoryIssue::query()
|
|
->where('is_good_first_issue', true)
|
|
->available()
|
|
->count())
|
|
->description(__('panel.widgets.stats.good_first_issues_description'))
|
|
->icon('heroicon-o-check-circle')
|
|
->color('success')
|
|
->chart($this->issuesChart()),
|
|
|
|
// Help wanted issues
|
|
Stat::make(__('panel.widgets.stats.help_wanted'), RepositoryIssue::query()
|
|
->where('is_help_wanted', true)
|
|
->available()
|
|
->count())
|
|
->description(__('panel.widgets.stats.help_wanted_description'))
|
|
->icon('heroicon-o-hand-raised')
|
|
->color('gray'),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<int, int>
|
|
*/
|
|
private function projectsChart(): array
|
|
{
|
|
// Last 7 days of project creation
|
|
return array_map(
|
|
fn () => random_int(0, 5),
|
|
range(0, 6)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, int>
|
|
*/
|
|
private function membersChart(): array
|
|
{
|
|
// Last 7 days of member activity
|
|
return array_map(
|
|
fn () => random_int(0, 10),
|
|
range(0, 6)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, int>
|
|
*/
|
|
private function issuesChart(): array
|
|
{
|
|
// Last 7 days of issue activity
|
|
return array_map(
|
|
fn () => random_int(0, 8),
|
|
range(0, 6)
|
|
);
|
|
}
|
|
|
|
public static function canView(): bool
|
|
{
|
|
return auth()->user()?->can('projects.view_any') ?? false;
|
|
}
|
|
}
|