mirror of
https://github.com/IT4Change/gradido.git
synced 2026-04-06 01:25:28 +00:00
Merge pull request #473 from gradido/pagination-buttons
feat: Pagination Buttons for Transaction List
This commit is contained in:
commit
fca3e1d4d9
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -212,7 +212,7 @@ jobs:
|
|||||||
report_name: Coverage Frontend
|
report_name: Coverage Frontend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 20
|
min_coverage: 21
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
@ -348,7 +348,7 @@ class AppRequestsController extends AppController
|
|||||||
$decay = true;
|
$decay = true;
|
||||||
$transactions = [];
|
$transactions = [];
|
||||||
$transactions_from_db = $stateUserTransactionsQuery->toArray();
|
$transactions_from_db = $stateUserTransactionsQuery->toArray();
|
||||||
|
|
||||||
if(count($transactions_from_db)) {
|
if(count($transactions_from_db)) {
|
||||||
if($orderDirection == 'DESC') {
|
if($orderDirection == 'DESC') {
|
||||||
$transactions_from_db = array_reverse($transactions_from_db);
|
$transactions_from_db = array_reverse($transactions_from_db);
|
||||||
|
|||||||
@ -35,7 +35,7 @@ const communityAPI = {
|
|||||||
balance: async (sessionId) => {
|
balance: async (sessionId) => {
|
||||||
return apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId)
|
return apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId)
|
||||||
},
|
},
|
||||||
transactions: async (sessionId, firstPage = 1, items = 1000, order = 'DESC') => {
|
transactions: async (sessionId, firstPage = 1, items = 5, order = 'DESC') => {
|
||||||
return apiGet(
|
return apiGet(
|
||||||
`${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`,
|
`${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`,
|
||||||
)
|
)
|
||||||
|
|||||||
53
frontend/src/components/PaginationButtons.spec.js
Normal file
53
frontend/src/components/PaginationButtons.spec.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import PaginationButtons from './PaginationButtons'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('PaginationButtons', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(PaginationButtons, { localVue })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the component', () => {
|
||||||
|
expect(wrapper.find('div.pagination-buttons').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has previous page button disabled by default', () => {
|
||||||
|
expect(wrapper.find('button.previous-page').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has bext page button disabled by default', () => {
|
||||||
|
expect(wrapper.find('button.next-page').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the text "1 / 1" by default"', () => {
|
||||||
|
expect(wrapper.find('p.text-center').text()).toBe('1 / 1')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with active buttons', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({
|
||||||
|
hasNext: true,
|
||||||
|
hasPrevious: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits show-previous when previous page button is clicked', () => {
|
||||||
|
wrapper.find('button.previous-page').trigger('click')
|
||||||
|
expect(wrapper.emitted('show-previous')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits show-next when next page button is clicked', () => {
|
||||||
|
wrapper.find('button.next-page').trigger('click')
|
||||||
|
expect(wrapper.emitted('show-next')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
30
frontend/src/components/PaginationButtons.vue
Normal file
30
frontend/src/components/PaginationButtons.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pagination-buttons">
|
||||||
|
<b-row class="m-4">
|
||||||
|
<b-col class="text-right">
|
||||||
|
<b-button class="previous-page" :disabled="!hasPrevious" @click="$emit('show-previous')">
|
||||||
|
<b-icon icon="chevron-left" variant="primary"></b-icon>
|
||||||
|
</b-button>
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="2">
|
||||||
|
<p class="text-center pt-2">{{ currentPage }} / {{ totalPages }}</p>
|
||||||
|
</b-col>
|
||||||
|
<b-col>
|
||||||
|
<b-button class="next-page" :disabled="!hasNext" @click="$emit('show-next')">
|
||||||
|
<b-icon icon="chevron-right" variant="primary"></b-icon>
|
||||||
|
</b-button>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PaginationButtons',
|
||||||
|
props: {
|
||||||
|
hasNext: { type: Boolean, default: false },
|
||||||
|
hasPrevious: { type: Boolean, default: false },
|
||||||
|
totalPages: { type: Number, default: 1 },
|
||||||
|
currentPage: { type: Number, default: 1 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -106,9 +106,13 @@ export default {
|
|||||||
this.$store.dispatch('logout')
|
this.$store.dispatch('logout')
|
||||||
this.$router.push('/login')
|
this.$router.push('/login')
|
||||||
},
|
},
|
||||||
async updateTransactions() {
|
async updateTransactions(pagination) {
|
||||||
this.pending = true
|
this.pending = true
|
||||||
const result = await communityAPI.transactions(this.$store.state.sessionId)
|
const result = await communityAPI.transactions(
|
||||||
|
this.$store.state.sessionId,
|
||||||
|
pagination.firstPage,
|
||||||
|
pagination.items,
|
||||||
|
)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.GdtBalance = Number(result.result.data.gdtSum)
|
this.GdtBalance = Number(result.result.data.gdtSum)
|
||||||
this.transactions = result.result.data.transactions
|
this.transactions = result.result.data.transactions
|
||||||
@ -129,7 +133,7 @@ export default {
|
|||||||
this.initScrollbar()
|
this.initScrollbar()
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.updateTransactions()
|
this.updateTransactions({ firstPage: 1, items: 5 })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -31,10 +31,10 @@
|
|||||||
<gdd-transaction-list
|
<gdd-transaction-list
|
||||||
v-if="showContext"
|
v-if="showContext"
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:max="5"
|
:page-size="5"
|
||||||
:timestamp="timestamp"
|
:timestamp="timestamp"
|
||||||
:transactionCount="transactionCount"
|
:transaction-count="transactionCount"
|
||||||
@update-transactions="$emit('update-transactions')"
|
@update-transactions="updateTransactions"
|
||||||
/>
|
/>
|
||||||
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
|
<gdd-transaction-list-footer v-if="showContext" :count="transactionCount" />
|
||||||
</b-container>
|
</b-container>
|
||||||
@ -116,6 +116,9 @@ export default {
|
|||||||
this.transactionData = EMPTY_TRANSACTION_DATA
|
this.transactionData = EMPTY_TRANSACTION_DATA
|
||||||
this.currentTransactionStep = 0
|
this.currentTransactionStep = 0
|
||||||
},
|
},
|
||||||
|
updateTransactions(pagination) {
|
||||||
|
this.$emit('update-transactions', pagination)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -202,16 +202,6 @@ describe('GddTransactionList', () => {
|
|||||||
expect(transaction.findAll('div').at(3).text()).toBe('decay')
|
expect(transaction.findAll('div').at(3).text()).toBe('decay')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('max property set to 2', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.setProps({ max: 2 })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('shows only 2 transactions', () => {
|
|
||||||
expect(wrapper.findAll('div.gdd-transaction-list-item')).toHaveLength(2)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with invalid transaction type', () => {
|
describe('with invalid transaction type', () => {
|
||||||
@ -234,5 +224,69 @@ describe('GddTransactionList', () => {
|
|||||||
expect(errorHandler).toHaveBeenCalled()
|
expect(errorHandler).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('pagination buttons', () => {
|
||||||
|
const transactions = Array.from({ length: 42 }, (_, idx) => {
|
||||||
|
return {
|
||||||
|
balance: '3.14',
|
||||||
|
date: '2021-04-29T17:26:40+00:00',
|
||||||
|
memo: 'Kreiszahl PI',
|
||||||
|
name: 'Euklid',
|
||||||
|
transaction_id: idx + 1,
|
||||||
|
type: 'receive',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let paginationButtons
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.setProps({
|
||||||
|
transactions,
|
||||||
|
transactionCount: 42,
|
||||||
|
showPagination: true,
|
||||||
|
})
|
||||||
|
paginationButtons = wrapper.find('div.pagination-buttons')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the pagination buttons', () => {
|
||||||
|
expect(paginationButtons.exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the previous button disabled', () => {
|
||||||
|
expect(paginationButtons.find('button.previous-page').attributes('disabled')).toBe(
|
||||||
|
'disabled',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the text "1 / 2"', () => {
|
||||||
|
expect(paginationButtons.find('p.text-center').text()).toBe('1 / 2')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits update-transactions when next button is clicked', async () => {
|
||||||
|
paginationButtons.find('button.next-page').trigger('click')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.emitted('update-transactions')[1]).toEqual([{ firstPage: 2, items: 25 }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows text "2 / 2" when next button is clicked', async () => {
|
||||||
|
paginationButtons.find('button.next-page').trigger('click')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(paginationButtons.find('p.text-center').text()).toBe('2 / 2')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has next-button disabled when next button is clicked', async () => {
|
||||||
|
paginationButtons.find('button.next-page').trigger('click')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(paginationButtons.find('button.next-page').attributes('disabled')).toBe('disabled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits update-transactions when preivous button is clicked after next buton', async () => {
|
||||||
|
paginationButtons.find('button.next-page').trigger('click')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
paginationButtons.find('button.previous-page').trigger('click')
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.emitted('update-transactions')[2]).toEqual([{ firstPage: 1, items: 25 }])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="gdd-transaction-list">
|
<div class="gdd-transaction-list">
|
||||||
<b-list-group>
|
<b-list-group>
|
||||||
<b-list-group-item
|
<b-list-group-item
|
||||||
v-for="item in transactions.slice(0, max)"
|
v-for="item in transactions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
style="background-color: #ebebeba3 !important"
|
style="background-color: #ebebeba3 !important"
|
||||||
>
|
>
|
||||||
@ -66,6 +66,15 @@
|
|||||||
</b-card>
|
</b-card>
|
||||||
</b-collapse>
|
</b-collapse>
|
||||||
</b-list-group-item>
|
</b-list-group-item>
|
||||||
|
<pagination-buttons
|
||||||
|
v-if="showPagination && transactionCount > pageSize"
|
||||||
|
:has-next="hasNext"
|
||||||
|
:has-previous="hasPrevious"
|
||||||
|
:total-pages="totalPages"
|
||||||
|
:current-page="currentPage"
|
||||||
|
@show-next="showNext"
|
||||||
|
@show-previous="showPrevious"
|
||||||
|
></pagination-buttons>
|
||||||
<div v-if="transactions.length === 0" class="mt-4 text-center">
|
<div v-if="transactions.length === 0" class="mt-4 text-center">
|
||||||
<span>{{ $t('transaction.nullTransactions') }}</span>
|
<span>{{ $t('transaction.nullTransactions') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -74,6 +83,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import PaginationButtons from '../../../components/PaginationButtons'
|
||||||
|
|
||||||
const iconsByType = {
|
const iconsByType = {
|
||||||
send: { icon: 'arrow-left-circle', classes: 'text-danger', operator: '-' },
|
send: { icon: 'arrow-left-circle', classes: 'text-danger', operator: '-' },
|
||||||
receive: { icon: 'arrow-right-circle', classes: 'gradido-global-color-accent', operator: '+' },
|
receive: { icon: 'arrow-right-circle', classes: 'gradido-global-color-accent', operator: '+' },
|
||||||
@ -83,11 +94,20 @@ const iconsByType = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'gdd-transaction-list',
|
name: 'gdd-transaction-list',
|
||||||
|
components: {
|
||||||
|
PaginationButtons,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
transactions: { default: () => [] },
|
transactions: { default: () => [] },
|
||||||
max: { type: Number, default: 1000 },
|
pageSize: { type: Number, default: 25 },
|
||||||
timestamp: { type: Number, default: 0 },
|
timestamp: { type: Number, default: 0 },
|
||||||
transactionCount: { type: Number, default: 0 },
|
transactionCount: { type: Number, default: 0 },
|
||||||
|
showPagination: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
timestamp: {
|
timestamp: {
|
||||||
@ -95,9 +115,23 @@ export default {
|
|||||||
handler: 'updateTransactions',
|
handler: 'updateTransactions',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
hasNext() {
|
||||||
|
return this.currentPage * this.pageSize < this.transactionCount
|
||||||
|
},
|
||||||
|
hasPrevious() {
|
||||||
|
return this.currentPage > 1
|
||||||
|
},
|
||||||
|
totalPages() {
|
||||||
|
return Math.ceil(this.transactionCount / this.pageSize)
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateTransactions() {
|
updateTransactions() {
|
||||||
this.$emit('update-transactions')
|
this.$emit('update-transactions', {
|
||||||
|
firstPage: this.currentPage,
|
||||||
|
items: this.pageSize,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
getProperties(item) {
|
getProperties(item) {
|
||||||
const type = iconsByType[item.type]
|
const type = iconsByType[item.type]
|
||||||
@ -112,6 +146,14 @@ export default {
|
|||||||
throwError(msg) {
|
throwError(msg) {
|
||||||
throw new Error(msg)
|
throw new Error(msg)
|
||||||
},
|
},
|
||||||
|
showNext() {
|
||||||
|
this.currentPage++
|
||||||
|
this.updateTransactions()
|
||||||
|
},
|
||||||
|
showPrevious() {
|
||||||
|
this.currentPage--
|
||||||
|
this.updateTransactions()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
:timestamp="timestamp"
|
:timestamp="timestamp"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
|
:show-pagination="true"
|
||||||
@update-transactions="updateTransactions"
|
@update-transactions="updateTransactions"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -32,8 +33,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateTransactions() {
|
updateTransactions(pagination) {
|
||||||
this.$emit('update-transactions')
|
this.$emit('update-transactions', pagination)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user