resolveFilters(); $totals = $this->totalsByEventType($filters); $success = $this->sumTotals($totals, self::SUCCESS_EVENTS); $failures = $this->sumTotals($totals, self::FAILURE_EVENTS); $rateLimited = $this->sumTotals($totals, self::RATE_LIMIT_EVENTS); $uploads = $this->sumTotals($totals, self::UPLOAD_EVENTS); $ratio = $success > 0 ? round(($failures / $success) * 100, 1) : null; $ratioLabel = $ratio !== null ? "{$ratio}%" : __('admin.join_token_analytics.stats.no_data'); return [ Stat::make(__('admin.join_token_analytics.stats.success'), number_format($success)) ->color('success'), Stat::make(__('admin.join_token_analytics.stats.failures'), number_format($failures)) ->color($failures > 0 ? 'danger' : 'success'), Stat::make(__('admin.join_token_analytics.stats.rate_limited'), number_format($rateLimited)) ->color($rateLimited > 0 ? 'warning' : 'success'), Stat::make(__('admin.join_token_analytics.stats.uploads'), number_format($uploads)) ->color('primary'), Stat::make(__('admin.join_token_analytics.stats.failure_ratio'), $ratioLabel) ->color($ratio !== null && $ratio >= 50 ? 'danger' : 'warning'), ]; } private function totalsByEventType(array $filters): array { return $this->baseQuery($filters) ->selectRaw('event_type, COUNT(*) as total') ->groupBy('event_type') ->get() ->mapWithKeys(fn (EventJoinTokenEvent $event) => [$event->event_type => (int) $event->total]) ->all(); } private function sumTotals(array $totals, array $types): int { $sum = 0; foreach ($types as $type) { $sum += (int) ($totals[$type] ?? 0); } return $sum; } private function baseQuery(array $filters): Builder { $query = EventJoinTokenEvent::query() ->whereBetween('occurred_at', [$filters['start'], $filters['end']]); if ($filters['event_id']) { $query->where('event_id', $filters['event_id']); } return $query; } private function resolveFilters(): array { $eventId = $this->pageFilters['event_id'] ?? null; $eventId = is_numeric($eventId) ? (int) $eventId : null; $range = is_string($this->pageFilters['range'] ?? null) ? $this->pageFilters['range'] : '24h'; [$start, $end] = $this->resolveWindow($range, $eventId); return [ 'event_id' => $eventId, 'range' => $range, 'start' => $start, 'end' => $end, ]; } private function resolveWindow(string $range, ?int $eventId): array { $now = now(); $start = match ($range) { '2h' => $now->copy()->subHours(2), '6h' => $now->copy()->subHours(6), '12h' => $now->copy()->subHours(12), '7d' => $now->copy()->subDays(7), default => $now->copy()->subHours(24), }; $end = $now; if ($range === 'event_day' && $eventId) { $eventDate = Event::query()->whereKey($eventId)->value('date'); if ($eventDate) { $eventDay = Carbon::parse($eventDate); $start = $eventDay->copy()->startOfDay(); $end = $eventDay->copy()->endOfDay(); } } return [$start, $end]; } }