Mini menu client + luminosite impression + endpoints systeme

- Menu client coin haut-gauche : WiFi, luminosite, relancer, redemarrer, eteindre
- Popup WiFi : scan, statut, connexion (reutilise les endpoints existants)
- Popup luminosite : slider -50% a +50%, ajuste la luminosite avant impression
  (ImageEnhance.Brightness dans _preparer_image)
- Endpoints systeme : /api/systeme/eteindre, redemarrer, redemarrer-app
- Endpoint exposition Canon : /api/camera/exposition (GET choix + POST valeur)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 10:22:00 +02:00
parent bb49c2c82d
commit 5e8cff7951
5 changed files with 297 additions and 3 deletions

View File

@@ -157,6 +157,107 @@ html, body {
color: rgba(255,255,255,0.4);
}
/* Mini menu client */
.btn-menu-client {
position: absolute;
top: 12px;
left: 12px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
color: rgba(255,255,255,0.2);
cursor: pointer;
border-radius: 50%;
transition: color 0.3s;
z-index: 60;
}
.btn-menu-client:active {
color: rgba(255,255,255,0.5);
}
.menu-client {
position: absolute;
top: 60px;
left: 12px;
background: rgba(0,0,0,0.92);
border-radius: 16px;
padding: 16px;
z-index: 200;
min-width: 220px;
display: flex;
flex-direction: column;
gap: 8px;
}
.menu-client button {
display: block;
width: 100%;
padding: 14px 16px;
font-size: 1rem;
text-align: left;
background: rgba(255,255,255,0.08);
color: #fff;
border: none;
border-radius: 10px;
cursor: pointer;
}
.menu-client button:active {
background: rgba(255,255,255,0.2);
}
.menu-client-titre {
font-size: .85rem;
font-weight: 600;
color: rgba(255,255,255,0.5);
text-transform: uppercase;
letter-spacing: 1px;
padding: 0 4px 8px;
}
.popup-wifi {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.85);
z-index: 300;
display: flex;
align-items: center;
justify-content: center;
}
.popup-wifi-inner {
background: rgba(30,30,30,0.98);
border-radius: 20px;
padding: 24px;
width: 90%;
max-width: 400px;
}
.popup-wifi-inner button {
padding: 12px;
font-size: 1rem;
background: rgba(255,255,255,0.1);
color: #fff;
border: none;
border-radius: 10px;
cursor: pointer;
}
.popup-wifi-inner button:active {
background: rgba(255,255,255,0.2);
}
.wifi-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
margin: 4px 0;
background: rgba(255,255,255,0.06);
border-radius: 10px;
cursor: pointer;
}
.wifi-item:active {
background: rgba(255,255,255,0.15);
}
.wifi-item.actif {
border-left: 3px solid #4caf50;
}
/* Popup mot de passe */
.popup-overlay {
position: fixed;

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=16">
<link rel="stylesheet" href="/css/style.css?v=17">
<link rel="stylesheet" href="/css/themes.css?v=2">
</head>
<body>
@@ -59,6 +59,38 @@
<div id="btn-admin" class="btn-admin" onclick="ouvrirAdmin()">&#9881;</div>
<!-- Icone photostation coin bas-gauche (visible seulement si installée) -->
<div id="btn-photostation" class="btn-photostation cache" onclick="ouvrirPhotostation()">&#128444;</div>
<!-- Mini menu client coin haut-gauche -->
<div id="btn-menu-client" class="btn-menu-client" onclick="toggleMenuClient()">&#9776;</div>
<div id="menu-client" class="menu-client cache">
<div class="menu-client-titre">Maintenance</div>
<button onclick="menuClientAction('wifi')">&#128246; WiFi</button>
<button onclick="menuClientAction('exposition')">&#9728; Luminosite</button>
<button onclick="menuClientAction('restart-app')">&#128260; Relancer l'appli</button>
<button onclick="menuClientAction('reboot')">&#128259; Redemarrer</button>
<button onclick="menuClientAction('shutdown')">&#9211; Eteindre</button>
<button onclick="toggleMenuClient()" style="background:transparent;color:var(--text2)">Fermer</button>
</div>
<!-- Popup WiFi -->
<div id="popup-wifi" class="popup-wifi cache">
<div class="popup-wifi-inner">
<div class="menu-client-titre">WiFi</div>
<div id="wifi-status"></div>
<div id="wifi-list" style="max-height:300px;overflow-y:auto"></div>
<button onclick="scanWifi()" style="margin-top:8px;width:100%">Actualiser</button>
<button onclick="document.getElementById('popup-wifi').classList.add('cache')" style="margin-top:4px;width:100%;background:transparent;color:var(--text2)">Fermer</button>
</div>
</div>
<!-- Popup Exposition -->
<div id="popup-exposition" class="popup-wifi cache">
<div class="popup-wifi-inner">
<div class="menu-client-titre">Luminosite photo</div>
<div id="expo-status" style="text-align:center;margin:12px 0;font-size:1.5rem;font-weight:700"></div>
<input type="range" id="expo-slider" min="-9" max="9" value="0" style="width:100%" oninput="updateExpoLabel(this.value)">
<div id="expo-label" style="text-align:center;margin:8px 0;font-size:.9rem;color:var(--text2)">0</div>
<button onclick="appliquerExpo()" style="width:100%">Appliquer</button>
<button onclick="document.getElementById('popup-exposition').classList.add('cache')" style="margin-top:4px;width:100%;background:transparent;color:var(--text2)">Fermer</button>
</div>
</div>
</section>
<!-- Choix du mode -->
@@ -1145,7 +1177,7 @@
</style>
<script src="/js/websocket.js?v=16"></script>
<script src="/js/app.js?v=25"></script>
<script src="/js/app.js?v=26"></script>
<script src="/js/camera.js?v=26"></script>
<script src="/js/effects.js?v=4"></script>
<script src="/js/gallery.js?v=5"></script>

View File

@@ -707,6 +707,91 @@ function eteindreSpots() {
fetch('/api/relais/veille', { method: 'POST' }).catch(() => {});
}
// --- Mini menu client ---
function toggleMenuClient() {
document.getElementById('menu-client').classList.toggle('cache');
}
async function menuClientAction(action) {
document.getElementById('menu-client').classList.add('cache');
if (action === 'wifi') {
document.getElementById('popup-wifi').classList.remove('cache');
scanWifi();
} else if (action === 'exposition') {
document.getElementById('popup-exposition').classList.remove('cache');
chargerExposition();
} else if (action === 'shutdown') {
if (confirm('Eteindre la borne ?')) apiPost('/api/systeme/eteindre');
} else if (action === 'reboot') {
if (confirm('Redemarrer la borne ?')) apiPost('/api/systeme/redemarrer');
} else if (action === 'restart-app') {
apiPost('/api/systeme/redemarrer-app');
setTimeout(() => location.reload(), 3000);
}
}
async function scanWifi() {
const list = document.getElementById('wifi-list');
const status = document.getElementById('wifi-status');
list.innerHTML = '<div style="text-align:center;padding:16px;color:var(--text2)">Scan en cours...</div>';
try {
const s = await apiGet('/api/wifi/status');
status.innerHTML = s.connecte
? `<div style="padding:8px;color:#4caf50">Connecte a <b>${s.ssid}</b> (${s.signal || '?'}%)</div>`
: '<div style="padding:8px;color:#f44336">Non connecte</div>';
const nets = await apiGet('/api/wifi/scan');
if (!nets.length) { list.innerHTML = '<div style="padding:12px;color:var(--text2)">Aucun reseau</div>'; return; }
list.innerHTML = nets.map(n => `
<div class="wifi-item${n.actif ? ' actif' : ''}" onclick="connecterWifi('${n.ssid.replace(/'/g,"\\'")}', ${n.enregistre})">
<div>
<div style="font-weight:600">${n.ssid}</div>
<div style="font-size:.75rem;color:var(--text2)">${n.signal}% ${n.securise ? '&#128274;' : ''} ${n.enregistre ? '(enregistre)' : ''}</div>
</div>
${n.actif ? '<span style="color:#4caf50;font-weight:700">&#10003;</span>' : ''}
</div>
`).join('');
} catch(e) { list.innerHTML = '<div style="padding:12px;color:#f44336">Erreur: ' + e + '</div>'; }
}
async function connecterWifi(ssid, enregistre) {
if (enregistre) {
const r = await apiPost('/api/wifi/connect', {ssid});
alert(r.message || (r.succes ? 'Connecte' : 'Erreur'));
scanWifi();
return;
}
const mdp = prompt('Mot de passe WiFi pour ' + ssid + ' :');
if (mdp === null) return;
const r = await apiPost('/api/wifi/connect', {ssid, password: mdp});
alert(r.message || (r.succes ? 'Connecte' : 'Erreur'));
scanWifi();
}
async function chargerExposition() {
const cfg = charger_config ? charger_config() : config;
const luminosite = (cfg || config).impression?.luminosite_impression || 0;
document.getElementById('expo-slider').value = luminosite;
document.getElementById('expo-slider').min = -50;
document.getElementById('expo-slider').max = 50;
document.getElementById('expo-slider').step = 5;
updateExpoLabel(luminosite);
}
function updateExpoLabel(val) {
const signe = val > 0 ? '+' : '';
document.getElementById('expo-label').textContent = `${signe}${val}%`;
document.getElementById('expo-status').textContent = val == 0 ? 'Normal' : `${signe}${val}%`;
}
async function appliquerExpo() {
const val = parseInt(document.getElementById('expo-slider').value);
await apiPost('/api/config', {impression: {luminosite_impression: val}});
document.getElementById('popup-exposition').classList.add('cache');
afficherStatut('Luminosite impression : ' + (val > 0 ? '+' : '') + val + '%', 'succes');
config = await apiGet('/api/config');
}
// Demarrage
document.addEventListener('DOMContentLoaded', () => {
init().then(() => {