From 6bbcf85e526cbba4015f197a5b8a496b92b0bb8d Mon Sep 17 00:00:00 2001 From: Jules Date: Tue, 23 Jun 2026 00:45:47 +0200 Subject: [PATCH] Formats activables par evenement + cadre impose exclut strips - Toggles formats (10x15, 15x20, strip) par evenement - Strip desactive masque le bouton pellicule sur l'accueil - Cadre impose/defaut s'applique uniquement sur 10x15 et 15x20 (pas strip) - Upload cadre evenement le copie dans les deux formats automatiquement - Section cadres admin simplifiee (un seul bloc au lieu de 3) Co-Authored-By: Claude Opus 4.6 --- backend/evenements.py | 5 ++ frontend/index.html | 18 +++++-- frontend/js/admin.js | 113 +++++++++++++++++++++++++----------------- frontend/js/app.js | 6 ++- frontend/js/camera.js | 4 ++ 5 files changed, 93 insertions(+), 53 deletions(-) diff --git a/backend/evenements.py b/backend/evenements.py index bc2eca6..af2f4c1 100644 --- a/backend/evenements.py +++ b/backend/evenements.py @@ -45,6 +45,7 @@ def creer_evenement(nom: str, **kwargs) -> dict: "couleur_primaire": kwargs.get("couleur_primaire", "#e91e63"), "couleur_secondaire": kwargs.get("couleur_secondaire", "#ffffff"), "media_accueil": kwargs.get("media_accueil"), + "formats_actifs": kwargs.get("formats_actifs", ["10x15", "15x20", "strip"]), "cadres": {}, } _chemin_event(event_id).write_text(json.dumps(event, ensure_ascii=False, indent=2), "utf-8") @@ -97,6 +98,7 @@ def activer_evenement(event_id: str) -> dict | None: "couleur_primaire": event.get("couleur_primaire", "#e91e63"), "couleur_secondaire": event.get("couleur_secondaire", "#ffffff"), "media_accueil": event.get("media_accueil"), + "formats_actifs": event.get("formats_actifs", ["10x15", "15x20", "strip"]), } }) log.info(f"Evenement active : {event['nom']} ({event_id})") @@ -135,7 +137,10 @@ def set_cadre_event(event_id: str, format_papier: str, mode: str, cadre: str | N def cadre_actif_pour_impression(format_papier: str) -> tuple[str | None, str]: """Retourne (nom_cadre, mode) en tenant compte de l'evenement actif. mode: 'aucun', 'disponible', 'impose', 'defaut' + Strips exclus du cadre impose/defaut. """ + if format_papier == "strip": + return None, "aucun" config = charger_config() event_id = config.get("evenement", {}).get("event_id") if not event_id: diff --git a/frontend/index.html b/frontend/index.html index 9a4894b..0d8d609 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -626,8 +626,16 @@ -

Cadres evenement

-

Cadres specifiques a cet evenement (n'apparaissent pas dans les autres).

+

Formats d'impression

+

Formats papier disponibles pour cet evenement.

+
+ + + +
+ +

Cadre evenement

+

Cadre specifique a cet evenement. Applique sur tous les formats sauf strips.

@@ -760,11 +768,11 @@ - - + + - + diff --git a/frontend/js/admin.js b/frontend/js/admin.js index 715ecb1..2acc3e9 100644 --- a/frontend/js/admin.js +++ b/frontend/js/admin.js @@ -762,6 +762,12 @@ function afficherDetailEvent(event) { const select = document.getElementById('admin-media-accueil'); if (event.media_accueil) select.value = event.media_accueil; }); + + const fmts = event.formats_actifs || ['10x15', '15x20', 'strip']; + document.getElementById('tog-event-fmt-10x15').checked = fmts.includes('10x15'); + document.getElementById('tog-event-fmt-15x20').checked = fmts.includes('15x20'); + document.getElementById('tog-event-fmt-strip').checked = fmts.includes('strip'); + chargerCadresEvent(); } @@ -809,12 +815,18 @@ async function uploaderMediaAccueil() { } async function sauvegarderEvenement() { + const formats_actifs = []; + if (document.getElementById('tog-event-fmt-10x15').checked) formats_actifs.push('10x15'); + if (document.getElementById('tog-event-fmt-15x20').checked) formats_actifs.push('15x20'); + if (document.getElementById('tog-event-fmt-strip').checked) formats_actifs.push('strip'); + 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, + formats_actifs, }; if (eventEditId) { @@ -834,79 +846,88 @@ async function sauvegarderEvenement() { // === CADRES EVENEMENT === -const FORMATS_EVENT = ['strip', '10x15', '15x20']; +const FORMATS_CADRE_EVENT = ['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'; + // Charger cadres du premier format pour afficher le mode + liste + const data = await apiGet(`/api/evenements/${eventEditId}/cadres/15x20`); + const data2 = await apiGet(`/api/evenements/${eventEditId}/cadres/10x15`); + const tous = [...new Set([...(data.disponibles || []), ...(data2.disponibles || [])])]; - let cadresHtml = ''; - for (const nom of data.disponibles) { - const selected = data.cadre === nom ? ' cadre-selectionne' : ''; - cadresHtml += `
- ${nom} - ${nom.replace('.png','')} - -
`; - } - - section.innerHTML = ` -

Format ${fmt}

-
- - -
-
${cadresHtml || '

Aucun cadre

'}
- - - `; - container.appendChild(section); + let cadresHtml = ''; + for (const nom of tous) { + const selected = data.cadre === nom ? ' cadre-selectionne' : ''; + const src15 = `/data/evenements/${eventEditId}/cadres/15x20/${nom}`; + const src10 = `/data/evenements/${eventEditId}/cadres/10x15/${nom}`; + cadresHtml += `
+ ${nom} + ${nom.replace('.png','')} + +
`; } + + container.innerHTML = ` +
+ + +
+
${cadresHtml || '

Aucun cadre importe

'}
+ + + `; } -async function selectionnerCadreEvent(fmt, nom) { +async function selectionnerCadreEvent(nom) { if (!eventEditId) return; - const mode = document.getElementById(`mode-cadre-event-${fmt}`).value; - await apiPost(`/api/evenements/${eventEditId}/cadres/${fmt}`, { mode, cadre: nom }); + const mode = document.getElementById('mode-cadre-event').value; + for (const fmt of FORMATS_CADRE_EVENT) { + await apiPost(`/api/evenements/${eventEditId}/cadres/${fmt}`, { mode, cadre: nom }); + } await chargerCadresEvent(); } -async function changerModeCadreEvent(fmt) { +async function changerModeCadreEvent() { 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'); + const mode = document.getElementById('mode-cadre-event').value; + const ev = await apiGet(`/api/evenements/${eventEditId}`); + const cadre = (ev.cadres || {})['15x20']?.cadre || (ev.cadres || {})['10x15']?.cadre || null; + for (const fmt of FORMATS_CADRE_EVENT) { + await apiPost(`/api/evenements/${eventEditId}/cadres/${fmt}`, { mode, cadre }); + } + afficherStatut(`Mode cadre : ${mode}`, 'succes'); } -async function uploaderCadreEvent(fmt) { +async function uploaderCadreEvent() { if (!eventEditId) return; - const input = document.getElementById(`input-cadre-event-${fmt}`); + const input = document.getElementById('input-cadre-event'); 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 }); + for (const fmt of FORMATS_CADRE_EVENT) { + const fd = new FormData(); + fd.append('fichier', input.files[0]); + await fetch(`/api/evenements/${eventEditId}/upload-cadre/${fmt}`, { method: 'POST', body: fd }); + } input.value = ''; await chargerCadresEvent(); - afficherStatut(`Cadre ${fmt} importe`, 'succes'); + afficherStatut('Cadre importe (10x15 + 15x20)', 'succes'); } -async function supprimerCadreEvent(fmt, nom) { +async function supprimerCadreEvent(nom) { if (!eventEditId) return; - await fetch(`/api/evenements/${eventEditId}/cadres/${fmt}/${encodeURIComponent(nom)}`, { method: 'DELETE' }); + for (const fmt of FORMATS_CADRE_EVENT) { + await fetch(`/api/evenements/${eventEditId}/cadres/${fmt}/${encodeURIComponent(nom)}`, { method: 'DELETE' }).catch(() => {}); + } await chargerCadresEvent(); afficherStatut('Cadre supprime', 'succes'); } diff --git a/frontend/js/app.js b/frontend/js/app.js index 9cf2474..1eb4cfc 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -48,8 +48,10 @@ function appliquerConfig() { const h1 = document.getElementById('nom-evenement'); if (h1) h1.textContent = event.nom || 'Photobooth'; - // Visibilite des modes - toggleVisible('btn-multi', fonc.multi_shot); + // 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; diff --git a/frontend/js/camera.js b/frontend/js/camera.js index 9c0fdd5..44798c6 100644 --- a/frontend/js/camera.js +++ b/frontend/js/camera.js @@ -437,6 +437,10 @@ async function majBoutonImprimerCompteur() { } async function chargerCadreImpose() { + if (modeActuel === 'multi') { + afficherCadreOverlay(null); + return; + } const fmt = (config.impression?.format || '15x20').replace('-2up', ''); const data = await apiGet(`/api/cadres-impression/${fmt}`).catch(() => null); if (!data) { afficherCadreOverlay(null); return; }