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 modaleProfileModal) - consultation nom / email / rôle / projets rattachés (depuisuseAuth+useData). →src/components/modals/ProfileModal.jsx. - TS7.2 : Édition du nom d'affichage (update
public.member, RLSmember_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.verifyPinvia 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).ChangePinModaldé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(danslib/authBackend.js, exposé par/api/member), + propagationpublic.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éecreateOrUpdateMember. - 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.email↔member.emailaprès édition. Vérifié + couvert parlib/__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_validationle concernant (manager = self) depuistasks(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é detasksdeuseData) ; clic → filtrepending_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é auxLOCAL_ONLY_KEYSdestorage.jspour 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 depending_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 avec09_create_logs_table.sqletc.) : à 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é dansindex.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(libweb-push+ clés VAPID), exposé parserver.js(POST /api/notify-validation) etnetlify/functions/notify-validation.js(parité iso-contrat). Déclenché depuisuseData.saveTaskau passage enpending_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-validationrépond 200 de façon best-effort même sans la table (vérifié par lecture de code :dataest 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.userspasse par le serveur (Admin API). - Parité
server.js↔netlify/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]oùsessionest l'objetSessionSupabase complet - recréé à chaqueonAuthStateChange(y compris un simple refresh de jeton, sans changement d'utilisateur). Un objet instable en dépendance deuseEffect/useCallbackréabonne le canal Realtime et relancefetchDataà chaque réémission. - Correctif appliqué :
useData.jsxdéclare désormaisuid = session?.user?.idetaccessToken = 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'objetsessiondansfetchData, l'effet de chargement initial, l'effet Realtime,saveTask,saveMemberetdeleteMember. Idem dansuseAuth.js(canal Realtime self de US7-PROF-01), déjà calé sursession?.user?.iddè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_logsa 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.
