feat(loyalty): add paginated transaction history to card detail
Some checks failed
Some checks failed
The store card detail page now shows paginated transaction history instead of a flat list of 50. Uses PlatformSettings.getRowsPerPage() for the page size (default 20), with Previous/Next navigation and "Page X of Y" indicator using server-rendered i18n. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -761,7 +761,8 @@
|
|||||||
"col_location": "Location",
|
"col_location": "Location",
|
||||||
"col_notes": "Notes",
|
"col_notes": "Notes",
|
||||||
"no_transactions": "No transactions yet",
|
"no_transactions": "No transactions yet",
|
||||||
"card_label": "Card"
|
"card_label": "Card",
|
||||||
|
"page_x_of_y": "Page {page} of {pages}"
|
||||||
},
|
},
|
||||||
"enroll": {
|
"enroll": {
|
||||||
"title": "Enroll Customer",
|
"title": "Enroll Customer",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ function storeLoyaltyCardDetail() {
|
|||||||
cardId: null,
|
cardId: null,
|
||||||
card: null,
|
card: null,
|
||||||
transactions: [],
|
transactions: [],
|
||||||
|
txPagination: { page: 1, perPage: 20, total: 0, pages: 0 },
|
||||||
|
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -38,6 +39,13 @@ function storeLoyaltyCardDetail() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use platform pagination setting if available
|
||||||
|
if (window.PlatformSettings) {
|
||||||
|
try {
|
||||||
|
this.txPagination.perPage = await window.PlatformSettings.getRowsPerPage();
|
||||||
|
} catch (e) { /* use default */ }
|
||||||
|
}
|
||||||
|
|
||||||
await this.loadData();
|
await this.loadData();
|
||||||
loyaltyCardDetailLog.info('=== LOYALTY CARD DETAIL PAGE INITIALIZATION COMPLETE ===');
|
loyaltyCardDetailLog.info('=== LOYALTY CARD DETAIL PAGE INITIALIZATION COMPLETE ===');
|
||||||
},
|
},
|
||||||
@@ -67,18 +75,32 @@ function storeLoyaltyCardDetail() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadTransactions() {
|
async loadTransactions(page = 1) {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/store/loyalty/cards/${this.cardId}/transactions?limit=50`);
|
const skip = (page - 1) * this.txPagination.perPage;
|
||||||
|
const response = await apiClient.get(
|
||||||
|
`/store/loyalty/cards/${this.cardId}/transactions?skip=${skip}&limit=${this.txPagination.perPage}`
|
||||||
|
);
|
||||||
if (response && response.transactions) {
|
if (response && response.transactions) {
|
||||||
this.transactions = response.transactions;
|
this.transactions = response.transactions;
|
||||||
loyaltyCardDetailLog.info(`Loaded ${this.transactions.length} transactions`);
|
this.txPagination.total = response.total || 0;
|
||||||
|
this.txPagination.page = page;
|
||||||
|
this.txPagination.pages = Math.ceil(this.txPagination.total / this.txPagination.perPage);
|
||||||
|
loyaltyCardDetailLog.info(`Loaded ${this.transactions.length} of ${this.txPagination.total} transactions (page ${page})`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loyaltyCardDetailLog.warn('Failed to load transactions:', error.message);
|
loyaltyCardDetailLog.warn('Failed to load transactions:', error.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
txPreviousPage() {
|
||||||
|
if (this.txPagination.page > 1) this.loadTransactions(this.txPagination.page - 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
txNextPage() {
|
||||||
|
if (this.txPagination.page < this.txPagination.pages) this.loadTransactions(this.txPagination.page + 1);
|
||||||
|
},
|
||||||
|
|
||||||
formatNumber(num) {
|
formatNumber(num) {
|
||||||
return num == null ? '0' : new Intl.NumberFormat('en-US').format(num);
|
return num == null ? '0' : new Intl.NumberFormat('en-US').format(num);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -151,6 +151,23 @@
|
|||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div x-show="txPagination.pages > 1" class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||||
|
<button @click="txPreviousPage()" :disabled="txPagination.page <= 1"
|
||||||
|
type="button"
|
||||||
|
class="px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50">
|
||||||
|
{{ _('loyalty.common.previous') }}
|
||||||
|
</button>
|
||||||
|
<span class="text-sm text-gray-500 dark:text-gray-400"
|
||||||
|
x-text="'{{ _('loyalty.store.card_detail.page_x_of_y') }}'.replace('{page}', txPagination.page).replace('{pages}', txPagination.pages)">
|
||||||
|
</span>
|
||||||
|
<button @click="txNextPage()" :disabled="txPagination.page >= txPagination.pages"
|
||||||
|
type="button"
|
||||||
|
class="px-3 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50">
|
||||||
|
{{ _('loyalty.common.next') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user