46836fde2e
This commit updates the Calendar component by changing the default start time for meetings to 9:00 AM and the end time to 5:01 PM, ensuring better alignment with typical work hours. Additionally, the styling of calendar event items is refined by removing fixed width and height properties, allowing for more flexible rendering. These changes enhance the usability and visual presentation of the calendar feature, contributing to an improved user experience.
340 lines
12 KiB
Plaintext
340 lines
12 KiB
Plaintext
@page "/calendar"
|
|
@attribute [Authorize]
|
|
@using WebApp.Models
|
|
@using WebApp.Services
|
|
@using Heron.MudCalendar
|
|
@using Microsoft.Extensions.Logging
|
|
@using WebApp.Authentication
|
|
@using Core.Utility
|
|
@inject IEventOccurrenceService EventOccurrenceService
|
|
@inject ITeamMeetingHistoryService TeamMeetingHistoryService
|
|
@inject ILogger<Index> Logger
|
|
@inject IDialogService DialogService
|
|
|
|
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" Icon="@AppIcons.EventCalendar">
|
|
<ActionButtons>
|
|
<MudTooltip Text="Import">
|
|
<MudButton StartIcon="@Icons.Material.Filled.ImportExport" Href="calendar/event-occurrences/import" Variant="Variant.Filled" Color="Color.Primary">Import</MudButton>
|
|
</MudTooltip>
|
|
<AuthorizeView Roles="@AuthRoles.Administrator">
|
|
<MudTooltip Text="Admin">
|
|
<MudButton StartIcon="@Icons.Material.Filled.AdminPanelSettings" Href="calendar/admin" Variant="Variant.Outlined" Color="Color.Default">Admin</MudButton>
|
|
</MudTooltip>
|
|
</AuthorizeView>
|
|
<PageNoteButton PageIdentifier="Event Calendar" />
|
|
</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">
|
|
|
|
<MudCalendar T="CalendarItemWrapper"
|
|
Items="_calendarItems"
|
|
@bind-View="_currentView"
|
|
@bind-CurrentDay="_calendarDate"
|
|
Class="event-calendar"
|
|
ItemClicked="OnItemClicked">
|
|
<MonthTemplate>
|
|
<MudTooltip Text="@GetEventTooltip(context)">
|
|
<div class="calendar-event-item">
|
|
@context.Text
|
|
</div>
|
|
</MudTooltip>
|
|
</MonthTemplate>
|
|
<WeekTemplate>
|
|
<MudTooltip Text="@GetEventTooltip(context)">
|
|
<div class="calendar-event-item">
|
|
@context.Text
|
|
</div>
|
|
</MudTooltip>
|
|
</WeekTemplate>
|
|
<DayTemplate>
|
|
<MudTooltip Text="@GetEventTooltip(context)">
|
|
<div class="calendar-event-item">
|
|
@context.Text
|
|
</div>
|
|
</MudTooltip>
|
|
</DayTemplate>
|
|
</MudCalendar>
|
|
</MudStack>
|
|
}
|
|
</MudPaper>
|
|
|
|
@code {
|
|
private List<CalendarItemWrapper>? _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<CalendarItemWrapper> items = [];
|
|
|
|
// Add event occurrences
|
|
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 && teamsByEventId.TryGetValue(occ.EventDefinition.Id, out var teams)
|
|
? TeamStudentNameFormatter.FormatStudentListForEvent(
|
|
occ.EventDefinition,
|
|
teams,
|
|
new TeamStudentNameFormatter.FormatOptions
|
|
{
|
|
CaptainIndicator = TeamStudentNameFormatter.CaptainIndicatorStyle.Star,
|
|
Ordering = TeamStudentNameFormatter.OrderingStyle.Alphabetical
|
|
})
|
|
: [];
|
|
|
|
var calendarEventItem = new CalendarEventItem(occ, studentFirstNames);
|
|
items.Add(new CalendarItemWrapper(calendarEventItem));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error creating CalendarEventItem for occurrence Id={Id}, Name={Name}",
|
|
occ?.Id, occ?.Name);
|
|
// Continue processing other items
|
|
}
|
|
}
|
|
|
|
// Load and add meeting histories
|
|
try
|
|
{
|
|
Logger.LogInformation("Loading meeting histories");
|
|
var meetingHistories = await TeamMeetingHistoryService.GetMeetingHistoriesAsync();
|
|
|
|
foreach (var meetingHistory in meetingHistories)
|
|
{
|
|
try
|
|
{
|
|
var calendarMeetingItem = new CalendarMeetingItem(meetingHistory);
|
|
items.Add(new CalendarItemWrapper(calendarMeetingItem));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error creating CalendarMeetingItem for meeting history Id={Id}, Date={Date}",
|
|
meetingHistory?.Id, meetingHistory?.MeetingDate);
|
|
// Continue processing other items
|
|
}
|
|
}
|
|
|
|
Logger.LogInformation("Added {Count} meeting histories to calendar", meetingHistories.Count());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error loading meeting histories");
|
|
// Continue - don't fail the entire calendar load if meetings fail
|
|
}
|
|
|
|
_calendarItems = items;
|
|
Logger.LogInformation("Created {Count} calendar items from {OccurrenceCount} occurrences and meetings",
|
|
_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 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 nextItem = _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 (nextItem != null)
|
|
{
|
|
return nextItem.Start.Date;
|
|
}
|
|
|
|
// Fallback to first item if no future items
|
|
var firstItem = _calendarItems
|
|
.OrderBy(item => item.Start)
|
|
.FirstOrDefault();
|
|
|
|
return firstItem != null ? firstItem.Start.Date : DateTime.Today;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError(ex, "Error in GetNextDateWithEvents");
|
|
return DateTime.Today;
|
|
}
|
|
}
|
|
|
|
private string GetEventTooltip(CalendarItemWrapper wrapper)
|
|
{
|
|
if (wrapper.ItemType == CalendarItemType.Event && wrapper.EventItem != null)
|
|
{
|
|
return GetEventTooltip(wrapper.EventItem);
|
|
}
|
|
else if (wrapper.ItemType == CalendarItemType.Meeting && wrapper.MeetingItem != null)
|
|
{
|
|
return GetMeetingTooltip(wrapper.MeetingItem);
|
|
}
|
|
return wrapper.Text;
|
|
}
|
|
|
|
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 string GetMeetingTooltip(CalendarMeetingItem item)
|
|
{
|
|
if (item.MeetingHistoryData == null)
|
|
return "Team Meeting";
|
|
|
|
return $"Team Meeting\nDate: {item.MeetingHistoryData.MeetingDate:g}";
|
|
}
|
|
|
|
private async Task OnItemClicked(CalendarItemWrapper wrapper)
|
|
{
|
|
if (_calendarItems == null)
|
|
return;
|
|
|
|
if (wrapper.ItemType == CalendarItemType.Event && wrapper.EventItem != null)
|
|
{
|
|
await ShowEventDetails(wrapper.EventItem);
|
|
}
|
|
else if (wrapper.ItemType == CalendarItemType.Meeting && wrapper.MeetingItem != null)
|
|
{
|
|
await ShowMeetingDetails(wrapper.MeetingItem);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private async Task ShowMeetingDetails(CalendarMeetingItem item)
|
|
{
|
|
if (item.MeetingHistoryData == null) return;
|
|
|
|
var parameters = new DialogParameters
|
|
{
|
|
["MeetingHistoryId"] = item.MeetingHistoryData.Id
|
|
};
|
|
|
|
var options = new DialogOptions
|
|
{
|
|
CloseOnEscapeKey = true,
|
|
CloseButton = true,
|
|
MaxWidth = MaxWidth.Large,
|
|
FullWidth = true
|
|
};
|
|
|
|
await DialogService.ShowAsync<MeetingHistoryDetailDialog>("Meeting Details", parameters, options);
|
|
}
|
|
}
|
|
|