From d683f04305007f0351138fd0a075f858562669de Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 21 Dec 2025 21:53:19 +0000 Subject: [PATCH 1/2] Fix page loader alignment with dynamic navbar Co-authored-by: mathieumoran1 --- public/css/app.css | 5 ++++- public/js/app_checklist.js | 30 ++++++++++++++++++++++++++++++ public/js/app_movies.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/public/css/app.css b/public/css/app.css index ee75ea4..abdb7df 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -58,7 +58,10 @@ footer { .page-loader-overlay { position: fixed; - inset: 0; + top: var(--page-loader-top, 0px); + left: 0; + right: 0; + bottom: 0; z-index: 2000; display: flex; align-items: center; diff --git a/public/js/app_checklist.js b/public/js/app_checklist.js index e72ae74..1b7f9a0 100644 --- a/public/js/app_checklist.js +++ b/public/js/app_checklist.js @@ -4,6 +4,7 @@ function createPageLoader(options = {}) { let progress = 0; let removed = false; + let navResizeObserver = null; const overlay = document.createElement('div'); overlay.className = 'page-loader-overlay'; @@ -46,11 +47,36 @@ function createPageLoader(options = {}) { if (el) el.textContent = String(text || ''); } + function getNavHeightPx() { + const nav = document.querySelector('nav.navbar'); + if (!nav) return 0; + const rect = nav.getBoundingClientRect(); + const h = Number(rect?.height) || 0; + return h > 0 ? Math.round(h) : 0; + } + + function updateOverlayTopOffset() { + // Scope the offset to this overlay instance (no global CSS var). + overlay.style.setProperty('--page-loader-top', `${getNavHeightPx()}px`); + } + function ensureMounted() { if (removed) return; if (!overlay.isConnected) { + updateOverlayTopOffset(); document.body.classList.add('page-loading'); document.body.appendChild(overlay); + + // Keep the overlay aligned if the navbar height changes (mobile collapse, resize, etc.) + const nav = document.querySelector('nav.navbar'); + if (nav && typeof ResizeObserver !== 'undefined') { + try { + navResizeObserver = new ResizeObserver(() => updateOverlayTopOffset()); + navResizeObserver.observe(nav); + } catch (_) { + navResizeObserver = null; + } + } } } @@ -61,6 +87,10 @@ function createPageLoader(options = {}) { document.body.classList.remove('page-loading'); window.setTimeout(() => { removed = true; + if (navResizeObserver) { + try { navResizeObserver.disconnect(); } catch (_) {} + navResizeObserver = null; + } try { overlay.remove(); } catch (_) {} }, 220); } diff --git a/public/js/app_movies.js b/public/js/app_movies.js index 18b3cb0..b3b8330 100644 --- a/public/js/app_movies.js +++ b/public/js/app_movies.js @@ -4,6 +4,7 @@ function createPageLoader(options = {}) { let progress = 0; let removed = false; + let navResizeObserver = null; const overlay = document.createElement('div'); overlay.className = 'page-loader-overlay'; @@ -46,11 +47,36 @@ function createPageLoader(options = {}) { if (el) el.textContent = String(text || ''); } + function getNavHeightPx() { + const nav = document.querySelector('nav.navbar'); + if (!nav) return 0; + const rect = nav.getBoundingClientRect(); + const h = Number(rect?.height) || 0; + return h > 0 ? Math.round(h) : 0; + } + + function updateOverlayTopOffset() { + // Scope the offset to this overlay instance (no global CSS var). + overlay.style.setProperty('--page-loader-top', `${getNavHeightPx()}px`); + } + function ensureMounted() { if (removed) return; if (!overlay.isConnected) { + updateOverlayTopOffset(); document.body.classList.add('page-loading'); document.body.appendChild(overlay); + + // Keep the overlay aligned if the navbar height changes (mobile collapse, resize, etc.) + const nav = document.querySelector('nav.navbar'); + if (nav && typeof ResizeObserver !== 'undefined') { + try { + navResizeObserver = new ResizeObserver(() => updateOverlayTopOffset()); + navResizeObserver.observe(nav); + } catch (_) { + navResizeObserver = null; + } + } } } @@ -61,6 +87,10 @@ function createPageLoader(options = {}) { document.body.classList.remove('page-loading'); window.setTimeout(() => { removed = true; + if (navResizeObserver) { + try { navResizeObserver.disconnect(); } catch (_) {} + navResizeObserver = null; + } try { overlay.remove(); } catch (_) {} }, 220); } From 00f5328c75b2dabb06ee0539e0af50cf202f8441 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 21 Dec 2025 21:54:59 +0000 Subject: [PATCH 2/2] Refactor: Simplify page loader UI and error handling Remove subtitle and retry button from page loader. Update CSS for new loader style. Co-authored-by: mathieumoran1 --- public/css/app.css | 7 ++++++- public/js/app_checklist.js | 28 +++++++++------------------- public/js/app_movies.js | 28 +++++++++------------------- 3 files changed, 24 insertions(+), 39 deletions(-) diff --git a/public/css/app.css b/public/css/app.css index abdb7df..544ca01 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -88,6 +88,11 @@ footer { box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12); } + .page-loader-card.page-loader-card--baronly { + width: min(420px, 90vw); + padding: 14px; + } + .page-loader-title { margin: 0 0 10px 0; font-weight: 700; @@ -112,7 +117,7 @@ footer { height: 100%; width: 0%; border-radius: 999px; - background: linear-gradient(90deg, #ff4d4d 0%, #ffb200 50%, #4cd964 100%); + background: #111; transition: width 220ms ease; } diff --git a/public/js/app_checklist.js b/public/js/app_checklist.js index 1b7f9a0..0b91ba5 100644 --- a/public/js/app_checklist.js +++ b/public/js/app_checklist.js @@ -1,5 +1,6 @@ function createPageLoader(options = {}) { const title = String(options.title || 'Chargement…'); + // Subtitle kept for backwards-compat with callers, but we no longer render text in the UI. const subtitle = String(options.subtitle || 'Préparation de la page…'); let progress = 0; @@ -13,22 +14,15 @@ function createPageLoader(options = {}) { overlay.setAttribute('aria-busy', 'true'); overlay.innerHTML = ` -
-

${title}

-

${subtitle}

+
+ ${title} -
- -
`; const barEl = () => overlay.querySelector('#page-loader-bar'); - const subtitleEl = () => overlay.querySelector('#page-loader-subtitle'); - const actionsEl = () => overlay.querySelector('#page-loader-actions'); - const retryBtnEl = () => overlay.querySelector('#page-loader-retry'); function clampPct(p) { const n = Number(p); @@ -42,9 +36,8 @@ function createPageLoader(options = {}) { if (bar) bar.style.width = `${progress}%`; } - function setSubtitle(text) { - const el = subtitleEl(); - if (el) el.textContent = String(text || ''); + function setSubtitle(_) { + // Intentionally no-op (we only show the bar now). } function getNavHeightPx() { @@ -100,13 +93,10 @@ function createPageLoader(options = {}) { hideAndRemoveSoon(); } - function fail(message) { - setSubtitle(message || 'Erreur lors du chargement.'); - setProgress(Math.max(progress, 90)); - const actions = actionsEl(); - if (actions) actions.classList.remove('d-none'); - const btn = retryBtnEl(); - if (btn) btn.onclick = () => window.location.reload(); + function fail(_) { + // Don't block the UI on errors: let the page render its own error message. + setProgress(Math.max(progress, 95)); + window.setTimeout(() => hideAndRemoveSoon(), 200); } ensureMounted(); diff --git a/public/js/app_movies.js b/public/js/app_movies.js index b3b8330..ff69751 100644 --- a/public/js/app_movies.js +++ b/public/js/app_movies.js @@ -1,5 +1,6 @@ function createPageLoader(options = {}) { const title = String(options.title || 'Chargement…'); + // Subtitle kept for backwards-compat with callers, but we no longer render text in the UI. const subtitle = String(options.subtitle || 'Préparation de la page…'); let progress = 0; @@ -13,22 +14,15 @@ function createPageLoader(options = {}) { overlay.setAttribute('aria-busy', 'true'); overlay.innerHTML = ` -
-

${title}

-

${subtitle}

+
+ ${title} -
- -
`; const barEl = () => overlay.querySelector('#page-loader-bar'); - const subtitleEl = () => overlay.querySelector('#page-loader-subtitle'); - const actionsEl = () => overlay.querySelector('#page-loader-actions'); - const retryBtnEl = () => overlay.querySelector('#page-loader-retry'); function clampPct(p) { const n = Number(p); @@ -42,9 +36,8 @@ function createPageLoader(options = {}) { if (bar) bar.style.width = `${progress}%`; } - function setSubtitle(text) { - const el = subtitleEl(); - if (el) el.textContent = String(text || ''); + function setSubtitle(_) { + // Intentionally no-op (we only show the bar now). } function getNavHeightPx() { @@ -100,13 +93,10 @@ function createPageLoader(options = {}) { hideAndRemoveSoon(); } - function fail(message) { - setSubtitle(message || 'Erreur lors du chargement.'); - setProgress(Math.max(progress, 90)); - const actions = actionsEl(); - if (actions) actions.classList.remove('d-none'); - const btn = retryBtnEl(); - if (btn) btn.onclick = () => window.location.reload(); + function fail(_) { + // Don't block the UI on errors: let the page render its own error message. + setProgress(Math.max(progress, 95)); + window.setTimeout(() => hideAndRemoveSoon(), 200); } ensureMounted();