diff --git a/dashboard/index.html b/dashboard/index.html
index 3423238..5b1e81c 100644
--- a/dashboard/index.html
+++ b/dashboard/index.html
@@ -99,6 +99,43 @@
+
+
+
totalPages && totalPages > 0) {
+ currentPage = totalPages;
+ }
+ if (currentPage < 1) currentPage = 1;
+
+ if (totalItems === 0) {
lockGrid.replaceChildren(emptyState);
emptyState.textContent =
lockStore.size === 0
? 'Waiting for lock events from relays\u2026'
: 'No locks match the current filters.';
+ paginationControls.classList.add('hidden');
return;
}
- const cards = locks.map((lock) => createLockCard(lock, now));
+ // Slice for pagination
+ const startIdx = (currentPage - 1) * itemsPerPage;
+ const endIdx = Math.min(startIdx + itemsPerPage, totalItems);
+ const pageLocks = allLocks.slice(startIdx, endIdx);
+
+ const cards = pageLocks.map((lock) => createLockCard(lock, now));
lockGrid.replaceChildren(...cards);
+
+ // Update Pagination Controls
+ if (totalItems > itemsPerPage) {
+ paginationControls.classList.remove('hidden');
+
+ // Update text
+ pageStartEl.textContent = startIdx + 1;
+ pageEndEl.textContent = endIdx;
+ totalItemsEl.textContent = totalItems;
+
+ // Update buttons state
+ const isFirst = currentPage === 1;
+ const isLast = currentPage === totalPages;
+
+ prevBtn.disabled = isFirst;
+ nextBtn.disabled = isLast;
+ mobilePrevBtn.disabled = isFirst;
+ mobileNextBtn.disabled = isLast;
+ } else {
+ paginationControls.classList.add('hidden');
+ }
}
// -----------------------------------------------------------------------
@@ -729,8 +815,19 @@
// -----------------------------------------------------------------------
// Event handlers
// -----------------------------------------------------------------------
- if (cadenceFilter) cadenceFilter.addEventListener('change', renderLocks);
- if (statusFilter) statusFilter.addEventListener('change', renderLocks);
+ function setPage(p) {
+ currentPage = p;
+ renderLocks();
+ }
+
+ if (cadenceFilter) cadenceFilter.addEventListener('change', () => { currentPage = 1; renderLocks(); });
+ if (statusFilter) statusFilter.addEventListener('change', () => { currentPage = 1; renderLocks(); });
+
+ if (prevBtn) prevBtn.addEventListener('click', () => setPage(currentPage - 1));
+ if (nextBtn) nextBtn.addEventListener('click', () => setPage(currentPage + 1));
+ if (mobilePrevBtn) mobilePrevBtn.addEventListener('click', () => setPage(currentPage - 1));
+ if (mobileNextBtn) mobileNextBtn.addEventListener('click', () => setPage(currentPage + 1));
+
if (refreshBtn) refreshBtn.addEventListener('click', () => {
// Close existing connections and reconnect
for (const ws of sockets) {
diff --git a/src/dashboard.mjs b/src/dashboard.mjs
index 247c16b..3474029 100644
--- a/src/dashboard.mjs
+++ b/src/dashboard.mjs
@@ -68,6 +68,7 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT) {
switch (extname) {
case '.html': contentType = 'text/html'; break;
case '.js': contentType = 'text/javascript'; break;
+ case '.mjs': contentType = 'text/javascript'; break;
case '.css': contentType = 'text/css'; break;
case '.json': contentType = 'application/json'; break;
case '.png': contentType = 'image/png'; break;