Spielerpositionen, Statistiken, Fahrgemeinschaften, Spielfeld-Visualisierung

- PlayerPosition Enum (7 Handball-Positionen) mit Label/ShortLabel
- Spielerstatistik pro Spiel (Tore, Würfe, TW-Paraden, Bemerkung)
- Position-Dropdown in Spieler-Editor und Event-Stats-Formular
- Statistik-Seite: TW zuerst, Trennlinie, Feldspieler, Position-Badges
- Spielfeld-SVG mit Ampel-Performance (grün/gelb/rot)
- Anklickbare Spieler im Spielfeld öffnen Detail-Modal
- Fahrgemeinschaften (Anbieten, Zuordnen, Zurückziehen)
- Übersetzungen in allen 6 Sprachen (de, en, pl, ru, ar, tr)
- .gitignore für Laravel hinzugefügt
- Demo-Daten mit Positionen und Statistiken

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Rhino
2026-03-02 11:47:34 +01:00
parent 2e24a40d68
commit ad60e7a9f9
46 changed files with 2041 additions and 86 deletions

View File

@@ -357,6 +357,106 @@
</div>
@endif
{{-- Spielerstatistik (nur Spieltypen mit zugesagten Spielern) --}}
@if ($event->type->isGameType())
@php
$confirmedPlayers = $event->participants
->where('status', \App\Enums\ParticipantStatus::Yes)
->whereNotNull('player_id')
->sortBy(fn($p) => $p->player->last_name ?? '');
@endphp
@if ($confirmedPlayers->isNotEmpty())
<div class="bg-white rounded-lg shadow p-6 max-w-4xl mt-6">
<h2 class="text-lg font-semibold mb-1">{{ __('events.stats') }}</h2>
<p class="text-xs text-gray-500 mb-4">{{ __('events.stats_confirmed_only') }}</p>
<form method="POST" action="{{ route('admin.events.update-stats', $event) }}">
@csrf
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50">
<tr>
<th class="text-left px-2 py-2 font-medium text-gray-600">{{ __('admin.nav_players') }}</th>
<th class="text-center px-2 py-2 font-medium text-gray-600 w-28">{{ __('events.stats_position') }}</th>
<th class="text-center px-2 py-2 font-medium text-gray-600 w-10" title="{{ __('events.stats_goalkeeper_long') }}">{{ __('events.stats_goalkeeper') }}</th>
<th class="text-center px-2 py-2 font-medium text-gray-600 w-20">{{ __('events.stats_shots_on_goal') }}</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_goals') }}</th>
<th class="text-left px-2 py-2 font-medium text-gray-600">{{ __('events.stats_note') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@foreach ($confirmedPlayers as $participant)
@php
$pid = $participant->player_id;
$stat = $playerStatsMap[$pid] ?? null;
@endphp
<tr x-data="{ isGk: {{ $stat && $stat->is_goalkeeper ? 'true' : 'false' }} }">
<td class="px-2 py-2 font-medium text-gray-900 whitespace-nowrap">
{{ $participant->player->full_name }}
</td>
<td class="px-2 py-2">
<select name="stats[{{ $pid }}][position]"
class="w-full px-1 py-1 border border-gray-300 rounded text-sm"
@change="isGk = ($event.target.value === 'torwart')">
<option value=""></option>
@foreach (\App\Enums\PlayerPosition::cases() as $pos)
<option value="{{ $pos->value }}"
{{ ($stat?->position?->value ?? $participant->player->position?->value) === $pos->value ? 'selected' : '' }}>
{{ $pos->shortLabel() }}
</option>
@endforeach
</select>
</td>
<td class="px-2 py-2 text-center">
<input type="checkbox" name="stats[{{ $pid }}][is_goalkeeper]" value="1"
x-model="isGk"
class="rounded border-gray-300 text-blue-600">
</td>
<td class="px-2 py-2 text-center" x-show="isGk" x-cloak>
<input type="number" name="stats[{{ $pid }}][goalkeeper_shots]" min="0" max="999"
value="{{ $stat?->goalkeeper_shots }}"
class="w-16 px-1 py-1 border border-gray-300 rounded text-center text-sm">
</td>
<td class="px-2 py-2 text-center" x-show="!isGk"><span class="text-gray-300"></span></td>
<td class="px-2 py-2 text-center" x-show="isGk" x-cloak>
<input type="number" name="stats[{{ $pid }}][goalkeeper_saves]" min="0" max="999"
value="{{ $stat?->goalkeeper_saves }}"
class="w-16 px-1 py-1 border border-gray-300 rounded text-center text-sm">
</td>
<td class="px-2 py-2 text-center" x-show="!isGk"><span class="text-gray-300"></span></td>
<td class="px-2 py-2 text-center">
<input type="number" name="stats[{{ $pid }}][shots]" min="0" max="999"
value="{{ $stat?->shots }}"
class="w-16 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 }}][goals]" min="0" max="999"
value="{{ $stat?->goals }}"
class="w-16 px-1 py-1 border border-gray-300 rounded text-center text-sm">
</td>
<td class="px-2 py-2">
<input type="text" name="stats[{{ $pid }}][note]" maxlength="500"
value="{{ $stat?->note }}"
placeholder="{{ __('events.stats_note') }}..."
class="w-full px-2 py-1 border border-gray-300 rounded text-sm">
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-4">
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 text-sm font-medium">
{{ __('events.stats_save') }}
</button>
</div>
</form>
</div>
@endif
@endif
{{-- Quill JS --}}
<script src="https://cdn.jsdelivr.net/npm/quill@1.3.7/dist/quill.min.js" integrity="sha384-QUJ+ckWz1M+a7w0UfG1sEn4pPrbQwSxGm/1TIPyioqXBrwuT9l4f9gdHWLDLbVWI" crossorigin="anonymous"></script>
<script>