From 23a5f0a96f84927daee9abe5bf31c6a3e613924e Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sat, 7 Mar 2026 01:04:47 +0800 Subject: [PATCH] display total transactions, total amount, average amount, median amount, minimum amount, and maximum amount in the Data Table tab of Insights Explorer page --- src/stores/explorer.ts | 57 +++++++++++++++++++ src/styles/desktop/global.scss | 10 ++++ .../insights/tabs/ExplorerDataTableTab.vue | 49 +++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/stores/explorer.ts b/src/stores/explorer.ts index dd5d729b..35eb07e0 100644 --- a/src/stores/explorer.ts +++ b/src/stores/explorer.ts @@ -111,6 +111,15 @@ export interface CategoriedTransactionExplorerDataItem extends SeriesInfo { value: number; } +export interface InsightsExplorerTransactionStatisticData { + totalCount: number; + totalAmount: number; + averageAmount: number; + medianAmount: number; + minimumAmount: number; + maximumAmount: number; +} + export const useExplorersStore = defineStore('explorers', () => { const settingsStore = useSettingsStore(); const userStore = useUserStore(); @@ -578,6 +587,53 @@ export const useExplorersStore = defineStore('explorers', () => { return result; }); + const filteredTransactionsInDataTableStatistic = computed(() => { + const statisticData: InsightsExplorerTransactionStatisticData = { + totalCount: 0, + totalAmount: 0, + averageAmount: 0, + medianAmount: 0, + minimumAmount: Number.MAX_SAFE_INTEGER, + maximumAmount: Number.MIN_SAFE_INTEGER + }; + + const sourceAmounts: number[] = []; + + for (const transaction of filteredTransactionsInDataTable.value) { + statisticData.totalCount++; + statisticData.totalAmount += transaction.sourceAmount; + + if (transaction.sourceAmount >= 0 && transaction.sourceAmount < statisticData.minimumAmount) { + statisticData.minimumAmount = transaction.sourceAmount; + } + + if (transaction.sourceAmount > statisticData.maximumAmount) { + statisticData.maximumAmount = transaction.sourceAmount; + } + + sourceAmounts.push(transaction.sourceAmount); + } + + if (statisticData.totalCount > 0) { + statisticData.averageAmount = Math.trunc(statisticData.totalAmount / statisticData.totalCount); + } + + if (sourceAmounts.length > 0) { + sourceAmounts.sort((a, b) => a - b); + statisticData.medianAmount = sourceAmounts[Math.floor(sourceAmounts.length / 2)] as number; + } + + if (statisticData.minimumAmount === Number.MAX_SAFE_INTEGER) { + statisticData.minimumAmount = 0; + } + + if (statisticData.maximumAmount === Number.MIN_SAFE_INTEGER) { + statisticData.maximumAmount = 0; + } + + return statisticData; + }); + const categoriedTransactions = computed>(() => { if (!allTransactions.value || allTransactions.value.length < 1) { return {}; @@ -1174,6 +1230,7 @@ export const useExplorersStore = defineStore('explorers', () => { insightsExplorerListStateInvalid, // computed filteredTransactionsInDataTable, + filteredTransactionsInDataTableStatistic, categoriedTransactionExplorerData, // functions updateTransactionExplorerInvalidState, diff --git a/src/styles/desktop/global.scss b/src/styles/desktop/global.scss index b5704e34..d80be5fc 100644 --- a/src/styles/desktop/global.scss +++ b/src/styles/desktop/global.scss @@ -407,6 +407,16 @@ html[dir="rtl"] .v-img.img-with-direction > img { } } +.table-tooltip { + &.v-tooltip > .v-overlay__content { + background-color: initial; + + .v-table { + box-shadow: 0 6px 16px 0 rgba(var(--v-shadow-key-umbra-color), var(--v-shadow-lg-opacity)), 0 0 transparent, 0 0 transparent; + } + } +} + .v-snackbar { .v-snackbar__content { color: rgb(var(--v-tooltip-color)); diff --git a/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue b/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue index ce2ca0cd..53ea7b0a 100644 --- a/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue +++ b/src/views/desktop/insights/tabs/ExplorerDataTableTab.vue @@ -25,6 +25,49 @@ :items="allPageCounts" v-model="currentExplorer.countPerPage" /> + +
+ {{ tt('Total Transactions') }} + + + + + {{ formatNumberToLocalizedNumerals(filteredTransactions.length) }} + + {{ tt('Total Amount') }} + + + + + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.totalAmount) }} + + + + + + {{ tt('Total Amount') }} + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.totalAmount) }} + + + {{ tt('Average Amount') }} + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.averageAmount) }} + + + {{ tt('Median Amount') }} + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.medianAmount) }} + + + {{ tt('Minimum Amount') }} + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.minimumAmount) }} + + + {{ tt('Maximum Amount') }} + {{ formatAmountToLocalizedNumeralsWithCurrency(filteredTransactionsStatistic.maximumAmount) }} + + + + +
@@ -126,7 +169,7 @@ import { useI18n } from '@/locales/helpers.ts'; import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; -import { useExplorersStore } from '@/stores/explorer.ts'; +import { type InsightsExplorerTransactionStatisticData, useExplorersStore } from '@/stores/explorer.ts'; import { type NameValue, type NameNumeralValue, itemAndIndex } from '@/core/base.ts'; import type { NumeralSystem } from '@/core/numeral.ts'; @@ -166,7 +209,8 @@ const { formatDateTimeToLongDateTime, formatDateTimeToGregorianDefaultDateTime, formatAmountToWesternArabicNumeralsWithoutDigitGrouping, - formatAmountToLocalizedNumeralsWithCurrency + formatAmountToLocalizedNumeralsWithCurrency, + formatNumberToLocalizedNumerals } = useI18n(); const settingsStore = useSettingsStore(); @@ -181,6 +225,7 @@ const defaultCurrency = computed(() => userStore.currentUserDefaultCurre const currentExplorer = computed(() => explorersStore.currentInsightsExplorer); const filteredTransactions = computed(() => explorersStore.filteredTransactionsInDataTable); +const filteredTransactionsStatistic = computed(() => explorersStore.filteredTransactionsInDataTableStatistic); const allDataTableQuerySources = computed(() => { const sources: NameValue[] = [];