This commit is contained in:
Claudio Schaad 2025-09-06 16:46:17 +02:00
parent 419dd4453f
commit f3389936b5
4 changed files with 33 additions and 38 deletions

View file

@ -7,20 +7,10 @@
<button class="btn month-btn @(_isCurrentMonth ? "" : "selected")" @onclick="SelectNextMonth" disabled="@_isLoadingInitial">@MonthAndYear.NextMonthName</button>
</div>
<div class="action-buttons">
<button class="btn export-btn" @onclick="ExportToIcs" disabled="@_isLoadingInitial"><span class="oi oi-calendar" aria-hidden="true"></span> Export to ICS</button>
<button class="btn export-btn" @onclick="SyncToGoogleCalendar" disabled="@(_isLoadingInitial || _isSyncing)">
@if (_isSyncing)
{
<span class="loading-spinner">⏳</span>
}
else
{
<span class="oi oi-cloud-upload" aria-hidden="true"></span>
}
Sync to Google Calendar
</button>
<button class="btn export-btn" @onclick="ExportToPdf" disabled="@_isLoadingInitial"><span class="oi oi-document" aria-hidden="true"></span>Export to PDF</button>
<button class="btn config-btn" @onclick="ShowConfiguration" disabled="@_isLoadingInitial"><span class="oi oi-cog" aria-hidden="true"></span> Configuration</button>
<button class="btn export-btn" @onclick="ExportToIcs" disabled="@(_isLoadingInitial || _isSyncing)"><span class="oi oi-calendar" aria-hidden="true"></span> Export to ICS</button>
<button class="btn export-btn" @onclick="SyncToGoogleCalendar" disabled="@(_isLoadingInitial || _isSyncing)"><span class="oi oi-cloud-upload" aria-hidden="true"></span> Sync to Google Calendar</button>
<button class="btn export-btn" @onclick="ExportToPdf" disabled="@(_isLoadingInitial || _isSyncing)"><span class="oi oi-document" aria-hidden="true"></span>Export to PDF</button>
<button class="btn config-btn" @onclick="ShowConfiguration" disabled="@(_isLoadingInitial || _isSyncing)"><span class="oi oi-cog" aria-hidden="true"></span> Configuration</button>
</div>
</div>
@ -92,6 +82,15 @@
</div>
</div>
}
else if (_isSyncing)
{
<div class="loading-overlay">
<div class="loading-content">
<div class="loading-spinner">⏳</div>
<p>Syncing with google calendar...</p>
</div>
</div>
}
else
{

View file

@ -5,6 +5,7 @@ namespace ShiftScheduler.Services
{
public interface IConfigurationService
{
string GetTimeZone();
ApplicationConfiguration GetConfiguration();
void UpdateConfiguration(ApplicationConfiguration configuration);
Task<string> ExportConfigurationAsync();
@ -29,7 +30,12 @@ namespace ShiftScheduler.Services
private readonly string _shiftsFilePath;
private readonly string _transportFilePath;
readonly TimeZoneInfo localTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Zurich");
private readonly TimeZoneInfo localTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Zurich");
public string GetTimeZone()
{
return localTimeZone.Id;
}
public ConfigurationService(ApplicationConfiguration initialConfiguration)
{

View file

@ -5,7 +5,6 @@ using Google.Apis.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using ShiftScheduler.Shared;
using System.Net;
namespace ShiftScheduler.Services;
@ -15,17 +14,9 @@ public interface IGoogleCalendarService
Task SyncShiftsToCalendarAsync(string calendarId, List<ShiftWithTransport> shifts);
}
public class GoogleCalendarService : IGoogleCalendarService
public class GoogleCalendarService(IHttpContextAccessor httpContextAccessor, IConfigurationService configurationService)
: IGoogleCalendarService
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IcsExportService _icsExportService;
public GoogleCalendarService(IHttpContextAccessor httpContextAccessor, IcsExportService icsExportService)
{
_httpContextAccessor = httpContextAccessor;
_icsExportService = icsExportService;
}
public async Task<List<CalendarListEntry>> GetCalendarsAsync()
{
var service = await CreateCalendarServiceAsync();
@ -110,25 +101,25 @@ public class GoogleCalendarService : IGoogleCalendarService
// Create morning event if it exists
if (!string.IsNullOrEmpty(shift.MorningTime))
{
var morningEvent = CreateEventFromShift(shift, date, shift.MorningTime, "Morning", shiftWithTransport.MorningTransport);
var morningEvent = CreateEventFromShift(shift, date, shift.MorningTime, shiftWithTransport.MorningTransport);
await ExecuteWithRetryAsync(async () => await service.Events.Insert(morningEvent, calendarId).ExecuteAsync());
}
// Create afternoon event if it exists
if (!string.IsNullOrEmpty(shift.AfternoonTime))
{
var afternoonEvent = CreateEventFromShift(shift, date, shift.AfternoonTime, "Afternoon", shiftWithTransport.AfternoonTransport);
var afternoonEvent = CreateEventFromShift(shift, date, shift.AfternoonTime, shiftWithTransport.AfternoonTransport);
await ExecuteWithRetryAsync(async () => await service.Events.Insert(afternoonEvent, calendarId).ExecuteAsync());
}
}
private static Event CreateEventFromShift(Shift shift, DateTime date, string timeRange, string period, TransportConnection? transport)
private Event CreateEventFromShift(Shift shift, DateTime date, string timeRange, TransportConnection? transport)
{
var times = timeRange.Split('-');
var startTime = date.Add(TimeSpan.Parse(times[0]));
var endTime = date.Add(TimeSpan.Parse(times[1]));
var summary = $"{shift.Name} ({period})";
var summary = $"{shift.Name}";
var description = "";
if (transport != null)
@ -144,20 +135,19 @@ public class GoogleCalendarService : IGoogleCalendarService
Start = new EventDateTime
{
DateTimeDateTimeOffset = startTime,
TimeZone = "Europe/Zurich"
TimeZone = configurationService.GetTimeZone()
},
End = new EventDateTime
{
DateTimeDateTimeOffset = endTime,
TimeZone = "Europe/Zurich"
TimeZone = configurationService.GetTimeZone()
},
ExtendedProperties = new Event.ExtendedPropertiesData
{
Private__ = new Dictionary<string, string>
{
["shiftSchedulerEvent"] = "true",
["shiftName"] = shift.Name,
["period"] = period.ToLower()
["shiftName"] = shift.Name
}
}
};
@ -167,12 +157,12 @@ public class GoogleCalendarService : IGoogleCalendarService
{
var departure = transport.DepartureTime.ToString("HH:mm");
var arrival = transport.ArrivalTime.ToString("HH:mm");
return $"{departure} → {arrival}";
return $"{transport.Platform}: {departure} → {arrival} ({transport.Duration.TotalMinutes} Minutes)";
}
private async Task<CalendarService> CreateCalendarServiceAsync()
{
var httpContext = _httpContextAccessor.HttpContext ?? throw new InvalidOperationException("No HTTP context available");
var httpContext = httpContextAccessor.HttpContext ?? throw new InvalidOperationException("No HTTP context available");
var accessToken = await httpContext.GetTokenAsync("access_token");
if (string.IsNullOrEmpty(accessToken))

View file

@ -23,7 +23,7 @@ namespace ShiftScheduler.Services
if (!string.IsNullOrEmpty(shift.MorningTime))
{
var times = shift.MorningTime.Split('-');
var summary = $"{shift.Name} (Morning)";
var summary = $"{shift.Name}";
var description = "";
if (shiftWithTransport.MorningTransport != null)
@ -44,7 +44,7 @@ namespace ShiftScheduler.Services
if (!string.IsNullOrEmpty(shift.AfternoonTime))
{
var times = shift.AfternoonTime.Split('-');
var summary = $"{shift.Name} (Afternoon)";
var summary = $"{shift.Name}";
var description = "";
if (shiftWithTransport.AfternoonTransport != null)