diff --git a/app/javascript/controllers/entry_drag_controller.js b/app/javascript/controllers/entry_drag_controller.js index 7251a93e..40daaca9 100644 --- a/app/javascript/controllers/entry_drag_controller.js +++ b/app/javascript/controllers/entry_drag_controller.js @@ -62,6 +62,15 @@ export default class extends Controller { font-weight: 500; } + .entry-loading-overlay.success { + background: rgba(220, 255, 220, 0.95); + } + + .entry-loading-overlay .success-text { + color: #198754; + font-weight: 500; + } + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } @@ -279,11 +288,13 @@ export default class extends Controller { throw new Error(data.error || 'Failed to update rankings') } - // Remove loading overlay before refresh - this.removeLoadingOverlay(event.item) + // Show success state so judges see "Ranking recorded" before refresh + this.showSuccessOverlay(event.item) - // Refresh both sections using Turbo - Turbo.visit(window.location.href, { action: "replace" }) + setTimeout(() => { + this.removeLoadingOverlay(event.item) + Turbo.visit(window.location.href, { action: "replace" }) + }, 2500) } catch (error) { console.error('Error updating rankings:', error) // If there's an error, revert the UI update and show error in overlay @@ -474,11 +485,35 @@ export default class extends Controller {
Still working... +
+ Please wait for "Ranking recorded" before closing or navigating away. ` } }, 5000) // Show warning after 5 seconds } + // Helper method to show success state on the overlay before removing it + showSuccessOverlay(element) { + if (!element) return + + const overlay = element.querySelector('.entry-loading-overlay') + if (!overlay) return + + if (this.slowConnectionTimeout) { + clearTimeout(this.slowConnectionTimeout) + this.slowConnectionTimeout = null + } + + overlay.classList.add('success') + const spinnerContainer = overlay.querySelector('.spinner-container') + if (spinnerContainer) { + spinnerContainer.innerHTML = ` + +
Ranking recorded
+ ` + } + } + // Helper method to remove loading overlay removeLoadingOverlay(element, error = false) { if (!element) return diff --git a/spec/javascript/controllers/entry_drag_controller.spec.js b/spec/javascript/controllers/entry_drag_controller.spec.js index 30c54a42..fefb6335 100644 --- a/spec/javascript/controllers/entry_drag_controller.spec.js +++ b/spec/javascript/controllers/entry_drag_controller.spec.js @@ -128,6 +128,32 @@ describe("EntryDragController", () => { }) }) + describe("showSuccessOverlay", () => { + it("replaces overlay content with success state and 'Ranking recorded' text", () => { + const card = document.createElement('div') + card.setAttribute('data-entry-id', '42') + card.style.position = 'relative' + card.innerHTML = ` +
+
+
+
Updating...
+
+
+ ` + document.body.appendChild(card) + + controller.showSuccessOverlay(card) + + const overlay = card.querySelector('.entry-loading-overlay') + expect(overlay.classList.contains('success')).toBe(true) + expect(overlay.textContent).toMatch(/Ranking recorded/) + expect(overlay.querySelector('.bi-check-circle-fill')).not.toBeNull() + + document.body.removeChild(card) + }) + }) + describe("UI updates", () => { it("updates counter and rank badges correctly", () => { // Add one entry to rated entries diff --git a/yarn.lock b/yarn.lock index 35d5af1e..b70326fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1044,9 +1044,9 @@ "@rails/actioncable" "^7.0" "@hotwired/turbo@^8.0.6": - version "8.0.10" - resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.10.tgz#d95569d259f0daad6e824ee1ada877ff94beb72b" - integrity sha512-xen1YhNQirAHlA8vr/444XsTNITC1Il2l/Vx4w8hAWPpI5nQO78mVHNsmFuayETodzPwh25ob2TgfCEV/Loiog== + version "8.0.21" + resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.21.tgz#a3e80c01d70048200f64bbe3582b84f9bfac034e" + integrity sha512-fJTv3JnzFHeDxBb23esZSOhT4r142xf5o3lKMFMvzPC6AllkqbBKk5Yb31UZhtIsKQCwmO/pUQrtTUlYl5CHAQ== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0"