Étape 4 du projet inventaire : dashboard global du parc, KPI et graphique d'évolution avec Chart.js.
Tu as maintenant une fiche par PC. Cette étape ajoute la vue d'ensemble du parc, un graphique d'évolution et quelques KPI globaux.
Construire :
/inventaire) listant tous les PC
avec leurs indicateurs clés.Ajoute dans src/storage.php :
function lister_pcs(): array
{
if (!is_dir(DATA_ROOT)) {
return [];
}
$resultats = [];
foreach (scandir(DATA_ROOT) as $entree) {
if ($entree === '.' || $entree === '..') continue;
$fichier = DATA_ROOT . "/$entree/latest.json";
if (is_file($fichier)) {
$data = json_decode(file_get_contents($fichier), true);
if (is_array($data)) {
$resultats[] = $data;
}
}
}
// Tri par hostname
usort($resultats, fn($a, $b) => strcmp($a['hostname'] ?? '', $b['hostname'] ?? ''));
return $resultats;
}
💡
scandirest volontairement simple ici. Sur un vrai parc avec 10 000 PC, on indexerait dans une vraie base. Sur notre parc-classe à 8 PC, ça marche très bien et ça reste lisible.
Crée public/dashboard.php :
<?php
declare(strict_types=1);
require __DIR__ . '/../src/storage.php';
$pcs = lister_pcs();
// Indicateurs globaux
$nb = count($pcs);
$ramTotale = 0;
$diskTotal = 0;
$diskLibre = 0;
$dernierRapport = null;
foreach ($pcs as $pc) {
$ramTotale += (float)($pc['memory']['totalGo'] ?? 0);
foreach (($pc['disks'] ?? []) as $d) {
$diskTotal += (float)($d['totalGo'] ?? 0);
$diskLibre += (float)($d['freeGo'] ?? 0);
}
$t = $pc['timestamp'] ?? null;
if ($t && (!$dernierRapport || $t > $dernierRapport)) {
$dernierRapport = $t;
}
}
$titre = "Dashboard";
require __DIR__ . '/partials/header.php';
?>
<h1 class="h2 mb-4">Parc informatique — vue d'ensemble</h1>
<!-- KPI cards -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="display-5"><?= $nb ?></div>
<div class="text-secondary">PC inventoriés</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="display-5"><?= number_format($ramTotale, 0, ',', ' ') ?></div>
<div class="text-secondary">Go de RAM cumulés</div>
</div>
</div>
</div>
<!-- À toi : disque total, disque libre, % de rapports < 24 h -->
</div>
<!-- Liste des PC -->
<h2 class="h5">PC</h2>
<div class="row g-3">
<?php foreach ($pcs as $pc):
$hostname = $pc['hostname'] ?? '?';
$ramT = (float)($pc['memory']['totalGo'] ?? 0);
$disque0 = $pc['disks'][0] ?? null;
$usageDisk = ($disque0 && $disque0['totalGo'])
? round((1 - $disque0['freeGo']/$disque0['totalGo']) * 100)
: 0;
?>
<div class="col-md-6 col-lg-4">
<a href="/inventaire/pc/<?= urlencode($hostname) ?>"
class="card h-100 text-decoration-none">
<div class="card-body">
<h5 class="card-title"><?= htmlspecialchars($hostname) ?></h5>
<p class="text-secondary small mb-2">
<?= htmlspecialchars($pc['system']['os'] ?? '') ?>
</p>
<div class="d-flex justify-content-between">
<span>💾 <?= $ramT ?> Go</span>
<span>📀 <?= $usageDisk ?> % utilisé</span>
</div>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<?php require __DIR__ . '/partials/footer.php'; ?>
💡 Astuce : un
<a>avecclass="card"donne une carte cliquable avec toute la zone active. Très bon pour le tactile.
Dans src/storage.php, ajoute :
function charger_historique(string $hostname, int $limite = 30): array
{
$dossier = DATA_ROOT . "/$hostname/history";
if (!is_dir($dossier)) return [];
$fichiers = glob($dossier . '/*.json');
sort($fichiers); // ordre alphabétique = ordre chronologique grâce à Ymd-His
// Ne garder que les N derniers
$fichiers = array_slice($fichiers, -$limite);
$rapports = [];
foreach ($fichiers as $f) {
$r = json_decode(file_get_contents($f), true);
if (is_array($r)) $rapports[] = $r;
}
return $rapports;
}
⚠️ Pourquoi
Ymd-Hisest important ? Parce quesort()trie en ordre alphabétique sur le nom de fichier. Avec un formatYmd-His(ex :20260513-103015), l'ordre alphabétique = l'ordre chronologique. Si tu avais utilisé13-05-2026, ton historique serait dans le désordre.
Sur la fiche PC (public/pc.php), récupère l'historique :
$historique = charger_historique($hostname, 30);
// On prépare les données pour le JS
$labels = [];
$freeDataC = [];
foreach ($historique as $h) {
$labels[] = $h['timestamp'] ?? '';
$diskC = null;
foreach (($h['disks'] ?? []) as $d) {
if (($d['letter'] ?? '') === 'C') { $diskC = $d; break; }
}
$freeDataC[] = $diskC ? (float)$diskC['freeGo'] : null;
}
Et ajoute dans la page un canvas + un petit script :
<section class="mb-4">
<h2 class="h5">Espace libre — disque C: (Go)</h2>
<canvas id="chartDisque" height="100"></canvas>
</section>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
const labels = <?= json_encode($labels) ?>;
const freeData = <?= json_encode($freeDataC) ?>;
new Chart(document.getElementById('chartDisque'), {
type: 'line',
data: {
labels: labels.map(t => new Date(t).toLocaleString('fr-BE', {
day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit'
})),
datasets: [{
label: 'Espace libre C: (Go)',
data: freeData,
tension: 0.3,
fill: true,
backgroundColor: 'rgba(99, 102, 241, 0.15)',
borderColor: 'rgba(99, 102, 241, 1)',
pointRadius: 3
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: {
y: { beginAtZero: false, ticks: { color: '#9ca3af' } },
x: { ticks: { color: '#9ca3af' } }
}
}
});
</script>
💡 Pour tester sans attendre, rejoue ton script PowerShell 4-5 fois d'affilée en modifiant un peu les valeurs (lance un téléchargement, ferme une appli…). Tu auras 5 points sur ton graphique.
Trois choses à vérifier :
Sur le dashboard, ouvre les outils de développement → Réseau. Le HTML
doit charger en moins de 200 ms même avec 50 PC simulés. Si c'est plus
lent : tu fais probablement de la lecture JSON inutilement en boucle. Mets
un var_dump(memory_get_peak_usage()) pour t'en rendre compte.
Renomme temporairement le dossier data/pcs/ en data/pcs.bak/ et
recharge /inventaire. Tu dois voir un message « Aucun PC inventorié »
et non un crash.
Bascule data-bs-theme="dark" ↔ light dans header.php. Le rendu doit
rester propre dans les deux modes. Ajuste app.css si besoin pour les
zones non couvertes par Bootstrap (par exemple le tracé du graphe).
Ajoute au moins l'un des indicateurs suivants au dashboard :
Bon prompt :
J'ai pour chaque PC une liste de rapports horodatés contenant
disks[i].freeGo. Je veux un seul indicateur visuel pour le dashboard, qui montre d'un coup d'œil les PC dont l'espace disque diminue rapidement. Propose-moi 3 façons de visualiser ça avec Chart.js, avec leurs avantages et inconvénients (pas de code). Je choisirai après.
L'IA propose : sparklines dans chaque carte, heatmap, classement, bar chart différentiel… Tu choisis. Ensuite tu re-prompts pour implémenter celle que tu as retenue.
C'est la séquence : explorer → choisir → implémenter. Trois prompts, pas un.
| Piège | Solution |
|---|---|
| Chart.js ne s'affiche pas | Vérifier le <canvas> dans le DOM avant le <script> |
| Les dates s'affichent en anglais | toLocaleString('fr-BE', …) |
glob() renvoie false |
Le dossier n'existe pas — is_dir d'abord |
| Historique dans le désordre | Format Ymd-His impératif (ou tri sur timestamp) |
| Le dashboard rame avec beaucoup de PC | Ne lire que latest.json, pas tout l'historique |
À l'étape 5, on ajoute ton module spécifique — celui qui fait que ton PC se distingue dans le parc. Réseau, sécurité, USB, etc., selon ton numéro.