refactor: unify Letzshop pagination to use standard macro

Consolidate all tab-specific pagination into a unified pagination object
and use the shared pagination macro for consistent look and feel across
Orders, Products, Jobs, and Exceptions tabs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-25 10:44:54 +01:00
parent 4902ff274b
commit ef7c79908c
5 changed files with 133 additions and 222 deletions

View File

@@ -90,11 +90,16 @@ function adminMarketplaceLetzshop() {
},
savingCarrierSettings: false,
// Unified pagination (shared across tabs, updated based on activeTab)
pagination: {
page: 1,
per_page: 20,
total: 0,
pages: 0
},
// Orders
orders: [],
totalOrders: 0,
ordersPage: 1,
ordersLimit: 20,
ordersFilter: '',
ordersSearch: '',
ordersHasDeclinedItems: false,
@@ -102,9 +107,6 @@ function adminMarketplaceLetzshop() {
// Exceptions
exceptions: [],
totalExceptions: 0,
exceptionsPage: 1,
exceptionsLimit: 20,
exceptionsFilter: '',
exceptionsSearch: '',
exceptionStats: { pending: 0, resolved: 0, ignored: 0, total: 0, orders_with_exceptions: 0 },
@@ -113,13 +115,9 @@ function adminMarketplaceLetzshop() {
// Jobs
jobs: [],
jobsFilter: { type: '', status: '' },
jobsPagination: { page: 1, per_page: 10, total: 0 },
// Products Tab
products: [],
totalProducts: 0,
productsPage: 1,
productsLimit: 20,
loadingProducts: false,
productFilters: { search: '', is_active: '' },
productStats: { total: 0, active: 0, inactive: 0, last_sync: null },
@@ -140,6 +138,93 @@ function adminMarketplaceLetzshop() {
searchingProducts: false,
submittingResolve: false,
// Computed: Total pages
get totalPages() {
return this.pagination.pages || Math.ceil(this.pagination.total / this.pagination.per_page) || 0;
},
// Computed: Start index for pagination display
get startIndex() {
if (this.pagination.total === 0) return 0;
return (this.pagination.page - 1) * this.pagination.per_page + 1;
},
// Computed: End index for pagination display
get endIndex() {
const end = this.pagination.page * this.pagination.per_page;
return end > this.pagination.total ? this.pagination.total : end;
},
// Computed: Page numbers for pagination
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;
},
// Pagination: Previous page
previousPage() {
if (this.pagination.page > 1) {
this.pagination.page--;
this._loadCurrentTabData();
}
},
// Pagination: Next page
nextPage() {
if (this.pagination.page < this.totalPages) {
this.pagination.page++;
this._loadCurrentTabData();
}
},
// Pagination: Go to specific page
goToPage(pageNum) {
if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) {
this.pagination.page = pageNum;
this._loadCurrentTabData();
}
},
// Helper: Load data for current active tab
_loadCurrentTabData() {
switch (this.activeTab) {
case 'orders':
this.loadOrders();
break;
case 'products':
this.loadProducts();
break;
case 'jobs':
this.loadJobs();
break;
case 'exceptions':
this.loadExceptions();
break;
}
},
async init() {
marketplaceLetzshopLog.info('init() called');
@@ -153,10 +238,7 @@ function adminMarketplaceLetzshop() {
// Load platform settings for pagination
if (window.PlatformSettings) {
const rowsPerPage = await window.PlatformSettings.getRowsPerPage();
this.ordersLimit = rowsPerPage;
this.exceptionsLimit = rowsPerPage;
this.productsLimit = rowsPerPage;
this.jobsPagination.per_page = rowsPerPage;
this.pagination.per_page = rowsPerPage;
marketplaceLetzshopLog.info('Loaded rows per page setting:', rowsPerPage);
}
@@ -168,11 +250,14 @@ function adminMarketplaceLetzshop() {
// Watch for tab changes to reload relevant data
this.$watch('activeTab', async (newTab) => {
marketplaceLetzshopLog.info('Tab changed to:', newTab);
// Reset pagination to page 1 when switching tabs
this.pagination.page = 1;
this.pagination.total = 0;
this.pagination.pages = 0;
if (newTab === 'jobs') {
// Load jobs for selected vendor or all Letzshop jobs
await this.loadJobs();
} else if (newTab === 'products') {
// Load products for selected vendor or all Letzshop products
await this.loadProducts();
} else if (newTab === 'orders') {
await this.loadOrders();
@@ -451,8 +536,8 @@ function adminMarketplaceLetzshop() {
try {
const params = new URLSearchParams({
marketplace: 'Letzshop',
skip: ((this.productsPage - 1) * this.productsLimit).toString(),
limit: this.productsLimit.toString()
skip: ((this.pagination.page - 1) * this.pagination.per_page).toString(),
limit: this.pagination.per_page.toString()
});
// Filter by vendor if one is selected
@@ -470,14 +555,15 @@ function adminMarketplaceLetzshop() {
const response = await apiClient.get(`/admin/products?${params}`);
this.products = response.products || [];
this.totalProducts = response.total || 0;
this.pagination.total = response.total || 0;
this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page);
// Load stats separately
await this.loadProductStats();
} catch (error) {
marketplaceLetzshopLog.error('Failed to load products:', error);
this.products = [];
this.totalProducts = 0;
this.pagination.total = 0;
} finally {
this.loadingProducts = false;
}
@@ -656,8 +742,8 @@ function adminMarketplaceLetzshop() {
try {
const params = new URLSearchParams({
skip: ((this.ordersPage - 1) * this.ordersLimit).toString(),
limit: this.ordersLimit.toString()
skip: ((this.pagination.page - 1) * this.pagination.per_page).toString(),
limit: this.pagination.per_page.toString()
});
if (this.ordersFilter) {
@@ -680,7 +766,8 @@ function adminMarketplaceLetzshop() {
const response = await apiClient.get(`${url}?${params}`);
this.orders = response.orders || [];
this.totalOrders = response.total || 0;
this.pagination.total = response.total || 0;
this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page);
// Use server-side stats (counts all orders, not just visible page)
if (response.stats) {
@@ -1235,8 +1322,8 @@ function adminMarketplaceLetzshop() {
try {
const params = new URLSearchParams({
skip: ((this.exceptionsPage - 1) * this.exceptionsLimit).toString(),
limit: this.exceptionsLimit.toString()
skip: ((this.pagination.page - 1) * this.pagination.per_page).toString(),
limit: this.pagination.per_page.toString()
});
if (this.exceptionsFilter) {
@@ -1254,7 +1341,8 @@ function adminMarketplaceLetzshop() {
const response = await apiClient.get(`/admin/order-exceptions?${params}`);
this.exceptions = response.exceptions || [];
this.totalExceptions = response.total || 0;
this.pagination.total = response.total || 0;
this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page);
} catch (error) {
marketplaceLetzshopLog.error('Failed to load exceptions:', error);
this.error = error.message || 'Failed to load exceptions';
@@ -1390,44 +1478,6 @@ function adminMarketplaceLetzshop() {
// JOBS TABLE
// ═══════════════════════════════════════════════════════════════
/**
* Get total pages for jobs pagination
*/
get jobsTotalPages() {
return Math.ceil(this.jobsPagination.total / this.jobsPagination.per_page);
},
/**
* Get array of page numbers to display for jobs pagination
*/
getPageNumbers() {
const total = this.jobsTotalPages;
const current = this.jobsPagination.page;
const maxVisible = 5;
if (total <= maxVisible) {
return Array.from({length: total}, (_, i) => i + 1);
}
const half = Math.floor(maxVisible / 2);
let start = Math.max(1, current - half);
let end = Math.min(total, start + maxVisible - 1);
if (end - start < maxVisible - 1) {
start = Math.max(1, end - maxVisible + 1);
}
return Array.from({length: end - start + 1}, (_, i) => start + i);
},
/**
* Go to specific page for jobs
*/
goToPage(page) {
this.jobsPagination.page = page;
this.loadJobs();
},
/**
* Load jobs for selected vendor or all vendors
*/
@@ -1436,8 +1486,8 @@ function adminMarketplaceLetzshop() {
try {
const params = new URLSearchParams({
skip: ((this.jobsPagination.page - 1) * this.jobsPagination.per_page).toString(),
limit: this.jobsPagination.per_page.toString()
skip: ((this.pagination.page - 1) * this.pagination.per_page).toString(),
limit: this.pagination.per_page.toString()
});
if (this.jobsFilter.type) {
@@ -1454,7 +1504,8 @@ function adminMarketplaceLetzshop() {
const response = await apiClient.get(endpoint);
this.jobs = response.jobs || [];
this.jobsPagination.total = response.total || 0;
this.pagination.total = response.total || 0;
this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page);
} catch (error) {
marketplaceLetzshopLog.error('Failed to load jobs:', error);
// Don't show error for jobs - not critical