fix: settings - add missing previewFlash/saveFlash/colorPickerToRgb/loadFlashConfig; fix previewBrightness to call API; fix printer refresh to update state badges
🚀 Deploy — JH Photomaton / 🔍 Vérification (push) Has been cancelled
🚀 Deploy — JH Photomaton / 🍓 Deploy sur le Pi (push) Has been cancelled

This commit is contained in:
2026-07-17 15:49:13 +02:00
parent fed824fbb1
commit f6d4eafd20
2 changed files with 130 additions and 18 deletions
+65 -16
View File
@@ -128,14 +128,14 @@
</div> </div>
<div class="printer-states"> <div class="printer-states">
<span class="badge-printer bs-{{ p.state }}"> <span class="badge-printer badge-state bs-{{ p.state }}">
{% if p.state == 'idle' %}✅ Disponible {% if p.state == 'idle' %}✅ Disponible
{% elif p.state == 'printing' %}🖨 En impression {% elif p.state == 'printing' %}🖨 En impression
{% elif p.state == 'disabled' %}⏸ Désactivée {% elif p.state == 'disabled' %}⏸ Désactivée
{% elif p.state == 'offline' %}❌ Hors ligne {% elif p.state == 'offline' %}❌ Hors ligne
{% else %}❓ {{ p.state }}{% endif %} {% else %}❓ {{ p.state }}{% endif %}
</span> </span>
<span class="badge-printer {{ 'bs-accept' if p.accepting else 'bs-reject' }}"> <span class="badge-printer badge-accept {{ 'bs-accept' if p.accepting else 'bs-reject' }}">
{{ '✓ Accepte les jobs' if p.accepting else '✗ Refuse les jobs' }} {{ '✓ Accepte les jobs' if p.accepting else '✗ Refuse les jobs' }}
</span> </span>
{% if p.jobs_count %} {% if p.jobs_count %}
@@ -151,15 +151,15 @@
</div> </div>
<!-- Erreurs / raisons CUPS --> <!-- Erreurs / raisons CUPS -->
<div class="printer-reasons" style="display:flex;flex-direction:column;gap:.3rem">
{% if p.reasons %} {% if p.reasons %}
<div style="display:flex;flex-direction:column;gap:.3rem">
{% for reason in p.reasons %} {% for reason in p.reasons %}
<div style="background:rgba(192,0,0,.12);border:1px solid rgba(192,0,0,.25);border-radius:6px;padding:.3rem .65rem;font-size:.82rem;color:#e05050"> <div style="background:rgba(192,0,0,.12);border:1px solid rgba(192,0,0,.25);border-radius:6px;padding:.3rem .65rem;font-size:.82rem;color:#e05050">
{{ reason }} {{ reason }}
</div> </div>
{% endfor %} {% endfor %}
</div>
{% endif %} {% endif %}
</div>
<!-- Niveaux encre/ruban --> <!-- Niveaux encre/ruban -->
{% if p.markers %} {% if p.markers %}
@@ -344,27 +344,76 @@ document.querySelectorAll('input[name="print-mode"]').forEach(r => {
}); });
// ── Imprimantes ──────────────────────────────────────────────────────────────── // ── Imprimantes ────────────────────────────────────────────────────────────────
const STATE_LABELS = {
idle: '✅ Disponible',
printing: '🖨 En impression',
disabled: '⏸ Désactivée',
offline: '❌ Hors ligne',
error: '❌ Erreur',
unknown: '❓ Inconnu',
};
const STATE_CLASSES = {
idle: 'bs-idle', printing: 'bs-printing', disabled: 'bs-disabled',
offline: 'bs-offline', error: 'bs-offline', unknown: 'bs-unknown',
};
async function refreshPrinters() { async function refreshPrinters() {
try { try {
const printers = await api('GET', '/api/print/printers'); const printers = await api('GET', '/api/print/printers');
printers.forEach(p => { printers.forEach(p => {
// Met à jour les badges et compteur de jobs
const card = document.getElementById('pc-' + p.name); const card = document.getElementById('pc-' + p.name);
if (!card) return; if (!card) return;
// Classe de la carte
card.className = `printer-card state-${p.state}`; card.className = `printer-card state-${p.state}`;
// Actualise les jobs CUPS
// Badge état
const stateBadge = card.querySelector('.badge-state');
if (stateBadge) {
stateBadge.className = `badge-printer badge-state ${STATE_CLASSES[p.state] || 'bs-unknown'}`;
stateBadge.textContent = STATE_LABELS[p.state] || p.state;
}
// Badge accepting
const acceptBadge = card.querySelector('.badge-accept');
if (acceptBadge) {
acceptBadge.className = `badge-printer badge-accept ${p.accepting ? 'bs-accept' : 'bs-reject'}`;
acceptBadge.textContent = p.accepting ? '✓ Accepte les jobs' : '✗ Refuse les jobs';
}
// Boutons actions
const actionsDiv = card.querySelector('.printer-actions');
if (actionsDiv) {
actionsDiv.innerHTML = `
${p.state === 'disabled'
? `<button class="btn-xs btn-enable" onclick="printerAction('${p.name}','enable')">▶ Activer</button>`
: `<button class="btn-xs btn-disable" onclick="printerAction('${p.name}','disable')">⏸ Désactiver</button>`}
${p.accepting
? `<button class="btn-xs btn-reject" onclick="printerAction('${p.name}','reject')">🚫 Refuser jobs</button>`
: `<button class="btn-xs btn-enable" onclick="printerAction('${p.name}','enable')">✓ Accepter jobs</button>`}
${p.jobs_count ? `<button class="btn-xs btn-clear" onclick="clearJobs('${p.name}')">✕ Vider file</button>` : ''}
`;
}
// Jobs CUPS
const jobsDiv = document.getElementById('jobs-' + p.name); const jobsDiv = document.getElementById('jobs-' + p.name);
if (jobsDiv) { if (jobsDiv) {
if (p.jobs && p.jobs.length) { jobsDiv.innerHTML = p.jobs && p.jobs.length
jobsDiv.innerHTML = p.jobs.map(j => ` ? p.jobs.map(j => `
<div class="cups-job-row"> <div class="cups-job-row">
<span class="cups-job-id">${j.id}</span> <span class="cups-job-id">${j.id}</span>
<span class="cups-job-info">${j.user}${j.size}</span> <span class="cups-job-info">${j.user}${j.size}</span>
<button class="btn-xs btn-clear" style="padding:.2rem .5rem;font-size:.75rem" onclick="cancelJob('${j.id}')">✕</button> <button class="btn-xs btn-clear" style="padding:.2rem .5rem;font-size:.75rem" onclick="cancelJob('${j.id}')">✕</button>
</div>`).join(''); </div>`).join('')
} else { : '<div class="cups-empty">Aucun job en cours</div>';
jobsDiv.innerHTML = '<div class="cups-empty">Aucun job en cours</div>'; }
}
// Erreurs/raisons
const reasonsDiv = card.querySelector('.printer-reasons');
if (reasonsDiv) {
reasonsDiv.innerHTML = p.reasons && p.reasons.length
? p.reasons.map(r => `<div style="background:rgba(192,0,0,.12);border:1px solid rgba(192,0,0,.25);border-radius:6px;padding:.3rem .65rem;font-size:.82rem;color:#e05050">${r}</div>`).join('')
: '';
} }
}); });
} catch(err) {} } catch(err) {}
+65 -2
View File
@@ -807,20 +807,82 @@ async function loadBrightnessConfig() {
} }
} }
function previewBrightness(val) { async function previewBrightness() {
const val = parseInt(document.getElementById('brightness-slider').value);
document.getElementById('brightness-val').textContent = val; document.getElementById('brightness-val').textContent = val;
try {
await api('PUT', `/api/leds/brightness?value=${val}&save=false`);
showToast(`👁 Aperçu luminosité : ${val}`, 'info');
} catch(e) {
showToast('Erreur aperçu : ' + e.message, 'error');
}
} }
async function saveBrightness() { async function saveBrightness() {
const val = parseInt(document.getElementById('brightness-slider').value); const val = parseInt(document.getElementById('brightness-slider').value);
try { try {
await api('PUT', `/api/leds/brightness?value=${val}`); await api('PUT', `/api/leds/brightness?value=${val}`);
showToast(`✅ Luminosité réglée à ${val}`, 'success'); showToast(`✅ Luminosité ${val} sauvegardée`, 'success');
} catch(e) { } catch(e) {
showToast('Erreur : ' + e.message, 'error'); showToast('Erreur : ' + e.message, 'error');
} }
} }
async function previewFlash() {
try {
await api('POST', '/api/leds/effect?effect=capture');
showToast('💡 Flash déclenché', 'info');
} catch(e) {
showToast('Erreur flash : ' + e.message, 'error');
}
}
async function saveFlash() {
const r = parseInt(document.getElementById('flash-r').value) || 255;
const g = parseInt(document.getElementById('flash-g').value) || 200;
const b = parseInt(document.getElementById('flash-b').value) || 80;
const duration = parseFloat(document.getElementById('flash-duration').value) || 0.3;
const flashes = parseInt(document.getElementById('flash-count').value) || 2;
try {
await api('PUT', `/api/leds/effect/capture?r=${r}&g=${g}&b=${b}&flash_duration=${duration}&flashes=${flashes}`);
showToast('✅ Flash sauvegardé', 'success');
} catch(e) {
showToast('Erreur : ' + e.message, 'error');
}
}
function colorPickerToRgb(hex) {
const r = parseInt(hex.slice(1,3), 16);
const g = parseInt(hex.slice(3,5), 16);
const b = parseInt(hex.slice(5,7), 16);
document.getElementById('flash-r').value = r;
document.getElementById('flash-g').value = g;
document.getElementById('flash-b').value = b;
document.getElementById('flash-preview').style.background = `rgb(${r},${g},${b})`;
}
async function loadFlashConfig() {
try {
const effects = await api('GET', '/api/leds/effects');
const cap = effects.capture;
if (!cap) return;
if (cap.color) {
document.getElementById('flash-r').value = cap.color[0] ?? 255;
document.getElementById('flash-g').value = cap.color[1] ?? 200;
document.getElementById('flash-b').value = cap.color[2] ?? 80;
document.getElementById('flash-preview').style.background =
`rgb(${cap.color[0]},${cap.color[1]},${cap.color[2]})`;
}
if (cap.flash_duration !== undefined) {
document.getElementById('flash-duration').value = cap.flash_duration;
document.getElementById('flash-dur-val').textContent = cap.flash_duration.toFixed(2);
}
if (cap.flashes !== undefined) {
document.getElementById('flash-count').value = cap.flashes;
}
} catch(e) { console.warn('loadFlashConfig:', e); }
}
async function setAutoBrightness(enabled) { async function setAutoBrightness(enabled) {
try { try {
await api('PUT', `/api/leds/auto-brightness?enabled=${enabled}`); await api('PUT', `/api/leds/auto-brightness?enabled=${enabled}`);
@@ -836,5 +898,6 @@ async function setAutoBrightness(enabled) {
// ── Init ────────────────────────────────────────────────────────────────────── // ── Init ──────────────────────────────────────────────────────────────────────
loadBrightnessConfig(); loadBrightnessConfig();
loadFlashConfig();
</script> </script>
{% endblock %} {% endblock %}