Suivi consommables : compteur papier, ruban et photos avec diagnostic ratio

- Onglet Consommables dans General : barres de progression papier/ruban, compteur photos
- Capacites configurables (feuilles par bobine / poses par bobine)
- Reset individuel papier/ruban (quand on change la bobine)
- Diagnostic ratio ruban/papier pour verifier que le rembobinage fonctionne
- Tracking automatique a chaque impression (format-aware)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 15:23:17 +02:00
parent 44a9992557
commit 3b6f376ded
5 changed files with 239 additions and 5 deletions

View File

@@ -935,6 +935,42 @@ html, body {
}
.admin-onglets.cache { display: none; }
.conso-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.conso-card {
background: rgba(255,255,255,0.04);
border-radius: 12px;
padding: 1.2rem;
text-align: center;
}
.conso-icon { font-size: 2rem; margin-bottom: 0.3rem; }
.conso-titre { font-weight: 600; margin-bottom: 0.8rem; font-size: 0.95rem; }
.conso-barre-fond {
height: 12px;
background: rgba(255,255,255,0.08);
border-radius: 6px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.conso-barre {
height: 100%;
border-radius: 6px;
transition: width 0.5s ease, background 0.5s ease;
}
.conso-chiffres { font-size: 0.85rem; color: var(--texte-secondaire); margin-bottom: 0.8rem; }
.conso-gros { font-size: 2.5rem; font-weight: 800; margin: 0.5rem 0; }
.conso-diag {
background: rgba(255,255,255,0.03);
border-radius: 8px;
padding: 1rem;
font-size: 0.85rem;
line-height: 1.6;
}
.video-situation-bloc {
background: rgba(255,255,255,0.03);
border-radius: 8px;

View File

@@ -7,7 +7,7 @@
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="google" content="notranslate">
<meta http-equiv="Content-Language" content="fr">
<link rel="stylesheet" href="/css/style.css?v=13">
<link rel="stylesheet" href="/css/style.css?v=14">
<link rel="stylesheet" href="/css/themes.css?v=2">
</head>
<body>
@@ -379,6 +379,7 @@
<div class="admin-onglets" id="onglets-general">
<button class="onglet actif" data-onglet="materiel">Materiel</button>
<button class="onglet" data-onglet="compteur-admin">Compteur</button>
<button class="onglet" data-onglet="consommables-admin" onclick="chargerConsommables()">Consommables</button>
<button class="onglet" data-onglet="fonctions">Fonctions</button>
<button class="onglet" data-onglet="personnalisation">Personnalisation</button>
<button class="onglet" data-onglet="videos-admin" onclick="chargerVideosAdmin()">Videos</button>
@@ -476,6 +477,48 @@
</div>
</div>
<!-- Panneau Consommables -->
<div class="admin-panneau" id="panneau-consommables-admin">
<h3>Suivi consommables</h3>
<p class="aide">Suivez l'usure du papier et du ruban. Reinitalisez quand vous changez une bobine.</p>
<div class="conso-grid">
<div class="conso-card">
<div class="conso-icon">&#128196;</div>
<div class="conso-titre">Papier</div>
<div class="conso-barre-fond"><div class="conso-barre conso-barre-papier" id="conso-barre-papier"></div></div>
<div class="conso-chiffres"><span id="conso-papier-restant">--</span> / <span id="conso-papier-cap">--</span> feuilles</div>
<button class="btn-secondaire btn-petit" onclick="resetConsommable('papier')">Bobine changee</button>
</div>
<div class="conso-card">
<div class="conso-icon">&#127912;</div>
<div class="conso-titre">Ruban</div>
<div class="conso-barre-fond"><div class="conso-barre conso-barre-ruban" id="conso-barre-ruban"></div></div>
<div class="conso-chiffres"><span id="conso-ruban-restant">--</span> / <span id="conso-ruban-cap">--</span> poses</div>
<button class="btn-secondaire btn-petit" onclick="resetConsommable('ruban')">Ruban change</button>
</div>
<div class="conso-card">
<div class="conso-icon">&#128247;</div>
<div class="conso-titre">Photos sorties</div>
<div class="conso-gros" id="conso-photos">--</div>
</div>
</div>
<h3 style="margin-top:1.5rem">Capacites</h3>
<div class="champ">
<label>Capacite papier (feuilles par bobine)</label>
<input type="number" id="conso-cap-papier" min="1" max="9999" value="400">
</div>
<div class="champ">
<label>Capacite ruban (poses par bobine)</label>
<input type="number" id="conso-cap-ruban" min="1" max="9999" value="400">
</div>
<button class="btn-action" onclick="sauvegarderCapacites()">Sauvegarder capacites</button>
<button class="btn-danger" style="margin-top:0.5rem" onclick="resetConsommable('tout')">Tout reinitialiser</button>
<div class="conso-diag" id="conso-diagnostic" style="margin-top:1.5rem"></div>
</div>
<!-- Panneau Destinations -->
<div class="admin-panneau" id="panneau-destinations-admin">
<h3>Ou sauvegarder les photos ?</h3>
@@ -1017,6 +1060,6 @@
<script src="/js/effects.js?v=4"></script>
<script src="/js/gallery.js?v=4"></script>
<script src="/js/share.js?v=8"></script>
<script src="/js/admin.js?v=10"></script>
<script src="/js/admin.js?v=11"></script>
</body>
</html>

View File

@@ -1401,6 +1401,67 @@ async function chargerDiagCamera() {
}
}
// === CONSOMMABLES ===
async function chargerConsommables() {
try {
const data = await apiGet('/api/consommables');
document.getElementById('conso-papier-restant').textContent = data.papier_restant;
document.getElementById('conso-papier-cap').textContent = data.papier_capacite;
document.getElementById('conso-ruban-restant').textContent = data.ruban_restant;
document.getElementById('conso-ruban-cap').textContent = data.ruban_capacite;
document.getElementById('conso-photos').textContent = data.photos_imprimees;
setValue('conso-cap-papier', data.papier_capacite);
setValue('conso-cap-ruban', data.ruban_capacite);
const pctPapier = data.papier_capacite > 0 ? Math.max(0, (data.papier_restant / data.papier_capacite) * 100) : 0;
const pctRuban = data.ruban_capacite > 0 ? Math.max(0, (data.ruban_restant / data.ruban_capacite) * 100) : 0;
const barrePapier = document.getElementById('conso-barre-papier');
const barreRuban = document.getElementById('conso-barre-ruban');
barrePapier.style.width = pctPapier + '%';
barreRuban.style.width = pctRuban + '%';
barrePapier.style.background = pctPapier < 10 ? '#f44336' : pctPapier < 25 ? '#fb8c00' : '#43a047';
barreRuban.style.background = pctRuban < 10 ? '#f44336' : pctRuban < 25 ? '#fb8c00' : '#43a047';
const diag = document.getElementById('conso-diagnostic');
const papierUsed = data.papier_utilise;
const rubanUsed = data.ruban_utilise;
let html = '<strong>Diagnostic</strong><br>';
html += `Feuilles consommees : ${papierUsed}<br>`;
html += `Poses ruban consommees : ${rubanUsed}<br>`;
html += `Photos imprimees : ${data.photos_imprimees}<br>`;
if (papierUsed > 0) {
const ratio = (rubanUsed / papierUsed).toFixed(2);
html += `<br>Ratio ruban/papier : <strong>${ratio}</strong>`;
if (parseFloat(ratio) <= 0.75) {
html += ' &#9989; Rembobinage actif (economie ruban)';
} else if (parseFloat(ratio) >= 0.95) {
html += ' &#9888; Ratio 1:1 — pas d\'economie ruban';
}
}
diag.innerHTML = html;
} catch (e) {
console.warn('Erreur chargement consommables:', e);
}
}
async function resetConsommable(quoi) {
const labels = { papier: 'Reinitialiser le compteur papier ?', ruban: 'Reinitialiser le compteur ruban ?', tout: 'Reinitialiser tous les compteurs ?' };
if (!confirm(labels[quoi] || 'Reinitialiser ?')) return;
await apiPost('/api/consommables/reset', { quoi });
chargerConsommables();
afficherStatut('Consommable reinitialise', 'succes');
}
async function sauvegarderCapacites() {
const papier = parseInt(getValue('conso-cap-papier')) || 400;
const ruban = parseInt(getValue('conso-cap-ruban')) || 400;
await apiPost('/api/consommables/capacite', { papier_capacite: papier, ruban_capacite: ruban });
chargerConsommables();
afficherStatut('Capacites sauvegardees', 'succes');
}
// === VIDEOS DE SITUATION ===
async function chargerVideosAdmin() {