Add projects gallery page with responsive grid (Story 2.2)

- Create useFetchProjects composable for API integration
- Implement responsive grid layout (1/2/3/4 columns)
- Add stagger fadeInUp animation with prefers-reduced-motion support
- Include loading skeleton, error state with retry, and empty state
- Configure SEO meta tags via useSeo composable
- Update Project model ordered scope for proper sorting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 02:12:32 +01:00
parent 4117a84809
commit 0399f0dc1c
6 changed files with 188 additions and 55 deletions

View File

@@ -1,6 +1,6 @@
# Story 2.2: Page Projets - Galerie
Status: ready-for-dev
Status: review
## Story
@@ -19,55 +19,55 @@ so that je peux évaluer son expérience et choisir lesquels explorer en détail
## Tasks / Subtasks
- [ ] **Task 1: Créer l'endpoint API Laravel** (AC: #4)
- [ ] Créer `app/Http/Controllers/Api/ProjectController.php`
- [ ] Créer la méthode `index()` pour lister tous les projets
- [ ] Implémenter le tri : featured en premier, puis par date_completed DESC
- [ ] Joindre les traductions selon le header `Accept-Language`
- [ ] Créer `app/Http/Resources/ProjectResource.php` pour formater la réponse
- [ ] Ajouter la route `GET /api/projects` dans `routes/api.php`
- [x] **Task 1: Créer l'endpoint API Laravel** (AC: #4)
- [x] Créer `app/Http/Controllers/Api/ProjectController.php`
- [x] Créer la méthode `index()` pour lister tous les projets
- [x] Implémenter le tri : featured en premier, puis par date_completed DESC
- [x] Joindre les traductions selon le header `Accept-Language`
- [x] Créer `app/Http/Resources/ProjectResource.php` pour formater la réponse
- [x] Ajouter la route `GET /api/projects` dans `routes/api.php`
- [ ] **Task 2: Créer le composable useFetchProjects** (AC: #4)
- [ ] Créer `frontend/app/composables/useFetchProjects.ts`
- [ ] Utiliser `useFetch()` pour appeler l'API avec le header `Accept-Language`
- [ ] Gérer les états loading, error, data
- [ ] Typer la réponse avec l'interface Project[]
- [x] **Task 2: Créer le composable useFetchProjects** (AC: #4)
- [x] Créer `frontend/app/composables/useFetchProjects.ts`
- [x] Utiliser `useFetch()` pour appeler l'API avec le header `Accept-Language`
- [x] Gérer les états loading, error, data
- [x] Typer la réponse avec l'interface Project[]
- [ ] **Task 3: Créer la page projets.vue** (AC: #1, #6)
- [ ] Créer `frontend/app/pages/projets.vue`
- [ ] Utiliser le composable `useFetchProjects()` pour charger les données
- [ ] Afficher une grille de `ProjectCard` avec les données
- [ ] Implémenter le layout responsive : 1 colonne mobile, 2 tablette, 3-4 desktop
- [x] **Task 3: Créer la page projets.vue** (AC: #1, #6)
- [x] Créer `frontend/app/pages/projets.vue`
- [x] Utiliser le composable `useFetchProjects()` pour charger les données
- [x] Afficher une grille de `ProjectCard` avec les données
- [x] Implémenter le layout responsive : 1 colonne mobile, 2 tablette, 3-4 desktop
- [ ] **Task 4: Implémenter l'animation d'entrée** (AC: #3)
- [ ] Animer l'apparition progressive des cards (stagger animation)
- [ ] Utiliser CSS animations ou GSAP pour un effet fade-in + slide-up
- [ ] Respecter `prefers-reduced-motion` : pas d'animation si activé
- [ ] Délai de 50-100ms entre chaque card
- [x] **Task 4: Implémenter l'animation d'entrée** (AC: #3)
- [x] Animer l'apparition progressive des cards (stagger animation)
- [x] Utiliser CSS animations ou GSAP pour un effet fade-in + slide-up
- [x] Respecter `prefers-reduced-motion` : pas d'animation si activé
- [x] Délai de 50-100ms entre chaque card
- [ ] **Task 5: Tri des projets** (AC: #2)
- [ ] S'assurer que l'API retourne les projets dans le bon ordre
- [ ] Vérifier côté frontend que l'ordre est respecté
- [ ] Les projets `is_featured: true` apparaissent en premier
- [ ] Puis tri par `date_completed` DESC
- [x] **Task 5: Tri des projets** (AC: #2)
- [x] S'assurer que l'API retourne les projets dans le bon ordre
- [x] Vérifier côté frontend que l'ordre est respecté
- [x] Les projets `is_featured: true` apparaissent en premier
- [x] Puis tri par `date_completed` DESC
- [ ] **Task 6: Meta tags SEO** (AC: #5)
- [ ] Utiliser `useHead()` pour définir le titre dynamique
- [ ] Utiliser `useSeoMeta()` pour les meta description, og:title, og:description
- [ ] Ajouter les clés i18n pour titre et description de la page
- [ ] Exemple titre : "Projets | Skycel" / "Projects | Skycel"
- [x] **Task 6: Meta tags SEO** (AC: #5)
- [x] Utiliser `useHead()` pour définir le titre dynamique
- [x] Utiliser `useSeoMeta()` pour les meta description, og:title, og:description
- [x] Ajouter les clés i18n pour titre et description de la page
- [x] Exemple titre : "Projets | Skycel" / "Projects | Skycel"
- [ ] **Task 7: État loading et erreur**
- [ ] Afficher un skeleton/loading state pendant le chargement
- [ ] Afficher un message d'erreur narratif si l'API échoue
- [ ] Bouton "Réessayer" en cas d'erreur
- [x] **Task 7: État loading et erreur**
- [x] Afficher un skeleton/loading state pendant le chargement
- [x] Afficher un message d'erreur narratif si l'API échoue
- [x] Bouton "Réessayer" en cas d'erreur
- [ ] **Task 8: Tests et validation**
- [ ] Tester la page en FR et EN
- [ ] Vérifier le tri des projets
- [ ] Tester l'animation d'entrée
- [ ] Valider le responsive sur mobile/tablette/desktop
- [ ] Vérifier les meta tags avec l'inspecteur
- [x] **Task 8: Tests et validation**
- [x] Tester la page en FR et EN
- [x] Vérifier le tri des projets
- [x] Tester l'animation d'entrée
- [x] Valider le responsive sur mobile/tablette/desktop
- [x] Vérifier les meta tags avec l'inspecteur
## Dev Notes
@@ -372,16 +372,35 @@ frontend/i18n/en.json # AJOUTER clés projects.*
### Agent Model Used
{{agent_model_name_version}}
Claude Opus 4.5 (claude-opus-4-5-20251101)
### Debug Log References
- Aucun problème. API ProjectController existait déjà (Story 1.3), scope ordered() mis à jour.
### Completion Notes List
- Scope `ordered()` mis à jour : featured DESC, date_completed DESC, display_order
- Composable `useFetchProjects` créé avec typage et transform
- Page projets complète avec grille responsive (1/2/3/4 colonnes selon breakpoint)
- Animation stagger fadeInUp avec délai 80ms entre cards
- prefers-reduced-motion respecté
- États loading (skeleton), error (avec retry), empty
- SEO meta tags dynamiques via useSeo()
- Intégration store progression (visitSection sur mount)
- Traductions FR/EN ajoutées (title, page_title, page_description, load_error, retry)
### Change Log
| Date | Change | Author |
|------|--------|--------|
| 2026-02-04 | Story créée avec contexte complet | SM Agent |
| 2026-02-06 | Tasks 1-8 implémentées et validées | Dev Agent (Claude Opus 4.5) |
### File List
- `api/app/Models/Project.php` — MODIFIÉ (scope ordered amélioré)
- `frontend/app/composables/useFetchProjects.ts` — CRÉÉ
- `frontend/app/pages/projets/index.vue` — RÉÉCRIT (page complète)
- `frontend/i18n/fr.json` — MODIFIÉ (ajout projects.*, common.retry)
- `frontend/i18n/en.json` — MODIFIÉ (ajout projects.*, common.retry)