# Étape 4 — Vue 2 : profil complet > **Prérequis :** [étape 3 — recherche](etape-03-vue-recherche.md) (navigation vers vue 2 avec un login valide). ## Objectif sujet | Exigence | Cette étape | |----------|-------------| | ≥ 4 infos + photo | login (titre / avatar), email, téléphone, niveau cursus, localisation, wallet | | Skills | Niveau entier + **pourcentage** (partie décimale du `level` API) | | Projets | Statuts **`finished`** et **`failed`** | | Erreurs | Réseau, 401, réponse API inattendue — message + réessai | | Token | Un seul OAuth : `IntraAuthService` pour tous les `GET` | | Retour vue 1 | `AppBar` + bouton retour système (`Navigator.pop`) | | Layout | `LayoutBuilder` : colonne sur téléphone, 2 colonnes si largeur ≥ 720 px | --- ## Parcours utilisateur (vue 2) 1. Arrivée depuis la recherche avec un objet `user` minimal (`id`, `login`, parfois `image`). 2. **Chargement** : appels API en parallèle (spinner plein écran). 3. **Succès** : photo, carte « Informations », liste skills, liste projets. 4. **Échec** : message rouge + bouton **Réessayer**. 5. **Retour** : flèche AppBar → vue 1, nouvelle recherche possible. --- ## API 42 utilisée Doc : [api.intra.42.fr/apidoc](https://api.intra.42.fr/apidoc) | Donnée | Endpoint | Notes | |--------|----------|--------| | Fiche user | *(déjà renvoyée par la recherche étape 3)* | email, phone, location, wallet, image — pas de 2ᵉ `GET /users/{id}` pour limiter le quota | | Fiche complète | `GET /v2/users/{login}` | inclut souvent `cursus_users` + `skills` par cursus | | Niveau / skills | `cursus_users[].skills` | pas `/experiences` (souvent **403** avec token étudiant) | | Projets | `GET /v2/users/{id}/projects_users?page[size]=100` | scope OAuth **`projects`** ; filtrer côté app : `finished` / `failed` | Tous les appels : ```http Authorization: Bearer Accept: application/json ``` **Confidentialité :** certains champs peuvent être masqués par l’API selon le profil consulté — afficher `—` si absent (sujet OAuth). ### Skills — niveau et pourcentage L’API renvoie un `level` flottant (ex. `8.42`) : - **Niveau affiché** : partie entière (`8`) - **Pourcentage** : partie décimale × 100 (`42 %`) --- ## Fichiers (étape 4) ``` lib/ ├── services/ │ ├── intra_auth_service.dart # existant — headers Bearer │ ├── intra_user_service.dart # existant — recherche │ └── intra_profile_service.dart # NEW — agrégation profil └── screens/ └── profile_screen.dart # StatefulWidget — chargement + UI ``` ### `IntraProfileService` - `fetchProfile(Map summaryUser)` → `IntraUserProfile` - Requêtes **séquentielles** (pause ~450 ms) + retry sur 429 (`intra_http.dart`) - Cache profil 2 min par `user_id` pour éviter de re-taper l’API au retour arrière - Mêmes exceptions que l’étape 3 : `IntraNetworkException`, `IntraUnauthorizedException`, etc. ### `ProfileScreen` - Reçoit le `user` de l’étape 3 - `initState` → `_load()` - Pas de second OAuth --- ## Gestion des erreurs (vue 2) | Situation | Comportement | |-----------|----------------| | Pas de réseau | Message + **Réessayer** | | Token expiré (401) | « Session expirée… » — reconnecter depuis vue 1 | | Trop de requêtes (429) | Message dédié ; retry automatique côté client + attendre avant de réessayer | | Accès refusé (403) | Scopes `public` + `projects` sur l’app OAuth, puis **reconnexion** dans l’app | | ID manquant | Erreur métier explicite | | Listes vides skills / projets | Texte informatif, pas de crash | --- ## Critères « étape 4 terminée » | # | Test | |---|------| | 1 | Login valide → photo + au moins 4 champs remplis ou `—` | | 2 | Section skills avec niveau **et** pourcentage | | 3 | Au moins un projet `finished` ou `failed` visible (si l’étudiant en a) | | 4 | Mode avion sur vue 2 → erreur + réessai | | 5 | Retour → vue 1 sans perdre la session OAuth | | 6 | Largeur tablette / fenêtre large → mise en page 2 colonnes | --- ## Ensuite — étape 5 → Affiner navigation, contraintes et polish responsive sur **les deux vues** (`etape-05-navigation-layout.md` à rédiger).