feat: add loading spinner during credentials fetch

This commit is contained in:
stellarshenson
2026-01-06 21:15:42 +01:00
parent f5b8a5717e
commit bc76c4f767
2 changed files with 41 additions and 0 deletions

View File

@@ -87,3 +87,9 @@ This journal tracks substantive work on documents, diagrams, and documentation c
28. **Task - Fix admin template URL handling**: Fixed fetch interceptor and nav links in templates_enhanced<br>
**Result**: Fixed `isUserCreation` check in admin.html to strip query params (`?_xsrf=...`) before checking if URL ends with `api/users` - React admin adds XSRF token as query param which broke the endpoint detection. Fixed double "hub" prefix in page.html nav links - changed `{{ base_url }}hub/authorize` to `{{ base_url }}authorize` and `{{ base_url }}hub/change-password` to `{{ base_url }}change-password` since base_url already includes `/hub/`
29. **Task - Fix credentials API route conflict**: Changed credentials endpoint from `/api/users/credentials` to `/api/admin/credentials`<br>
**Result**: JupyterHub's built-in `/api/users/*` handler was catching requests before custom handler, returning "Invalid JSON keys" error. Changed route in jupyterhub_config.py, admin.html, and custom_handlers.py docstring
30. **Task - Credentials modal UX improvements**: Enhanced modal layout, scrolling, and loading feedback<br>
**Result**: Moved Copy/Download buttons to top of modal body. Added scrollable container (max-height 300px) for long user lists. Removed `<code>` styling for plain text display. Added loading spinner modal shown between user creation and credentials display. Improved Makefile version output format to show "Current version" and "New version" lines

View File

@@ -51,6 +51,20 @@
</div>
</div>
</div>
<!-- Loading Spinner Modal -->
<div class="modal fade" id="loading-modal" tabindex="-1" role="dialog" data-bs-backdrop="static" data-bs-keyboard="false" aria-hidden="true">
<div class="modal-dialog modal-sm modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-body text-center py-4">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<div>Generating credentials...</div>
</div>
</div>
</div>
</div>
{% endblock main %}
{% block footer %}
@@ -131,6 +145,9 @@
console.log('[Admin] Users created:', createdUsers);
pendingUsernames.push(...createdUsers);
// Show loading spinner
showLoadingSpinner();
// Debounce - wait for batch completion then fetch credentials
clearTimeout(window._credentialsFetchTimeout);
window._credentialsFetchTimeout = setTimeout(() => {
@@ -172,14 +189,32 @@
}
} else {
console.error('[Admin] Failed to fetch credentials:', response.status);
hideLoadingSpinner();
}
} catch (e) {
console.error('[Admin] Error fetching credentials:', e);
hideLoadingSpinner();
}
}
// Loading spinner functions
let loadingModal = null;
function showLoadingSpinner() {
const modalEl = document.getElementById('loading-modal');
if (!loadingModal) {
loadingModal = new bootstrap.Modal(modalEl);
}
loadingModal.show();
}
function hideLoadingSpinner() {
if (loadingModal) {
loadingModal.hide();
}
}
// Show modal with credentials
function showCredentialsModal(credentials) {
hideLoadingSpinner();
const tbody = document.getElementById('credentials-body');
tbody.innerHTML = '';