Add loading state to save mapping button and timeout handling

This commit is contained in:
2026-02-04 19:24:02 -05:00
parent cf1428d678
commit d11b656b23
2 changed files with 38 additions and 11 deletions

View File

@@ -841,16 +841,24 @@ public class AdminController : ControllerBase
{
_logger.LogInformation("Triggering immediate playlist rebuild for {Playlist} with new manual mapping", decodedName);
// Wait for the rebuild to complete before responding to ensure UI gets updated cache
try
// Run rebuild in background with timeout to avoid blocking the response
_ = Task.Run(async () =>
{
await _matchingService.TriggerMatchingForPlaylistAsync(decodedName);
_logger.LogInformation("✓ Playlist {Playlist} rebuilt successfully with manual mapping", decodedName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to rebuild playlist {Playlist} after manual mapping", decodedName);
}
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); // 2 minute timeout
await _matchingService.TriggerMatchingForPlaylistAsync(decodedName);
_logger.LogInformation("✓ Playlist {Playlist} rebuilt successfully with manual mapping", decodedName);
}
catch (OperationCanceledException)
{
_logger.LogWarning("Playlist rebuild for {Playlist} timed out after 2 minutes", decodedName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to rebuild playlist {Playlist} after manual mapping", decodedName);
}
});
}
else
{

View File

@@ -2042,13 +2042,24 @@
return;
}
// Show loading state
const saveBtn = document.getElementById('map-save-btn');
const originalText = saveBtn.textContent;
saveBtn.textContent = 'Saving...';
saveBtn.disabled = true;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
const res = await fetch('/api/admin/playlists/' + encodeURIComponent(playlistName) + '/map', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ spotifyId, jellyfinId })
body: JSON.stringify({ spotifyId, jellyfinId }),
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await res.json();
if (res.ok) {
@@ -2093,7 +2104,15 @@
showToast(data.error || 'Failed to save mapping', 'error');
}
} catch (error) {
showToast('Failed to save mapping', 'error');
if (error.name === 'AbortError') {
showToast('Request timed out - mapping may still be processing', 'warning');
} else {
showToast('Failed to save mapping', 'error');
}
} finally {
// Reset button state
saveBtn.textContent = originalText;
saveBtn.disabled = false;
}
}