This commit is contained in:
2026-09-29 15:07:05 +02:00
parent 06fe43bba3
commit 4de305bbf4
12 changed files with 837 additions and 97 deletions

View File

@@ -1,21 +1,113 @@
# Étape 4 — Vue 2 : profil complet
> **Prérequis :** [étape 3 — recherche](etape-03-vue-recherche.md) (navigation vers vue 2 avec login valide).
> **Prérequis :** [étape 3 — recherche](etape-03-vue-recherche.md) (navigation vers vue 2 avec un login valide).
## Objectif sujet (partie obligatoire)
## Objectif sujet
| Exigence | Détail |
|----------|--------|
| ≥ 4 infos | login, email, mobile, level, location, wallet, evaluations, … |
| Photo | Image de profil (`image_url` / `image.link` selon API) |
| Skills | Niveau + pourcentage |
| Projets | Terminés **y compris failed** |
| Retour | Bouton retour vers vue 1 |
| Layout | Responsive (étape 5 peut affiner) |
| 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 |
## API
---
- `GET /v2/users/{id}` avec champs / includes selon apidoc (`cursus_users`, `projects_users`, `skills`, etc.).
- Token : toujours via `IntraAuthService`.
## Parcours utilisateur (vue 2)
Guide détaillé à rédiger quand l’étape 3 est codée — demande l’implémentation pas à pas dans le chat.
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 <access_token>
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<String, dynamic> 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).