Project

General

Profile

Actions

Feature #124

closed
CD

Récupérer automatiquement la photo de profil LinkedIn à la connexion

Feature #124: Récupérer automatiquement la photo de profil LinkedIn à la connexion

Added by Client Dashboard about 2 months ago. Updated about 2 months ago.

Status:
Shipped
Priority:
Normal
Assignee:
-
Start date:
06/15/2026
Due date:
% Done:

100%

Estimated time:
spec_ref:

## Spec — Récupération auto de la photo de profil LinkedIn à la connexion

### Décision de découpage
**Pas de découpage (unité de dev unique, fullstack).** C'est une correction de bug à surface unique sur GetYourJob (Next.js) : on reproduit pour le provider LinkedIn la logique d'avatar déjà en place pour Google, plus un fallback d'initiales. Le callback OAuth `profile` (`src/lib/auth.ts`) et le composant d'affichage d'avatar partagent une même chaîne de priorité (upload manuel > provider > initiales) ; les scinder créerait des sous-tâches triviales et redondantes.

### Problème
À la connexion LinkedIn, la photo de profil du candidat n'est pas récupérée automatiquement (contrairement à Google) → profil incomplet / icône vide tant que l'utilisateur n'uploade pas manuellement une photo.

### Approche
1. **Auth / mapping provider (NextAuth — `src/lib/auth.ts`)** : dans le provider LinkedIn (OIDC "Sign In with LinkedIn using OpenID Connect"), mapper la claim `picture` du profil vers `image`, comme le fait déjà le provider Google. Vérifier que le scope `profile` (OpenID Connect) est bien demandé — sinon `picture` est absent côté API (à valider avant de conclure à un bug de code).
2. **Persistance** : à la création/màj de l'utilisateur, renseigner l'URL d'avatar provider uniquement si l'utilisateur n'a pas déjà une photo uploadée manuellement (l'upload manuel prime toujours). Réutiliser la même logique de priorité que Google.
3. **Affichage / fallback (composant avatar)** : si aucune photo (ni manuelle, ni provider) n'est disponible, afficher une icône générique ronde avec les initiales "DP" sur fond coloré (ex. bleu clair). Comportement identique quel que soit le provider.
4. **Aucune étape de confirmation** : récupération et affichage automatiques, sans prompt utilisateur.

### Critères d'acceptation
- [ ] La photo LinkedIn est récupérée automatiquement à la connexion et affichée en haut à droite du profil candidat.
- [ ] Sans photo LinkedIn, une icône ronde avec initiales "DP" sur fond coloré s'affiche.
- [ ] Une photo uploadée manuellement dans GetYourJob prime toujours sur la photo automatique (LinkedIn ou Google).
- [ ] Comportement cohérent avec Google (même logique de fallback et de priorité).
- [ ] Aucun message de confirmation n'est demandé à l'utilisateur.

### Routage
Fullstack (modif config NextAuth + persistance utilisateur côté serveur, plus composant avatar côté présentation). Petite surface, à traiter en TDD.

### Estimation
S

build_status:
build_number:
ci_run_url:
scan_status:
scan_report_url:
deploy_status:
preprod_url:
https://github.com/omdev-tech/GetYourJob/pull/267
deployed_at:
branch:
feat/124-linkedin-profile-picture
pr_url:
https://github.com/omdev-tech/GetYourJob/pull/255
security_key:
severity:
paused:

Description

### Problème
Actuellement, lors de la connexion via LinkedIn, la photo de profil du candidat n'est pas récupérée automatiquement, contrairement à la connexion via Google. Cela entraîne un affichage incomplet du profil (icône vide ou par défaut) si l'utilisateur n'a pas uploadé manuellement une photo dans son profil GetYourJob.

### Contexte
- Pour les connexions Google, la photo de profil est bien récupérée automatiquement (ou remplacée par une photo manuelle si l'utilisateur en ajoute une).
- Pour les connexions LinkedIn, aucune photo n'est récupérée, ce qui crée une incohérence dans l'expérience utilisateur.
- Le comportement attendu est une récupération automatique de la photo LinkedIn, sans étape de confirmation, avec un fallback vers une icône générique si aucune photo n'est disponible.

### Comportement proposé
1. Récupérer automatiquement la photo de profil LinkedIn dès la connexion (comme pour Google).
2. Si aucune photo n'est disponible, afficher une icône générique ronde avec les initiales "DP" et un fond coloré (ex. : bleu clair).
3. La photo manuelle uploadée par l'utilisateur dans son profil GetYourJob prime toujours sur la photo automatique (LinkedIn/Google).

## Acceptance criteria
['- [ ] La photo de profil LinkedIn est récupérée automatiquement lors de la connexion et affichée en haut à droite du profil candidat.', '- [ ] Si aucune photo LinkedIn n\'est disponible, une icône générique ronde avec les initiales "DP" et un fond coloré est affichée.', "- [ ] La photo manuelle uploadée par l'utilisateur dans son profil GetYourJob remplace toujours la photo automatique (LinkedIn ou Google).", '- [ ] Le comportement est cohérent avec celui de la connexion Google (même logique de fallback et de priorité).', "- [ ] Aucun message de confirmation n'est demandé à l'utilisateur pour l'affichage de la photo LinkedIn."]

## Classification
- bug

## Complexity
- 5/10 — L'intégration avec l'API LinkedIn pour récupérer la photo nécessite une gestion des permissions et des erreurs, mais le fallback et la logique de priorité sont déjà partiellement implémentés pour Google.


Files

Capture d’écran du 2026-06-15 12-55-46.png (25.6 KB) Capture d’écran du 2026-06-15 12-55-46.png Client Dashboard, 06/15/2026 10:57 AM
124-rapport-technique-qa.txt (5.45 KB) 124-rapport-technique-qa.txt Redmine Admin, 06/15/2026 12:59 PM
124-avatar-states.png (58.7 KB) 124-avatar-states.png Redmine Admin, 06/15/2026 12:59 PM
Capture d’écran du 2026-06-15 12-55-46.png
124-avatar-states.png

RA Updated by Redmine Admin about 2 months ago Actions #1

  • Status changed from Submitted to Spec

RA Updated by Redmine Admin about 2 months ago Actions #2

  • % Done changed from 0 to 100
  • spec_ref updated (diff)

RA Updated by Redmine Admin about 2 months ago Actions #3

  • Status changed from Spec to In development

RA Updated by Redmine Admin about 2 months ago Actions #4

  • Status changed from In development to QA
  • branch set to feat/124-linkedin-profile-picture
  • pr_url set to https://github.com/omdev-tech/GetYourJob/pull/255

PR ouverte vers `dev` : https://github.com/omdev-tech/GetYourJob/pull/255

Implémentation (fullstack, TDD test-first) :
- Auth (`src/lib/auth.ts` + nouveau `src/lib/auth-providers.ts`) : la claim OIDC `picture` de LinkedIn est mappée vers `image`, exactement comme Google. Vérifié au préalable : le scope `profile` était **déjà demandé** et le mapping `picture`→`image` déjà présent — pas de bug de scope. Le seam pur `mapLinkedInProfileToUser()` rend le mapping testable.
- Priorité : la photo provider n'est écrite qu'à la création du compte (PrismaAdapter), jamais réécrite à la reconnexion → une photo uploadée manuellement (DeveloperProfile.profileImageUrl) prime toujours. Aucune confirmation demandée.
- Affichage (`src/components/atoms/UserAvatar.tsx`, intégré dans `ConnectionButton.tsx` — avatar en haut à droite) : avatar rond, chaîne upload manuel > photo provider > initiales « DP » sur fond bleu clair quand aucune photo.

Tests verts : `auth-providers.test.ts` (4) + `UserAvatar.test.tsx` (7). tsc : aucune nouvelle erreur sur les fichiers touchés (erreurs pré-existantes hors périmètre). `npm run build` : Compiled successfully.

Statut : In development → QA.

RA Updated by Redmine Admin about 2 months ago Actions #7

## Rapport QA — VERDICT : PASS (5/5 critères OK)

Smoke test sur un worktree QA dédié (`.worktrees/qa-124`, depuis `origin/feat/124-linkedin-profile-picture`, commit `b99ff5d`, PR #255). Le ticket reste en **QA** (promotion preprod = autre agent).

### Critères d'acceptation
| # | Critère | Résultat | Preuve |
|---|---------|----------|--------|
| AC1 | Photo LinkedIn récupérée auto à la connexion, affichée en haut à droite | **OK** | `auth.ts` provider LinkedIn (scope `profile` déjà demandé) → `profile()` délègue à `mapLinkedInProfileToUser()` qui mappe `picture`→`image` (`auth-providers.ts` l.41) ; `ConnectionButton.tsx` rend `<UserAvatar image={user.image}>` dans l'avatar coin haut-droit |
| AC2 | Sans photo : icône ronde, initiales « DP », fond coloré | **OK** | `UserAvatar.tsx` l.70-81 `div rounded-full bg-sky-100 text-sky-700`, `getAvatarInitials` défaut « DP ». Preuve visuelle jointe (`124-avatar-states.png`) |
| AC3 | Photo manuelle GetYourJob prime toujours sur l'auto (LinkedIn/Google) | **OK** | Double garantie : (a) l'image provider n'est écrite qu'à la création du compte, jamais réécrite à la reconnexion ; (b) `UserAvatar.tsx` l.53 `resolvedImageUrl = profileImageUrl \|\| image` → l'upload manuel passe avant le provider |
| AC4 | Comportement cohérent avec Google (même fallback + priorité) | **OK** | Même forme de retour `profile()` que GoogleProvider, même composant `UserAvatar` et même chaîne de priorité quel que soit le provider |
| AC5 | Aucun message de confirmation demandé | **OK** | Aucun prompt/modal sur le chemin récupération/affichage ; mapping + rendu purement automatiques |

### Tests exécutés
- **Vitest (suites touchées)** : `auth-providers.test.ts` (4) + `UserAvatar.test.tsx` (7) → **11/11 verts**.
- **tsc --noEmit (fichiers touchés)** : `auth-providers.ts`, `UserAvatar.tsx`, `ConnectionButton.tsx` → **0 erreur**. Une (1) erreur subsiste dans `auth.ts:46` (typage Prisma `subscription.createMany`) — **vérifiée pré-existante** : bloc identique sur `origin/dev`, **non touché** par le diff #124 → hors périmètre, non bloquant.
- **Inspection source** : chaîne de priorité et persistance confirmées (voir tableau).

### Pièces jointes
- `124-rapport-technique-qa.txt` — rapport technique détaillé (secrets filtrés).
- `124-avatar-states.png` — preuve visuelle des 3 états (manuel / provider / initiales « DP »).

Aucun secret/token n'a été imprimé ni joint. Statut conservé : **QA**.

RA Updated by Redmine Admin about 2 months ago Actions #8

  • Status changed from QA to Preprod
  • preprod_url set to https://github.com/omdev-tech/GetYourJob/pull/267

Déployé sur preprod ✅ — PR #267 (dev→preprod) mergée ; builds Jenkins App/preprod #74 + Cron/preprod #71 = SUCCESS. En attente de validation sur preprod ; promotion preprod→master volontairement en attente (décision utilisateur).

RA Updated by Redmine Admin about 2 months ago Actions #9

  • Status changed from Preprod to Shipped

Livré en production ✅ — déployé sur master/prod (App/master #26 SUCCESS, prod-app à jour 2026-06-16). Clôturé.

Actions

Also available in: PDF Atom