Implement filters and column management in admin UI lists
build / build-linux-amd64 (push) Successful in 1m52s
build / build-linux-amd64 (push) Successful in 1m52s
This commit is contained in:
+95
-6
@@ -75,11 +75,24 @@
|
||||
<div id="toast"
|
||||
class="fixed bottom-4 right-4 max-w-sm space-y-2 pointer-events-none"></div>
|
||||
|
||||
<!-- Load fragment + highlight active link based on the URL hash. -->
|
||||
<!-- Load fragment + highlight active link based on the URL hash.
|
||||
Sub-routes like #users/new map to dedicated fragment URLs but
|
||||
keep the parent section's nav-link highlighted. -->
|
||||
<script>
|
||||
// Hash → fragment URL for routes that aren't owned by a sidebar
|
||||
// nav-link (e.g. forms on their own page). The first path segment
|
||||
// also tells us which nav-link to highlight.
|
||||
const SUB_ROUTES = {
|
||||
'#users/new': '/admin/pages/users/new',
|
||||
};
|
||||
function topLevelHash(hash) {
|
||||
const slash = hash.indexOf('/');
|
||||
return slash === -1 ? hash : hash.slice(0, slash);
|
||||
}
|
||||
function linkForHash() {
|
||||
const hash = location.hash || '#users';
|
||||
return document.querySelector('.nav-link[hx-push-url="' + hash + '"]')
|
||||
const top = topLevelHash(hash);
|
||||
return document.querySelector('.nav-link[hx-push-url="' + top + '"]')
|
||||
|| document.querySelector('.nav-link[hx-push-url="#users"]');
|
||||
}
|
||||
function refreshActive() {
|
||||
@@ -89,10 +102,16 @@
|
||||
});
|
||||
}
|
||||
function loadFromHash() {
|
||||
const link = linkForHash();
|
||||
if (link) {
|
||||
htmx.ajax('GET', link.getAttribute('hx-get'),
|
||||
{ target: '#main', swap: 'innerHTML' });
|
||||
const hash = location.hash || '#users';
|
||||
const subUrl = SUB_ROUTES[hash];
|
||||
if (subUrl) {
|
||||
htmx.ajax('GET', subUrl, { target: '#main', swap: 'innerHTML' });
|
||||
} else {
|
||||
const link = linkForHash();
|
||||
if (link) {
|
||||
htmx.ajax('GET', link.getAttribute('hx-get'),
|
||||
{ target: '#main', swap: 'innerHTML' });
|
||||
}
|
||||
}
|
||||
refreshActive();
|
||||
}
|
||||
@@ -116,6 +135,76 @@
|
||||
if (!d.contains(e.target)) d.removeAttribute('open');
|
||||
});
|
||||
});
|
||||
|
||||
// Read the current value of the users-search input (if present).
|
||||
// Used by usersColumnToggle/usersPageSize so a column or page-size
|
||||
// change preserves the active filter.
|
||||
function usersSearchValue() {
|
||||
const el = document.getElementById('users-search');
|
||||
return el ? el.value : '';
|
||||
}
|
||||
|
||||
// Users table column-visibility toggle. The popover in the page header
|
||||
// emits checkboxes with onchange="usersColumnToggle(this)" — we POST
|
||||
// the new state to the server (which persists it in user_prefs) and
|
||||
// swap in the re-rendered table so the popover stays open.
|
||||
function usersColumnToggle(input) {
|
||||
const col = input.dataset.col;
|
||||
if (!col) return;
|
||||
htmx.ajax('POST', '/admin/pages/users/columns', {
|
||||
target: '#users-region',
|
||||
swap: 'innerHTML',
|
||||
values: {
|
||||
col: col,
|
||||
visible: input.checked ? '1' : '0',
|
||||
q: usersSearchValue(),
|
||||
},
|
||||
});
|
||||
}
|
||||
window.usersColumnToggle = usersColumnToggle;
|
||||
|
||||
// Users table per-page selector. Driven by the <select> in the
|
||||
// pagination footer — POSTs to persist the choice and re-renders the
|
||||
// table at page 1 (size change shifts which rows are on which page).
|
||||
function usersPageSize(size) {
|
||||
htmx.ajax('POST', '/admin/pages/users/page-size', {
|
||||
target: '#users-region',
|
||||
swap: 'innerHTML',
|
||||
values: { size: size, q: usersSearchValue() },
|
||||
});
|
||||
}
|
||||
window.usersPageSize = usersPageSize;
|
||||
|
||||
// Devices table — mirrors the users helpers above. Same persistence
|
||||
// model (per-user prefs in `user_prefs`) and the same fragment-swap
|
||||
// approach so the columns popover and search input stay put while
|
||||
// pagination/columns/page-size all preserve the active filter.
|
||||
function devicesSearchValue() {
|
||||
const el = document.getElementById('devices-search');
|
||||
return el ? el.value : '';
|
||||
}
|
||||
function devicesColumnToggle(input) {
|
||||
const col = input.dataset.col;
|
||||
if (!col) return;
|
||||
htmx.ajax('POST', '/admin/pages/devices/columns', {
|
||||
target: '#devices-region',
|
||||
swap: 'innerHTML',
|
||||
values: {
|
||||
col: col,
|
||||
visible: input.checked ? '1' : '0',
|
||||
q: devicesSearchValue(),
|
||||
},
|
||||
});
|
||||
}
|
||||
window.devicesColumnToggle = devicesColumnToggle;
|
||||
function devicesPageSize(size) {
|
||||
htmx.ajax('POST', '/admin/pages/devices/page-size', {
|
||||
target: '#devices-region',
|
||||
swap: 'innerHTML',
|
||||
values: { size: size, q: devicesSearchValue() },
|
||||
});
|
||||
}
|
||||
window.devicesPageSize = devicesPageSize;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user