diff --git a/.ideai/memory/MEMORY.md b/.ideai/memory/MEMORY.md index 214c6ec..d7ca5ca 100644 --- a/.ideai/memory/MEMORY.md +++ b/.ideai/memory/MEMORY.md @@ -16,3 +16,6 @@ - [gametime-server-architecture-sync-sharing](gametime-server-architecture-sync-sharing.md) — memory note gametime-server-architecture-sync-sharing - [gametime-ux-exercise-steps](gametime-ux-exercise-steps.md) — memory note gametime-ux-exercise-steps - [gametime-architecture-exercise-steps](gametime-architecture-exercise-steps.md) — memory note gametime-architecture-exercise-steps +- [gametime-online-layer-philosophy](gametime-online-layer-philosophy.md) — memory note gametime-online-layer-philosophy +- [gametime-ux-online-client](gametime-ux-online-client.md) — memory note gametime-ux-online-client +- [gametime-architecture-online-client](gametime-architecture-online-client.md) — memory note gametime-architecture-online-client diff --git a/.ideai/memory/gametime-architecture-online-client.md b/.ideai/memory/gametime-architecture-online-client.md new file mode 100644 index 0000000..23709b1 --- /dev/null +++ b/.ideai/memory/gametime-architecture-online-client.md @@ -0,0 +1,171 @@ +--- +name: gametime-architecture-online-client +description: memory note gametime-architecture-online-client +metadata: + type: project +--- +# GameTime — Architecture client online, auth, sync et partage + +Décision d'architecture pour le ticket #63, basée sur les mémoires `gametime-ux-online-client`, `gametime-online-layer-philosophy` et `gametime-server-architecture-sync-sharing`. + +## Conclusion serveur : pas d'adaptation requise pour les étapes d'exercice + +Le serveur ne valide pas la forme interne des payloads synchronisés. `synced_resources.payload_json` est un JSONB opaque stocké et retourné tel quel. + +Preuves dans le code : +- `server/migrations/0001_initial_schema.sql` : `payload_json jsonb NOT NULL` sur `synced_resources`, avec contraintes seulement sur `resource_type`, `client_id`, `schema_version`, pas sur la forme de `payload_json`. +- `server/lib/infrastructure/postgres/synced_resource_repository.dart` : l'upsert écrit `@payload_json::jsonb`, avec `jsonEncode(resource.payloadJson)`, puis retourne `payload_json` via `_payloadValue(...)`. Aucune validation métier de structure exercice/programme/template n'est faite dans ce repository. +- `server/openapi.yaml` : `SyncPushItem.payload` et `SyncedResourceItem.payload` référencent seulement `JsonObject`. `CreateShareRequest.payload` et `ShareInboxItem.payload` idem. + +Conclusion : l'ajout des étapes d'exercice côté client ne nécessite pas de modification serveur pour la sync v1. Le client peut envoyer la nouvelle forme JSON dans `payload` tant qu'elle reste un objet JSON valide et porte `schemaVersion` correctement. + +## Principe produit non négociable + +La couche online est optionnelle et additive : +- pas d'écran de login au démarrage ; +- aucune action locale ne dépend du serveur ; +- aucun échec réseau ne déclenche de popup globale ; +- les données nécessaires à l'UI restent en local ; +- logout ne supprime jamais exercices/programmes/séances/historique locaux. + +## Architecture lib/ + +Conserver l'architecture existante : + +- `lib/domain/entities.dart` : entités pures `UserAccountSession`, `SyncStatusSnapshot`, `ShareInboxItem`, `PendingShareAction` si elles sont stables métier. +- `lib/application/ports.dart` : ports `AuthTokenStore`, `OnlineAccountRepository`, `RemoteSyncApi`, `RemoteShareApi`, `OnlineSessionRepository`, `SyncMetadataRepository`, `ShareInboxRepository`. +- `lib/application/use_cases.dart` : `AuthUseCases`, `SyncUseCases`, `ShareUseCases`. +- `lib/infrastructure/local/` : cache profil, curseur sync, inbox partages, queue d'actions pending, mapping Drift. +- `lib/infrastructure/remote/` : adapter HTTP vers `server/openapi.yaml`, DTO wire, sérialisation JSON. +- `lib/presentation/` : Profil, auth, statut sync, partage sortant, inbox. + +Le domaine/application ne dépend pas de `http`, `flutter_secure_storage` ni Drift. + +## Dépendances client retenues + +- Tokens : `flutter_secure_storage`. + - Justification : stockage sécurisé cross-platform via mécanismes natifs ; adapté à access/refresh tokens. + - Ne pas stocker le profil affichable uniquement dedans. +- HTTP : `package:http` avec `Client` injecté. + - Justification : API simple, composable, facile à fake en tests, dépendance plus légère que Dio. Ajouter un wrapper pour baseUrl, auth bearer, JSON, timeouts et mapping d'erreurs. + +## Authentification client + +Entité locale recommandée : `UserAccountSession`. + +Champs : +- `id` local. +- `serverUserId`. +- `email`. +- `displayName?`. +- `avatarLocalUri?` ou `avatarRemoteUri?` si disponible plus tard. +- `isLoggedIn`. +- `createdAt`, `updatedAt`, `lastAuthenticatedAt?`. + +Stockage : +- tokens opaques : `flutter_secure_storage` via port `AuthTokenStore`. +- profil/cache visible : Drift, pour affichage instantané hors ligne. + +Use cases : +- `register(email, password)` -> API `POST /auth/register`, stocke tokens si réponse connectante, cache profil, déclenche sync en arrière-plan. +- `login(email, password)` -> `POST /auth/login`, stocke tokens, cache profil, déclenche sync en arrière-plan. +- `logout()` -> tente `POST /auth/logout`, mais supprime tokens localement même si réseau KO ; conserve données locales et profil dernier connu si utile à l'historique d'affichage, avec état déconnecté. +- `currentSession()` -> lit cache local + présence token. + +Erreurs : +- erreurs credentials : retournées au formulaire auth en erreur inline. +- réseau/timeout : erreur typée non intrusive ; jamais popup globale. + +## Synchronisation client + +Ressources synchronisées v1 : +- `exercise` +- `program` +- `workoutTemplate` +- `workoutHistory` +- `mediaAsset` metadata uniquement + +Mapping push : + +```json +{ + "resourceType": "exercise", + "clientId": "", + "schemaVersion": , + "clientUpdatedAt": "", + "deletedAt": "", + "payload": { "...": "snapshot complet local" } +} +``` + +Le payload doit être le snapshot local complet et autonome nécessaire pour reconstruire l'entité. Pour les exercices, il inclut les étapes ajoutées par #54. Le serveur ne l'interprète pas. + +Source des changements à pousser : utiliser `change_log`, `syncState`, `updatedAt`, `deletedAt`, `localRevision`. Le gateway doit lire les entités concernées, assembler les payloads, appeler `POST /sync/push` ou `POST /sync/exchange`, puis marquer les items acceptés comme synced et enregistrer les mappings serveur si nécessaires. + +Pull : +- stocker `serverCursor` localement. +- appeler `GET /sync/pull?since=` ou `POST /sync/exchange`. +- appliquer chaque item selon LWW côté client : si `server.clientUpdatedAt` est plus récent que `local.updatedAt`, appliquer ; sinon ignorer localement. +- soft delete distant : appliquer `deletedAt` local sans hard delete. +- mettre à jour le curseur seulement après transaction locale réussie. + +Déclencheurs : +- après login/register : sync en arrière-plan, sans loader bloquant. +- au démarrage si connecté : tentative silencieuse. +- après mutation locale : planifier une sync arrière-plan courte/debounced. +- périodique quand app active : intervalle raisonnable, pas besoin de temps réel. +- manuel depuis Profil : `Synchroniser maintenant`. + +Échec sync : +- pas de popup ; +- statut neutre dans Profil ; +- retry différé ; +- l'app reste utilisable localement. + +## Drift / migration client + +Le schéma local actuel est `schemaVersion = 9`. Si les tables online sont ajoutées, passer à `schemaVersion = 10`. + +Tables recommandées : + +### `online_account_sessions` +- cache profil et état connecté/déconnecté. +- pas de tokens en clair. + +### `sync_metadata` +- singleton ou key/value : `serverCursor`, `lastSuccessfulSyncAt`, `lastAttemptAt`, `lastFailureAt`, `status`, `pendingPushCount?`. + +### `remote_resource_mappings` +- `resourceType`, `clientId`, `serverId`, `serverUpdatedAt`, unique `(resourceType, clientId)`. + +### `share_inbox_items` +- `shareId`, sender info, `resourceType`, `payloadJson`, `status`, `createdAt`, `updatedAt`, cache offline. + +### `pending_share_actions` +- queue locale pour share/send/accept/decline/revoke si réseau indisponible. +- champs : `id`, `actionType`, `shareId?`, `resourceType?`, `payloadJson?`, `recipientEmailsJson?`, `createdAt`, `lastAttemptAt?`, `attemptCount`, `status`. + +## Partage client + +Ports/use cases : +- `sendShare(resourceType, localResourceId, recipientEmails)` : construit payload snapshot local de Program ou WorkoutTemplate, appelle `POST /shares`; si réseau KO, met en queue. +- `refreshInbox()` : appelle `GET /shares/inbox`, cache les items. +- `acceptShare(shareId)` : appelle `POST /shares/{id}/accept`. +- `declineShare(shareId)`. +- `revokeShare(shareId)`. + +Acceptation : OpenAPI indique que `AcceptShareResponse.createdResource` renvoie directement un `SyncedResourceItem`. Le client peut donc importer immédiatement la ressource créée dans le stockage local, puis le prochain pull sert de convergence. Si l'accusé serveur ne peut pas partir mais que le payload inbox est déjà local, l'app peut créer une copie locale indépendante et mettre l'action accept en queue selon le choix d'implémentation, avec message neutre. + +Invariant : un partage accepté devient une copie locale normale, non liée dynamiquement à l'expéditeur. Les doublons de noms sont autorisés. + +## Tickets créés + +- #64 `[DevBackend] Client online : session compte, stockage sécurisé et adapter API`. +- #65 `[DevBackend] Sync client incrémentale LWW vers API serveur`, dépend de #64. +- #66 `[DevBackend] Partage client : use cases, inbox cache et import local`, dépend de #64 et #65. +- #67 `[DevFrontend] Profil et écrans auth optionnels`, dépend de #64. +- #68 `[DevFrontend] Statut de synchronisation discret et action manuelle`, dépend de #65 et #67. +- #69 `[DevFrontend] Partage sortant et boîte de réception`, dépend de #66 et #67. +- #70 `[QA] Validation client online offline-first`, dépend de #68 et #69. + +Ordre recommandé : #64 -> #65 -> #66, en parallèle #67 après #64, puis #68 et #69, puis #70. \ No newline at end of file diff --git a/.ideai/memory/gametime-online-layer-philosophy.md b/.ideai/memory/gametime-online-layer-philosophy.md new file mode 100644 index 0000000..dfb08e0 --- /dev/null +++ b/.ideai/memory/gametime-online-layer-philosophy.md @@ -0,0 +1,31 @@ +--- +name: gametime-online-layer-philosophy +description: memory note gametime-online-layer-philosophy +metadata: + type: project +--- +# GameTime — Philosophie de la couche online (compte, sync, partage) + +Décision produit posée par l'utilisateur pour le ticket #57, à respecter par tous les tickets futurs touchant au serveur/à la synchronisation/au compte utilisateur (notamment #63 et la suite). + +## Principe directeur + +L'application reste **offline-first en priorité**, la couche online est un **complément transparent**, jamais une dépendance bloquante. Référence produit explicite : Hevy. + +- La connexion à un compte est **toujours optionnelle**. L'app doit être pleinement utilisable sans jamais se connecter. +- Si le serveur est indisponible (pas de réseau, serveur down, timeout...), **aucune popup d'erreur, aucun blocage, aucun message intrusif** ne doit apparaître à l'utilisateur pendant son usage normal. L'absence de connectivité doit être silencieuse pour les fonctionnalités de base. +- Le serveur **sert l'application, jamais l'inverse** : en cas de divergence de structure de données entre le client (source de vérité fonctionnelle) et le serveur, c'est le serveur qui doit être adapté pour accepter la donnée du client, pas le client qui doit se plier au serveur. (Exemple concret déjà identifié : le schéma serveur des exercices doit être mis à jour pour accepter les étapes ajoutées côté client par le chantier #54, la forme serveur ayant été figée avant ce chantier.) + +## Ce qui doit toujours rester stocké en local (jamais uniquement distant) + +Tout ce qui est nécessaire au bon affichage/fonctionnement normal de l'app doit avoir une copie locale à jour, sans dépendre d'un appel réseau pour s'afficher correctement : + +- Exercices, programmes, séances-modèles, historique de séances (déjà le cas, c'est la base offline-first existante). +- Statistiques calculées, si/quand elles existeront. +- Données de profil utilisateur une fois connecté : pseudo, photo de profil, et toute autre donnée de compte affichée dans l'UI — gardées en cache local pour un affichage instantané et cohérent même hors ligne, sans jamais afficher une valeur fausse ou périmée qui induirait l'utilisateur en erreur (mieux vaut réafficher la dernière valeur connue que d'afficher une erreur ou un vide trompeur). + +## Comment appliquer ce principe + +- Toute UI liée au compte/sync (menu "Profil", statut de connexion, etc.) doit être conçue comme une couche additive et discrète, jamais comme un gate ou une interruption du parcours principal. +- Les échecs réseau/sync doivent être gérés silencieusement en arrière-plan (retry différé, file d'attente, etc.) — voir [[gametime-server-architecture-sync-sharing]] pour le protocole de sync LWW déjà défini côté serveur, qui doit être consommé côté client dans cet esprit. +- Ne jamais bloquer une action locale (créer un exercice, terminer une série, etc.) en attendant une confirmation serveur. \ No newline at end of file diff --git a/.ideai/memory/gametime-ux-online-client.md b/.ideai/memory/gametime-ux-online-client.md new file mode 100644 index 0000000..13c7f5a --- /dev/null +++ b/.ideai/memory/gametime-ux-online-client.md @@ -0,0 +1,429 @@ +--- +name: gametime-ux-online-client +description: memory note gametime-ux-online-client +metadata: + type: project +--- +# GameTime — UX client couche online, compte, sync, partage (ticket #63, UX 2026-07-19) + +Conception client pour consommer les fonctionnalités serveur : comptes utilisateur, synchronisation incrémentale LWW, partage ciblé de programmes/séances. Cette conception respecte la mémoire `gametime-online-layer-philosophy` : l'app reste offline-first, la connexion est optionnelle, aucune action locale n'attend le serveur, aucun échec réseau ne doit interrompre le parcours principal. + +## Principe directeur UI + +La couche online est une **option de profil**, pas une porte d'entrée obligatoire. + +- Ne jamais afficher d'écran de connexion au démarrage. +- Ne jamais bloquer `Exercices`, `Programmes`, `Séances`, `Historique` parce que l'utilisateur est déconnecté. +- Ne jamais afficher de popup globale en cas d'échec de sync. +- Les statuts online apparaissent uniquement dans des zones volontaires : `Profil`, formulaires de connexion, partage, boîte de réception. +- Toute donnée locale reste immédiatement consultable/modifiable, connecté ou non. + +## Navigation + +Contexte actuel : `HomeScreen` est une liste d'entrées (`Exercices`, `Programmes`, `Séances`, `Historique`) avec AppBar logo + menu thème, sans bottom nav. + +Décision UX : ne pas introduire de bottom nav ni refondre la navigation. + +Ajouter une entrée `Profil` dans la liste d'accueil, après `Historique` : + +```text +Profil +Compte, synchronisation et partages +``` + +Icône : `Icons.account_circle_outlined` si déconnecté, avatar/photo locale si connecté. + +L'AppBar peut rester dédiée au logo et au thème. Si une indication rapide est souhaitée plus tard, préférer un petit avatar en AppBar seulement sur l'accueil, mais ce n'est pas nécessaire au MVP. + +## 1. Écran Profil + +### État déconnecté + +Titre : `Profil` + +Bloc principal Court Blazer : + +```text +Compte optionnel +GameTime fonctionne entièrement sans compte. Connecte-toi seulement si tu veux sauvegarder tes données en ligne ou partager des programmes et séances. + +[Créer un compte] +[Se connecter] +``` + +Style : ton neutre, aucune culpabilisation, aucun warning. + +Section informative : + +```text +Données locales +Tes exercices, programmes, séances et historiques sont enregistrés sur cet appareil. +``` + +Section partages, non interactive ou secondaire : + +```text +Partages +Connecte-toi pour envoyer et recevoir des programmes ou des séances. +``` + +Ne pas afficher de bouton `Continuer sans compte` : l'utilisateur est déjà dans l'app, donc ce bouton serait redondant. + +### État connecté + +En haut : carte profil. + +```text +[photo/avatar] +Pseudo +email@example.com +``` + +Si pseudo absent : afficher l'email comme identifiant principal. Si photo absente : avatar initiales ou icône compte. Pseudo/photo doivent venir du cache local et s'afficher instantanément même hors ligne. + +Actions : + +```text +[Modifier le profil] // seulement si API/client le supporte dans ce lot +[Se déconnecter] +``` + +Si `Modifier le profil` n'est pas supporté par le serveur, ne pas afficher l'action au MVP. + +Section synchronisation : + +```text +Synchronisation +À jour à 14:32 +``` + +États possibles, toujours neutres : + +- Sync en cours : `Synchronisation en cours...` +- Sync réussie : `À jour à 14:32` +- Sync jamais faite : `Synchronisation en attente` +- Sync échouée : `Dernière synchronisation : hier à 18:20. Nouvelle tentative automatique.` +- Hors ligne détecté : `Hors ligne. Les données restent disponibles.` + +Ne pas utiliser de rouge pour la sync échouée. Utiliser icônes neutres : `cloud_outlined`, `sync`, `cloud_done_outlined`, `schedule`. + +Section partages : + +```text +Partages reçus [badge si éléments en attente] +Programmes et séances reçus d'autres comptes +``` + +Tap → `Partages reçus`. + +Déconnexion : confirmation nécessaire. + +```text +Se déconnecter ? +Les données restent sur cet appareil. La synchronisation et les partages seront suspendus jusqu'à une prochaine connexion. + +[Annuler] +[Se déconnecter] +``` + +Après logout : retour à l'état déconnecté, aucune suppression locale. + +## 2. Création de compte / Connexion + +Écrans accessibles depuis `Profil`, jamais imposés ailleurs. + +### Se connecter + +AppBar : `Se connecter` + +Champs : + +```text +Email +Mot de passe +``` + +Actions : + +```text +[Se connecter] +[Créer un compte] +``` + +Texte secondaire bas d'écran : + +```text +Tu peux continuer à utiliser GameTime sans compte. +``` + +Validation locale : + +- email vide/invalide : `Saisis une adresse email valide.` +- mot de passe vide : `Saisis ton mot de passe.` + +Erreurs serveur affichées inline dans le formulaire, jamais en popup : + +- identifiants invalides : `Email ou mot de passe incorrect.` +- serveur/réseau indisponible : `Connexion impossible pour le moment. Réessaie plus tard.` + +Après succès : revenir à `Profil` avec message discret : + +```text +Compte connecté. Synchronisation en arrière-plan. +``` + +Ne pas afficher de loader bloquant de synchronisation initiale. + +### Créer un compte + +AppBar : `Créer un compte` + +Champs : + +```text +Email +Mot de passe +Confirmer le mot de passe +``` + +Actions : + +```text +[Créer le compte] +[Déjà un compte ? Se connecter] +``` + +Texte secondaire : + +```text +Le compte sert à synchroniser tes données et partager tes contenus. L'app reste utilisable sans compte. +``` + +Validation locale : + +- email vide/invalide : `Saisis une adresse email valide.` +- mot de passe vide : `Saisis un mot de passe.` +- confirmation différente : `Les mots de passe ne correspondent pas.` + +Erreurs serveur inline : + +- email déjà utilisé : `Un compte existe déjà avec cet email.` +- réseau/serveur : `Création impossible pour le moment. Réessaie plus tard.` + +Après succès : connecter l'utilisateur si le serveur renvoie un token, retour Profil, sync arrière-plan. + +## 3. Indication de synchronisation + +La synchronisation doit être transparente et non intrusive. + +### Où afficher + +- Principalement dans `Profil`, section `Synchronisation`. +- Optionnel : sous-titre de l'entrée `Profil` sur l'accueil : + - déconnecté : `Compte optionnel` + - connecté à jour : `Synchronisé récemment` + - connecté sync en attente : `Synchronisation en attente` + +Ne pas ajouter de bannière globale ou snackbar automatique sur échec réseau. + +### États visuels + +- `Synchronisation en cours...` : petite icône `sync`, éventuellement rotation subtile. +- `À jour à 14:32` : icône neutre ou `cloud_done_outlined` en couleur texte secondaire, pas nécessairement vert. +- `Dernière synchronisation : hier à 18:20. Nouvelle tentative automatique.` : icône `schedule`, texte secondaire. +- `Hors ligne. Les données restent disponibles.` : neutre/informatif. + +Actions possibles dans Profil : + +```text +[Synchroniser maintenant] +``` + +Si échec : rester sur le même écran avec texte neutre, pas de dialog. + +## 4. Partage sortant + +Le partage concerne les `Programmes` et les `Séances`. + +### Point d'entrée + +Ne pas surcharger les listes avec une nouvelle icône visible sur chaque ligne. + +Point d'entrée recommandé : dans l'écran de modification/détail d'un programme ou d'une séance déjà enregistrée, ajouter une action AppBar : + +```text +Partager +``` + +Icône : `Icons.ios_share` ou `Icons.share_outlined`. + +Dans les listes, si un menu `...` existe plus tard, `Partager` peut y être ajouté, mais ce n'est pas obligatoire au MVP. + +### Si utilisateur déconnecté + +Quand il tape `Partager` : écran ou bottom sheet explicative, non bloquante pour le reste de l'app. + +```text +Compte requis pour partager +Connecte-toi pour envoyer ce programme à un autre compte GameTime. + +[Se connecter] +[Créer un compte] +[Annuler] +``` + +Aucune obligation de connexion pour continuer à modifier localement. + +### Formulaire de partage + +Titre : + +```text +Partager le programme +``` + +ou : + +```text +Partager la séance +``` + +Contenu : + +```text +Programme à partager +Programme tirs extérieur +6 exercices · 18 séries + +Email du destinataire +[ joueur@example.com ] + +[Envoyer le partage] +``` + +Validation locale : + +- email vide/invalide : `Saisis l'email du destinataire.` + +Après action : + +- si envoyé immédiatement : `Partage envoyé.` +- si réseau/serveur indisponible : `Partage enregistré. Envoi dès que possible.` + +Pas de popup d'erreur réseau. Le partage peut être mis en file d'attente si l'architecture le permet ; sinon rester inline avec `Envoi impossible pour le moment. Réessaie plus tard.` dans le formulaire, mais ne jamais perturber les autres écrans. + +## 5. Partages reçus + +Accessible depuis `Profil` > `Partages reçus`. + +### Liste + +AppBar : `Partages reçus` + +États : + +- chargement local : indicateur discret si nécessaire ; +- vide : + +```text +Aucun partage reçu +Les programmes et séances qu'on t'envoie apparaîtront ici. +``` + +Chaque item : + +```text +Programme +Programme tirs extérieur +Envoyé par Alex +6 exercices · 18 séries + +[Accepter] [Refuser] +``` + +ou : + +```text +Séance +Prépa match +Envoyée par Alex +2 programmes · 11 exercices + +[Accepter] [Refuser] +``` + +### Acceptation + +`Accepter` importe une copie locale immédiatement si les données du partage sont disponibles localement. + +Après acceptation : + +- Programme : `Programme ajouté.` +- Séance : `Séance ajoutée.` + +La copie devient un objet local normal, disponible offline. Elle n'est pas liée dynamiquement à l'expéditeur. + +Si l'accusé serveur ne peut pas partir : + +```text +Accepté sur cet appareil. Mise à jour du partage dès que possible. +``` + +### Refus + +Confirmation légère non obligatoire. Recommandation : action directe avec undo possible si facile ; sinon confirmation courte. + +Message : + +```text +Partage refusé. +``` + +Si réseau indisponible : + +```text +Refus enregistré. Mise à jour dès que possible. +``` + +### Conflits / doublons + +Ne pas bloquer l'acceptation si un programme ou une séance porte déjà le même nom. Autoriser les doublons comme le reste de l'app. Optionnellement ajouter suffixe local si nécessaire : `Prépa match (partagé)`. + +## 6. Données locales et cache profil + +Exigence UX : après connexion, l'écran Profil doit afficher instantanément la dernière identité connue. + +À mettre en cache local : + +- email ; +- pseudo si disponible ; +- photo/avatar si disponible ; +- dernier état de sync affichable ; +- partages reçus déjà récupérés si possible. + +Ne jamais vider les listes locales lors du logout. Le logout suspend seulement token/sync/partage. + +## 7. Libellés à éviter + +Éviter : + +- `Erreur de synchronisation` +- `Non synchronisé` en rouge +- `Connexion requise` +- `Impossible d'utiliser l'app` + +Préférer : + +- `Synchronisation en attente` +- `Nouvelle tentative automatique` +- `Compte optionnel` +- `Les données restent disponibles` +- `Partage enregistré. Envoi dès que possible.` + +## 8. Découpage tickets conseillé + +1. `Profil · entrée navigation + états connecté/déconnecté`. +2. `Auth · écrans créer un compte / se connecter / se déconnecter`. +3. `Sync · statut discret + action Synchroniser maintenant`. +4. `Partage sortant · action Partager sur programme/séance + formulaire email`. +5. `Partages reçus · boîte de réception + accepter/refuser + import local`. \ No newline at end of file diff --git a/.ideai/tickets/46/carnet.md b/.ideai/tickets/46/carnet.md index b59cdf9..0e3b0be 100644 --- a/.ideai/tickets/46/carnet.md +++ b/.ideai/tickets/46/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#46" -version: 6 -updatedBy: {"kind":"user"} -updatedAt: 1784482343900 +version: 7 +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} +updatedAt: 1784488112374 --- diff --git a/.ideai/tickets/46/issue.md b/.ideai/tickets/46/issue.md index fc297b5..4a793ba 100644 --- a/.ideai/tickets/46/issue.md +++ b/.ideai/tickets/46/issue.md @@ -2,15 +2,15 @@ id: "dda499ac-7197-4321-bd2b-a303510b1bbe" number: 46 title: "Créer un serveur headless pour l'application" -status: "open" +status: "qa" priority: "low" sprint: "4237adfd-91eb-45ff-9f32-6ce1daa39822" links: [] agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] createdBy: {"kind":"user"} -updatedBy: {"kind":"user"} +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} createdAt: 1784379695135 -updatedAt: 1784482343900 -version: 6 +updatedAt: 1784488112374 +version: 7 --- J'aimerais que l'application puisse proposer des services online aussi. Un peu a la manière de Heavy avec la possibilité de partager ses séances etc. A la manière de Heavy, j'aimerais que l'on puisse se créer un compte et s'y connecter, synchroniser son historique de seances, ses séances, ses programmes ainsi que ses exercices. Ce ticket est la partie serveur/web API, il faudra que ça soit un serveur headless linux qui tourne dans un container docker. Il faudra que je puisse y acceder via un reverse proxy qui tourne sur une machine différente de celle du serveur headless mais dans le même réseau local. J'y accederais via une adresse https. le container se lancera ensuite à partir d'un docker-compose.yaml dans lequel tu laissera les champs libre de tout ce qui sera necessaire de configurer (adresse ip du reverse proxy, de la machine sur laquelle tourne le docker si necessaire etc.). Pour la création du serveur, créé un subdirectory spécialement pour cette partie serveur. Dans le dossier serveur à lma racine, je veux un script pour pouvoir push le container sur le gitea qui est déjà lié au projet, je veux aussi le docker-compose.yaml demandé. \ No newline at end of file diff --git a/.ideai/tickets/54/carnet.md b/.ideai/tickets/54/carnet.md index 963f4f0..add2e2b 100644 --- a/.ideai/tickets/54/carnet.md +++ b/.ideai/tickets/54/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#54" -version: 4 -updatedBy: {"kind":"user"} -updatedAt: 1784451193831 +version: 5 +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} +updatedAt: 1784487430850 --- diff --git a/.ideai/tickets/54/issue.md b/.ideai/tickets/54/issue.md index 4e2b981..e6b4d92 100644 --- a/.ideai/tickets/54/issue.md +++ b/.ideai/tickets/54/issue.md @@ -2,15 +2,15 @@ id: "7e6894d5-36ad-413b-9fda-be3ba26db6cb" number: 54 title: "Exercice à plusieurs étapes" -status: "open" +status: "qa" priority: "medium" sprint: "1bb8bdf2-9c35-4f53-9a31-1390a47bec63" links: [] agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] createdBy: {"kind":"user"} -updatedBy: {"kind":"user"} +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} createdAt: 1784449895047 -updatedAt: 1784451193831 -version: 4 +updatedAt: 1784487430850 +version: 5 --- Dans certains exercices, il peut être interessant qu'il y ai plusieurs étapes. Par exemple, pour un exercice de dribble, il peut être interessant de dire "dribbler main droite pendant 10 sec, puis dribller main gauche pendant 10 secondes, effectuer un double pas et réaliser 10 pompes" par exemple. Le tout représente alors une répétition mais se décompose en 4 étapes avec deux étapes qui se base sur un temps, et deux étapes sur un nombre de répétitions. J'aimerais donc pouvoir définir des étapes pour chaque exercice. L'ensemble de toutes les étape de l'exercice forme une répetition d'une série. Comme pour une série, chacune des étape doit pouvoir avoir un temps à définir (avec une valeur par défaut obligatoire > 0) ou un nombre de répétitions. Dans le cas d'une étape avec un temps, un chronometre est lancé. Dans les 3 dernieres secondes, un bip retenti pour chaque seconde et quand le temps arrive à 0 le bip est plus long que les autres. dans le cas ou deux étapes avec un temps s'enchainent, le second chronometre part directement à la fin de l'etape précédent. Le but est d'enchainer les étapes, il n'y a pas de pauses entre les différentes étapes. On eput aussi proposer de renseigner un score pour chaque étape de la même façon que pour les série. \ No newline at end of file diff --git a/.ideai/tickets/57/carnet.md b/.ideai/tickets/57/carnet.md index 24b7cb1..cd7fdd3 100644 --- a/.ideai/tickets/57/carnet.md +++ b/.ideai/tickets/57/carnet.md @@ -1,6 +1,7 @@ --- issueRef: "#57" -version: 4 -updatedBy: {"kind":"user"} -updatedAt: 1784482343900 +version: 6 +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} +updatedAt: 1784488107329 --- +Mémoire IdeA écrite par Main : `gametime-online-layer-philosophy`. Couvre : connexion toujours optionnelle, aucune interruption/popup si le serveur est indisponible, principe "le serveur sert l'app, jamais l'inverse" (adapter le schéma serveur aux changements client, ex. étapes d'exercice du chantier #54), et la liste de ce qui doit rester en cache local (profil, pseudo, photo, stats futures) pour un affichage toujours cohérent hors ligne. Cette mémoire est référencée pour les tickets futurs (#63 et suite). \ No newline at end of file diff --git a/.ideai/tickets/57/issue.md b/.ideai/tickets/57/issue.md index ee3c413..0ae2db7 100644 --- a/.ideai/tickets/57/issue.md +++ b/.ideai/tickets/57/issue.md @@ -2,15 +2,15 @@ id: "d5b0bd7e-a021-4824-a278-b2e9c14a1cf6" number: 57 title: "Allimenter la mémoire projet pour l'UI online de l'app" -status: "open" +status: "closed" priority: "medium" sprint: "4237adfd-91eb-45ff-9f32-6ce1daa39822" links: [] agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}] createdBy: {"kind":"user"} -updatedBy: {"kind":"user"} +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} createdAt: 1784481519943 -updatedAt: 1784482343900 -version: 4 +updatedAt: 1784488107329 +version: 6 --- L'application aura donc une couche online. L'application se veut cependant rester un maximum offline sur les ofnctionnalités de base. Je veux que cette couche online soit le plus transparente possible pour l'utilisateur. En clair, si le serveur n'est pas disponible, je ne veux pas de popup qui lui dise qu'il n'est pas connecté. Le but, c'est que les informations soient autant que possibles stockées en local pour tout ce qui est necessaire au bon affichage de l'application sans pour autant donner de mauvaises ou de fausses informations (par exemple, garder les exercices créés, les programmes, les séances etc en local, si plus tard on calcule des statistiques pareilles, les garder aussi en local, si il y a un systeme de compte, garder la photo de profile ainsi que le pseudo etc en local. En bref la référence reste encore une fois Heavy qui fait très bien ça). Ajoute ceci à la mémoire du projet pour les futurs features qui traiteront de ça. \ No newline at end of file diff --git a/.ideai/tickets/62/carnet.md b/.ideai/tickets/62/carnet.md index b9ea2f7..b95ec0e 100644 --- a/.ideai/tickets/62/carnet.md +++ b/.ideai/tickets/62/carnet.md @@ -1,6 +1,6 @@ --- issueRef: "#62" -version: 1 -updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} -updatedAt: 1784481549150 +version: 2 +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} +updatedAt: 1784487426036 --- diff --git a/.ideai/tickets/62/issue.md b/.ideai/tickets/62/issue.md index 59a506e..dcad27c 100644 --- a/.ideai/tickets/62/issue.md +++ b/.ideai/tickets/62/issue.md @@ -2,15 +2,15 @@ id: "a53d84b5-06d9-4683-ac64-9100cb60669f" number: 62 title: "[QA] Validation exercices à étapes, persistance et historique" -status: "open" +status: "qa" priority: "medium" sprint: null links: [{"target":"#54","kind":"relatesTo"},{"target":"#60","kind":"dependsOn"},{"target":"#61","kind":"dependsOn"}] agentRefs: [{"agentId":"7efa512f-3b3a-47b5-ade0-a2dd13073055","role":"assigned"}] createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} -updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} createdAt: 1784481549150 -updatedAt: 1784481549150 -version: 1 +updatedAt: 1784487426036 +version: 2 --- Préparer et exécuter le plan QA pour les exercices à étapes : création/édition et limites max 8, snapshots programme/séance, exécution étapes temps/répétitions, bips 3/2/1/0, enchaînement de chronos, skips étape/passage/série, pause/reprise/kill app, projection historique, édition ponctuelle sans déplacer la progression ni mélanger résultats d'étape et résultats de série. \ No newline at end of file diff --git a/.ideai/tickets/64/carnet.md b/.ideai/tickets/64/carnet.md new file mode 100644 index 0000000..0ba581e --- /dev/null +++ b/.ideai/tickets/64/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#64" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488337677 +--- diff --git a/.ideai/tickets/64/issue.md b/.ideai/tickets/64/issue.md new file mode 100644 index 0000000..042713e --- /dev/null +++ b/.ideai/tickets/64/issue.md @@ -0,0 +1,16 @@ +--- +id: "4a18ba38-9b18-4b39-99a4-ad5a8779e47f" +number: 64 +title: "[DevBackend] Client online : session compte, stockage sécurisé et adapter API" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"}] +agentRefs: [{"agentId":"10ee045b-1c41-479e-ba03-dceed9edd495","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488337677 +updatedAt: 1784488337677 +version: 1 +--- +Ajouter le socle client online côté domain/application/infrastructure : entité locale `UserAccountSession` (profil cache non sensible), ports `AuthTokenStore`, `OnlineAccountRepository`/`RemoteGameTimeApi`, adapter HTTP sous `lib/infrastructure/remote/` basé sur `package:http` avec timeouts courts et erreurs typées silencieuses. Stocker access/refresh token via `flutter_secure_storage`; stocker email/pseudo/avatar/statut sync en Drift. Ne jamais bloquer l'usage offline si l'API échoue. \ No newline at end of file diff --git a/.ideai/tickets/65/carnet.md b/.ideai/tickets/65/carnet.md new file mode 100644 index 0000000..69b42a1 --- /dev/null +++ b/.ideai/tickets/65/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#65" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488343589 +--- diff --git a/.ideai/tickets/65/issue.md b/.ideai/tickets/65/issue.md new file mode 100644 index 0000000..440c06e --- /dev/null +++ b/.ideai/tickets/65/issue.md @@ -0,0 +1,16 @@ +--- +id: "f0c554b7-e388-49b3-aa74-0a47886961ad" +number: 65 +title: "[DevBackend] Sync client incrémentale LWW vers API serveur" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#64","kind":"dependsOn"}] +agentRefs: [{"agentId":"10ee045b-1c41-479e-ba03-dceed9edd495","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488343589 +updatedAt: 1784488343589 +version: 1 +--- +Remplacer le `NoOpSyncGateway` par une orchestration réelle offline-first. Mapper Exercise, Program, WorkoutTemplate, WorkoutHistory et MediaAsset metadata vers `resourceType/clientId/schemaVersion/clientUpdatedAt/deletedAt/payload`. Utiliser `change_log`, `syncState`, `updatedAt` et `localRevision` pour pousser les mutations, stocker localement `serverCursor`, appliquer `/sync/pull` LWW sans résolution interactive, puis marquer les ressources acceptées comme synced. Les erreurs réseau restent silencieuses et déclenchent retry différé. \ No newline at end of file diff --git a/.ideai/tickets/66/carnet.md b/.ideai/tickets/66/carnet.md new file mode 100644 index 0000000..e85cb83 --- /dev/null +++ b/.ideai/tickets/66/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#66" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488349264 +--- diff --git a/.ideai/tickets/66/issue.md b/.ideai/tickets/66/issue.md new file mode 100644 index 0000000..c11351f --- /dev/null +++ b/.ideai/tickets/66/issue.md @@ -0,0 +1,16 @@ +--- +id: "bdcf80ef-1360-4baa-9c49-86b091412ba1" +number: 66 +title: "[DevBackend] Partage client : use cases, inbox cache et import local" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#64","kind":"dependsOn"},{"target":"#65","kind":"dependsOn"}] +agentRefs: [{"agentId":"10ee045b-1c41-479e-ba03-dceed9edd495","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488349264 +updatedAt: 1784488349264 +version: 1 +--- +Ajouter les ports/use cases client pour `POST /shares`, `GET /shares/inbox`, accept/decline/revoke. Stocker une inbox locale lisible offline et une file d'actions de partage en attente si réseau indisponible. À l'acceptation, importer une copie locale indépendante du Programme ou WorkoutTemplate depuis le payload reçu si disponible; l'accusé serveur peut partir ensuite, et la ressource créée côté serveur retombe aussi via le prochain `/sync/pull` sans doublon grâce aux IDs/mapping. \ No newline at end of file diff --git a/.ideai/tickets/67/carnet.md b/.ideai/tickets/67/carnet.md new file mode 100644 index 0000000..6c99454 --- /dev/null +++ b/.ideai/tickets/67/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#67" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488354627 +--- diff --git a/.ideai/tickets/67/issue.md b/.ideai/tickets/67/issue.md new file mode 100644 index 0000000..0b43d48 --- /dev/null +++ b/.ideai/tickets/67/issue.md @@ -0,0 +1,16 @@ +--- +id: "961d38c4-a124-47da-8118-be8b559fc617" +number: 67 +title: "[DevFrontend] Profil et écrans auth optionnels" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#64","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488354627 +updatedAt: 1784488354627 +version: 1 +--- +Ajouter l'entrée `Profil` sur l'accueil et les surfaces UX connecté/déconnecté. Créer les écrans `Créer un compte`, `Se connecter`, confirmation logout. La connexion reste optionnelle, jamais affichée au démarrage. Erreurs serveur/réseau uniquement inline dans les formulaires, jamais popup globale. Profil affiché depuis cache local même hors ligne. \ No newline at end of file diff --git a/.ideai/tickets/68/carnet.md b/.ideai/tickets/68/carnet.md new file mode 100644 index 0000000..6d59d99 --- /dev/null +++ b/.ideai/tickets/68/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#68" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488359218 +--- diff --git a/.ideai/tickets/68/issue.md b/.ideai/tickets/68/issue.md new file mode 100644 index 0000000..7c30873 --- /dev/null +++ b/.ideai/tickets/68/issue.md @@ -0,0 +1,16 @@ +--- +id: "ac634023-e7ba-4588-9c7c-9f93c12d9aad" +number: 68 +title: "[DevFrontend] Statut de synchronisation discret et action manuelle" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#65","kind":"dependsOn"},{"target":"#67","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488359218 +updatedAt: 1784488359218 +version: 1 +--- +Afficher la section Synchronisation dans Profil : en cours, à jour, jamais faite, retry automatique, hors ligne. Ajouter `Synchroniser maintenant`. Ne jamais afficher de snackbar/dialog d'échec réseau hors contexte Profil. Optionnel : sous-titre discret de l'entrée Profil sur l'accueil. \ No newline at end of file diff --git a/.ideai/tickets/69/carnet.md b/.ideai/tickets/69/carnet.md new file mode 100644 index 0000000..7e74077 --- /dev/null +++ b/.ideai/tickets/69/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#69" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488364646 +--- diff --git a/.ideai/tickets/69/issue.md b/.ideai/tickets/69/issue.md new file mode 100644 index 0000000..b33e4df --- /dev/null +++ b/.ideai/tickets/69/issue.md @@ -0,0 +1,16 @@ +--- +id: "05482a20-83a6-44da-b0c6-d065a5a1ffba" +number: 69 +title: "[DevFrontend] Partage sortant et boîte de réception" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#66","kind":"dependsOn"},{"target":"#67","kind":"dependsOn"}] +agentRefs: [{"agentId":"9933c93a-b8a1-4164-a3bb-7063fdad747d","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488364646 +updatedAt: 1784488364646 +version: 1 +--- +Ajouter l'action `Partager` dans les écrans programme/séance enregistrés, formulaire email destinataire, états déconnecté et messages discrets. Ajouter `Profil > Partages reçus`, liste inbox, accepter/refuser, import local des programmes/séances reçus. Les erreurs réseau restent inline/neutres; accepter un partage rend la copie disponible offline. \ No newline at end of file diff --git a/.ideai/tickets/70/carnet.md b/.ideai/tickets/70/carnet.md new file mode 100644 index 0000000..9b7f7e3 --- /dev/null +++ b/.ideai/tickets/70/carnet.md @@ -0,0 +1,6 @@ +--- +issueRef: "#70" +version: 1 +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedAt: 1784488373778 +--- diff --git a/.ideai/tickets/70/issue.md b/.ideai/tickets/70/issue.md new file mode 100644 index 0000000..287d131 --- /dev/null +++ b/.ideai/tickets/70/issue.md @@ -0,0 +1,16 @@ +--- +id: "cb93ee24-0041-4926-b0c6-3ab26aab46ec" +number: 70 +title: "[QA] Validation client online offline-first" +status: "open" +priority: "medium" +sprint: null +links: [{"target":"#63","kind":"relatesTo"},{"target":"#68","kind":"dependsOn"},{"target":"#69","kind":"dependsOn"}] +agentRefs: [{"agentId":"7efa512f-3b3a-47b5-ade0-a2dd13073055","role":"assigned"}] +createdBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +updatedBy: {"kind":"agent","agent_id":"f8f40941-ecf7-4830-b9de-8818a099f448"} +createdAt: 1784488373778 +updatedAt: 1784488373778 +version: 1 +--- +Valider le chantier client online : app utilisable sans compte, login/register/logout optionnels, tokens persistés, profil cache hors ligne, sync push/pull LWW, erreurs réseau silencieuses, retry différé, partage sortant ciblé, inbox, accept/decline/revoke, import local offline, absence de régression sur les parcours locaux exercices/programmes/séances/historique. \ No newline at end of file diff --git a/.ideai/tickets/counter.json b/.ideai/tickets/counter.json index 79e9b2a..d70a5bd 100644 --- a/.ideai/tickets/counter.json +++ b/.ideai/tickets/counter.json @@ -1,3 +1,3 @@ { - "nextNumber": 64 + "nextNumber": 71 } \ No newline at end of file diff --git a/.ideai/tickets/index.json b/.ideai/tickets/index.json index 01a0f12..abc5a73 100644 --- a/.ideai/tickets/index.json +++ b/.ideai/tickets/index.json @@ -525,13 +525,13 @@ "issueRef": "#46", "path": "46", "title": "Créer un serveur headless pour l'application", - "status": "open", + "status": "qa", "priority": "low", "sprint": "4237adfd-91eb-45ff-9f32-6ce1daa39822", "assignedAgentIds": [ "57695b92-24d0-4876-837c-76116e70a6ae" ], - "updatedAt": 1784482343900 + "updatedAt": 1784488112374 }, { "issueRef": "#47", @@ -607,13 +607,13 @@ "issueRef": "#54", "path": "54", "title": "Exercice à plusieurs étapes", - "status": "open", + "status": "qa", "priority": "medium", "sprint": "1bb8bdf2-9c35-4f53-9a31-1390a47bec63", "assignedAgentIds": [ "57695b92-24d0-4876-837c-76116e70a6ae" ], - "updatedAt": 1784451193831 + "updatedAt": 1784487430850 }, { "issueRef": "#56", @@ -631,13 +631,13 @@ "issueRef": "#57", "path": "57", "title": "Allimenter la mémoire projet pour l'UI online de l'app", - "status": "open", + "status": "closed", "priority": "medium", "sprint": "4237adfd-91eb-45ff-9f32-6ce1daa39822", "assignedAgentIds": [ "57695b92-24d0-4876-837c-76116e70a6ae" ], - "updatedAt": 1784482343900 + "updatedAt": 1784488107329 }, { "issueRef": "#58", @@ -691,13 +691,13 @@ "issueRef": "#62", "path": "62", "title": "[QA] Validation exercices à étapes, persistance et historique", - "status": "open", + "status": "qa", "priority": "medium", "sprint": null, "assignedAgentIds": [ "7efa512f-3b3a-47b5-ade0-a2dd13073055" ], - "updatedAt": 1784481549150 + "updatedAt": 1784487426036 }, { "issueRef": "#63", @@ -710,6 +710,90 @@ "57695b92-24d0-4876-837c-76116e70a6ae" ], "updatedAt": 1784482673684 + }, + { + "issueRef": "#64", + "path": "64", + "title": "[DevBackend] Client online : session compte, stockage sécurisé et adapter API", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "10ee045b-1c41-479e-ba03-dceed9edd495" + ], + "updatedAt": 1784488337677 + }, + { + "issueRef": "#65", + "path": "65", + "title": "[DevBackend] Sync client incrémentale LWW vers API serveur", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "10ee045b-1c41-479e-ba03-dceed9edd495" + ], + "updatedAt": 1784488343589 + }, + { + "issueRef": "#66", + "path": "66", + "title": "[DevBackend] Partage client : use cases, inbox cache et import local", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "10ee045b-1c41-479e-ba03-dceed9edd495" + ], + "updatedAt": 1784488349264 + }, + { + "issueRef": "#67", + "path": "67", + "title": "[DevFrontend] Profil et écrans auth optionnels", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784488354627 + }, + { + "issueRef": "#68", + "path": "68", + "title": "[DevFrontend] Statut de synchronisation discret et action manuelle", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784488359218 + }, + { + "issueRef": "#69", + "path": "69", + "title": "[DevFrontend] Partage sortant et boîte de réception", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "9933c93a-b8a1-4164-a3bb-7063fdad747d" + ], + "updatedAt": 1784488364646 + }, + { + "issueRef": "#70", + "path": "70", + "title": "[QA] Validation client online offline-first", + "status": "open", + "priority": "medium", + "sprint": null, + "assignedAgentIds": [ + "7efa512f-3b3a-47b5-ade0-a2dd13073055" + ], + "updatedAt": 1784488373778 } ] } \ No newline at end of file