diff --git a/public/index.html b/public/index.html index 949cdc3..8de7d98 100644 --- a/public/index.html +++ b/public/index.html @@ -86,6 +86,16 @@ .avatar.e { background: #5c3d2e; } .avatar.m { background: #b85c3a; } + /* ── Filter bar ── */ + .filter-bar { background: var(--bg-alt); border-bottom: 1px solid var(--border); padding: 10px 28px; display: flex; align-items: center; gap: 12px; flex-shrink: 0; flex-wrap: wrap; } + .filter-bar select, .filter-bar input { padding: 7px 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 13px; font-family: 'Lora', serif; color: var(--text); background: var(--bg-input); outline: none; } + .filter-bar select:focus, .filter-bar input:focus { border-color: var(--accent); } + .filter-bar label { font-size: 12px; color: var(--text-hint); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; } + .filter-group { display: flex; align-items: center; gap: 6px; } + .btn-clear-filters { padding: 6px 14px; background: transparent; border: 1px solid var(--border); border-radius: 8px; font-size: 12px; font-family: 'Lora', serif; color: var(--text-muted); cursor: pointer; } + .btn-clear-filters:hover { background: var(--border); color: var(--text); } + .card.filtered-out { display: none; } + /* ── Frise container ── */ .frise-wrapper { flex: 1; display: flex; flex-direction: column; overflow: hidden; } @@ -206,6 +216,25 @@
M
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
Chargement...
@@ -290,6 +319,71 @@ function toggleDark() { } if (localStorage.getItem('tdb-dark') === '1') document.body.classList.add('dark'); +// ── Filters ── +function populateFilterOptions() { + // Collect unique personnages from data + const persoSet = new Set(); + records.forEach(r => { + if (r.Personnage) r.Personnage.split(',').forEach(p => persoSet.add(p.trim())); + }); + const persoSel = document.getElementById('filter-perso'); + const curPerso = persoSel.value; + persoSel.innerHTML = ''; + [...persoSet].sort().forEach(p => { + const opt = document.createElement('option'); + opt.value = p; opt.textContent = p; + persoSel.appendChild(opt); + }); + persoSel.value = curPerso; + + // Statut and Type from column options + ['Statut', 'Type'].forEach(name => { + const sel = document.getElementById(`filter-${name.toLowerCase()}`); + const cur = sel.value; + sel.innerHTML = ''; + if (columns[name]) { + columns[name].options.forEach(o => { + const opt = document.createElement('option'); + opt.value = o; opt.textContent = o; + sel.appendChild(opt); + }); + } + sel.value = cur; + }); +} + +function applyFilters() { + const perso = document.getElementById('filter-perso').value; + const statut = document.getElementById('filter-statut').value; + const type = document.getElementById('filter-type').value; + const search = document.getElementById('filter-search').value.toLowerCase().trim(); + + document.querySelectorAll('.card').forEach(card => { + const id = Number(card.dataset.id); + const r = records.find(rec => rec.Id === id); + if (!r) return; + + let visible = true; + if (perso && !(r.Personnage || '').split(',').map(s => s.trim()).includes(perso)) visible = false; + if (statut && r.Statut !== statut) visible = false; + if (type && r.Type !== type) visible = false; + if (search) { + const haystack = [r.Title, r['Résumé'], r.Personnage, r['Intérêt narratif'], r.Implications, r.Origine].filter(Boolean).join(' ').toLowerCase(); + if (!haystack.includes(search)) visible = false; + } + + card.classList.toggle('filtered-out', !visible); + }); +} + +function clearFilters() { + document.getElementById('filter-perso').value = ''; + document.getElementById('filter-statut').value = ''; + document.getElementById('filter-type').value = ''; + document.getElementById('filter-search').value = ''; + applyFilters(); +} + const statusClass = s => { if (!s) return ''; const map = { 'Germe': 'badge-germe', 'À étudier': 'badge-etudier', 'Posée': 'badge-posee', 'Abandonnée': 'badge-abandonnee', 'En conflit': 'badge-conflit' }; @@ -544,7 +638,9 @@ async function loadData() { records = recData.list || []; columns = await colRes.json(); populateSelects(); + populateFilterOptions(); renderFrise(); + applyFilters(); } function esc(s) {