Files
Portfolio-Codex/docs/stories/4.5.section-temoignages.md

282 lines
9.5 KiB
Markdown

# Story 4.5: Section Témoignages (JSON Dynamique)
## Status
Ready for Dev
## Story
**As a** visiteur,
**I want** lire des témoignages de clients ou employeurs,
**so that** j'ai une preuve sociale de la qualité du travail.
## Acceptance Criteria
1. Le fichier `data/testimonials.json` stocke tous les témoignages
2. La structure JSON supporte : id, quote, author_name, author_role, author_company, author_photo, project_slug (optionnel), date, featured (booléen)
3. Une fonction PHP `getTestimonials()` lit et décode le JSON
4. La section témoignages affiche dynamiquement les entrées du JSON
5. Chaque témoignage affiche : citation, nom, rôle/entreprise, photo (si disponible)
6. Si un témoignage est lié à un projet (`project_slug`), un lien vers le projet est affiché
7. Les témoignages `featured: true` peuvent être affichés sur la page d'accueil
8. Si le JSON est vide ou le fichier absent, la section affiche "Témoignages à venir" ou est masquée
9. Le design utilise des guillemets ou un style "citation" reconnaissable
## Tasks / Subtasks
- [] **Task 1 : Créer le fichier testimonials.json** (AC: 1, 2)
- [] Créer `data/testimonials.json`
- [] Définir la structure complète
- [] Ajouter 3 témoignages de test
- [] **Task 2 : Créer les fonctions PHP** (AC: 3)
- [] `getTestimonials()` - tous les témoignages
- [] `getFeaturedTestimonials()` - témoignages mis en avant
- [] `getTestimonialByProject($slug)` - témoignage lié à un projet
- [] **Task 3 : Créer le template testimonial.php** (AC: 5, 9)
- [] Style citation avec guillemets SVG
- [] Photo de l'auteur (optionnelle, sinon initiale)
- [] Nom, rôle, entreprise
- [] **Task 4 : Ajouter la section dans about.php** (AC: 4, 8)
- [] Grille de témoignages (1→2→3 colonnes)
- [] Gestion du cas vide (section masquée)
- [] **Task 5 : Lien vers le projet** (AC: 6)
- [] Si project_slug existe, afficher le lien
- [] "Voir le projet →" avec icône
- [] **Task 6 : Témoignages sur l'accueil** (AC: 7)
- [] Afficher 2 témoignages featured sur home.php
- [] Lien "Voir tous les témoignages"
## Dev Notes
### Structure data/testimonials.json
```json
{
"testimonials": [
{
"id": 1,
"quote": "Excellent travail ! Le site a été livré dans les délais avec une qualité irréprochable. Communication fluide tout au long du projet.",
"author_name": "Marie Dupont",
"author_role": "Directrice Marketing",
"author_company": "Entreprise XYZ",
"author_photo": "marie-dupont.webp",
"project_slug": "ecommerce-xyz",
"date": "2025-06-15",
"featured": true
},
{
"id": 2,
"quote": "Un développeur rigoureux et créatif. Il a su comprendre nos besoins et proposer des solutions adaptées.",
"author_name": "Jean Martin",
"author_role": "CEO",
"author_company": "Startup ABC",
"author_photo": null,
"project_slug": "app-gestion",
"date": "2025-03-20",
"featured": true
},
{
"id": 3,
"quote": "Travail soigné et professionnel. Je recommande vivement.",
"author_name": "Sophie Leroy",
"author_role": "Gérante",
"author_company": "Restaurant Le Bon Goût",
"author_photo": null,
"project_slug": null,
"date": "2024-11-10",
"featured": false
}
]
}
```
### Fonctions PHP (includes/functions.php)
```php
/**
* Récupère tous les témoignages
*/
function getTestimonials(): array
{
$data = loadJsonData('testimonials.json');
return $data['testimonials'] ?? [];
}
/**
* Récupère les témoignages mis en avant
*/
function getFeaturedTestimonials(): array
{
return array_filter(getTestimonials(), fn($t) => $t['featured'] === true);
}
/**
* Récupère le témoignage lié à un projet
*/
function getTestimonialByProject(string $projectSlug): ?array
{
$testimonials = getTestimonials();
foreach ($testimonials as $testimonial) {
if (($testimonial['project_slug'] ?? '') === $projectSlug) {
return $testimonial;
}
}
return null;
}
```
### Template templates/testimonial.php
```php
<?php
/**
* Composant témoignage
* @param array $testimonial Données du témoignage
* @param bool $showProjectLink Afficher le lien vers le projet
*/
$quote = $testimonial['quote'] ?? '';
$authorName = $testimonial['author_name'] ?? 'Anonyme';
$authorRole = $testimonial['author_role'] ?? '';
$authorCompany = $testimonial['author_company'] ?? '';
$authorPhoto = $testimonial['author_photo'] ?? null;
$projectSlug = $testimonial['project_slug'] ?? null;
$showProjectLink = $showProjectLink ?? true;
?>
<blockquote class="testimonial">
<!-- Guillemets décoratifs -->
<svg class="w-8 h-8 text-primary/30 mb-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/>
</svg>
<!-- Citation -->
<p class="text-text-primary text-lg leading-relaxed mb-6 italic">
"<?= htmlspecialchars($quote) ?>"
</p>
<!-- Auteur -->
<footer class="flex items-center gap-4">
<?php if ($authorPhoto): ?>
<img
src="/assets/img/testimonials/<?= htmlspecialchars($authorPhoto) ?>"
alt="<?= htmlspecialchars($authorName) ?>"
class="w-12 h-12 rounded-full object-cover"
loading="lazy"
>
<?php else: ?>
<div class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center">
<span class="text-primary font-semibold text-lg">
<?= strtoupper(substr($authorName, 0, 1)) ?>
</span>
</div>
<?php endif; ?>
<div>
<p class="font-semibold text-text-primary"><?= htmlspecialchars($authorName) ?></p>
<p class="text-sm text-text-muted">
<?= htmlspecialchars($authorRole) ?>
<?php if ($authorCompany): ?>
<span class="text-text-muted">—</span> <?= htmlspecialchars($authorCompany) ?>
<?php endif; ?>
</p>
</div>
</footer>
<!-- Lien vers le projet -->
<?php if ($showProjectLink && $projectSlug): ?>
<a href="/projet/<?= htmlspecialchars($projectSlug) ?>" class="inline-flex items-center gap-1 text-primary text-sm mt-4 hover:underline">
Voir le projet
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</a>
<?php endif; ?>
</blockquote>
```
### Section dans pages/about.php
```php
<!-- Témoignages -->
<?php $testimonials = getTestimonials(); ?>
<?php if (!empty($testimonials)): ?>
<section class="section bg-surface">
<div class="container-content">
<div class="section-header">
<h2 class="section-title">Ce Qu'ils Disent</h2>
<p class="section-subtitle">
Retours de clients et collaborateurs.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<?php foreach ($testimonials as $testimonial): ?>
<?php include_template('testimonial', ['testimonial' => $testimonial]); ?>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
```
### Dossier des Photos
```
assets/img/testimonials/
├── marie-dupont.webp # 96x96 carré
├── jean-martin.webp
└── ...
```
## Testing
- [] Le fichier JSON est valide (3 témoignages)
- [] `getTestimonials()` retourne les témoignages (3)
- [] `getFeaturedTestimonials()` filtre correctement (2 featured)
- [] La section s'affiche sur la page À propos
- [] Les guillemets SVG et style citation sont visibles
- [] Les initiales s'affichent si pas de photo
- [] Le lien vers le projet fonctionne
- [] Cas vide : section masquée (if !empty)
- [] 2 témoignages featured affichés sur la home
## Dev Agent Record
### Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
### File List
| File | Action | Description |
|------|--------|-------------|
| `data/testimonials.json` | Created | 3 témoignages de test |
| `includes/functions.php` | Modified | Fonctions getTestimonials(), getFeaturedTestimonials(), getTestimonialByProject() |
| `templates/testimonial.php` | Created | Template avec guillemets, auteur, lien projet |
| `pages/about.php` | Modified | Section "Ce Qu'ils Disent" |
| `pages/home.php` | Modified | 2 témoignages featured |
### Completion Notes
- Structure JSON complète : id, quote, author_name, author_role, author_company, author_photo, project_slug, date, featured
- 3 fonctions PHP pour accéder aux témoignages
- Template réutilisable avec guillemets SVG décoratifs
- Photo optionnelle : si absente, affiche l'initiale sur fond coloré
- Lien vers projet optionnel (paramètre showProjectLink)
- Section masquée si JSON vide
- 2 témoignages featured sur la home avec lien "Voir tous"
- Note: Les photos peuvent être ajoutées dans `/assets/img/testimonials/`
### Debug Log References
Aucun problème rencontré.
## Change Log
| Date | Version | Description | Author |
|------|---------|-------------|--------|
| 2026-01-22 | 0.1 | Création initiale | Sarah (PO) |
| 2026-01-23 | 1.0 | Implémentation complète | James (Dev) |