/** * Übernimmt Sortierung, Kategorie-Filter und Pagination der Suchergebnisse * vollständig clientseitig * * Ablauf: * 1. Kompletten Ergebnis-Datensatz einmalig per fetch() nachladen * (php/controller/search-results-data.php, liest nur die Session-Daten, * keine erneute Datenbank-Suche). * 2. Sortieren + filtern (JS-Array). * 3. Ergebnisliste + Pagination-UI daraus neu rendern. * * Ersetzt sorter.js, filter.js und paginator.js, deren Klick-Handler sich * gegenseitig ins Gehege kamen. * Ohne JS bleibt die serverseitig gerenderte Seite (echte Links/Formulare) * voll funktionsfähig. */ (function () { const state = { allItems: [], sort: 'alphabet', category: 'all', itemsPerPage: 10, currentPage: 1, }; let listContainer, sortRadios, categorySelect, limitSelect; let prevBtn, nextBtn, numbersContainer, jsNav, resultCountEl; let loadingElement; function init() { listContainer = document.querySelector('.s-res-list'); if (!listContainer) return; sortRadios = document.querySelectorAll('.sort-radio'); categorySelect = document.getElementById('category-filter'); limitSelect = document.getElementById('s-res-per-page'); prevBtn = document.getElementById('prev-page-btn'); nextBtn = document.getElementById('next-page-btn'); numbersContainer = document.getElementById('dynamic-page-numbers'); jsNav = document.getElementById('js-page-navigation'); resultCountEl = document.getElementById('s-res-result-count'); loadingElement = document.getElementById('results-loading'); const checkedRadio = document.querySelector('.sort-radio:checked'); state.sort = checkedRadio ? checkedRadio.value : 'alphabet'; state.category = categorySelect ? categorySelect.value : 'all'; state.itemsPerPage = limitSelect ? (parseInt(limitSelect.value, 10) || 10) : 10; if (loadingElement) { loadingElement.hidden = false; } fetchFullDataset() .then(function (data) { state.allItems = data.results || []; attachEvents(); render(); if (jsNav) { jsNav.style.display = 'flex'; } }) .catch(function (err) { /* * Falls das Nachladen nicht funktioniert, bleibt die bereits * serverseitig ausgegebene Ergebnisliste sichtbar. */ console.error( 'Suchergebnisse konnten nicht nachgeladen werden:', err ); }) .finally(function () { if (loadingElement) { loadingElement.hidden = true; } }); } function fetchFullDataset() { return fetch('php/controller/search-results-data.php', { credentials: 'same-origin' }) .then(function (res) { if (!res.ok) throw new Error('HTTP ' + res.status); return res.json(); }); } function getFilteredSortedItems() { let items = state.allItems; if (state.category !== 'all') { const wanted = state.category.toLowerCase(); items = items.filter(function (item) { return (item.category || '').toLowerCase() === wanted; }); } items = items.slice().sort(function (a, b) { if (state.sort === 'likes') { return (b.likes || 0) - (a.likes || 0); } if (state.sort === 'newest' || state.sort === 'oldest') { return state.sort === 'newest' ? (b.id - a.id) : (a.id - b.id); } // alphabet (Standard) return (a.title || '').toLowerCase().localeCompare((b.title || '').toLowerCase()); }); return items; } function render() { const items = getFilteredSortedItems(); if (resultCountEl) resultCountEl.textContent = items.length; const totalPages = Math.max(1, Math.ceil(items.length / state.itemsPerPage)); if (state.currentPage > totalPages) state.currentPage = totalPages; if (state.currentPage < 1) state.currentPage = 1; const start = (state.currentPage - 1) * state.itemsPerPage; const pageItems = items.slice(start, start + state.itemsPerPage); listContainer.innerHTML = ''; if (pageItems.length === 0) { const empty = document.createElement('p'); empty.textContent = 'Keine Beiträge gefunden.'; listContainer.appendChild(empty); } else { pageItems.forEach(function (item) { listContainer.appendChild(buildCard(item)); }); } updatePaginatorUI(state.currentPage, totalPages); } function buildCard(item) { const card = document.createElement('div'); card.className = 's-res-item'; card.setAttribute('data-likes', item.likes); card.setAttribute('data-category', (item.category || '').toLowerCase()); const link = document.createElement('a'); link.className = 's-res-link'; link.href = 'index.php?pfad=showArticle&id=' + encodeURIComponent(item.id); link.textContent = item.title; const title = document.createElement('h2'); title.className = 's-res-item-title'; title.appendChild(link); const authorName = document.createElement('span'); authorName.className = 's-res-author-name'; authorName.textContent = item.author; const author = document.createElement('p'); author.className = 's-res-author'; author.append('Von: ', authorName); const likes = document.createElement('span'); likes.className = 's-res-likes'; likes.textContent = '❤️ ' + item.likes; const metaRow = document.createElement('div'); metaRow.className = 's-res-meta-row'; metaRow.append(author, likes); const content = document.createElement('div'); content.className = 's-res-content'; content.append(title, metaRow); const arrow = document.createElement('div'); arrow.className = 's-res-arrow'; arrow.textContent = '→'; card.append(content, arrow); return card; } function updatePaginatorUI(currentPage, totalPages) { if (!prevBtn || !nextBtn || !numbersContainer) return; prevBtn.disabled = currentPage <= 1; nextBtn.disabled = currentPage >= totalPages; numbersContainer.innerHTML = ''; for (let i = 1; i <= totalPages; i++) { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 's-res-page-btn' + (i === currentPage ? ' s-res-page-btn-active' : ''); btn.textContent = i; btn.addEventListener('click', function () { state.currentPage = i; render(); }); numbersContainer.appendChild(btn); } } function attachEvents() { sortRadios.forEach(function (radio) { radio.addEventListener('change', function () { state.sort = this.value; state.currentPage = 1; render(); }); }); if (categorySelect) { categorySelect.addEventListener('change', function () { state.category = this.value; state.currentPage = 1; render(); }); } if (limitSelect) { limitSelect.addEventListener('change', function () { state.itemsPerPage = parseInt(this.value, 10) || 10; state.currentPage = 1; render(); }); } if (prevBtn) { prevBtn.addEventListener('click', function () { if (state.currentPage > 1) { state.currentPage -= 1; render(); } }); } if (nextBtn) { nextBtn.addEventListener('click', function () { state.currentPage += 1; render(); }); } // Mit aktivem JS übernimmt render() Sortierung/Filter live // ein echtes Absenden des Formulars (No-JS-Fallback) ist dann nicht mehr nötig. const filterForm = document.getElementById('s-res-filter-form'); if (filterForm) { filterForm.addEventListener('submit', function (e) { e.preventDefault(); }); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();