Improve team identifier handling

This commit is contained in:
2025-11-23 13:30:24 -05:00
parent 1aaebe5dce
commit d8f2a3cf52
6 changed files with 222 additions and 44 deletions
@@ -155,19 +155,10 @@
<MudDivider Class="my-4" /> <MudDivider Class="my-4" />
<MudText Typo="Typo.h4">Absent Students</MudText> <StudentToggleSelector Students="@_students"
<MudToggleGroup T="Student" @bind-SelectedStudents="_absentStudents"
SelectionMode="SelectionMode.MultiSelection" Title="Absent Students"
@bind-Values="_absentStudents" ShowFullName="true" />
Vertical="true"
CheckMark>
@foreach (var student in _students.OrderBy(e => e.FirstName))
{
<MudToggleItem Value="@student" Style="font-size: .75rem;">
@student.FirstNameLastName
</MudToggleItem>
}
</MudToggleGroup>
</MudStack> </MudStack>
</MudItem> </MudItem>
+99 -3
View File
@@ -15,18 +15,45 @@
<MudGrid> <MudGrid>
<MudItem xs="12" sm="7"> <MudItem xs="12" sm="7">
<MudPaper Class="pa-4"> <MudPaper Class="pa-4">
<MudSelect T="EventDefinition" @bind-Value="@Team.Event" Label="Event"> <MudSelect T="EventDefinition" Value="@Team.Event" ValueChanged="OnEventChanged" Label="Event">
@foreach (var evt in _events) @foreach (var evt in _events)
{ {
<MudSelectItem T="EventDefinition" Value="@(evt)"></MudSelectItem> <MudSelectItem T="EventDefinition" Value="@(evt)"></MudSelectItem>
} }
</MudSelect> </MudSelect>
<MudTextField T="string?" Label="Number" @bind-Value="Team.Identifier" For="@(() => Team.Identifier)"></MudTextField> @if (_existingTeams?.Count == 1)
{
<MudAlert Severity="Severity.Info" Class="my-2">
A team for @Team.Event.Name already exists. This will be team 2, and the existing team will become team 1.
</MudAlert>
}
else if (_existingTeams?.Count >= 2)
{
<MudAlert Severity="Severity.Error" Class="my-2">
Two teams for @Team.Event.Name already exist. Cannot create a third team.
</MudAlert>
}
<MudDivider Class="my-4" />
<StudentToggleSelector Students="@_students"
@bind-SelectedStudents="_selectedStudents"
Title="Students"
ShowFullName="true" />
<MudStack Class="mt-4">
<TeamCaptainSelector Students="@_selectedStudents"
@bind-SelectedCaptain="Team.Captain"
Title="Captain" />
</MudStack>
</MudPaper> </MudPaper>
</MudItem> </MudItem>
</MudGrid> </MudGrid>
@if (!string.IsNullOrEmpty(_errorMessage))
{
<MudAlert Severity="Severity.Error" Class="mt-3">@_errorMessage</MudAlert>
}
<MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students">Back</MudButton> <MudButton StartIcon="@Icons.Material.Filled.ArrowBack" Href="students">Back</MudButton>
<MudButton StartIcon="@Icons.Material.Filled.Add" OnClick="AddTeam">Add</MudButton> <MudButton StartIcon="@Icons.Material.Filled.Add" OnClick="AddTeam">Add</MudButton>
</EditForm> </EditForm>
@@ -36,6 +63,10 @@
private Team Team { get; set; } = new(); private Team Team { get; set; } = new();
private List<EventDefinition>? _events; private List<EventDefinition>? _events;
private List<Student> _students = [];
private IEnumerable<Student> _selectedStudents = [];
private string? _errorMessage;
private List<Team>? _existingTeams;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@@ -43,11 +74,76 @@
await Context.Events await Context.Events
.OrderBy(e => e.Name) .OrderBy(e => e.Name)
.ToListAsync(); .ToListAsync();
_students = await Context.Students.ToListAsync();
}
private async Task OnEventChanged(EventDefinition selectedEvent)
{
Team.Event = selectedEvent;
// Clear any previous error messages
_errorMessage = null;
// Get all existing teams for this event
_existingTeams = await Context.Teams
.Include(t => t.Event)
.Where(t => t.Event.Id == selectedEvent.Id)
.ToListAsync();
} }
private async Task AddTeam() private async Task AddTeam()
{ {
Team.Identifier = Team.Event.Name; // Clear previous error message
_errorMessage = null;
// Get current count of teams for this event
var existingTeamCount = await Context.Teams
.CountAsync(t => t.Event.Id == Team.Event.Id);
// Prohibit creation of third team
if (existingTeamCount >= 2)
{
_errorMessage = $"Cannot create a third team for {Team.Event.Name}. Maximum of 2 teams allowed.";
return;
}
// Handle automatic numbering based on event format
if (Team.Event.EventFormat == EventFormat.Individual && _selectedStudents.Count() == 1)
{
// For individual events, use student's first name as identifier
var student = _selectedStudents.First();
Team.Identifier = student.FirstName;
Team.Captain = student;
}
else if (existingTeamCount == 1)
{
// This is the second team - assign numbers
var existingTeam = await Context.Teams
.FirstOrDefaultAsync(t => t.Event.Id == Team.Event.Id);
if (existingTeam != null)
{
// Update existing team to number 1
existingTeam.Identifier = "1";
Context.Teams.Update(existingTeam);
}
// Set new team to number 2
Team.Identifier = "2";
}
else
{
// This is the first team - no number
Team.Identifier = null;
}
// Add selected students to the team
foreach (var student in _selectedStudents)
{
Team.Students.Add(student);
}
Context.Teams.Add(Team); Context.Teams.Add(Team);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
+17 -26
View File
@@ -20,28 +20,15 @@ else
<MudGrid> <MudGrid>
<MudItem xs="12" sm="7"> <MudItem xs="12" sm="7">
<MudPaper Class="pa-4"> <MudPaper Class="pa-4">
<MudSelect <StudentToggleSelector Students="@_students"
T="Student" @bind-SelectedStudents="_selectedStudents"
MultiSelection="true" Title="Students"
@bind-SelectedValues="@_selectedStudents" ShowFullName="true" />
ToStringFunc="e => e.Name" <MudStack Class="mt-4">
Label="Students"> <TeamCaptainSelector Students="@_selectedStudents"
@bind-SelectedCaptain="Team.Captain"
@foreach (var student in _students.OrderBy(e => e.FirstName)) Title="Captain" />
{
<MudSelectItem T="Student" Value="@student">@student.Name</MudSelectItem>
}
</MudSelect>
<MudStack>
<MudText>Captain</MudText>
<MudToggleGroup T="Student" SelectionMode="SelectionMode.ToggleSelection" @bind-Value="Team.Captain" CheckMark>
@foreach (var student in _selectedStudents.OrderBy(e => e.FirstName))
{
<MudToggleItem Value="@(student)" Text="@student.Name" />
}
</MudToggleGroup>
</MudStack> </MudStack>
<MudTextField T="string?" Label="Identifier" @bind-Value="Team.Identifier" For="@(() => Team.Identifier)" Required="false" Clearable="true"></MudTextField>
</MudPaper> </MudPaper>
</MudItem> </MudItem>
</MudGrid> </MudGrid>
@@ -57,7 +44,7 @@ else
[SupplyParameterFromForm] [SupplyParameterFromForm]
private Team? Team { get; set; } private Team? Team { get; set; }
private IEnumerable<Student> _selectedStudents = new HashSet<Student>(); private IEnumerable<Student> _selectedStudents = [];
private List<Student> _students = []; private List<Student> _students = [];
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@@ -67,10 +54,7 @@ else
.Include(e => e.Students) .Include(e => e.Students)
.FirstOrDefaultAsync(m => m.Id == Id); .FirstOrDefaultAsync(m => m.Id == Id);
_students = await Context.Students.ToListAsync(); _students = await Context.Students.ToListAsync();
foreach (var s in Team.Students) _selectedStudents = Team.Students.ToList();
{
((HashSet<Student>)_selectedStudents).Add(s);
}
if (Team is null) if (Team is null)
{ {
@@ -97,6 +81,13 @@ else
Team.Students.Add(s); Team.Students.Add(s);
} }
// Update identifier for individual events
if (Team.Event.EventFormat == EventFormat.Individual && Team.Students.Count == 1)
{
Team.Captain ??= Team.Students[0];
Team.Identifier = Team.Captain.FirstName;
}
try try
{ {
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
@@ -83,6 +83,20 @@
if (result == true) if (result == true)
{ {
// If deleting a numbered team (1 or 2), clear the identifier of the remaining team
if (team.Identifier == "1" || team.Identifier == "2")
{
var remainingTeam = await Context.Teams
.Include(t => t.Event)
.FirstOrDefaultAsync(t => t.Event.Id == team.Event.Id && t.Id != team.Id);
if (remainingTeam != null)
{
remainingTeam.Identifier = null;
Context.Teams.Update(remainingTeam);
}
}
Context.Teams.Remove(team!); Context.Teams.Remove(team!);
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
Snackbar.Add($"Delete event: Delete of Team {team}", Severity.Info); Snackbar.Add($"Delete event: Delete of Team {team}", Severity.Info);
@@ -0,0 +1,48 @@
@if (Title != null)
{
<MudText Typo="Typo.h4">@Title</MudText>
}
<MudToggleGroup T="Student"
SelectionMode="SelectionMode.MultiSelection"
Values="@SelectedStudents"
ValuesChanged="@OnSelectedStudentsChanged"
Vertical="true"
CheckMark>
@foreach (var student in Students.OrderBy(e => e.FirstName))
{
<MudToggleItem Value="@student" Style="font-size: .75rem;">
@if (ShowFullName)
{
@student.FirstNameLastName
}
else
{
@student.FirstName
}
</MudToggleItem>
}
</MudToggleGroup>
@code {
[Parameter]
public IEnumerable<Student> Students { get; set; } = [];
[Parameter]
public IEnumerable<Student> SelectedStudents { get; set; } = [];
[Parameter]
public EventCallback<IEnumerable<Student>> SelectedStudentsChanged { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public bool ShowFullName { get; set; } = true;
private async Task OnSelectedStudentsChanged(IEnumerable<Student> value)
{
SelectedStudents = value;
await SelectedStudentsChanged.InvokeAsync(value);
}
}
@@ -0,0 +1,38 @@
@if (Title != null)
{
<MudText Typo="@TitleTypo">@Title</MudText>
}
<MudToggleGroup T="Student"
SelectionMode="SelectionMode.ToggleSelection"
Value="@SelectedCaptain"
ValueChanged="@OnSelectedCaptainChanged"
CheckMark>
@foreach (var student in Students.OrderBy(e => e.FirstName))
{
<MudToggleItem Value="@student" Text="@student.Name" />
}
</MudToggleGroup>
@code {
[Parameter]
public IEnumerable<Student> Students { get; set; } = [];
[Parameter]
public Student? SelectedCaptain { get; set; }
[Parameter]
public EventCallback<Student?> SelectedCaptainChanged { get; set; }
[Parameter]
public string? Title { get; set; } = "Captain";
[Parameter]
public Typo TitleTypo { get; set; } = Typo.body1;
private async Task OnSelectedCaptainChanged(Student? value)
{
SelectedCaptain = value;
await SelectedCaptainChanged.InvokeAsync(value);
}
}