@page "/" @attribute [Authorize] @using Microsoft.EntityFrameworkCore @using WebApp.Models @using Core.Entities @using WebApp.Services @using WebApp.Components.Shared.Components @inject IConfiguration Configuration @inject AppDbContext Context @inject INotesService NotesService @inject IDialogService DialogService @implements IAsyncDisposable @Configuration["ChapterSettings:Name"] - TSA Chapter Organizer @Configuration["ChapterSettings:Name"] @Configuration["ChapterSettings:Name"] @Configuration["ChapterSettings:CompetitionYear"] Competition Year @if (_pinnedNotes.Any()) { @foreach (var note in _pinnedNotes) { } } @if (!_hasStudents) { Getting Started Add your chapter's students to begin Import or add your chapter roster } else if (!_hasTeams) { Team Building Collect event rankings and build your teams Chapter Data @if (!string.IsNullOrEmpty(_gradeDistribution) && _gradeDistribution != "No students yet") { @((MarkupString)_gradeDistribution) } } else { Scheduling Teams & Registration Team Building Chapter Data @if (!string.IsNullOrEmpty(_gradeDistribution) && _gradeDistribution != "No students yet") { @((MarkupString)_gradeDistribution) } } @code { private int _eventCount; private int _individualEventsCount; private int _teamEventsCount; private int _studentCount; private string _gradeDistribution = ""; private int _teamCount; private int _individualTeamsCount; private int _groupTeamsCount; private List _pinnedNotes = []; private CancellationTokenSource? _cancellationTokenSource; private bool _isDisposed = false; private bool _hasStudents => _studentCount > 0; private bool _hasTeams => _teamCount > 0; protected override void OnInitialized() { _cancellationTokenSource = new CancellationTokenSource(); } protected override async Task OnInitializedAsync() { await LoadStatistics(); await LoadPinnedNotes(); } private async Task HandleNoteChanged() { if (!_isDisposed) { await LoadPinnedNotes(); StateHasChanged(); } } private async Task LoadPinnedNotes() { if (_isDisposed) return; try { var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None; _pinnedNotes = (await NotesService.GetPinnedNotesAsync()).ToList(); } catch (TaskCanceledException) { // Component was disposed, ignore } catch (JSDisconnectedException) { // JS connection lost, ignore } catch (Exception) { // Error loading pinned notes - ignore } } private async Task LoadStatistics() { // Events statistics var events = await Context.Events.ToListAsync(); _eventCount = events.Count(); _individualEventsCount = events.Count(e => e.EventFormat == EventFormat.Individual); _teamEventsCount = events.Count(e => e.EventFormat == EventFormat.Team); // Students statistics _studentCount = await Context.Students.CountAsync(); // Grade distribution var gradeGroups = await Context.Students .GroupBy(s => s.Grade) .Select(g => new { Grade = g.Key, Count = g.Count() }) .OrderBy(g => g.Grade) .ToListAsync(); if (gradeGroups.Any()) { _gradeDistribution = string.Join(" | ", gradeGroups.Select(g => $"{AppIcons.GetOrdinalSuperscript(g.Grade)}: {g.Count}")); } else { _gradeDistribution = "No students yet"; } // Teams statistics var contextTeams = await Context.Teams.ToListAsync(); _teamCount = contextTeams.Count; _individualTeamsCount = contextTeams.Count(e => e.Event.EventFormat == EventFormat.Individual); _groupTeamsCount = contextTeams.Count(e => e.Event.EventFormat == EventFormat.Team); } public async ValueTask DisposeAsync() { if (!_isDisposed) { _isDisposed = true; _cancellationTokenSource?.Cancel(); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; } await ValueTask.CompletedTask; } }