diff --git a/Client/Pages/Index.razor.cs b/Client/Pages/Index.razor.cs index b771e92..55c70ec 100644 --- a/Client/Pages/Index.razor.cs +++ b/Client/Pages/Index.razor.cs @@ -15,7 +15,7 @@ namespace ShiftScheduler.Client.Pages private Dictionary SelectedSchedule { get; set; } = new(); private Dictionary SelectedShiftsWithTransport { get; set; } = new(); - private bool _isCurrentMonth = false; + private bool _isCurrentMonth = true; private bool _isLoadingTransport = false; private bool _isLoadingInitial = false; private bool _showConfigDialog = false; diff --git a/Server/Controllers/ShiftController.cs b/Server/Controllers/ShiftController.cs index a051151..065e7de 100644 --- a/Server/Controllers/ShiftController.cs +++ b/Server/Controllers/ShiftController.cs @@ -45,14 +45,15 @@ namespace ShiftScheduler.Server.Controllers var transportConfig = _configurationService.GetTransportConfiguration(); TransportConnection? morningTransport = null; TransportConnection? afternoonTransport = null; + + var shifTimes = _configurationService.ParseShiftTimes(request.Date, shift); // Get transport for morning shift if it has morning time if (!string.IsNullOrEmpty(shift.MorningTime)) { - var morningStartTime = ParseShiftTime(request.Date, shift.MorningTime); - if (morningStartTime.HasValue) + if (shifTimes.MorningStart.HasValue) { - morningTransport = await _transportService.GetConnectionAsync(morningStartTime.Value); + morningTransport = await _transportService.GetConnectionAsync(shifTimes.MorningStart.Value); } } @@ -65,22 +66,18 @@ namespace ShiftScheduler.Server.Controllers // If both morning and afternoon shifts exist, check break duration if (!string.IsNullOrEmpty(shift.MorningTime) && !string.IsNullOrEmpty(shift.AfternoonTime)) { - var morningEndTime = ParseShiftEndTime(request.Date, shift.MorningTime); - var afternoonStartTime = ParseShiftTime(request.Date, shift.AfternoonTime); - - if (morningEndTime.HasValue && afternoonStartTime.HasValue) + if (shifTimes.MorningEnd.HasValue && shifTimes.AfternoonStart.HasValue) { - var breakDurationMinutes = (afternoonStartTime.Value - morningEndTime.Value).TotalMinutes; + var breakDurationMinutes = (shifTimes.AfternoonStart.Value - shifTimes.MorningEnd.Value).TotalMinutes; shouldLoadAfternoonTransport = breakDurationMinutes >= transportConfig.MinBreakMinutes; } } if (shouldLoadAfternoonTransport) { - var afternoonStartTime = ParseShiftTime(request.Date, shift.AfternoonTime); - if (afternoonStartTime.HasValue) + if (shifTimes.AfternoonStart.HasValue) { - afternoonTransport = await _transportService.GetConnectionAsync(afternoonStartTime.Value); + afternoonTransport = await _transportService.GetConnectionAsync(shifTimes.AfternoonStart.Value); } } } @@ -96,40 +93,6 @@ namespace ShiftScheduler.Server.Controllers return Ok(shiftWithTransport); } - private static DateTime? ParseShiftTime(DateTime date, string timeRange) - { - try - { - var times = timeRange.Split('-'); - if (times.Length > 0 && TimeSpan.TryParse(times[0], out var startTime)) - { - return date.Add(startTime); - } - } - catch - { - // Ignore parsing errors - } - return null; - } - - private static DateTime? ParseShiftEndTime(DateTime date, string timeRange) - { - try - { - var times = timeRange.Split('-'); - if (times.Length > 1 && TimeSpan.TryParse(times[1], out var endTime)) - { - return date.Add(endTime); - } - } - catch - { - // Ignore parsing errors - } - return null; - } - [HttpPost("export_ics")] public IActionResult ExportIcsWithTransport([FromBody] List shiftsWithTransport) { diff --git a/Services/ConfigurationService.cs b/Services/ConfigurationService.cs index 03901b5..1e1ac62 100644 --- a/Services/ConfigurationService.cs +++ b/Services/ConfigurationService.cs @@ -12,12 +12,15 @@ namespace ShiftScheduler.Services List GetShifts(); TransportConfiguration GetTransportConfiguration(); - // Schedule persistence methods Task SaveScheduleAsync(int year, int month, Dictionary schedule); Task> LoadScheduleAsync(int year, int month); Task DeleteScheduleAsync(int year, int month); + + ShiftTimes ParseShiftTimes(DateTime date, Shift shift); } + public record ShiftTimes(DateTimeOffset? MorningStart, DateTimeOffset? MorningEnd, DateTimeOffset? AfternoonStart, DateTimeOffset? AfternoonEnd); + public class ConfigurationService : IConfigurationService { private ApplicationConfiguration _configuration; @@ -201,7 +204,45 @@ namespace ShiftScheduler.Services // Log error if needed, but don't throw } } - + + public ShiftTimes ParseShiftTimes(DateTime date, Shift shift) + { + DateTimeOffset? morningStart = null; + DateTimeOffset? morningEnd = null; + DateTimeOffset? afternoonStart = null; + DateTimeOffset? afternoonEnd = null; + + if (!string.IsNullOrEmpty(shift.MorningTime)) + { + var times = shift.MorningTime.Split('-'); + if (times.Length == 2) + { + var start = $"{date:yyyy-MM-dd}T{times[0]}:00"; + var end = $"{date:yyyy-MM-dd}T{times[1]}:00"; + morningStart = DateTimeOffset.Parse(start); + morningEnd = DateTimeOffset.Parse(end); + } + } + + if (!string.IsNullOrEmpty(shift.AfternoonTime)) + { + var times = shift.AfternoonTime.Split('-'); + if (times.Length == 2) + { + var start = $"{date:yyyy-MM-dd}T{times[0]}:00"; + var end = $"{date:yyyy-MM-dd}T{times[1]}:00"; + afternoonStart = DateTimeOffset.Parse(start); + afternoonEnd = DateTimeOffset.Parse(end); + } + } + + return new ShiftTimes( + MorningStart: morningStart, + MorningEnd: morningEnd, + AfternoonStart: afternoonStart, + AfternoonEnd: afternoonEnd); + } + private ApplicationConfiguration LoadConfigurationFromFiles(ApplicationConfiguration fallbackConfiguration) { try diff --git a/ShiftScheduler.Services.Tests/ConfigurationServiceTests.cs b/ShiftScheduler.Services.Tests/ConfigurationServiceTests.cs new file mode 100644 index 0000000..7470216 --- /dev/null +++ b/ShiftScheduler.Services.Tests/ConfigurationServiceTests.cs @@ -0,0 +1,47 @@ +using ShiftScheduler.Shared; +using Shouldly; + +namespace ShiftScheduler.Services.Tests; + +public class ConfigurationServiceTests +{ + [Fact] + public void ParseShiftTimes_WithMorningAndAfternoon_ShouldReturnValid() + { + var applicationConfig = new ApplicationConfiguration(); + var configurationService = new ConfigurationService(applicationConfig); + var shift = new Shift + { + MorningTime = "06:00-12:30", + AfternoonTime = "13:00-17:30" + }; + + var shiftTimes = configurationService.ParseShiftTimes(DateTime.Parse("2025-07-13T13:00:00"), shift); + shiftTimes.MorningStart.ShouldBe(T("2025-07-13T06:00:00+02:00")); + shiftTimes.MorningEnd.ShouldBe(T("2025-07-13T12:30:00+02:00")); + shiftTimes.AfternoonStart.ShouldBe(T("2025-07-13T13:00:00+02:00")); + shiftTimes.AfternoonEnd.ShouldBe(T("2025-07-13T17:30:00+02:00")); + } + + [Fact] + public void ParseShiftTimes_WithMorning_ShouldReturnValid() + { + var applicationConfig = new ApplicationConfiguration(); + var configurationService = new ConfigurationService(applicationConfig); + var shift = new Shift + { + MorningTime = "06:00-12:30" + }; + + var shiftTimes = configurationService.ParseShiftTimes(DateTime.Parse("2025-07-13T13:00:00"), shift); + shiftTimes.MorningStart.ShouldBe(T("2025-07-13T06:00:00+02:00")); + shiftTimes.MorningEnd.ShouldBe(T("2025-07-13T12:30:00+02:00")); + shiftTimes.AfternoonStart.ShouldBeNull(); + shiftTimes.AfternoonEnd.ShouldBeNull(); + } + + private DateTimeOffset T(string dateTimeString) + { + return DateTimeOffset.Parse(dateTimeString); + } +} \ No newline at end of file diff --git a/ShiftScheduler.Services.Tests/TransportConnectionCalculatorTests.cs b/ShiftScheduler.Services.Tests/TransportConnectionCalculatorTests.cs index c89b81b..892ae2d 100644 --- a/ShiftScheduler.Services.Tests/TransportConnectionCalculatorTests.cs +++ b/ShiftScheduler.Services.Tests/TransportConnectionCalculatorTests.cs @@ -9,7 +9,6 @@ public class TransportConnectionCalculatorTests { private readonly Mock loggerMock = new(); - // Tests for new enhanced logic [Fact] public void FindBestConnectionEnhanced_WithGoodTimingConnection_ShouldReturnLatestValidConnection() {