- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er) - Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link - Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest - Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen) - Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
210 lines
6.8 KiB
PHP
Executable File
210 lines
6.8 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Enums\UserRole;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\ActivityLog;
|
|
use App\Models\File;
|
|
use App\Models\FileCategory;
|
|
use App\Models\Player;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\View\View;
|
|
|
|
class TeamController extends Controller
|
|
{
|
|
public function index(): View
|
|
{
|
|
$teams = Team::withCount(['players', 'events'])->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);
|
|
}
|
|
}
|