- CRUD evenements : creer, activer, editer, supprimer dans panneau admin - Cadres specifiques par evenement (aucun/disponible/defaut/impose) - Fix cadre paysage deformé : rotation auto si orientation ne correspond pas - Photos non imprimees vont dans sous-dossier "non_imprimees" (USB/FTP) au lieu d'etre ignorees ; booth recoit toujours toutes les photos Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1145 lines
38 KiB
JavaScript
1145 lines
38 KiB
JavaScript
/* Module administration - Backoffice complet */
|
|
|
|
// Mapping toggle ID -> cle config fonctionnalites
|
|
const TOGGLES_MAP = {
|
|
'tog-photo-simple': 'photo_simple',
|
|
'tog-multi-shot': 'multi_shot',
|
|
'tog-filtres': 'filtres',
|
|
'tog-overlays': 'overlays',
|
|
'tog-chroma-key': 'chroma_key',
|
|
'tog-impression': 'impression',
|
|
'tog-email': 'email',
|
|
'tog-qr-code': 'qr_code',
|
|
'tog-galerie': 'galerie',
|
|
};
|
|
|
|
async function chargerAdmin() {
|
|
config = await apiGet('/api/config');
|
|
chargerMateriel();
|
|
chargerCompteur();
|
|
chargerDestinations();
|
|
chargerCadres();
|
|
chargerCadresImpression();
|
|
chargerAnimations();
|
|
chargerFonctionnalites();
|
|
chargerEmailAdmin();
|
|
chargerFondsAdmin();
|
|
chargerGalerieAdmin();
|
|
chargerBoothAdmin();
|
|
chargerEvenementAdmin();
|
|
}
|
|
|
|
// === MATERIEL ===
|
|
|
|
async function chargerMateriel() {
|
|
const cam = config.camera || {};
|
|
const imp = config.impression || {};
|
|
|
|
// Camera
|
|
const statut = await apiGet('/api/camera/statut');
|
|
const badge = document.getElementById('admin-camera-statut');
|
|
badge.textContent = statut.connectee ? 'Connectee' : 'Deconnectee';
|
|
badge.style.background = statut.connectee ? 'rgba(76,175,80,0.2)' : 'rgba(244,67,54,0.2)';
|
|
badge.style.color = statut.connectee ? 'var(--succes)' : 'var(--danger)';
|
|
|
|
// Liste appareils
|
|
const selectApp = document.getElementById('admin-appareil');
|
|
selectApp.innerHTML = '';
|
|
for (const a of statut.appareils) {
|
|
const opt = document.createElement('option');
|
|
opt.value = a.source;
|
|
opt.textContent = `${a.nom} [${a.type}]`;
|
|
if (a.source === statut.mode || a.source === cam.appareil) opt.selected = true;
|
|
selectApp.appendChild(opt);
|
|
}
|
|
if (cam.appareil) selectApp.value = cam.appareil;
|
|
|
|
setValue('admin-car', cam.compte_a_rebours || 3);
|
|
document.getElementById('tog-flash-integre').checked = cam.flash_integre !== false;
|
|
|
|
// Imprimantes
|
|
await rafraichirImprimantes();
|
|
rafraichirStatutImprimante();
|
|
if (imp.imprimante) document.getElementById('admin-imprimante').value = imp.imprimante;
|
|
if (imp.format) document.getElementById('admin-format-papier').value = imp.format;
|
|
const fmt = (imp.format || '15x20').replace('-2up', '');
|
|
const orient = (imp.orientations || {})[fmt] || 'portrait';
|
|
document.querySelector(`input[name="admin-orientation"][value="${orient}"]`).checked = true;
|
|
setValue('admin-copies-max', imp.copies_max || 5);
|
|
}
|
|
|
|
async function rafraichirImprimantes() {
|
|
const imprimantes = await apiGet('/api/imprimantes');
|
|
const select = document.getElementById('admin-imprimante');
|
|
select.innerHTML = '';
|
|
for (const p of imprimantes) {
|
|
const opt = document.createElement('option');
|
|
opt.value = p.nom;
|
|
opt.textContent = `${p.nom} (${p.statut})`;
|
|
select.appendChild(opt);
|
|
}
|
|
}
|
|
|
|
async function reconnecterCamera() {
|
|
const source = getValue('admin-appareil') || null;
|
|
const resultat = await apiPost('/api/camera/reconnecter', { source });
|
|
chargerMateriel();
|
|
}
|
|
|
|
function _getOrientations() {
|
|
const fmt = (getValue('admin-format-papier') || '15x20').replace('-2up', '');
|
|
const orient = document.querySelector('input[name="admin-orientation"]:checked')?.value || 'portrait';
|
|
// On conserve les orientations des autres formats depuis la config courante
|
|
const existing = (config.impression || {}).orientations || {};
|
|
return { ...existing, [fmt]: orient };
|
|
}
|
|
|
|
async function couperPapier() {
|
|
afficherStatut('Envoi commande coupe...', 'succes');
|
|
const r = await apiPost('/api/imprimante/couper', {});
|
|
afficherStatut(r.message, r.succes ? 'succes' : 'erreur');
|
|
}
|
|
|
|
async function resetUsb() {
|
|
afficherStatut('Réinitialisation USB...', 'succes');
|
|
const r = await apiPost('/api/imprimante/reset-usb', {});
|
|
afficherStatut(r.message, r.succes ? 'succes' : 'erreur');
|
|
}
|
|
|
|
async function evacuerBourrage() {
|
|
const r = await apiPost('/api/imprimante/evacuer', {});
|
|
afficherStatut(r.message || 'Imprimante réactivée', r.succes ? 'succes' : 'erreur');
|
|
const el = document.getElementById('imprimante-erreur-msg');
|
|
const bloc = document.getElementById('imprimante-statut-detail');
|
|
if (r.message && r.message !== 'Imprimante réactivée') {
|
|
el.textContent = '⚠ ' + r.message;
|
|
bloc.style.display = 'block';
|
|
} else {
|
|
bloc.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
async function rafraichirStatutImprimante() {
|
|
const r = await apiGet('/api/imprimante/statut-detail').catch(() => null);
|
|
if (!r) return;
|
|
const el = document.getElementById('imprimante-erreur-msg');
|
|
const bloc = document.getElementById('imprimante-statut-detail');
|
|
if (r.derniere_erreur) {
|
|
let msg = r.derniere_erreur;
|
|
if (msg.includes('media does not match')) msg = '⚠ Format papier incorrect — vérifiez que le format configuré correspond à la cassette chargée dans l\'imprimante';
|
|
else if (msg.includes('jam')) msg = '⚠ Bourrage papier détecté';
|
|
else msg = '⚠ ' + msg.split(']').pop().trim();
|
|
el.textContent = msg;
|
|
bloc.style.display = 'block';
|
|
} else {
|
|
bloc.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
async function sauvegarderMateriel() {
|
|
const flashIntegre = document.getElementById('tog-flash-integre').checked;
|
|
await apiPost('/api/config', {
|
|
camera: {
|
|
appareil: getValue('admin-appareil'),
|
|
compte_a_rebours: parseInt(getValue('admin-car')) || 3,
|
|
flash_integre: flashIntegre,
|
|
},
|
|
impression: {
|
|
imprimante: getValue('admin-imprimante'),
|
|
format: getValue('admin-format-papier'),
|
|
copies_max: parseInt(getValue('admin-copies-max')) || 5,
|
|
orientations: _getOrientations(),
|
|
},
|
|
});
|
|
afficherStatut('Materiel sauvegarde', 'succes');
|
|
}
|
|
|
|
// === COMPTEUR ===
|
|
|
|
async function chargerCompteur() {
|
|
const etat = await apiGet('/api/compteur');
|
|
document.getElementById('tog-compteur-actif').checked = etat.actif;
|
|
document.getElementById('admin-compteur-restant').textContent = etat.restantes;
|
|
document.getElementById('admin-compteur-limite-display').textContent = etat.limite;
|
|
setValue('admin-compteur-limite', etat.limite);
|
|
}
|
|
|
|
async function sauvegarderCompteur() {
|
|
await apiPost('/api/config', {
|
|
compteur: {
|
|
actif: document.getElementById('tog-compteur-actif').checked,
|
|
limite: parseInt(getValue('admin-compteur-limite')) || 400,
|
|
},
|
|
});
|
|
chargerCompteur();
|
|
afficherStatut('Compteur sauvegarde', 'succes');
|
|
}
|
|
|
|
async function resetCompteur() {
|
|
if (confirm('Remettre le compteur a zero ?')) {
|
|
await apiPost('/api/compteur/reset');
|
|
chargerCompteur();
|
|
afficherStatut('Compteur remis a zero', 'succes');
|
|
}
|
|
}
|
|
|
|
// === DESTINATIONS ===
|
|
|
|
function chargerDestinations() {
|
|
const dest = config.destinations || {};
|
|
const booth = config.booth || {};
|
|
|
|
document.getElementById('tog-dest-memoire').checked = dest.memoire_interne !== false;
|
|
document.getElementById('tog-dest-usb').checked = dest.cle_usb || false;
|
|
document.getElementById('tog-dest-ftp').checked = dest.ftp || false;
|
|
document.getElementById('tog-dest-galerie').checked = booth.actif || false;
|
|
document.getElementById('tog-dest-email-auto').checked = dest.email_auto || false;
|
|
|
|
// Sous-configs visibles si actives
|
|
toggleSousConfig('tog-dest-usb', 'dest-usb-details');
|
|
toggleSousConfig('tog-dest-ftp', 'dest-ftp-details');
|
|
toggleSousConfig('tog-dest-galerie', 'dest-galerie-details');
|
|
|
|
setValue('admin-ftp-host', dest.ftp_host);
|
|
setValue('admin-ftp-port', dest.ftp_port || 21);
|
|
setValue('admin-ftp-user', dest.ftp_user);
|
|
setValue('admin-ftp-pass', dest.ftp_password);
|
|
setValue('admin-ftp-chemin', dest.ftp_chemin || '/photobooth');
|
|
|
|
// Booth fields
|
|
setValue('admin-booth-url', booth.url || 'https://booth.copydev.fr');
|
|
setValue('admin-booth-apikey', booth.api_key || 'booth-photobooth-2026');
|
|
setValue('admin-booth-eventid', booth.event_id || 'default');
|
|
|
|
// Radio sauvegarder tout / imprimees
|
|
if (dest.sauvegarder_tout === false) {
|
|
document.getElementById('sauv-imprimees').checked = true;
|
|
} else {
|
|
document.getElementById('sauv-tout').checked = true;
|
|
}
|
|
|
|
// Listeners pour afficher/cacher sous-configs
|
|
['tog-dest-usb', 'tog-dest-ftp', 'tog-dest-galerie'].forEach(id => {
|
|
const el = document.getElementById(id);
|
|
el.onchange = () => {
|
|
const map = { 'tog-dest-usb': 'dest-usb-details', 'tog-dest-ftp': 'dest-ftp-details', 'tog-dest-galerie': 'dest-galerie-details' };
|
|
toggleSousConfig(id, map[id]);
|
|
};
|
|
});
|
|
}
|
|
|
|
function toggleSousConfig(toggleId, detailsId) {
|
|
const checked = document.getElementById(toggleId).checked;
|
|
const el = document.getElementById(detailsId);
|
|
if (checked) el.classList.remove('cache');
|
|
else el.classList.add('cache');
|
|
}
|
|
|
|
async function detecterUSB() {
|
|
const usbs = await apiGet('/api/usb/detecter');
|
|
const select = document.getElementById('admin-chemin-usb');
|
|
select.innerHTML = '';
|
|
if (usbs.length === 0) {
|
|
const opt = document.createElement('option');
|
|
opt.value = '';
|
|
opt.textContent = 'Aucune cle detectee';
|
|
select.appendChild(opt);
|
|
} else {
|
|
for (const u of usbs) {
|
|
const opt = document.createElement('option');
|
|
opt.value = u;
|
|
opt.textContent = u;
|
|
select.appendChild(opt);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function sauvegarderDestinations() {
|
|
await apiPost('/api/config', {
|
|
destinations: {
|
|
memoire_interne: document.getElementById('tog-dest-memoire').checked,
|
|
cle_usb: document.getElementById('tog-dest-usb').checked,
|
|
chemin_usb: getValue('admin-chemin-usb') || '/media/usb',
|
|
ftp: document.getElementById('tog-dest-ftp').checked,
|
|
ftp_host: getValue('admin-ftp-host'),
|
|
ftp_port: parseInt(getValue('admin-ftp-port')) || 21,
|
|
ftp_user: getValue('admin-ftp-user'),
|
|
ftp_password: getValue('admin-ftp-pass'),
|
|
ftp_chemin: getValue('admin-ftp-chemin') || '/photobooth',
|
|
email_auto: document.getElementById('tog-dest-email-auto').checked,
|
|
sauvegarder_tout: document.getElementById('sauv-tout').checked,
|
|
},
|
|
booth: {
|
|
actif: document.getElementById('tog-dest-galerie').checked,
|
|
url: getValue('admin-booth-url'),
|
|
api_key: getValue('admin-booth-apikey'),
|
|
event_id: getValue('admin-booth-eventid'),
|
|
},
|
|
});
|
|
afficherStatut('Destinations sauvegardees', 'succes');
|
|
}
|
|
|
|
// === CADRES IMPRESSION (par format) ===
|
|
|
|
const FORMATS_IMPRESSION = ['strip', '10x15', '15x20'];
|
|
|
|
async function chargerCadresImpression() {
|
|
for (const fmt of FORMATS_IMPRESSION) {
|
|
await chargerCadresFormat(fmt);
|
|
}
|
|
}
|
|
|
|
async function chargerCadresFormat(fmt) {
|
|
const data = await apiGet(`/api/cadres-impression/${fmt}`);
|
|
const liste = document.getElementById(`cadres-liste-${fmt}`);
|
|
liste.innerHTML = '';
|
|
|
|
// Option "Aucun cadre"
|
|
const divAucun = _creerItemCadreImpression(fmt, null, data.actif);
|
|
liste.appendChild(divAucun);
|
|
|
|
if (data.disponibles.length === 0) {
|
|
const p = document.createElement('p');
|
|
p.className = 'texte-secondaire';
|
|
p.textContent = 'Aucun cadre importe';
|
|
liste.appendChild(p);
|
|
return;
|
|
}
|
|
|
|
for (const nom of data.disponibles) {
|
|
const div = _creerItemCadreImpression(fmt, nom, data.actif);
|
|
liste.appendChild(div);
|
|
}
|
|
}
|
|
|
|
function _creerItemCadreImpression(fmt, nom, actif) {
|
|
const div = document.createElement('div');
|
|
div.className = 'cadre-imp-item';
|
|
|
|
const rb = document.createElement('input');
|
|
rb.type = 'radio';
|
|
rb.name = `cadre-imp-${fmt}`;
|
|
rb.value = nom || '';
|
|
rb.checked = (actif === nom);
|
|
rb.onchange = () => activerCadreFormat(fmt, nom);
|
|
|
|
if (nom) {
|
|
const img = document.createElement('img');
|
|
img.src = `/assets/cadres/${fmt}/${nom}`;
|
|
img.alt = nom;
|
|
img.className = 'cadre-preview';
|
|
|
|
const btnDel = document.createElement('button');
|
|
btnDel.className = 'btn-danger btn-icone';
|
|
btnDel.title = 'Supprimer';
|
|
btnDel.textContent = '✕';
|
|
btnDel.onclick = () => supprimerCadreFormat(fmt, nom);
|
|
|
|
const span = document.createElement('span');
|
|
span.textContent = nom.replace('.png', '');
|
|
|
|
div.appendChild(rb);
|
|
div.appendChild(img);
|
|
div.appendChild(span);
|
|
div.appendChild(btnDel);
|
|
} else {
|
|
const span = document.createElement('span');
|
|
span.textContent = 'Aucun cadre';
|
|
div.appendChild(rb);
|
|
div.appendChild(span);
|
|
}
|
|
|
|
return div;
|
|
}
|
|
|
|
async function activerCadreFormat(fmt, nom) {
|
|
await apiPost(`/api/cadres-impression/${fmt}/actif`, { nom: nom || null });
|
|
afficherStatut(`Cadre ${fmt} mis a jour`, 'succes');
|
|
}
|
|
|
|
async function supprimerCadreFormat(fmt, nom) {
|
|
await fetch(`/api/cadres-impression/${fmt}/${encodeURIComponent(nom)}`, { method: 'DELETE' });
|
|
await chargerCadresFormat(fmt);
|
|
afficherStatut('Cadre supprime', 'succes');
|
|
}
|
|
|
|
async function uploaderCadreFormat(fmt) {
|
|
const input = document.getElementById(`input-cadre-${fmt}`);
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
|
|
const r = await fetch(`/api/upload/cadre/${fmt}`, { method: 'POST', body: formData });
|
|
if (r.ok) {
|
|
input.value = '';
|
|
await chargerCadresFormat(fmt);
|
|
afficherStatut(`Cadre ${fmt} importe`, 'succes');
|
|
} else {
|
|
afficherStatut('Erreur import cadre', 'erreur');
|
|
}
|
|
}
|
|
|
|
// === CADRES OVERLAYS PHOTO ===
|
|
|
|
async function chargerCadres() {
|
|
const data = await apiGet('/api/cadres');
|
|
const liste = document.getElementById('liste-cadres');
|
|
liste.innerHTML = '';
|
|
|
|
if (data.tous.length === 0) {
|
|
liste.innerHTML = '<p class="texte-secondaire">Aucun cadre importe</p>';
|
|
return;
|
|
}
|
|
|
|
for (const nom of data.tous) {
|
|
const div = document.createElement('div');
|
|
div.className = 'cadre-item';
|
|
|
|
const cb = document.createElement('input');
|
|
cb.type = 'checkbox';
|
|
cb.checked = data.actifs.includes(nom);
|
|
cb.onchange = () => majCadresActifs();
|
|
|
|
const img = document.createElement('img');
|
|
img.src = `/assets/overlays/${nom}`;
|
|
img.alt = nom;
|
|
|
|
const span = document.createElement('span');
|
|
span.textContent = nom.replace('.png', '');
|
|
|
|
div.appendChild(cb);
|
|
div.appendChild(img);
|
|
div.appendChild(span);
|
|
liste.appendChild(div);
|
|
}
|
|
}
|
|
|
|
async function majCadresActifs() {
|
|
const items = document.querySelectorAll('.cadre-item input[type="checkbox"]');
|
|
const noms = document.querySelectorAll('.cadre-item span');
|
|
const actifs = [];
|
|
items.forEach((cb, i) => {
|
|
if (cb.checked) actifs.push(noms[i].textContent + '.png');
|
|
});
|
|
await apiPost('/api/cadres', { actifs });
|
|
}
|
|
|
|
async function uploaderCadre() {
|
|
const input = document.getElementById('input-cadre');
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
|
|
await fetch('/api/upload/overlay', { method: 'POST', body: formData });
|
|
input.value = '';
|
|
chargerCadres();
|
|
afficherStatut('Cadre importe', 'succes');
|
|
}
|
|
|
|
// === FONDS VERTS ===
|
|
|
|
async function chargerFondsAdmin() {
|
|
const fonds = await apiGet('/api/fonds');
|
|
const liste = document.getElementById('liste-fonds');
|
|
liste.innerHTML = '';
|
|
|
|
if (fonds.length === 0) {
|
|
liste.innerHTML = '<p class="texte-secondaire">Aucun fond importe</p>';
|
|
return;
|
|
}
|
|
|
|
for (const nom of fonds) {
|
|
const div = document.createElement('div');
|
|
div.className = 'fond-item';
|
|
|
|
const img = document.createElement('img');
|
|
img.src = `/assets/backgrounds/${nom}`;
|
|
img.alt = nom;
|
|
|
|
const btnSuppr = document.createElement('button');
|
|
btnSuppr.className = 'btn-supprimer';
|
|
btnSuppr.innerHTML = '✕';
|
|
btnSuppr.onclick = () => supprimerFond(nom);
|
|
|
|
div.appendChild(img);
|
|
div.appendChild(btnSuppr);
|
|
liste.appendChild(div);
|
|
}
|
|
}
|
|
|
|
async function uploaderFond() {
|
|
const input = document.getElementById('input-fond');
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
await fetch('/api/upload/fond', { method: 'POST', body: formData });
|
|
input.value = '';
|
|
chargerFondsAdmin();
|
|
afficherStatut('Fond importe', 'succes');
|
|
}
|
|
|
|
async function supprimerFond(nom) {
|
|
if (!confirm(`Supprimer le fond "${nom}" ?`)) return;
|
|
await fetch(`/api/fonds/${encodeURIComponent(nom)}`, { method: 'DELETE' });
|
|
chargerFondsAdmin();
|
|
}
|
|
|
|
// === ANIMATIONS ===
|
|
|
|
const EMOJI_MAP = { 3: '🤪', 2: '😱', 1: '🔥' };
|
|
|
|
async function chargerAnimations() {
|
|
// Animations CSS internes
|
|
const anims = await apiGet('/api/animations');
|
|
const cssActives = config.camera?.animations_css_actives || ['classique'];
|
|
const liste = document.getElementById('liste-animations');
|
|
liste.innerHTML = '';
|
|
|
|
for (const [id, nom] of Object.entries(anims)) {
|
|
const div = document.createElement('div');
|
|
div.className = 'anim-item';
|
|
|
|
const cb = document.createElement('input');
|
|
cb.type = 'checkbox';
|
|
cb.checked = cssActives.includes(id);
|
|
cb.dataset.animId = id;
|
|
cb.onchange = () => majAnimationsCss();
|
|
|
|
const label = document.createElement('span');
|
|
label.textContent = nom;
|
|
|
|
div.appendChild(cb);
|
|
div.appendChild(label);
|
|
liste.appendChild(div);
|
|
}
|
|
|
|
// Animations custom (video/GIF)
|
|
await chargerAnimationsCustom();
|
|
}
|
|
|
|
async function chargerAnimationsCustom() {
|
|
const data = await apiGet('/api/animations/custom');
|
|
const liste = document.getElementById('liste-animations-custom');
|
|
liste.innerHTML = '';
|
|
|
|
if (data.tous.length === 0) {
|
|
liste.innerHTML = '<p class="texte-secondaire">Aucune animation importee</p>';
|
|
return;
|
|
}
|
|
|
|
for (const nom of data.tous) {
|
|
const div = document.createElement('div');
|
|
div.className = 'anim-item';
|
|
|
|
const cb = document.createElement('input');
|
|
cb.type = 'checkbox';
|
|
cb.checked = data.actives.includes(nom);
|
|
cb.dataset.animNom = nom;
|
|
cb.onchange = () => majAnimationsCustom();
|
|
|
|
// Preview miniature
|
|
const ext = nom.split('.').pop().toLowerCase();
|
|
let preview;
|
|
if (ext === 'gif') {
|
|
preview = document.createElement('img');
|
|
preview.src = `/assets/animations/${nom}`;
|
|
preview.className = 'anim-item-preview';
|
|
} else {
|
|
preview = document.createElement('video');
|
|
preview.src = `/assets/animations/${nom}`;
|
|
preview.className = 'anim-item-preview';
|
|
preview.muted = true;
|
|
preview.preload = 'metadata';
|
|
}
|
|
|
|
const label = document.createElement('span');
|
|
label.textContent = nom;
|
|
|
|
const btnSuppr = document.createElement('button');
|
|
btnSuppr.className = 'btn-supprimer';
|
|
btnSuppr.innerHTML = '✕';
|
|
btnSuppr.onclick = () => supprimerAnimation(nom);
|
|
|
|
div.appendChild(cb);
|
|
div.appendChild(preview);
|
|
div.appendChild(label);
|
|
div.appendChild(btnSuppr);
|
|
liste.appendChild(div);
|
|
}
|
|
}
|
|
|
|
async function majAnimationsCss() {
|
|
const checkboxes = document.querySelectorAll('#liste-animations .anim-item input[type="checkbox"]');
|
|
const actives = [];
|
|
checkboxes.forEach(cb => { if (cb.checked) actives.push(cb.dataset.animId); });
|
|
// Au moins une doit rester active
|
|
if (actives.length === 0) actives.push('classique');
|
|
await apiPost('/api/config', { camera: { animations_css_actives: actives } });
|
|
}
|
|
|
|
async function majAnimationsCustom() {
|
|
const checkboxes = document.querySelectorAll('#liste-animations-custom .anim-item input[type="checkbox"]');
|
|
const actives = [];
|
|
checkboxes.forEach(cb => { if (cb.checked) actives.push(cb.dataset.animNom); });
|
|
await apiPost('/api/animations/custom', { actives });
|
|
}
|
|
|
|
async function uploaderAnimation() {
|
|
const input = document.getElementById('input-animation');
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
|
|
await fetch('/api/upload/animation', { method: 'POST', body: formData });
|
|
input.value = '';
|
|
chargerAnimationsCustom();
|
|
afficherStatut('Animation importee', 'succes');
|
|
}
|
|
|
|
async function supprimerAnimation(nom) {
|
|
if (!confirm(`Supprimer l'animation "${nom}" ?`)) return;
|
|
await fetch(`/api/animations/custom/${encodeURIComponent(nom)}`, { method: 'DELETE' });
|
|
chargerAnimationsCustom();
|
|
}
|
|
|
|
function testerAnimationPreview() {
|
|
const chiffre = document.getElementById('preview-anim-chiffre');
|
|
const video = document.getElementById('preview-anim-video');
|
|
|
|
// Verifier s'il y a des animations custom actives
|
|
const customActives = [];
|
|
document.querySelectorAll('#liste-animations-custom .anim-item input[type="checkbox"]:checked').forEach(cb => {
|
|
customActives.push(cb.dataset.animNom);
|
|
});
|
|
|
|
// Verifier les CSS actives
|
|
const cssActives = [];
|
|
document.querySelectorAll('#liste-animations .anim-item input[type="checkbox"]:checked').forEach(cb => {
|
|
cssActives.push(cb.dataset.animId);
|
|
});
|
|
|
|
const toutes = [...cssActives.map(a => ({type: 'css', id: a})), ...customActives.map(a => ({type: 'custom', nom: a}))];
|
|
if (toutes.length === 0) return;
|
|
|
|
const choix = toutes[Math.floor(Math.random() * toutes.length)];
|
|
|
|
if (choix.type === 'custom') {
|
|
// Jouer la video/gif
|
|
chiffre.classList.add('cache');
|
|
video.classList.remove('cache');
|
|
video.src = `/assets/animations/${choix.nom}`;
|
|
video.currentTime = 0;
|
|
video.play();
|
|
video.onended = () => {
|
|
video.classList.add('cache');
|
|
chiffre.classList.remove('cache');
|
|
};
|
|
// Pour les GIF, masquer apres 3s
|
|
if (choix.nom.endsWith('.gif')) {
|
|
setTimeout(() => {
|
|
video.classList.add('cache');
|
|
chiffre.classList.remove('cache');
|
|
}, 3000);
|
|
}
|
|
} else {
|
|
// Animation CSS
|
|
video.classList.add('cache');
|
|
chiffre.classList.remove('cache');
|
|
let count = 3;
|
|
function afficher() {
|
|
if (choix.id === 'emoji') {
|
|
chiffre.textContent = EMOJI_MAP[count] || count;
|
|
} else {
|
|
chiffre.textContent = count;
|
|
}
|
|
chiffre.className = 'anim-chiffre';
|
|
void chiffre.offsetWidth;
|
|
chiffre.classList.add('anim-' + choix.id);
|
|
count--;
|
|
if (count > 0) setTimeout(afficher, 1000);
|
|
}
|
|
afficher();
|
|
}
|
|
}
|
|
|
|
// === FONCTIONNALITES ===
|
|
|
|
function chargerFonctionnalites() {
|
|
const fonc = config.fonctionnalites || {};
|
|
for (const [id, cle] of Object.entries(TOGGLES_MAP)) {
|
|
const el = document.getElementById(id);
|
|
if (el) el.checked = fonc[cle] !== false;
|
|
}
|
|
setupToggleListeners();
|
|
}
|
|
|
|
function setupToggleListeners() {
|
|
for (const [id, cle] of Object.entries(TOGGLES_MAP)) {
|
|
const el = document.getElementById(id);
|
|
if (!el) continue;
|
|
const nouveau = el.cloneNode(true);
|
|
el.parentNode.replaceChild(nouveau, el);
|
|
nouveau.addEventListener('change', () => {
|
|
apiPost('/api/config', { fonctionnalites: { [cle]: nouveau.checked } });
|
|
});
|
|
}
|
|
}
|
|
|
|
// === EVENEMENT ===
|
|
|
|
let eventEditId = null;
|
|
|
|
async function chargerEvenementAdmin() {
|
|
await chargerListeEvenements();
|
|
const event = config.evenement || {};
|
|
if (event.event_id) {
|
|
eventEditId = event.event_id;
|
|
afficherDetailEvent(event);
|
|
}
|
|
}
|
|
|
|
async function chargerListeEvenements() {
|
|
const events = await apiGet('/api/evenements');
|
|
const liste = document.getElementById('liste-evenements');
|
|
liste.innerHTML = '';
|
|
const actifId = (config.evenement || {}).event_id;
|
|
|
|
for (const ev of events) {
|
|
const div = document.createElement('div');
|
|
div.className = 'event-item' + (ev.id === actifId ? ' event-actif' : '');
|
|
div.innerHTML = `
|
|
<span class="event-nom">${ev.nom}</span>
|
|
<span class="event-actions">
|
|
${ev.id !== actifId ? `<button class="btn-secondaire btn-petit" onclick="activerEventAdmin('${ev.id}')">Activer</button>` : '<span class="badge-actif">Actif</span>'}
|
|
<button class="btn-secondaire btn-petit" onclick="editerEventAdmin('${ev.id}')">Editer</button>
|
|
<button class="btn-danger btn-petit" onclick="supprimerEventAdmin('${ev.id}','${ev.nom}')">Suppr</button>
|
|
</span>`;
|
|
liste.appendChild(div);
|
|
}
|
|
|
|
if (events.length === 0) {
|
|
liste.innerHTML = '<p class="texte-secondaire">Aucun evenement. Creez-en un ci-dessus.</p>';
|
|
}
|
|
}
|
|
|
|
async function creerEvenementAdmin() {
|
|
const nom = document.getElementById('input-nouveau-event').value.trim();
|
|
if (!nom) return;
|
|
const event = await apiPost('/api/evenements', { nom });
|
|
document.getElementById('input-nouveau-event').value = '';
|
|
await activerEventAdmin(event.id);
|
|
}
|
|
|
|
async function activerEventAdmin(id) {
|
|
await apiPost(`/api/evenements/${id}/activer`);
|
|
config = await apiGet('/api/config');
|
|
eventEditId = id;
|
|
await chargerListeEvenements();
|
|
afficherDetailEvent(config.evenement);
|
|
appliquerConfig();
|
|
afficherStatut('Evenement active', 'succes');
|
|
}
|
|
|
|
async function editerEventAdmin(id) {
|
|
const event = await apiGet(`/api/evenements/${id}`);
|
|
if (!event) return;
|
|
eventEditId = id;
|
|
afficherDetailEvent(event);
|
|
}
|
|
|
|
function afficherDetailEvent(event) {
|
|
document.getElementById('titre-event-detail').classList.remove('cache');
|
|
document.getElementById('detail-evenement').classList.remove('cache');
|
|
document.getElementById('admin-theme').value = event.theme || 'base';
|
|
setValue('admin-nom-event', event.nom);
|
|
setValue('admin-couleur-primaire', event.couleur_primaire || '#e91e63');
|
|
setValue('admin-couleur-secondaire', event.couleur_secondaire || '#ffffff');
|
|
rafraichirMediaAccueil().then(() => {
|
|
const select = document.getElementById('admin-media-accueil');
|
|
if (event.media_accueil) select.value = event.media_accueil;
|
|
});
|
|
chargerCadresEvent();
|
|
}
|
|
|
|
async function supprimerEventAdmin(id, nom) {
|
|
if (!confirm(`Supprimer l'evenement "${nom}" et ses cadres ?`)) return;
|
|
await fetch(`/api/evenements/${id}`, { method: 'DELETE' });
|
|
if (eventEditId === id) {
|
|
eventEditId = null;
|
|
document.getElementById('titre-event-detail').classList.add('cache');
|
|
document.getElementById('detail-evenement').classList.add('cache');
|
|
}
|
|
config = await apiGet('/api/config');
|
|
await chargerListeEvenements();
|
|
afficherStatut('Evenement supprime', 'succes');
|
|
}
|
|
|
|
function changerTheme(theme) {
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
}
|
|
|
|
async function rafraichirMediaAccueil() {
|
|
const data = await apiGet('/api/animations/custom');
|
|
const select = document.getElementById('admin-media-accueil');
|
|
const valeur = select.value;
|
|
select.innerHTML = '<option value="">Aucun (animation par defaut)</option>';
|
|
for (const nom of data.tous) {
|
|
const opt = document.createElement('option');
|
|
opt.value = nom;
|
|
opt.textContent = nom;
|
|
select.appendChild(opt);
|
|
}
|
|
if (valeur) select.value = valeur;
|
|
}
|
|
|
|
async function uploaderMediaAccueil() {
|
|
const input = document.getElementById('input-media-accueil');
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
await fetch('/api/upload/animation', { method: 'POST', body: formData });
|
|
input.value = '';
|
|
await rafraichirMediaAccueil();
|
|
afficherStatut('Media importe', 'succes');
|
|
}
|
|
|
|
async function sauvegarderEvenement() {
|
|
const donnees = {
|
|
theme: document.getElementById('admin-theme').value,
|
|
nom: getValue('admin-nom-event'),
|
|
couleur_primaire: getValue('admin-couleur-primaire'),
|
|
couleur_secondaire: getValue('admin-couleur-secondaire'),
|
|
media_accueil: getValue('admin-media-accueil') || null,
|
|
};
|
|
|
|
if (eventEditId) {
|
|
await apiPost('/api/config', { evenement: { ...donnees, event_id: eventEditId } });
|
|
await fetch(`/api/evenements/${eventEditId}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(donnees),
|
|
});
|
|
} else {
|
|
await apiPost('/api/config', { evenement: donnees });
|
|
}
|
|
appliquerConfig();
|
|
await chargerListeEvenements();
|
|
afficherStatut('Evenement sauvegarde', 'succes');
|
|
}
|
|
|
|
// === CADRES EVENEMENT ===
|
|
|
|
const FORMATS_EVENT = ['strip', '10x15', '15x20'];
|
|
|
|
async function chargerCadresEvent() {
|
|
if (!eventEditId) return;
|
|
const container = document.getElementById('cadres-event-formats');
|
|
container.innerHTML = '';
|
|
|
|
for (const fmt of FORMATS_EVENT) {
|
|
const data = await apiGet(`/api/evenements/${eventEditId}/cadres/${fmt}`);
|
|
const section = document.createElement('div');
|
|
section.className = 'cadre-event-section';
|
|
|
|
let cadresHtml = '';
|
|
for (const nom of data.disponibles) {
|
|
const selected = data.cadre === nom ? ' cadre-selectionne' : '';
|
|
cadresHtml += `<div class="cadre-event-item${selected}" onclick="selectionnerCadreEvent('${fmt}','${nom}')">
|
|
<img src="/data/evenements/${eventEditId}/cadres/${fmt}/${nom}" alt="${nom}">
|
|
<span>${nom.replace('.png','')}</span>
|
|
<button class="btn-danger btn-icone" onclick="event.stopPropagation();supprimerCadreEvent('${fmt}','${nom}')">✕</button>
|
|
</div>`;
|
|
}
|
|
|
|
section.innerHTML = `
|
|
<h4>Format ${fmt}</h4>
|
|
<div class="champ">
|
|
<label>Mode cadre</label>
|
|
<select id="mode-cadre-event-${fmt}" onchange="changerModeCadreEvent('${fmt}')">
|
|
<option value="aucun" ${data.mode === 'aucun' ? 'selected' : ''}>Aucun</option>
|
|
<option value="disponible" ${data.mode === 'disponible' ? 'selected' : ''}>Disponible (choix utilisateur)</option>
|
|
<option value="defaut" ${data.mode === 'defaut' ? 'selected' : ''}>Par defaut (pre-selectionne)</option>
|
|
<option value="impose" ${data.mode === 'impose' ? 'selected' : ''}>Impose (toujours applique)</option>
|
|
</select>
|
|
</div>
|
|
<div class="cadres-event-grille">${cadresHtml || '<p class="texte-secondaire">Aucun cadre</p>'}</div>
|
|
<input type="file" id="input-cadre-event-${fmt}" accept=".png" class="input-fichier">
|
|
<button class="btn-secondaire btn-petit" onclick="uploaderCadreEvent('${fmt}')">Importer cadre ${fmt}</button>
|
|
`;
|
|
container.appendChild(section);
|
|
}
|
|
}
|
|
|
|
async function selectionnerCadreEvent(fmt, nom) {
|
|
if (!eventEditId) return;
|
|
const mode = document.getElementById(`mode-cadre-event-${fmt}`).value;
|
|
await apiPost(`/api/evenements/${eventEditId}/cadres/${fmt}`, { mode, cadre: nom });
|
|
await chargerCadresEvent();
|
|
}
|
|
|
|
async function changerModeCadreEvent(fmt) {
|
|
if (!eventEditId) return;
|
|
const mode = document.getElementById(`mode-cadre-event-${fmt}`).value;
|
|
const event = await apiGet(`/api/evenements/${eventEditId}`);
|
|
const cadre = (event.cadres || {})[fmt]?.cadre || null;
|
|
await apiPost(`/api/evenements/${eventEditId}/cadres/${fmt}`, { mode, cadre });
|
|
afficherStatut(`Mode cadre ${fmt} : ${mode}`, 'succes');
|
|
}
|
|
|
|
async function uploaderCadreEvent(fmt) {
|
|
if (!eventEditId) return;
|
|
const input = document.getElementById(`input-cadre-event-${fmt}`);
|
|
if (!input.files.length) return;
|
|
|
|
const formData = new FormData();
|
|
formData.append('fichier', input.files[0]);
|
|
await fetch(`/api/evenements/${eventEditId}/upload-cadre/${fmt}`, { method: 'POST', body: formData });
|
|
input.value = '';
|
|
await chargerCadresEvent();
|
|
afficherStatut(`Cadre ${fmt} importe`, 'succes');
|
|
}
|
|
|
|
async function supprimerCadreEvent(fmt, nom) {
|
|
if (!eventEditId) return;
|
|
await fetch(`/api/evenements/${eventEditId}/cadres/${fmt}/${encodeURIComponent(nom)}`, { method: 'DELETE' });
|
|
await chargerCadresEvent();
|
|
afficherStatut('Cadre supprime', 'succes');
|
|
}
|
|
|
|
// === EMAIL ===
|
|
|
|
function chargerEmailAdmin() {
|
|
const email = config.email || {};
|
|
setValue('admin-smtp-host', email.smtp_host);
|
|
setValue('admin-smtp-port', email.smtp_port || 587);
|
|
setValue('admin-smtp-user', email.smtp_user);
|
|
setValue('admin-smtp-pass', email.smtp_password);
|
|
setValue('admin-expediteur', email.expediteur);
|
|
}
|
|
|
|
async function sauvegarderEmail() {
|
|
await apiPost('/api/config', {
|
|
email: {
|
|
smtp_host: getValue('admin-smtp-host'),
|
|
smtp_port: parseInt(getValue('admin-smtp-port')) || 587,
|
|
smtp_user: getValue('admin-smtp-user'),
|
|
smtp_password: getValue('admin-smtp-pass'),
|
|
expediteur: getValue('admin-expediteur'),
|
|
},
|
|
});
|
|
afficherStatut('Email sauvegarde', 'succes');
|
|
}
|
|
|
|
// === GALERIE ===
|
|
|
|
async function chargerGalerieAdmin() {
|
|
const compteur = await apiGet('/api/galerie/compteur');
|
|
document.getElementById('admin-nb-photos').textContent = compteur.photos_prises || 0;
|
|
document.getElementById('admin-nb-exports').textContent = compteur.exports || 0;
|
|
setValue('admin-url-galerie', (config.qr_code || {}).url_galerie);
|
|
}
|
|
|
|
async function sauvegarderGalerie() {
|
|
await apiPost('/api/config', {
|
|
qr_code: { url_galerie: getValue('admin-url-galerie') },
|
|
});
|
|
afficherStatut('Configuration sauvegardee', 'succes');
|
|
}
|
|
|
|
function confirmerViderGalerie() {
|
|
if (confirm('Supprimer TOUTES les photos ? Cette action est irreversible.')) {
|
|
apiPost('/api/galerie/vider').then(() => {
|
|
afficherStatut('Galerie videe', 'succes');
|
|
chargerGalerieAdmin();
|
|
});
|
|
}
|
|
}
|
|
|
|
// === SYSTEME ===
|
|
|
|
function confirmerRedemarrage() {
|
|
if (confirm('Redemarrer le systeme ?')) apiPost('/api/systeme/redemarrer');
|
|
}
|
|
|
|
function confirmerExtinction() {
|
|
if (confirm('Eteindre le systeme ?')) apiPost('/api/systeme/eteindre');
|
|
}
|
|
|
|
function quitterNavigateur() {
|
|
apiPost('/api/systeme/quitter-navigateur');
|
|
}
|
|
|
|
// === Utilitaires ===
|
|
|
|
function setValue(id, val) {
|
|
const el = document.getElementById(id);
|
|
if (el) el.value = val || '';
|
|
}
|
|
|
|
function getValue(id) {
|
|
const el = document.getElementById(id);
|
|
return el ? el.value : '';
|
|
}
|
|
|
|
// === BOOTH (Galerie Live) ===
|
|
|
|
async function chargerBoothAdmin() {
|
|
const booth = config.booth || {};
|
|
|
|
// Recuperer le code actuel
|
|
if (booth.actif && booth.url) {
|
|
try {
|
|
const info = await apiGet('/api/booth/info');
|
|
if (info.password) {
|
|
document.getElementById('admin-booth-password').textContent = info.password;
|
|
}
|
|
} catch (e) {
|
|
document.getElementById('admin-booth-password').textContent = '--';
|
|
}
|
|
}
|
|
}
|
|
|
|
async function sauvegarderBooth() {
|
|
await apiPost('/api/config', {
|
|
booth: {
|
|
actif: document.getElementById('tog-dest-galerie').checked,
|
|
url: getValue('admin-booth-url'),
|
|
api_key: getValue('admin-booth-apikey'),
|
|
event_id: getValue('admin-booth-eventid'),
|
|
}
|
|
});
|
|
afficherStatut('Galerie live sauvegardee', 'succes');
|
|
chargerBoothAdmin();
|
|
chargerBoothInfo();
|
|
}
|
|
|
|
// === INFOS SYSTEME ===
|
|
|
|
async function chargerInfosSysteme() {
|
|
const contenu = document.getElementById('infos-systeme-contenu');
|
|
contenu.innerHTML = '<p class="infos-chargement">Chargement...</p>';
|
|
try {
|
|
const info = await apiGet('/api/systeme/info');
|
|
let html = '';
|
|
|
|
// Reseau
|
|
html += '<div class="infos-section">';
|
|
html += '<h4>🌐 Reseau</h4>';
|
|
html += `<div class="infos-ligne"><span>Hostname</span><strong>${info.hostname}</strong></div>`;
|
|
if (info.interfaces && info.interfaces.length > 0) {
|
|
info.interfaces.forEach(iface => {
|
|
iface.ips.forEach(ip => {
|
|
html += `<div class="infos-ligne"><span>${iface.nom}</span><strong class="infos-ip">${ip}</strong></div>`;
|
|
});
|
|
});
|
|
} else {
|
|
html += '<div class="infos-ligne"><span>Aucune interface</span><strong>—</strong></div>';
|
|
}
|
|
html += '</div>';
|
|
|
|
// Systeme
|
|
html += '<div class="infos-section">';
|
|
html += '<h4>💻 Systeme</h4>';
|
|
if (info.uptime) html += `<div class="infos-ligne"><span>Uptime</span><strong>${info.uptime}</strong></div>`;
|
|
if (info.temperature_cpu !== null && info.temperature_cpu !== undefined) {
|
|
const couleur = info.temperature_cpu > 70 ? '#e53935' : info.temperature_cpu > 55 ? '#fb8c00' : '#43a047';
|
|
html += `<div class="infos-ligne"><span>Temperature CPU</span><strong style="color:${couleur}">${info.temperature_cpu} °C</strong></div>`;
|
|
}
|
|
if (info.python) html += `<div class="infos-ligne"><span>Python</span><strong>${info.python}</strong></div>`;
|
|
html += '</div>';
|
|
|
|
// Stockage
|
|
if (info.disque && info.disque.total) {
|
|
const pct = Math.round((info.disque.utilise / info.disque.total) * 100);
|
|
const couleurDisk = pct > 85 ? '#e53935' : pct > 65 ? '#fb8c00' : '#43a047';
|
|
html += '<div class="infos-section">';
|
|
html += '<h4>💾 Stockage</h4>';
|
|
html += `<div class="infos-ligne"><span>Total</span><strong>${info.disque.total} Go</strong></div>`;
|
|
html += `<div class="infos-ligne"><span>Utilise</span><strong style="color:${couleurDisk}">${info.disque.utilise} Go (${pct}%)</strong></div>`;
|
|
html += `<div class="infos-ligne"><span>Libre</span><strong>${info.disque.libre} Go</strong></div>`;
|
|
html += '<div class="infos-barre-fond"><div class="infos-barre" style="width:' + pct + '%;background:' + couleurDisk + '"></div></div>';
|
|
html += '</div>';
|
|
}
|
|
|
|
// RAM
|
|
if (info.ram && info.ram.total) {
|
|
const pctRam = Math.round((info.ram.utilise / info.ram.total) * 100);
|
|
const couleurRam = pctRam > 85 ? '#e53935' : pctRam > 65 ? '#fb8c00' : '#43a047';
|
|
html += '<div class="infos-section">';
|
|
html += '<h4>🫀 Memoire RAM</h4>';
|
|
html += `<div class="infos-ligne"><span>Total</span><strong>${info.ram.total} Go</strong></div>`;
|
|
html += `<div class="infos-ligne"><span>Utilise</span><strong style="color:${couleurRam}">${info.ram.utilise} Go (${pctRam}%)</strong></div>`;
|
|
html += `<div class="infos-ligne"><span>Libre</span><strong>${info.ram.libre} Go</strong></div>`;
|
|
html += '<div class="infos-barre-fond"><div class="infos-barre" style="width:' + pctRam + '%;background:' + couleurRam + '"></div></div>';
|
|
html += '</div>';
|
|
}
|
|
|
|
// Camera
|
|
html += '<div class="infos-section">';
|
|
html += '<h4>📷 Camera</h4>';
|
|
const camOk = info.camera_connectee;
|
|
html += `<div class="infos-ligne"><span>Mode</span><strong>${info.camera_mode}</strong></div>`;
|
|
html += `<div class="infos-ligne"><span>Statut</span><strong style="color:${camOk ? '#43a047' : '#e53935'}">${camOk ? 'Connectee' : 'Deconnectee'}</strong></div>`;
|
|
html += '</div>';
|
|
|
|
contenu.innerHTML = html;
|
|
} catch (e) {
|
|
contenu.innerHTML = '<p class="infos-erreur">Impossible de charger les informations systeme.</p>';
|
|
}
|
|
}
|
|
|
|
async function imprimerFlyerQR() {
|
|
afficherStatut('Generation du flyer QR...', 'succes');
|
|
const r = await apiPost('/api/booth/imprimer-qr', {});
|
|
if (r.succes) {
|
|
afficherStatut('Flyer QR envoye a l\'imprimante !', 'succes');
|
|
} else {
|
|
afficherStatut(r.erreur || 'Erreur impression flyer', 'erreur');
|
|
}
|
|
}
|
|
|
|
async function regenererBoothPassword() {
|
|
const booth = config.booth || {};
|
|
const url = getValue('admin-booth-url').replace(/\/$/, '');
|
|
const apiKey = getValue('admin-booth-apikey');
|
|
const eventId = getValue('admin-booth-eventid') || 'default';
|
|
|
|
try {
|
|
const resp = await fetch(`${url}/api/${eventId}/reset-password`, {
|
|
method: 'POST',
|
|
headers: { 'X-Api-Key': apiKey },
|
|
});
|
|
const data = await resp.json();
|
|
if (data.password) {
|
|
document.getElementById('admin-booth-password').textContent = data.password;
|
|
afficherStatut('Nouveau code genere : ' + data.password, 'succes');
|
|
}
|
|
} catch (e) {
|
|
afficherStatut('Erreur connexion au serveur booth', 'erreur');
|
|
}
|
|
}
|
|
|
|
async function chargerDiagCamera() {
|
|
const el = document.getElementById('diag-camera-contenu');
|
|
el.textContent = 'Test en cours...';
|
|
try {
|
|
const data = await apiGet('/api/camera/debug');
|
|
let txt = '';
|
|
for (const [k, v] of Object.entries(data)) {
|
|
if (k === 'config_widgets_top') {
|
|
txt += `${k}: ${Array.isArray(v) ? v.join(', ') : v}\n`;
|
|
} else {
|
|
txt += `${k}: ${JSON.stringify(v)}\n`;
|
|
}
|
|
}
|
|
el.textContent = txt || 'Aucune donnee';
|
|
} catch (e) {
|
|
el.textContent = 'ERREUR : ' + e.message;
|
|
}
|
|
}
|