Écran unique « Synthèse Kms » fusionnant résumés et montants réglés

- Nouvelle page Synthèse Kms (/synthese-kms) : sélecteur temporel en
  mode année seule + véhicule + Afficher, barème appliqué, tableau 12
  mois (km, remb. réel du mois, indication 80%, montant réglé
  saisissable, export PDF mensuel), ligne TOTAL (km annuel, montant
  réel, total réglé, export PDF annuel), bouton Enregistrer global
  (seuls les montants modifiés), auto-affichage à l'arrivée. Aucun
  nouvel endpoint (composition d'endpoints existants).
- Suppression des écrans « Résumé mensuel », « Résumé annuel » et
  « Montants réglés » (routes et liens de menu retirés).
- SelecteurTemporelComponent : mode « année seule » (Input/Output
  dédiés) et repli automatique au clic sur le niveau le plus bas.
- Pleine largeur des écrans Trajets, Carnet d'adresses et Synthèse Kms
  (Specs conserve sa largeur de lecture).
- Liste des trajets : boutons d'action déplacés en début de ligne
  (après la poignée, avant A/R), alignés à droite.
- Specs (requirements/design-requirements/design) synchronisées :
  glossaire Synthèse_Kms, Exigences 3/4/13/14 adaptées, critères 12.25-27.
This commit is contained in:
2026-10-10 15:46:34 +02:00
parent e9f34e59e8
commit 3d9557f852
22 changed files with 706 additions and 1177 deletions
@@ -55,9 +55,19 @@ export class SelecteurTemporelComponent implements OnInit, OnChanges {
*/
@Input() date: string | null = null;
/** Émis à chaque changement de sélection (année + mois). */
/**
* Mode « année seule » : masque la grille des mois et limite le sélecteur au
* choix d'une année. La sélection d'une année émet alors `anneeSelectionnee`
* (et non `moisSelectionne`). Par défaut faux (mode année + mois).
*/
@Input() modeAnneeSeule = false;
/** Émis à chaque changement de sélection (année + mois) — mode année + mois. */
@Output() readonly moisSelectionne = new EventEmitter<MoisSelectionne>();
/** Émis à chaque sélection d'année — mode « année seule ». */
@Output() readonly anneeChoisie = new EventEmitter<number>();
/** Libellés des douze mois (index 0 = janvier). */
readonly nomsMois = [
'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
@@ -87,6 +97,9 @@ export class SelecteurTemporelComponent implements OnInit, OnChanges {
*/
get libelleMoisAnnee(): string {
const annee = this.anneeSelectionnee();
if (this.modeAnneeSeule) {
return annee == null ? '' : `${annee}`;
}
const mois = this.moisSelectionneSignal();
if (annee == null || mois == null) {
return '';
@@ -94,6 +107,11 @@ export class SelecteurTemporelComponent implements OnInit, OnChanges {
return `${this.nomsMois[mois - 1]} ${annee}`;
}
/** Libellé du bouton d'en-tête, selon le mode. */
get libelleEntete(): string {
return this.modeAnneeSeule ? '📅 Choisir une année' : '📅 Choisir un mois';
}
/** Bascule l'ouverture / fermeture du sélecteur repliable. */
basculerOuverture(): void {
this.ouvert.update((v) => !v);
@@ -158,6 +176,14 @@ export class SelecteurTemporelComponent implements OnInit, OnChanges {
this.moisSelectionneSignal.set(null);
this.moisAvecDonnees.set(new Set<number>());
// Mode « année seule » : l'année est le niveau le plus bas → on émet et on
// replie automatiquement le sélecteur. Pas de chargement des mois (non affichés).
if (this.modeAnneeSeule) {
this.anneeChoisie.emit(annee);
this.ouvert.set(false);
return;
}
this.trajetService.getMoisDisponibles(annee).subscribe((mois) => {
this.moisAvecDonnees.set(new Set<number>(mois));
});
@@ -171,6 +197,8 @@ export class SelecteurTemporelComponent implements OnInit, OnChanges {
}
this.moisSelectionneSignal.set(mois);
this.moisSelectionne.emit({ annee, mois });
// Le mois est le niveau le plus bas en mode année + mois → repli automatique.
this.ouvert.set(false);
}
/** Un mois est en surbrillance s'il comporte au moins un trajet. */