docs: update pagination documentation for server-side pattern
- Rewrite pagination.md for server-side pagination approach - Update quick-start guide with new implementation pattern - Document shared pattern across Vendors, Companies, Users pages - Add examples for filters, search, and API integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,201 +1,178 @@
|
||||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ PAGINATION FEATURE - QUICK START ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
# Pagination & Search - Quick Start Guide
|
||||
|
||||
✨ WHAT'S NEW
|
||||
═════════════════════════════════════════════════════════════════
|
||||
## Overview
|
||||
|
||||
1. ✅ Edit Button - Pencil icon already present in table
|
||||
2. ✅ Pagination - Smart pagination with 10 items per page
|
||||
3. ✅ Page Navigation - Previous/Next buttons + page numbers
|
||||
4. ✅ Smart Display - Shows "..." for large page ranges
|
||||
All admin list pages use consistent **server-side pagination** with search and filters.
|
||||
|
||||
**Pages using this pattern:**
|
||||
- `/admin/vendors` - Vendor Management
|
||||
- `/admin/companies` - Company Management
|
||||
- `/admin/users` - User Management
|
||||
|
||||
📦 INSTALLATION (3 STEPS)
|
||||
═════════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
Step 1: Backup current files
|
||||
$ cp templates/admin/vendors.html templates/admin/vendors.html.backup
|
||||
$ cp static/admin/js/vendors.js static/admin/js/vendors.js.backup
|
||||
## Visual Layout
|
||||
|
||||
Step 2: Replace with new versions
|
||||
$ cp vendors.html templates/admin/vendors.html
|
||||
$ cp vendors.js static/admin/js/vendors.js
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Page Title [+ Create Button] │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ [Stat 1] [Stat 2] [Stat 3] [Stat 4] ← Stats Cards │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ [🔍 Search... ] [Filter ▼] [Filter ▼] [Refresh] ← Search Bar │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ Column 1 │ Column 2 │ Column 3 │ Actions │
|
||||
├──────────┼──────────┼──────────┼─────────────────────────────────┤
|
||||
│ Row 1 │ ... │ ... │ 👁 ✏️ 🗑 │
|
||||
│ Row 2 │ ... │ ... │ 👁 ✏️ 🗑 │
|
||||
│ ... │ ... │ ... │ ... │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ Showing 1-10 of 45 ← 1 2 [3] 4 ... 9 → │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Step 3: Clear cache and test
|
||||
- Hard refresh: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
|
||||
- Test with >10 vendors to see pagination
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
🎨 VISUAL PREVIEW
|
||||
═════════════════════════════════════════════════════════════════
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| Server-side pagination | Uses `skip`/`limit` API params |
|
||||
| Debounced search | 300ms delay before API call |
|
||||
| Filter dropdowns | Status, Verification, Role filters |
|
||||
| Smart page numbers | Shows ellipsis for large page ranges |
|
||||
| Refresh button | Manually reload data |
|
||||
| Contextual empty state | Different message when filters applied |
|
||||
|
||||
Before (No Pagination):
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Vendor │ Subdomain │ Status │ Created │ Actions │
|
||||
├────────┼───────────┼────────┼─────────┼───────────────────┤
|
||||
│ Vendor1│ vendor1 │ ✓ │ Jan 1 │ 👁 🗑 │ ❌ No edit
|
||||
│ Vendor2│ vendor2 │ ⏰ │ Jan 2 │ 👁 🗑 │
|
||||
│ ...50 more vendors... │
|
||||
│ Vendor52│vendor52 │ ✓ │ Jan 52 │ 👁 🗑 │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
❌ All vendors on one page (hard to scroll through!)
|
||||
---
|
||||
|
||||
## State Structure
|
||||
|
||||
After (With Pagination):
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Vendor │ Subdomain │ Status │ Created │ Actions │
|
||||
├────────┼───────────┼────────┼─────────┼───────────────────┤
|
||||
│ Vendor1│ vendor1 │ ✓ │ Jan 1 │ 👁 ✏️ 🗑 │ ✅ Edit added!
|
||||
│ Vendor2│ vendor2 │ ⏰ │ Jan 2 │ 👁 ✏️ 🗑 │
|
||||
│ ...8 more vendors... │
|
||||
│Vendor10│ vendor10 │ ✓ │ Jan 10 │ 👁 ✏️ 🗑 │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ Showing 1-10 of 52 ← 1 [2] 3 4 5 ... 9 → │ ✅ Pagination!
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
### JavaScript State
|
||||
```javascript
|
||||
// Filters
|
||||
filters: {
|
||||
search: '',
|
||||
is_active: '', // 'true', 'false', or ''
|
||||
is_verified: '', // 'true', 'false', or ''
|
||||
}
|
||||
|
||||
// Pagination
|
||||
pagination: {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
pages: 0
|
||||
}
|
||||
```
|
||||
|
||||
🎯 ACTION BUTTONS (3 BUTTONS PER ROW)
|
||||
═════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
1. 👁 View (Blue) ─────────→ View vendor details
|
||||
2. ✏️ Edit (Purple) ⭐ NEW → Edit vendor form
|
||||
3. 🗑 Delete (Red) ────────→ Delete with confirmation
|
||||
## Computed Properties
|
||||
|
||||
| Property | Purpose |
|
||||
|----------|---------|
|
||||
| `totalPages` | Total number of pages |
|
||||
| `startIndex` | First item number on current page |
|
||||
| `endIndex` | Last item number on current page |
|
||||
| `pageNumbers` | Array of page numbers with '...' |
|
||||
|
||||
🔢 PAGINATION CONTROLS
|
||||
═════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
Page 1 of 10:
|
||||
[←] 1 2 3 4 5 ... 9 10 [→]
|
||||
^ ^ ^ ^ ^
|
||||
| | | | └─ Next button
|
||||
| | | └──── Last page
|
||||
| | └─────── Ellipsis (...)
|
||||
| └───────────────────── Page numbers
|
||||
└──────────────────────── Previous button (disabled)
|
||||
## Methods
|
||||
|
||||
Page 5 of 10:
|
||||
[←] 1 ... 4 [5] 6 ... 10 [→]
|
||||
↑ ↑ ↑
|
||||
| | └─ Next page
|
||||
| └───── Current page (purple)
|
||||
└───────── Previous pages
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `debouncedSearch()` | Triggers search after 300ms |
|
||||
| `previousPage()` | Go to previous page |
|
||||
| `nextPage()` | Go to next page |
|
||||
| `goToPage(num)` | Jump to specific page |
|
||||
|
||||
---
|
||||
|
||||
📊 HOW IT WORKS
|
||||
═════════════════════════════════════════════════════════════
|
||||
## Page Number Display
|
||||
|
||||
1. Template displays: paginatedVendors (not all vendors)
|
||||
2. Alpine.js computes: Which 10 vendors to show
|
||||
3. User clicks page: currentPage updates
|
||||
4. Template refreshes: Shows new 10 vendors
|
||||
**Few pages (7 or less):**
|
||||
```
|
||||
← 1 2 3 4 5 6 7 →
|
||||
```
|
||||
|
||||
Simple and reactive! 🎉
|
||||
**Many pages, at start:**
|
||||
```
|
||||
← [1] 2 3 ... 10 →
|
||||
```
|
||||
|
||||
**Many pages, in middle:**
|
||||
```
|
||||
← 1 ... 4 [5] 6 ... 10 →
|
||||
```
|
||||
|
||||
⚙️ CONFIGURATION
|
||||
═════════════════════════════════════════════════════════════
|
||||
**Many pages, at end:**
|
||||
```
|
||||
← 1 ... 8 9 [10] →
|
||||
```
|
||||
|
||||
Change items per page in vendors.js:
|
||||
---
|
||||
|
||||
// Find this line:
|
||||
itemsPerPage: 10,
|
||||
## API Integration
|
||||
|
||||
// Change to any number:
|
||||
itemsPerPage: 25, // Show 25 per page
|
||||
itemsPerPage: 50, // Show 50 per page
|
||||
### Request
|
||||
```
|
||||
GET /admin/vendors?skip=0&limit=10&search=acme&is_active=true
|
||||
```
|
||||
|
||||
### Response
|
||||
```json
|
||||
{
|
||||
"vendors": [...],
|
||||
"total": 45,
|
||||
"skip": 0,
|
||||
"limit": 10
|
||||
}
|
||||
```
|
||||
|
||||
🧪 TESTING CHECKLIST
|
||||
═════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
After installation:
|
||||
## Configuration
|
||||
|
||||
□ Vendors page loads
|
||||
□ If <10 vendors: No pagination shows (correct!)
|
||||
□ If >10 vendors: Pagination appears
|
||||
□ Can click page numbers to navigate
|
||||
□ Can click ← → arrows
|
||||
□ "Showing X-Y of Z" updates correctly
|
||||
□ Edit button (pencil icon) appears
|
||||
□ Edit button navigates to edit page
|
||||
□ View button still works
|
||||
□ Delete button still works
|
||||
□ Dark mode works
|
||||
□ Pagination styling matches theme
|
||||
### Change Items Per Page
|
||||
|
||||
Edit in JavaScript file:
|
||||
```javascript
|
||||
pagination: {
|
||||
page: 1,
|
||||
per_page: 25, // Change from 10
|
||||
total: 0,
|
||||
pages: 0
|
||||
}
|
||||
```
|
||||
|
||||
🎨 FEATURES
|
||||
═════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
✅ Smart pagination (10 per page)
|
||||
✅ Edit button added (purple pencil)
|
||||
✅ Dynamic page numbers with "..."
|
||||
✅ Previous/Next disabled at boundaries
|
||||
✅ Shows "X-Y of Total" count
|
||||
✅ Dark mode compatible
|
||||
✅ Windmill theme styling
|
||||
✅ Fully responsive
|
||||
✅ Client-side pagination (fast!)
|
||||
✅ Auto-reset to page 1 after data reload
|
||||
## Files Reference
|
||||
|
||||
| Page | 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` |
|
||||
|
||||
💡 TIPS
|
||||
═════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
1. Need more vendors per page?
|
||||
Change itemsPerPage in vendors.js
|
||||
## Adding to a New Page
|
||||
|
||||
2. Want server-side pagination?
|
||||
For 1000+ vendors, consider API pagination
|
||||
1. **Add state** to JavaScript:
|
||||
```javascript
|
||||
filters: { search: '', is_active: '' },
|
||||
pagination: { page: 1, per_page: 10, total: 0, pages: 0 }
|
||||
```
|
||||
|
||||
3. Want to preserve page on refresh?
|
||||
Add localStorage for currentPage
|
||||
2. **Add computed properties**: `totalPages`, `startIndex`, `endIndex`, `pageNumbers`
|
||||
|
||||
4. Want to add search/filter?
|
||||
Filter vendors array first, then paginate
|
||||
3. **Add methods**: `debouncedSearch()`, `previousPage()`, `nextPage()`, `goToPage()`
|
||||
|
||||
5. Page numbers look weird?
|
||||
Check you have enough vendors (need >10 to see pagination)
|
||||
4. **Update load function** to use query params
|
||||
|
||||
5. **Add HTML** for search bar and pagination footer
|
||||
|
||||
🆘 TROUBLESHOOTING
|
||||
═════════════════════════════════════════════════════════════
|
||||
|
||||
Problem: Pagination not showing
|
||||
→ Need more than 10 vendors to see it
|
||||
→ Check itemsPerPage value
|
||||
|
||||
Problem: Edit button doesn't work
|
||||
→ Check backend route exists: /admin/vendors/{code}/edit
|
||||
→ Check browser console for errors
|
||||
|
||||
Problem: Page numbers stuck
|
||||
→ Clear browser cache (Ctrl+Shift+R)
|
||||
→ Check Alpine.js loaded correctly
|
||||
|
||||
Problem: "Showing 0-0 of 0"
|
||||
→ Vendors not loading from API
|
||||
→ Check API endpoint /admin/vendors
|
||||
|
||||
|
||||
📁 FILES INCLUDED
|
||||
═════════════════════════════════════════════════════════════
|
||||
|
||||
vendors.html ................. Updated template with pagination
|
||||
vendors.js ................... Updated script with pagination logic
|
||||
PAGINATION_DOCUMENTATION.md .. Full technical documentation
|
||||
PAGINATION_QUICK_START.txt ... This file (quick reference)
|
||||
|
||||
|
||||
📖 LEARN MORE
|
||||
═════════════════════════════════════════════════════════════
|
||||
|
||||
For detailed technical explanation, see:
|
||||
→ PAGINATION_DOCUMENTATION.md
|
||||
|
||||
|
||||
══════════════════════════════════════════════════════════════
|
||||
Pagination made easy! 📖✨
|
||||
══════════════════════════════════════════════════════════════
|
||||
See `docs/frontend/shared/pagination.md` for full implementation details.
|
||||
|
||||
@@ -1,297 +1,351 @@
|
||||
# Vendor Page Pagination Implementation
|
||||
# Admin List Pages - Pagination & Search Implementation
|
||||
|
||||
## 🎯 What's New
|
||||
## Overview
|
||||
|
||||
Your vendors page now includes:
|
||||
1. ✅ **Edit button** - Already present (pencil icon next to view icon)
|
||||
2. ✅ **Pagination** - New pagination controls at the bottom of the table
|
||||
All admin list pages (Vendors, Companies, Users) share a consistent pagination and search pattern using **server-side pagination** with Alpine.js.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Files Updated
|
||||
## Files Using This Pattern
|
||||
|
||||
### 1. vendors.html
|
||||
**Changes:**
|
||||
- Changed `x-for="vendor in vendors"` → `x-for="vendor in paginatedVendors"`
|
||||
- Added pagination footer with controls
|
||||
- Added "Showing X-Y of Z" results display
|
||||
|
||||
### 2. vendors.js
|
||||
**Changes:**
|
||||
- Added pagination state: `currentPage`, `itemsPerPage`
|
||||
- Added computed properties:
|
||||
- `paginatedVendors` - Returns current page's vendors
|
||||
- `totalPages` - Calculates total number of pages
|
||||
- `startIndex` / `endIndex` - For "Showing X-Y" display
|
||||
- `pageNumbers` - Generates smart page number array with ellipsis
|
||||
- Added pagination methods:
|
||||
- `goToPage(page)` - Navigate to specific page
|
||||
- `nextPage()` - Go to next page
|
||||
- `previousPage()` - Go to previous page
|
||||
| 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` |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Pagination Features
|
||||
## State Structure
|
||||
|
||||
### Smart Page Number Display
|
||||
The pagination intelligently shows page numbers:
|
||||
### 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
|
||||
}
|
||||
```
|
||||
|
||||
**Example 1: Few pages (≤7)**
|
||||
### 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
|
||||
<div class="mb-6 p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
|
||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<!-- Search Input -->
|
||||
<div class="flex-1 max-w-md">
|
||||
<div class="relative">
|
||||
<span class="absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span x-html="$icon('search', 'w-5 h-5 text-gray-400')"></span>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
x-model="filters.search"
|
||||
@input="debouncedSearch()"
|
||||
placeholder="Search..."
|
||||
class="w-full pl-10 pr-4 py-2 text-sm border rounded-lg..."
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<select x-model="filters.is_active" @change="pagination.page = 1; loadVendors()">
|
||||
<option value="">All Status</option>
|
||||
<option value="true">Active</option>
|
||||
<option value="false">Inactive</option>
|
||||
</select>
|
||||
|
||||
<select x-model="filters.is_verified" @change="pagination.page = 1; loadVendors()">
|
||||
<option value="">All Verification</option>
|
||||
<option value="true">Verified</option>
|
||||
<option value="false">Pending</option>
|
||||
</select>
|
||||
|
||||
<button @click="loadVendors()">Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Pagination Footer
|
||||
```html
|
||||
<div class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t bg-gray-50 sm:grid-cols-9">
|
||||
<!-- Results Info -->
|
||||
<span class="flex items-center col-span-3">
|
||||
Showing <span x-text="startIndex"></span>-<span x-text="endIndex"></span>
|
||||
of <span x-text="pagination.total"></span>
|
||||
</span>
|
||||
<span class="col-span-2"></span>
|
||||
|
||||
<!-- Pagination Controls -->
|
||||
<span class="flex col-span-4 mt-2 sm:mt-auto sm:justify-end">
|
||||
<nav>
|
||||
<ul class="inline-flex items-center">
|
||||
<!-- Previous -->
|
||||
<li>
|
||||
<button @click="previousPage()" :disabled="pagination.page === 1">
|
||||
<!-- SVG arrow -->
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<!-- Page Numbers -->
|
||||
<template x-for="pageNum in pageNumbers" :key="pageNum">
|
||||
<li>
|
||||
<button
|
||||
x-show="pageNum !== '...'"
|
||||
@click="goToPage(pageNum)"
|
||||
:class="pagination.page === pageNum ? 'bg-purple-600 text-white' : ''"
|
||||
x-text="pageNum"
|
||||
></button>
|
||||
<span x-show="pageNum === '...'" class="px-3 py-1">...</span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<!-- Next -->
|
||||
<li>
|
||||
<button @click="nextPage()" :disabled="pagination.page === totalPages">
|
||||
<!-- SVG arrow -->
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Page Number Display Examples
|
||||
|
||||
**Few pages (<=7):**
|
||||
```
|
||||
← 1 2 3 4 5 6 7 →
|
||||
```
|
||||
|
||||
**Example 2: Many pages, current = 1**
|
||||
**Many pages, current = 1:**
|
||||
```
|
||||
← 1 2 3 ... 9 10 →
|
||||
← [1] 2 3 ... 10 →
|
||||
```
|
||||
|
||||
**Example 3: Many pages, current = 5**
|
||||
**Many pages, current = 5:**
|
||||
```
|
||||
← 1 ... 4 5 6 ... 10 →
|
||||
← 1 ... 4 [5] 6 ... 10 →
|
||||
```
|
||||
|
||||
**Example 4: Many pages, current = 10**
|
||||
**Many pages, current = 10:**
|
||||
```
|
||||
← 1 ... 8 9 10 →
|
||||
```
|
||||
|
||||
### Configurable Items Per Page
|
||||
Default: 10 vendors per page
|
||||
|
||||
To change, edit in `vendors.js`:
|
||||
```javascript
|
||||
itemsPerPage: 10, // Change this number
|
||||
← 1 ... 8 9 [10] →
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 How It Works
|
||||
## Visual Layout
|
||||
|
||||
### 1. Computed Properties (Alpine.js)
|
||||
Alpine.js computes these values reactively:
|
||||
|
||||
```javascript
|
||||
get paginatedVendors() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const end = start + this.itemsPerPage;
|
||||
return this.vendors.slice(start, end);
|
||||
}
|
||||
```
|
||||
|
||||
When `currentPage` changes, `paginatedVendors` automatically updates!
|
||||
|
||||
### 2. Template Binding
|
||||
The HTML template uses the computed property:
|
||||
```html
|
||||
<template x-for="vendor in paginatedVendors" :key="vendor.vendor_code">
|
||||
<!-- Vendor row -->
|
||||
</template>
|
||||
```
|
||||
|
||||
### 3. Pagination Controls
|
||||
Buttons call the pagination methods:
|
||||
```html
|
||||
<button @click="previousPage()" :disabled="currentPage === 1">
|
||||
<button @click="goToPage(page)">
|
||||
<button @click="nextPage()" :disabled="currentPage === totalPages">
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ 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 → │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Visual Layout
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Vendor Management [+ Create Vendor] │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ [Total] [Verified] [Pending] [Inactive] ← Stats Cards │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Vendor │ Subdomain │ Status │ Created │ Actions │
|
||||
├────────┼───────────┼────────┼─────────┼─────────────────┤
|
||||
│ Acme │ acme │ ✓ │ Jan 1 │ 👁 ✏️ 🗑 │
|
||||
│ Beta │ beta │ ⏰ │ Jan 2 │ 👁 ✏️ 🗑 │
|
||||
│ ... │ ... │ ... │ ... │ ... │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Showing 1-10 of 45 ← 1 2 [3] 4 ... 9 → │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
↑
|
||||
Pagination controls
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎭 Action Buttons Explained
|
||||
|
||||
Each vendor row has 3 action buttons:
|
||||
|
||||
1. **View Button** (👁 Eye icon - Blue)
|
||||
- Shows vendor details
|
||||
- Calls: `viewVendor(vendor.vendor_code)`
|
||||
- Navigates to: `/admin/vendors/{code}`
|
||||
|
||||
2. **Edit Button** (✏️ Pencil icon - Purple) ⭐ NEW
|
||||
- Opens edit form
|
||||
- Calls: `editVendor(vendor.vendor_code)`
|
||||
- Navigates to: `/admin/vendors/{code}/edit`
|
||||
|
||||
3. **Delete Button** (🗑 Trash icon - Red)
|
||||
- Deletes vendor with confirmation
|
||||
- Calls: `deleteVendor(vendor)`
|
||||
- Shows confirmation dialog first
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing the Pagination
|
||||
|
||||
### Test Scenarios
|
||||
|
||||
**Test 1: With few vendors (<10)**
|
||||
- Pagination should not show if only 1 page
|
||||
- All vendors visible on page 1
|
||||
|
||||
**Test 2: With many vendors (>10)**
|
||||
- Pagination controls appear
|
||||
- Can navigate between pages
|
||||
- "Previous" disabled on page 1
|
||||
- "Next" disabled on last page
|
||||
|
||||
**Test 3: Navigation**
|
||||
- Click page numbers to jump to specific page
|
||||
- Click "Previous" arrow to go back
|
||||
- Click "Next" arrow to go forward
|
||||
- Page numbers update dynamically
|
||||
|
||||
**Test 4: After Delete**
|
||||
- Delete a vendor
|
||||
- Data reloads
|
||||
- Returns to page 1
|
||||
- Pagination updates
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Styling
|
||||
|
||||
The pagination uses the same Windmill theme as your dashboard:
|
||||
|
||||
- **Normal buttons**: Gray with hover effect
|
||||
- **Current page**: Purple background (matches your theme)
|
||||
- **Disabled buttons**: 50% opacity, cursor not-allowed
|
||||
- **Hover effects**: Light gray background
|
||||
- **Dark mode**: Full support
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Customization Options
|
||||
## Configuration
|
||||
|
||||
### Change Items Per Page
|
||||
In `vendors.js`:
|
||||
In the JavaScript file:
|
||||
```javascript
|
||||
itemsPerPage: 20, // Show 20 vendors per page
|
||||
```
|
||||
|
||||
### Change Page Number Display Logic
|
||||
In `vendors.js`, modify the `pageNumbers` computed property:
|
||||
```javascript
|
||||
get pageNumbers() {
|
||||
// Modify this logic to change how page numbers appear
|
||||
pagination: {
|
||||
page: 1,
|
||||
per_page: 25, // Change from 10 to 25
|
||||
total: 0,
|
||||
pages: 0
|
||||
}
|
||||
```
|
||||
|
||||
### Change Pagination Position
|
||||
In `vendors.html`, the pagination footer is at line ~220.
|
||||
Move this entire `<div class="grid px-4 py-3...">` section.
|
||||
|
||||
---
|
||||
|
||||
## 📝 Code Comparison
|
||||
## Features Summary
|
||||
|
||||
### Before (No Pagination)
|
||||
```javascript
|
||||
// vendors.js
|
||||
vendors: [], // All vendors displayed at once
|
||||
|
||||
// vendors.html
|
||||
<template x-for="vendor in vendors">
|
||||
```
|
||||
|
||||
### After (With Pagination)
|
||||
```javascript
|
||||
// vendors.js
|
||||
vendors: [],
|
||||
currentPage: 1,
|
||||
itemsPerPage: 10,
|
||||
get paginatedVendors() {
|
||||
return this.vendors.slice(start, end);
|
||||
}
|
||||
|
||||
// vendors.html
|
||||
<template x-for="vendor in paginatedVendors">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Installation Checklist
|
||||
|
||||
Replace these files in your project:
|
||||
|
||||
- [ ] `static/admin/js/vendors.js` → Use `vendors-with-pagination.js`
|
||||
- [ ] `templates/admin/vendors.html` → Use `vendors-with-pagination.html`
|
||||
- [ ] Clear browser cache (Ctrl+Shift+R)
|
||||
- [ ] Test with >10 vendors to see pagination
|
||||
- [ ] Test edit button works
|
||||
- [ ] Test page navigation
|
||||
- [ ] Test in dark mode
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Troubleshooting
|
||||
|
||||
### Pagination not showing?
|
||||
- Make sure you have more than 10 vendors
|
||||
- Check `itemsPerPage` value in vendors.js
|
||||
- Verify `paginatedVendors` is used in template
|
||||
|
||||
### Edit button not working?
|
||||
- Check browser console for errors
|
||||
- Verify `editVendor()` method exists in vendors.js
|
||||
- Check the route exists on your backend: `/admin/vendors/{code}/edit`
|
||||
|
||||
### Page numbers not updating?
|
||||
- This is a computed property - it should update automatically
|
||||
- Check Alpine.js is loaded correctly
|
||||
- Clear browser cache
|
||||
|
||||
### "Showing X-Y of Z" incorrect?
|
||||
- Check `startIndex` and `endIndex` computed properties
|
||||
- Verify `vendors.length` is correct
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
1. **Performance**: Pagination is client-side. For 1000+ vendors, consider server-side pagination.
|
||||
|
||||
2. **State Preservation**: When returning from edit page, user goes back to page 1. To preserve page state, you'd need to:
|
||||
- Store `currentPage` in localStorage
|
||||
- Restore it on page load
|
||||
|
||||
3. **Search/Filter**: To add search, filter `vendors` array first, then paginate the filtered results.
|
||||
|
||||
4. **Sorting**: Add sorting before pagination to maintain consistent page content.
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Features Summary
|
||||
|
||||
✅ Edit button added (pencil icon)
|
||||
✅ Smart pagination (10 items per page)
|
||||
✅ Dynamic page numbers with ellipsis
|
||||
✅ Previous/Next navigation
|
||||
✅ Disabled states for boundary pages
|
||||
✅ "Showing X-Y of Z" display
|
||||
✅ Dark mode support
|
||||
✅ Fully responsive
|
||||
✅ Follows Windmill design theme
|
||||
|
||||
Happy paginating! 📖
|
||||
- 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
|
||||
|
||||
Reference in New Issue
Block a user