diff --git a/hubzilla/addon/assoc_profile/view/js/assoc_profile.js b/hubzilla/addon/assoc_profile/view/js/assoc_profile.js index af5b099..5aef183 100644 --- a/hubzilla/addon/assoc_profile/view/js/assoc_profile.js +++ b/hubzilla/addon/assoc_profile/view/js/assoc_profile.js @@ -1,8 +1,156 @@ -/* assoc_profile — v0.1.0 */ +/* assoc_profile — v0.2.0 + Client-side search, filter, pagination, and bulk select for the manage index. + No dependencies beyond Bootstrap 5 (already loaded by Hubzilla). */ + (function () { 'use strict'; - function init() { - // TODO: progressive enhancement for profile edit form if needed + + // ---------------------------------------------------------------- + // MANAGE INDEX — search, filter, paginate, bulk select + // ---------------------------------------------------------------- + + var PAGE_SIZE = 25; + + function initManageIndex() { + var table = document.getElementById('assoc-manage-table'); + if (!table) return; + + var rows = Array.from(table.querySelectorAll('tbody tr[data-slug]')); + var searchInput = document.getElementById('assoc-search'); + var filterCounty = document.getElementById('assoc-filter-county'); + var filterType = document.getElementById('assoc-filter-type'); + var filterStatus = document.getElementById('assoc-filter-status'); + var pageInfo = document.getElementById('assoc-page-info'); + var prevBtn = document.getElementById('assoc-page-prev'); + var nextBtn = document.getElementById('assoc-page-next'); + var selectAll = document.getElementById('assoc-select-all'); + var bulkBar = document.getElementById('assoc-bulk-bar'); + var bulkCount = document.getElementById('assoc-bulk-count'); + var exportBtn = document.getElementById('assoc-export-selected'); + + var currentPage = 1; + var filtered = rows.slice(); + + function getRowText(row) { + return (row.dataset.slug + ' ' + + row.dataset.name + ' ' + + (row.dataset.county || '')).toLowerCase(); + } + + function applyFilters() { + var q = searchInput ? searchInput.value.toLowerCase().trim() : ''; + var county = filterCounty ? filterCounty.value : ''; + var type = filterType ? filterType.value : ''; + var status = filterStatus ? filterStatus.value : ''; + + filtered = rows.filter(function (row) { + if (q && getRowText(row).indexOf(q) === -1) return false; + if (county && row.dataset.county !== county) return false; + if (type && row.dataset.type !== type) return false; + if (status && row.dataset.status !== status) return false; + return true; + }); + + currentPage = 1; + render(); + } + + function render() { + var total = filtered.length; + var pages = Math.max(1, Math.ceil(total / PAGE_SIZE)); + currentPage = Math.min(currentPage, pages); + var start = (currentPage - 1) * PAGE_SIZE; + var end = Math.min(start + PAGE_SIZE, total); + + rows.forEach(function (row) { row.style.display = 'none'; }); + filtered.slice(start, end).forEach(function (row) { row.style.display = ''; }); + + if (pageInfo) { + pageInfo.textContent = total === 0 + ? 'No associations found' + : 'Showing ' + (start + 1) + '–' + end + ' of ' + total; + } + if (prevBtn) prevBtn.disabled = currentPage <= 1; + if (nextBtn) nextBtn.disabled = currentPage >= pages; + } + + function updateBulkBar() { + if (!bulkBar) return; + var checked = table.querySelectorAll('tbody input[name="assoc_select[]"]:checked'); + var n = checked.length; + bulkBar.style.display = n > 0 ? 'flex' : 'none'; + if (bulkCount) bulkCount.textContent = n + ' selected'; + } + + // Event wiring + if (searchInput) searchInput.addEventListener('input', applyFilters); + if (filterCounty) filterCounty.addEventListener('change', applyFilters); + if (filterType) filterType.addEventListener('change', applyFilters); + if (filterStatus) filterStatus.addEventListener('change', applyFilters); + + if (prevBtn) prevBtn.addEventListener('click', function () { + if (currentPage > 1) { currentPage--; render(); } + }); + if (nextBtn) nextBtn.addEventListener('click', function () { + var pages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE)); + if (currentPage < pages) { currentPage++; render(); } + }); + + if (selectAll) { + selectAll.addEventListener('change', function () { + var visible = table.querySelectorAll('tbody tr[data-slug]:not([style*="none"]) input[name="assoc_select[]"]'); + visible.forEach(function (cb) { cb.checked = selectAll.checked; }); + updateBulkBar(); + }); + } + + table.addEventListener('change', function (e) { + if (e.target.name === 'assoc_select[]') updateBulkBar(); + }); + + if (exportBtn) { + exportBtn.addEventListener('click', function () { + var checked = Array.from(table.querySelectorAll('tbody input[name="assoc_select[]"]:checked')); + var slugs = checked.map(function (cb) { return cb.value; }); + if (!slugs.length) return; + var form = document.getElementById('assoc-export-form'); + if (!form) return; + document.getElementById('assoc-export-slugs').value = slugs.join(','); + form.submit(); + }); + } + + render(); } - document.addEventListener('DOMContentLoaded', init); + + // ---------------------------------------------------------------- + // DIFF VIEW — confirm/skip per entry + // ---------------------------------------------------------------- + + function initDiffView() { + var container = document.getElementById('assoc-diff-container'); + if (!container) return; + + container.addEventListener('change', function (e) { + if (e.target.name === 'diff_action[]') { + var entry = e.target.closest('.assoc-diff-entry'); + if (!entry) return; + if (e.target.value === 'skip') { + entry.style.opacity = '0.45'; + } else { + entry.style.opacity = '1'; + } + } + }); + } + + // ---------------------------------------------------------------- + // INIT + // ---------------------------------------------------------------- + + document.addEventListener('DOMContentLoaded', function () { + initManageIndex(); + initDiffView(); + }); + })();