DOC
Accueil/ Projets/ MJY Task Platform/ Tâches techniques - Sprint 07 (Compte utilisateur, onboarding & fonctionnalités demandées)

Tâches techniques - Sprint 07 (Compte utilisateur, onboarding & fonctionnalités demandées)

Décomposition des stories du Sprint Backlog 07 en tâches techniques. Cibles : page Profil, changement de PIN renforcé, gestion des emails (US1.3), notifications in-app (Supabase Realtime) + Web Push (VAPID, sans Firebase), report validation import.


🛠️ Kanban des Tâches Techniques

US7-PROF-01 · Page Profil utilisateur

  • TS7.1 : Créer src/pages/Profile.jsx (ou modale ProfileModal) - consultation nom / email / rôle / projets rattachés (depuis useAuth + useData). → src/components/modals/ProfileModal.jsx.
  • TS7.2 : Édition du nom d'affichage (update public.member, RLS member_update_self) + retour visuel de succès/erreur.
  • TS7.3 : Bouton d'accès « 👤 Profil » dans l'en-tête des 3 dashboards ; point d'entrée unifié vers le changement de PIN.
  • TS7.4 : Champ email - lecture seule pour soi, édition déléguée au super-admin (cf. TS7.9). ⚠️ Décision PO #1 tranchée par défaut conservateur (pas de flux de confirmation d'email ce sprint) - à valider/ajuster.

US7-PIN-02 · Changement de PIN renforcé

  • TS7.5 : Étendre ChangePinModal : champ ancien PIN obligatoire.
  • TS7.6 : Vérifier l'ancien PIN avant modification (authService.verifyPin via client Supabase éphémère non persistant) ; message clair si incorrect. ⚠️ Note sécurité : cette vérification passe directement par Supabase Auth et n'est pas couverte par le rate limiting de /api/login (lib/rateLimiter.js), qui ne protège que le flux de connexion. À durcir si jugé nécessaire (cf. ADR 0007 v2).
  • TS7.7 : Conserver le refus du PIN par défaut + updateUser(newPin) + pin_customized = true. Aucun hashage manuel (bcrypt Supabase). ChangePinModal délègue désormais à authService.changePin (suppression de la logique dupliquée).
  • TS7.8 : Tests unitaires (ancien PIN incorrect → refus ; nominal → succès). → src/__tests__/changePin.test.jsx (3 tests).

US7-EMAIL-03 · Édition des emails par le super-admin (US1.3)

  • TS7.9 : Endpoint serveur : le super-admin met à jour l'email d'un membre via auth.admin.updateUserById (dans lib/authBackend.js, exposé par /api/member), + propagation public.member.email. Déjà implémenté (constaté à l'audit de lancement du sprint, pas de code nouveau nécessaire).
  • TS7.10 : Parité Netlify Function netlify/functions/member.js (même contrat, même contrôle de rôle → 403 sinon). Déjà implémenté - les deux endpoints appellent la même fonction partagée createOrUpdateMember.
  • TS7.11 : UI dans MemberModal / gestion des membres : champ email éditable pour le super-admin. Déjà implémenté (MemberModal.jsx).
  • TS7.12 : Vérifier la cohérence auth.users.emailmember.email après édition. Vérifié + couvert par lib/__tests__/authBackend.test.js (garde-fou 403 employé, mise à jour effective par un manager).

US7-NOTIF-04 · Notification in-app temps réel

  • TS7.13 : Dériver côté manager la liste des tâches en pending_validation le concernant (manager = self) depuis tasks (aucune nouvelle requête, données déjà chargées). → src/utils/notifications.js (tasksPendingValidation).
  • TS7.14 : Badge « 🔔 X à valider » dans l'en-tête ManagerDashboard/SuperManagerDashboard, mis à jour via le Realtime existant (aucune requête additionnelle : dérivé de tasks de useData) ; clic → filtre pending_validation (réutilise la tuile StatsBar déjà existante comme « liste dédiée ») + reset du filtre projet à « tous ».
  • TS7.15 : Marquage lu/non-lu local (notif_read_task_ids, ajouté aux LOCAL_ONLY_KEYS de storage.js pour persister en localStorage) ; le badge affiche « (N nouv.) » tant que non ouvert, et redescend naturellement à 0 (badge masqué) une fois la tâche validée/rejetée (elle sort de pending_validation).
  • TS7.16 : Tests unitaires de la dérivation « à valider par manager » (helper pur) + marquage lu. → src/__tests__/notifications.test.js (6 tests).
  • Vérifié en preview réelle (Supabase live) : tâche créée par un employé (Chef Chen) → apparaît immédiatement « 🔔 1 à valider (1 nouv.) » côté manager assigné (My Tam) après connexion ; clic sur le badge marque lu (disparition du « nouv. ») ; validation de la tâche fait disparaître le badge. Tâche de test archivée après vérification.

US7-NOTIF-05 · Web Push (VAPID, sans Firebase)

  • TS7.17 : ADR - Web Push standard + VAPID. Pas de Firebase. → ADR_0010_web_push_vapid.md, statut Accepté (confirmé PO le 09/07 - compatible compte Supabase gratuit, aucune fonctionnalité payante requise).
  • TS7.18 : Migration SQL écrite → 10_push_subscriptions.sql (table + RLS self-service). ⚠️ Pas encore exécutée en base - aucun outil CLI/migration automatisé dans ce projet (cohérent avec 09_create_logs_table.sql etc.) : à jouer manuellement dans le SQL Editor Supabase avant que l'abonnement Push ne fonctionne réellement.
  • TS7.19 : Service Worker minimal → public/sw.js + manifest.webmanifest (lien ajouté dans index.html). Abonnement/désabonnement gérés directement côté client par RLS (src/services/pushService.js) - pas besoin d'endpoint serveur pour cette partie.
  • TS7.20 : Endpoint d'envoi → lib/pushBackend.js (lib web-push + clés VAPID), exposé par server.js (POST /api/notify-validation) et netlify/functions/notify-validation.js (parité iso-contrat). Déclenché depuis useData.saveTask au passage en pending_validation (fire-and-forget, ne bloque jamais la sauvegarde de la tâche). Nettoyage automatique des abonnements expirés (404/410).
  • TS7.21 : UI opt-in/opt-out dans ProfileModal.jsx - statut par appareil (supporté/abonné), bouton Activer/Désactiver, abonnement révocable (suppression de la ligne en base).
  • TS7.22 : Dégradation gracieuse vérifiée : navigateur non compatible → message clair, l'in-app (US7-NOTIF-04) reste le socle ; permission refusée → message clair sans crash. Non vérifiable en preview automatisée : la réception réelle app fermée nécessite (a) la migration TS7.18 exécutée, (b) un vrai navigateur avec permission de notification accordée par un humain, (c) idéalement un test desktop + Android - à faire manuellement avant de considérer la story pleinement close.
  • Vérifié en preview réelle (Supabase live, sans la migration appliquée) : Service Worker s'enregistre (/sw.js), manifest servi (/manifest.webmanifest), section Profil s'affiche, tentative d'abonnement échoue proprement sur « Permission de notification refusée » (navigateur automatisé, comportement attendu). L'endpoint /api/notify-validation répond 200 de façon best-effort même sans la table (vérifié par lecture de code : data est toujours falsy sur une requête en erreur, jamais de crash).

US7-VAL-06 · Validation import par les chefs (report TS6.32)

  • TS7.23 : Présenter la démo d'import à Julien & Marco (après le vendredi soir).
  • TS7.24 : Consigner la validation formelle au §4 de recette_demo.md + ajustements éventuels.

📌 Pré-requis / décisions

  • Onboarding email automatisé : reporté (décision PO) - roster conservé, pas de fournisseur d'email à choisir ce sprint.
  • Notifications sans Firebase : in-app via Supabase Realtime + Web Push standard (VAPID). Firebase seulement pour un futur push mobile natif.
  • Écran de connexion : roster conservé (proposition §1.B écartée).
  • Auth : Supabase natif - aucun hashage manuel ; l'édition d'email auth.users passe par le serveur (Admin API).
  • Parité server.jsnetlify/functions à maintenir sur tout endpoint touché.
  • Déploiement production : sprint dédié ultérieur (hors périmètre S7).

✅ Incident critique constaté au lancement - RÉSOLU

En vérifiant le Bloc 1 en preview réelle (Supabase live), une boucle de refetch très rapide (des centaines de requêtes/s vers member/project/phase/task/task_notes, plus des écritures en rafale dans app_logs) a été observée côté client peu après connexion. Le serveur de preview a été arrêté immédiatement par précaution.

  • Cause confirmée : dans useData.jsx, fetchData (useCallback) et les effets de chargement initial + d'abonnement Realtime (db-changes) étaient calés sur [session, fetchData]session est l'objet Session Supabase complet - recréé à chaque onAuthStateChange (y compris un simple refresh de jeton, sans changement d'utilisateur). Un objet instable en dépendance de useEffect/useCallback réabonne le canal Realtime et relance fetchData à chaque réémission.
  • Correctif appliqué : useData.jsx déclare désormais uid = session?.user?.id et accessToken = session?.access_token, deux primitifs stables (ne changent que sur vraie connexion/déconnexion/rotation de jeton), utilisés comme dépendances à la place de l'objet session dans fetchData, l'effet de chargement initial, l'effet Realtime, saveTask, saveMember et deleteMember. Idem dans useAuth.js (canal Realtime self de US7-PROF-01), déjà calé sur session?.user?.id dès sa création.
  • Vérifié en preview réelle (Supabase live) : après correctif, le volume de requêtes réseau reste stable (78 entrées, aucune croissance) sur plusieurs interactions (connexion, ouverture Profil, ouverture PIN, tentative d'ancien PIN incorrect). Plus de boucle observée.
  • Effet de bord à vérifier côté Supabase (hors mon accès) : la table app_logs a probablement reçu un volume anormal d'écritures pendant l'incident initial (quelques minutes, avant correctif) - un nettoyage ciblé (DELETE FROM app_logs WHERE created_at BETWEEN ...) peut être utile ; vérifier aussi la conso Supabase (dashboard → Usage) sur cette fenêtre au cas où des quotas auraient été affectés.