latest()->paginate(20); return view('admin.teams.index', compact('teams')); } public function create(): View { return view('admin.teams.create'); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'year_group' => ['nullable', 'string', 'max:20'], 'is_active' => ['boolean'], ]); $validated['is_active'] = $request->boolean('is_active', true); Team::create($validated); return redirect()->route('admin.teams.index') ->with('success', __('admin.team_created')); } public function edit(Team $team): View { $team->load([ 'coaches', 'players' => fn ($q) => $q->orderBy('last_name'), 'files.category', ]); $allCoaches = User::where('role', UserRole::Coach) ->where('is_active', true) ->orderBy('name') ->get(); $parentReps = $team->parentReps(); $allTeams = Team::active()->orderBy('name')->get(); $fileCategories = FileCategory::active()->ordered() ->with(['files' => fn ($q) => $q->latest()]) ->get(); return view('admin.teams.edit', compact( 'team', 'allCoaches', 'parentReps', 'allTeams', 'fileCategories' )); } public function update(Request $request, Team $team): RedirectResponse { $request->validate([ 'name' => ['required', 'string', 'max:255'], 'year_group' => ['nullable', 'string', 'max:20'], 'is_active' => ['boolean'], 'notes' => ['nullable', 'string', 'max:5000'], 'coach_ids' => ['nullable', 'array'], 'coach_ids.*' => ['integer', 'exists:users,id', function ($attr, $value, $fail) { $user = User::find($value); if (!$user || $user->role !== \App\Enums\UserRole::Coach) { $fail(__('validation.exists', ['attribute' => $attr])); } }], 'existing_files' => ['nullable', 'array'], 'existing_files.*' => ['integer', 'exists:files,id'], 'new_files' => ['nullable', 'array'], 'new_files.*' => ['file', 'max:10240', 'mimes:pdf,docx,xlsx,jpg,jpeg,png,gif,webp'], 'new_file_categories' => ['nullable', 'array'], 'new_file_categories.*' => ['integer', 'exists:file_categories,id'], ]); $oldData = [ 'name' => $team->name, 'year_group' => $team->year_group, 'is_active' => $team->is_active, 'notes' => $team->notes, ]; $team->update([ 'name' => $request->input('name'), 'year_group' => $request->input('year_group'), 'is_active' => $request->boolean('is_active', true), 'notes' => $request->input('notes'), ]); // Trainer-Zuordnung sync $coachIds = $request->input('coach_ids', []); $team->coaches()->sync(array_map('intval', $coachIds)); // Dateien sync $this->syncTeamFiles($team, $request); $newData = [ 'name' => $team->name, 'year_group' => $team->year_group, 'is_active' => $team->is_active, 'notes' => $team->notes, ]; ActivityLog::logWithChanges('updated', __('admin.log_team_updated', ['name' => $team->name]), 'Team', $team->id, $oldData, $newData); return redirect()->route('admin.teams.edit', $team) ->with('success', __('admin.team_updated')); } public function updatePlayerTeam(Request $request, Team $team): JsonResponse { $validated = $request->validate([ 'player_id' => ['required', 'integer', 'exists:players,id'], 'new_team_id' => ['required', 'integer', 'exists:teams,id'], ]); $player = Player::findOrFail($validated['player_id']); // Spieler muss aktuell im Route-Team sein if ($player->team_id !== $team->id) { return response()->json(['error' => 'Forbidden'], 403); } // Ziel-Team muss existieren und aktiv sein $newTeam = Team::where('id', $validated['new_team_id'])->where('is_active', true)->first(); if (!$newTeam) { return response()->json(['error' => 'Ziel-Team nicht gefunden oder inaktiv'], 422); } $oldTeamId = $player->team_id; $player->update(['team_id' => $newTeam->id]); ActivityLog::logWithChanges( 'updated', __('admin.log_player_team_changed', ['name' => $player->full_name]), 'Player', $player->id, ['team_id' => $oldTeamId], ['team_id' => (int) $validated['new_team_id']] ); return response()->json(['success' => true]); } private function syncTeamFiles(Team $team, Request $request): void { $existingFileIds = $request->input('existing_files', []); $newFileIds = []; $newFiles = $request->file('new_files', []); $newCategories = $request->input('new_file_categories', []); foreach ($newFiles as $index => $uploadedFile) { if (!$uploadedFile || !$uploadedFile->isValid()) { continue; } $categoryId = $newCategories[$index] ?? null; if (!$categoryId) { continue; } $extension = $uploadedFile->guessExtension(); $storedName = Str::uuid() . '.' . $extension; Storage::disk('local')->putFileAs('files', $uploadedFile, $storedName); $file = new File([ 'file_category_id' => $categoryId, 'original_name' => $uploadedFile->getClientOriginalName(), 'mime_type' => $uploadedFile->getClientMimeType(), 'size' => $uploadedFile->getSize(), ]); $file->stored_name = $storedName; $file->disk = 'private'; $file->uploaded_by = auth()->id(); $file->save(); $newFileIds[] = $file->id; } $allFileIds = array_merge( array_map('intval', $existingFileIds), $newFileIds ); $team->files()->sync($allFileIds); } }