From e53403c934bfc8f2cb0ee5a5be03bf2934a54e15 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Sun, 11 Jan 2026 09:54:32 -0500 Subject: [PATCH] Refactor UI components to utilize MudBlazor's layout system This commit updates several components to replace traditional HTML layout elements with MudBlazor's MudStack component for improved styling and responsiveness. Changes include the CareerMapping.razor, Index.razor, Registration.razor, TeamStudents.razor, and TeamToggleSelector.razor files. These modifications enhance the visual consistency and maintainability of the UI by adhering to the project's design standards. --- .../Features/Events/CareerMapping.razor | 9 +- .../Components/Features/Students/Index.razor | 4 +- .../Features/Students/Registration.razor | 4 +- .../Teams/Components/TeamStudents.razor | 4 +- .../Teams/Components/TeamToggleSelector.razor | 4 +- WebApp/Components/Features/Teams/Index.razor | 138 ++++++++++++++---- 6 files changed, 124 insertions(+), 39 deletions(-) diff --git a/WebApp/Components/Features/Events/CareerMapping.razor b/WebApp/Components/Features/Events/CareerMapping.razor index 3f7ebdc..4bd4e7b 100644 --- a/WebApp/Components/Features/Events/CareerMapping.razor +++ b/WebApp/Components/Features/Events/CareerMapping.razor @@ -37,7 +37,11 @@ else Events are shown in blue, career fields in green. Career fields are clusters of related careers. Click on a node to see details. Use mouse to zoom and pan the graph. - + +
+ +
+ @if (_selectedNodeInfo != null) { @@ -61,9 +65,6 @@ else } -
- -
} diff --git a/WebApp/Components/Features/Students/Index.razor b/WebApp/Components/Features/Students/Index.razor index 47fbb65..b840e37 100644 --- a/WebApp/Components/Features/Students/Index.razor +++ b/WebApp/Components/Features/Students/Index.razor @@ -30,7 +30,7 @@ -
+ @@ -40,7 +40,7 @@ { @context.Item.OfficerRole } -
+
t?.Event is { RegionalEvent: true }).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); @@ -98,7 +98,7 @@ } -
+
diff --git a/WebApp/Components/Features/Teams/Components/TeamStudents.razor b/WebApp/Components/Features/Teams/Components/TeamStudents.razor index 58cba4a..51c9f0f 100644 --- a/WebApp/Components/Features/Teams/Components/TeamStudents.razor +++ b/WebApp/Components/Features/Teams/Components/TeamStudents.razor @@ -1,5 +1,5 @@ @using WebApp.Models -
+ @foreach (var student in Team.Students .OrderBy(e => @@ -31,7 +31,7 @@ } -
+ @code { [Parameter] diff --git a/WebApp/Components/Features/Teams/Components/TeamToggleSelector.razor b/WebApp/Components/Features/Teams/Components/TeamToggleSelector.razor index 32f3709..443ff2b 100644 --- a/WebApp/Components/Features/Teams/Components/TeamToggleSelector.razor +++ b/WebApp/Components/Features/Teams/Components/TeamToggleSelector.razor @@ -15,13 +15,13 @@ { -
+ @team.ToString() @if (ShowEventAttributes) { } -
+
} diff --git a/WebApp/Components/Features/Teams/Index.razor b/WebApp/Components/Features/Teams/Index.razor index 04a3d59..a13fb84 100644 --- a/WebApp/Components/Features/Teams/Index.razor +++ b/WebApp/Components/Features/Teams/Index.razor @@ -3,6 +3,7 @@ @using WebApp.Models @page "/teams" @attribute [Authorize] +@implements IAsyncDisposable @inject AppDbContext Context @inject IDialogService DialogService @inject ISnackbar Snackbar @@ -74,30 +75,58 @@ MudDataGrid _dataGrid = null!; private bool _isLoading = true; private bool _showRegionalOnly = false; + private CancellationTokenSource? _cancellationTokenSource; + private bool _isDisposed = false; + + protected override void OnInitialized() + { + _cancellationTokenSource = new CancellationTokenSource(); + } private async Task ToggleRegionalFilter() { + if (_isDisposed) return; + try { _showRegionalOnly = !_showRegionalOnly; - if (_dataGrid != null) + if (_dataGrid != null && !_isDisposed) { await _dataGrid.ReloadServerData(); } } + catch (TaskCanceledException) + { + // Component was disposed, ignore + } + catch (JSDisconnectedException) + { + // JS connection lost, ignore + } catch (Exception ex) { - Snackbar.Add($"Error applying filter: {ex.Message}", Severity.Error); + if (!_isDisposed) + { + Snackbar.Add($"Error applying filter: {ex.Message}", Severity.Error); + } } } private async Task> ServerReload(GridState state) { + if (_isDisposed) + { + return new GridData { TotalItems = 0, Items = [] }; + } + _isLoading = true; try { + var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None; + IQueryable query = Context.Teams + .AsNoTracking() .Include(e => e.Event) .Include(e => e.Students) .ThenInclude(e => e.EventRankings); @@ -113,7 +142,7 @@ query = query.Where(state.FilterDefinitions); // Load all data first - var allTeams = await query.ToArrayAsync(); + var allTeams = await query.ToArrayAsync(cancellationToken); // Always sort by EventFormat FIRST to separate group/individual teams // Sort in memory to ensure this ordering is maintained regardless of user sorts @@ -173,45 +202,100 @@ Items = pagedData }; } + catch (TaskCanceledException) + { + // Component was disposed, return empty result + return new GridData { TotalItems = 0, Items = [] }; + } + catch (JSDisconnectedException) + { + // JS connection lost, return empty result + return new GridData { TotalItems = 0, Items = [] }; + } finally { - _isLoading = false; + if (!_isDisposed) + { + _isLoading = false; + } } } private async Task DeleteTeam(Team team) { - //_isRowBlocked = true; + if (_isDisposed) return; - var result = await DialogService - .ShowMessageBox("Delete team", - (MarkupString)$"Are you sure want to delete {team}? This cannot be undone.", - yesText: "Yes", - noText: "Cancel"); - - if (result == true) + try { - // 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); + var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None; - if (remainingTeam != null) + var result = await DialogService + .ShowMessageBox("Delete team", + (MarkupString)$"Are you sure want to delete {team}? This cannot be undone.", + yesText: "Yes", + noText: "Cancel"); + + if (_isDisposed) return; + + 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") { - remainingTeam.Identifier = null; - Context.Teams.Update(remainingTeam); + var remainingTeam = await Context.Teams + .Include(t => t.Event) + .FirstOrDefaultAsync(t => t.Event.Id == team.Event.Id && t.Id != team.Id, cancellationToken); + + if (_isDisposed) return; + + if (remainingTeam != null) + { + remainingTeam.Identifier = null; + Context.Teams.Update(remainingTeam); + } + } + + Context.Teams.Remove(team!); + await Context.SaveChangesAsync(cancellationToken); + + if (!_isDisposed) + { + Snackbar.Add($"Team {team} deleted", Severity.Info); } } - Context.Teams.Remove(team!); - await Context.SaveChangesAsync(); - Snackbar.Add($"Delete event: Delete of Team {team}", Severity.Info); + if (!_isDisposed) + { + StateHasChanged(); + await _dataGrid.ReloadServerData(); + } } + catch (TaskCanceledException) + { + // Component was disposed, ignore + } + catch (JSDisconnectedException) + { + // JS connection lost, ignore + } + catch (Exception ex) + { + if (!_isDisposed) + { + Snackbar.Add($"Error deleting team: {ex.Message}", Severity.Error); + } + } + } - //_isRowBlocked = false; - StateHasChanged(); - await _dataGrid.ReloadServerData(); + public async ValueTask DisposeAsync() + { + if (!_isDisposed) + { + _isDisposed = true; + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + _cancellationTokenSource = null; + } + await ValueTask.CompletedTask; } }