Corrections UX : première connexion, carte Leaflet en prod, mise en page

- Onboarding : redirection automatique vers /register lorsqu'aucun compte n'existe
  (première utilisation), au lieu d'afficher la page de connexion.
- Carte Leaflet en production : import normalisé (Leaflet est CommonJS, l'API pouvait
  se retrouver sous .default en build prod -> échec d'initialisation). Nettoyage du
  marqueur _leaflet_id du conteneur pour que « Réessayer » fonctionne après un échec.
  Logs d'erreur explicites en cas d'échec d'import/init.
- Mise en page : layout applicatif en flex pleine hauteur (nav de hauteur naturelle,
  <main> occupe le reste et défile si besoin) sans étirer les vues courtes. La vue
  Trajets occupe l'espace restant (height:100%) sans scrollbar de page : seule la
  liste défile. Reset global (body margin 0, box-sizing border-box) supprimant la
  scrollbar de page parasite.
- Centrage uniforme des pages : ajout de margin:0 auto aux vues profil, véhicules,
  barème, résumé mensuel/annuel et montants réglés (comme trajets et carnet).

Builds backend (solution) et frontend (production) OK.
This commit is contained in:
2026-09-15 19:53:16 +02:00
parent 8d3528df37
commit f3465e04d2
11 changed files with 56 additions and 17 deletions
+5 -13
View File
@@ -1,12 +1,12 @@
:host { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100dvh;
min-height: 0; min-height: 0;
} }
.nav-bar { .nav-bar {
flex: 0 0 auto; flex: 0 0 auto; // hauteur naturelle (même si les liens passent sur plusieurs lignes)
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
@@ -37,18 +37,10 @@
} }
} }
/* Le contenu routé occupe la hauteur restante sous le menu et défile si nécessaire.
/* Le contenu routé occupe toute la hauteur restante sous le menu. Les vues courtes gardent leur hauteur naturelle (aucun flex:1 imposé aux enfants). */
Les vues qui le souhaitent (ex. consultation) peuvent ainsi figer leur mise en page
et ne faire défiler qu'une sous-zone interne. */
main { main {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
display: flex; overflow-y: auto;
flex-direction: column;
}
main > * {
flex: 1 1 auto;
min-height: 0;
} }
@@ -56,7 +56,14 @@ export class LoginComponent implements OnInit {
}); });
this.authService.compteExiste().subscribe({ this.authService.compteExiste().subscribe({
next: (resultat) => this.compteExiste.set(resultat.existe), next: (resultat) => {
this.compteExiste.set(resultat.existe);
// Première utilisation (aucun compte) : on va directement à l'inscription
// pour simplifier le parcours, plutôt que d'afficher la connexion.
if (!resultat.existe) {
this.router.navigate(['/register']);
}
},
// En cas d'échec, on conserve la valeur par défaut (masque l'inscription). // En cas d'échec, on conserve la valeur par défaut (masque l'inscription).
error: () => this.compteExiste.set(true) error: () => this.compteExiste.set(true)
}); });
@@ -1,6 +1,7 @@
.bareme { .bareme {
padding: 1.5rem; padding: 1.5rem;
max-width: 960px; max-width: 960px;
margin: 0 auto;
} }
h1 { h1 {
@@ -158,9 +158,18 @@ export class CarteTrajetsComponent implements AfterViewInit, OnChanges, OnDestro
try { try {
if (!this.L) { if (!this.L) {
this.L = await import('leaflet'); const mod = await import('leaflet');
// Leaflet est un module CommonJS : selon l'interop du bundler (dev vs prod),
// l'API peut se trouver sur le module lui-même ou sous `default`. On normalise.
const candidate = mod as unknown as { default?: typeof import('leaflet') };
const api = candidate.default ?? (mod as typeof import('leaflet'));
// Vérifie que l'API attendue est présente (sinon l'init échouerait silencieusement).
this.L = (api && typeof (api as { map?: unknown }).map === 'function')
? api
: (mod as typeof import('leaflet'));
} }
} catch { } catch (e) {
console.error('Échec du chargement de Leaflet :', e);
this.basculerErreur('La librairie de carte n\'a pas pu être chargée.'); this.basculerErreur('La librairie de carte n\'a pas pu être chargée.');
return; return;
} }
@@ -188,7 +197,8 @@ export class CarteTrajetsComponent implements AfterViewInit, OnChanges, OnDestro
this.tileLayer.addTo(this.map); this.tileLayer.addTo(this.map);
this.layerGroup = L.layerGroup().addTo(this.map); this.layerGroup = L.layerGroup().addTo(this.map);
} catch { } catch (e) {
console.error('Échec de l\'initialisation de la carte Leaflet :', e);
this.basculerErreur('La carte n\'a pas pu être initialisée.'); this.basculerErreur('La carte n\'a pas pu être initialisée.');
return; return;
} }
@@ -446,5 +456,15 @@ export class CarteTrajetsComponent implements AfterViewInit, OnChanges, OnDestro
this.map.remove(); this.map.remove();
this.map = null; this.map = null;
} }
// Filet de sécurité : si une initialisation précédente a échoué APRÈS que Leaflet
// ait marqué le conteneur (`_leaflet_id`) mais AVANT l'assignation de `this.map`,
// le node reste « déjà initialisé » et un nouvel appel à `L.map()` lèverait
// « Map container is already initialized » (Réessayer resterait alors sans effet).
// On nettoie donc explicitement le marqueur et le contenu résiduel du conteneur.
const node = this.mapContainer?.nativeElement as (HTMLElement & { _leaflet_id?: number }) | undefined;
if (node) {
delete node._leaflet_id;
node.innerHTML = '';
}
} }
} }
@@ -1,7 +1,11 @@
:host { :host {
display: block; display: block;
// La vue occupe toute la hauteur de <main> (qui vaut le viewport moins la barre de
// navigation, quelle que soit sa hauteur réelle grâce au layout flex global).
// overflow:hidden : aucune scrollbar de page ; seule la sous-zone liste défile.
height: 100%; height: 100%;
min-height: 0; min-height: 0;
overflow: hidden;
} }
.consultation { .consultation {
@@ -1,6 +1,7 @@
.montants-regles { .montants-regles {
padding: 1.5rem; padding: 1.5rem;
max-width: 720px; max-width: 720px;
margin: 0 auto;
} }
h1 { h1 {
@@ -1,6 +1,7 @@
.profil { .profil {
padding: 1.5rem; padding: 1.5rem;
max-width: 640px; max-width: 640px;
margin: 0 auto;
} }
h1 { h1 {
@@ -1,6 +1,7 @@
.resume-annuel { .resume-annuel {
padding: 1.5rem; padding: 1.5rem;
max-width: 640px; max-width: 640px;
margin: 0 auto;
} }
h1 { h1 {
@@ -1,6 +1,7 @@
.resume-mensuel { .resume-mensuel {
padding: 1.5rem; padding: 1.5rem;
max-width: 900px; max-width: 900px;
margin: 0 auto;
} }
h1 { h1 {
@@ -1,6 +1,7 @@
.vehicules { .vehicules {
padding: 1.5rem; padding: 1.5rem;
max-width: 720px; max-width: 720px;
margin: 0 auto;
} }
h1 { h1 {
+10
View File
@@ -1,5 +1,15 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
/* Reset global minimal : supprime la marge par défaut du body (sinon 100dvh + 8px
provoque une scrollbar de page parasite) et fixe une base de rendu cohérente. */
html, body {
margin: 0;
padding: 0;
}
*, *::before, *::after {
box-sizing: border-box;
}
/* Styles globaux des éléments de carte injectés par Leaflet (hors encapsulation /* Styles globaux des éléments de carte injectés par Leaflet (hors encapsulation
des composants) : pastilles de marqueurs et contenu des infobulles. */ des composants) : pastilles de marqueurs et contenu des infobulles. */
.carte-marqueur { .carte-marqueur {