- Ecran partage : boutons bas masqués quand formulaire impression ou email ouvert - "Terminer" remplacé par "Recommencer" (retour mode, pas accueil) - Reset photoImpression/formatImpression/nbExemplaires dans recommencer() - Bouton Pellicule affiche "N poses = 2 tirages" pour expliquer le strip dupliqué - CSS .mode-detail pour sous-titre discret sur bouton mode - Bump cache versions (style v18, app v27, share v11) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
814 lines
26 KiB
JavaScript
814 lines
26 KiB
JavaScript
/* Application principale - Routeur SPA et logique globale */
|
|
|
|
let config = {};
|
|
let modeActuel = 'simple';
|
|
let photosSession = []; // Photos de la session en cours
|
|
let photoFinale = null; // Photo finale (avec effets)
|
|
let ecranActuel = 'accueil';
|
|
let _veilleTimer = null;
|
|
let _enVeille = false;
|
|
let _spotsAllumes = false;
|
|
const _shutterSound = new Audio('/sounds/shutter.wav');
|
|
_shutterSound.volume = 0.8;
|
|
|
|
// --- Initialisation ---
|
|
|
|
async function init() {
|
|
config = await apiGet('/api/config');
|
|
appliquerConfig();
|
|
setupEcranAccueil();
|
|
setupModes();
|
|
setupOnglets();
|
|
chargerBoothInfo();
|
|
verifierPhotostationDisponible();
|
|
}
|
|
|
|
function appliquerConfig() {
|
|
const event = config.evenement || {};
|
|
const fonc = config.fonctionnalites || {};
|
|
|
|
// Theme
|
|
const theme = event.theme || 'base';
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
|
|
// Couleurs (seulement si theme base, sinon le theme CSS gere)
|
|
if (theme === 'base') {
|
|
document.documentElement.style.setProperty('--primaire', event.couleur_primaire || '#e91e63');
|
|
document.documentElement.style.setProperty('--secondaire', event.couleur_secondaire || '#ffffff');
|
|
}
|
|
|
|
// Logo selon le theme
|
|
const logo = document.getElementById('accueil-logo');
|
|
if (logo) {
|
|
const logos = { sikapics: '/assets/logos/sikapics.png' };
|
|
if (logos[theme]) {
|
|
logo.src = logos[theme];
|
|
logo.classList.remove('cache');
|
|
} else {
|
|
logo.classList.add('cache');
|
|
}
|
|
}
|
|
|
|
// Nom evenement
|
|
const h1 = document.getElementById('nom-evenement');
|
|
if (h1) h1.textContent = event.nom || 'Photobooth';
|
|
|
|
// Visibilite des modes (restreint par formats evenement)
|
|
const fmtsEvent = event.formats_actifs;
|
|
const stripDispo = !fmtsEvent || fmtsEvent.includes('strip');
|
|
toggleVisible('btn-multi', fonc.multi_shot && stripDispo);
|
|
toggleVisible('btn-imprimer', fonc.impression);
|
|
toggleVisible('btn-email', fonc.email);
|
|
const boothActif = config.booth?.actif;
|
|
toggleVisible('btn-qr', fonc.qr_code || boothActif);
|
|
|
|
// Media d'accueil (video/gif en boucle)
|
|
appliquerMediaAccueil(event.media_accueil);
|
|
|
|
// Precharger image surprise
|
|
if (typeof prechargerSurprise === 'function') prechargerSurprise();
|
|
}
|
|
|
|
function appliquerMediaAccueil(media) {
|
|
const video = document.getElementById('accueil-video');
|
|
const gif = document.getElementById('accueil-gif');
|
|
const animDefaut = document.getElementById('accueil-animation-defaut');
|
|
|
|
// Reset
|
|
video.classList.add('cache');
|
|
gif.classList.add('cache');
|
|
video.pause();
|
|
video.src = '';
|
|
gif.src = '';
|
|
|
|
if (!media) {
|
|
// Pas de media, afficher l'animation par defaut
|
|
animDefaut.classList.remove('cache');
|
|
return;
|
|
}
|
|
|
|
animDefaut.classList.add('cache');
|
|
const ext = media.split('.').pop().toLowerCase();
|
|
|
|
if (ext === 'gif') {
|
|
gif.src = `/assets/animations/${media}`;
|
|
gif.classList.remove('cache');
|
|
} else {
|
|
video.src = `/assets/animations/${media}`;
|
|
video.classList.remove('cache');
|
|
video.play().catch(() => {});
|
|
}
|
|
}
|
|
|
|
// --- Navigation ---
|
|
|
|
function allerA(ecran) {
|
|
const tous = document.querySelectorAll('.ecran');
|
|
tous.forEach(e => e.classList.remove('actif'));
|
|
|
|
const cible = document.getElementById('ecran-' + ecran);
|
|
if (cible) {
|
|
cible.classList.add('actif');
|
|
ecranActuel = ecran;
|
|
}
|
|
|
|
// Masquer le popup QR a chaque changement d'ecran
|
|
fermerQR();
|
|
|
|
// Actions au changement d'ecran
|
|
if (ecran === 'accueil') {
|
|
photosSession = [];
|
|
photoFinale = null;
|
|
arreterPreview();
|
|
majCompteurAccueil();
|
|
eteindreSpots();
|
|
lancerTimerVeille();
|
|
} else {
|
|
arreterTimerVeille();
|
|
reveillerEclairage();
|
|
}
|
|
if (ecran === 'capture') {
|
|
lancerCapture();
|
|
} else if (ecran === 'partage') {
|
|
majBoutonImprimerCompteur();
|
|
} else if (ecran === 'galerie') {
|
|
chargerGalerie();
|
|
} else if (ecran === 'admin') {
|
|
chargerAdmin();
|
|
demarrerTimeoutAdmin();
|
|
} else {
|
|
arreterTimeoutAdmin();
|
|
}
|
|
}
|
|
|
|
function demarrerTimeoutAdmin() {}
|
|
function arreterTimeoutAdmin() {}
|
|
|
|
function recommencer() {
|
|
photosSession = [];
|
|
photoFinale = null;
|
|
photoImpression = null;
|
|
formatImpression = null;
|
|
nbExemplaires = 1;
|
|
allerA('mode');
|
|
}
|
|
|
|
// --- Ecran accueil ---
|
|
|
|
function setupEcranAccueil() {
|
|
const accueil = document.getElementById('ecran-accueil');
|
|
|
|
// Toucher pour commencer (ignorer le bouton admin et le popup)
|
|
accueil.addEventListener('click', (e) => {
|
|
if (e.target.closest('.btn-admin')) return;
|
|
if (e.target.closest('.btn-photostation')) return;
|
|
if (_enVeille) reveillerEclairage();
|
|
allerA('mode');
|
|
});
|
|
}
|
|
|
|
// --- Admin : icone + mot de passe ---
|
|
|
|
const MDP_ADMIN = '1234';
|
|
const PIN_PHOTOSTATION = '1234';
|
|
let _pinPhotostationSaisi = '';
|
|
|
|
function ouvrirAdmin() {
|
|
const popup = document.getElementById('popup-mdp');
|
|
const input = document.getElementById('input-mdp-admin');
|
|
popup.classList.remove('cache');
|
|
input.value = '';
|
|
document.getElementById('mdp-erreur').classList.add('cache');
|
|
}
|
|
|
|
function fermerPopupMdp() {
|
|
document.getElementById('popup-mdp').classList.add('cache');
|
|
document.getElementById('input-mdp-admin').value = '';
|
|
}
|
|
|
|
function paveTouche(chiffre) {
|
|
const input = document.getElementById('input-mdp-admin');
|
|
input.value += chiffre;
|
|
}
|
|
|
|
function paveEffacer() {
|
|
const input = document.getElementById('input-mdp-admin');
|
|
input.value = input.value.slice(0, -1);
|
|
}
|
|
|
|
// --- Photostation ---
|
|
|
|
async function verifierPhotostationDisponible() {
|
|
const data = await apiGet('/api/photostation/disponible').catch(() => null);
|
|
if (data?.disponible) {
|
|
document.getElementById('btn-photostation').classList.remove('cache');
|
|
}
|
|
}
|
|
|
|
function ouvrirPhotostation() {
|
|
_pinPhotostationSaisi = '';
|
|
_majAffichagePin();
|
|
document.getElementById('pin-photostation-erreur').classList.add('cache');
|
|
document.getElementById('popup-pin-photostation').classList.remove('cache');
|
|
}
|
|
|
|
function fermerPopupPhotostation() {
|
|
document.getElementById('popup-pin-photostation').classList.add('cache');
|
|
_pinPhotostationSaisi = '';
|
|
}
|
|
|
|
function pinPhotostationTouche(c) {
|
|
if (_pinPhotostationSaisi.length >= 4) return;
|
|
_pinPhotostationSaisi += c;
|
|
_majAffichagePin();
|
|
if (_pinPhotostationSaisi.length === 4) {
|
|
setTimeout(_validerPinPhotostation, 150);
|
|
}
|
|
}
|
|
|
|
function pinPhotostationEffacer() {
|
|
_pinPhotostationSaisi = _pinPhotostationSaisi.slice(0, -1);
|
|
_majAffichagePin();
|
|
}
|
|
|
|
function _majAffichagePin() {
|
|
const n = _pinPhotostationSaisi.length;
|
|
const el = document.getElementById('pin-photostation-affichage');
|
|
el.textContent = '●'.repeat(n) + '○'.repeat(4 - n);
|
|
}
|
|
|
|
async function _validerPinPhotostation() {
|
|
if (_pinPhotostationSaisi !== PIN_PHOTOSTATION) {
|
|
document.getElementById('pin-photostation-erreur').classList.remove('cache');
|
|
_pinPhotostationSaisi = '';
|
|
_majAffichagePin();
|
|
return;
|
|
}
|
|
fermerPopupPhotostation();
|
|
afficherStatut('Lancement de la station d\'impression...', 'succes');
|
|
await apiPost('/api/photostation/lancer', {});
|
|
}
|
|
|
|
function validerMdpAdmin() {
|
|
const input = document.getElementById('input-mdp-admin');
|
|
if (input.value === MDP_ADMIN) {
|
|
fermerPopupMdp();
|
|
allerA('admin-choix');
|
|
} else {
|
|
document.getElementById('mdp-erreur').classList.remove('cache');
|
|
input.value = '';
|
|
}
|
|
}
|
|
|
|
function ouvrirWizard() {
|
|
allerA('wizard');
|
|
wizardInit();
|
|
}
|
|
|
|
// Valider avec Entree
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' && !document.getElementById('popup-mdp').classList.contains('cache')) {
|
|
validerMdpAdmin();
|
|
}
|
|
});
|
|
|
|
// --- Modes ---
|
|
|
|
let cadreChoisi = null; // cadre sélectionné par l'utilisateur avant capture
|
|
|
|
function setupModes() {
|
|
document.querySelectorAll('#ecran-mode .btn-mode').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
modeActuel = btn.dataset.mode;
|
|
cadreChoisi = null;
|
|
allerA('capture');
|
|
});
|
|
});
|
|
majDetailPellicule();
|
|
}
|
|
|
|
function majDetailPellicule() {
|
|
const el = document.getElementById('pellicule-detail');
|
|
if (!el) return;
|
|
const nb = config.multi_shot?.nombre_photos || 4;
|
|
el.textContent = `${nb} poses = 2 tirages`;
|
|
}
|
|
|
|
async function chargerCadresChoix() {
|
|
const cfg = await apiGet('/api/config');
|
|
const fmt = (cfg.impression?.format || '15x20').replace('-2up', '');
|
|
const data = await apiGet(`/api/cadres-impression/${fmt}`).catch(() => null);
|
|
if (!data || data.disponibles.length === 0) return false;
|
|
|
|
const grille = document.getElementById('grille-cadres-choix');
|
|
// Conserver "Sans cadre" en premier, supprimer les anciens cadres
|
|
const aucun = grille.querySelector('.cadre-choix-aucun');
|
|
grille.innerHTML = '';
|
|
if (aucun) grille.appendChild(aucun);
|
|
|
|
for (const nom of data.disponibles) {
|
|
const div = document.createElement('div');
|
|
div.className = 'cadre-choix-item';
|
|
div.innerHTML = `<div class="cadre-thumb-wrap"><img src="/assets/cadres/${fmt}/${nom}" alt="${nom}"></div><span>${nom.replace('.png','')}</span>`;
|
|
div.onclick = () => {
|
|
cadreChoisi = nom;
|
|
allerA('capture');
|
|
};
|
|
grille.appendChild(div);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function lancerSansCadre() {
|
|
cadreChoisi = null;
|
|
allerA('capture');
|
|
}
|
|
|
|
// --- Rubriques + Onglets admin ---
|
|
|
|
let rubriqueActive = 'general';
|
|
|
|
function changerRubrique(nom) {
|
|
rubriqueActive = nom;
|
|
document.querySelectorAll('.rubrique').forEach(r => r.classList.toggle('actif', r.dataset.rubrique === nom));
|
|
document.querySelectorAll('.admin-onglets').forEach(g => g.classList.toggle('cache', g.id !== 'onglets-' + nom));
|
|
document.querySelectorAll('.admin-panneau').forEach(p => p.classList.remove('actif'));
|
|
document.querySelectorAll('.onglet').forEach(o => o.classList.remove('actif'));
|
|
const groupe = document.getElementById('onglets-' + nom);
|
|
if (groupe) {
|
|
const premier = groupe.querySelector('.onglet');
|
|
if (premier) { premier.click(); }
|
|
}
|
|
}
|
|
|
|
function setupOnglets() {
|
|
document.querySelectorAll('.onglet').forEach(onglet => {
|
|
onglet.addEventListener('click', () => {
|
|
const groupe = onglet.closest('.admin-onglets');
|
|
if (groupe) groupe.querySelectorAll('.onglet').forEach(o => o.classList.remove('actif'));
|
|
document.querySelectorAll('.admin-panneau').forEach(p => p.classList.remove('actif'));
|
|
onglet.classList.add('actif');
|
|
const panneau = document.getElementById('panneau-' + onglet.dataset.onglet);
|
|
if (panneau) panneau.classList.add('actif');
|
|
});
|
|
});
|
|
}
|
|
|
|
// --- Utilitaires ---
|
|
|
|
async function apiGet(url) {
|
|
const resp = await fetch(url);
|
|
return resp.json();
|
|
}
|
|
|
|
async function apiPost(url, data = {}) {
|
|
const resp = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return resp.json();
|
|
}
|
|
|
|
function toggleVisible(id, visible) {
|
|
const el = document.getElementById(id);
|
|
if (el) {
|
|
if (visible === false) el.classList.add('cache');
|
|
else el.classList.remove('cache');
|
|
}
|
|
}
|
|
|
|
async function chargerBoothInfo() {
|
|
const booth = config.booth || {};
|
|
const el = document.getElementById('booth-info');
|
|
if (!booth.actif || !booth.url) {
|
|
el.classList.add('cache');
|
|
return;
|
|
}
|
|
try {
|
|
const info = await apiGet('/api/booth/info');
|
|
if (info.password) {
|
|
const eventId = (config.evenement || {}).event_id || booth.event_id || 'default';
|
|
const qrUrl = `${booth.url}/g/${eventId}?p=${info.password}`;
|
|
document.getElementById('booth-qr').src = `/api/qr?url=${encodeURIComponent(qrUrl)}`;
|
|
el.classList.remove('cache');
|
|
}
|
|
} catch (e) {
|
|
el.classList.add('cache');
|
|
}
|
|
}
|
|
|
|
function fermerQR() {
|
|
document.getElementById('popup-qr').classList.add('cache');
|
|
}
|
|
|
|
function afficherStatut(message, type = 'succes') {
|
|
const el = document.getElementById('statut-partage');
|
|
el.textContent = message;
|
|
el.className = 'statut-partage ' + type;
|
|
el.classList.remove('cache');
|
|
setTimeout(() => el.classList.add('cache'), 4000);
|
|
}
|
|
|
|
// WebSocket : mise a jour config en temps reel
|
|
wsOnMessage('config_maj', (msg) => {
|
|
config = msg.config;
|
|
appliquerConfig();
|
|
if (ecranActuel === 'accueil') lancerTimerVeille();
|
|
});
|
|
|
|
// === Erreur equipement : overlay intelligent avec escalade ===
|
|
let _erreurCameraTimer = null;
|
|
let _erreurCameraCount = 0;
|
|
let _erreurCameraPhase = 0; // 0=auto-fix, 1=patience, 2=operator, 3=restart
|
|
|
|
function _showErreurEquipement(icon, titre, msg, phase) {
|
|
const el = document.getElementById('erreur-equipement');
|
|
document.getElementById('erreur-equip-icon').textContent = icon;
|
|
document.getElementById('erreur-equip-titre').textContent = titre;
|
|
document.getElementById('erreur-equip-msg').textContent = msg;
|
|
const spinner = document.getElementById('erreur-equip-spinner');
|
|
const steps = document.getElementById('erreur-equip-steps');
|
|
const btnRetry = document.getElementById('erreur-equip-btn-retry');
|
|
const btnVideo = document.getElementById('erreur-equip-btn-video');
|
|
const btnRestart = document.getElementById('erreur-equip-btn-restart');
|
|
|
|
spinner.classList.toggle('cache', phase >= 2);
|
|
steps.classList.toggle('cache', phase < 2);
|
|
btnRetry.classList.toggle('cache', phase < 1);
|
|
btnVideo.classList.toggle('cache', phase < 2);
|
|
btnRestart.classList.toggle('cache', phase < 2);
|
|
|
|
if (phase >= 2) {
|
|
steps.innerHTML = '<ol>' +
|
|
'<li>Verifiez que l\'appareil photo est allume (bouton ON)</li>' +
|
|
'<li>Verifiez le cable USB entre l\'appareil et la borne</li>' +
|
|
'<li>Eteignez et rallumez l\'appareil photo</li>' +
|
|
'<li>Si le probleme persiste, redemarrez la borne</li>' +
|
|
'</ol>';
|
|
}
|
|
el.classList.remove('cache');
|
|
}
|
|
|
|
function fermerErreurEquipement() {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
_erreurCameraCount = 0;
|
|
_erreurCameraPhase = 0;
|
|
allerA('accueil');
|
|
}
|
|
|
|
function erreurEquipRetry() {
|
|
document.getElementById('erreur-equip-titre').textContent = 'Verification en cours...';
|
|
document.getElementById('erreur-equip-msg').textContent = 'Un instant...';
|
|
document.getElementById('erreur-equip-spinner').classList.remove('cache');
|
|
document.getElementById('erreur-equip-steps').classList.add('cache');
|
|
document.getElementById('erreur-equip-btn-retry').classList.add('cache');
|
|
if (_erreurEquipType === 'imprimante') {
|
|
fetch('/api/imprimante/reactiver', { method: 'POST' }).catch(() => {});
|
|
} else {
|
|
fetch('/api/camera/reconnecter', { method: 'POST', headers: {'Content-Type':'application/json'}, body: '{}' }).catch(() => {});
|
|
}
|
|
}
|
|
|
|
let _erreurEquipType = 'camera';
|
|
|
|
function erreurEquipVideo() {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
const situationMap = { camera: 'camera_hs', imprimante: window._imprimanteErreurSituation || 'depannage_imprimante' };
|
|
const situation = situationMap[_erreurEquipType] || 'camera_hs';
|
|
if (typeof lancerWizardSituation === 'function') {
|
|
lancerWizardSituation(situation, _erreurEquipType);
|
|
}
|
|
}
|
|
|
|
function erreurEquipRestart() {
|
|
_showErreurEquipement('⏳', 'Redemarrage en cours', 'La borne redemarre, patientez 30 secondes...', 0);
|
|
document.getElementById('erreur-equip-btn-accueil').classList.add('cache');
|
|
fetch('/api/systeme/redemarrer', { method: 'POST' }).catch(() => {});
|
|
}
|
|
|
|
wsOnMessage('camera_erreur', (data) => {
|
|
if (window.location.pathname === '/admin') return;
|
|
_erreurCameraCount++;
|
|
_erreurEquipType = 'camera';
|
|
if (_erreurCameraCount <= 2) {
|
|
_erreurCameraPhase = 0;
|
|
_showErreurEquipement('📷', 'Preparation de l\'appareil photo', 'Un instant, reconnexion automatique...', 0);
|
|
} else if (_erreurCameraCount <= 5) {
|
|
_erreurCameraPhase = 1;
|
|
_showErreurEquipement('📷', 'L\'appareil photo ne repond pas', 'Le systeme essaie de le reconnecter. Vous pouvez aussi reessayer manuellement.', 1);
|
|
} else {
|
|
_erreurCameraPhase = 2;
|
|
_showErreurEquipement('📷', 'Appareil photo injoignable', 'Suivez ces etapes pour le remettre en marche :', 2);
|
|
}
|
|
if (_erreurCameraTimer) clearTimeout(_erreurCameraTimer);
|
|
_erreurCameraTimer = setTimeout(() => { location.reload(); }, 180000);
|
|
});
|
|
|
|
wsOnMessage('shutter', () => {
|
|
_shutterSound.currentTime = 0;
|
|
_shutterSound.play().catch(() => {});
|
|
});
|
|
|
|
wsOnMessage('camera_ok', () => {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
_erreurCameraCount = 0;
|
|
_erreurCameraPhase = 0;
|
|
if (_erreurCameraTimer) { clearTimeout(_erreurCameraTimer); _erreurCameraTimer = null; }
|
|
});
|
|
|
|
wsOnMessage('imprimante_erreur', (data) => {
|
|
if (window.location.pathname === '/admin') return;
|
|
_erreurEquipType = 'imprimante';
|
|
const titre = data.titre || 'Probleme imprimante';
|
|
const msg = data.message || 'L\'imprimante ne fonctionne pas correctement.';
|
|
const etapes = data.etapes || [];
|
|
const situation = data.situation || 'depannage_imprimante';
|
|
_showErreurEquipement('🖨', titre, msg, 2);
|
|
const steps = document.getElementById('erreur-equip-steps');
|
|
if (etapes.length) {
|
|
steps.innerHTML = '<ol>' + etapes.map(e => '<li>' + e + '</li>').join('') + '</ol>';
|
|
steps.classList.remove('cache');
|
|
}
|
|
document.getElementById('erreur-equip-btn-retry').classList.remove('cache');
|
|
document.getElementById('erreur-equip-btn-retry').textContent = 'Verifier';
|
|
document.getElementById('erreur-equip-btn-retry').onclick = function() {
|
|
fetch('/api/imprimante/reactiver', { method: 'POST' }).catch(() => {});
|
|
_showErreurEquipement('🖨', 'Verification en cours...', 'Un instant...', 0);
|
|
};
|
|
document.getElementById('erreur-equip-btn-video').classList.remove('cache');
|
|
document.getElementById('erreur-equip-btn-video').onclick = function() {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
if (typeof lancerWizardSituation === 'function') lancerWizardSituation(situation, 'imprimante');
|
|
};
|
|
window._imprimanteErreurSituation = situation;
|
|
});
|
|
|
|
wsOnMessage('imprimante_ok', () => {
|
|
if (_erreurEquipType === 'imprimante') {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
}
|
|
document.getElementById('printer-erreur').classList.add('cache');
|
|
});
|
|
|
|
function _afficherErreurImprimante(msg) {
|
|
const el = document.getElementById('printer-erreur');
|
|
document.getElementById('printer-erreur-msg').textContent = msg;
|
|
el.classList.remove('cache');
|
|
}
|
|
|
|
async function _pollStatutImprimante() {
|
|
try {
|
|
const r = await apiGet('/api/imprimante/statut-detail');
|
|
const el = document.getElementById('printer-erreur');
|
|
if (r.statut && !r.statut.includes('stopped') && !r.statut.includes('disabled')) {
|
|
el.classList.add('cache');
|
|
if (_erreurEquipType === 'imprimante') {
|
|
document.getElementById('erreur-equipement').classList.add('cache');
|
|
}
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
setInterval(_pollStatutImprimante, 30000);
|
|
|
|
// === WIZARD ===
|
|
|
|
let wizardStep = 1;
|
|
const WIZARD_TOTAL = 6;
|
|
let wizardData = {
|
|
nom: '',
|
|
theme: 'base',
|
|
modes: { simple: true, multi: true },
|
|
nbPhotos: 4,
|
|
fonctions: {},
|
|
};
|
|
|
|
function wizardInit() {
|
|
wizardStep = 1;
|
|
wizardData.nom = config.evenement?.nom || '';
|
|
wizardData.theme = config.evenement?.theme || 'base';
|
|
wizardData.nbPhotos = config.multi_shot?.nombre_photos || 4;
|
|
document.getElementById('wiz-nom').value = wizardData.nom;
|
|
document.getElementById('wiz-nb-photos').textContent = wizardData.nbPhotos;
|
|
wizardAfficherStep();
|
|
}
|
|
|
|
function wizardAfficherStep() {
|
|
document.querySelectorAll('.wizard-step').forEach(s => s.classList.remove('actif'));
|
|
const step = document.querySelector(`.wizard-step[data-step="${wizardStep}"]`);
|
|
if (step) step.classList.add('actif');
|
|
|
|
// Progress dots
|
|
const prog = document.getElementById('wizard-progress');
|
|
prog.innerHTML = '';
|
|
for (let i = 1; i <= WIZARD_TOTAL; i++) {
|
|
const dot = document.createElement('div');
|
|
dot.className = 'wizard-dot' + (i === wizardStep ? ' actif' : '') + (i < wizardStep ? ' fait' : '');
|
|
prog.appendChild(dot);
|
|
}
|
|
}
|
|
|
|
function wizardSuivant() {
|
|
// Sauvegarder l'etape courante
|
|
if (wizardStep === 1) wizardData.nom = document.getElementById('wiz-nom').value;
|
|
if (wizardStep === 4) wizardData.nbPhotos = parseInt(document.getElementById('wiz-nb-photos').textContent);
|
|
|
|
wizardStep++;
|
|
if (wizardStep > WIZARD_TOTAL) wizardStep = WIZARD_TOTAL;
|
|
|
|
// Resume
|
|
if (wizardStep === WIZARD_TOTAL) {
|
|
const modes = Object.keys(wizardData.modes).filter(k => wizardData.modes[k]);
|
|
const foncs = Object.keys(wizardData.fonctions).filter(k => wizardData.fonctions[k]);
|
|
document.getElementById('wiz-resume').innerHTML = `
|
|
<div><strong>Evenement :</strong> ${wizardData.nom || 'Photobooth'}</div>
|
|
<div><strong>Theme :</strong> ${wizardData.theme}</div>
|
|
<div><strong>Modes :</strong> ${modes.join(', ') || 'photo'}</div>
|
|
<div><strong>Photos/pellicule :</strong> ${wizardData.nbPhotos}</div>
|
|
<div><strong>Partage :</strong> ${foncs.join(', ') || 'aucun'}</div>
|
|
`;
|
|
}
|
|
|
|
wizardAfficherStep();
|
|
}
|
|
|
|
function wizardChoisir(btn, key) {
|
|
btn.closest('.wizard-choix').querySelectorAll('.wizard-card').forEach(c => c.classList.remove('actif'));
|
|
btn.classList.add('actif');
|
|
wizardData[key] = btn.dataset.val;
|
|
}
|
|
|
|
function wizardToggle(btn) {
|
|
btn.classList.toggle('actif');
|
|
const val = btn.dataset.val;
|
|
const isActive = btn.classList.contains('actif');
|
|
|
|
if (['simple', 'multi'].includes(val)) {
|
|
wizardData.modes[val] = isActive;
|
|
} else {
|
|
wizardData.fonctions[val] = isActive;
|
|
}
|
|
}
|
|
|
|
function wizardNbPhotos(delta) {
|
|
const el = document.getElementById('wiz-nb-photos');
|
|
let n = parseInt(el.textContent) + delta;
|
|
n = Math.max(2, Math.min(8, n));
|
|
el.textContent = n;
|
|
wizardData.nbPhotos = n;
|
|
}
|
|
|
|
async function wizardTerminer() {
|
|
// Sauvegarder toute la config
|
|
await apiPost('/api/config', {
|
|
evenement: {
|
|
nom: wizardData.nom || 'Photobooth',
|
|
theme: wizardData.theme,
|
|
},
|
|
fonctionnalites: {
|
|
photo_simple: wizardData.modes.simple !== false,
|
|
multi_shot: wizardData.modes.multi !== false,
|
|
impression: !!wizardData.fonctions.impression,
|
|
email: !!wizardData.fonctions.email,
|
|
qr_code: !!wizardData.fonctions.qr,
|
|
},
|
|
multi_shot: {
|
|
nombre_photos: wizardData.nbPhotos,
|
|
mode: 'strip',
|
|
},
|
|
booth: {
|
|
actif: !!wizardData.fonctions.galerie_live,
|
|
},
|
|
});
|
|
|
|
config = await apiGet('/api/config');
|
|
appliquerConfig();
|
|
chargerBoothInfo();
|
|
allerA('accueil');
|
|
}
|
|
|
|
// --- Veille eclairage ---
|
|
|
|
function lancerTimerVeille() {
|
|
arreterTimerVeille();
|
|
const delai = (config.relais?.veille_delai || 0) * 60000;
|
|
if (delai <= 0) return;
|
|
_veilleTimer = setTimeout(() => {
|
|
_enVeille = true;
|
|
_spotsAllumes = false;
|
|
fetch('/api/relais/veille', { method: 'POST' }).catch(() => {});
|
|
}, delai);
|
|
}
|
|
|
|
function arreterTimerVeille() {
|
|
if (_veilleTimer) { clearTimeout(_veilleTimer); _veilleTimer = null; }
|
|
}
|
|
|
|
function reveillerEclairage() {
|
|
if (!_enVeille && _spotsAllumes) return;
|
|
_enVeille = false;
|
|
_spotsAllumes = true;
|
|
arreterTimerVeille();
|
|
fetch('/api/relais/reveil', { method: 'POST' }).catch(() => {});
|
|
}
|
|
|
|
function eteindreSpots() {
|
|
_spotsAllumes = false;
|
|
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 ? '🔒' : ''} ${n.enregistre ? '(enregistre)' : ''}</div>
|
|
</div>
|
|
${n.actif ? '<span style="color:#4caf50;font-weight:700">✓</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(() => {
|
|
if (window.location.pathname === '/admin') {
|
|
allerA('admin-choix');
|
|
}
|
|
});
|
|
});
|