564 lines
15 KiB
C#
564 lines
15 KiB
C#
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 TeamsViewModel : ViewModelBase
|
|
{
|
|
private readonly TournamentContext _context;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<TeamDisplay> _teams = [];
|
|
|
|
[ObservableProperty]
|
|
private TeamDisplay? _selectedTeam;
|
|
|
|
[ObservableProperty]
|
|
private string _teamName = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<PlayerEntry> _players = [];
|
|
|
|
[ObservableProperty]
|
|
private PlayerEntry? _selectedPlayer;
|
|
|
|
[ObservableProperty]
|
|
private PlayerEntry? _selectedLeader;
|
|
|
|
[ObservableProperty]
|
|
private string _newPlayerName = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _newPlayerContact = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _filterMemberName = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string? _selectedEventFilter;
|
|
|
|
[ObservableProperty]
|
|
private string? _selectedTournamentFilter;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<string> _availableEvents = [];
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<string> _availableTournaments = [];
|
|
|
|
[ObservableProperty]
|
|
private string _eventFilterSearch = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _tournamentFilterSearch = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private bool _isEventDropdownOpen;
|
|
|
|
[ObservableProperty]
|
|
private bool _isTournamentDropdownOpen;
|
|
|
|
[ObservableProperty]
|
|
private bool _isEditing;
|
|
|
|
[ObservableProperty]
|
|
private string _statusMessage = string.Empty;
|
|
|
|
private readonly ObservableCollection<string> _filteredEvents = [];
|
|
public ObservableCollection<string> FilteredEvents => _filteredEvents;
|
|
|
|
private readonly ObservableCollection<string> _filteredTournaments = [];
|
|
public ObservableCollection<string> FilteredTournaments => _filteredTournaments;
|
|
|
|
partial void OnEventFilterSearchChanged(string value) => UpdateFilteredEvents();
|
|
|
|
partial void OnTournamentFilterSearchChanged(string value) => UpdateFilteredTournaments();
|
|
|
|
private void UpdateFilteredEvents()
|
|
{
|
|
_filteredEvents.Clear();
|
|
foreach (var e in AvailableEvents.Where(e => string.IsNullOrWhiteSpace(EventFilterSearch) || e.ToLower().Contains(EventFilterSearch.ToLower())))
|
|
{
|
|
_filteredEvents.Add(e);
|
|
}
|
|
}
|
|
|
|
private void UpdateFilteredTournaments()
|
|
{
|
|
_filteredTournaments.Clear();
|
|
foreach (var t in AvailableTournaments.Where(t => string.IsNullOrWhiteSpace(TournamentFilterSearch) || t.ToLower().Contains(TournamentFilterSearch.ToLower())))
|
|
{
|
|
_filteredTournaments.Add(t);
|
|
}
|
|
}
|
|
|
|
public TeamsViewModel()
|
|
{
|
|
_context = new TournamentContext();
|
|
}
|
|
|
|
public async Task LoadTeams()
|
|
{
|
|
var events = await _context.Events.ToListAsync();
|
|
AvailableEvents.Clear();
|
|
foreach (var e in events)
|
|
{
|
|
AvailableEvents.Add(e.Name);
|
|
}
|
|
UpdateFilteredEvents();
|
|
|
|
var games = await _context.Games.ToListAsync();
|
|
AvailableTournaments.Clear();
|
|
foreach (var g in games)
|
|
{
|
|
foreach (var e in events)
|
|
{
|
|
AvailableTournaments.Add($"{g.Name} @ {e.Name}");
|
|
}
|
|
}
|
|
UpdateFilteredTournaments();
|
|
|
|
var query = _context.Teams
|
|
.Include(t => t.Players)
|
|
.Include(t => t.Leader)
|
|
.Include(t => t.Matches)
|
|
.ThenInclude(tp => tp.Round)
|
|
.ThenInclude(m => m.Tournament)
|
|
.ThenInclude(t => t.Event)
|
|
.AsSplitQuery();
|
|
|
|
var allTeams = await query.ToListAsync();
|
|
|
|
var filtered = allTeams.AsEnumerable();
|
|
|
|
if (!string.IsNullOrWhiteSpace(FilterMemberName))
|
|
{
|
|
var filter = FilterMemberName.ToLower();
|
|
filtered = filtered.Where(t => t.Players.Any(p => p.Name.ToLower().Contains(filter)));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(SelectedEventFilter))
|
|
{
|
|
var filter = SelectedEventFilter.ToLower();
|
|
filtered = filtered.Where(t => GetAssociatedEvents(t).Any(e => e.ToLower().Contains(filter)));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(SelectedTournamentFilter))
|
|
{
|
|
var filter = SelectedTournamentFilter.ToLower();
|
|
filtered = filtered.Where(t => GetAssociatedTournaments(t).Any(tn => tn.ToLower().Contains(filter)));
|
|
}
|
|
|
|
Teams.Clear();
|
|
foreach (var team in filtered)
|
|
{
|
|
Teams.Add(new TeamDisplay(team));
|
|
}
|
|
|
|
SelectedTeam = null;
|
|
IsEditing = false;
|
|
}
|
|
|
|
public void SelectEventFilter(string eventName)
|
|
{
|
|
SelectedEventFilter = eventName;
|
|
IsEventDropdownOpen = false;
|
|
EventFilterSearch = string.Empty;
|
|
_ = LoadTeams();
|
|
}
|
|
|
|
public void ClearEventFilter()
|
|
{
|
|
SelectedEventFilter = null;
|
|
EventFilterSearch = string.Empty;
|
|
_ = LoadTeams();
|
|
}
|
|
|
|
public void SelectTournamentFilter(string tournamentName)
|
|
{
|
|
SelectedTournamentFilter = tournamentName;
|
|
IsTournamentDropdownOpen = false;
|
|
TournamentFilterSearch = string.Empty;
|
|
_ = LoadTeams();
|
|
}
|
|
|
|
public void ClearTournamentFilter()
|
|
{
|
|
SelectedTournamentFilter = null;
|
|
TournamentFilterSearch = string.Empty;
|
|
_ = LoadTeams();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task RefreshTeams()
|
|
{
|
|
await LoadTeams();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void CreateNewTeam()
|
|
{
|
|
TeamName = "New Team";
|
|
Players.Clear();
|
|
SelectedLeader = null;
|
|
SelectedPlayer = null;
|
|
IsEditing = true;
|
|
SelectedTeam = null;
|
|
NewPlayerName = string.Empty;
|
|
NewPlayerContact = string.Empty;
|
|
StatusMessage = "Creating new team";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task SaveTeam()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(TeamName))
|
|
{
|
|
StatusMessage = "Team name is required";
|
|
return;
|
|
}
|
|
|
|
if (Players.Count == 0)
|
|
{
|
|
StatusMessage = "Team must have at least one member";
|
|
return;
|
|
}
|
|
|
|
Team? team;
|
|
if (SelectedTeam != null && SelectedTeam.Id > 0)
|
|
{
|
|
team = await _context.Teams
|
|
.Include(t => t.Players)
|
|
.FirstOrDefaultAsync(t => t.Id == SelectedTeam.Id);
|
|
|
|
if (team == null)
|
|
{
|
|
StatusMessage = "Team not found";
|
|
return;
|
|
}
|
|
|
|
team.Name = TeamName;
|
|
|
|
var existingPlayerIds = team.Players.Select(p => p.Id).ToHashSet();
|
|
var currentPlayerIds = Players.Where(p => p.Id > 0).Select(p => p.Id).ToHashSet();
|
|
|
|
var playersToRemove = team.Players.Where(p => !currentPlayerIds.Contains(p.Id)).ToList();
|
|
foreach (var player in playersToRemove)
|
|
{
|
|
team.Players.Remove(player);
|
|
_context.Players.Remove(player);
|
|
}
|
|
|
|
foreach (var entry in Players)
|
|
{
|
|
if (entry.Id > 0)
|
|
{
|
|
var existing = team.Players.FirstOrDefault(p => p.Id == entry.Id);
|
|
if (existing != null)
|
|
{
|
|
existing.Name = entry.Name;
|
|
existing.Contact = entry.Contact;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var newPlayer = new Player
|
|
{
|
|
Name = entry.Name,
|
|
Contact = entry.Contact,
|
|
Team = team
|
|
};
|
|
team.Players.Add(newPlayer);
|
|
entry.Id = newPlayer.Id;
|
|
}
|
|
}
|
|
|
|
if (SelectedLeader != null)
|
|
{
|
|
var leader = team.Players.FirstOrDefault(p =>
|
|
(SelectedLeader.Id > 0 && p.Id == SelectedLeader.Id) ||
|
|
(SelectedLeader.Id == 0 && p.Name == SelectedLeader.Name && p.Contact == SelectedLeader.Contact));
|
|
team.Leader = leader;
|
|
}
|
|
else
|
|
{
|
|
team.Leader = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
team = new Team
|
|
{
|
|
Name = TeamName,
|
|
Players = [],
|
|
Matches = []
|
|
};
|
|
|
|
var playerEntries = new List<(PlayerEntry Entry, Player Player)>();
|
|
foreach (var entry in Players)
|
|
{
|
|
var player = new Player
|
|
{
|
|
Name = entry.Name,
|
|
Contact = entry.Contact,
|
|
Team = team
|
|
};
|
|
team.Players.Add(player);
|
|
playerEntries.Add((entry, player));
|
|
}
|
|
|
|
_context.Teams.Add(team);
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
if (team.Players.Count > 0)
|
|
{
|
|
int leaderIndex = 0;
|
|
if (SelectedLeader != null)
|
|
{
|
|
var idx = Players.IndexOf(SelectedLeader);
|
|
if (idx >= 0) leaderIndex = idx;
|
|
}
|
|
|
|
team.LeaderId = playerEntries[leaderIndex].Player.Id;
|
|
await _context.SaveChangesAsync();
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync();
|
|
StatusMessage = $"Team '{TeamName}' saved successfully";
|
|
await LoadTeams();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task DeleteTeam()
|
|
{
|
|
if (SelectedTeam == null || SelectedTeam.Id == 0)
|
|
{
|
|
StatusMessage = "Select a team to delete";
|
|
return;
|
|
}
|
|
|
|
var team = await _context.Teams
|
|
.Include(t => t.Players)
|
|
.FirstOrDefaultAsync(t => t.Id == SelectedTeam.Id);
|
|
|
|
if (team == null)
|
|
{
|
|
StatusMessage = "Team not found";
|
|
return;
|
|
}
|
|
|
|
_context.Teams.Remove(team);
|
|
await _context.SaveChangesAsync();
|
|
StatusMessage = $"Team '{team.Name}' deleted";
|
|
await LoadTeams();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void AddPlayer()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(NewPlayerName))
|
|
{
|
|
StatusMessage = "Player name is required";
|
|
return;
|
|
}
|
|
|
|
var entry = new PlayerEntry
|
|
{
|
|
Name = NewPlayerName,
|
|
Contact = NewPlayerContact
|
|
};
|
|
Players.Add(entry);
|
|
|
|
if (SelectedLeader == null)
|
|
{
|
|
SelectedLeader = entry;
|
|
}
|
|
|
|
NewPlayerName = string.Empty;
|
|
NewPlayerContact = string.Empty;
|
|
StatusMessage = $"Player '{entry.Name}' added";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void RemovePlayer()
|
|
{
|
|
if (SelectedPlayer == null)
|
|
{
|
|
StatusMessage = "Select a player to remove";
|
|
return;
|
|
}
|
|
|
|
var wasLeader = SelectedLeader == SelectedPlayer;
|
|
Players.Remove(SelectedPlayer);
|
|
|
|
if (wasLeader)
|
|
{
|
|
SelectedLeader = Players.FirstOrDefault();
|
|
}
|
|
|
|
SelectedPlayer = null;
|
|
StatusMessage = "Player removed";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void SetLeader()
|
|
{
|
|
if (SelectedPlayer == null)
|
|
{
|
|
StatusMessage = "Select a player to make leader";
|
|
return;
|
|
}
|
|
|
|
SelectedLeader = SelectedPlayer;
|
|
StatusMessage = $"{SelectedPlayer.Name} is now the leader";
|
|
}
|
|
|
|
partial void OnSelectedTeamChanged(TeamDisplay? value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
IsEditing = false;
|
|
return;
|
|
}
|
|
|
|
TeamName = value.Name;
|
|
Players.Clear();
|
|
foreach (var player in value.Players)
|
|
{
|
|
Players.Add(player);
|
|
}
|
|
|
|
SelectedLeader = Players.FirstOrDefault(p => p.Id == value.LeaderId);
|
|
SelectedPlayer = null;
|
|
IsEditing = true;
|
|
NewPlayerName = string.Empty;
|
|
NewPlayerContact = string.Empty;
|
|
StatusMessage = $"Editing team '{value.Name}'";
|
|
}
|
|
|
|
partial void OnFilterMemberNameChanged(string value) => ApplyFilters();
|
|
|
|
private async void ApplyFilters()
|
|
{
|
|
await LoadTeams();
|
|
}
|
|
|
|
private static List<string> GetAssociatedEvents(Team team)
|
|
{
|
|
var events = new List<string>();
|
|
if (team.Matches != null)
|
|
{
|
|
foreach (var tp in team.Matches)
|
|
{
|
|
if (tp.Round?.Tournament?.Event != null)
|
|
{
|
|
var eventName = tp.Round.Tournament.Event.Name;
|
|
if (!events.Contains(eventName))
|
|
{
|
|
events.Add(eventName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
|
|
private static List<string> GetAssociatedTournaments(Team team)
|
|
{
|
|
var tournaments = new List<string>();
|
|
if (team.Matches != null)
|
|
{
|
|
foreach (var tp in team.Matches)
|
|
{
|
|
if (tp.Round?.Tournament != null)
|
|
{
|
|
var tournamentName = GetTournamentDisplayName(tp.Round.Tournament);
|
|
if (!tournaments.Contains(tournamentName))
|
|
{
|
|
tournaments.Add(tournamentName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tournaments;
|
|
}
|
|
|
|
public static string GetTournamentDisplayName(Tournament tournament)
|
|
{
|
|
return $"{tournament.Game.Name} @ {tournament.Event.Name}";
|
|
}
|
|
}
|
|
|
|
public class TeamDisplay
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = string.Empty;
|
|
public int? LeaderId { get; set; }
|
|
public string? LeaderName { get; set; }
|
|
public List<PlayerEntry> Players { get; set; } = [];
|
|
public List<string> AssociatedEvents { get; set; } = [];
|
|
public List<string> AssociatedTournaments { get; set; } = [];
|
|
|
|
public TeamDisplay() { }
|
|
|
|
public TeamDisplay(Team team)
|
|
{
|
|
Id = team.Id;
|
|
Name = team.Name;
|
|
LeaderId = team.LeaderId;
|
|
LeaderName = team.Leader?.Name;
|
|
Players = team.Players?.Select(p => new PlayerEntry(p)).ToList() ?? [];
|
|
|
|
var events = new HashSet<string>();
|
|
var tournaments = new HashSet<string>();
|
|
|
|
if (team.Matches != null)
|
|
{
|
|
foreach (var tp in team.Matches)
|
|
{
|
|
if (tp.Round?.Tournament != null)
|
|
{
|
|
tournaments.Add(TeamsViewModel.GetTournamentDisplayName(tp.Round.Tournament));
|
|
if (tp.Round.Tournament.Event != null)
|
|
{
|
|
events.Add(tp.Round.Tournament.Event.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AssociatedEvents = events.ToList();
|
|
AssociatedTournaments = tournaments.ToList();
|
|
}
|
|
}
|
|
|
|
public partial class PlayerEntry : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private int _id;
|
|
|
|
[ObservableProperty]
|
|
private string _name = string.Empty;
|
|
|
|
[ObservableProperty]
|
|
private string _contact = string.Empty;
|
|
|
|
public PlayerEntry() { }
|
|
|
|
public PlayerEntry(Player player)
|
|
{
|
|
Id = player.Id;
|
|
Name = player.Name;
|
|
Contact = player.Contact;
|
|
}
|
|
}
|