diff --git a/assets/js/admin.js b/assets/js/admin.js index b6164d6..7580ce0 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -24,6 +24,28 @@ var $ = function (id) { return document.getElementById(id); }; var gate = $('adminGate'); var app = $('adminApp'); + var SA_ROLE_HINT_KEY = 'sa_role_hint_v1'; + + function writeAdminRoleHint(userId, isAdmin) { + if (typeof localStorage === 'undefined' || !userId) return; + try { + localStorage.setItem( + SA_ROLE_HINT_KEY, + JSON.stringify({ + user_id: String(userId), + is_admin: isAdmin === true, + updated_at: Date.now() + }) + ); + } catch (e) {} + } + + function clearAdminRoleHint() { + if (typeof localStorage === 'undefined') return; + try { + localStorage.removeItem(SA_ROLE_HINT_KEY); + } catch (e) {} + } function showFatal(msg) { gate.classList.remove('hidden'); @@ -94,6 +116,7 @@ if (gateDecided) return; gateDecided = true; if (!session) { + clearAdminRoleHint(); window.location.replace('/admin-login'); return; } @@ -107,11 +130,13 @@ return; } if (a.data !== true) { + writeAdminRoleHint(session && session.user ? session.user.id : '', false); sb.auth.signOut().then(function () { window.location.replace('/admin-login?raison=non-admin'); }); return; } + writeAdminRoleHint(session.user.id, true); $('adminWho').textContent = session.user.email || ''; gate.classList.add('hidden'); app.classList.remove('hidden'); @@ -173,6 +198,7 @@ } } catch (e2) {} } + clearAdminRoleHint(); clearAdminAuthLocalStorage(); try { sb.auth.signOut({ scope: 'global' }); diff --git a/assets/js/espace-dashboard.mjs b/assets/js/espace-dashboard.mjs index ea811c7..75bb37e 100644 --- a/assets/js/espace-dashboard.mjs +++ b/assets/js/espace-dashboard.mjs @@ -21,10 +21,25 @@ const sb = : null; const $ = (id) => document.getElementById(id); +const SA_ROLE_HINT_KEY = 'sa_role_hint_v1'; const escapeHtml = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +function writeAdminRoleHint(userId, isAdmin) { + if (typeof localStorage === 'undefined' || !userId) return; + try { + localStorage.setItem( + SA_ROLE_HINT_KEY, + JSON.stringify({ + user_id: String(userId), + is_admin: isAdmin === true, + updated_at: Date.now(), + }) + ); + } catch (_e) {} +} + function fmtDate(d) { if (!d) return ''; try { @@ -237,9 +252,11 @@ async function boot() { try { const { data: isAdm, error: admErr } = await sb.rpc('is_admin'); if (!admErr && isAdm === true) { + writeAdminRoleHint(session.user.id, true); window.location.replace('/admin'); return; } + if (!admErr) writeAdminRoleHint(session.user.id, false); } catch (_e) { /* reste sur l'espace personnel */ } diff --git a/assets/js/espace-etudiant.mjs b/assets/js/espace-etudiant.mjs index 19e2bd4..ded1486 100644 --- a/assets/js/espace-etudiant.mjs +++ b/assets/js/espace-etudiant.mjs @@ -45,6 +45,29 @@ function clearAllGoTrueAuthLocalStorage() { } catch (e) {} } +var SA_ROLE_HINT_KEY = 'sa_role_hint_v1'; + +function writeAdminRoleHint(userId, isAdmin) { + if (typeof localStorage === 'undefined' || !userId) return; + try { + localStorage.setItem( + SA_ROLE_HINT_KEY, + JSON.stringify({ + user_id: String(userId), + is_admin: isAdmin === true, + updated_at: Date.now(), + }) + ); + } catch (e) {} +} + +function clearAdminRoleHint() { + if (typeof localStorage === 'undefined') return; + try { + localStorage.removeItem(SA_ROLE_HINT_KEY); + } catch (e) {} +} + /* Après navigation post-déconnexion : purge synchrone AVANT createClient(), sinon la session peut être relue depuis localStorage encore présent. */ (function espaceEarlyPostLogoutCleanup() { @@ -80,22 +103,28 @@ function redirectAfterAuth(sb) { home = window.location.origin + '/'; } } catch (e0) {} + var user = null; return sb.auth .getSession() .then(function (sessRes) { - var u = sessRes && sessRes.data && sessRes.data.session && sessRes.data.session.user; - if (u) return syncUserSiteContextRow(sb, u); - return Promise.resolve(); - }) - .then(function () { + user = sessRes && sessRes.data && sessRes.data.session && sessRes.data.session.user; return sb.rpc('is_admin'); }) .then(function (a) { if (!a.error && a.data === true) { + writeAdminRoleHint(user && user.id, true); window.location.replace('/admin'); - } else { - window.location.replace(home); + return true; + } + if (!a.error && user && user.id) { + writeAdminRoleHint(user.id, false); } + if (user) return syncUserSiteContextRow(sb, user).then(function () { return false; }); + return false; + }) + .then(function (adminRedirected) { + if (adminRedirected === true) return; + window.location.replace(home); }) .catch(function () { window.location.replace(home); @@ -817,26 +846,42 @@ if (pageId === 'dashboard') { return; } var u = s.user; - var um = u.user_metadata || {}; - var persona = um.espace_persona; - if (!persona || ['cameroun', 'belgique_etudiant', 'travailleur', 'visiteur'].indexOf(persona) === -1) { - persona = null; - try { - var ps = sessionStorage.getItem('sa_espace_persona'); - if (ps && ['cameroun', 'belgique_etudiant', 'travailleur', 'visiteur'].indexOf(ps) !== -1) { - persona = ps; + var showStudentIdentity = function () { + var um = u.user_metadata || {}; + var persona = um.espace_persona; + if (!persona || ['cameroun', 'belgique_etudiant', 'travailleur', 'visiteur'].indexOf(persona) === -1) { + persona = null; + try { + var ps = sessionStorage.getItem('sa_espace_persona'); + if (ps && ['cameroun', 'belgique_etudiant', 'travailleur', 'visiteur'].indexOf(ps) !== -1) { + persona = ps; + } + } catch (ePs) {} + if (!persona) persona = 'cameroun'; + } + var meta = um.full_name || ''; + var display = meta.trim(); + if (!display) { + if (persona === 'visiteur') display = 'Invité(e)'; + else display = 'Membre'; + } + if (nameEl) nameEl.textContent = display; + if (emailEl) emailEl.textContent = u.email || ''; + }; + + sb.rpc('is_admin') + .then(function (a) { + if (!a.error && a.data === true) { + writeAdminRoleHint(u.id, true); + window.location.replace('/admin'); + return; } - } catch (ePs) {} - if (!persona) persona = 'cameroun'; - } - var meta = um.full_name || ''; - var display = meta.trim(); - if (!display) { - if (persona === 'visiteur') display = 'Invité(e)'; - else display = 'Membre'; - } - if (nameEl) nameEl.textContent = display; - if (emailEl) emailEl.textContent = u.email || ''; + if (!a.error) writeAdminRoleHint(u.id, false); + showStudentIdentity(); + }) + .catch(function () { + showStudentIdentity(); + }); }); } @@ -866,6 +911,7 @@ if (pageId === 'dashboard') { } }) .then(function () { + clearAdminRoleHint(); clearSupabaseAuthStorageForUrl(getConfig().SUPABASE_URL); clearAllGoTrueAuthLocalStorage(); try { diff --git a/assets/js/sa-nav-session.js b/assets/js/sa-nav-session.js index 255111d..1b22d0f 100644 --- a/assets/js/sa-nav-session.js +++ b/assets/js/sa-nav-session.js @@ -56,6 +56,33 @@ return getPersistedSupabaseAuth() != null; } +var SA_ROLE_HINT_KEY = 'sa_role_hint_v1'; + +function readRoleHint() { + if (typeof localStorage === 'undefined') return null; + try { + var raw = localStorage.getItem(SA_ROLE_HINT_KEY); + if (!raw) return null; + var parsed = JSON.parse(raw); + if (!parsed || typeof parsed !== 'object') return null; + return parsed; + } catch (e) { + return null; + } +} + +function isAdminHintForUser(user) { + if (!user || !user.id) return false; + var hint = readRoleHint(); + if (!hint) return false; + return hint.user_id === user.id && hint.is_admin === true; +} + +function resolveDashboardHref(defaultHref, auth) { + if (auth && isAdminHintForUser(auth.user)) return '/admin'; + return defaultHref; +} + function escapeHtml(s) { return String(s) .replace(/&/g, '&') @@ -139,9 +166,10 @@ document.querySelectorAll('[data-sa-profile-slot]').forEach(function (slot) { var href = slot.getAttribute('data-sa-dashboard-href'); if (!href) return; + var displayHref = resolveDashboardHref(href, auth); var variant = slot.getAttribute('data-sa-profile-variant') || 'desktop'; if (show) { - slot.innerHTML = buildLinkHtml(href, variant, displayLabel); + slot.innerHTML = buildLinkHtml(displayHref, variant, displayLabel); if (variant === 'desktop') { slot.className = 'lg:flex items-center min-w-0'; } else if (variant === 'mobile') {