8af86e22d9
This commit updates various files across the Core and WebApp projects to replace traditional collection initializers with C# 12 collection expressions. Changes include modifications to EventAssignment.cs, TeamScheduler_DecisionTree.cs, CareerField.cs, EventDefinition.cs, and several components in the WebApp. These updates enhance code readability and maintainability by adhering to modern C# syntax standards.
284 lines
10 KiB
Plaintext
284 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);
|
|
|
|
List<CalendarEventItem> items = [];
|
|
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 = occ.EventDefinition != null
|
|
? 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 = [];
|
|
}
|
|
finally
|
|
{
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private static List<string> StudentFirstNames(EventDefinition ed, Dictionary<int, List<Team>> teamsByEventId)
|
|
{
|
|
List<string> studentFirstNames = [];
|
|
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)
|
|
{
|
|
List<string> parts = [];
|
|
|
|
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);
|
|
}
|
|
}
|
|
|