Add Event Calendar feature with event occurrences service integration
Introduced a new Event Calendar component that displays scheduled events using the Heron.MudCalendar. Implemented IEventOccurrenceService to fetch event occurrences, and added mock data for initial testing. Updated navigation menu to include a link to the Event Calendar.
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
@page "/event-calendar"
|
||||||
|
@attribute [Authorize]
|
||||||
|
@using WebApp.Components.Shared.Components
|
||||||
|
@using WebApp.Models
|
||||||
|
@using WebApp.Services
|
||||||
|
@using Heron.MudCalendar
|
||||||
|
@inject IEventOccurrenceService EventOccurrenceService
|
||||||
|
|
||||||
|
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" />
|
||||||
|
|
||||||
|
<MudPaper Elevation="2" Class="pa-6">
|
||||||
|
@if (_calendarItems == null)
|
||||||
|
{
|
||||||
|
<MudProgressLinear Indeterminate="true" />
|
||||||
|
<MudText>Loading calendar events...</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudCalendar T="CalendarEventItem"
|
||||||
|
Items="_calendarItems"
|
||||||
|
View="CalendarView.Day"
|
||||||
|
CurrentDay=_calendarDate
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private List<CalendarEventItem>? _calendarItems;
|
||||||
|
private DateTime _calendarDate = DateTime.Today;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadCalendarEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadCalendarEvents()
|
||||||
|
{
|
||||||
|
var occurrences = await EventOccurrenceService.GetEventOccurrencesAsync();
|
||||||
|
_calendarItems = occurrences
|
||||||
|
.Select(occ => new CalendarEventItem(occ))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Find the next date with events
|
||||||
|
_calendarDate = GetNextDateWithEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime GetNextDateWithEvents()
|
||||||
|
{
|
||||||
|
if (_calendarItems == null || !_calendarItems.Any())
|
||||||
|
{
|
||||||
|
return DateTime.Today;
|
||||||
|
}
|
||||||
|
|
||||||
|
var today = DateTime.Today;
|
||||||
|
var nextEvent = _calendarItems
|
||||||
|
.Where(item => item.Start.Date >= today)
|
||||||
|
.OrderBy(item => item.Start)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
return nextEvent?.Start.Date ?? _calendarItems.OrderBy(item => item.Start).First().Start.Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
<MudNavLink Href="/teams/handout" Icon="@Icons.Material.Filled.Print">Handout</MudNavLink>
|
<MudNavLink Href="/teams/handout" Icon="@Icons.Material.Filled.Print">Handout</MudNavLink>
|
||||||
</MudNavGroup>
|
</MudNavGroup>
|
||||||
<MudNavLink Href="/meeting-schedule/" Icon="@AppIcons.Scheduler">Schedule</MudNavLink>
|
<MudNavLink Href="/meeting-schedule/" Icon="@AppIcons.Scheduler">Schedule</MudNavLink>
|
||||||
|
<MudNavLink Href="/event-calendar" Icon="@Icons.Material.Filled.CalendarMonth">Event Calendar</MudNavLink>
|
||||||
<MudNavGroup Title="Team Building" Icon="@Icons.Material.Filled.GroupAdd" Expanded="true">
|
<MudNavGroup Title="Team Building" Icon="@Icons.Material.Filled.GroupAdd" Expanded="true">
|
||||||
<MudNavLink Href="/students/event-ranking" Icon="@AppIcons.EventRank">Event Ranking</MudNavLink>
|
<MudNavLink Href="/students/event-ranking" Icon="@AppIcons.EventRank">Event Ranking</MudNavLink>
|
||||||
<MudNavLink Href="/teams/assignment" Icon="@AppIcons.TeamAssignment">Team Assignment</MudNavLink>
|
<MudNavLink Href="/teams/assignment" Icon="@AppIcons.TeamAssignment">Team Assignment</MudNavLink>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
@using WebApp.Components.Features.Events
|
@using WebApp.Components.Features.Events
|
||||||
@using WebApp.Components.Features.Events.Components
|
@using WebApp.Components.Features.Events.Components
|
||||||
@using WebApp.Components.Features.MeetingSchedule
|
@using WebApp.Components.Features.MeetingSchedule
|
||||||
|
@using WebApp.Components.Features.EventCalendar
|
||||||
@using MudBlazor
|
@using MudBlazor
|
||||||
@using Core.Entities
|
@using Core.Entities
|
||||||
@using Data
|
@using Data
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Heron.MudCalendar;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace WebApp.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calendar event item model for Heron.MudCalendar component.
|
||||||
|
/// Maps from EventOccurrence entity to calendar event format.
|
||||||
|
/// </summary>
|
||||||
|
public class CalendarEventItem : CalendarItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the original EventOccurrence data.
|
||||||
|
/// </summary>
|
||||||
|
public Core.Entities.EventOccurrence? EventOccurrenceData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the associated EventDefinition if available.
|
||||||
|
/// </summary>
|
||||||
|
public Core.Entities.EventDefinition? EventDefinition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameterless constructor required by Heron.MudCalendar component.
|
||||||
|
/// </summary>
|
||||||
|
public CalendarEventItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalendarEventItem(Core.Entities.EventOccurrence occurrence, Core.Entities.EventDefinition? eventDefinition = null)
|
||||||
|
{
|
||||||
|
// Set base class properties that the calendar component uses
|
||||||
|
Text = GetEventTitle(occurrence, eventDefinition);
|
||||||
|
Start = occurrence.StartTime;
|
||||||
|
End = occurrence.EndTime ?? occurrence.StartTime.AddHours(1);
|
||||||
|
this.EventOccurrenceData = occurrence;
|
||||||
|
this.EventDefinition = eventDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetEventTitle(Core.Entities.EventOccurrence occurrence, Core.Entities.EventDefinition? eventDefinition)
|
||||||
|
{
|
||||||
|
var title = occurrence.Name;
|
||||||
|
|
||||||
|
if (eventDefinition != null && !string.IsNullOrEmpty(eventDefinition.Name))
|
||||||
|
{
|
||||||
|
title = $"{eventDefinition.Name} - {title}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(occurrence.Location))
|
||||||
|
{
|
||||||
|
title += $" ({occurrence.Location})";
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -172,6 +172,7 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
|||||||
|
|
||||||
builder.Services.AddScoped<ClipboardService>();
|
builder.Services.AddScoped<ClipboardService>();
|
||||||
builder.Services.AddScoped<WebApp.LocalStorageService>();
|
builder.Services.AddScoped<WebApp.LocalStorageService>();
|
||||||
|
builder.Services.AddScoped<WebApp.Services.IEventOccurrenceService, WebApp.Services.EventOccurrenceService>();
|
||||||
|
|
||||||
// State container for maintaining state per user connection (Blazor Server)
|
// State container for maintaining state per user connection (Blazor Server)
|
||||||
builder.Services.AddScoped<StateContainer>();
|
builder.Services.AddScoped<StateContainer>();
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
|
||||||
|
namespace WebApp.Services;
|
||||||
|
|
||||||
|
public class EventOccurrenceService : IEventOccurrenceService
|
||||||
|
{
|
||||||
|
public Task<IEnumerable<EventOccurrence>> GetEventOccurrencesAsync()
|
||||||
|
{
|
||||||
|
// Mock data for demonstration purposes
|
||||||
|
// This will be replaced with database-backed implementation later
|
||||||
|
var mockOccurrences = new List<EventOccurrence>
|
||||||
|
{
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Opening Ceremony",
|
||||||
|
Date = "March 15",
|
||||||
|
Time = "8:00 a.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 15, 8, 0, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 15, 9, 0, 0),
|
||||||
|
Location = "Main Auditorium"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Sign-up",
|
||||||
|
Date = "March 15",
|
||||||
|
Time = "9:30 a.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 15, 9, 30, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 15, 11, 0, 0),
|
||||||
|
Location = "Registration Hall"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Judging",
|
||||||
|
Date = "March 15",
|
||||||
|
Time = "1:00 p.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 15, 13, 0, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 15, 17, 0, 0),
|
||||||
|
Location = "Exhibition Hall"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Submit",
|
||||||
|
Date = "March 16",
|
||||||
|
Time = "8:00 a.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 16, 8, 0, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 16, 10, 0, 0),
|
||||||
|
Location = "Submission Desk"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Competition 1",
|
||||||
|
Date = "March 16",
|
||||||
|
Time = "10:30 a.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 16, 10, 30, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 16, 12, 0, 0),
|
||||||
|
Location = "Competition Hall"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Competition 2",
|
||||||
|
Date = "March 16",
|
||||||
|
Time = "10:30 a.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 16, 11, 30, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 16, 13, 0, 0),
|
||||||
|
Location = "Competition Hall"
|
||||||
|
},
|
||||||
|
new EventOccurrence
|
||||||
|
{
|
||||||
|
Name = "Awards Ceremony",
|
||||||
|
Date = "March 17",
|
||||||
|
Time = "2:00 p.m.",
|
||||||
|
StartTime = new DateTime(2026, 3, 17, 14, 0, 0),
|
||||||
|
EndTime = new DateTime(2026, 3, 17, 16, 0, 0),
|
||||||
|
Location = "Main Auditorium"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.FromResult<IEnumerable<EventOccurrence>>(mockOccurrences);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IEnumerable<EventOccurrence>> GetEventOccurrencesForDateRangeAsync(DateTime start, DateTime end)
|
||||||
|
{
|
||||||
|
return GetEventOccurrencesAsync().ContinueWith(task =>
|
||||||
|
{
|
||||||
|
var allOccurrences = task.Result;
|
||||||
|
return allOccurrences.Where(eo =>
|
||||||
|
eo.StartTime.Date >= start.Date &&
|
||||||
|
eo.StartTime.Date <= end.Date);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Core.Entities;
|
||||||
|
|
||||||
|
namespace WebApp.Services;
|
||||||
|
|
||||||
|
public interface IEventOccurrenceService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all event occurrences.
|
||||||
|
/// </summary>
|
||||||
|
Task<IEnumerable<EventOccurrence>> GetEventOccurrencesAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets event occurrences within the specified date range.
|
||||||
|
/// </summary>
|
||||||
|
Task<IEnumerable<EventOccurrence>> GetEventOccurrencesForDateRangeAsync(DateTime start, DateTime end);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="BlazorSortableList" Version="2.1.0" />
|
<PackageReference Include="BlazorSortableList" Version="2.1.0" />
|
||||||
|
<PackageReference Include="Heron.MudCalendar" Version="3.4.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="9.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="9.0.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="9.0.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.8" />
|
||||||
|
|||||||
Reference in New Issue
Block a user