diff --git a/DiunaBI.UI.Web/Components/App.razor b/DiunaBI.UI.Web/Components/App.razor index bb1f8a6..4768157 100644 --- a/DiunaBI.UI.Web/Components/App.razor +++ b/DiunaBI.UI.Web/Components/App.razor @@ -31,9 +31,24 @@ 🗙 +
+
+
+
Connection Lost
+
+ Attempting to reconnect to the server... +
+
+ 0s +
+ +
+
+ + \ No newline at end of file diff --git a/DiunaBI.UI.Web/wwwroot/app.css b/DiunaBI.UI.Web/wwwroot/app.css index 613f145..47fce62 100644 --- a/DiunaBI.UI.Web/wwwroot/app.css +++ b/DiunaBI.UI.Web/wwwroot/app.css @@ -58,3 +58,93 @@ h1:focus { .mud-pagination li::marker { display: none; } + +/* Blazor Server Reconnection UI Customization */ +#components-reconnect-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); + z-index: 9999; + font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif; + display: none !important; + align-items: center; + justify-content: center; +} + +/* Show modal when Blazor applies these classes */ +#components-reconnect-modal.components-reconnect-show, +#components-reconnect-modal.components-reconnect-failed, +#components-reconnect-modal.components-reconnect-rejected { + display: flex !important; +} + +#components-reconnect-modal .reconnect-content { + background: white; + border-radius: 8px; + padding: 32px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + max-width: 400px; + text-align: center; +} + +#components-reconnect-modal h5 { + margin: 0 0 16px 0; + color: #424242; + font-size: 20px; + font-weight: 500; +} + +#components-reconnect-modal .reconnect-message { + color: #666; + margin-bottom: 24px; + font-size: 14px; + line-height: 1.5; +} + +#components-reconnect-modal .reconnect-spinner { + width: 48px; + height: 48px; + border: 4px solid #f3f3f3; + border-top: 4px solid #e7163d; + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 16px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +#components-reconnect-modal .reconnect-timer { + color: #e7163d; + font-size: 16px; + font-weight: 500; + margin-bottom: 16px; +} + +#components-reconnect-modal button { + background-color: #e7163d; + color: white; + border: none; + border-radius: 4px; + padding: 10px 24px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +#components-reconnect-modal button:hover { + background-color: #c01234; +} + +#components-reconnect-modal button:active { + background-color: #a01028; +} diff --git a/DiunaBI.UI.Web/wwwroot/js/reconnect.js b/DiunaBI.UI.Web/wwwroot/js/reconnect.js new file mode 100644 index 0000000..c27ad40 --- /dev/null +++ b/DiunaBI.UI.Web/wwwroot/js/reconnect.js @@ -0,0 +1,82 @@ +// Blazor Server Reconnection Timer +(function() { + let reconnectTimer = null; + let startTime = null; + + function startTimer() { + if (reconnectTimer) return; // Already running + + console.log('Blazor reconnection started, timer running...'); + startTime = Date.now(); + + reconnectTimer = setInterval(() => { + const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000); + const timerElement = document.getElementById('reconnect-elapsed-time'); + + if (timerElement) { + timerElement.textContent = `${elapsedSeconds}s`; + } + }, 1000); + } + + function stopTimer() { + if (reconnectTimer) { + console.log('Blazor reconnection ended, stopping timer'); + clearInterval(reconnectTimer); + reconnectTimer = null; + + // Reset timer display + const timerElement = document.getElementById('reconnect-elapsed-time'); + if (timerElement) { + timerElement.textContent = '0s'; + } + } + } + + function checkReconnectionState() { + const modal = document.getElementById('components-reconnect-modal'); + + if (!modal) return; + + // Check if modal has the "show" class (Blazor applies this when reconnecting) + if (modal.classList.contains('components-reconnect-show')) { + startTimer(); + } else { + stopTimer(); + } + } + + // MutationObserver to watch for class changes on the modal + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + checkReconnectionState(); + } + }); + }); + + // Start observing when DOM is ready + function init() { + const modal = document.getElementById('components-reconnect-modal'); + + if (modal) { + observer.observe(modal, { + attributes: true, + attributeFilter: ['class'] + }); + + // Check initial state + checkReconnectionState(); + console.log('Blazor reconnection timer initialized'); + } else { + console.warn('components-reconnect-modal not found, retrying...'); + setTimeout(init, 100); + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})();