Add pagination (Prev/Next with counter) and X-Total-Count header
This commit is contained in:
+48
-2
@@ -242,6 +242,8 @@
|
||||
.btn-outline { background: transparent; border: 1.5px solid var(--border); color: var(--text); }
|
||||
.btn-danger { background: transparent; border: 1.5px solid var(--red); color: var(--red); }
|
||||
.btn-green { background: var(--green); color: #000; }
|
||||
.btn-xs { padding: 6px 12px; font-size: 12px; width: auto; line-height: 1; }
|
||||
.pagination-bar { display: flex; align-items: center; gap: 6px; }
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
INPUTS
|
||||
@@ -1070,6 +1072,7 @@
|
||||
<button class="btn btn-outline btn-sm import-btn" onclick="showImportView()">📥 Import</button>
|
||||
</div>
|
||||
<div id="assetList"></div>
|
||||
<div id="pagination" class="pagination-bar" style="display:none;margin-top:8px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- ── Detail View ──────────────────────────────────────────────────── -->
|
||||
@@ -1297,6 +1300,8 @@
|
||||
// API WRAPPER
|
||||
// =========================================================================
|
||||
async function api(url, opts = {}) {
|
||||
const returnMeta = opts._returnMeta;
|
||||
delete opts._returnMeta;
|
||||
// Auto-attach auth token if available
|
||||
if (AppState.authToken) {
|
||||
opts.headers = opts.headers || {};
|
||||
@@ -1314,6 +1319,7 @@
|
||||
const detail = (data && data.detail) || text || `HTTP ${res.status}`;
|
||||
throw new Error(detail);
|
||||
}
|
||||
if (returnMeta) return { data, headers: res.headers };
|
||||
return data;
|
||||
} catch (e) {
|
||||
if (e.name === 'TypeError' && e.message === 'Failed to fetch') {
|
||||
@@ -3014,6 +3020,9 @@
|
||||
|
||||
// ── Filter state ──────────────────────────────────────────────────────
|
||||
let assetFilters = { category: null, status: null, make: null, disney_park: null, q: '' };
|
||||
let assetOffset = 0;
|
||||
const ASSET_PAGE_SIZE = 100;
|
||||
let assetTotal = 0;
|
||||
let assetDebounce = null;
|
||||
let cachedAssets = [];
|
||||
|
||||
@@ -3053,10 +3062,13 @@
|
||||
if (assetFilters.make) params.set('make', assetFilters.make);
|
||||
if (assetFilters.q) params.set('q', assetFilters.q);
|
||||
if (assetFilters.assigned_to) params.set('assigned_to', assetFilters.assigned_to);
|
||||
params.set('limit', '100');
|
||||
params.set('limit', String(ASSET_PAGE_SIZE));
|
||||
params.set('offset', String(assetOffset));
|
||||
|
||||
try {
|
||||
var assets = await api('/api/assets?' + params.toString());
|
||||
var result = await api('/api/assets?' + params.toString(), {_returnMeta: true});
|
||||
var assets = result.data;
|
||||
assetTotal = parseInt(result.headers.get('X-Total-Count')) || assets.length;
|
||||
// Client-side filter fallback if server doesn't support assigned_to
|
||||
var filtered = assetFilters.assigned_to
|
||||
? assets.filter(function(a) { return a.assigned_to === assetFilters.assigned_to; })
|
||||
@@ -3066,6 +3078,7 @@
|
||||
: filtered;
|
||||
renderAssetList(filtered);
|
||||
renderFilterPills(assets);
|
||||
renderPagination();
|
||||
} catch (e) {
|
||||
document.getElementById('assetList').innerHTML =
|
||||
'<div class="empty-state">Failed to load assets</div>';
|
||||
@@ -3138,14 +3151,47 @@
|
||||
assetFilters.status = status;
|
||||
assetFilters.make = make;
|
||||
assetFilters.disney_park = disney_park;
|
||||
assetOffset = 0;
|
||||
loadAssets();
|
||||
}
|
||||
|
||||
function clearAssetSearch() {
|
||||
document.getElementById('assetSearch').value = '';
|
||||
assetOffset = 0;
|
||||
loadAssets();
|
||||
}
|
||||
|
||||
// ── Pagination ──────────────────────────────────────────────────────────
|
||||
function renderPagination() {
|
||||
const el = document.getElementById('pagination');
|
||||
if (assetTotal <= ASSET_PAGE_SIZE) {
|
||||
el.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
el.style.display = 'flex';
|
||||
const current = assetOffset + 1;
|
||||
const end = Math.min(assetOffset + ASSET_PAGE_SIZE, assetTotal);
|
||||
el.innerHTML =
|
||||
'<span style="font-size:12px;color:var(--text2);margin-right:auto;">' +
|
||||
current + '–' + end + ' of ' + assetTotal + '</span>' +
|
||||
'<button class="btn btn-outline btn-xs" onclick="prevPage()" ' + (assetOffset === 0 ? 'disabled' : '') + '>‹ Prev</button>' +
|
||||
'<button class="btn btn-outline btn-xs" onclick="nextPage()" ' + (end >= assetTotal ? 'disabled' : '') + '>Next ›</button>';
|
||||
}
|
||||
|
||||
function prevPage() {
|
||||
if (assetOffset > 0) {
|
||||
assetOffset = Math.max(0, assetOffset - ASSET_PAGE_SIZE);
|
||||
_loadAssets();
|
||||
}
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
if (assetOffset + ASSET_PAGE_SIZE < assetTotal) {
|
||||
assetOffset += ASSET_PAGE_SIZE;
|
||||
_loadAssets();
|
||||
}
|
||||
}
|
||||
|
||||
// ── View switching ────────────────────────────────────────────────────
|
||||
function hideAllAssetViews() {
|
||||
['assetsListView','assetsDetailView','assetsEditView','assetsImportView'].forEach(id => {
|
||||
|
||||
Reference in New Issue
Block a user