Saisie/insertion : date de journée, repositionnement, point A/R, retour domicile

Améliorations de saisie issues du retour d'usage :

- Insertion : la date pré-remplie est celle de la JOURNÉE du trajet source (via
  prefill.date), et non la date de la carte, pour éviter les erreurs de saisie.
- Insertion « après » / « avant le suivant » : l'adresse de départ pré-remplie
  est le point de FIN du trajet source — son arrivée, ou son DÉPART si c'est un
  aller/retour (le véhicule est revenu à son point d'origine).
- Après création/modification sur une autre journée : repositionnement auto sur
  le mois du trajet, cadrage de la carte sur sa date, et dépliage du groupe de
  cette journée (TrajetListComponent.ouvrirJour, différé après recalcul du
  repliage) pour rendre le trajet ajouté visible.
- Bouton « Utiliser mon domicile » sur l'arrivée : pré-remplit le commentaire
  avec « Retour domicile » si le commentaire est vide (jamais de remplacement).

Specs : requirements.md (exigences 10.19, 11.1/11.2/11.12, 12.23) et design.md
(TrajetForm, TrajetList, ConsultationJour) mis à jour.
This commit is contained in:
2026-09-20 20:16:30 +02:00
parent e84c2356f1
commit 173b123f03
6 changed files with 112 additions and 18 deletions
@@ -107,7 +107,7 @@
<app-trajet-form
[trajet]="f.trajet"
[vehiculeId]="vehiculeId()"
(updated)="onFormEnregistre()"
(updated)="onFormEnregistre($event)"
(cancelled)="fermerFormulaire()"
></app-trajet-form>
}
@@ -116,7 +116,7 @@
[prefill]="f.prefill"
[vehiculeId]="vehiculeId()"
[dateParDefaut]="date()"
(created)="onFormEnregistre()"
(created)="onFormEnregistre($event)"
(cancelled)="fermerFormulaire()"
></app-trajet-form>
}
@@ -124,7 +124,7 @@
<app-trajet-form
[vehiculeId]="vehiculeId()"
[dateParDefaut]="date()"
(created)="onFormEnregistre()"
(created)="onFormEnregistre($event)"
(cancelled)="fermerFormulaire()"
></app-trajet-form>
}
@@ -71,6 +71,9 @@ export class ConsultationJourComponent implements OnInit, OnDestroy {
/** Référence au panneau des totaux, pour forcer son rafraîchissement à la même journée. */
@ViewChild(PanneauTotauxComponent) private panneauTotaux?: PanneauTotauxComponent;
/** Référence à la liste, pour déplier la journée d'un trajet ajouté après rechargement. */
@ViewChild(TrajetListComponent) private trajetList?: TrajetListComponent;
/**
* Mois consulté `{ annee, mois }` : pilote le chargement mensuel de la liste et
* la synchronisation du sélecteur temporel. `null` tant qu'aucun mois n'est choisi.
@@ -115,6 +118,13 @@ export class ConsultationJourComponent implements OnInit, OnDestroy {
*/
private recalerJourApresChargement = false;
/**
* Date (`yyyy-MM-dd`) de la journée à déplier après le prochain chargement de la
* liste : renseignée après l'ajout/insertion d'un trajet pour rendre visible la
* journée concernée. `null` si aucune ouverture n'est demandée.
*/
private jourAOuvrirApresChargement: string | null = null;
ngOnInit(): void {
// Par défaut : mois courant, et jour = aujourd'hui (contenu dans le mois courant).
const auj = new Date();
@@ -276,9 +286,28 @@ export class ConsultationJourComponent implements OnInit, OnDestroy {
}
}
/** Un trajet a été créé ou modifié : ferme le formulaire et recharge la journée. */
onFormEnregistre(): void {
/**
* Un trajet a été créé ou modifié : ferme le formulaire, se repositionne sur la
* journée du trajet (mois + date de la carte) si elle diffère de l'affichage
* courant, recharge la liste puis déplie la journée concernée pour la rendre visible.
*/
onFormEnregistre(trajet?: Trajet): void {
this.formulaire.set(null);
const dateTrajet = (trajet?.date ?? '').slice(0, 10);
if (dateTrajet) {
const [annee, mois] = dateTrajet.split('-').map(Number);
const moisCourant = this.mois();
// Repositionne le mois si le trajet appartient à un autre mois.
if (!moisCourant || moisCourant.annee !== annee || moisCourant.mois !== mois) {
this.mois.set({ annee, mois });
}
// La carte et la date par défaut suivent la journée du trajet ajouté.
this.date.set(dateTrajet);
// Déplie cette journée une fois la liste rechargée.
this.jourAOuvrirApresChargement = dateTrajet;
}
this.rafraichirJournee();
}
@@ -364,6 +393,15 @@ export class ConsultationJourComponent implements OnInit, OnDestroy {
// par la liste elle-même.
this.trajets.set(resume.trajets ?? []);
// Déplie la journée d'un trajet qu'on vient d'ajouter/insérer. On diffère
// au tick suivant : le setter `trajets` de la liste recalcule l'état de
// repliage (au changement de mois), donc on ouvre APRÈS ce recalcul.
if (this.jourAOuvrirApresChargement) {
const jour = this.jourAOuvrirApresChargement;
this.jourAOuvrirApresChargement = null;
setTimeout(() => this.trajetList?.ouvrirJour(jour), 0);
}
// Recalage éventuel sur un jour ayant des trajets (après sélection d'un
// mois). Si la date par défaut n'est pas navigable (journée vide d'un mois
// passé), on cible le dernier jour du mois comportant des trajets.
@@ -67,6 +67,12 @@ export interface TrajetInsertionPrefill {
adresseDepart?: string | null;
adresseArrivee?: string | null;
vehiculeId?: number | null;
/**
* Date (ISO `yyyy-MM-dd`) de la journée du trajet à partir duquel on insère.
* En mode insertion, elle prime sur `dateParDefaut` (date de la carte) afin que
* le nouveau trajet soit rattaché à la BONNE journée (Exigences 11.1–11.4).
*/
date?: string | null;
}
/** Identifie lequel des deux champs adresse est concerné. */
@@ -400,8 +406,14 @@ export class TrajetFormComponent implements OnInit, OnDestroy {
return;
}
// Création / insertion : pré-remplit la date avec la journée affichée si fournie.
const dateDefaut = this.versIsoInput(this.dateParDefaut ?? '');
// Création / insertion : pré-remplit la date. En mode insertion, la date de la
// JOURNÉE du trajet concerné (prefill.date) prime sur la date de la carte
// (dateParDefaut), afin de rattacher le nouveau trajet à la bonne journée.
const dateSource =
this.mode === 'insert' && this.prefill?.date
? this.prefill.date
: (this.dateParDefaut ?? '');
const dateDefaut = this.versIsoInput(dateSource);
if (dateDefaut) {
this.form.patchValue({ date: dateDefaut });
}
@@ -511,6 +523,13 @@ export class TrajetFormComponent implements OnInit, OnDestroy {
this.coordArrivee.set(null);
this.sourceFiableArrivee.set(true);
this.suggestionsArrivee.set([]);
// Confort de saisie : un trajet dont l'arrivée est le domicile est typiquement
// un retour. On pré-remplit le commentaire avec « Retour domicile » s'il est
// encore vide (on ne remplace jamais un commentaire déjà saisi).
if (!(this.commentaireCtrl.value ?? '').trim()) {
this.commentaireCtrl.setValue('Retour domicile');
this.commentaireCtrl.markAsDirty();
}
}
}
@@ -230,6 +230,23 @@ export class TrajetListComponent {
});
}
/**
* Déplie explicitement la journée `dateIso` (`yyyy-MM-dd`), sans toucher aux autres.
* Appelé par le conteneur après l'ajout/insertion d'un trajet pour rendre la journée
* concernée visible. Sans effet si la date n'est pas dans le mois affiché.
*/
ouvrirJour(dateIso: string): void {
const cle = (dateIso ?? '').slice(0, 10);
if (!cle) {
return;
}
this.joursOuverts.update((ouverts) => {
const suivant = new Set(ouverts);
suivant.add(cle);
return suivant;
});
}
// --- Helpers d'affichage ---
/** Vrai si le trajet est le dernier DE SA JOURNÉE (pas d'« insérer avant le suivant »). */
@@ -263,14 +280,22 @@ export class TrajetListComponent {
}
/**
* Insérer après le trajet courant : pré-remplit l'adresse de départ avec
* l'adresse d'arrivée du trajet courant (Exigence 11.1/11.2).
* Insérer après le trajet courant : pré-remplit l'adresse de départ avec le point
* où l'on se trouve à la fin de ce trajet (Exigence 11.1/11.2).
*
* - Trajet simple : on se trouve à l'ADRESSE D'ARRIVÉE → départ pré-rempli = arrivée.
* - Trajet aller/retour : le véhicule est revenu à son point de départ → départ
* pré-rempli = ADRESSE DE DÉPART du trajet (et non son arrivée).
*/
insererApres(trajet: Trajet): void {
const pointDeFin = trajet.allerRetour ? trajet.adresseDepart : trajet.adresseArrivee;
this.insertAfter.emit({
prefill: {
adresseDepart: trajet.adresseArrivee,
vehiculeId: trajet.vehiculeId
adresseDepart: pointDeFin,
vehiculeId: trajet.vehiculeId,
// Rattache l'insertion à la JOURNÉE du trajet concerné (et non à la date de
// la carte), pour éviter les erreurs de saisie de date.
date: (trajet.date ?? '').slice(0, 10)
},
positionApres: trajet.position
});
@@ -287,11 +312,17 @@ export class TrajetListComponent {
if (!courant || !suivant) {
return;
}
// Point de fin du trajet courant : son arrivée, ou son départ s'il est aller/retour
// (le véhicule est alors revenu à son point d'origine).
const finCourant = courant.allerRetour ? courant.adresseDepart : courant.adresseArrivee;
this.insertBetween.emit({
prefill: {
adresseDepart: courant.adresseArrivee,
adresseDepart: finCourant,
// Le trajet suivant part toujours de son adresse de départ (indépendant de l'A/R).
adresseArrivee: suivant.adresseDepart,
vehiculeId: courant.vehiculeId
vehiculeId: courant.vehiculeId,
// Journée du trajet courant (insertion au sein de la même journée).
date: (courant.date ?? '').slice(0, 10)
},
positionApres: courant.position,
positionAvant: suivant.position