✨ Add skill projects modal with Headless UI (Story 2.5)
- Add GET /skills/{slug}/projects endpoint with level progression
- Install @headlessui/vue for accessible modal
- Create SkillProjectsModal with Dialog component:
- Focus trap and keyboard navigation (automatic)
- Fade + scale transitions with backdrop blur
- prefers-reduced-motion support
- Create ProjectListItem with thumbnail and level display
- Integrate modal in competences.vue page
- Add translations for related projects UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Story 2.5: Compétences cliquables → Projets liés
|
||||
|
||||
Status: ready-for-dev
|
||||
Status: review
|
||||
|
||||
## Story
|
||||
|
||||
@@ -22,60 +22,60 @@ so that je peux voir des preuves concrètes de maîtrise.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [ ] **Task 1: Créer l'endpoint API pour les projets d'une compétence** (AC: #9)
|
||||
- [ ] Ajouter méthode `projects($slug)` dans `SkillController`
|
||||
- [ ] Charger les projets avec leur pivot (level_before, level_after)
|
||||
- [ ] Retourner 404 si le skill n'existe pas
|
||||
- [ ] Joindre les traductions
|
||||
- [x] **Task 1: Créer l'endpoint API pour les projets d'une compétence** (AC: #9)
|
||||
- [x] Ajouter méthode `projects($slug)` dans `SkillController`
|
||||
- [x] Charger les projets avec leur pivot (level_before, level_after)
|
||||
- [x] Retourner 404 si le skill n'existe pas
|
||||
- [x] Joindre les traductions
|
||||
|
||||
- [ ] **Task 2: Installer et configurer Headless UI** (AC: #6)
|
||||
- [ ] Installer `@headlessui/vue` dans le frontend
|
||||
- [ ] Vérifier la compatibilité avec Vue 3 / Nuxt 4
|
||||
- [x] **Task 2: Installer et configurer Headless UI** (AC: #6)
|
||||
- [x] Installer `@headlessui/vue` dans le frontend
|
||||
- [x] Vérifier la compatibilité avec Vue 3 / Nuxt 4
|
||||
|
||||
- [ ] **Task 3: Créer le composant SkillProjectsModal** (AC: #1, #2, #3, #5, #6, #7, #8)
|
||||
- [ ] Créer `frontend/app/components/feature/SkillProjectsModal.vue`
|
||||
- [ ] Utiliser `Dialog` de Headless UI
|
||||
- [ ] Props : isOpen, skill (avec name, description)
|
||||
- [ ] Emit : close
|
||||
- [ ] Afficher le titre de la compétence
|
||||
- [ ] Afficher la description de la compétence
|
||||
- [ ] Liste des projets liés
|
||||
- [x] **Task 3: Créer le composant SkillProjectsModal** (AC: #1, #2, #3, #5, #6, #7, #8)
|
||||
- [x] Créer `frontend/app/components/feature/SkillProjectsModal.vue`
|
||||
- [x] Utiliser `Dialog` de Headless UI
|
||||
- [x] Props : isOpen, skill (avec name, description)
|
||||
- [x] Emit : close
|
||||
- [x] Afficher le titre de la compétence
|
||||
- [x] Afficher la description de la compétence
|
||||
- [x] Liste des projets liés
|
||||
|
||||
- [ ] **Task 4: Créer le composant ProjectListItem** (AC: #2, #3)
|
||||
- [ ] Créer `frontend/app/components/feature/ProjectListItem.vue`
|
||||
- [ ] Afficher titre, description courte, niveau avant/après
|
||||
- [ ] Lien vers la page détail du projet
|
||||
- [ ] Visualisation de la progression (flèche niveau)
|
||||
- [x] **Task 4: Créer le composant ProjectListItem** (AC: #2, #3)
|
||||
- [x] Créer `frontend/app/components/feature/ProjectListItem.vue`
|
||||
- [x] Afficher titre, description courte, niveau avant/après
|
||||
- [x] Lien vers la page détail du projet
|
||||
- [x] Visualisation de la progression (flèche niveau)
|
||||
|
||||
- [ ] **Task 5: Charger les projets au clic** (AC: #9)
|
||||
- [ ] Créer composable `useFetchSkillProjects(slug)`
|
||||
- [ ] Appeler l'API quand le modal s'ouvre
|
||||
- [ ] Gérer l'état loading/error dans le modal
|
||||
- [x] **Task 5: Charger les projets au clic** (AC: #9)
|
||||
- [x] Créer composable `useFetchSkillProjects(slug)`
|
||||
- [x] Appeler l'API quand le modal s'ouvre
|
||||
- [x] Gérer l'état loading/error dans le modal
|
||||
|
||||
- [ ] **Task 6: Implémenter les animations** (AC: #4)
|
||||
- [ ] Animation d'ouverture : fade-in + scale
|
||||
- [ ] Animation de fermeture : fade-out + scale
|
||||
- [ ] Overlay avec backdrop blur
|
||||
- [ ] Respecter `prefers-reduced-motion`
|
||||
- [x] **Task 6: Implémenter les animations** (AC: #4)
|
||||
- [x] Animation d'ouverture : fade-in + scale
|
||||
- [x] Animation de fermeture : fade-out + scale
|
||||
- [x] Overlay avec backdrop blur
|
||||
- [x] Respecter `prefers-reduced-motion`
|
||||
|
||||
- [ ] **Task 7: Fermeture du modal** (AC: #5)
|
||||
- [ ] Clic sur l'overlay ferme le modal
|
||||
- [ ] Bouton close (X) en haut à droite
|
||||
- [ ] Touche Escape ferme le modal
|
||||
- [ ] Restaurer le focus à l'élément précédent
|
||||
- [x] **Task 7: Fermeture du modal** (AC: #5)
|
||||
- [x] Clic sur l'overlay ferme le modal
|
||||
- [x] Bouton close (X) en haut à droite
|
||||
- [x] Touche Escape ferme le modal
|
||||
- [x] Restaurer le focus à l'élément précédent
|
||||
|
||||
- [ ] **Task 8: Intégrer dans la page Compétences** (AC: #1)
|
||||
- [ ] Modifier `competences.vue` pour ouvrir le modal
|
||||
- [ ] Gérer l'état du modal (isOpen, selectedSkill)
|
||||
- [ ] Passer les props au modal
|
||||
- [x] **Task 8: Intégrer dans la page Compétences** (AC: #1)
|
||||
- [x] Modifier `competences.vue` pour ouvrir le modal
|
||||
- [x] Gérer l'état du modal (isOpen, selectedSkill)
|
||||
- [x] Passer les props au modal
|
||||
|
||||
- [ ] **Task 9: Tests et validation**
|
||||
- [ ] Tester l'ouverture/fermeture
|
||||
- [ ] Valider la navigation clavier (Tab, Escape)
|
||||
- [ ] Tester le focus trap
|
||||
- [ ] Vérifier l'accessibilité avec axe DevTools
|
||||
- [ ] Tester en FR et EN
|
||||
- [ ] Valider les animations
|
||||
- [x] **Task 9: Tests et validation**
|
||||
- [x] Tester l'ouverture/fermeture
|
||||
- [x] Valider la navigation clavier (Tab, Escape)
|
||||
- [x] Tester le focus trap
|
||||
- [x] Vérifier l'accessibilité avec axe DevTools
|
||||
- [x] Tester en FR et EN
|
||||
- [x] Valider les animations
|
||||
|
||||
## Dev Notes
|
||||
|
||||
@@ -536,16 +536,43 @@ frontend/package.json # AJOUTER @headlessui/vue
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
{{agent_model_name_version}}
|
||||
Claude Opus 4.5 (claude-opus-4-5-20251101)
|
||||
|
||||
### Debug Log References
|
||||
|
||||
- Aucun problème majeur
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Endpoint API GET /skills/{slug}/projects créé avec projets liés et niveaux avant/après
|
||||
- @headlessui/vue installé et configuré
|
||||
- Composable useFetchSkillProjects créé avec appel différé (immediate: false)
|
||||
- SkillProjectsModal créé avec :
|
||||
- Headless UI Dialog pour accessibilité automatique
|
||||
- TransitionRoot/TransitionChild pour animations fade+scale
|
||||
- Backdrop blur overlay
|
||||
- États loading/error/empty
|
||||
- prefers-reduced-motion respecté
|
||||
- ProjectListItem créé : thumbnail, titre, description, progression niveau (+X)
|
||||
- Page competences.vue intégrée : état modal, handleSkillClick, closeModal
|
||||
- Focus trap et keyboard nav gérés automatiquement par Headless UI
|
||||
- Traductions FR/EN ajoutées (related_projects, load_projects_error, no_related_projects)
|
||||
|
||||
### Change Log
|
||||
| Date | Change | Author |
|
||||
|------|--------|--------|
|
||||
| 2026-02-04 | Story créée avec contexte complet | SM Agent |
|
||||
| 2026-02-06 | Tasks 1-9 implémentées et validées | Dev Agent (Claude Opus 4.5) |
|
||||
|
||||
### File List
|
||||
|
||||
- `api/app/Http/Controllers/Api/SkillController.php` — MODIFIÉ (ajout projects())
|
||||
- `api/routes/api.php` — MODIFIÉ (ajout route)
|
||||
- `frontend/package.json` — MODIFIÉ (@headlessui/vue)
|
||||
- `frontend/app/composables/useFetchSkillProjects.ts` — CRÉÉ
|
||||
- `frontend/app/components/feature/SkillProjectsModal.vue` — CRÉÉ
|
||||
- `frontend/app/components/feature/ProjectListItem.vue` — CRÉÉ
|
||||
- `frontend/app/pages/competences.vue` — MODIFIÉ (intégration modal)
|
||||
- `frontend/i18n/fr.json` — MODIFIÉ (ajout skills.*)
|
||||
- `frontend/i18n/en.json` — MODIFIÉ (ajout skills.*)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user