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

@@ -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 %}
<!-- Header -->
<div class="flex items-center justify-between mb-6">
@@ -73,13 +74,13 @@
<input
type="text"
x-model="exceptionsSearch"
@input.debounce.300ms="exceptionsPage = 1; loadExceptions()"
@input.debounce.300ms="pagination.page = 1; loadExceptions()"
placeholder="Search by GTIN, product name, or order..."
class="w-full pl-9 pr-8 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md focus:border-purple-400 focus:outline-none"
/>
<button
x-show="exceptionsSearch"
@click="exceptionsSearch = ''; exceptionsPage = 1; loadExceptions()"
@click="exceptionsSearch = ''; pagination.page = 1; loadExceptions()"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
>
<span x-html="$icon('x', 'w-4 h-4')"></span>
@@ -89,7 +90,7 @@
<!-- Status filter -->
<select
x-model="exceptionsFilter"
@change="exceptionsPage = 1; loadExceptions()"
@change="pagination.page = 1; loadExceptions()"
class="px-3 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md focus:border-purple-400 focus:outline-none"
>
<option value="">All Status</option>
@@ -205,35 +206,5 @@
</tbody>
</table>
</div>
<!-- Pagination -->
<div x-show="totalExceptions > exceptionsLimit" class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
<span class="flex items-center col-span-3">
Showing <span x-text="((exceptionsPage - 1) * exceptionsLimit) + 1" class="mx-1"></span>-<span x-text="Math.min(exceptionsPage * exceptionsLimit, totalExceptions)" class="mx-1"></span> of <span x-text="totalExceptions" class="mx-1"></span>
</span>
<span class="col-span-2"></span>
<span class="flex col-span-4 mt-2 sm:mt-auto sm:justify-end">
<nav aria-label="Table navigation">
<ul class="inline-flex items-center">
<li>
<button
@click="exceptionsPage--; loadExceptions()"
:disabled="exceptionsPage <= 1"
class="px-3 py-1 rounded-md rounded-l-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
>
<span x-html="$icon('chevron-left', 'w-4 h-4')"></span>
</button>
</li>
<li>
<button
@click="exceptionsPage++; loadExceptions()"
:disabled="exceptionsPage * exceptionsLimit >= totalExceptions"
class="px-3 py-1 rounded-md rounded-r-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
>
<span x-html="$icon('chevron-right', 'w-4 h-4')"></span>
</button>
</li>
</ul>
</nav>
</span>
</div>
{{ pagination(show_condition="!loadingExceptions && pagination.total > 0") }}
</div>

View File

@@ -1,5 +1,6 @@
{# app/templates/admin/partials/letzshop-jobs-table.html #}
{# Unified jobs table for admin Letzshop management - Import, Export, and Sync jobs #}
{% from 'shared/macros/pagination.html' import pagination %}
<div class="bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-6">
@@ -161,61 +162,7 @@
</table>
</div>
<!-- Numbered Pagination -->
<div x-show="jobsPagination.total > jobsPagination.per_page" class="flex flex-col sm:flex-row items-center justify-between gap-4 mt-4 pt-4 border-t dark:border-gray-700">
<span class="text-sm text-gray-600 dark:text-gray-400">
Showing <span x-text="((jobsPagination.page - 1) * jobsPagination.per_page) + 1"></span>-<span x-text="Math.min(jobsPagination.page * jobsPagination.per_page, jobsPagination.total)"></span> of <span x-text="jobsPagination.total"></span> jobs
</span>
<div class="flex items-center gap-1">
{# First page #}
<button
@click="jobsPagination.page = 1; loadJobs()"
:disabled="jobsPagination.page <= 1"
class="px-2 py-1 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
title="First page"
>
<span x-html="$icon('chevron-double-left', 'w-4 h-4')"></span>
</button>
{# Previous page #}
<button
@click="jobsPagination.page--; loadJobs()"
:disabled="jobsPagination.page <= 1"
class="px-2 py-1 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
title="Previous page"
>
<span x-html="$icon('chevron-left', 'w-4 h-4')"></span>
</button>
{# Page numbers #}
<template x-for="p in getPageNumbers()" :key="p">
<button
@click="goToPage(p)"
class="px-3 py-1 text-sm font-medium rounded-md border transition-colors"
:class="p === jobsPagination.page
? 'bg-purple-600 text-white border-purple-600 dark:bg-purple-500 dark:border-purple-500'
: 'text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600'"
x-text="p"
></button>
</template>
{# Next page #}
<button
@click="jobsPagination.page++; loadJobs()"
:disabled="jobsPagination.page >= jobsTotalPages"
class="px-2 py-1 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
title="Next page"
>
<span x-html="$icon('chevron-right', 'w-4 h-4')"></span>
</button>
{# Last page #}
<button
@click="jobsPagination.page = jobsTotalPages; loadJobs()"
:disabled="jobsPagination.page >= jobsTotalPages"
class="px-2 py-1 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
title="Last page"
>
<span x-html="$icon('chevron-double-right', 'w-4 h-4')"></span>
</button>
</div>
</div>
{{ pagination(show_condition="!loadingJobs && pagination.total > 0") }}
</div>
</div>

View File

@@ -1,5 +1,6 @@
{# app/templates/admin/partials/letzshop-orders-tab.html #}
{# Orders tab for admin Letzshop management #}
{% from 'shared/macros/pagination.html' import pagination %}
<!-- Header with Import Buttons -->
<div class="flex items-center justify-between mb-6">
@@ -144,13 +145,13 @@
<input
type="text"
x-model="ordersSearch"
@input.debounce.300ms="ordersPage = 1; loadOrders()"
@input.debounce.300ms="pagination.page = 1; loadOrders()"
placeholder="Search by order #, name, or email..."
class="w-full pl-9 pr-8 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md focus:border-purple-400 focus:outline-none"
/>
<button
x-show="ordersSearch"
@click="ordersSearch = ''; ordersPage = 1; loadOrders()"
@click="ordersSearch = ''; pagination.page = 1; loadOrders()"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
>
<span x-html="$icon('x', 'w-4 h-4')"></span>
@@ -160,7 +161,7 @@
<!-- Status filter -->
<select
x-model="ordersFilter"
@change="ordersHasDeclinedItems = false; ordersPage = 1; loadOrders()"
@change="ordersHasDeclinedItems = false; pagination.page = 1; loadOrders()"
class="px-3 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md focus:border-purple-400 focus:outline-none"
>
<option value="">All Status</option>
@@ -172,7 +173,7 @@
<!-- Declined items filter -->
<button
@click="ordersFilter = ''; ordersHasDeclinedItems = !ordersHasDeclinedItems; ordersPage = 1; loadOrders()"
@click="ordersFilter = ''; ordersHasDeclinedItems = !ordersHasDeclinedItems; pagination.page = 1; loadOrders()"
:class="ordersHasDeclinedItems ? 'bg-red-100 dark:bg-red-900 border-red-300 dark:border-red-700 text-red-700 dark:text-red-300' : 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300'"
class="px-3 py-2 text-sm border rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
:title="ordersHasDeclinedItems ? 'Showing orders with declined items' : 'Show only orders with declined items'"
@@ -302,35 +303,5 @@
</tbody>
</table>
</div>
<!-- Pagination -->
<div x-show="totalOrders > ordersLimit" class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-800">
<span class="flex items-center col-span-3">
Showing <span x-text="((ordersPage - 1) * ordersLimit) + 1" class="mx-1"></span>-<span x-text="Math.min(ordersPage * ordersLimit, totalOrders)" class="mx-1"></span> of <span x-text="totalOrders" class="mx-1"></span>
</span>
<span class="col-span-2"></span>
<span class="flex col-span-4 mt-2 sm:mt-auto sm:justify-end">
<nav aria-label="Table navigation">
<ul class="inline-flex items-center">
<li>
<button
@click="ordersPage--; loadOrders()"
:disabled="ordersPage <= 1"
class="px-3 py-1 rounded-md rounded-l-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
>
<span x-html="$icon('chevron-left', 'w-4 h-4')"></span>
</button>
</li>
<li>
<button
@click="ordersPage++; loadOrders()"
:disabled="ordersPage * ordersLimit >= totalOrders"
class="px-3 py-1 rounded-md rounded-r-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
>
<span x-html="$icon('chevron-right', 'w-4 h-4')"></span>
</button>
</li>
</ul>
</nav>
</span>
</div>
{{ pagination(show_condition="!loadingOrders && pagination.total > 0") }}
</div>

View File

@@ -107,7 +107,7 @@
<!-- Status Filter -->
<select
x-model="productFilters.is_active"
@change="productsPage = 1; loadProducts()"
@change="pagination.page = 1; loadProducts()"
class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none"
>
<option value="">All Status</option>
@@ -242,36 +242,7 @@
</tbody>
{% endcall %}
<!-- Pagination -->
<div x-show="totalProducts > productsLimit" class="mt-4">
<div class="flex items-center justify-between px-4 py-3 bg-white border-t border-gray-200 dark:bg-gray-800 dark:border-gray-700 rounded-b-lg">
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
<span>Showing</span>
<span class="font-medium mx-1" x-text="((productsPage - 1) * productsLimit) + 1"></span>
<span>to</span>
<span class="font-medium mx-1" x-text="Math.min(productsPage * productsLimit, totalProducts)"></span>
<span>of</span>
<span class="font-medium mx-1" x-text="totalProducts"></span>
<span>products</span>
</div>
<div class="flex gap-2">
<button
@click="productsPage--; loadProducts()"
:disabled="productsPage <= 1"
class="px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
Previous
</button>
<button
@click="productsPage++; loadProducts()"
:disabled="productsPage * productsLimit >= totalProducts"
class="px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
</button>
</div>
</div>
</div>
{{ pagination(show_condition="!loadingProducts && pagination.total > 0") }}
</div>
<!-- Import Modal -->