diff --git a/docs/pedagogie/00-plan-preparation-soutenance.md b/docs/pedagogie/00-plan-preparation-soutenance.md index a3747cf..34bd197 100644 --- a/docs/pedagogie/00-plan-preparation-soutenance.md +++ b/docs/pedagogie/00-plan-preparation-soutenance.md @@ -2,7 +2,7 @@ > **Méthode :** pour chaque lot, l’agent décrit ce qu’il compte faire → **tu valides ou ajustes** → on exécute → on coche. -**Dernière mise à jour :** lot 3 livré ([fiche-soutenance-orale.md](fiche-soutenance-orale.md)). +**Dernière mise à jour :** lot 4 livré (commentaires `lib/` + DEBUG conservé). --- @@ -15,7 +15,7 @@ | `.env` | **Uniquement** `swifty_companion/.env` en local ; **jamais** dans Git ; `.env.example` versionné (sujet : échec direct si secrets publics) | | Fiche orale (lot 3) | **Déroulé de soutenance** détaillé (sujet, points critiques, réalisation, commandes expliquées) — un fichier structuré logiquement | | Commentaires code | **Français** — sujet ne l’interdit pas | -| Bouton DEBUG | Voir [fiche-depot-soutenance.md §8](fiche-depot-soutenance.md) — pas lié aux secrets ; visible seulement en build debug ; choix garder/retirer avant rendu final | +| Bouton DEBUG | **Garder** — aide à démontrer le bonus refresh en `flutter run` ; absent en release (`kDebugMode`) — [fiche-depot-soutenance.md §8](fiche-depot-soutenance.md) | --- @@ -37,7 +37,7 @@ | B | 1 Dépôt + branche | ✅ [fiche-depot-soutenance.md](fiche-depot-soutenance.md) | | C | 2 README rendu autonome | ✅ fait | | D | 3 Fiche soutenance orale | ✅ [fiche-soutenance-orale.md](fiche-soutenance-orale.md) | -| E | 4 Commentaires `lib/` | ☐ à faire | +| E | 4 Commentaires `lib/` | ✅ fait | --- @@ -47,19 +47,18 @@ - `.env.example` commenté (racine + `swifty_companion/.env.example`). - `swifty_companion/README.md` : pointeur court vers le README racine. -**Prochaine étape :** dis **« OK lot 4 »** pour les commentaires FR dans `lib/` (+ décision bouton DEBUG). - ---- - ## Lot 3 — Livré - [fiche-soutenance-orale.md](fiche-soutenance-orale.md) — déroulé oral, commandes commentées, démo, FAQ. --- -## Lot 4 — Commentaires code +## Lot 4 — Livré -`lib/` en français, sans lien vers doc externe ; décision DEBUG bouton documentée en oral. +- Commentaires / dartdoc en **français** dans `swiftyCompanion/swifty_companion/lib/`. +- Bouton **DEBUG** conservé (`kDebugMode`) pour la démo du bonus refresh — pas présent en APK release. + +**Plan préparation :** tous les lots 0–4 sont cochés ; prochaine action opérationnelle = branche `soutenance` + push Vogosphere ([fiche-depot-soutenance.md](fiche-depot-soutenance.md)). --- @@ -71,4 +70,4 @@ | 1 | ✅ | | 2 | ✅ | | 3 | ✅ | -| 4 | ☐ | +| 4 | ✅ | diff --git a/docs/pedagogie/fiche-depot-soutenance.md b/docs/pedagogie/fiche-depot-soutenance.md index d1368b7..f49a05a 100644 --- a/docs/pedagogie/fiche-depot-soutenance.md +++ b/docs/pedagogie/fiche-depot-soutenance.md @@ -204,9 +204,9 @@ Dans `search_screen.dart`, un bouton **« DEBUG : simuler token expiré »** n |----------|---------| | Le sujet l’interdit ? | **Non** — rien dans le PDF sur les boutons de debug. | | Risque en soutenance ? | L’évaluateur peut le voir en `flutter run` ; tu peux l’expliquer pour le **bonus refresh**, ou le retirer avant rendu final si tu veux une UI plus « propre ». | -| Décision à prendre (lot 4 ou avant push) | **Garder** (démo bonus) **ou** **retirer** le bouton — les deux sont défendables. | +| Décision (lot 4) | **Garder** — le bonus sujet = refresh token ; le bouton ne fait qu’**invalider l’access** en debug pour tester le refresh sans attendre l’expiration. Absent en `flutter build apk --release`. | -→ Détails oraux : [fiche-soutenance-orale.md](fiche-soutenance-orale.md) (lot 3, à rédiger). +→ Démo orale : [fiche-soutenance-orale.md](fiche-soutenance-orale.md) §7. --- diff --git a/docs/pedagogie/fiche-soutenance-orale.md b/docs/pedagogie/fiche-soutenance-orale.md index 8352c19..dc30795 100644 --- a/docs/pedagogie/fiche-soutenance-orale.md +++ b/docs/pedagogie/fiche-soutenance-orale.md @@ -209,7 +209,7 @@ Si refresh échoue → IntraUnauthorizedException, l’utilisateur se reconnecte **Si évaluateur en release :** montrer `flutter test` sur `intra_oauth_tokens_test.dart` + expliquer le flux §7. -**Bouton DEBUG au rendu final :** voir [fiche-depot-soutenance.md §8](fiche-depot-soutenance.md) — garder (visible seulement en debug) ou retirer avant push ; décision à trancher au lot 4. +**Bouton DEBUG au rendu :** **conservé** — visible uniquement en build debug (`kDebugMode`), utile pour prouver le bonus refresh ; absent en release. Voir [fiche-depot-soutenance.md §8](fiche-depot-soutenance.md). --- diff --git a/swiftyCompanion/swifty_companion/lib/main.dart b/swiftyCompanion/swifty_companion/lib/main.dart index 5a813cb..8a6c3ee 100644 --- a/swiftyCompanion/swifty_companion/lib/main.dart +++ b/swiftyCompanion/swifty_companion/lib/main.dart @@ -1,3 +1,4 @@ +// Point d'entrée : charge les secrets depuis `.env` (asset, non versionné) puis vue recherche. import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; diff --git a/swiftyCompanion/swifty_companion/lib/screens/profile_screen.dart b/swiftyCompanion/swifty_companion/lib/screens/profile_screen.dart index e550dc2..aab5972 100644 --- a/swiftyCompanion/swifty_companion/lib/screens/profile_screen.dart +++ b/swiftyCompanion/swifty_companion/lib/screens/profile_screen.dart @@ -4,7 +4,7 @@ import '../services/intra_api_exception.dart'; import '../services/intra_profile_service.dart'; import '../theme/swifty_ui.dart'; -/// Vue 2 — profil complet (étape 4). +/// Vue 2 — photo, ≥ 4 champs, skills (niveau + %), projets validés et failed. class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key, required this.user}); diff --git a/swiftyCompanion/swifty_companion/lib/screens/search_screen.dart b/swiftyCompanion/swifty_companion/lib/screens/search_screen.dart index a84d5fc..f2e1a31 100644 --- a/swiftyCompanion/swifty_companion/lib/screens/search_screen.dart +++ b/swiftyCompanion/swifty_companion/lib/screens/search_screen.dart @@ -186,6 +186,7 @@ class _SearchScreenState extends State { loading: _searching, onPressed: _search, ), + // Bonus refresh : invalide l'access token en mémoire (build debug uniquement). if (kDebugMode) ...[ const SizedBox(height: 12), TextButton( diff --git a/swiftyCompanion/swifty_companion/lib/services/intra_auth_service.dart b/swiftyCompanion/swifty_companion/lib/services/intra_auth_service.dart index 3191618..173d38b 100644 --- a/swiftyCompanion/swifty_companion/lib/services/intra_auth_service.dart +++ b/swiftyCompanion/swifty_companion/lib/services/intra_auth_service.dart @@ -139,8 +139,7 @@ class IntraAuthService { _refreshToken = refresh; } - /// Simule un access token expiré (garde le refresh) — tests bonus en debug. - @visibleForTesting + /// Simule un access token expiré (garde le refresh) — démo bonus, appelé depuis l’UI en `kDebugMode`. void debugInvalidateAccessToken() { if (!canRefresh) return; _accessToken = 'invalid_access_token_for_refresh_test'; diff --git a/swiftyCompanion/swifty_companion/lib/services/intra_http.dart b/swiftyCompanion/swifty_companion/lib/services/intra_http.dart index 7c94a75..87d889b 100644 --- a/swiftyCompanion/swifty_companion/lib/services/intra_http.dart +++ b/swiftyCompanion/swifty_companion/lib/services/intra_http.dart @@ -50,6 +50,7 @@ Duration _retryDelay(http.Response response, int attempt) { return Duration(milliseconds: ms.clamp(800, 8000)); } +/// Lance une [IntraApiException] si le code HTTP n'est pas 200. void ensureIntraOk(http.Response response) { if (response.statusCode == 401) { throw const IntraUnauthorizedException(); @@ -67,6 +68,7 @@ void ensureIntraOk(http.Response response) { } } +/// Décode le corps JSON après contrôle du statut HTTP. dynamic decodeIntraBody(http.Response response) { ensureIntraOk(response); return jsonDecode(response.body); diff --git a/swiftyCompanion/swifty_companion/lib/services/intra_profile_service.dart b/swiftyCompanion/swifty_companion/lib/services/intra_profile_service.dart index 7a1d99b..c809922 100644 --- a/swiftyCompanion/swifty_companion/lib/services/intra_profile_service.dart +++ b/swiftyCompanion/swifty_companion/lib/services/intra_profile_service.dart @@ -55,10 +55,12 @@ class _ProfileCacheEntry { final DateTime fetchedAt; } +/// Agrège fiche user, skills (cursus, pas `/experiences`) et `projects_users` (scope `projects`). class IntraProfileService { IntraProfileService._(); static final IntraProfileService instance = IntraProfileService._(); + /// Cache court + délai entre GET pour limiter les 429 sur l'API 42. static const Duration _cacheTtl = Duration(minutes: 2); static const Duration _betweenRequests = Duration(milliseconds: 450); diff --git a/swiftyCompanion/swifty_companion/lib/services/intra_user_service.dart b/swiftyCompanion/swifty_companion/lib/services/intra_user_service.dart index f9ac44d..1e9d8e6 100644 --- a/swiftyCompanion/swifty_companion/lib/services/intra_user_service.dart +++ b/swiftyCompanion/swifty_companion/lib/services/intra_user_service.dart @@ -8,6 +8,7 @@ import 'intra_api_exception.dart'; import 'intra_auth_service.dart'; import 'intra_http.dart'; +/// Recherche d'un login via `GET /v2/users?filter[login]=` (même token OAuth que le profil). class IntraUserService { IntraUserService._(); static final IntraUserService instance = IntraUserService._(); diff --git a/swiftyCompanion/swifty_companion/lib/theme/swifty_ui.dart b/swiftyCompanion/swifty_companion/lib/theme/swifty_ui.dart index e6bf51f..fefb090 100644 --- a/swiftyCompanion/swifty_companion/lib/theme/swifty_ui.dart +++ b/swiftyCompanion/swifty_companion/lib/theme/swifty_ui.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -/// Constantes UI partagées (étape 5 — layout). +/// Constantes UI partagées (layout responsive, thème 42). abstract final class SwiftyUi { static const Color brand = Color(0xFF00BABC); @@ -66,6 +66,7 @@ class ResponsiveScrollBody extends StatelessWidget { } } +/// Message d'erreur centré (recherche ou profil). class ErrorMessage extends StatelessWidget { const ErrorMessage({super.key, required this.message}); @@ -81,6 +82,7 @@ class ErrorMessage extends StatelessWidget { } } +/// Bouton principal avec indicateur de chargement. class LoadingFilledButton extends StatelessWidget { const LoadingFilledButton({ super.key,