ecd6173a44
This commit updates the EventOccurrenceParseResult and EventOccurrenceParserResult classes to consolidate the handling of skipped section headers and event counts into a single set of properties. The previous separate lists and counts for middle school and high school sections have been replaced with a unified approach, improving clarity and maintainability. Additionally, the EventOccurrenceParserService has been modified to reflect these changes, ensuring consistent behavior across the application. This refactor enhances the overall structure of the event parsing logic.
282 lines
10 KiB
Plaintext
282 lines
10 KiB
Plaintext
@page "/calendar"
|
|
@attribute [Authorize]
|
|
@using WebApp.Models
|
|
@using WebApp.Services
|
|
@using Heron.MudCalendar
|
|
@using Microsoft.Extensions.Logging
|
|
@using WebApp.Authentication
|
|
@inject IEventOccurrenceService EventOccurrenceService
|
|
@inject ILogger<Index> Logger
|
|
@inject IDialogService DialogService
|
|
|
|
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" Icon="@AppIcons.EventCalendar">
|
|
<ActionButtons>
|
|
<MudButton StartIcon="@Icons.Material.Filled.ImportExport" Href="calendar/event-occurrences/import" Variant="Variant.Filled" Color="Color.Primary">Import</MudButton>
|
|
<AuthorizeView Roles="@AuthRoles.Administrator">
|
|
<MudButton StartIcon="@Icons.Material.Filled.AdminPanelSettings" Href="calendar/admin" Variant="Variant.Outlined" Color="Color.Default">Admin</MudButton>
|
|
</AuthorizeView>
|
|
</ActionButtons>
|
|
</PageHeader>
|
|
|
|
<MudPaper Elevation="2" Class="pa-3 pa-md-6">
|
|
@if (_calendarItems == null)
|
|
{
|
|
<MudProgressLinear Indeterminate="true" />
|
|
<MudText>Loading calendar events...</MudText>
|
|
}
|
|
else
|
|
{
|
|
<MudStack Spacing="2">
|
|
<MudButtonGroup Variant="Variant.Outlined" Size="Size.Small">
|
|
<MudButton Color="@(_currentView == CalendarView.Month ? Color.Primary : Color.Default)"
|
|
OnClick="() => { _currentView = CalendarView.Month; StateHasChanged(); }">
|
|
Month
|
|
</MudButton>
|
|
<MudButton Color="@(_currentView == CalendarView.Day ? Color.Primary : Color.Default)"
|
|
OnClick="() => { _currentView = CalendarView.Day; StateHasChanged(); }">
|
|
Day
|
|
</MudButton>
|
|
</MudButtonGroup>
|
|
|
|
<MudCalendar T="CalendarEventItem"
|
|
Items="_calendarItems"
|
|
View="_currentView"
|
|
CurrentDay="@_calendarDate"
|
|
Class="event-calendar"
|
|
ItemClicked="OnItemClicked">
|
|
<MonthTemplate>
|
|
@* <MudTooltip Text="@GetEventTooltip(context)"> *@
|
|
<div class="d-flex gap-1">
|
|
<MudIcon Icon="@Icons.Material.Filled.Circle" Color="Color.Secondary" Size="Size.Small"/>
|
|
<div>@context.Text</div>
|
|
</div>
|
|
@* </MudTooltip> *@
|
|
</MonthTemplate>
|
|
<WeekTemplate>
|
|
@* <MudTooltip Text="@GetEventTooltip(context)"> *@
|
|
<div style="width: 100%; height: 100%;">
|
|
@context.Text
|
|
</div>
|
|
@* </MudTooltip> *@
|
|
</WeekTemplate>
|
|
<DayTemplate>
|
|
@* <MudTooltip Text="@GetEventTooltip(context)"> *@
|
|
<div>@context.Text</div>
|
|
@* </MudTooltip> *@
|
|
</DayTemplate>
|
|
</MudCalendar>
|
|
</MudStack>
|
|
}
|
|
</MudPaper>
|
|
|
|
@code {
|
|
private List<CalendarEventItem>? _calendarItems;
|
|
private DateTime _calendarDate = DateTime.Today;
|
|
private CalendarView _currentView = CalendarView.Month;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadCalendarEvents();
|
|
}
|
|
|
|
private async Task LoadCalendarEvents()
|
|
{
|
|
try
|
|
{
|
|
Logger.LogInformation("Loading calendar events");
|
|
var occurrences = await EventOccurrenceService.GetEventOccurrencesAsync();
|
|
|
|
var eventOccurrences = occurrences as EventOccurrence[] ?? occurrences.ToArray();
|
|
Logger.LogDebug("Received {Count} occurrences from service", eventOccurrences.Count());
|
|
|
|
// Get all unique event definition IDs that have occurrences
|
|
var eventDefinitionIds = eventOccurrences
|
|
.Where(occ => occ?.EventDefinition?.Id != null)
|
|
.Select(occ => occ!.EventDefinition!.Id)
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
// Load teams for all event definitions
|
|
var teamsByEventId = await EventOccurrenceService.GetTeamsByEventDefinitionIdsAsync(eventDefinitionIds);
|
|
|
|
var items = new List<CalendarEventItem>();
|
|
foreach (var occ in eventOccurrences)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(occ.Name))
|
|
{
|
|
Logger.LogWarning("Occurrence with Id={Id} has null or empty Name", occ.Id);
|
|
}
|
|
|
|
// Get student first names for this event definition
|
|
var studentFirstNames = StudentFirstNames(occ.EventDefinition, teamsByEventId);
|
|
|
|
var calendarItem = new CalendarEventItem(occ, studentFirstNames);
|
|
items.Add(calendarItem);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error creating CalendarEventItem for occurrence Id={Id}, Name={Name}",
|
|
occ?.Id, occ?.Name);
|
|
// Continue processing other items
|
|
}
|
|
}
|
|
|
|
_calendarItems = items;
|
|
Logger.LogInformation("Created {Count} calendar items from {OccurrenceCount} occurrences",
|
|
_calendarItems.Count, eventOccurrences.Count());
|
|
|
|
// Find the next date with events
|
|
_calendarDate = GetNextDateWithEvents();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error loading calendar events");
|
|
_calendarItems = new List<CalendarEventItem>();
|
|
}
|
|
finally
|
|
{
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private static List<string> StudentFirstNames(EventDefinition ed, Dictionary<int, List<Team>> teamsByEventId)
|
|
{
|
|
var studentFirstNames = new List<string>();
|
|
if (ed?.Id == null || !teamsByEventId.TryGetValue(ed.Id, out var teams)) return studentFirstNames;
|
|
|
|
// Get all unique student first names from all teams for this event
|
|
// Include captain indicator (*) for team events
|
|
var allStudents = teams
|
|
.SelectMany(t => t.Students)
|
|
.DistinctBy(s => s.Id) // Ensure uniqueness by student ID
|
|
.Select(s =>
|
|
{
|
|
var isCaptain = teams.Any(t => t.Captain?.Id == s.Id);
|
|
var name = s.FirstName;
|
|
// Add star for captain in team events (EventFormat == Team)
|
|
if (isCaptain && ed.EventFormat == Core.Entities.EventFormat.Team)
|
|
{
|
|
name += "*";
|
|
}
|
|
return name;
|
|
})
|
|
.OrderBy(name => name)
|
|
.ToList();
|
|
|
|
studentFirstNames = allStudents;
|
|
|
|
return studentFirstNames;
|
|
}
|
|
|
|
private DateTime GetNextDateWithEvents()
|
|
{
|
|
try
|
|
{
|
|
if (_calendarItems == null || !_calendarItems.Any())
|
|
{
|
|
Logger.LogDebug("No calendar items available, returning today's date");
|
|
return DateTime.Today;
|
|
}
|
|
|
|
var today = DateTime.Today;
|
|
var nextEvent = _calendarItems
|
|
.Where(item =>
|
|
{
|
|
try
|
|
{
|
|
return item.Start.Date >= today;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogWarning(ex, "Error checking item date, skipping item");
|
|
return false;
|
|
}
|
|
})
|
|
.OrderBy(item => item.Start)
|
|
.FirstOrDefault();
|
|
|
|
if (nextEvent != null)
|
|
{
|
|
return nextEvent.Start.Date;
|
|
}
|
|
|
|
// Fallback to first event if no future events
|
|
var firstEvent = _calendarItems
|
|
.OrderBy(item => item.Start)
|
|
.FirstOrDefault();
|
|
|
|
return firstEvent != null ? firstEvent.Start.Date : DateTime.Today;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error in GetNextDateWithEvents");
|
|
return DateTime.Today;
|
|
}
|
|
}
|
|
|
|
private string GetEventTooltip(CalendarEventItem item)
|
|
{
|
|
var parts = new List<string>();
|
|
|
|
if (!string.IsNullOrEmpty(item.EventDefinition?.Name))
|
|
{
|
|
parts.Add($"Event: {item.EventDefinition.Name}");
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(item.EventOccurrenceData?.Name))
|
|
{
|
|
parts.Add($"Occurrence: {item.EventOccurrenceData.Name}");
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(item.EventOccurrenceData?.Location))
|
|
{
|
|
parts.Add($"Location: {item.EventOccurrenceData.Location}");
|
|
}
|
|
|
|
if (item.EventOccurrenceData?.StartTime != null)
|
|
{
|
|
parts.Add($"Time: {item.EventOccurrenceData.StartTime:g}");
|
|
}
|
|
|
|
if (item.StudentFirstNames.Any())
|
|
{
|
|
parts.Add($"Students: {string.Join(", ", item.StudentFirstNames)}");
|
|
}
|
|
|
|
return string.Join("\n", parts);
|
|
}
|
|
|
|
private async Task OnItemClicked(CalendarEventItem calendarEventItem)
|
|
{
|
|
if (_calendarItems == null)
|
|
return;
|
|
|
|
await ShowEventDetails(calendarEventItem);
|
|
}
|
|
|
|
private async Task ShowEventDetails(CalendarEventItem item)
|
|
{
|
|
if (item.EventOccurrenceData == null) return;
|
|
|
|
var parameters = new DialogParameters
|
|
{
|
|
["EventOccurrence"] = item.EventOccurrenceData,
|
|
["EventDefinition"] = item.EventDefinition,
|
|
["StudentFirstNames"] = item.StudentFirstNames
|
|
};
|
|
|
|
var options = new DialogOptions
|
|
{
|
|
CloseOnEscapeKey = true,
|
|
CloseButton = true,
|
|
MaxWidth = MaxWidth.Medium,
|
|
FullWidth = true
|
|
};
|
|
|
|
await DialogService.ShowAsync<EventOccurrenceDetailsDialog>("Event Details", parameters, options);
|
|
}
|
|
}
|
|
|