diff --git a/public/css/style.css b/public/css/style.css index c5e473b..8ec8798 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -892,6 +892,58 @@ input:checked+.slider:before { box-shadow: 0 0 8px rgba(244, 63, 94, 0.4); } +/* Pagination Footer */ +.pagination-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 22px; + background: rgba(0, 0, 0, 0.15); + border-top: 1px solid var(--border-color); + font-size: 0.8rem; + color: var(--text-secondary); +} + +.page-size-selector { + display: flex; + align-items: center; + gap: 8px; +} + +.pagination-controls { + display: flex; + gap: 4px; +} + +.page-btn { + padding: 4px 10px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 4px; + color: var(--text-primary); + font-family: var(--font-mono); + cursor: pointer; + transition: all 0.2s; +} + +.page-btn:hover { + background: rgba(99, 102, 241, 0.1); + border-color: var(--accent-indigo); + color: var(--accent-indigo); +} + +.page-btn.active { + background: var(--accent-indigo); + color: white; + border-color: var(--accent-indigo); +} + +.page-btn[disabled] { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; +} + /* Server Detail Modal Enhancements */ .detail-container { display: flex; diff --git a/public/index.html b/public/index.html index aa4b43e..f032174 100644 --- a/public/index.html +++ b/public/index.html @@ -283,6 +283,21 @@ +
diff --git a/public/js/app.js b/public/js/app.js index a5c0c97..f8a9a29 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -71,7 +71,9 @@ detailMemTotal: document.getElementById('detailMemTotal'), detailUptime: document.getElementById('detailUptime'), detailContainer: document.getElementById('detailContainer'), - sourceFilter: document.getElementById('sourceFilter') + sourceFilter: document.getElementById('sourceFilter'), + pageSizeSelect: document.getElementById('pageSizeSelect'), + paginationControls: document.getElementById('paginationControls') }; // ---- State ---- @@ -81,6 +83,8 @@ let currentServerDetail = { instance: null, job: null, source: null, charts: {} }; let allServersData = []; let currentSourceFilter = 'all'; + let currentPage = 1; + let pageSize = 20; // ---- Initialize ---- function init() { @@ -163,6 +167,16 @@ if (dom.sourceFilter) { dom.sourceFilter.addEventListener('change', () => { currentSourceFilter = dom.sourceFilter.value; + currentPage = 1; // Reset page on filter change + renderFilteredServers(); + }); + } + + // Page size listener + if (dom.pageSizeSelect) { + dom.pageSizeSelect.addEventListener('change', () => { + pageSize = parseInt(dom.pageSizeSelect.value, 10); + currentPage = 1; // Reset page on size change renderFilteredServers(); }); } @@ -380,9 +394,57 @@ if (currentSourceFilter !== 'all') { filtered = allServersData.filter(s => s.source === currentSourceFilter); } - updateServerTable(filtered); + + // Sort before paginating + filtered.sort((a, b) => { + if (a.up !== b.up) return b.up ? 1 : -1; + return b.cpuPercent - a.cpuPercent; + }); + + const totalFiltered = filtered.length; + const totalPages = Math.ceil(totalFiltered / pageSize) || 1; + + if (currentPage > totalPages) currentPage = totalPages; + + const startIndex = (currentPage - 1) * pageSize; + const paginated = filtered.slice(startIndex, startIndex + pageSize); + + updateServerTable(paginated); + renderPagination(totalPages); } + function renderPagination(totalPages) { + if (!dom.paginationControls) return; + + if (totalPages <= 1) { + dom.paginationControls.innerHTML = ''; + return; + } + + let html = ''; + // Previous button + html += ``; + + // Page numbers + for (let i = 1; i <= totalPages; i++) { + if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) { + html += ``; + } else if (i === currentPage - 3 || i === currentPage + 3) { + html += `...`; + } + } + + // Next button + html += ``; + + dom.paginationControls.innerHTML = html; + } + + window.changePage = function(page) { + currentPage = page; + renderFilteredServers(); + }; + // ---- Server Table ---- function updateServerTable(servers) { if (!servers || servers.length === 0) { @@ -394,12 +456,6 @@ return; } - // Sort servers: online first, then by cpu usage - servers.sort((a, b) => { - if (a.up !== b.up) return b.up ? 1 : -1; - return b.cpuPercent - a.cpuPercent; - }); - dom.serverTableBody.innerHTML = servers.map(server => { const memPct = server.memTotal > 0 ? (server.memUsed / server.memTotal * 100) : 0; const diskPct = server.diskTotal > 0 ? (server.diskUsed / server.diskTotal * 100) : 0;