Authentification
L'écran de connexion suit le design de la référence (design uniquement, aucune logique React/Supabase). Il se découpe en deux niveaux.
Écran routé — src/views/Login.vue
Fond bg-primary (vert forêt) plein écran, en-tête (titre + sous-titre), sélecteur de langue (LocaleSwitcher) et animation d'entrée animate-fade-up.
onSubmit (mode connexion) appelle login() (voir Couche API) : il gère l'état de chargement (loading) et traduit les échecs en message localisé (error) — identifiants invalides (401/422) → login.errorInvalidCredentials, sinon login.errorGeneric. En cas de succès, le jeton est mémorisé par la couche API et la vue navigue vers le ?redirect interne validé (resolveLoginRedirect — le lien profond intercepté par la garde d'auth, voir Routage) ou, à défaut, vers /dashboard (resolveHomeRoute()). Le mode inscription appelle register() (compte créé en attente, activation par lien e-mail) et affiche l'avis register.success.
Composant présentationnel — src/components/auth/LoginForm.vue
La carte blanche : onglets Connexion / Inscription, champs (nom complet, email, mot de passe), message d'erreur serveur (role="alert"), bouton de soumission avec état de chargement et lien « mot de passe oublié ». Les libellés passent par t() (catalogue login.* — voir Internationalisation).
Validation des champs (Regle)
La validation côté formulaire utilise Regle via le composable partagé useRegle (src/lib/regle.ts, voir Validation de formulaires). Le <form> porte novalidate : la validation native du navigateur (et ses bulles non traduisibles) est désactivée au profit de messages passant par vue-i18n. Règles appliquées : email required + email, mot de passe required + minLength(6), nom complet required uniquement en mode inscription. onSubmit appelle r$.$validate() (asynchrone) et n'émet submit que si le formulaire est valide ; chaque champ affiche son premier message sous lui (text-destructive, role="alert", aria-invalid).
Contrat du composant LoginForm
Composant « muet » : il reçoit ses données par props et émet l'intention de l'utilisateur par événements. Aucune logique de données, d'authentification ou de persistance n'y figure.
- Props :
loading?: boolean— affiche un indicateur de chargement sur le bouton.error?: string | null— message d'erreur affiché au-dessus du bouton.
- Événements :
submit— charge utile{ mode, email, password, displayName }.forgotPassword— l'email saisi.
L'état local (mode et l'objet réactif form : email, password, displayName) est purement présentationnel (affichage des champs, de l'onglet actif et des messages de validation).
Couche API
Les appels d'authentification vivent dans src/api/auth.ts (login, logout, getCurrentUser) — voir Couche API (HTTP). login ajoute le device_name requis, renvoie { token, user }, mémorise le jeton et lève une ApiError (avec fieldErrors pour la validation 422).

