diff --git a/.kiro/specs/schotziad-km-tracker/design-requirements.md b/.kiro/specs/schotziad-km-tracker/design-requirements.md index 70082bb..a82c110 100644 --- a/.kiro/specs/schotziad-km-tracker/design-requirements.md +++ b/.kiro/specs/schotziad-km-tracker/design-requirements.md @@ -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. 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. +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. --- diff --git a/.kiro/specs/schotziad-km-tracker/design.md b/.kiro/specs/schotziad-km-tracker/design.md index f0aba87..b345250 100644 --- a/.kiro/specs/schotziad-km-tracker/design.md +++ b/.kiro/specs/schotziad-km-tracker/design.md @@ -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) - 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) -- 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 -- **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 +- 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 +- **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`** - 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`) - `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). - `ResetPasswordComponent` : nouveau mot de passe via le jeton reçu → `POST /api/auth/reset-password`. diff --git a/.kiro/specs/schotziad-km-tracker/requirements.md b/.kiro/specs/schotziad-km-tracker/requirements.md index 2cdcdbb..4e7a636 100644 --- a/.kiro/specs/schotziad-km-tracker/requirements.md +++ b/.kiro/specs/schotziad-km-tracker/requirements.md @@ -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. - **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. +- **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. - **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). @@ -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. 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. +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 ». --- diff --git a/.kiro/steering/langue.md b/.kiro/steering/langue.md new file mode 100644 index 0000000..9470ee8 --- /dev/null +++ b/.kiro/steering/langue.md @@ -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. diff --git a/frontend/src/app/features/auth/login/login.component.html b/frontend/src/app/features/auth/login/login.component.html index 5e3391e..0bcde78 100644 --- a/frontend/src/app/features/auth/login/login.component.html +++ b/frontend/src/app/features/auth/login/login.component.html @@ -30,6 +30,11 @@ } +
+ + +
+
{ 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. 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. + } + } } diff --git a/frontend/src/app/features/panneau-totaux/panneau-totaux.component.html b/frontend/src/app/features/panneau-totaux/panneau-totaux.component.html index 7e8d7b7..867e52f 100644 --- a/frontend/src/app/features/panneau-totaux/panneau-totaux.component.html +++ b/frontend/src/app/features/panneau-totaux/panneau-totaux.component.html @@ -10,11 +10,11 @@
Total du jour
{{ (totaux()?.totalJour ?? 0) | number: formatKm() }} km
-
+
Total du mois
{{ (totaux()?.totalMois ?? 0) | number: formatKm() }} km
-
+
Total annuel partiel
{{ (totaux()?.totalAnnuelPartiel ?? 0) | number: formatKm() }} km
@@ -24,7 +24,7 @@
- Remboursement indicatif + Remboursement indicatif pour ce mois
-
+
Montant prévisionnel annuel