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:
@@ -6,10 +6,12 @@ use App\Enums\CateringStatus;
|
||||
use App\Enums\EventStatus;
|
||||
use App\Enums\EventType;
|
||||
use App\Enums\ParticipantStatus;
|
||||
use App\Enums\PlayerPosition;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventCatering;
|
||||
use App\Models\EventPlayerStat;
|
||||
use App\Models\EventTimekeeper;
|
||||
use App\Models\File;
|
||||
use App\Models\FileCategory;
|
||||
@@ -120,13 +122,16 @@ class EventController extends Controller
|
||||
$participantRelations = $event->type === EventType::Meeting
|
||||
? ['participants.user']
|
||||
: ['participants.player'];
|
||||
$event->load(array_merge($participantRelations, ['caterings', 'timekeepers', 'files.category']));
|
||||
$event->load(array_merge($participantRelations, ['caterings', 'timekeepers', 'files.category', 'playerStats']));
|
||||
$assignedCatering = $event->caterings->where('status', CateringStatus::Yes)->pluck('user_id')->toArray();
|
||||
$assignedTimekeeper = $event->timekeepers->where('status', CateringStatus::Yes)->pluck('user_id')->toArray();
|
||||
$knownLocations = Location::orderBy('name')->get();
|
||||
$fileCategories = FileCategory::active()->ordered()->with(['files' => fn ($q) => $q->latest()])->get();
|
||||
|
||||
return view('admin.events.edit', compact('event', 'teams', 'types', 'statuses', 'teamParents', 'assignedCatering', 'assignedTimekeeper', 'eventDefaults', 'knownLocations', 'fileCategories'));
|
||||
// Spielerstatistik-Daten für Spieltypen
|
||||
$playerStatsMap = $event->playerStats->keyBy('player_id');
|
||||
|
||||
return view('admin.events.edit', compact('event', 'teams', 'types', 'statuses', 'teamParents', 'assignedCatering', 'assignedTimekeeper', 'eventDefaults', 'knownLocations', 'fileCategories', 'playerStatsMap'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Event $event): RedirectResponse
|
||||
@@ -210,6 +215,58 @@ class EventController extends Controller
|
||||
->with('success', __('admin.event_restored'));
|
||||
}
|
||||
|
||||
public function updateStats(Request $request, Event $event): RedirectResponse
|
||||
{
|
||||
if (! $event->type->isGameType()) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'stats' => ['required', 'array'],
|
||||
'stats.*.is_goalkeeper' => ['nullable'],
|
||||
'stats.*.position' => ['nullable', 'string', \Illuminate\Validation\Rule::in(PlayerPosition::values())],
|
||||
'stats.*.goalkeeper_saves' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||
'stats.*.goalkeeper_shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||
'stats.*.goals' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||
'stats.*.shots' => ['nullable', 'integer', 'min:0', 'max:999'],
|
||||
'stats.*.note' => ['nullable', 'string', 'max:500'],
|
||||
]);
|
||||
|
||||
$stats = $request->input('stats', []);
|
||||
|
||||
foreach ($stats as $playerId => $data) {
|
||||
$position = ! empty($data['position']) ? $data['position'] : null;
|
||||
$isGk = $position === 'torwart' || ! empty($data['is_goalkeeper']);
|
||||
$goals = isset($data['goals']) && $data['goals'] !== '' ? (int) $data['goals'] : null;
|
||||
$shots = isset($data['shots']) && $data['shots'] !== '' ? (int) $data['shots'] : 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;
|
||||
$note = ! empty($data['note']) ? trim($data['note']) : null;
|
||||
|
||||
// Leere Einträge löschen
|
||||
if (! $isGk && $goals === null && $shots === null && $note === null && $position === null) {
|
||||
EventPlayerStat::where('event_id', $event->id)->where('player_id', $playerId)->delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
EventPlayerStat::updateOrCreate(
|
||||
['event_id' => $event->id, 'player_id' => (int) $playerId],
|
||||
[
|
||||
'is_goalkeeper' => $isGk,
|
||||
'position' => $position,
|
||||
'goalkeeper_saves' => $gkSaves,
|
||||
'goalkeeper_shots' => $gkShots,
|
||||
'goals' => $goals,
|
||||
'shots' => $shots,
|
||||
'note' => $note,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return redirect()->route('admin.events.edit', $event)
|
||||
->with('success', __('events.stats_saved'));
|
||||
}
|
||||
|
||||
private function validateEvent(Request $request): array
|
||||
{
|
||||
$request->validate([
|
||||
|
||||
Reference in New Issue
Block a user