116 lines
3.9 KiB
Vue
116 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import { AccountApiClient, AccountHistoryItemType, type Account, type AccountHistoryItem, type AccountHistoryJournalEntryItem, type AccountHistoryValueRecordItem } from '@/api/account';
|
|
import type { PageRequest } from '@/api/pagination';
|
|
import { onMounted, ref, type Ref } from 'vue';
|
|
import ValueRecordHistoryItem from './ValueRecordHistoryItem.vue';
|
|
import JournalEntryHistoryItem from './JournalEntryHistoryItem.vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import AppButton from '../common/AppButton.vue';
|
|
import AppBadge from '../common/AppBadge.vue';
|
|
import HistoryItemDivider from './HistoryItemDivider.vue';
|
|
import { getSelectedProfile } from '@/api/profile';
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const props = defineProps<{ account: Account }>()
|
|
const historyItems: Ref<AccountHistoryItem[]> = ref([])
|
|
const canLoadMore = ref(true)
|
|
const nextPage: Ref<PageRequest> = ref({ page: 1, size: 10, sorts: [{ attribute: 'timestamp', dir: 'DESC' }] })
|
|
|
|
onMounted(async () => {
|
|
await loadNextPage()
|
|
})
|
|
|
|
async function loadNextPage() {
|
|
const api = new AccountApiClient(route)
|
|
try {
|
|
const page = await api.getHistory(props.account.id, nextPage.value)
|
|
historyItems.value.push(...page.items)
|
|
canLoadMore.value = !page.isLast
|
|
if (canLoadMore.value) {
|
|
nextPage.value.page++
|
|
}
|
|
} catch (err) {
|
|
console.error(err)
|
|
historyItems.value = []
|
|
canLoadMore.value = false
|
|
nextPage.value.page = 1
|
|
}
|
|
}
|
|
|
|
async function loadAll() {
|
|
while (canLoadMore.value) {
|
|
await loadNextPage()
|
|
}
|
|
}
|
|
|
|
function reload() {
|
|
nextPage.value.page = 1
|
|
canLoadMore.value = true
|
|
historyItems.value = []
|
|
loadNextPage()
|
|
}
|
|
|
|
function asVR(i: AccountHistoryItem): AccountHistoryValueRecordItem {
|
|
return i as AccountHistoryValueRecordItem
|
|
}
|
|
|
|
function asJE(i: AccountHistoryItem): AccountHistoryJournalEntryItem {
|
|
return i as AccountHistoryJournalEntryItem
|
|
}
|
|
|
|
function canView(item: AccountHistoryItem) {
|
|
return item.type === AccountHistoryItemType.JOURNAL_ENTRY ||
|
|
item.type === AccountHistoryItemType.VALUE_RECORD
|
|
}
|
|
|
|
function viewItem(item: AccountHistoryItem) {
|
|
const profile = getSelectedProfile(route)
|
|
if (item.type === AccountHistoryItemType.VALUE_RECORD) {
|
|
router.push(`/profiles/${profile}/accounts/${props.account.id}/value-records/${asVR(item).valueRecordId}`)
|
|
} else if (item.type === AccountHistoryItemType.JOURNAL_ENTRY) {
|
|
router.push(`/profiles/${getSelectedProfile(route)}/transactions/${asJE(item).transactionId}`)
|
|
}
|
|
}
|
|
|
|
defineExpose({ reload })
|
|
</script>
|
|
<template>
|
|
<div>
|
|
<div v-for="item, idx in historyItems" :key="idx">
|
|
<div class="history-item">
|
|
<!-- The main body on the left. -->
|
|
<div style="flex-grow: 1;">
|
|
<div class="font-mono font-size-xsmall text-muted" style="margin-bottom: 0.25rem;">
|
|
{{ new Date(item.timestamp).toLocaleString() }}
|
|
</div>
|
|
|
|
<ValueRecordHistoryItem v-if="item.type === AccountHistoryItemType.VALUE_RECORD" :item="asVR(item)"
|
|
:account-id="account.id" />
|
|
|
|
<JournalEntryHistoryItem v-if="item.type === AccountHistoryItemType.JOURNAL_ENTRY" :item="asJE(item)" />
|
|
</div>
|
|
<!-- A "view item" button on the right. -->
|
|
<div>
|
|
<AppButton icon="eye" size="sm" @click="viewItem(item)" v-if="canView(item)">View</AppButton>
|
|
</div>
|
|
</div>
|
|
|
|
<HistoryItemDivider v-if="idx + 1 < historyItems.length" />
|
|
</div>
|
|
<div class="align-center">
|
|
<AppButton size="md" @click="loadNextPage()" v-if="canLoadMore">Load more history...</AppButton>
|
|
<AppButton size="sm" @click="loadAll()" theme="secondary" v-if="canLoadMore">Load all</AppButton>
|
|
<AppBadge v-if="!canLoadMore">This is the start of this account's history.</AppBadge>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<style lang="css">
|
|
.history-item {
|
|
padding: 0 0.5rem;
|
|
margin: 1rem 0;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
</style>
|