user())) { abort(403); } $request->validate([ 'team_id' => ['nullable', 'integer', 'exists:teams,id'], 'from' => ['nullable', 'date'], 'to' => ['nullable', 'date'], ]); $query = Event::with(['team']) ->withCount([ 'participants as players_yes_count' => fn ($q) => $q->where('status', 'yes'), 'caterings as caterings_yes_count' => fn ($q) => $q->where('status', 'yes'), 'timekeepers as timekeepers_yes_count' => fn ($q) => $q->where('status', 'yes'), ]) ->whereIn('type', [EventType::HomeGame, EventType::AwayGame]) ->where('status', EventStatus::Published); if ($request->filled('team_id')) { $query->where('team_id', $request->team_id); } if ($request->filled('from')) { $query->where('start_at', '>=', $request->from); } if ($request->filled('to')) { $query->where('start_at', '<=', $request->to . ' 23:59:59'); } $games = $query->orderByDesc('start_at')->get(); // Statistiken berechnen $gamesWithScore = $games->filter(fn ($g) => $g->score_home !== null && $g->score_away !== null); $wins = 0; $losses = 0; $draws = 0; foreach ($gamesWithScore as $game) { if ($game->type === EventType::HomeGame) { if ($game->score_home > $game->score_away) { $wins++; } elseif ($game->score_home < $game->score_away) { $losses++; } else { $draws++; } } else { if ($game->score_away > $game->score_home) { $wins++; } elseif ($game->score_away < $game->score_home) { $losses++; } else { $draws++; } } } $totalWithScore = $gamesWithScore->count(); $winRate = $totalWithScore > 0 ? round(($wins / $totalWithScore) * 100) : 0; // Chart-Daten $chartWinLoss = [ 'labels' => [__('admin.wins'), __('admin.losses'), __('admin.draws')], 'data' => [$wins, $losses, $draws], 'colors' => ['#22c55e', '#ef4444', '#9ca3af'], ]; // Spieler-Teilnahme pro Spiel (nur die letzten 15 Spiele) $recentGames = $games->take(15)->reverse()->values(); $chartPlayerParticipation = [ 'labels' => $recentGames->map(fn ($g) => $g->start_at->format('d.m.'))->toArray(), 'data' => $recentGames->map(fn ($g) => $g->players_yes_count)->toArray(), ]; // Eltern-Engagement (Catering + Zeitnehmer) $chartParentInvolvement = [ 'labels' => $recentGames->map(fn ($g) => $g->start_at->format('d.m.'))->toArray(), 'catering' => $recentGames->map(fn ($g) => $g->caterings_yes_count)->toArray(), 'timekeepers' => $recentGames->map(fn ($g) => $g->timekeepers_yes_count)->toArray(), ]; $teams = Team::where('is_active', true)->orderBy('name')->get(); // ── Spieler-Rangliste ────────────────────────────────── $gameIds = $games->pluck('id'); $totalGames = $games->count(); $playerRanking = collect(); if ($totalGames > 0) { $playerRanking = EventParticipant::select('player_id', DB::raw('COUNT(*) as total_assigned'), DB::raw('SUM(CASE WHEN status = \'yes\' THEN 1 ELSE 0 END) as games_played')) ->whereIn('event_id', $gameIds) ->whereNotNull('player_id') ->groupBy('player_id') ->get() ->map(function ($row) use ($totalGames) { $player = Player::withTrashed()->find($row->player_id); if (!$player) { return null; } return (object) [ 'player' => $player, 'games_played' => (int) $row->games_played, 'total_assigned' => (int) $row->total_assigned, 'total_games' => $totalGames, 'rate' => $row->total_assigned > 0 ? round(($row->games_played / $row->total_assigned) * 100) : 0, ]; }) ->filter() ->sortByDesc('games_played') ->values(); } // ── Eltern-Engagement-Rangliste ──────────────────────── // Alle publizierten Events (nicht nur Spiele) mit gleichen Team/Datum-Filtern $allEventsQuery = Event::where('status', EventStatus::Published); if ($request->filled('team_id')) { $allEventsQuery->where('team_id', $request->team_id); } if ($request->filled('from')) { $allEventsQuery->where('start_at', '>=', $request->from); } if ($request->filled('to')) { $allEventsQuery->where('start_at', '<=', $request->to . ' 23:59:59'); } $allEventIds = $allEventsQuery->pluck('id'); // Catering-Events (nur Typen die Catering haben) $cateringEventIds = $allEventsQuery->clone() ->whereNotIn('type', [EventType::AwayGame, EventType::Meeting]) ->pluck('id'); // Zeitnehmer-Events (identisch wie Catering) $timekeeperEventIds = $cateringEventIds; $cateringCounts = EventCatering::select('user_id', DB::raw('COUNT(*) as count')) ->whereIn('event_id', $cateringEventIds) ->where('status', CateringStatus::Yes) ->groupBy('user_id') ->pluck('count', 'user_id'); $timekeeperCounts = EventTimekeeper::select('user_id', DB::raw('COUNT(*) as count')) ->whereIn('event_id', $timekeeperEventIds) ->where('status', CateringStatus::Yes) ->groupBy('user_id') ->pluck('count', 'user_id'); $parentUserIds = $cateringCounts->keys()->merge($timekeeperCounts->keys())->unique(); $parentRanking = User::withTrashed() ->whereIn('id', $parentUserIds) ->get() ->map(function ($user) use ($cateringCounts, $timekeeperCounts) { $catering = $cateringCounts->get($user->id, 0); $timekeeper = $timekeeperCounts->get($user->id, 0); return (object) [ 'user' => $user, 'catering_count' => $catering, 'timekeeper_count' => $timekeeper, 'total' => $catering + $timekeeper, ]; }) ->sortByDesc('total') ->values(); $totalCateringEvents = $cateringEventIds->count(); $totalTimekeeperEvents = $timekeeperEventIds->count(); return view('admin.statistics.index', compact( 'games', 'teams', 'wins', 'losses', 'draws', 'winRate', 'totalWithScore', 'chartWinLoss', 'chartPlayerParticipation', 'chartParentInvolvement', 'playerRanking', 'totalGames', 'parentRanking', 'totalCateringEvents', 'totalTimekeeperEvents' )); } }