Files
fotospiel-app/app/Filament/Clusters/DailyOps/Pages/JoinTokenAnalyticsDashboard.php
Codex Agent 15e19d4e8b
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Add join token analytics dashboard and align Filament views
2026-01-04 18:21:59 +01:00

128 lines
4.1 KiB
PHP

<?php
namespace App\Filament\Clusters\DailyOps\Pages;
use App\Filament\Clusters\DailyOps\DailyOpsCluster;
use App\Filament\Widgets\JoinTokenOverviewWidget;
use App\Filament\Widgets\JoinTokenTopTokensWidget;
use App\Filament\Widgets\JoinTokenTrendWidget;
use App\Models\Event;
use BackedEnum;
use Filament\Forms\Components\Select;
use Filament\Pages\Dashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use UnitEnum;
class JoinTokenAnalyticsDashboard extends Dashboard
{
use HasFiltersForm;
protected static ?string $cluster = DailyOpsCluster::class;
protected static string $routePath = 'join-token-analytics';
protected static null|string|BackedEnum $navigationIcon = 'heroicon-o-chart-bar';
protected static null|string|UnitEnum $navigationGroup = null;
protected static ?int $navigationSort = 12;
public static function getNavigationGroup(): UnitEnum|string|null
{
return __('admin.nav.security');
}
public static function getNavigationLabel(): string
{
return __('admin.join_token_analytics.navigation.label');
}
public function getHeading(): string
{
return __('admin.join_token_analytics.heading');
}
public function getSubheading(): ?string
{
return __('admin.join_token_analytics.subheading');
}
public function getColumns(): int|array
{
return 1;
}
public function getWidgets(): array
{
return [
JoinTokenOverviewWidget::class,
JoinTokenTrendWidget::class,
JoinTokenTopTokensWidget::class,
];
}
public function filtersForm(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
Select::make('range')
->label(__('admin.join_token_analytics.filters.range'))
->options(trans('admin.join_token_analytics.filters.range_options'))
->default('24h')
->native(false),
Select::make('event_id')
->label(__('admin.join_token_analytics.filters.event'))
->placeholder(__('admin.join_token_analytics.filters.event_placeholder'))
->searchable()
->getSearchResultsUsing(fn (string $search): array => $this->searchEvents($search))
->getOptionLabelUsing(fn ($value): ?string => $this->resolveEventLabel($value))
->native(false),
])
->columns(2),
]);
}
private function searchEvents(string $search): array
{
return Event::query()
->with('tenant')
->when($search !== '', function ($query) use ($search) {
$query->where('slug', 'like', "%{$search}%")
->orWhere('name->de', 'like', "%{$search}%")
->orWhere('name->en', 'like', "%{$search}%");
})
->orderByDesc('date')
->limit(25)
->get()
->mapWithKeys(fn (Event $event) => [$event->id => $this->formatEventLabel($event)])
->all();
}
private function resolveEventLabel(mixed $value): ?string
{
if (! is_numeric($value)) {
return null;
}
$event = Event::query()
->with('tenant')
->find((int) $value);
return $event ? $this->formatEventLabel($event) : null;
}
private function formatEventLabel(Event $event): string
{
$locale = app()->getLocale();
$name = $event->name[$locale] ?? $event->name['de'] ?? $event->name['en'] ?? $event->slug ?? __('admin.common.unnamed');
$tenant = $event->tenant?->name ?? __('admin.common.unnamed');
$date = $event->date?->format('Y-m-d');
return $date ? "{$name} ({$tenant}) {$date}" : "{$name} ({$tenant})";
}
}