@page "/calendar-event" @page "/calendar-event/{DateStr}" @page "/calendar-event/{DateStr}/{EventId}" @attribute [Authorize] @rendermode InteractiveServer @inject IWorkDayService WorkDayService @inject NavigationManager Navigation @inject IJSRuntime JS Calendar Event

Calendar Event

@if (!loaded) {

Loading...

} else {
Optional. Leave empty for a single-day event.
@FormatDuration()
@if (isExistingEvent) { } @if (!string.IsNullOrWhiteSpace(statusMessage)) { @statusMessage }
} @code { [Parameter] public string? DateStr { get; set; } [Parameter] public string? EventId { get; set; } private bool loaded; private bool isExistingEvent; private DateOnly selectedDate = DateOnly.FromDateTime(DateTime.Today); private string eventId = string.Empty; private CalendarEventType eventType = CalendarEventType.Generic; private string description = string.Empty; private string? startTimeStr; private string? endTimeStr; private DateOnly? endDate; private string? statusMessage; protected override async Task OnInitializedAsync() { if (!string.IsNullOrEmpty(DateStr) && DateOnly.TryParseExact(DateStr, "yyyy-MM-dd", out var parsed)) { selectedDate = parsed; } await LoadEventAsync(); loaded = true; } private async Task LoadEventAsync() { if (string.IsNullOrWhiteSpace(EventId)) { SetDefaults(); return; } var existing = await WorkDayService.GetCalendarEventAsync(selectedDate, EventId); if (existing is not null) { eventId = existing.Id; selectedDate = existing.StartDate == default ? selectedDate : existing.StartDate; eventType = existing.EventType; description = existing.Description; startTimeStr = existing.StartTime?.ToString("HH:mm"); endTimeStr = existing.EndTime?.ToString("HH:mm"); endDate = existing.EndDate; isExistingEvent = true; } else { SetDefaults(); statusMessage = "The selected calendar event was not found. A new event will be created for this day."; } } private void SetDefaults() { eventId = string.Empty; eventType = CalendarEventType.Generic; description = string.Empty; startTimeStr = null; endTimeStr = null; endDate = null; isExistingEvent = false; } private Task OnDateChangedAsync(DateOnly? value) { if (value.HasValue) { selectedDate = value.Value; statusMessage = null; } return Task.CompletedTask; } private Task OnEndDateChangedAsync(DateOnly? value) { endDate = value; statusMessage = null; return Task.CompletedTask; } private Task OnStartTimeChanged(ChangeEventArgs e) { startTimeStr = e.Value?.ToString(); statusMessage = null; return Task.CompletedTask; } private Task OnEndTimeChanged(ChangeEventArgs e) { endTimeStr = e.Value?.ToString(); statusMessage = null; return Task.CompletedTask; } private async Task SaveAsync() { var calendarEvent = new CalendarEventDocument { Id = eventId, StartDate = selectedDate, EndDate = endDate, EventType = eventType, Description = description, StartTime = ParseTime(startTimeStr), EndTime = ParseTime(endTimeStr) }; var saved = await WorkDayService.SaveCalendarEventAsync(selectedDate, calendarEvent); eventId = saved.Id; isExistingEvent = true; startTimeStr = saved.StartTime?.ToString("HH:mm"); endTimeStr = saved.EndTime?.ToString("HH:mm"); Navigation.NavigateTo($"/calendar/{selectedDate:yyyy-MM}"); } private async Task DeleteAsync() { if (!isExistingEvent || string.IsNullOrWhiteSpace(eventId)) { return; } var eventName = CalendarEventFormatter.GetDisplayDescription(eventType, description); var confirmed = await JS.InvokeAsync("confirm", $"Delete calendar event '{eventName}' starting on {FormatDisplayDate(selectedDate)}?\nThis cannot be undone."); if (!confirmed) { return; } var deleted = await WorkDayService.DeleteCalendarEventAsync(selectedDate, eventId); if (deleted) { Navigation.NavigateTo($"/calendar/{selectedDate:yyyy-MM}"); return; } statusMessage = "Unable to delete the calendar event."; } private void BackToCalendar() { Navigation.NavigateTo($"/calendar/{selectedDate:yyyy-MM}"); } private decimal? GetDuration() { var start = ParseTime(startTimeStr); var end = ParseTime(endTimeStr); if (!start.HasValue || !end.HasValue || end <= start) { return null; } return Math.Round((decimal)(end.Value - start.Value).TotalHours, 2, MidpointRounding.AwayFromZero); } private string FormatDuration() => GetDuration() is { } duration ? FormatDurationHours(duration) : "—"; private static string FormatDurationHours(decimal value) { var totalMinutes = (int)Math.Round(value * 60m, MidpointRounding.AwayFromZero); var hours = totalMinutes / 60; var minutes = totalMinutes % 60; return $"{hours:00}:{minutes:00}"; } private static TimeOnly? ParseTime(string? value) { return !string.IsNullOrWhiteSpace(value) && TimeOnly.TryParse(value, out var parsed) ? parsed : null; } private static string FormatDisplayDate(DateOnly date) { return date.ToString("dddd dd/MM/yyyy"); } }