diff --git a/app/templates/admin/partials/letzshop-exceptions-tab.html b/app/templates/admin/partials/letzshop-exceptions-tab.html index 16120b06..5d4f4bdd 100644 --- a/app/templates/admin/partials/letzshop-exceptions-tab.html +++ b/app/templates/admin/partials/letzshop-exceptions-tab.html @@ -1,5 +1,6 @@ {# app/templates/admin/partials/letzshop-exceptions-tab.html #} {# Exceptions tab for admin Letzshop management - Order Item Exception Resolution #} +{% from 'shared/macros/pagination.html' import pagination %}
@@ -73,13 +74,13 @@ - -
- - + {{ pagination(show_condition="!loadingProducts && pagination.total > 0") }} diff --git a/static/admin/js/marketplace-letzshop.js b/static/admin/js/marketplace-letzshop.js index d11f5adc..1e9a86a0 100644 --- a/static/admin/js/marketplace-letzshop.js +++ b/static/admin/js/marketplace-letzshop.js @@ -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