Feature: Pages projets complètes + Optimisation images (Stories 3.4-3.6)

Story 3.4 - Page projet individuelle:
- Breadcrumb, header avec badges technologies
- Boutons "Voir en ligne" / "GitHub"
- Sections: Contexte, Solution, Travail d'équipe
- Galerie screenshots, sidebar durée
- Navigation retour + CTA contact

Story 3.5 - Projets secondaires:
- Section "Autres projets" sur /projets
- Template project-card-compact.php
- Format liste avec lien externe direct

Story 3.6 - Optimisation images:
- Fonction projectImage() avec <picture> WebP + fallback JPG
- Dimensions explicites (400x225, 800x450, 1200x675)
- Lazy loading configurable

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-23 10:59:13 +01:00
parent 013b98ad3c
commit 1711f8f723
5 changed files with 287 additions and 22 deletions

View File

@@ -1,9 +1,17 @@
<?php
/**
* Page projet individuel
* Page projet individuelle
*/
// Récupérer le slug depuis le router
$slug = $GLOBALS['routeParams'][0] ?? null;
if (!$slug) {
http_response_code(404);
include __DIR__ . '/404.php';
exit;
}
$project = getProjectBySlug($slug);
if (!$project) {
@@ -13,7 +21,7 @@ if (!$project) {
}
$pageTitle = $project['title'];
$pageDescription = $project['context'];
$pageDescription = $project['context'] ?? "Découvrez le projet {$project['title']}";
$currentPage = 'projets';
include_template('header', compact('pageTitle', 'pageDescription'));
@@ -21,20 +29,156 @@ include_template('navbar', compact('currentPage'));
?>
<main>
<section class="section">
<article class="section">
<div class="container-content">
<div class="section-header">
<h1 class="section-title"><?= htmlspecialchars($project['title']) ?></h1>
<p class="section-subtitle">
<?= htmlspecialchars($project['duration']) ?>
</p>
<!-- Breadcrumb -->
<nav class="flex items-center gap-2 text-sm mb-8" aria-label="Fil d'Ariane">
<a href="/" class="text-text-secondary hover:text-primary transition-colors">Accueil</a>
<span class="text-text-secondary">/</span>
<a href="/projets" class="text-text-secondary hover:text-primary transition-colors">Projets</a>
<span class="text-text-secondary">/</span>
<span class="text-text-primary font-medium"><?= htmlspecialchars($project['title']) ?></span>
</nav>
<!-- Header du projet -->
<header class="mb-12">
<h1 class="text-3xl lg:text-display font-bold text-text-primary mb-4">
<?= htmlspecialchars($project['title']) ?>
</h1>
<!-- Technologies -->
<div class="flex flex-wrap gap-2 mb-6">
<?php foreach ($project['technologies'] ?? [] as $tech): ?>
<span class="badge"><?= htmlspecialchars($tech) ?></span>
<?php endforeach; ?>
</div>
<!-- Boutons d'action -->
<div class="flex flex-wrap gap-4">
<?php if (!empty($project['url'])): ?>
<a href="<?= htmlspecialchars($project['url']) ?>" target="_blank" rel="noopener" class="btn-primary inline-flex items-center gap-2">
Voir le projet en ligne
<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="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
</a>
<?php endif; ?>
<?php if (!empty($project['github'])): ?>
<a href="<?= htmlspecialchars($project['github']) ?>" target="_blank" rel="noopener" class="btn-secondary inline-flex items-center gap-2">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
Voir sur GitHub
</a>
<?php endif; ?>
<?php if (empty($project['url']) && empty($project['github'])): ?>
<span class="text-text-secondary italic">Projet non disponible en ligne</span>
<?php endif; ?>
</div>
</header>
<!-- Image principale -->
<?php if (!empty($project['thumbnail'])): ?>
<div class="mb-12 rounded-lg overflow-hidden bg-surface-alt">
<?= projectImage(
$project['thumbnail'],
$project['title'],
1200,
675,
false,
"w-full"
) ?>
</div>
<?php endif; ?>
<!-- Contenu -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-12">
<!-- Colonne principale -->
<div class="lg:col-span-2 space-y-10">
<!-- Contexte -->
<?php if (!empty($project['context'])): ?>
<section>
<h2 class="text-xl lg:text-heading font-semibold text-text-primary mb-4">Contexte</h2>
<p class="text-text-secondary leading-relaxed">
<?= nl2br(htmlspecialchars($project['context'])) ?>
</p>
</section>
<?php endif; ?>
<!-- Solution technique -->
<?php if (!empty($project['solution'])): ?>
<section>
<h2 class="text-xl lg:text-heading font-semibold text-text-primary mb-4">Solution Technique</h2>
<p class="text-text-secondary leading-relaxed">
<?= nl2br(htmlspecialchars($project['solution'])) ?>
</p>
</section>
<?php endif; ?>
<!-- Travail d'équipe -->
<?php if (!empty($project['teamwork'])): ?>
<section>
<h2 class="text-xl lg:text-heading font-semibold text-text-primary mb-4">Travail d'Équipe</h2>
<p class="text-text-secondary leading-relaxed">
<?= nl2br(htmlspecialchars($project['teamwork'])) ?>
</p>
</section>
<?php endif; ?>
<!-- Galerie -->
<?php if (!empty($project['screenshots'])): ?>
<section>
<h2 class="text-xl lg:text-heading font-semibold text-text-primary mb-4">Captures d'écran</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<?php foreach ($project['screenshots'] as $screenshot): ?>
<div class="rounded-lg overflow-hidden bg-surface-alt">
<?= projectImage(
$screenshot,
"Capture d'écran - " . $project['title'],
800,
450,
true,
"w-full h-auto"
) ?>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
</div>
<!-- Sidebar -->
<aside class="space-y-6">
<!-- Durée -->
<div class="card">
<div class="card-body">
<h3 class="text-sm font-medium text-text-secondary mb-1">Durée du projet</h3>
<p class="text-lg font-semibold text-text-primary">
<?= htmlspecialchars($project['duration'] ?? 'Non spécifiée') ?>
</p>
</div>
</div>
<!-- Placeholder témoignage (Story 4.5) -->
</aside>
</div>
<p class="text-text-secondary text-center">
Page en construction - Story 3.4
</p>
<!-- Navigation bas de page -->
<footer class="mt-16 pt-8 border-t border-border flex flex-wrap justify-between items-center gap-4">
<a href="/projets" class="inline-flex items-center gap-2 text-text-secondary hover:text-primary transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
Retour aux projets
</a>
<a href="/contact" class="btn-primary">
Me contacter
</a>
</footer>
</div>
</section>
</article>
</main>
<?php include_template('footer'); ?>