Connexion « Se souvenir de moi » et ajustements panneau Totaux
- Login : case à cocher « Se souvenir de moi » sous le champ email ; mémorisation de l'identifiant (email uniquement) dans le localStorage du navigateur, pré-remplissage à la visite suivante sur le même navigateur, effacement si décochée ; erreurs de stockage ignorées - Panneau Totaux : « Total du mois » en gras et « Total annuel partiel » non gras ; « Remboursement indicatif » renommé « Remboursement indicatif pour ce mois » ; « Montant prévisionnel annuel » non gras - Steering : nouvelle règle imposant les réponses en français - Mise à jour requirements.md (Req 17 + glossaire), design-requirements.md (Req 17 technique) et design.md (LoginComponent, PanneauTotaux)
This commit is contained in:
@@ -236,6 +236,11 @@ Le document est organisé **en miroir** des exigences de `requirements.md` : cha
|
|||||||
6. IF l'envoi de l'e-mail de réinitialisation n'est pas possible (service e-mail non configuré ou indisponible), THEN THE Backend SHALL journaliser le lien de réinitialisation en repli (fallback) afin que la réinitialisation reste possible, sans faire échouer la demande.
|
6. IF l'envoi de l'e-mail de réinitialisation n'est pas possible (service e-mail non configuré ou indisponible), THEN THE Backend SHALL journaliser le lien de réinitialisation en repli (fallback) afin que la réinitialisation reste possible, sans faire échouer la demande.
|
||||||
7. WHEN l'utilisateur suit un Jeton_Reinitialisation valide et fournit un nouveau Mot_De_Passe valide, THE Backend SHALL remplacer le hachage du Mot_De_Passe du Compte et invalider le Jeton_Reinitialisation.
|
7. WHEN l'utilisateur suit un Jeton_Reinitialisation valide et fournit un nouveau Mot_De_Passe valide, THE Backend SHALL remplacer le hachage du Mot_De_Passe du Compte et invalider le Jeton_Reinitialisation.
|
||||||
8. THE Backend SHALL exiger la présence d'une clé secrète de signature JWT au démarrage (variable d'environnement obligatoire) et SHALL refuser de démarrer si elle est absente.
|
8. THE Backend SHALL exiger la présence d'une clé secrète de signature JWT au démarrage (variable d'environnement obligatoire) et SHALL refuser de démarrer si elle est absente.
|
||||||
|
9. WHEN la case « Se souvenir de moi » est cochée lors d'une connexion réussie, THE LoginComponent SHALL écrire l'Adresse_Email saisie dans le `localStorage` du navigateur sous la clé `schotziad.login.emailMemorise` via `setItem`, et SHALL n'y stocker que l'Adresse_Email (jamais le Mot_De_Passe).
|
||||||
|
10. WHEN la case « Se souvenir de moi » est décochée lors d'une connexion réussie, THE LoginComponent SHALL supprimer la clé `schotziad.login.emailMemorise` du `localStorage` du navigateur via `removeItem`.
|
||||||
|
11. WHEN le LoginComponent est initialisé (`ngOnInit`), IF la clé `schotziad.login.emailMemorise` est présente et non vide dans le `localStorage`, THEN THE LoginComponent SHALL pré-remplir le champ Adresse_Email avec cette valeur et cocher la case « Se souvenir de moi ».
|
||||||
|
12. WHERE l'Identifiant_Mémorisé est conservé dans le `localStorage` du navigateur, THE LoginComponent SHALL s'appuyer sur la portée propre du `localStorage` (limitée à l'origine — scheme, host et port — et au navigateur courant) pour réaliser l'absence de pré-remplissage sur une autre machine ou un autre navigateur tant qu'aucune connexion « se souvenir » n'y a eu lieu.
|
||||||
|
13. IF un accès au `localStorage` échoue (navigation privée, quota dépassé ou stockage désactivé), THEN THE LoginComponent SHALL intercepter l'erreur et l'ignorer silencieusement, la mémorisation de l'Identifiant_Mémorisé restant sans effet sans faire échouer la connexion ni le chargement de la page de connexion.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -328,8 +328,9 @@ src/
|
|||||||
- Affiche trois totaux : total du jour ciblé par la navigation, total du mois sélectionné, Total_Annuel_Partiel (cumul de l'année jusqu'au mois sélectionné inclus)
|
- Affiche trois totaux : total du jour ciblé par la navigation, total du mois sélectionné, Total_Annuel_Partiel (cumul de l'année jusqu'au mois sélectionné inclus)
|
||||||
- Reçoit en entrée la date sélectionnée et recalcule les totaux à chaque changement
|
- Reçoit en entrée la date sélectionnée et recalcule les totaux à chaque changement
|
||||||
- Tous les totaux affichés à 2 décimales, calculés par sommation de distances déjà arrondies selon Precision_Distance (voir spec `precision-distances-km`, Requirement 5)
|
- Tous les totaux affichés à 2 décimales, calculés par sommation de distances déjà arrondies selon Precision_Distance (voir spec `precision-distances-km`, Requirement 5)
|
||||||
- Affiche le libellé **« Remboursement indicatif »** (renommé, anciennement « Indication de versement (80%) ») présentant le Montant_Previsionnel_Mensuel (80%, calculé sur le seul mois affiché) et, en dessous, une ligne « Montant prévisionnel annuel » présentant le Montant_Previsionnel_Annuel (100%, sur le Total_Annuel_Partiel) — détail complet du calcul dans la spec `precision-distances-km`, Requirements 7 et 8
|
- Affiche le libellé **« Remboursement indicatif pour ce mois »** (renommé, anciennement « Indication de versement (80%) ») présentant le Montant_Previsionnel_Mensuel (80%, calculé sur le seul mois affiché) et, en dessous, une ligne « Montant prévisionnel annuel » présentant le Montant_Previsionnel_Annuel (100%, sur le Total_Annuel_Partiel) — détail complet du calcul dans la spec `precision-distances-km`, Requirements 7 et 8
|
||||||
- **Infobulles d'aide** : le texte explicatif de chacune de ces deux lignes (« Remboursement indicatif » et « Montant prévisionnel annuel »), auparavant affiché en permanence sous le montant, est retiré du flux visuel et remplacé par un bouton icône (SVG inline `help-circle`, 16×16, `aria-label` dédié) affiché juste après le libellé ; le texte explicatif est porté par l'attribut HTML `title` de ce bouton et s'affiche via l'infobulle native du navigateur au survol ou au focus clavier de l'icône
|
- **Mise en forme (emphase)** : les informations relatives au mois affiché sont mises en avant en gras (classe `--fort`) — la ligne « Total du mois » et la ligne « Remboursement indicatif pour ce mois » ; à l'inverse, la ligne « Total annuel partiel » et la ligne « Montant prévisionnel annuel » ne sont PAS en gras
|
||||||
|
- **Infobulles d'aide** : le texte explicatif de chacune de ces deux lignes (« Remboursement indicatif pour ce mois » et « Montant prévisionnel annuel »), auparavant affiché en permanence sous le montant, est retiré du flux visuel et remplacé par un bouton icône (SVG inline `help-circle`, 16×16, `aria-label` dédié) affiché juste après le libellé ; le texte explicatif est porté par l'attribut HTML `title` de ce bouton et s'affiche via l'infobulle native du navigateur au survol ou au focus clavier de l'icône
|
||||||
|
|
||||||
**`ParametresApplicationComponent`**
|
**`ParametresApplicationComponent`**
|
||||||
- Nouveau composant du Sous_Menu_Application de la Page_Parametrage, positionné entre « Compte » et « Véhicules »
|
- Nouveau composant du Sous_Menu_Application de la Page_Parametrage, positionné entre « Compte » et « Véhicules »
|
||||||
@@ -374,7 +375,7 @@ src/
|
|||||||
|
|
||||||
**Composants d'authentification** (`LoginComponent`, `RegisterComponent`, `ForgotPasswordComponent`, `ResetPasswordComponent`)
|
**Composants d'authentification** (`LoginComponent`, `RegisterComponent`, `ForgotPasswordComponent`, `ResetPasswordComponent`)
|
||||||
- `RegisterComponent` : création de l'unique compte (email + mot de passe ≥ 8 car.), étape 1 de l'initialisation ; à la première ouverture sans compte, l'application y redirige directement. Après création, redirige vers la connexion.
|
- `RegisterComponent` : création de l'unique compte (email + mot de passe ≥ 8 car.), étape 1 de l'initialisation ; à la première ouverture sans compte, l'application y redirige directement. Après création, redirige vers la connexion.
|
||||||
- `LoginComponent` : connexion (email + mot de passe) → `POST /api/auth/login` ; à la réussite, stocke le JWT et navigue vers la vue protégée. Propose un lien « Mot de passe oublié ».
|
- `LoginComponent` : connexion (email + mot de passe) → `POST /api/auth/login` ; à la réussite, stocke le JWT et navigue vers la vue protégée. Propose un lien « Mot de passe oublié ». Propose également une case à cocher « Se souvenir de moi » placée sous le champ email : à une connexion réussie, si elle est cochée, l'email (identifiant uniquement, jamais le mot de passe) est conservé dans le `localStorage` du navigateur sous la clé `schotziad.login.emailMemorise` et pré-rempli (case cochée) aux visites suivantes sur le même navigateur ; si elle est décochée, la clé est supprimée. Le stockage `localStorage` étant propre à l'origine et au navigateur, aucun pré-remplissage n'a lieu sur une autre machine ou un autre navigateur, et les erreurs d'accès au stockage sont ignorées silencieusement. Détail dans `requirements.md` Requirement 17 (critères 14-18) et `design-requirements.md` Requirement 17 (critères 9-13).
|
||||||
- `ForgotPasswordComponent` : saisie de l'email → `POST /api/auth/forgot-password` (lien envoyé par email, ou journalisé en repli).
|
- `ForgotPasswordComponent` : saisie de l'email → `POST /api/auth/forgot-password` (lien envoyé par email, ou journalisé en repli).
|
||||||
- `ResetPasswordComponent` : nouveau mot de passe via le jeton reçu → `POST /api/auth/reset-password`.
|
- `ResetPasswordComponent` : nouveau mot de passe via le jeton reçu → `POST /api/auth/reset-password`.
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ L'application permet de gérer un profil (identité, matricule, adresse de domic
|
|||||||
- **Mot_De_Passe** : Le secret d'au moins 8 caractères associé au Compte, jamais conservé en clair.
|
- **Mot_De_Passe** : Le secret d'au moins 8 caractères associé au Compte, jamais conservé en clair.
|
||||||
- **Session** : L'état authentifié d'un utilisateur, établi à la connexion et vérifié à chaque accès protégé.
|
- **Session** : L'état authentifié d'un utilisateur, établi à la connexion et vérifié à chaque accès protégé.
|
||||||
- **Jeton_Reinitialisation** : Un jeton à usage unique et à durée de vie limitée, envoyé par e-mail, permettant de définir un nouveau Mot_De_Passe sans connaître l'ancien.
|
- **Jeton_Reinitialisation** : Un jeton à usage unique et à durée de vie limitée, envoyé par e-mail, permettant de définir un nouveau Mot_De_Passe sans connaître l'ancien.
|
||||||
|
- **Identifiant_Mémorisé** : L'Adresse_Email de connexion conservée par le navigateur de l'utilisateur, propre à la machine et au navigateur utilisés, lorsque l'utilisateur a demandé « Se souvenir de moi » lors d'une connexion réussie ; elle sert à pré-remplir le champ Adresse_Email lors des visites ultérieures sur ce même navigateur. Le Mot_De_Passe n'en fait jamais partie.
|
||||||
- **Aller_Retour** : Un indicateur booléen d'un Trajet signifiant que le déplacement est effectué dans les deux sens. Lorsqu'il est vrai, la Distance comptée du Trajet (Distance_Effective) est le double de la distance simple mesurée entre le départ et l'arrivée.
|
- **Aller_Retour** : Un indicateur booléen d'un Trajet signifiant que le déplacement est effectué dans les deux sens. Lorsqu'il est vrai, la Distance comptée du Trajet (Distance_Effective) est le double de la distance simple mesurée entre le départ et l'arrivée.
|
||||||
- **Distance_Effective** : La distance comptée d'un Trajet dans tous les agrégats (liste, totaux, carte, PDF, barème) : égale à la distance simple, ou à son double lorsque le Trajet est un Aller_Retour.
|
- **Distance_Effective** : La distance comptée d'un Trajet dans tous les agrégats (liste, totaux, carte, PDF, barème) : égale à la distance simple, ou à son double lorsque le Trajet est un Aller_Retour.
|
||||||
- **Document_Specification** : Un document de conception du projet consultable en lecture seule dans l'Application : les Exigences (requirements), la Conception technique (design) et le Plan de tâches (tasks).
|
- **Document_Specification** : Un document de conception du projet consultable en lecture seule dans l'Application : les Exigences (requirements), la Conception technique (design) et le Plan de tâches (tasks).
|
||||||
@@ -447,6 +448,11 @@ L'application permet de gérer un profil (identité, matricule, adresse de domic
|
|||||||
11. WHEN l'utilisateur demande la réinitialisation de son Mot_De_Passe en fournissant son Adresse_Email, THE Backend SHALL générer un Jeton_Reinitialisation à usage unique et à durée de vie limitée, et l'envoyer par e-mail à l'Adresse_Email du Compte.
|
11. WHEN l'utilisateur demande la réinitialisation de son Mot_De_Passe en fournissant son Adresse_Email, THE Backend SHALL générer un Jeton_Reinitialisation à usage unique et à durée de vie limitée, et l'envoyer par e-mail à l'Adresse_Email du Compte.
|
||||||
12. WHEN l'utilisateur suit un Jeton_Reinitialisation valide et fournit un nouveau Mot_De_Passe d'au moins 8 caractères, THE Backend SHALL remplacer le Mot_De_Passe du Compte et invalider le Jeton_Reinitialisation.
|
12. WHEN l'utilisateur suit un Jeton_Reinitialisation valide et fournit un nouveau Mot_De_Passe d'au moins 8 caractères, THE Backend SHALL remplacer le Mot_De_Passe du Compte et invalider le Jeton_Reinitialisation.
|
||||||
13. IF le Jeton_Reinitialisation est expiré, déjà utilisé ou inconnu, THEN THE Backend SHALL rejeter la réinitialisation avec une erreur explicite sans modifier le Mot_De_Passe.
|
13. IF le Jeton_Reinitialisation est expiré, déjà utilisé ou inconnu, THEN THE Backend SHALL rejeter la réinitialisation avec une erreur explicite sans modifier le Mot_De_Passe.
|
||||||
|
14. THE Frontend SHALL afficher sur la page de connexion, sous le champ Adresse_Email, une case à cocher « Se souvenir de moi ».
|
||||||
|
15. WHEN l'utilisateur réussit une connexion alors que la case « Se souvenir de moi » est cochée, THE Frontend SHALL conserver l'Adresse_Email saisie comme Identifiant_Mémorisé sur le navigateur utilisé, et pré-remplir le champ Adresse_Email avec cet Identifiant_Mémorisé en présentant la case « Se souvenir de moi » cochée lors de la visite suivante de la page de connexion sur ce même navigateur.
|
||||||
|
16. WHILE aucune connexion « Se souvenir de moi » n'a été réalisée sur le navigateur utilisé, THE Frontend SHALL présenter la page de connexion avec le champ Adresse_Email vide et la case « Se souvenir de moi » décochée.
|
||||||
|
17. WHEN l'utilisateur réussit une connexion alors que la case « Se souvenir de moi » est décochée, THE Frontend SHALL effacer tout Identifiant_Mémorisé existant sur le navigateur utilisé, de sorte que l'Adresse_Email devra être ressaisie lors de la visite suivante.
|
||||||
|
18. THE Frontend SHALL ne jamais conserver le Mot_De_Passe au titre de la fonctionnalité « Se souvenir de moi ».
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Langue des échanges
|
||||||
|
|
||||||
|
## Règle
|
||||||
|
|
||||||
|
Toujours répondre à l'utilisateur **en français**, quelle que soit la langue de la question ou du contenu technique affiché.
|
||||||
|
|
||||||
|
Cela s'applique à :
|
||||||
|
|
||||||
|
- toutes les réponses et explications dans le chat ;
|
||||||
|
- les messages de synthèse de fin de tâche ;
|
||||||
|
- les questions de clarification posées à l'utilisateur ;
|
||||||
|
- les messages de commit Git ;
|
||||||
|
- les commentaires de code rédigés pour ce projet ;
|
||||||
|
- le contenu rédactionnel des documents de spec (`requirements.md`, `design-requirements.md`, `design.md`, `tasks.md`).
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- Ne pas traduire les termes techniques, mots-clés de langage, noms d'API, identifiants de code, commandes shell ni le contenu littéral des fichiers (le code reste tel quel).
|
||||||
|
- Les mots-clés EARS des specs (WHEN / IF / WHILE / WHERE / THE ... SHALL) restent en anglais, conformément au format.
|
||||||
@@ -30,6 +30,11 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="champ-case">
|
||||||
|
<input id="seSouvenir" type="checkbox" formControlName="seSouvenir" />
|
||||||
|
<label for="seSouvenir">Se souvenir de moi</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="champ">
|
<div class="champ">
|
||||||
<label for="motDePasse">Mot de passe</label>
|
<label for="motDePasse">Mot de passe</label>
|
||||||
<app-password-input
|
<app-password-input
|
||||||
|
|||||||
@@ -38,6 +38,24 @@ h1 {
|
|||||||
border-color: #c62828;
|
border-color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.champ-case {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.champ-case input {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.champ-case label {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.erreur {
|
.erreur {
|
||||||
color: #c62828;
|
color: #c62828;
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { PasswordInputComponent } from '../../../shared/components/password-inpu
|
|||||||
import { NotificationService } from '../../../core/services/notification.service';
|
import { NotificationService } from '../../../core/services/notification.service';
|
||||||
import { LoginDto } from '../../../core/models';
|
import { LoginDto } from '../../../core/models';
|
||||||
|
|
||||||
|
/** Clé localStorage de l'identifiant mémorisé via « Se souvenir de moi ». */
|
||||||
|
const CLE_EMAIL_MEMORISE = 'schotziad.login.emailMemorise';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Écran de connexion (`/login`) — Etape de session de l'authentification.
|
* Écran de connexion (`/login`) — Etape de session de l'authentification.
|
||||||
*
|
*
|
||||||
@@ -18,6 +21,14 @@ import { LoginDto } from '../../../core/models';
|
|||||||
* pas encore configuré. Sur 401, un message générique « Identifiants invalides. »
|
* pas encore configuré. Sur 401, un message générique « Identifiants invalides. »
|
||||||
* est affiché (Exigence 2.2).
|
* est affiché (Exigence 2.2).
|
||||||
*
|
*
|
||||||
|
* Case « Se souvenir de moi » : lorsqu'elle est cochée au moment d'une connexion
|
||||||
|
* réussie, l'identifiant (email) est stocké dans le `localStorage` du navigateur
|
||||||
|
* et pré-rempli aux prochaines visites sur cette même machine/navigateur. Sur un
|
||||||
|
* autre poste ou navigateur, aucun pré-remplissage n'a lieu tant qu'une connexion
|
||||||
|
* « se souvenir » n'y a pas été faite. Si la case est décochée lors d'une
|
||||||
|
* connexion, l'identifiant mémorisé est effacé et devra être ressaisi ensuite.
|
||||||
|
* Le mot de passe n'est jamais stocké.
|
||||||
|
*
|
||||||
* Contrainte zoneless : tout état async est exposé via des signals
|
* Contrainte zoneless : tout état async est exposé via des signals
|
||||||
* (`soumissionEnCours`, `erreur`, `compteExiste`).
|
* (`soumissionEnCours`, `erreur`, `compteExiste`).
|
||||||
*
|
*
|
||||||
@@ -51,9 +62,12 @@ export class LoginComponent implements OnInit {
|
|||||||
readonly compteExiste = signal(true);
|
readonly compteExiste = signal(true);
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
const emailMemorise = this.lireEmailMemorise();
|
||||||
|
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
email: ['', [Validators.required, Validators.email]],
|
email: [emailMemorise ?? '', [Validators.required, Validators.email]],
|
||||||
motDePasse: ['', [Validators.required, Validators.minLength(8)]]
|
motDePasse: ['', [Validators.required, Validators.minLength(8)]],
|
||||||
|
seSouvenir: [emailMemorise !== null]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.authService.compteExiste().subscribe({
|
this.authService.compteExiste().subscribe({
|
||||||
@@ -78,8 +92,9 @@ export class LoginComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const valeur = this.form.getRawValue();
|
const valeur = this.form.getRawValue();
|
||||||
|
const email = (valeur.email ?? '').trim();
|
||||||
const dto: LoginDto = {
|
const dto: LoginDto = {
|
||||||
email: (valeur.email ?? '').trim(),
|
email,
|
||||||
motDePasse: valeur.motDePasse ?? ''
|
motDePasse: valeur.motDePasse ?? ''
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,6 +103,8 @@ export class LoginComponent implements OnInit {
|
|||||||
this.authService.login(dto).subscribe({
|
this.authService.login(dto).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
this.soumissionEnCours.set(false);
|
this.soumissionEnCours.set(false);
|
||||||
|
// Mémorisation de l'identifiant selon le choix « Se souvenir de moi ».
|
||||||
|
this.appliquerMemorisation(valeur.seSouvenir === true, email);
|
||||||
// La cible protégée oriente ensuite vers l'onboarding si nécessaire.
|
// La cible protégée oriente ensuite vers l'onboarding si nécessaire.
|
||||||
this.router.navigate(['/consultation']);
|
this.router.navigate(['/consultation']);
|
||||||
},
|
},
|
||||||
@@ -105,4 +122,33 @@ export class LoginComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lit l'identifiant mémorisé dans le `localStorage`, ou `null` si absent ou
|
||||||
|
* si le stockage est indisponible (mode privé, quota, etc.).
|
||||||
|
*/
|
||||||
|
private lireEmailMemorise(): string | null {
|
||||||
|
try {
|
||||||
|
const valeur = localStorage.getItem(CLE_EMAIL_MEMORISE);
|
||||||
|
return valeur && valeur.trim() !== '' ? valeur : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stocke l'identifiant si « Se souvenir de moi » est coché, sinon l'efface.
|
||||||
|
* Toute erreur d'accès au stockage est ignorée silencieusement.
|
||||||
|
*/
|
||||||
|
private appliquerMemorisation(seSouvenir: boolean, email: string): void {
|
||||||
|
try {
|
||||||
|
if (seSouvenir && email !== '') {
|
||||||
|
localStorage.setItem(CLE_EMAIL_MEMORISE, email);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(CLE_EMAIL_MEMORISE);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Stockage indisponible : la mémorisation est simplement sans effet.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
<dt>Total du jour</dt>
|
<dt>Total du jour</dt>
|
||||||
<dd>{{ (totaux()?.totalJour ?? 0) | number: formatKm() }} km</dd>
|
<dd>{{ (totaux()?.totalJour ?? 0) | number: formatKm() }} km</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="panneau-totaux__ligne">
|
<div class="panneau-totaux__ligne panneau-totaux__ligne--fort">
|
||||||
<dt>Total du mois</dt>
|
<dt>Total du mois</dt>
|
||||||
<dd>{{ (totaux()?.totalMois ?? 0) | number: formatKm() }} km</dd>
|
<dd>{{ (totaux()?.totalMois ?? 0) | number: formatKm() }} km</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="panneau-totaux__ligne panneau-totaux__ligne--fort">
|
<div class="panneau-totaux__ligne">
|
||||||
<dt>Total annuel partiel</dt>
|
<dt>Total annuel partiel</dt>
|
||||||
<dd>{{ (totaux()?.totalAnnuelPartiel ?? 0) | number: formatKm() }} km</dd>
|
<dd>{{ (totaux()?.totalAnnuelPartiel ?? 0) | number: formatKm() }} km</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<div class="panneau-totaux__previsionnel">
|
<div class="panneau-totaux__previsionnel">
|
||||||
<div class="panneau-totaux__ligne panneau-totaux__ligne--fort">
|
<div class="panneau-totaux__ligne panneau-totaux__ligne--fort">
|
||||||
<span class="panneau-totaux__previsionnel-libelle">
|
<span class="panneau-totaux__previsionnel-libelle">
|
||||||
Remboursement indicatif
|
Remboursement indicatif pour ce mois
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="panneau-totaux__aide"
|
class="panneau-totaux__aide"
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panneau-totaux__previsionnel">
|
<div class="panneau-totaux__previsionnel">
|
||||||
<div class="panneau-totaux__ligne panneau-totaux__ligne--fort">
|
<div class="panneau-totaux__ligne">
|
||||||
<span class="panneau-totaux__previsionnel-libelle">
|
<span class="panneau-totaux__previsionnel-libelle">
|
||||||
Montant prévisionnel annuel
|
Montant prévisionnel annuel
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user