Erweiterte Spielerstatistiken: 7-Meter, Strafen, Spielzeit
Neue Metriken für Jugendhandball: 7m-Würfe/-Tore, Gelbe Karten, 2-Minuten-Strafen und Spielzeit. Migration, Model, Controller, Views und Übersetzungen (6 Sprachen) vollständig implementiert. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Models\EventTimekeeper;
|
|||||||
use App\Models\File;
|
use App\Models\File;
|
||||||
use App\Models\FileCategory;
|
use App\Models\FileCategory;
|
||||||
use App\Models\Location;
|
use App\Models\Location;
|
||||||
|
use App\Models\Season;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@@ -55,11 +56,20 @@ class EventController extends Controller
|
|||||||
$query->where('status', $request->status);
|
$query->where('status', $request->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saison-Filter
|
||||||
|
if ($request->filled('season_id')) {
|
||||||
|
$season = Season::find($request->season_id);
|
||||||
|
if ($season) {
|
||||||
|
$query->whereBetween('start_at', [$season->start_date, $season->end_date->endOfDay()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$events = $query->paginate(20)->withQueryString();
|
$events = $query->paginate(20)->withQueryString();
|
||||||
$teams = Team::active()->orderBy('name')->get();
|
$teams = Team::active()->orderBy('name')->get();
|
||||||
|
$seasons = Season::orderByDesc('start_date')->get();
|
||||||
$trashedEvents = Event::onlyTrashed()->with('team')->latest('deleted_at')->get();
|
$trashedEvents = Event::onlyTrashed()->with('team')->latest('deleted_at')->get();
|
||||||
|
|
||||||
return view('admin.events.index', compact('events', 'teams', 'trashedEvents'));
|
return view('admin.events.index', compact('events', 'teams', 'seasons', 'trashedEvents'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(): View
|
public function create(): View
|
||||||
@@ -229,6 +239,11 @@ class EventController extends Controller
|
|||||||
'stats.*.goalkeeper_shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
'stats.*.goalkeeper_shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||||
'stats.*.goals' => ['nullable', 'integer', 'min:0', 'max:999'],
|
'stats.*.goals' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||||
'stats.*.shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
'stats.*.shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||||
|
'stats.*.penalty_goals' => ['nullable', 'integer', 'min:0', 'max:99'],
|
||||||
|
'stats.*.penalty_shots' => ['nullable', 'integer', 'min:0', 'max:99'],
|
||||||
|
'stats.*.yellow_cards' => ['nullable', 'integer', 'min:0', 'max:3'],
|
||||||
|
'stats.*.two_minute_suspensions' => ['nullable', 'integer', 'min:0', 'max:3'],
|
||||||
|
'stats.*.playing_time_minutes' => ['nullable', 'integer', 'min:0', 'max:90'],
|
||||||
'stats.*.note' => ['nullable', 'string', 'max:500'],
|
'stats.*.note' => ['nullable', 'string', 'max:500'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -242,9 +257,17 @@ class EventController extends Controller
|
|||||||
$gkSaves = $isGk && isset($data['goalkeeper_saves']) && $data['goalkeeper_saves'] !== '' ? (int) $data['goalkeeper_saves'] : null;
|
$gkSaves = $isGk && isset($data['goalkeeper_saves']) && $data['goalkeeper_saves'] !== '' ? (int) $data['goalkeeper_saves'] : null;
|
||||||
$gkShots = $isGk && isset($data['goalkeeper_shots']) && $data['goalkeeper_shots'] !== '' ? (int) $data['goalkeeper_shots'] : null;
|
$gkShots = $isGk && isset($data['goalkeeper_shots']) && $data['goalkeeper_shots'] !== '' ? (int) $data['goalkeeper_shots'] : null;
|
||||||
$note = ! empty($data['note']) ? trim($data['note']) : null;
|
$note = ! empty($data['note']) ? trim($data['note']) : null;
|
||||||
|
$penaltyGoals = isset($data['penalty_goals']) && $data['penalty_goals'] !== '' ? (int) $data['penalty_goals'] : null;
|
||||||
|
$penaltyShots = isset($data['penalty_shots']) && $data['penalty_shots'] !== '' ? (int) $data['penalty_shots'] : null;
|
||||||
|
$yellowCards = isset($data['yellow_cards']) && $data['yellow_cards'] !== '' ? (int) $data['yellow_cards'] : null;
|
||||||
|
$twoMinSuspensions = isset($data['two_minute_suspensions']) && $data['two_minute_suspensions'] !== '' ? (int) $data['two_minute_suspensions'] : null;
|
||||||
|
$playingTime = isset($data['playing_time_minutes']) && $data['playing_time_minutes'] !== '' ? (int) $data['playing_time_minutes'] : null;
|
||||||
|
|
||||||
// Leere Einträge löschen
|
// Leere Einträge löschen
|
||||||
if (! $isGk && $goals === null && $shots === null && $note === null && $position === null) {
|
$hasData = $isGk || $goals !== null || $shots !== null || $note !== null || $position !== null
|
||||||
|
|| $penaltyGoals !== null || $penaltyShots !== null || $yellowCards !== null
|
||||||
|
|| $twoMinSuspensions !== null || $playingTime !== null;
|
||||||
|
if (! $hasData) {
|
||||||
EventPlayerStat::where('event_id', $event->id)->where('player_id', $playerId)->delete();
|
EventPlayerStat::where('event_id', $event->id)->where('player_id', $playerId)->delete();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -258,6 +281,11 @@ class EventController extends Controller
|
|||||||
'goalkeeper_shots' => $gkShots,
|
'goalkeeper_shots' => $gkShots,
|
||||||
'goals' => $goals,
|
'goals' => $goals,
|
||||||
'shots' => $shots,
|
'shots' => $shots,
|
||||||
|
'penalty_goals' => $penaltyGoals,
|
||||||
|
'penalty_shots' => $penaltyShots,
|
||||||
|
'yellow_cards' => $yellowCards,
|
||||||
|
'two_minute_suspensions' => $twoMinSuspensions,
|
||||||
|
'playing_time_minutes' => $playingTime,
|
||||||
'note' => $note,
|
'note' => $note,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use App\Models\EventParticipant;
|
|||||||
use App\Models\EventPlayerStat;
|
use App\Models\EventPlayerStat;
|
||||||
use App\Models\EventTimekeeper;
|
use App\Models\EventTimekeeper;
|
||||||
use App\Models\Player;
|
use App\Models\Player;
|
||||||
|
use App\Models\Season;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@@ -32,6 +33,7 @@ class StatisticsController extends Controller
|
|||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'team_id' => ['nullable', 'integer', 'exists:teams,id'],
|
'team_id' => ['nullable', 'integer', 'exists:teams,id'],
|
||||||
|
'season_id' => ['nullable', 'integer', 'exists:seasons,id'],
|
||||||
'from' => ['nullable', 'date'],
|
'from' => ['nullable', 'date'],
|
||||||
'to' => ['nullable', 'date'],
|
'to' => ['nullable', 'date'],
|
||||||
]);
|
]);
|
||||||
@@ -49,13 +51,21 @@ class StatisticsController extends Controller
|
|||||||
$query->where('team_id', $request->team_id);
|
$query->where('team_id', $request->team_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saison-Filter (hat Vorrang vor from/to)
|
||||||
|
$activeSeason = null;
|
||||||
|
if ($request->filled('season_id')) {
|
||||||
|
$activeSeason = Season::find($request->season_id);
|
||||||
|
if ($activeSeason) {
|
||||||
|
$query->whereBetween('start_at', [$activeSeason->start_date, $activeSeason->end_date->endOfDay()]);
|
||||||
|
}
|
||||||
|
} elseif ($request->filled('from') || $request->filled('to')) {
|
||||||
if ($request->filled('from')) {
|
if ($request->filled('from')) {
|
||||||
$query->where('start_at', '>=', $request->from);
|
$query->where('start_at', '>=', $request->from);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->filled('to')) {
|
if ($request->filled('to')) {
|
||||||
$query->where('start_at', '<=', $request->to . ' 23:59:59');
|
$query->where('start_at', '<=', $request->to . ' 23:59:59');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$games = $query->orderByDesc('start_at')->get();
|
$games = $query->orderByDesc('start_at')->get();
|
||||||
|
|
||||||
@@ -93,7 +103,7 @@ class StatisticsController extends Controller
|
|||||||
$chartWinLoss = [
|
$chartWinLoss = [
|
||||||
'labels' => [__('admin.wins'), __('admin.losses'), __('admin.draws')],
|
'labels' => [__('admin.wins'), __('admin.losses'), __('admin.draws')],
|
||||||
'data' => [$wins, $losses, $draws],
|
'data' => [$wins, $losses, $draws],
|
||||||
'colors' => ['#22c55e', '#ef4444', '#9ca3af'],
|
'colors' => ['#3e7750', '#8f504b', '#8e9db3'],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Spieler-Teilnahme pro Spiel (nur die letzten 15 Spiele)
|
// Spieler-Teilnahme pro Spiel (nur die letzten 15 Spiele)
|
||||||
@@ -138,7 +148,12 @@ class StatisticsController extends Controller
|
|||||||
DB::raw('COALESCE(SUM(goals), 0) as total_goals_agg'),
|
DB::raw('COALESCE(SUM(goals), 0) as total_goals_agg'),
|
||||||
DB::raw('COALESCE(SUM(shots), 0) as total_shots_agg'),
|
DB::raw('COALESCE(SUM(shots), 0) as total_shots_agg'),
|
||||||
DB::raw('COALESCE(SUM(goalkeeper_saves), 0) as total_gk_saves'),
|
DB::raw('COALESCE(SUM(goalkeeper_saves), 0) as total_gk_saves'),
|
||||||
DB::raw('COALESCE(SUM(goalkeeper_shots), 0) as total_gk_shots')
|
DB::raw('COALESCE(SUM(goalkeeper_shots), 0) as total_gk_shots'),
|
||||||
|
DB::raw('COALESCE(SUM(penalty_goals), 0) as total_penalty_goals'),
|
||||||
|
DB::raw('COALESCE(SUM(penalty_shots), 0) as total_penalty_shots'),
|
||||||
|
DB::raw('COALESCE(SUM(yellow_cards), 0) as total_yellow_cards'),
|
||||||
|
DB::raw('COALESCE(SUM(two_minute_suspensions), 0) as total_suspensions'),
|
||||||
|
DB::raw('AVG(playing_time_minutes) as avg_playing_time')
|
||||||
)
|
)
|
||||||
->groupBy('player_id')
|
->groupBy('player_id')
|
||||||
->get()
|
->get()
|
||||||
@@ -195,6 +210,11 @@ class StatisticsController extends Controller
|
|||||||
'is_primary_gk' => $isPrimaryGk,
|
'is_primary_gk' => $isPrimaryGk,
|
||||||
'performance_rate' => $performanceRate,
|
'performance_rate' => $performanceRate,
|
||||||
'performance_color' => $performanceColor,
|
'performance_color' => $performanceColor,
|
||||||
|
'total_penalty_goals' => $agg ? (int) $agg->total_penalty_goals : 0,
|
||||||
|
'total_penalty_shots' => $agg ? (int) $agg->total_penalty_shots : 0,
|
||||||
|
'total_yellow_cards' => $agg ? (int) $agg->total_yellow_cards : 0,
|
||||||
|
'total_suspensions' => $agg ? (int) $agg->total_suspensions : 0,
|
||||||
|
'avg_playing_time' => $agg && $agg->avg_playing_time ? (int) round($agg->avg_playing_time) : null,
|
||||||
];
|
];
|
||||||
})
|
})
|
||||||
->filter()
|
->filter()
|
||||||
@@ -270,11 +290,14 @@ class StatisticsController extends Controller
|
|||||||
$totalCateringEvents = $cateringEventIds->count();
|
$totalCateringEvents = $cateringEventIds->count();
|
||||||
$totalTimekeeperEvents = $timekeeperEventIds->count();
|
$totalTimekeeperEvents = $timekeeperEventIds->count();
|
||||||
|
|
||||||
|
$seasons = Season::orderByDesc('start_date')->get();
|
||||||
|
|
||||||
return view('admin.statistics.index', compact(
|
return view('admin.statistics.index', compact(
|
||||||
'games', 'teams', 'wins', 'losses', 'draws', 'winRate', 'totalWithScore',
|
'games', 'teams', 'wins', 'losses', 'draws', 'winRate', 'totalWithScore',
|
||||||
'chartWinLoss', 'chartPlayerParticipation', 'chartParentInvolvement',
|
'chartWinLoss', 'chartPlayerParticipation', 'chartParentInvolvement',
|
||||||
'playerRanking', 'totalGames', 'courtPlayers',
|
'playerRanking', 'totalGames', 'courtPlayers',
|
||||||
'parentRanking', 'totalCateringEvents', 'totalTimekeeperEvents'
|
'parentRanking', 'totalCateringEvents', 'totalTimekeeperEvents',
|
||||||
|
'seasons', 'activeSeason'
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +319,12 @@ class StatisticsController extends Controller
|
|||||||
$gkGames = $stats->where('is_goalkeeper', true);
|
$gkGames = $stats->where('is_goalkeeper', true);
|
||||||
$totalGkSaves = $gkGames->sum('goalkeeper_saves');
|
$totalGkSaves = $gkGames->sum('goalkeeper_saves');
|
||||||
$totalGkShots = $gkGames->sum('goalkeeper_shots');
|
$totalGkShots = $gkGames->sum('goalkeeper_shots');
|
||||||
|
$totalPenaltyGoals = $stats->sum('penalty_goals');
|
||||||
|
$totalPenaltyShots = $stats->sum('penalty_shots');
|
||||||
|
$totalYellowCards = $stats->sum('yellow_cards');
|
||||||
|
$totalSuspensions = $stats->sum('two_minute_suspensions');
|
||||||
|
$playingTimeStats = $stats->whereNotNull('playing_time_minutes');
|
||||||
|
$avgPlayingTime = $playingTimeStats->count() > 0 ? (int) round($playingTimeStats->avg('playing_time_minutes')) : null;
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'player' => [
|
'player' => [
|
||||||
@@ -312,6 +341,12 @@ class StatisticsController extends Controller
|
|||||||
'total_saves' => $totalGkSaves,
|
'total_saves' => $totalGkSaves,
|
||||||
'total_gk_shots' => $totalGkShots,
|
'total_gk_shots' => $totalGkShots,
|
||||||
'save_rate' => $totalGkShots > 0 ? round(($totalGkSaves / $totalGkShots) * 100, 1) : null,
|
'save_rate' => $totalGkShots > 0 ? round(($totalGkSaves / $totalGkShots) * 100, 1) : null,
|
||||||
|
'total_penalty_goals' => $totalPenaltyGoals,
|
||||||
|
'total_penalty_shots' => $totalPenaltyShots,
|
||||||
|
'penalty_rate' => $totalPenaltyShots > 0 ? round(($totalPenaltyGoals / $totalPenaltyShots) * 100, 1) : null,
|
||||||
|
'total_yellow_cards' => $totalYellowCards,
|
||||||
|
'total_suspensions' => $totalSuspensions,
|
||||||
|
'avg_playing_time' => $avgPlayingTime,
|
||||||
],
|
],
|
||||||
'games' => $stats->map(fn ($s) => [
|
'games' => $stats->map(fn ($s) => [
|
||||||
'date' => $s->event->start_at->format('d.m.Y'),
|
'date' => $s->event->start_at->format('d.m.Y'),
|
||||||
@@ -320,6 +355,11 @@ class StatisticsController extends Controller
|
|||||||
'position' => $s->position?->shortLabel(),
|
'position' => $s->position?->shortLabel(),
|
||||||
'goals' => $s->goals,
|
'goals' => $s->goals,
|
||||||
'shots' => $s->shots,
|
'shots' => $s->shots,
|
||||||
|
'penalty_goals' => $s->penalty_goals,
|
||||||
|
'penalty_shots' => $s->penalty_shots,
|
||||||
|
'yellow_cards' => $s->yellow_cards,
|
||||||
|
'two_minute_suspensions' => $s->two_minute_suspensions,
|
||||||
|
'playing_time_minutes' => $s->playing_time_minutes,
|
||||||
'is_goalkeeper' => $s->is_goalkeeper,
|
'is_goalkeeper' => $s->is_goalkeeper,
|
||||||
'goalkeeper_saves' => $s->goalkeeper_saves,
|
'goalkeeper_saves' => $s->goalkeeper_saves,
|
||||||
'goalkeeper_shots' => $s->goalkeeper_shots,
|
'goalkeeper_shots' => $s->goalkeeper_shots,
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ class EventPlayerStat extends Model
|
|||||||
'goalkeeper_shots',
|
'goalkeeper_shots',
|
||||||
'goals',
|
'goals',
|
||||||
'shots',
|
'shots',
|
||||||
|
'penalty_goals',
|
||||||
|
'penalty_shots',
|
||||||
|
'yellow_cards',
|
||||||
|
'two_minute_suspensions',
|
||||||
|
'playing_time_minutes',
|
||||||
'note',
|
'note',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -27,6 +32,11 @@ class EventPlayerStat extends Model
|
|||||||
'goalkeeper_shots' => 'integer',
|
'goalkeeper_shots' => 'integer',
|
||||||
'goals' => 'integer',
|
'goals' => 'integer',
|
||||||
'shots' => 'integer',
|
'shots' => 'integer',
|
||||||
|
'penalty_goals' => 'integer',
|
||||||
|
'penalty_shots' => 'integer',
|
||||||
|
'yellow_cards' => 'integer',
|
||||||
|
'two_minute_suspensions' => 'integer',
|
||||||
|
'playing_time_minutes' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function event(): BelongsTo
|
public function event(): BelongsTo
|
||||||
@@ -63,6 +73,18 @@ class EventPlayerStat extends Model
|
|||||||
return round(($this->goals / $this->shots) * 100, 1);
|
return round(($this->goals / $this->shots) * 100, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 7-Meter-Quote in Prozent.
|
||||||
|
*/
|
||||||
|
public function penaltyRate(): ?float
|
||||||
|
{
|
||||||
|
if (! $this->penalty_shots || $this->penalty_shots === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return round(($this->penalty_goals / $this->penalty_shots) * 100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft ob der Eintrag leer ist (keine relevanten Daten).
|
* Prüft ob der Eintrag leer ist (keine relevanten Daten).
|
||||||
*/
|
*/
|
||||||
@@ -73,6 +95,11 @@ class EventPlayerStat extends Model
|
|||||||
&& ! $this->goalkeeper_shots
|
&& ! $this->goalkeeper_shots
|
||||||
&& ! $this->goals
|
&& ! $this->goals
|
||||||
&& ! $this->shots
|
&& ! $this->shots
|
||||||
|
&& ! $this->penalty_goals
|
||||||
|
&& ! $this->penalty_shots
|
||||||
|
&& ! $this->yellow_cards
|
||||||
|
&& ! $this->two_minute_suspensions
|
||||||
|
&& ! $this->playing_time_minutes
|
||||||
&& ! $this->note;
|
&& ! $this->note;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('event_player_stats', function (Blueprint $table) {
|
||||||
|
$table->unsignedSmallInteger('penalty_goals')->nullable()->after('shots');
|
||||||
|
$table->unsignedSmallInteger('penalty_shots')->nullable()->after('penalty_goals');
|
||||||
|
$table->unsignedTinyInteger('yellow_cards')->nullable()->after('penalty_shots');
|
||||||
|
$table->unsignedTinyInteger('two_minute_suspensions')->nullable()->after('yellow_cards');
|
||||||
|
$table->unsignedSmallInteger('playing_time_minutes')->nullable()->after('two_minute_suspensions');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('event_player_stats', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['penalty_goals', 'penalty_shots', 'yellow_cards', 'two_minute_suspensions', 'playing_time_minutes']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -89,6 +89,12 @@ return [
|
|||||||
'stats_note' => 'ملاحظة',
|
'stats_note' => 'ملاحظة',
|
||||||
'stats_hit_rate' => 'نسبة الإصابة',
|
'stats_hit_rate' => 'نسبة الإصابة',
|
||||||
'stats_save_rate' => 'نسبة التصدي',
|
'stats_save_rate' => 'نسبة التصدي',
|
||||||
|
'stats_penalty_shots' => 'رميات 7م',
|
||||||
|
'stats_penalty_goals' => 'أهداف 7م',
|
||||||
|
'stats_yellow_cards' => 'بطاقة صفراء',
|
||||||
|
'stats_two_min' => '2 دقيقة',
|
||||||
|
'stats_playing_time_short' => 'دقيقة',
|
||||||
|
'stats_cards' => 'عقوبات',
|
||||||
'stats_no_data' => 'لا توجد بيانات إحصائية.',
|
'stats_no_data' => 'لا توجد بيانات إحصائية.',
|
||||||
'stats_position' => 'المركز',
|
'stats_position' => 'المركز',
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ return [
|
|||||||
'stats_note' => 'Bemerkung',
|
'stats_note' => 'Bemerkung',
|
||||||
'stats_hit_rate' => 'Trefferquote',
|
'stats_hit_rate' => 'Trefferquote',
|
||||||
'stats_save_rate' => 'Fangquote',
|
'stats_save_rate' => 'Fangquote',
|
||||||
|
'stats_penalty_shots' => '7m-Würfe',
|
||||||
|
'stats_penalty_goals' => '7m-Tore',
|
||||||
|
'stats_yellow_cards' => 'Gelbe K.',
|
||||||
|
'stats_two_min' => '2-Min',
|
||||||
|
'stats_playing_time_short' => 'Min.',
|
||||||
|
'stats_cards' => 'Strafen',
|
||||||
'stats_no_data' => 'Keine Statistikdaten.',
|
'stats_no_data' => 'Keine Statistikdaten.',
|
||||||
'stats_position' => 'Position',
|
'stats_position' => 'Position',
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,12 @@ return [
|
|||||||
'stats_note' => 'Note',
|
'stats_note' => 'Note',
|
||||||
'stats_hit_rate' => 'Hit rate',
|
'stats_hit_rate' => 'Hit rate',
|
||||||
'stats_save_rate' => 'Save rate',
|
'stats_save_rate' => 'Save rate',
|
||||||
|
'stats_penalty_shots' => 'Pen. shots',
|
||||||
|
'stats_penalty_goals' => 'Pen. goals',
|
||||||
|
'stats_yellow_cards' => 'Yellow C.',
|
||||||
|
'stats_two_min' => '2-Min',
|
||||||
|
'stats_playing_time_short' => 'Min.',
|
||||||
|
'stats_cards' => 'Cards',
|
||||||
'stats_no_data' => 'No statistics data.',
|
'stats_no_data' => 'No statistics data.',
|
||||||
'stats_position' => 'Position',
|
'stats_position' => 'Position',
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ return [
|
|||||||
'stats_note' => 'Uwaga',
|
'stats_note' => 'Uwaga',
|
||||||
'stats_hit_rate' => 'Skuteczność',
|
'stats_hit_rate' => 'Skuteczność',
|
||||||
'stats_save_rate' => 'Skuteczność obron',
|
'stats_save_rate' => 'Skuteczność obron',
|
||||||
|
'stats_penalty_shots' => 'Rzuty 7m',
|
||||||
|
'stats_penalty_goals' => 'Bramki 7m',
|
||||||
|
'stats_yellow_cards' => 'Żółte k.',
|
||||||
|
'stats_two_min' => '2-Min',
|
||||||
|
'stats_playing_time_short' => 'Min.',
|
||||||
|
'stats_cards' => 'Kary',
|
||||||
'stats_no_data' => 'Brak danych statystycznych.',
|
'stats_no_data' => 'Brak danych statystycznych.',
|
||||||
'stats_position' => 'Pozycja',
|
'stats_position' => 'Pozycja',
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ return [
|
|||||||
'stats_note' => 'Примечание',
|
'stats_note' => 'Примечание',
|
||||||
'stats_hit_rate' => 'Процент попаданий',
|
'stats_hit_rate' => 'Процент попаданий',
|
||||||
'stats_save_rate' => 'Процент отражений',
|
'stats_save_rate' => 'Процент отражений',
|
||||||
|
'stats_penalty_shots' => 'Бр. 7м',
|
||||||
|
'stats_penalty_goals' => 'Голы 7м',
|
||||||
|
'stats_yellow_cards' => 'Жёлт. к.',
|
||||||
|
'stats_two_min' => '2 мин',
|
||||||
|
'stats_playing_time_short' => 'Мин.',
|
||||||
|
'stats_cards' => 'Наказания',
|
||||||
'stats_no_data' => 'Нет данных статистики.',
|
'stats_no_data' => 'Нет данных статистики.',
|
||||||
'stats_position' => 'Позиция',
|
'stats_position' => 'Позиция',
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ return [
|
|||||||
'stats_note' => 'Not',
|
'stats_note' => 'Not',
|
||||||
'stats_hit_rate' => 'İsabet oranı',
|
'stats_hit_rate' => 'İsabet oranı',
|
||||||
'stats_save_rate' => 'Kurtarış oranı',
|
'stats_save_rate' => 'Kurtarış oranı',
|
||||||
|
'stats_penalty_shots' => '7m atış',
|
||||||
|
'stats_penalty_goals' => '7m gol',
|
||||||
|
'stats_yellow_cards' => 'Sarı k.',
|
||||||
|
'stats_two_min' => '2 dk',
|
||||||
|
'stats_playing_time_short' => 'Dk.',
|
||||||
|
'stats_cards' => 'Cezalar',
|
||||||
'stats_no_data' => 'İstatistik verisi yok.',
|
'stats_no_data' => 'İstatistik verisi yok.',
|
||||||
'stats_position' => 'Pozisyon',
|
'stats_position' => 'Pozisyon',
|
||||||
|
|
||||||
|
|||||||
@@ -383,6 +383,11 @@
|
|||||||
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_saves') }}</th>
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_saves') }}</th>
|
||||||
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_shots') }}</th>
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_shots') }}</th>
|
||||||
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_goals') }}</th>
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_goals') }}</th>
|
||||||
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-16" title="{{ __('events.stats_penalty_shots') }}">7m-W</th>
|
||||||
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-16" title="{{ __('events.stats_penalty_goals') }}">7m-T</th>
|
||||||
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-14" title="{{ __('events.stats_yellow_cards') }}">{{ __('events.stats_yellow_cards') }}</th>
|
||||||
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-14" title="{{ __('events.stats_two_min') }}">{{ __('events.stats_two_min') }}</th>
|
||||||
|
<th class="text-center px-2 py-2 font-medium text-gray-600 w-16" title="{{ __('events.stats_playing_time') }}">{{ __('events.stats_playing_time_short') }}</th>
|
||||||
<th class="text-left px-2 py-2 font-medium text-gray-600">{{ __('events.stats_note') }}</th>
|
<th class="text-left px-2 py-2 font-medium text-gray-600">{{ __('events.stats_note') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -436,6 +441,31 @@
|
|||||||
value="{{ $stat?->goals }}"
|
value="{{ $stat?->goals }}"
|
||||||
class="w-16 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
class="w-16 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-2 py-2 text-center">
|
||||||
|
<input type="number" name="stats[{{ $pid }}][penalty_shots]" min="0" max="99"
|
||||||
|
value="{{ $stat?->penalty_shots }}"
|
||||||
|
class="w-14 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2 text-center">
|
||||||
|
<input type="number" name="stats[{{ $pid }}][penalty_goals]" min="0" max="99"
|
||||||
|
value="{{ $stat?->penalty_goals }}"
|
||||||
|
class="w-14 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2 text-center">
|
||||||
|
<input type="number" name="stats[{{ $pid }}][yellow_cards]" min="0" max="3"
|
||||||
|
value="{{ $stat?->yellow_cards }}"
|
||||||
|
class="w-12 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2 text-center">
|
||||||
|
<input type="number" name="stats[{{ $pid }}][two_minute_suspensions]" min="0" max="3"
|
||||||
|
value="{{ $stat?->two_minute_suspensions }}"
|
||||||
|
class="w-12 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2 text-center">
|
||||||
|
<input type="number" name="stats[{{ $pid }}][playing_time_minutes]" min="0" max="90"
|
||||||
|
value="{{ $stat?->playing_time_minutes }}"
|
||||||
|
class="w-14 px-1 py-1 border border-gray-300 rounded text-center text-sm">
|
||||||
|
</td>
|
||||||
<td class="px-2 py-2">
|
<td class="px-2 py-2">
|
||||||
<input type="text" name="stats[{{ $pid }}][note]" maxlength="500"
|
<input type="text" name="stats[{{ $pid }}][note]" maxlength="500"
|
||||||
value="{{ $stat?->note }}"
|
value="{{ $stat?->note }}"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
{{-- Filter --}}
|
{{-- Filter --}}
|
||||||
<div class="bg-white rounded-lg shadow p-4 mb-6">
|
<div class="bg-white rounded-lg shadow p-4 mb-6">
|
||||||
<form method="GET" action="{{ route('admin.statistics.index') }}" class="flex flex-wrap items-end gap-4">
|
<form method="GET" action="{{ route('admin.statistics.index') }}" class="flex flex-wrap items-end gap-4" x-data="{ useSeason: {{ request()->filled('season_id') || (!request()->filled('from') && !request()->filled('to')) ? 'true' : 'false' }} }">
|
||||||
<div>
|
<div>
|
||||||
<label for="team_id" class="block text-xs font-medium text-gray-600 mb-1">{{ __('ui.team') }}</label>
|
<label for="team_id" class="block text-xs font-medium text-gray-600 mb-1">{{ __('ui.team') }}</label>
|
||||||
<select name="team_id" id="team_id" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
<select name="team_id" id="team_id" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
||||||
@@ -13,16 +13,29 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@if ($seasons->isNotEmpty())
|
||||||
<div>
|
<div>
|
||||||
|
<label class="block text-xs font-medium text-gray-600 mb-1">{{ __('admin.season') }}</label>
|
||||||
|
<select name="season_id" class="px-3 py-2 border border-gray-300 rounded-md text-sm" @change="useSeason = $el.value !== ''">
|
||||||
|
<option value="">–</option>
|
||||||
|
@foreach ($seasons as $season)
|
||||||
|
<option value="{{ $season->id }}" {{ request('season_id', $activeSeason?->id) == $season->id ? 'selected' : '' }}>
|
||||||
|
{{ $season->name }}{{ $season->is_current ? ' ●' : '' }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div x-show="!useSeason" x-cloak>
|
||||||
<label for="from" class="block text-xs font-medium text-gray-600 mb-1">{{ __('admin.filter_from') }}</label>
|
<label for="from" class="block text-xs font-medium text-gray-600 mb-1">{{ __('admin.filter_from') }}</label>
|
||||||
<input type="date" name="from" id="from" value="{{ request('from') }}" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
<input type="date" name="from" id="from" value="{{ request('from') }}" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div x-show="!useSeason" x-cloak>
|
||||||
<label for="to" class="block text-xs font-medium text-gray-600 mb-1">{{ __('admin.filter_to') }}</label>
|
<label for="to" class="block text-xs font-medium text-gray-600 mb-1">{{ __('admin.filter_to') }}</label>
|
||||||
<input type="date" name="to" id="to" value="{{ request('to') }}" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
<input type="date" name="to" id="to" value="{{ request('to') }}" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md text-sm hover:bg-blue-700">{{ __('admin.filter_apply') }}</button>
|
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md text-sm hover:bg-blue-700">{{ __('admin.filter_apply') }}</button>
|
||||||
@if (request()->hasAny(['team_id', 'from', 'to']))
|
@if (request()->hasAny(['team_id', 'from', 'to', 'season_id']))
|
||||||
<a href="{{ route('admin.statistics.index') }}" class="text-sm text-gray-500 hover:underline">{{ __('admin.filter_reset') }}</a>
|
<a href="{{ route('admin.statistics.index') }}" class="text-sm text-gray-500 hover:underline">{{ __('admin.filter_reset') }}</a>
|
||||||
@endif
|
@endif
|
||||||
</form>
|
</form>
|
||||||
@@ -157,6 +170,9 @@
|
|||||||
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.position') }}</th>
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.position') }}</th>
|
||||||
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.games_played') }}</th>
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.games_played') }}</th>
|
||||||
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.player_goals') }}</th>
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.player_goals') }}</th>
|
||||||
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600" title="{{ __('admin.stats_penalties') }}">7m</th>
|
||||||
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600" title="{{ __('admin.stats_cards') }}">{{ __('admin.stats_cards') }}</th>
|
||||||
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600" title="{{ __('admin.stats_avg_time') }}">{{ __('admin.stats_avg_time') }}</th>
|
||||||
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.participation_rate') }}</th>
|
<th class="text-center px-4 py-2.5 font-medium text-gray-600">{{ __('admin.participation_rate') }}</th>
|
||||||
<th class="px-4 py-2.5 font-medium text-gray-600 w-32"></th>
|
<th class="px-4 py-2.5 font-medium text-gray-600 w-32"></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -167,7 +183,7 @@
|
|||||||
@if (!$separatorShown && !$entry->is_primary_gk)
|
@if (!$separatorShown && !$entry->is_primary_gk)
|
||||||
@php $separatorShown = true; @endphp
|
@php $separatorShown = true; @endphp
|
||||||
@if ($index > 0)
|
@if ($index > 0)
|
||||||
<tr><td colspan="7" class="px-4 py-1"><hr class="border-gray-300"></td></tr>
|
<tr><td colspan="10" class="px-4 py-1"><hr class="border-gray-300"></td></tr>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<tr class="hover:bg-gray-50 cursor-pointer" @click="openModal({{ $entry->player->id }})">
|
<tr class="hover:bg-gray-50 cursor-pointer" @click="openModal({{ $entry->player->id }})">
|
||||||
@@ -197,6 +213,32 @@
|
|||||||
<span class="text-gray-300">0</span>
|
<span class="text-gray-300">0</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
@if ($entry->total_penalty_shots > 0)
|
||||||
|
<span class="text-xs" title="{{ $entry->total_penalty_goals }}/{{ $entry->total_penalty_shots }} ({{ $entry->total_penalty_shots > 0 ? round(($entry->total_penalty_goals / $entry->total_penalty_shots) * 100) : 0 }}%)">{{ $entry->total_penalty_goals }}/{{ $entry->total_penalty_shots }}</span>
|
||||||
|
@else
|
||||||
|
<span class="text-gray-300">–</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
@if ($entry->total_yellow_cards > 0 || $entry->total_suspensions > 0)
|
||||||
|
@if ($entry->total_yellow_cards > 0)
|
||||||
|
<span class="inline-block w-3.5 h-4.5 bg-yellow-400 rounded-sm text-[9px] font-bold text-yellow-900 leading-[18px] text-center" title="{{ $entry->total_yellow_cards }}× Gelbe Karte">{{ $entry->total_yellow_cards }}</span>
|
||||||
|
@endif
|
||||||
|
@if ($entry->total_suspensions > 0)
|
||||||
|
<span class="inline-block px-1 py-0.5 rounded text-[10px] font-medium bg-red-100 text-red-700" title="{{ $entry->total_suspensions }}× 2-Min">{{ $entry->total_suspensions }}×2'</span>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<span class="text-gray-300">–</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
@if ($entry->avg_playing_time)
|
||||||
|
<span class="text-xs text-gray-600">{{ $entry->avg_playing_time }}'</span>
|
||||||
|
@else
|
||||||
|
<span class="text-gray-300">–</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
<td class="px-4 py-2 text-center">
|
<td class="px-4 py-2 text-center">
|
||||||
<span class="inline-block px-2 py-0.5 rounded-full text-xs font-medium {{ $entry->rate >= 75 ? 'bg-green-100 text-green-800' : ($entry->rate >= 50 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800') }}">
|
<span class="inline-block px-2 py-0.5 rounded-full text-xs font-medium {{ $entry->rate >= 75 ? 'bg-green-100 text-green-800' : ($entry->rate >= 50 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800') }}">
|
||||||
{{ $entry->rate }}%
|
{{ $entry->rate }}%
|
||||||
@@ -234,31 +276,31 @@
|
|||||||
'kreislaeufer' => ['x' => 200, 'y' => 180],
|
'kreislaeufer' => ['x' => 200, 'y' => 180],
|
||||||
];
|
];
|
||||||
$colorMap = [
|
$colorMap = [
|
||||||
'green' => ['fill' => '#22c55e', 'text' => '#fff'],
|
'green' => ['fill' => '#305f3f', 'text' => '#fff'],
|
||||||
'yellow' => ['fill' => '#eab308', 'text' => '#fff'],
|
'yellow' => ['fill' => '#806130', 'text' => '#fff'],
|
||||||
'red' => ['fill' => '#ef4444', 'text' => '#fff'],
|
'red' => ['fill' => '#76403b', 'text' => '#fff'],
|
||||||
'gray' => ['fill' => '#9ca3af', 'text' => '#fff'],
|
'gray' => ['fill' => '#667788', 'text' => '#fff'],
|
||||||
];
|
];
|
||||||
@endphp
|
@endphp
|
||||||
<svg viewBox="0 0 400 320" class="w-full max-w-lg" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 400 320" class="w-full max-w-lg" xmlns="http://www.w3.org/2000/svg">
|
||||||
{{-- Spielfeld-Hintergrund --}}
|
{{-- Spielfeld-Hintergrund --}}
|
||||||
<rect x="0" y="0" width="400" height="320" rx="8" fill="#16a34a" />
|
<rect x="0" y="0" width="400" height="320" rx="8" fill="#BBFEC3" />
|
||||||
<rect x="10" y="10" width="380" height="300" rx="4" fill="none" stroke="#fff" stroke-width="1.5" opacity="0.5" />
|
<rect x="10" y="10" width="380" height="300" rx="4" fill="none" stroke="#000" stroke-width="1.5" opacity="0.4" />
|
||||||
|
|
||||||
{{-- Mittellinie --}}
|
{{-- Mittellinie --}}
|
||||||
<line x1="10" y1="160" x2="390" y2="160" stroke="#fff" stroke-width="1" opacity="0.3" />
|
<line x1="10" y1="160" x2="390" y2="160" stroke="#000" stroke-width="1" opacity="0.25" />
|
||||||
|
|
||||||
{{-- Tor (unten) --}}
|
{{-- Tor (unten) --}}
|
||||||
<rect x="155" y="298" width="90" height="12" rx="2" fill="none" stroke="#fff" stroke-width="2" opacity="0.7" />
|
<rect x="155" y="298" width="90" height="12" rx="2" fill="none" stroke="#000" stroke-width="2" opacity="0.5" />
|
||||||
|
|
||||||
{{-- 6m-Torraum (Halbkreis) --}}
|
{{-- 6m-Torraum (Halbkreis) --}}
|
||||||
<path d="M 120 310 Q 120 230 200 220 Q 280 230 280 310" fill="none" stroke="#fff" stroke-width="1.5" opacity="0.5" />
|
<path d="M 120 310 Q 120 230 200 220 Q 280 230 280 310" fill="none" stroke="#000" stroke-width="1.5" opacity="0.4" />
|
||||||
|
|
||||||
{{-- 9m-Freiwurflinie (gestrichelt) --}}
|
{{-- 9m-Freiwurflinie (gestrichelt) --}}
|
||||||
<path d="M 80 310 Q 80 200 200 185 Q 320 200 320 310" fill="none" stroke="#fff" stroke-width="1" stroke-dasharray="6,4" opacity="0.35" />
|
<path d="M 80 310 Q 80 200 200 185 Q 320 200 320 310" fill="none" stroke="#000" stroke-width="1" stroke-dasharray="6,4" opacity="0.3" />
|
||||||
|
|
||||||
{{-- 7m-Markierung --}}
|
{{-- 7m-Markierung --}}
|
||||||
<line x1="193" y1="248" x2="207" y2="248" stroke="#fff" stroke-width="2" opacity="0.5" />
|
<line x1="193" y1="248" x2="207" y2="248" stroke="#000" stroke-width="2" opacity="0.4" />
|
||||||
|
|
||||||
{{-- Spieler-Positionen --}}
|
{{-- Spieler-Positionen --}}
|
||||||
@foreach ($courtPositions as $posValue => $coords)
|
@foreach ($courtPositions as $posValue => $coords)
|
||||||
@@ -268,7 +310,7 @@
|
|||||||
$posEnum = \App\Enums\PlayerPosition::tryFrom($posValue);
|
$posEnum = \App\Enums\PlayerPosition::tryFrom($posValue);
|
||||||
@endphp
|
@endphp
|
||||||
<g @if ($entry) @click="openModal({{ $entry->player->id }})" style="cursor: pointer;" @endif>
|
<g @if ($entry) @click="openModal({{ $entry->player->id }})" style="cursor: pointer;" @endif>
|
||||||
<circle cx="{{ $coords['x'] }}" cy="{{ $coords['y'] }}" r="22" fill="{{ $color['fill'] }}" opacity="0.9" stroke="#fff" stroke-width="1.5" />
|
<circle cx="{{ $coords['x'] }}" cy="{{ $coords['y'] }}" r="22" fill="{{ $color['fill'] }}" opacity="0.9" stroke="#1a3a1a" stroke-width="1.5" />
|
||||||
@if ($entry)
|
@if ($entry)
|
||||||
<text x="{{ $coords['x'] }}" y="{{ $coords['y'] - 4 }}" text-anchor="middle" fill="{{ $color['text'] }}" font-size="11" font-weight="bold" style="pointer-events: none;">
|
<text x="{{ $coords['x'] }}" y="{{ $coords['y'] - 4 }}" text-anchor="middle" fill="{{ $color['text'] }}" font-size="11" font-weight="bold" style="pointer-events: none;">
|
||||||
{{ $entry->player->jersey_number ?? $entry->player->getInitials() }}
|
{{ $entry->player->jersey_number ?? $entry->player->getInitials() }}
|
||||||
@@ -282,7 +324,7 @@
|
|||||||
</text>
|
</text>
|
||||||
@endif
|
@endif
|
||||||
{{-- Positions-Kürzel unter dem Kreis --}}
|
{{-- Positions-Kürzel unter dem Kreis --}}
|
||||||
<text x="{{ $coords['x'] }}" y="{{ $coords['y'] + 35 }}" text-anchor="middle" fill="#fff" font-size="8" opacity="0.7" style="pointer-events: none;">
|
<text x="{{ $coords['x'] }}" y="{{ $coords['y'] + 35 }}" text-anchor="middle" fill="#1a3a1a" font-size="8" opacity="0.6" style="pointer-events: none;">
|
||||||
{{ $posEnum?->shortLabel() }}
|
{{ $posEnum?->shortLabel() }}
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
@@ -372,6 +414,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
{{-- Erweiterte Statistiken --}}
|
||||||
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3 mt-3">
|
||||||
|
<template x-if="data.summary.total_penalty_shots > 0">
|
||||||
|
<div class="text-center p-2 bg-orange-50 rounded-lg">
|
||||||
|
<div class="text-lg font-bold text-orange-700" x-text="data.summary.total_penalty_goals + '/' + data.summary.total_penalty_shots"></div>
|
||||||
|
<div class="text-xs text-orange-600">7-Meter <span x-show="data.summary.penalty_rate !== null" x-text="'(' + data.summary.penalty_rate + '%)'"></span></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template x-if="data.summary.total_yellow_cards > 0 || data.summary.total_suspensions > 0">
|
||||||
|
<div class="text-center p-2 bg-red-50 rounded-lg">
|
||||||
|
<div class="text-lg font-bold text-red-700">
|
||||||
|
<span x-show="data.summary.total_yellow_cards > 0" x-text="data.summary.total_yellow_cards + '× Gelb'"></span>
|
||||||
|
<span x-show="data.summary.total_yellow_cards > 0 && data.summary.total_suspensions > 0">, </span>
|
||||||
|
<span x-show="data.summary.total_suspensions > 0" x-text="data.summary.total_suspensions + '× 2-Min'"></span>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-red-600">{{ __('events.stats_cards') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template x-if="data.summary.avg_playing_time !== null">
|
||||||
|
<div class="text-center p-2 bg-cyan-50 rounded-lg">
|
||||||
|
<div class="text-lg font-bold text-cyan-700" x-text="Math.round(data.summary.avg_playing_time) + ' Min.'"></div>
|
||||||
|
<div class="text-xs text-cyan-600">⌀ {{ __('events.stats_playing_time') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -393,6 +461,9 @@
|
|||||||
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_goals') }}</th>
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_goals') }}</th>
|
||||||
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_shots') }}</th>
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_shots') }}</th>
|
||||||
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_goalkeeper') }}</th>
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_goalkeeper') }}</th>
|
||||||
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">7m</th>
|
||||||
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_cards') }}</th>
|
||||||
|
<th class="text-center px-4 py-2 text-xs font-medium text-gray-500">Min.</th>
|
||||||
<th class="text-left px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_note') }}</th>
|
<th class="text-left px-4 py-2 text-xs font-medium text-gray-500">{{ __('events.stats_note') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -417,6 +488,20 @@
|
|||||||
<span class="text-gray-300">–</span>
|
<span class="text-gray-300">–</span>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
<template x-if="game.penalty_shots > 0">
|
||||||
|
<span class="text-xs text-orange-700" x-text="game.penalty_goals + '/' + game.penalty_shots"></span>
|
||||||
|
</template>
|
||||||
|
<template x-if="!game.penalty_shots || game.penalty_shots === 0">
|
||||||
|
<span class="text-gray-300">–</span>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
<span x-show="game.yellow_cards > 0" class="text-xs bg-yellow-100 text-yellow-800 px-1 py-0.5 rounded mr-0.5" x-text="game.yellow_cards + '×🟡'"></span>
|
||||||
|
<span x-show="game.two_minute_suspensions > 0" class="text-xs bg-red-100 text-red-700 px-1 py-0.5 rounded" x-text="game.two_minute_suspensions + '×2m'"></span>
|
||||||
|
<span x-show="!game.yellow_cards && !game.two_minute_suspensions" class="text-gray-300">–</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-2 text-center text-gray-500" x-text="game.playing_time_minutes ? game.playing_time_minutes + '\'' : '–'"></td>
|
||||||
<td class="px-4 py-2 text-gray-500 text-xs" x-text="game.note ?? ''"></td>
|
<td class="px-4 py-2 text-gray-500 text-xs" x-text="game.note ?? ''"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
@@ -564,7 +649,7 @@
|
|||||||
datasets: [{
|
datasets: [{
|
||||||
label: @js(__('admin.nav_players')),
|
label: @js(__('admin.nav_players')),
|
||||||
data: playerData.data,
|
data: playerData.data,
|
||||||
backgroundColor: '#3b82f6',
|
backgroundColor: '#4a5e7a',
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
@@ -586,13 +671,13 @@
|
|||||||
{
|
{
|
||||||
label: @js(__('events.catering_short')),
|
label: @js(__('events.catering_short')),
|
||||||
data: parentData.catering,
|
data: parentData.catering,
|
||||||
backgroundColor: '#f59e0b',
|
backgroundColor: '#99783a',
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: @js(__('events.timekeeper_short')),
|
label: @js(__('events.timekeeper_short')),
|
||||||
data: parentData.timekeepers,
|
data: parentData.timekeepers,
|
||||||
backgroundColor: '#8b5cf6',
|
backgroundColor: '#6b5a84',
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user