Ne plus afficher une coquille vide quand un appel échoue à la connexion

Un échec pendant onLogin (chargement des sociétés) interrompait le rendu
avant la navigation et le tableau de bord : l'utilisateur arrivait sur une
interface vide, et le message d'erreur était écrit dans l'encart du
formulaire de connexion qui venait d'être masqué — donc invisible.

- onLogin isole le chargement des sociétés : la navigation et le tableau
  de bord s'affichent toujours, l'échec est signalé par un toast.
- Les erreurs survenant après la bascule vers l'application sont
  rapportées en toast et non plus dans l'encart de connexion masqué.
- boot() ne renvoie vers l'écran de connexion que si la session elle-même
  est invalide, au lieu d'avaler toute erreur de chargement.
- Le tableau de bord admin tolère un backend plus ancien que la page : un
  404 sur /validations/overview affiche « reconstruisez aussi l'image du
  backend » et un tableau de bord réduit au lieu de rester vide.

Vérifié : les deux scénarios de panne rendus visibles, et la suite E2E
complète toujours à 62/62.
This commit is contained in:
Claude committed 2026-08-03 18:08:56 +00:00
1 parent 626b46d9d6
commit 8693bf15b5
1 file changed
+53 -6
+53 -6
View File
@@ -81,12 +81,20 @@
// ── auth ────────────────────────────────────────────────────────────────
async function boot() {
let me;
try {
const me = await api('/auth/me');
await onLogin(me);
me = await api('/auth/me');
} catch (e) {
// No usable session — show the login screen. Any other failure below
// must not bounce the user back here.
$('view-login').hidden = false;
$('app').hidden = true;
return;
}
try {
await onLogin(me);
} catch (err) {
toast(`Erreur au chargement de l’application : ${err.message}`, 'error');
}
}
@@ -100,7 +108,18 @@
$('user-meta').textContent = user.role === 'admin'
? 'Administrateur'
: `Cadre · ${user.companyName || '—'}`;
if (user.role === 'admin') await loadCompanies();
// The company list is only needed to populate selects. If it fails, the
// app must still come up with a usable navigation and a visible error —
// never a blank shell.
if (user.role === 'admin') {
try {
await loadCompanies();
} catch (err) {
state.companies = [];
toast(`Chargement des sociétés impossible : ${err.message}`, 'error');
}
}
renderNav();
await go('dashboard');
}
@@ -109,15 +128,23 @@
e.preventDefault();
const errBox = $('login-error');
errBox.hidden = true;
let user;
try {
const user = await api('/auth/login', {
user = await api('/auth/login', {
method: 'POST',
body: { email: $('login-email').value.trim(), password: $('login-password').value },
});
await onLogin(user);
} catch (err) {
errBox.textContent = err.message || 'Connexion impossible';
errBox.hidden = false;
return;
}
// Past this point the login screen is gone, so its error box would be
// invisible: report anything that goes wrong as a toast instead.
try {
await onLogin(user);
} catch (err) {
toast(`Erreur au chargement de l’application : ${err.message}`, 'error');
}
});
@@ -227,7 +254,27 @@
const workHalves = workdayHalves();
if (isAdmin) {
const ov = await api(`/validations/overview?year=${state.y}&month=${state.m}`);
let ov;
try {
ov = await api(`/validations/overview?year=${state.y}&month=${state.m}`);
} catch (err) {
// A 404 here means the API is older than this page (backend image not
// rebuilt). Say so plainly rather than leaving the dashboard blank.
if (err.status === 404) {
toast('API incomplète : reconstruisez aussi l’image du backend.', 'error');
} else {
toast(err.message, 'error');
}
$('dash-subtitle').textContent = `Vue d'ensemble de ${monthLabel()} indisponible.`;
tiles([{ label: 'Ma saisie', value: `${counts.filled}/${workHalves}`, hint: 'demi-journées ouvrées' }]);
rowActions([{
icon: 'ph-calendar-dots', title: 'Compléter mon propre planning',
detail: `${Math.max(0, workHalves - counts.filled)} demi-journées manquantes`, tag: 'Saisie',
onClick: () => go('planning'),
}]);
renderCompanyMonthRows([]);
return;
}
setNavBadge('validation', ov.pendingCompanies || 0);
$('dash-subtitle').textContent =
`Vue d'ensemble de ${monthLabel()} — ${ov.companies.length} société(s), ${ov.totalActiveCadres} cadre(s).`;