Add Recent Transactions pagination and table view to with filtering

Convert the Recent Transactions card from a list view to a paginated table
  with enhanced filtering capabilities for super users.

Frontend changes:
  - Replace q-list with q-table for better data presentation
  - Add pagination with configurable page size (default: 20 items)
  - Add transaction filter dropdown for super users to filter by username
  - Define table columns: Status, Date, Description, User, Amount, Fiat, Reference
  - Implement prev/next page navigation with page info display
  - Add filter controls with clear filter button

Backend changes (views_api.py):
  - Add pagination support with limit/offset parameters
  - Add filter_user_id parameter for filtering by user (super user only)
  - Enrich transaction entries with user_id and username from account lookups
  - Return paginated response with total count and pagination metadata

Database changes (crud.py):
  - Update get_all_journal_entries() to support offset parameter
  - Update get_journal_entries_by_user() to support offset parameter
  - Add count_all_journal_entries() for total count
  - Add count_journal_entries_by_user() for user-specific count

This improves the Recent Transactions UX by providing better organization, easier navigation through large transaction lists, and the ability for admins to filter transactions by user.
This commit is contained in:
padreug 2025-11-09 00:14:49 +01:00
parent 093cecbff2
commit f3d0d8652b
3 changed files with 172 additions and 47 deletions

View file

@ -17,6 +17,9 @@ window.app = Vue.createApp({
has_next: false,
has_prev: false
},
transactionFilter: {
user_id: null // For filtering by user
},
accounts: [],
currencies: [],
users: [],
@ -183,6 +186,17 @@ window.app = Vue.createApp({
}
},
computed: {
transactionColumns() {
return [
{ name: 'flag', label: 'Status', field: 'flag', align: 'left', sortable: true },
{ name: 'date', label: 'Date', field: 'entry_date', align: 'left', sortable: true },
{ name: 'description', label: 'Description', field: 'description', align: 'left', sortable: false },
{ name: 'username', label: 'User', field: 'username', align: 'left', sortable: true },
{ name: 'amount', label: 'Amount (sats)', field: 'amount', align: 'right', sortable: false },
{ name: 'fiat', label: 'Fiat Amount', field: 'fiat', align: 'right', sortable: false },
{ name: 'reference', label: 'Reference', field: 'reference', align: 'left', sortable: false }
]
},
expenseAccounts() {
return this.accounts.filter(a => a.account_type === 'expense')
},
@ -330,9 +344,15 @@ window.app = Vue.createApp({
const limit = parseInt(this.transactionPagination.limit) || 20
// Build query params with filter
let queryParams = `limit=${limit}&offset=${currentOffset}`
if (this.transactionFilter.user_id) {
queryParams += `&filter_user_id=${this.transactionFilter.user_id}`
}
const response = await LNbits.api.request(
'GET',
`/castle/api/v1/entries/user?limit=${limit}&offset=${currentOffset}`,
`/castle/api/v1/entries/user?${queryParams}`,
this.g.user.wallets[0].inkey
)
@ -346,6 +366,16 @@ window.app = Vue.createApp({
LNbits.utils.notifyApiError(error)
}
},
applyTransactionFilter() {
// Reset to first page when applying filter
this.transactionPagination.offset = 0
this.loadTransactions(0)
},
clearTransactionFilter() {
this.transactionFilter.user_id = null
this.transactionPagination.offset = 0
this.loadTransactions(0)
},
nextTransactionsPage() {
if (this.transactionPagination.has_next) {
const newOffset = this.transactionPagination.offset + this.transactionPagination.limit