diff --git a/app/Http/Controllers/TournamentController.php b/app/Http/Controllers/TournamentController.php new file mode 100644 index 0000000..842607b --- /dev/null +++ b/app/Http/Controllers/TournamentController.php @@ -0,0 +1,221 @@ +all(), [ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'location' => 'nullable|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'registration_deadline' => 'nullable|date|before_or_equal:start_date', + 'max_teams' => 'nullable|integer|min:2', + 'status' => 'required|in:draft,public,private', + 'team_ids' => 'nullable|array', + 'team_ids.*' => 'exists:teams,id', + 'field_ids' => 'nullable|array', + 'field_ids.*' => 'exists:fields,id', + ]); + + if ($validator->fails()) { + return redirect()->back()->withErrors($validator)->withInput(); + } + + // Create the tournament (competition) + $tournament = new Competition(); + $tournament->name = $request->name; + $tournament->description = $request->description; + $tournament->location = $request->location; + $tournament->start_date = $request->start_date; + $tournament->end_date = $request->end_date; + $tournament->registration_deadline = $request->registration_deadline; + $tournament->max_teams = $request->max_teams; + $tournament->status = $request->status; + $tournament->owner = Auth::id(); + $tournament->save(); + + // Associate teams with the tournament + if ($request->has('team_ids') && is_array($request->team_ids)) { + $tournament->teams()->attach($request->team_ids, ['status' => 'confirmed']); + } + + // Associate fields with the tournament + if ($request->has('field_ids') && is_array($request->field_ids)) { + $tournament->fields()->attach($request->field_ids); + } + + // Create a default scheduling mode + $schedulingMode = new SchedulingMode(); + $schedulingMode->competition_id = $tournament->id; + $schedulingMode->name = 'Default'; + $schedulingMode->description = 'Default scheduling mode'; + $schedulingMode->algorithm = 'round_robin'; + $schedulingMode->config = json_encode([ + 'match_duration' => 90, // 90 minutes + 'break_between_matches' => 15, // 15 minutes + ]); + $schedulingMode->sequence_order = 1; + $schedulingMode->status = 'active'; + $schedulingMode->save(); + + // Set as current scheduling mode + $tournament->current_scheduling_mode_id = $schedulingMode->id; + $tournament->save(); + + return redirect()->route('tournaments.show', $tournament->id) + ->with('success', 'Tournament created successfully!'); + } + + /** + * Display the specified tournament. + * + * @param \App\Models\Competition $tournament + * @return \Inertia\Response + */ + public function show(Competition $tournament) + { + // Load relationships + $tournament->load(['teams', 'fields', 'matches', 'breakPeriods']); + + return Inertia::render('tournaments/Show', [ + 'tournament' => $tournament, + ]); + } + + /** + * Show the form for editing the specified tournament. + * + * @param \App\Models\Competition $tournament + * @return \Inertia\Response + */ + public function edit(Competition $tournament) + { + // Load relationships + $tournament->load(['teams', 'fields']); + + // Get all fields and teams for dropdowns + $fields = Field::all(); + $teams = Team::all(); + + return Inertia::render('tournaments/Edit', [ + 'tournament' => $tournament, + 'fields' => $fields, + 'teams' => $teams, + ]); + } + + /** + * Update the specified tournament in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Competition $tournament + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request, Competition $tournament) + { + // Check if user is tournament owner + if ($tournament->owner != Auth::id()) { + return redirect()->back()->with('error', 'You are not authorized to edit this tournament.'); + } + + // Validate the request data + $validator = Validator::make($request->all(), [ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'location' => 'nullable|string|max:255', + 'start_date' => 'required|date', + 'end_date' => 'required|date|after_or_equal:start_date', + 'registration_deadline' => 'nullable|date|before_or_equal:start_date', + 'max_teams' => 'nullable|integer|min:2', + 'status' => 'required|in:draft,public,private', + 'team_ids' => 'nullable|array', + 'team_ids.*' => 'exists:teams,id', + 'field_ids' => 'nullable|array', + 'field_ids.*' => 'exists:fields,id', + ]); + + if ($validator->fails()) { + return redirect()->back()->withErrors($validator)->withInput(); + } + + // Update the tournament + $tournament->name = $request->name; + $tournament->description = $request->description; + $tournament->location = $request->location; + $tournament->start_date = $request->start_date; + $tournament->end_date = $request->end_date; + $tournament->registration_deadline = $request->registration_deadline; + $tournament->max_teams = $request->max_teams; + $tournament->status = $request->status; + $tournament->save(); + + // Sync teams + if ($request->has('team_ids')) { + $teamSync = []; + foreach ($request->team_ids as $teamId) { + $teamSync[$teamId] = ['status' => 'confirmed']; + } + $tournament->teams()->sync($teamSync); + } + + // Sync fields + if ($request->has('field_ids')) { + $tournament->fields()->sync($request->field_ids); + } + + return redirect()->route('tournaments.show', $tournament->id) + ->with('success', 'Tournament updated successfully!'); + } + + /** + * Remove the specified tournament from storage. + * + * @param \App\Models\Competition $tournament + * @return \Illuminate\Http\RedirectResponse + */ + public function destroy(Competition $tournament) + { + // Check if user is tournament owner + if ($tournament->owner != Auth::id()) { + return redirect()->back()->with('error', 'You are not authorized to delete this tournament.'); + } + + // Delete the tournament + $tournament->delete(); + + return redirect()->route('home') + ->with('success', 'Tournament deleted successfully!'); + } +} diff --git a/package.json b/package.json index a424b46..c49a064 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "concurrently": "^9.1.2", + "date-fns": "^4.1.0", "embla-carousel-svelte": "^8.6.0", "eslint": "^9.27.0", "eslint-config-prettier": "^10.1.5", @@ -62,5 +63,6 @@ "@rollup/rollup-linux-x64-gnu": "4.41.1", "@tailwindcss/oxide-linux-x64-gnu": "^4.1.8", "lightningcss-linux-x64-gnu": "^1.30.1" - } + }, + "packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a8dac9..1f7ce68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: concurrently: specifier: ^9.1.2 version: 9.1.2 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 embla-carousel-svelte: specifier: ^8.6.0 version: 8.6.0(svelte@5.34.7) @@ -1237,6 +1240,9 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -3801,6 +3807,8 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 + date-fns@4.1.0: {} + dayjs@1.11.13: optional: true diff --git a/resources/js/pages/tournaments/Create.svelte b/resources/js/pages/tournaments/Create.svelte new file mode 100644 index 0000000..acc03f8 --- /dev/null +++ b/resources/js/pages/tournaments/Create.svelte @@ -0,0 +1,170 @@ + + + +
+

Create New Tournament

+ +
+
+ +
+ + + {#if errors.name} +

{errors.name}

+ {/if} +
+ + +
+ +