Team Registration view v1

This commit is contained in:
2026-05-06 09:02:00 +02:00
parent 1e25ca809b
commit a14cc8a0d9
12 changed files with 1120 additions and 25 deletions
@@ -35,6 +35,12 @@ public class TournamentContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Team>()
.HasOne(t => t.Leader)
.WithMany()
.HasForeignKey(t => t.LeaderId)
.OnDelete(DeleteBehavior.SetNull);
}
}
@@ -0,0 +1,348 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TournamentOrganizer;
#nullable disable
namespace TournamentOrganizer.Migrations
{
[DbContext(typeof(TournamentContext))]
[Migration("20260506061334_AddTeamLeader")]
partial class AddTeamLeader
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.5");
modelBuilder.Entity("TournamentOrganizer.Models.Event", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("End")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("Start")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Events");
});
modelBuilder.Entity("TournamentOrganizer.Models.Game", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("S1GroupAdvances")
.HasColumnType("INTEGER");
b.Property<int?>("S1Groups")
.HasColumnType("INTEGER");
b.Property<int>("S1RuleSet")
.HasColumnType("INTEGER");
b.Property<int?>("S2RuleSet")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Games");
});
modelBuilder.Entity("TournamentOrganizer.Models.Match", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TournamentId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("TournamentId");
b.ToTable("Matches");
});
modelBuilder.Entity("TournamentOrganizer.Models.Player", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Contact")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("TeamId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("TeamId");
b.ToTable("Players");
});
modelBuilder.Entity("TournamentOrganizer.Models.PlayerParticipant", b =>
{
b.Property<int>("RoundId")
.HasColumnType("INTEGER");
b.Property<int>("PlayerId")
.HasColumnType("INTEGER");
b.HasKey("RoundId", "PlayerId");
b.HasIndex("PlayerId");
b.ToTable("PlayerParticipants");
});
modelBuilder.Entity("TournamentOrganizer.Models.Round", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("MatchId")
.HasColumnType("INTEGER");
b.Property<int>("State")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("MatchId");
b.ToTable("Rounds");
});
modelBuilder.Entity("TournamentOrganizer.Models.Team", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("LeaderId")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("LeaderId");
b.ToTable("Teams");
});
modelBuilder.Entity("TournamentOrganizer.Models.TeamParticipant", b =>
{
b.Property<int>("MatchId")
.HasColumnType("INTEGER");
b.Property<int>("TeamId")
.HasColumnType("INTEGER");
b.Property<int>("Score")
.HasColumnType("INTEGER");
b.Property<int>("Seed")
.HasColumnType("INTEGER");
b.HasKey("MatchId", "TeamId");
b.HasIndex("TeamId");
b.ToTable("TeamParticipants");
});
modelBuilder.Entity("TournamentOrganizer.Models.Tournament", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("End")
.HasColumnType("TEXT");
b.Property<int>("EventId")
.HasColumnType("INTEGER");
b.Property<int>("GameId")
.HasColumnType("INTEGER");
b.Property<int?>("S1GroupAdvances")
.HasColumnType("INTEGER");
b.Property<int?>("S1Groups")
.HasColumnType("INTEGER");
b.Property<int>("S1RuleSet")
.HasColumnType("INTEGER");
b.Property<int?>("S2RuleSet")
.HasColumnType("INTEGER");
b.Property<DateTime>("Start")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("EventId");
b.HasIndex("GameId");
b.ToTable("Tournaments");
});
modelBuilder.Entity("TournamentOrganizer.Models.Match", b =>
{
b.HasOne("TournamentOrganizer.Models.Tournament", "Tournament")
.WithMany()
.HasForeignKey("TournamentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Tournament");
});
modelBuilder.Entity("TournamentOrganizer.Models.Player", b =>
{
b.HasOne("TournamentOrganizer.Models.Team", "Team")
.WithMany("Players")
.HasForeignKey("TeamId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Team");
});
modelBuilder.Entity("TournamentOrganizer.Models.PlayerParticipant", b =>
{
b.HasOne("TournamentOrganizer.Models.Player", "Player")
.WithMany()
.HasForeignKey("PlayerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TournamentOrganizer.Models.Round", "Round")
.WithMany()
.HasForeignKey("RoundId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Player");
b.Navigation("Round");
});
modelBuilder.Entity("TournamentOrganizer.Models.Round", b =>
{
b.HasOne("TournamentOrganizer.Models.Match", "Match")
.WithMany()
.HasForeignKey("MatchId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Match");
});
modelBuilder.Entity("TournamentOrganizer.Models.Team", b =>
{
b.HasOne("TournamentOrganizer.Models.Player", "Leader")
.WithMany()
.HasForeignKey("LeaderId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Leader");
});
modelBuilder.Entity("TournamentOrganizer.Models.TeamParticipant", b =>
{
b.HasOne("TournamentOrganizer.Models.Match", "Round")
.WithMany()
.HasForeignKey("MatchId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TournamentOrganizer.Models.Team", "Team")
.WithMany("Matches")
.HasForeignKey("TeamId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Round");
b.Navigation("Team");
});
modelBuilder.Entity("TournamentOrganizer.Models.Tournament", b =>
{
b.HasOne("TournamentOrganizer.Models.Event", "Event")
.WithMany("Tournaments")
.HasForeignKey("EventId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("TournamentOrganizer.Models.Game", "Game")
.WithMany("Tournaments")
.HasForeignKey("GameId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Event");
b.Navigation("Game");
});
modelBuilder.Entity("TournamentOrganizer.Models.Event", b =>
{
b.Navigation("Tournaments");
});
modelBuilder.Entity("TournamentOrganizer.Models.Game", b =>
{
b.Navigation("Tournaments");
});
modelBuilder.Entity("TournamentOrganizer.Models.Team", b =>
{
b.Navigation("Matches");
b.Navigation("Players");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TournamentOrganizer.Migrations
{
/// <inheritdoc />
public partial class AddTeamLeader : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "LeaderId",
table: "Teams",
type: "INTEGER",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Teams_LeaderId",
table: "Teams",
column: "LeaderId");
migrationBuilder.AddForeignKey(
name: "FK_Teams_Players_LeaderId",
table: "Teams",
column: "LeaderId",
principalTable: "Players",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Teams_Players_LeaderId",
table: "Teams");
migrationBuilder.DropIndex(
name: "IX_Teams_LeaderId",
table: "Teams");
migrationBuilder.DropColumn(
name: "LeaderId",
table: "Teams");
}
}
}
@@ -149,12 +149,17 @@ namespace TournamentOrganizer.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("LeaderId")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("LeaderId");
b.ToTable("Teams");
});
@@ -270,6 +275,16 @@ namespace TournamentOrganizer.Migrations
b.Navigation("Match");
});
modelBuilder.Entity("TournamentOrganizer.Models.Team", b =>
{
b.HasOne("TournamentOrganizer.Models.Player", "Leader")
.WithMany()
.HasForeignKey("LeaderId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Leader");
});
modelBuilder.Entity("TournamentOrganizer.Models.TeamParticipant", b =>
{
b.HasOne("TournamentOrganizer.Models.Match", "Round")
+3
View File
@@ -11,6 +11,9 @@ public class Team
public int Id { get; set; }
public string Name { get; set; } = "Example Team";
public int? LeaderId { get; set; }
public Player? Leader { get; set; }
public required List<Player> Players { get; set; }
public required List<TeamParticipant> Matches { get; set; }
@@ -1,9 +1,41 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace TournamentOrganizer.ViewModels;
public partial class MainViewModel : ViewModelBase
{
[ObservableProperty]
private string _greeting = "Welcome to Avalonia!";
}
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace TournamentOrganizer.ViewModels;
public partial class MainViewModel : ViewModelBase
{
[ObservableProperty]
private ViewModelBase _currentView;
[ObservableProperty]
private string _title = "Tournament Organizer";
public MainViewModel()
{
CurrentView = new HomeViewModel();
}
[RelayCommand]
private void NavigateToHome()
{
CurrentView = new HomeViewModel();
Title = "Tournament Organizer";
}
[RelayCommand]
private async Task NavigateToTeams()
{
var teamsVm = new TeamsViewModel();
CurrentView = teamsVm;
Title = "Teams Management";
await teamsVm.LoadTeams();
}
}
public partial class HomeViewModel : ViewModelBase
{
[ObservableProperty]
private string _welcomeMessage = "Welcome to Tournament Organizer!";
}
@@ -0,0 +1,470 @@
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 _filterEventName = string.Empty;
[ObservableProperty]
private string _filterTournamentName = string.Empty;
[ObservableProperty]
private bool _isEditing;
[ObservableProperty]
private string _statusMessage = string.Empty;
public TeamsViewModel()
{
_context = new TournamentContext();
}
public async Task LoadTeams()
{
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(FilterEventName))
{
var filter = FilterEventName.ToLower();
filtered = filtered.Where(t => GetAssociatedEvents(t).Any(e => e.ToLower().Contains(filter)));
}
if (!string.IsNullOrWhiteSpace(FilterTournamentName))
{
var filter = FilterTournamentName.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;
}
[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();
partial void OnFilterEventNameChanged(string value) => ApplyFilters();
partial void OnFilterTournamentNameChanged(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;
}
}
+17
View File
@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:TournamentOrganizer.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="TournamentOrganizer.Views.HomeView"
x:DataType="vm:HomeViewModel">
<Design.DataContext>
<vm:HomeViewModel />
</Design.DataContext>
<TextBlock Text="{Binding WelcomeMessage}"
FontSize="24"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</UserControl>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace TournamentOrganizer.Views;
public partial class HomeView : UserControl
{
public HomeView()
{
InitializeComponent();
}
}
+25 -16
View File
@@ -1,16 +1,25 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:TournamentOrganizer.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="TournamentOrganizer.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
</Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:TournamentOrganizer.ViewModels"
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="600"
x:Class="TournamentOrganizer.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<vm:MainViewModel />
</Design.DataContext>
<DockPanel>
<!-- Navigation Menu -->
<Border DockPanel.Dock="Top" Background="#2D2D30" Padding="8">
<StackPanel Orientation="Horizontal" Spacing="4">
<Button Content="Home" Command="{Binding NavigateToHomeCommand}" Padding="16,8" FontSize="14" Foreground="White" Background="Transparent" BorderThickness="0"/>
<Button Content="Teams" Command="{Binding NavigateToTeamsCommand}" Padding="16,8" FontSize="14" Foreground="White" Background="Transparent" BorderThickness="0"/>
</StackPanel>
</Border>
<!-- Content Area -->
<ContentControl Content="{Binding CurrentView}" Margin="0"/>
</DockPanel>
</UserControl>
+124
View File
@@ -0,0 +1,124 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:TournamentOrganizer.ViewModels"
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="600"
x:Class="TournamentOrganizer.Views.TeamsView"
x:DataType="vm:TeamsViewModel">
<Design.DataContext>
<vm:TeamsViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="*,*">
<TextBlock Grid.ColumnSpan="2" Grid.Row="0" Text="Teams Management" FontSize="20" FontWeight="Bold" Margin="16,16,16,8"/>
<!-- Left Panel: Team List and Filters -->
<Border Grid.Column="0" Grid.Row="1" BorderBrush="Gray" BorderThickness="1" Margin="8" CornerRadius="4" Padding="8">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Text="Teams" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,8"/>
<!-- Filters -->
<StackPanel DockPanel.Dock="Top" Spacing="6" Margin="0,0,0,8">
<TextBlock Text="Filters" FontWeight="SemiBold" FontSize="12"/>
<TextBox Watermark="Filter by member name..." Text="{Binding FilterMemberName}"/>
<TextBox Watermark="Filter by event..." Text="{Binding FilterEventName}"/>
<TextBox Watermark="Filter by tournament..." Text="{Binding FilterTournamentName}"/>
</StackPanel>
<!-- Team List -->
<ListBox ItemsSource="{Binding Teams}"
SelectedItem="{Binding SelectedTeam}"
Margin="0,0,0,8">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:TeamDisplay">
<StackPanel Spacing="2">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"/>
<TextBlock Text="{Binding LeaderName, StringFormat='Leader: {0}'}" FontSize="11" Foreground="Gray"/>
<TextBlock Text="{Binding Players.Count, StringFormat='{}{0} members'}" FontSize="11" Foreground="Gray"/>
<ItemsControl ItemsSource="{Binding AssociatedEvents}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{Binding, StringFormat='Event: {0}'}" FontSize="10" Foreground="#1976D2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding AssociatedTournaments}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{Binding, StringFormat='Tournament: {0}'}" FontSize="10" Foreground="#F57C00"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button DockPanel.Dock="Bottom" Content="New Team" Command="{Binding CreateNewTeamCommand}" HorizontalAlignment="Stretch"/>
</DockPanel>
</Border>
<!-- Right Panel: Team Editor -->
<Border Grid.Column="1" Grid.Row="1" BorderBrush="Gray" BorderThickness="1" Margin="8" CornerRadius="4" Padding="8">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Text="Team Details" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,8"/>
<StackPanel DockPanel.Dock="Bottom" Spacing="6" Margin="0,8,0,0">
<Button Content="Save Team" Command="{Binding SaveTeamCommand}" HorizontalAlignment="Stretch" IsEnabled="{Binding IsEditing}"/>
<Button Content="Delete Team" Command="{Binding DeleteTeamCommand}" HorizontalAlignment="Stretch" IsEnabled="{Binding IsEditing}"/>
</StackPanel>
<ScrollViewer>
<StackPanel Spacing="8" IsEnabled="{Binding IsEditing}">
<!-- Team Name -->
<StackPanel Spacing="4">
<TextBlock Text="Team Name" FontWeight="SemiBold"/>
<TextBox Text="{Binding TeamName}"/>
</StackPanel>
<!-- Members Section -->
<StackPanel Spacing="4">
<TextBlock Text="Members" FontWeight="SemiBold"/>
<!-- Add Player Form -->
<Grid ColumnDefinitions="*,*,Auto" RowDefinitions="Auto,Auto" RowSpacing="4" ColumnSpacing="4">
<TextBox Grid.Row="0" Grid.Column="0" Watermark="Player name" Text="{Binding NewPlayerName}"/>
<TextBox Grid.Row="0" Grid.Column="1" Watermark="Contact (optional)" Text="{Binding NewPlayerContact}"/>
<Button Grid.Row="0" Grid.Column="2" Content="Add" Command="{Binding AddPlayerCommand}" VerticalAlignment="Center"/>
<Button Grid.Row="1" Grid.Column="0" Content="Remove" Command="{Binding RemovePlayerCommand}"/>
<Button Grid.Row="1" Grid.Column="1" Content="Set as Leader" Command="{Binding SetLeaderCommand}"/>
</Grid>
<!-- Players List -->
<ListBox ItemsSource="{Binding Players}"
SelectedItem="{Binding SelectedPlayer}"
MinHeight="150"
MaxHeight="250">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:PlayerEntry">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Contact}" Foreground="Gray" FontSize="11"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<!-- Current Leader Display -->
<StackPanel Spacing="4">
<TextBlock Text="Current Leader" FontWeight="SemiBold"/>
<TextBlock Text="{Binding SelectedLeader.Name}" IsVisible="{Binding SelectedLeader, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<TextBlock Text="No leader set" IsVisible="{Binding SelectedLeader, Converter={x:Static ObjectConverters.IsNull}}" Foreground="Gray"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Border>
<!-- Status Bar -->
<TextBlock Grid.ColumnSpan="2" Grid.Row="2" Text="{Binding StatusMessage}" Margin="8" Foreground="Gray" FontSize="12"/>
</Grid>
</UserControl>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace TournamentOrganizer.Views;
public partial class TeamsView : UserControl
{
public TeamsView()
{
InitializeComponent();
}
}