✨ Story 3.1: data projects json
This commit is contained in:
55
data/projects.json
Normal file
55
data/projects.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"projects": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Site E-commerce XYZ",
|
||||||
|
"slug": "ecommerce-xyz",
|
||||||
|
"category": "vedette",
|
||||||
|
"thumbnail": "ecommerce-xyz-thumb.webp",
|
||||||
|
"url": "https://example.com",
|
||||||
|
"github": "https://github.com/user/project",
|
||||||
|
"technologies": ["PHP", "JavaScript", "Tailwind CSS", "MySQL"],
|
||||||
|
"context": "Client souhaitant moderniser sa boutique en ligne pour améliorer l'expérience utilisateur et augmenter les conversions.",
|
||||||
|
"solution": "Développement d'une solution e-commerce sur mesure avec panier persistant, paiement sécurisé Stripe, et interface d'administration.",
|
||||||
|
"teamwork": "Projet réalisé en collaboration avec un designer UI/UX. J'ai pris en charge l'intégration et le développement backend.",
|
||||||
|
"duration": "3 mois",
|
||||||
|
"screenshots": [
|
||||||
|
"ecommerce-xyz-screen-1.webp",
|
||||||
|
"ecommerce-xyz-screen-2.webp",
|
||||||
|
"ecommerce-xyz-screen-3.webp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Application de Gestion",
|
||||||
|
"slug": "app-gestion",
|
||||||
|
"category": "vedette",
|
||||||
|
"thumbnail": "app-gestion-thumb.webp",
|
||||||
|
"url": null,
|
||||||
|
"github": "https://github.com/user/app-gestion",
|
||||||
|
"technologies": ["React", "Node.js", "PostgreSQL", "Docker"],
|
||||||
|
"context": "Startup ayant besoin d'un outil interne pour gérer ses ressources et planifier ses projets.",
|
||||||
|
"solution": "Application web full-stack avec authentification, gestion des rôles, tableaux de bord et exports PDF.",
|
||||||
|
"teamwork": null,
|
||||||
|
"duration": "4 mois",
|
||||||
|
"screenshots": [
|
||||||
|
"app-gestion-screen-1.webp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "Site Vitrine Restaurant",
|
||||||
|
"slug": "restaurant-vitrine",
|
||||||
|
"category": "secondaire",
|
||||||
|
"thumbnail": "restaurant-thumb.webp",
|
||||||
|
"url": "https://restaurant-example.com",
|
||||||
|
"github": null,
|
||||||
|
"technologies": ["HTML", "CSS", "JavaScript"],
|
||||||
|
"context": "Restaurant local souhaitant une présence en ligne simple.",
|
||||||
|
"solution": null,
|
||||||
|
"teamwork": null,
|
||||||
|
"duration": "2 semaines",
|
||||||
|
"screenshots": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Ready for Dev
|
review
|
||||||
|
|
||||||
## Story
|
## Story
|
||||||
|
|
||||||
@@ -20,27 +20,27 @@ Ready for Dev
|
|||||||
|
|
||||||
## Tasks / Subtasks
|
## Tasks / Subtasks
|
||||||
|
|
||||||
- [] **Task 1 : Définir la structure JSON** (AC: 2)
|
- [x] **Task 1 : Définir la structure JSON** (AC: 2)
|
||||||
- [] Documenter tous les champs requis et optionnels
|
- [x] Documenter tous les champs requis et optionnels
|
||||||
- [] Définir les types de données pour chaque champ
|
- [x] Définir les types de données pour chaque champ
|
||||||
- [] Définir les valeurs possibles pour category
|
- [x] Définir les valeurs possibles pour category
|
||||||
|
|
||||||
- [] **Task 2 : Créer le fichier projects.json** (AC: 1, 3)
|
- [x] **Task 2 : Créer le fichier projects.json** (AC: 1, 3)
|
||||||
- [] Créer `data/projects.json`
|
- [x] Créer `data/projects.json`
|
||||||
- [] Ajouter 2-3 projets de test
|
- [x] Ajouter 2-3 projets de test
|
||||||
- [] Valider la syntaxe JSON
|
- [x] Valider la syntaxe JSON
|
||||||
|
|
||||||
- [] **Task 3 : Créer les fonctions PHP d'accès** (AC: 4, 5)
|
- [x] **Task 3 : Créer les fonctions PHP d'accès** (AC: 4, 5)
|
||||||
- [] Créer `loadJsonData()` générique
|
- [x] Créer `loadJsonData()` générique
|
||||||
- [] Créer `getProjects()`
|
- [x] Créer `getProjects()`
|
||||||
- [] Créer `getProjectsByCategory()`
|
- [x] Créer `getProjectsByCategory()`
|
||||||
- [] Créer `getProjectBySlug()`
|
- [x] Créer `getProjectBySlug()`
|
||||||
- [] Gérer les erreurs (fichier manquant, JSON invalide)
|
- [x] Gérer les erreurs (fichier manquant, JSON invalide)
|
||||||
|
|
||||||
- [] **Task 4 : Tester les fonctions**
|
- [x] **Task 4 : Tester les fonctions**
|
||||||
- [] Tester avec fichier valide
|
- [x] Tester avec fichier valide
|
||||||
- [] Tester avec fichier manquant
|
- [x] Tester avec fichier manquant
|
||||||
- [] Tester avec JSON invalide
|
- [x] Tester avec JSON invalide
|
||||||
|
|
||||||
## Dev Notes
|
## Dev Notes
|
||||||
|
|
||||||
@@ -209,27 +209,29 @@ function getAllTechnologies(): array
|
|||||||
## Dev Agent Record
|
## Dev Agent Record
|
||||||
|
|
||||||
### Agent Model Used
|
### Agent Model Used
|
||||||
Claude Opus 4.5 (claude-opus-4-5-20251101)
|
GPT-5 Codex
|
||||||
|
|
||||||
### File List
|
### File List
|
||||||
| File | Action | Description |
|
| File | Action | Description |
|
||||||
|------|--------|-------------|
|
|------|--------|-------------|
|
||||||
| `data/projects.json` | Created | Fichier JSON avec 3 projets de test |
|
| `data/projects.json` | Created | Fichier JSON avec 3 projets de test |
|
||||||
| `includes/functions.php` | Modified | Ajout des fonctions d'accès aux données JSON |
|
| `includes/functions.php` | Modified | Ajout des fonctions d'accès aux données JSON |
|
||||||
|
| `tests/projects.test.php` | Created | Tests fonctions JSON |
|
||||||
|
| `tests/run.ps1` | Modified | Ajout tests JSON |
|
||||||
|
|
||||||
### Completion Notes
|
### Completion Notes
|
||||||
- Structure JSON complète avec tous les champs requis et optionnels
|
- Structure JSON complète avec tous les champs requis et optionnels
|
||||||
- 3 projets de test ajoutés (2 vedettes, 1 secondaire)
|
- 3 projets de test ajoutés (2 vedettes, 1 secondaire)
|
||||||
- Fonctions PHP: `loadJsonData()`, `getProjects()`, `getProjectsByCategory()`, `getProjectBySlug()`, `getAllTechnologies()`
|
- Fonctions PHP: `loadJsonData()`, `getProjects()`, `getProjectsByCategory()`, `getProjectBySlug()`, `getAllTechnologies()`
|
||||||
- Gestion des erreurs: fichier manquant et JSON invalide retournent tableau vide avec log
|
- Gestion des erreurs: fichier manquant et JSON invalide retournent tableau vide avec log
|
||||||
- Tous les tests passent (8/8)
|
- Tests: `powershell -ExecutionPolicy Bypass -File tests/run.ps1`
|
||||||
|
|
||||||
### Debug Log References
|
### Debug Log References
|
||||||
Aucun problème rencontré.
|
Aucun problème bloquant (BOM retiré de `data/projects.json`).
|
||||||
|
|
||||||
## Change Log
|
## Change Log
|
||||||
|
|
||||||
| Date | Version | Description | Author |
|
| Date | Version | Description | Author |
|
||||||
|------|---------|-------------|--------|
|
|------|---------|-------------|--------|
|
||||||
| 2026-01-22 | 0.1 | Création initiale | Sarah (PO) |
|
| 2026-01-22 | 0.1 | Création initiale | Sarah (PO) |
|
||||||
| 2026-01-23 | 1.0 | Implémentation complète | James (Dev) |
|
| 2026-02-04 | 1.0 | Implémentation complète | Amelia |
|
||||||
|
|||||||
@@ -8,4 +8,74 @@ function include_template(string $name, array $data = []): void
|
|||||||
{
|
{
|
||||||
extract($data, EXTR_SKIP);
|
extract($data, EXTR_SKIP);
|
||||||
include __DIR__ . "/../templates/{$name}.php";
|
include __DIR__ . "/../templates/{$name}.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge et parse un fichier JSON
|
||||||
|
*/
|
||||||
|
function loadJsonData(string $filename): array
|
||||||
|
{
|
||||||
|
$path = __DIR__ . "/../data/{$filename}";
|
||||||
|
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
error_log("JSON file not found: {$filename}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($path);
|
||||||
|
$data = json_decode($content, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
error_log("JSON parse error in {$filename}: " . json_last_error_msg());
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère tous les projets
|
||||||
|
*/
|
||||||
|
function getProjects(): array
|
||||||
|
{
|
||||||
|
$data = loadJsonData('projects.json');
|
||||||
|
return $data['projects'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les projets par catégorie
|
||||||
|
*/
|
||||||
|
function getProjectsByCategory(string $category): array
|
||||||
|
{
|
||||||
|
return array_values(array_filter(getProjects(), fn($p) => ($p['category'] ?? '') === $category));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère un projet par son slug
|
||||||
|
*/
|
||||||
|
function getProjectBySlug(string $slug): ?array
|
||||||
|
{
|
||||||
|
foreach (getProjects() as $project) {
|
||||||
|
if (($project['slug'] ?? '') === $slug) {
|
||||||
|
return $project;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les technologies uniques de tous les projets
|
||||||
|
*/
|
||||||
|
function getAllTechnologies(): array
|
||||||
|
{
|
||||||
|
$technologies = [];
|
||||||
|
foreach (getProjects() as $project) {
|
||||||
|
foreach ($project['technologies'] ?? [] as $tech) {
|
||||||
|
if (!in_array($tech, $technologies, true)) {
|
||||||
|
$technologies[] = $tech;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort($technologies);
|
||||||
|
return $technologies;
|
||||||
}
|
}
|
||||||
34
tests/projects.test.php
Normal file
34
tests/projects.test.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../includes/functions.php';
|
||||||
|
|
||||||
|
function assertTrue($cond, $msg) {
|
||||||
|
if (!$cond) {
|
||||||
|
fwrite(STDERR, $msg . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$projects = getProjects();
|
||||||
|
assertTrue(is_array($projects), 'getProjects not array');
|
||||||
|
assertTrue(count($projects) >= 2, 'expected at least 2 projects');
|
||||||
|
|
||||||
|
$featured = getProjectsByCategory('vedette');
|
||||||
|
assertTrue(count($featured) >= 1, 'expected featured projects');
|
||||||
|
|
||||||
|
$found = getProjectBySlug('ecommerce-xyz');
|
||||||
|
assertTrue(is_array($found), 'project ecommerce-xyz not found');
|
||||||
|
assertTrue(($found['slug'] ?? '') === 'ecommerce-xyz', 'slug mismatch');
|
||||||
|
|
||||||
|
$missing = getProjectBySlug('inexistant');
|
||||||
|
assertTrue($missing === null, 'missing project should be null');
|
||||||
|
|
||||||
|
$missingData = loadJsonData('missing.json');
|
||||||
|
assertTrue($missingData === [], 'missing.json should return empty array');
|
||||||
|
|
||||||
|
$invalidPath = __DIR__ . '/../data/__invalid.json';
|
||||||
|
file_put_contents($invalidPath, '{invalid json');
|
||||||
|
$invalidData = loadJsonData('__invalid.json');
|
||||||
|
@unlink($invalidPath);
|
||||||
|
assertTrue($invalidData === [], 'invalid json should return empty array');
|
||||||
|
|
||||||
|
fwrite(STDOUT, "OK\n");
|
||||||
@@ -8,4 +8,5 @@ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|||||||
& (Join-Path $here 'cta.test.ps1')
|
& (Join-Path $here 'cta.test.ps1')
|
||||||
& (Join-Path $here 'home.test.ps1')
|
& (Join-Path $here 'home.test.ps1')
|
||||||
& (Join-Path $here 'quicknav.test.ps1')
|
& (Join-Path $here 'quicknav.test.ps1')
|
||||||
|
php (Join-Path $here 'projects.test.php')
|
||||||
'OK'
|
'OK'
|
||||||
Reference in New Issue
Block a user