# Admin List Pages - Pagination & Search Implementation ## Overview All admin list pages (Vendors, Companies, Users) share a consistent pagination and search pattern using **server-side pagination** with Alpine.js. --- ## Files Using This Pattern | Page | HTML Template | JavaScript | |------|---------------|------------| | Vendors | `templates/admin/vendors.html` | `static/admin/js/vendors.js` | | Companies | `templates/admin/companies.html` | `static/admin/js/companies.js` | | Users | `templates/admin/users.html` | `static/admin/js/users.js` | --- ## State Structure ### Filters Object ```javascript filters: { search: '', // Search query string is_active: '', // 'true', 'false', or '' (all) is_verified: '' // 'true', 'false', or '' (all) - vendors/companies only role: '' // 'admin', 'vendor', or '' (all) - users only } ``` ### Pagination Object ```javascript pagination: { page: 1, // Current page number per_page: 10, // Items per page total: 0, // Total items from API pages: 0 // Total pages (calculated) } ``` --- ## Computed Properties All three pages implement these computed properties: ### `paginatedVendors` / `paginatedCompanies` / `users` Returns the items array (already paginated from server): ```javascript get paginatedVendors() { return this.vendors; } ``` ### `totalPages` ```javascript get totalPages() { return this.pagination.pages; } ``` ### `startIndex` ```javascript get startIndex() { if (this.pagination.total === 0) return 0; return (this.pagination.page - 1) * this.pagination.per_page + 1; } ``` ### `endIndex` ```javascript get endIndex() { const end = this.pagination.page * this.pagination.per_page; return end > this.pagination.total ? this.pagination.total : end; } ``` ### `pageNumbers` Generates smart page number array with ellipsis: ```javascript get pageNumbers() { const pages = []; const totalPages = this.totalPages; const current = this.pagination.page; if (totalPages <= 7) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { pages.push(1); if (current > 3) pages.push('...'); const start = Math.max(2, current - 1); const end = Math.min(totalPages - 1, current + 1); for (let i = start; i <= end; i++) { pages.push(i); } if (current < totalPages - 2) pages.push('...'); pages.push(totalPages); } return pages; } ``` --- ## Methods ### `debouncedSearch()` Triggers search after 300ms delay: ```javascript debouncedSearch() { if (this._searchTimeout) { clearTimeout(this._searchTimeout); } this._searchTimeout = setTimeout(() => { this.pagination.page = 1; this.loadVendors(); // or loadCompanies(), loadUsers() }, 300); } ``` ### Pagination Methods ```javascript previousPage() { if (this.pagination.page > 1) { this.pagination.page--; this.loadVendors(); } } nextPage() { if (this.pagination.page < this.totalPages) { this.pagination.page++; this.loadVendors(); } } goToPage(pageNum) { if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) { this.pagination.page = pageNum; this.loadVendors(); } } ``` --- ## API Integration ### Building Query Parameters ```javascript async loadVendors() { const params = new URLSearchParams(); params.append('skip', (this.pagination.page - 1) * this.pagination.per_page); params.append('limit', this.pagination.per_page); if (this.filters.search) { params.append('search', this.filters.search); } if (this.filters.is_active !== '') { params.append('is_active', this.filters.is_active); } if (this.filters.is_verified !== '') { params.append('is_verified', this.filters.is_verified); } const response = await apiClient.get(`/admin/vendors?${params}`); this.vendors = response.vendors; this.pagination.total = response.total; this.pagination.pages = Math.ceil(response.total / this.pagination.per_page); } ``` ### API Response Format ```json { "vendors": [...], "total": 45, "skip": 0, "limit": 10 } ``` --- ## HTML Template Structure ### Search & Filters Bar ```html
``` ### Pagination Footer ```html
Showing - of
``` --- ## Page Number Display Examples **Few pages (<=7):** ``` ← 1 2 3 4 5 6 7 → ``` **Many pages, current = 1:** ``` ← [1] 2 3 ... 10 → ``` **Many pages, current = 5:** ``` ← 1 ... 4 [5] 6 ... 10 → ``` **Many pages, current = 10:** ``` ← 1 ... 8 9 [10] → ``` --- ## Visual Layout ``` ┌──────────────────────────────────────────────────────────────────┐ │ Vendor Management [+ Create Vendor] │ ├──────────────────────────────────────────────────────────────────┤ │ [Total] [Verified] [Pending] [Inactive] ← Stats Cards │ ├──────────────────────────────────────────────────────────────────┤ │ [🔍 Search... ] [Status ▼] [Verified ▼] [Refresh] │ ├──────────────────────────────────────────────────────────────────┤ │ Vendor │ Subdomain │ Status │ Created │ Actions │ ├────────┼───────────┼──────────┼─────────┼────────────────────────┤ │ Acme │ acme │ Verified │ Jan 1 │ 👁 ✏️ 🗑 │ │ Beta │ beta │ Pending │ Jan 2 │ 👁 ✏️ 🗑 │ │ ... │ ... │ ... │ ... │ ... │ ├──────────────────────────────────────────────────────────────────┤ │ Showing 1-10 of 45 ← 1 2 [3] 4 ... 9 → │ └──────────────────────────────────────────────────────────────────┘ ``` --- ## Configuration ### Change Items Per Page In the JavaScript file: ```javascript pagination: { page: 1, per_page: 25, // Change from 10 to 25 total: 0, pages: 0 } ``` --- ## Features Summary - Server-side pagination with `skip`/`limit` API params - Debounced search (300ms delay) - Multiple filter dropdowns - Smart page number display with ellipsis - Refresh button - Contextual empty state messages - Dark mode support - Responsive design - Consistent across all admin list pages