Improve loading speed with parallel transport calls and non-blocking UI (#52)

* Initial plan

* Implement parallel transport loading and non-blocking UI improvements

Co-authored-by: clayschaad <11883505+clayschaad@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: clayschaad <11883505+clayschaad@users.noreply.github.com>
This commit is contained in:
Copilot 2025-10-04 14:37:10 +02:00 committed by GitHub
parent fddca4e833
commit 335fe9821b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 11 deletions

View file

@ -111,8 +111,8 @@ else
<div class="tooltip-wrapper"> <div class="tooltip-wrapper">
<button class="shift-button @(SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name ? "selected" : "")" <button class="shift-button @(SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name ? "selected" : "")"
@onclick="() => SelectShift(day, shift.Name)" @onclick="() => SelectShift(day, shift.Name)"
disabled="@_isLoadingTransport"> disabled="@IsLoadingTransportForDay(day)">
@if (_isLoadingTransport && SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name) @if (IsLoadingTransportForDay(day) && SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name)
{ {
<span class="loading-spinner">⏳</span> <span class="loading-spinner">⏳</span>
} }
@ -169,8 +169,8 @@ else
<div class="tooltip-wrapper"> <div class="tooltip-wrapper">
<button class="shift-button @(SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name ? "selected" : "")" <button class="shift-button @(SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name ? "selected" : "")"
@onclick="() => SelectShift(day, shift.Name)" @onclick="() => SelectShift(day, shift.Name)"
disabled="@_isLoadingTransport"> disabled="@IsLoadingTransportForDay(day)">
@if (_isLoadingTransport && SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name) @if (IsLoadingTransportForDay(day) && SelectedSchedule.ContainsKey(day) && SelectedSchedule[day] == shift.Name)
{ {
<span class="loading-spinner">⏳</span> <span class="loading-spinner">⏳</span>
} }
@ -217,3 +217,11 @@ else
<ConfigurationDialog IsVisible="_showConfigDialog" <ConfigurationDialog IsVisible="_showConfigDialog"
OnClose="HideConfiguration" OnClose="HideConfiguration"
OnConfigurationChanged="OnConfigurationChanged" /> OnConfigurationChanged="OnConfigurationChanged" />
@code {
// Helper method to check if a specific day is loading transport data
private bool IsLoadingTransportForDay(DateTime day)
{
return _isLoadingTransportPerDay.GetValueOrDefault(day, false);
}
}

View file

@ -14,9 +14,9 @@ namespace ShiftScheduler.Client.Pages
private List<Shift> Shifts { get; set; } = new(); private List<Shift> Shifts { get; set; } = new();
private Dictionary<DateTime, string> SelectedSchedule { get; set; } = new(); private Dictionary<DateTime, string> SelectedSchedule { get; set; } = new();
private Dictionary<DateTime, ShiftWithTransport> SelectedShiftsWithTransport { get; set; } = new(); private Dictionary<DateTime, ShiftWithTransport> SelectedShiftsWithTransport { get; set; } = new();
private Dictionary<DateTime, bool> _isLoadingTransportPerDay { get; set; } = new();
private bool _isCurrentMonth = true; private bool _isCurrentMonth = true;
private bool _isLoadingTransport = false;
private bool _isLoadingInitial = false; private bool _isLoadingInitial = false;
private bool _showConfigDialog = false; private bool _showConfigDialog = false;
private bool _isSyncing = false; private bool _isSyncing = false;
@ -31,12 +31,19 @@ namespace ShiftScheduler.Client.Pages
{ {
_isLoadingInitial = true; _isLoadingInitial = true;
// Load shifts first to show the calendar immediately
Shifts = await HttpClient.GetFromJsonAsync<List<Shift>>("api/shift/shifts") ?? new(); Shifts = await HttpClient.GetFromJsonAsync<List<Shift>>("api/shift/shifts") ?? new();
await LoadScheduleFromStorage(); // Show calendar immediately, don't wait for transport data
_isLoadingInitial = false; _isLoadingInitial = false;
StateHasChanged(); StateHasChanged();
// Load schedule and transport data in the background
_ = Task.Run(async () =>
{
await LoadScheduleFromStorage();
await InvokeAsync(StateHasChanged);
});
} }
private async Task SelectCurrentMonth() private async Task SelectCurrentMonth()
@ -57,8 +64,8 @@ namespace ShiftScheduler.Client.Pages
{ {
SelectedSchedule[day] = shiftName; SelectedSchedule[day] = shiftName;
// Get transport information for this shift and date // Get transport information for this shift and date - per day loading
_isLoadingTransport = true; _isLoadingTransportPerDay[day] = true;
StateHasChanged(); StateHasChanged();
try try
@ -93,7 +100,7 @@ namespace ShiftScheduler.Client.Pages
} }
finally finally
{ {
_isLoadingTransport = false; _isLoadingTransportPerDay[day] = false;
StateHasChanged(); StateHasChanged();
} }
@ -312,7 +319,18 @@ namespace ShiftScheduler.Client.Pages
{ {
SelectedShiftsWithTransport.Clear(); SelectedShiftsWithTransport.Clear();
// Clear existing per-day loading states
_isLoadingTransportPerDay.Clear();
// Mark all days as loading
foreach (var kvp in SelectedSchedule) foreach (var kvp in SelectedSchedule)
{
_isLoadingTransportPerDay[kvp.Key] = true;
}
StateHasChanged();
// Create parallel tasks for all transport data requests
var transportTasks = SelectedSchedule.Select(async kvp =>
{ {
var day = kvp.Key; var day = kvp.Key;
var shiftName = kvp.Value; var shiftName = kvp.Value;
@ -347,7 +365,16 @@ namespace ShiftScheduler.Client.Pages
}; };
} }
} }
finally
{
// Mark this day as no longer loading
_isLoadingTransportPerDay[day] = false;
await InvokeAsync(StateHasChanged);
} }
}).ToArray();
// Wait for all transport data to load
await Task.WhenAll(transportTasks);
} }
private async Task ResetSchedule() private async Task ResetSchedule()