From b21f56e3649d67275c215ff77f0c4cfaeff9a548 Mon Sep 17 00:00:00 2001 From: william Date: Thu, 27 Aug 2026 13:17:10 +0200 Subject: [PATCH] =?UTF-8?q?Filtre=20de=20colonnes=20+=20panneau=20de=20d?= =?UTF-8?q?=C3=A9tail=20d'une=20entr=C3=A9e=20(onglet=20Donn=C3=A9es)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un objet avec beaucoup de champs rendait le tableau "Données enregistrées" illisible (toutes les colonnes serrées sur une ligne, valeurs tronquées) — deux ajouts pour compenser : 1) "🔧 Colonnes" : menu déroulant (une case à cocher par champ, toutes cochées par défaut) au-dessus du tableau — décocher un champ masque sa colonne (th + td, via [data-col]) sans reconstruire le tableau. Un seul menu ouvert à la fois, fermé au clic ailleurs sur la page. 2) 👁️ par ligne : ouvre un panneau de détail (déplaçable/redimensionnable/ plein écran par défaut, comme les autres) listant TOUS les champs de cette entrée, un par ligne, valeur complète non tronquée — reprend le style .rowDetailField/.rowDetailLabel/.rowDetailValue de l'ancienne modale Bulma de data_list.html (retirée, mais ce CSS ne dépendait pas du template et a été gardé). Un seul panneau PAR OBJET (pas par ligne) : son contenu est reconstruit à l'ouverture en lisant en direct la ligne du tableau déjà affiché (via [data-row-id] sur chaque ) — reflète donc aussi une modification tout juste saisie mais pas encore "Enregistrer"ée, sans aller-retour serveur. Co-Authored-By: Claude Sonnet 5 --- static/style.css | 21 +++++- templates/game_dashboard.html | 123 ++++++++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 9 deletions(-) diff --git a/static/style.css b/static/style.css index aba495c1..e50cca49 100644 --- a/static/style.css +++ b/static/style.css @@ -78,8 +78,9 @@ button.danger{ color:var(--danger); border-color:rgba(226,104,95,.4); } .dataTable td{ max-width:220px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } /* ---------- Détail d'une entrée (bouton 👁️) ---------- */ -/* Contenu de la modale Bulma (.modal/.modal-card, voir data_list.html) — - affiche chaque champ en entier, sans troncature. */ +/* Contenu du panneau de détail (voir openRowDetailPanel() dans + game_dashboard.html) — affiche chaque champ en entier, sans troncature, + contrairement au tableau dense qui les tronque. */ .rowDetailField{ padding:10px 0; border-bottom:1px solid var(--border); } .rowDetailField:last-child{ border-bottom:none; } .rowDetailLabel{ font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.05em; margin-bottom:4px; } @@ -535,3 +536,19 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p .dashCreateBar{ display:flex; gap:8px; align-items:center; margin-bottom:12px; padding:10px; border:1px solid var(--border); border-radius:8px; background:var(--panel); } .dashCreateBar input, .dashCreateBar select{ width:auto; flex:1 1 160px; } .dashDangerZone{ margin-top:28px; } + +/* ---------- Filtre de colonnes (onglet Données, game_dashboard.html) ---------- */ +/* Menu déroulant compact sous le bouton "🔧 Colonnes" — un objet avec + beaucoup de champs rend le tableau illisible, ce menu permet d'en + masquer certains (voir toggleDataColumn() : agit directement sur + [data-col] plutôt que de reconstruire le tableau). */ +.columnFilterWrap{ position:relative; display:inline-block; } +.columnFilterMenu{ + position:absolute; top:100%; left:0; margin-top:4px; z-index:70; + background:var(--panel); border:1px solid var(--border); border-radius:8px; + padding:8px; box-shadow:0 12px 40px rgba(0,0,0,.55); + display:flex; flex-direction:column; gap:2px; min-width:180px; max-height:280px; overflow-y:auto; +} +.columnFilterMenu.hidden{ display:none; } +.columnFilterOption{ display:flex; align-items:center; gap:6px; padding:4px 6px; border-radius:4px; font-size:12.5px; white-space:nowrap; } +.columnFilterOption:hover{ background:var(--panel2); } diff --git a/templates/game_dashboard.html b/templates/game_dashboard.html index d625dac0..f53313da 100644 --- a/templates/game_dashboard.html +++ b/templates/game_dashboard.html @@ -376,9 +376,19 @@ {% endif %}

Données enregistrées

- {% if definition.fields %} - - {% endif %} +
+ {% if definition.fields %} +
+ + +
+ + {% endif %} +
{% if definition.fields %} {% set rows = rows_by_definition[d.id] %} @@ -389,17 +399,17 @@ onsubmit="return confirm('Supprimer définitivement l\'entrée #{{ r.id }} ?');"> {% endfor %}
- +
- {% for f in definition.fields %}{% endfor %} + {% for f in definition.fields %}{% endfor %} {% for r in rows %} - + {% for f in definition.fields %} {% set col = f.name|colname %} - {% endfor %} @@ -551,6 +562,25 @@
+ + + {% endif %} {% endfor %} @@ -814,6 +844,85 @@ function makeFloatPanelFullscreenable(panel, toggleBtn) { }; })(); +// ---------- Filtre de colonnes (onglet Données) ---------- +function toggleColumnFilterMenu(definitionId) { + var menu = document.getElementById('columnFilterMenu-' + definitionId); + if (!menu) return; + var opening = menu.classList.contains('hidden'); + // Un seul menu de filtre ouvert à la fois, tous objets confondus — évite + // d'en laisser traîner un ouvert par-dessus le panneau d'un autre objet. + document.querySelectorAll('.columnFilterMenu').forEach(function (m) { m.classList.add('hidden'); }); + if (opening) menu.classList.remove('hidden'); +} +document.addEventListener('click', function (e) { + if (e.target.closest('.columnFilterWrap')) return; + document.querySelectorAll('.columnFilterMenu').forEach(function (m) { m.classList.add('hidden'); }); +}); +function toggleDataColumn(definitionId, colKey, visible) { + var table = document.getElementById('dataTable-' + definitionId); + if (!table) return; + table.querySelectorAll('[data-col="' + colKey + '"]').forEach(function (cell) { + cell.style.display = visible ? '' : 'none'; + }); +} + +// ---------- Panneau de détail d'une entrée (un par objet) ---------- +(function () { + document.querySelectorAll('.rowDetailPanel').forEach(function (panel) { + makeFloatPanelDraggable(panel, panel.querySelector('.floatPanelHeader')); + makeFloatPanelResizable(panel, panel.querySelector('.floatPanelResizeHandle')); + makeFloatPanelFullscreenable(panel, panel.querySelector('.floatPanelFullscreenBtn')); + }); + + // Reconstruit le contenu du panneau à chaque ouverture, en lisant + // directement la ligne correspondante du tableau "Données enregistrées" + // déjà affiché — pas d'aller-retour serveur, et ça reflète une + // modification tout juste saisie mais pas encore "Enregistrer"ée. + window.openRowDetailPanel = function (definitionId, rowId) { + var panel = document.querySelector('.rowDetailPanel[data-definition-id="' + definitionId + '"]'); + var table = document.getElementById('dataTable-' + definitionId); + if (!panel || !table) return; + var row = table.querySelector('tr[data-row-id="' + rowId + '"]'); + if (!row) return; + + var body = panel.querySelector('.rowDetailPanelBody'); + body.innerHTML = ''; + var idField = document.createElement('div'); + idField.className = 'rowDetailField'; + idField.innerHTML = '
#
' + rowId + '
'; + body.appendChild(idField); + + row.querySelectorAll('td[data-field-label]').forEach(function (cell) { + var label = cell.dataset.fieldLabel; + var control = cell.querySelector('input, textarea, select'); + var value = '—'; + if (control) { + if (control.type === 'checkbox') value = control.checked ? 'Oui' : 'Non'; + else if (control.tagName === 'SELECT') value = control.options[control.selectedIndex] ? control.options[control.selectedIndex].text : '—'; + else value = control.value || '—'; + } + var field = document.createElement('div'); + field.className = 'rowDetailField'; + var labelEl = document.createElement('div'); + labelEl.className = 'rowDetailLabel'; + labelEl.textContent = label; + var valueEl = document.createElement('div'); + valueEl.className = 'rowDetailValue'; + valueEl.textContent = value; + field.appendChild(labelEl); + field.appendChild(valueEl); + body.appendChild(field); + }); + + panel.classList.remove('hidden'); + panel._fsToggle.enter(); + }; + window.closeRowDetailPanel = function (definitionId) { + var panel = document.querySelector('.rowDetailPanel[data-definition-id="' + definitionId + '"]'); + if (panel) panel.classList.add('hidden'); + }; +})(); + // Sous-onglets Champs/Données d'UN panneau précis (voir plus haut) — scopé // au panneau ancêtre du bouton cliqué, jamais global (plusieurs objets // peuvent chacun avoir leurs sous-onglets sur leur propre panneau, en même
#{{ f.name }}
#{{ f.name }}
{{ r.id }} + {% if f.type == 'texte' %} {% elif f.type == 'texte_long' %} @@ -423,6 +433,7 @@ +