Add a student/team list used for registration
This commit is contained in:
@@ -12,11 +12,11 @@
|
||||
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Create" Href="students/create">Create New</MudButton>
|
||||
<MudButton StartIcon="@AppIcons.EventRank" Href="students/event-ranking">Event Rankings</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.AppRegistration" Href="students/teams">Registration</MudButton>
|
||||
|
||||
<MudDataGrid T="Student" ServerData="ServerReload" @ref="_dataGrid" Filterable="true" RowsPerPage="25">
|
||||
<Columns>
|
||||
@* <PropertyColumn Property="@(e => e.Name)" Title="First Name" SortBy="e => e.FirstName" /> *@
|
||||
<TemplateColumn Title="Name" SortBy="e => e.LastName" Sortable="true" >
|
||||
<PropertyColumn Property="@(e => e.LastName)" Title="Name" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.LastNameFirstName
|
||||
@if (context.Item.OfficerRole != null)
|
||||
@@ -24,12 +24,12 @@
|
||||
<MudChip T="string" Icon="@(AppIcons.OfficerRoleIcon(context.Item.OfficerRole.Value))">@context.Item.OfficerRole</MudChip>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn Title="Grade (TSA Year)" SortBy="e => e.Grade" Sortable="true">
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="@(e => e.Grade)" Title="Grade (TSA Year)" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Grade (@context.Item.TsaYear)
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</PropertyColumn>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
<CrudActions DetailsHref="@($"/students/details?id={context.Item!.Id}")"
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
@page "/students/teams"
|
||||
@attribute [Authorize]
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Models
|
||||
@inject AppDbContext Context
|
||||
|
||||
<PageTitle>Registration - TSA Chapter Organizer</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h3">Registration</MudText>
|
||||
|
||||
<MudButtonGroup>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.FilterAlt"
|
||||
Variant="@(_showRegionalOnly ? Variant.Filled : Variant.Outlined)"
|
||||
Color="Color.Primary"
|
||||
OnClick="ToggleRegionalFilter">
|
||||
@(_showRegionalOnly ? "Showing Regional Only" : "Show Regional Only")
|
||||
</MudButton>
|
||||
</MudButtonGroup>
|
||||
|
||||
<MudText Typo="Typo.body2" Style="margin-top: 8px; margin-bottom: 4px;">
|
||||
<MudIcon Icon="@AppIcons.Captain" Size="Size.Small" /> = Team Captain
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.body2" Style="margin-bottom: 8px;">
|
||||
<strong>Show Columns:</strong>
|
||||
<MudCheckBox Value="_showGrade" ValueChanged="(bool val) => { _showGrade = val; _gridKey++; StateHasChanged(); }" Label="Grade" Dense="true" Size="Size.Small" />
|
||||
<MudCheckBox Value="_showRegionalId" ValueChanged="(bool val) => { _showRegionalId = val; _gridKey++; StateHasChanged(); }" Label="Regional ID" Dense="true" Size="Size.Small" />
|
||||
<MudCheckBox Value="_showStateId" ValueChanged="(bool val) => { _showStateId = val; _gridKey++; StateHasChanged(); }" Label="State ID" Dense="true" Size="Size.Small" />
|
||||
<MudCheckBox Value="_showNationalId" ValueChanged="(bool val) => { _showNationalId = val; _gridKey++; StateHasChanged(); }" Label="National ID" Dense="true" Size="Size.Small" />
|
||||
</MudText>
|
||||
|
||||
<MudDataGrid T="StudentTeamInfo" ServerData="ServerReload" @ref="_dataGrid" @key="_gridKey" Filterable="true" RowsPerPage="35">
|
||||
<Columns>
|
||||
<PropertyColumn Property="@(e => e.Student.LastName)" Title="Student" Sortable="true">
|
||||
<CellTemplate>
|
||||
@context.Item.Student.LastNameFirstName
|
||||
</CellTemplate>
|
||||
</PropertyColumn>
|
||||
@if (_showGrade)
|
||||
{
|
||||
<PropertyColumn Property="@(e => e.Student.Grade)" Title="Grade" Sortable="true" />
|
||||
}
|
||||
@if (_showRegionalId)
|
||||
{
|
||||
<PropertyColumn Property="@(e => e.Student.RegionalId)" Title="Regional ID" Sortable="true" />
|
||||
}
|
||||
@if (_showStateId)
|
||||
{
|
||||
<PropertyColumn Property="@(e => e.Student.StateId)" Title="State ID" Sortable="true" />
|
||||
}
|
||||
@if (_showNationalId)
|
||||
{
|
||||
<PropertyColumn Property="@(e => e.Student.NationalId)" Title="National ID" Sortable="true" />
|
||||
}
|
||||
<TemplateColumn Title="Events">
|
||||
<CellTemplate>
|
||||
@if (context.Item.Teams != null)
|
||||
{
|
||||
var teamsToDisplay = _showRegionalOnly
|
||||
? context.Item.Teams.Where(t => t?.Event != null && t.Event.RegionalEvent).OrderBy(t => t.Event.Name)
|
||||
: context.Item.Teams.Where(t => t?.Event != null).OrderBy(t => t.Event.Name);
|
||||
|
||||
foreach (var team in teamsToDisplay)
|
||||
{
|
||||
var isCaptain = team.Captain != null && team.Captain.Equals(context.Item.Student);
|
||||
var teamMembers = string.Join(", ", team.Students.Select(s => s.FirstName));
|
||||
|
||||
<MudTooltip Text="@teamMembers">
|
||||
<MudChip Size="Size.Small"
|
||||
Color="Color.Default"
|
||||
Href="@($"/teams/edit?id={team.Id}")"
|
||||
Style="cursor: pointer;">
|
||||
@team
|
||||
@if (isCaptain)
|
||||
{
|
||||
<MudIcon Icon="@AppIcons.Captain" Size="Size.Small" Style="margin-left: 4px;" />
|
||||
}
|
||||
</MudChip>
|
||||
</MudTooltip>
|
||||
}
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="StudentTeamInfo" PageSizeOptions="new int[] { 10, 25, 35, 50, 100 }"></MudDataGridPager>
|
||||
</PagerContent>
|
||||
</MudDataGrid>
|
||||
|
||||
@code {
|
||||
MudDataGrid<StudentTeamInfo> _dataGrid = null!;
|
||||
private bool _showRegionalOnly;
|
||||
private bool _showGrade;
|
||||
private bool _showRegionalId;
|
||||
private bool _showStateId;
|
||||
private bool _showNationalId;
|
||||
|
||||
// TODO: Remove this workaround once MudBlazor fixes dynamic column ordering
|
||||
// https://dev.to/the_real_slim_janey/get-in-line-customizing-column-order-in-mudblazor-3ail
|
||||
// To remove:
|
||||
// 1. Delete this _gridKey field
|
||||
// 2. Remove "_gridKey++;" from all checkbox ValueChanged handlers (lines 26-29)
|
||||
// 3. Remove "@key="_gridKey"" attribute from MudDataGrid (line 32)
|
||||
private int _gridKey = 0;
|
||||
|
||||
private async Task ToggleRegionalFilter()
|
||||
{
|
||||
_showRegionalOnly = !_showRegionalOnly;
|
||||
StateHasChanged();
|
||||
await Task.Delay(10);
|
||||
await _dataGrid.ReloadServerData();
|
||||
}
|
||||
|
||||
private async Task<GridData<StudentTeamInfo>> ServerReload(GridState<StudentTeamInfo> state)
|
||||
{
|
||||
// Load all students with their teams
|
||||
var students = await Context.Students
|
||||
.Include(s => s.Teams)
|
||||
.ThenInclude(t => t.Event)
|
||||
.Include(s => s.Teams)
|
||||
.ThenInclude(t => t.Captain)
|
||||
.ToListAsync();
|
||||
|
||||
// Filter to only students with teams
|
||||
var studentTeams = students
|
||||
.Where(s => s.Teams.Any(t => t?.Event != null && (!_showRegionalOnly || t.Event.RegionalEvent)))
|
||||
.Select(s => new StudentTeamInfo
|
||||
{
|
||||
Student = s,
|
||||
Teams = s.Teams?.Where(t => t?.Event != null).ToList() ?? []
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Apply sorting
|
||||
var sortedData = ApplySorting(studentTeams, state.SortDefinitions);
|
||||
|
||||
var totalItems = sortedData.Count();
|
||||
var pagedData = sortedData.Skip(state.Page * state.PageSize).Take(state.PageSize).ToArray();
|
||||
|
||||
return new GridData<StudentTeamInfo>
|
||||
{
|
||||
TotalItems = totalItems,
|
||||
Items = pagedData
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary mapping column property names to their corresponding sort expressions.
|
||||
/// Used to provide type-safe sorting for data grid columns.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each entry maps a property path (e.g., "Student.LastName") to a function that
|
||||
/// extracts the comparable value from a StudentTeamInfo instance.
|
||||
/// Nullable strings are coalesced to empty strings to ensure consistent sorting behavior.
|
||||
/// </remarks>
|
||||
private static readonly Dictionary<string, Func<StudentTeamInfo, IComparable>> SortExpressions = new()
|
||||
{
|
||||
{ "Student.LastName", s => s.Student.LastName },
|
||||
{ "Student.Grade", s => s.Student.Grade },
|
||||
{ "Student.RegionalId", s => s.Student.RegionalId ?? string.Empty },
|
||||
{ "Student.StateId", s => s.Student.StateId ?? string.Empty },
|
||||
{ "Student.NationalId", s => s.Student.NationalId ?? string.Empty }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Applies sorting to the student team data based on the provided sort definitions.
|
||||
/// </summary>
|
||||
/// <param name="data">The list of StudentTeamInfo records to sort.</param>
|
||||
/// <param name="sortDefinitions">Collection of sort definitions from the MudDataGrid state.</param>
|
||||
/// <returns>
|
||||
/// An IEnumerable of StudentTeamInfo sorted according to the first sort definition,
|
||||
/// or sorted by LastName if no valid sort definition is provided.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If no sort definitions are provided, or if the requested property is not found in SortExpressions,
|
||||
/// the data will default to sorting by Student.LastName in ascending order.
|
||||
/// Only the first sort definition is applied (multi-column sorting is not supported).
|
||||
/// </remarks>
|
||||
private IEnumerable<StudentTeamInfo> ApplySorting(
|
||||
List<StudentTeamInfo> data,
|
||||
ICollection<SortDefinition<StudentTeamInfo>> sortDefinitions)
|
||||
{
|
||||
if (!sortDefinitions.Any())
|
||||
{
|
||||
return data.OrderBy(s => s.Student.LastName);
|
||||
}
|
||||
|
||||
var sortDef = sortDefinitions.First();
|
||||
var propertyName = sortDef.SortBy;
|
||||
|
||||
if (!SortExpressions.TryGetValue(propertyName, out var sortExpression))
|
||||
{
|
||||
return data.OrderBy(s => s.Student.LastName);
|
||||
}
|
||||
|
||||
return sortDef.Descending
|
||||
? data.OrderByDescending(sortExpression)
|
||||
: data.OrderBy(sortExpression);
|
||||
}
|
||||
|
||||
public class StudentTeamInfo
|
||||
{
|
||||
public required Student Student { get; init; }
|
||||
public List<Team> Teams { get; init; } = [];
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
<MudNavGroup Title="Teams" Icon="@Icons.Material.Outlined.Groups" Expanded="true">
|
||||
<MudNavLink Href="/teams" Match="NavLinkMatch.All" Icon="@AppIcons.Teams">Teams</MudNavLink>
|
||||
<MudNavLink Href="/students/teams" Icon="@Icons.Material.Filled.AppRegistration">Registration</MudNavLink>
|
||||
<MudNavLink Href="/teams/printout" Icon="@Icons.Material.Filled.Print">Print out</MudNavLink>
|
||||
<MudNavLink Href="/teams/handout" Icon="@Icons.Material.Filled.Print">Handout</MudNavLink>
|
||||
</MudNavGroup>
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace WebApp.Models
|
||||
public static string TeamAssignment = Icons.Material.Filled.GroupAdd;
|
||||
public static string Events = Icons.Material.Filled.Dashboard;
|
||||
public static string Scheduler = Icons.Material.Filled.CalendarMonth;
|
||||
public static string Captain = Icons.Material.Filled.Star;
|
||||
public static string LevelOfEffortIcon(int? loe)
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user