From 67d27ab21cdb7d0789fd32a6949312aa2dbe015e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20Gol=C3=A1=C5=88=20jr?= Date: Wed, 6 May 2026 10:21:10 +0200 Subject: [PATCH] Event management --- .../ViewModels/EventsViewModel.cs | 386 ++++++++++++++++++ .../ViewModels/MainViewModel.cs | 18 + TournamentOrganizer/Views/EventsView.axaml | 143 +++++++ TournamentOrganizer/Views/EventsView.axaml.cs | 11 + TournamentOrganizer/Views/MainView.axaml | 2 + 5 files changed, 560 insertions(+) create mode 100644 TournamentOrganizer/ViewModels/EventsViewModel.cs create mode 100644 TournamentOrganizer/Views/EventsView.axaml create mode 100644 TournamentOrganizer/Views/EventsView.axaml.cs diff --git a/TournamentOrganizer/ViewModels/EventsViewModel.cs b/TournamentOrganizer/ViewModels/EventsViewModel.cs new file mode 100644 index 0000000..5a56d82 --- /dev/null +++ b/TournamentOrganizer/ViewModels/EventsViewModel.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.EntityFrameworkCore; +using TournamentOrganizer.Models; + +namespace TournamentOrganizer.ViewModels; + +public partial class EventsViewModel : ViewModelBase +{ + private readonly TournamentContext _context; + + [ObservableProperty] + private ObservableCollection _events = []; + + [ObservableProperty] + private EventDisplay? _selectedEvent; + + [ObservableProperty] + private string _eventName = string.Empty; + + [ObservableProperty] + private DateTime _eventStart = DateTime.Today; + + [ObservableProperty] + private DateTime _eventEnd = DateTime.Today.AddDays(1); + + [ObservableProperty] + private ObservableCollection _tournaments = []; + + [ObservableProperty] + private TournamentDisplay? _selectedTournament; + + [ObservableProperty] + private GameOption? _selectedGameToAdd; + + [ObservableProperty] + private string _filterEventName = string.Empty; + + [ObservableProperty] + private bool _isEditing; + + [ObservableProperty] + private string _statusMessage = string.Empty; + + private readonly ObservableCollection _availableGames = []; + private readonly ObservableCollection _availableGamesToAdd = []; + public ObservableCollection AvailableGamesToAdd => _availableGamesToAdd; + + public EventsViewModel() + { + _context = new TournamentContext(); + } + + partial void OnFilterEventNameChanged(string value) => ApplyFilters(); + + private async void ApplyFilters() + { + await LoadEvents(); + } + + public async Task LoadEvents() + { + var games = await _context.Games.ToListAsync(); + _availableGames.Clear(); + _availableGamesToAdd.Clear(); + foreach (var g in games) + { + var option = new GameOption(g); + _availableGames.Add(option); + _availableGamesToAdd.Add(option); + } + + var allEvents = await _context.Events + .Include(e => e.Tournaments) + .ThenInclude(t => t.Game) + .ToListAsync(); + + var filtered = allEvents.AsEnumerable(); + + if (!string.IsNullOrWhiteSpace(FilterEventName)) + { + var filter = FilterEventName.ToLower(); + filtered = filtered.Where(e => e.Name.ToLower().Contains(filter)); + } + + Events.Clear(); + foreach (var evt in filtered) + { + Events.Add(new EventDisplay(evt)); + } + + SelectedEvent = null; + IsEditing = false; + } + + [RelayCommand] + private async Task RefreshEvents() + { + await LoadEvents(); + } + + [RelayCommand] + private void CreateNewEvent() + { + EventName = "New Event"; + EventStart = DateTime.Today; + EventEnd = DateTime.Today.AddDays(1); + Tournaments.Clear(); + IsEditing = true; + SelectedEvent = null; + SelectedGameToAdd = null; + StatusMessage = "Creating new event"; + } + + [RelayCommand] + private async Task SaveEvent() + { + if (string.IsNullOrWhiteSpace(EventName)) + { + StatusMessage = "Event name is required"; + return; + } + + if (EventEnd <= EventStart) + { + StatusMessage = "End date must be after start date"; + return; + } + + Event? evt; + if (SelectedEvent != null && SelectedEvent.Id > 0) + { + evt = await _context.Events + .Include(e => e.Tournaments) + .FirstOrDefaultAsync(e => e.Id == SelectedEvent.Id); + + if (evt == null) + { + StatusMessage = "Event not found"; + return; + } + + evt.Name = EventName; + evt.Start = EventStart; + evt.End = EventEnd; + } + else + { + evt = new Event + { + Name = EventName, + Start = EventStart, + End = EventEnd, + Tournaments = [] + }; + + _context.Events.Add(evt); + } + + await _context.SaveChangesAsync(); + StatusMessage = $"Event '{EventName}' saved successfully"; + await LoadEvents(); + } + + [RelayCommand] + private async Task DeleteEvent() + { + if (SelectedEvent == null || SelectedEvent.Id == 0) + { + StatusMessage = "Select an event to delete"; + return; + } + + var evt = await _context.Events + .Include(e => e.Tournaments) + .FirstOrDefaultAsync(e => e.Id == SelectedEvent.Id); + + if (evt == null) + { + StatusMessage = "Event not found"; + return; + } + + _context.Events.Remove(evt); + await _context.SaveChangesAsync(); + StatusMessage = $"Event '{evt.Name}' deleted"; + await LoadEvents(); + } + + [RelayCommand] + private async Task AddGameToEvent() + { + if (SelectedGameToAdd == null) + { + StatusMessage = "Select a game to add"; + return; + } + + if (SelectedEvent == null || SelectedEvent.Id == 0) + { + StatusMessage = "Save the event first before adding games"; + return; + } + + var evt = await _context.Events + .Include(e => e.Tournaments) + .FirstOrDefaultAsync(e => e.Id == SelectedEvent.Id); + + if (evt == null) + { + StatusMessage = "Event not found"; + return; + } + + if (evt.Tournaments.Any(t => t.GameId == SelectedGameToAdd.Game.Id)) + { + StatusMessage = "This game is already added to the event"; + return; + } + + var tournament = new Tournament + { + GameId = SelectedGameToAdd.Game.Id, + EventId = evt.Id, + Start = evt.Start, + End = evt.End, + S1RuleSet = SelectedGameToAdd.Game.S1RuleSet, + S1Groups = SelectedGameToAdd.Game.S1Groups, + S1GroupAdvances = SelectedGameToAdd.Game.S1GroupAdvances, + S2RuleSet = SelectedGameToAdd.Game.S2RuleSet, + Game = SelectedGameToAdd.Game, + Event = evt + }; + + evt.Tournaments.Add(tournament); + await _context.SaveChangesAsync(); + StatusMessage = $"Added '{SelectedGameToAdd.Game.Name}' to event"; + await LoadEventDetails(); + } + + [RelayCommand] + private async Task RemoveGameFromEvent() + { + if (SelectedTournament == null) + { + StatusMessage = "Select a tournament to remove"; + return; + } + + if (SelectedEvent == null || SelectedEvent.Id == 0) + { + StatusMessage = "Select an event first"; + return; + } + + var tournament = await _context.Tournaments.FirstOrDefaultAsync(t => t.Id == SelectedTournament.Id); + if (tournament == null) + { + StatusMessage = "Tournament not found"; + return; + } + + _context.Tournaments.Remove(tournament); + await _context.SaveChangesAsync(); + StatusMessage = $"Removed '{SelectedTournament.GameName}' from event"; + await LoadEventDetails(); + } + + private async Task LoadEventDetails() + { + var allEvents = await _context.Events + .Include(e => e.Tournaments) + .ThenInclude(t => t.Game) + .ToListAsync(); + + var evt = allEvents.FirstOrDefault(e => e.Id == (SelectedEvent?.Id ?? 0)); + if (evt == null) + { + Tournaments.Clear(); + return; + } + + Tournaments.Clear(); + foreach (var t in evt.Tournaments) + { + Tournaments.Add(new TournamentDisplay(t)); + } + + SelectedTournament = null; + + _availableGamesToAdd.Clear(); + foreach (var g in _availableGames) + { + if (!Tournaments.Any(t => t.GameId == g.Game.Id)) + { + _availableGamesToAdd.Add(g); + } + } + + SelectedGameToAdd = null; + } + + partial void OnSelectedEventChanged(EventDisplay? value) + { + if (value == null) + { + IsEditing = false; + Tournaments.Clear(); + return; + } + + EventName = value.Name; + EventStart = value.Start; + EventEnd = value.End; + IsEditing = true; + _ = LoadEventDetails(); + StatusMessage = $"Editing event '{value.Name}'"; + } +} + +public class EventDisplay +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public DateTime Start { get; set; } + public DateTime End { get; set; } + public List TournamentNames { get; set; } = []; + + public EventDisplay() { } + + public EventDisplay(Event evt) + { + Id = evt.Id; + Name = evt.Name; + Start = evt.Start; + End = evt.End; + + if (evt.Tournaments != null) + { + TournamentNames = evt.Tournaments.Select(t => t.Game.Name).ToList(); + } + } +} + +public class TournamentDisplay +{ + public int Id { get; set; } + public int GameId { get; set; } + public string GameName { get; set; } = string.Empty; + public RuleSet S1RuleSet { get; set; } + public string S1RuleSetName => S1RuleSet.ToDisplayName(); + public RuleSet? S2RuleSet { get; set; } + public string? S2RuleSetName => S2RuleSet?.ToDisplayName(); + + public TournamentDisplay() { } + + public TournamentDisplay(Tournament tournament) + { + Id = tournament.Id; + GameId = tournament.GameId; + GameName = tournament.Game.Name; + S1RuleSet = tournament.S1RuleSet; + S2RuleSet = tournament.S2RuleSet; + } +} + +public class GameOption +{ + public Game Game { get; set; } = null!; + public string DisplayName { get; set; } = string.Empty; + + public GameOption() { } + + public GameOption(Game game) + { + Game = game; + DisplayName = game.Name; + } + + public override string ToString() => DisplayName; +} diff --git a/TournamentOrganizer/ViewModels/MainViewModel.cs b/TournamentOrganizer/ViewModels/MainViewModel.cs index 47b5eca..baf8ee9 100644 --- a/TournamentOrganizer/ViewModels/MainViewModel.cs +++ b/TournamentOrganizer/ViewModels/MainViewModel.cs @@ -32,6 +32,24 @@ public partial class MainViewModel : ViewModelBase Title = "Teams Management"; await teamsVm.LoadTeams(); } + + [RelayCommand] + private async Task NavigateToGames() + { + var gamesVm = new GamesViewModel(); + CurrentView = gamesVm; + Title = "Games Management"; + await gamesVm.LoadGames(); + } + + [RelayCommand] + private async Task NavigateToEvents() + { + var eventsVm = new EventsViewModel(); + CurrentView = eventsVm; + Title = "Events Management"; + await eventsVm.LoadEvents(); + } } public partial class HomeViewModel : ViewModelBase diff --git a/TournamentOrganizer/Views/EventsView.axaml b/TournamentOrganizer/Views/EventsView.axaml new file mode 100644 index 0000000..9346c02 --- /dev/null +++ b/TournamentOrganizer/Views/EventsView.axaml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +