refactor: lnbits-wallet-charts and clean up paymentFiltering (#3526)
This commit is contained in:
parent
3e0d0d9896
commit
faac56c14c
12 changed files with 314 additions and 357 deletions
|
|
@ -46,7 +46,7 @@
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="text-h3 q-my-none full-width">
|
<div class="text-h3 q-my-none full-width">
|
||||||
<strong
|
<strong
|
||||||
v-text="walletFormatBalance(this.g.wallet.sat)"
|
v-text="formatBalance(this.g.wallet.sat)"
|
||||||
class="text-no-wrap"
|
class="text-no-wrap"
|
||||||
:style="{fontSize: 'clamp(0.75rem, 10vw, 3rem)', display: 'inline-block', maxWidth: '100%'}"
|
:style="{fontSize: 'clamp(0.75rem, 10vw, 3rem)', display: 'inline-block', maxWidth: '100%'}"
|
||||||
></strong>
|
></strong>
|
||||||
|
|
@ -179,10 +179,9 @@
|
||||||
<q-card class="wallet-card">
|
<q-card class="wallet-card">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<lnbits-payment-list
|
<lnbits-payment-list
|
||||||
@filter-changed="handleFilterChange"
|
|
||||||
:update="updatePayments"
|
:update="updatePayments"
|
||||||
:mobile-simple="g.mobileSimple"
|
|
||||||
:expand-details="expandDetails"
|
:expand-details="expandDetails"
|
||||||
|
:payment-filter="paymentFilter"
|
||||||
></lnbits-payment-list>
|
></lnbits-payment-list>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
@ -501,8 +500,7 @@
|
||||||
<div class="col-md-4 col-sm-12">
|
<div class="col-md-4 col-sm-12">
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
dense
|
dense
|
||||||
@click="saveChartsPreferences"
|
v-model="chartConfig.showBalanceChart"
|
||||||
v-model="chartConfig.showBalance"
|
|
||||||
:label="$t('payments_balance_chart')"
|
:label="$t('payments_balance_chart')"
|
||||||
>
|
>
|
||||||
</q-checkbox>
|
</q-checkbox>
|
||||||
|
|
@ -511,8 +509,7 @@
|
||||||
<div class="col-md-4 col-sm-12">
|
<div class="col-md-4 col-sm-12">
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
dense
|
dense
|
||||||
@click="saveChartsPreferences"
|
v-model="chartConfig.showBalanceInOutChart"
|
||||||
v-model="chartConfig.showBalanceInOut"
|
|
||||||
:label="$t('payments_balance_in_out_chart')"
|
:label="$t('payments_balance_in_out_chart')"
|
||||||
>
|
>
|
||||||
</q-checkbox>
|
</q-checkbox>
|
||||||
|
|
@ -520,8 +517,7 @@
|
||||||
<div class="col-md-4 col-sm-12">
|
<div class="col-md-4 col-sm-12">
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
dense
|
dense
|
||||||
@click="saveChartsPreferences"
|
v-model="chartConfig.showPaymentInOutChart"
|
||||||
v-model="chartConfig.showPaymentCountInOut"
|
|
||||||
:label="$t('payments_count_in_out_chart')"
|
:label="$t('payments_count_in_out_chart')"
|
||||||
>
|
>
|
||||||
</q-checkbox>
|
</q-checkbox>
|
||||||
|
|
@ -550,37 +546,10 @@
|
||||||
</a>
|
</a>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
<div
|
<lnbits-wallet-charts
|
||||||
v-show="chartDataPointCount"
|
:payment-filter="paymentFilter"
|
||||||
class="col-12 col-md-5 q-gutter-y-md"
|
:chart-config="chartConfig"
|
||||||
>
|
></lnbits-wallet-charts>
|
||||||
<q-card v-if="chartConfig.showBalance">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div style="height: 200px" class="q-pa-sm">
|
|
||||||
<canvas ref="walletBalanceChart"></canvas>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card v-if="chartConfig.showBalanceInOut">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div style="height: 200px" class="q-pa-sm">
|
|
||||||
<canvas ref="walletBalanceInOut"></canvas>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card v-if="chartConfig.showPaymentCountInOut">
|
|
||||||
<q-card-section class="q-pa-none">
|
|
||||||
<div style="height: 200px" class="q-pa-sm">
|
|
||||||
<canvas ref="walletPaymentsInOut"></canvas>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div v-if="hasChartActive && !chartDataPointCount">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section> No chart data available</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -864,7 +833,7 @@
|
||||||
<div class="column content-center text-center q-mb-md">
|
<div class="column content-center text-center q-mb-md">
|
||||||
<div v-if="!isFiatPriority">
|
<div v-if="!isFiatPriority">
|
||||||
<h4 class="q-my-none text-bold">
|
<h4 class="q-my-none text-bold">
|
||||||
<span v-text="walletFormatBalance(parse.invoice.sat)"></span>
|
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
@ -886,9 +855,7 @@
|
||||||
<div v-if="g.fiatTracking">
|
<div v-if="g.fiatTracking">
|
||||||
<div v-if="isFiatPriority">
|
<div v-if="isFiatPriority">
|
||||||
<h5 class="q-my-none text-bold">
|
<h5 class="q-my-none text-bold">
|
||||||
<span
|
<span v-text="formatBalance(parse.invoice.sat)"></span>
|
||||||
v-text="walletFormatBalance(parse.invoice.sat)"
|
|
||||||
></span>
|
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div v-else style="opacity: 0.75">
|
<div v-else style="opacity: 0.75">
|
||||||
|
|
|
||||||
2
lnbits/static/bundle-components.min.js
vendored
2
lnbits/static/bundle-components.min.js
vendored
File diff suppressed because one or more lines are too long
2
lnbits/static/bundle.min.js
vendored
2
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,6 @@
|
||||||
window.app.component('lnbits-payment-list', {
|
window.app.component('lnbits-payment-list', {
|
||||||
template: '#lnbits-payment-list',
|
template: '#lnbits-payment-list',
|
||||||
props: ['update', 'lazy', 'wallet'],
|
props: ['update', 'lazy', 'wallet', 'paymentFilter'],
|
||||||
mixins: [window.windowMixin],
|
mixins: [window.windowMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -31,9 +31,6 @@ window.app.component('lnbits-payment-list', {
|
||||||
rowsNumber: 10
|
rowsNumber: 10
|
||||||
},
|
},
|
||||||
search: '',
|
search: '',
|
||||||
filter: {
|
|
||||||
'status[ne]': 'failed'
|
|
||||||
},
|
|
||||||
loading: false
|
loading: false
|
||||||
},
|
},
|
||||||
searchDate: {from: null, to: null},
|
searchDate: {from: null, to: null},
|
||||||
|
|
@ -156,24 +153,27 @@ window.app.component('lnbits-payment-list', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.searchDate.from) {
|
if (this.searchDate.from) {
|
||||||
this.paymentsTable.filter['time[ge]'] =
|
this.paymentFilter['time[ge]'] = this.searchDate.from + 'T00:00:00'
|
||||||
this.searchDate.from + 'T00:00:00'
|
|
||||||
}
|
}
|
||||||
if (this.searchDate.to) {
|
if (this.searchDate.to) {
|
||||||
this.paymentsTable.filter['time[le]'] = this.searchDate.to + 'T23:59:59'
|
this.paymentFilter['time[le]'] = this.searchDate.to + 'T23:59:59'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchPayments()
|
this.fetchPayments()
|
||||||
},
|
},
|
||||||
clearDateSeach() {
|
clearDateSeach() {
|
||||||
this.searchDate = {from: null, to: null}
|
this.searchDate = {from: null, to: null}
|
||||||
delete this.paymentsTable.filter['time[ge]']
|
delete this.paymentFilter['time[ge]']
|
||||||
delete this.paymentsTable.filter['time[le]']
|
delete this.paymentFilter['time[le]']
|
||||||
this.fetchPayments()
|
this.fetchPayments()
|
||||||
},
|
},
|
||||||
fetchPayments(props) {
|
fetchPayments(props) {
|
||||||
this.$emit('filter-changed', {...this.paymentsTable.filter})
|
const params = LNbits.utils.prepareFilterQuery(
|
||||||
const params = LNbits.utils.prepareFilterQuery(this.paymentsTable, props)
|
this.paymentsTable,
|
||||||
|
props,
|
||||||
|
this.paymentFilter
|
||||||
|
)
|
||||||
|
this.paymentsTable.loading = true
|
||||||
return LNbits.api
|
return LNbits.api
|
||||||
.getPayments(this.currentWallet, params)
|
.getPayments(this.currentWallet, params)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
@ -326,34 +326,34 @@ window.app.component('lnbits-payment-list', {
|
||||||
},
|
},
|
||||||
handleFilterChanged() {
|
handleFilterChanged() {
|
||||||
const {success, pending, failed, incoming, outgoing} = this.searchStatus
|
const {success, pending, failed, incoming, outgoing} = this.searchStatus
|
||||||
|
let paymentFilter = this.paymentFilter || {}
|
||||||
delete this.paymentsTable.filter['status[ne]']
|
delete paymentFilter['status[ne]']
|
||||||
delete this.paymentsTable.filter['status[eq]']
|
delete paymentFilter['status[eq]']
|
||||||
if (success && pending && failed) {
|
if (success && pending && failed) {
|
||||||
// No status filter
|
// No status filter
|
||||||
} else if (success && pending) {
|
} else if (success && pending) {
|
||||||
this.paymentsTable.filter['status[ne]'] = 'failed'
|
paymentFilter['status[ne]'] = 'failed'
|
||||||
} else if (success && failed) {
|
} else if (success && failed) {
|
||||||
this.paymentsTable.filter['status[ne]'] = 'pending'
|
paymentFilter['status[ne]'] = 'pending'
|
||||||
} else if (failed && pending) {
|
} else if (failed && pending) {
|
||||||
this.paymentsTable.filter['status[ne]'] = 'success'
|
paymentFilter['status[ne]'] = 'success'
|
||||||
} else if (success) {
|
} else if (success && !pending && !failed) {
|
||||||
this.paymentsTable.filter['status[eq]'] = 'success'
|
paymentFilter['status[eq]'] = 'success'
|
||||||
} else if (pending) {
|
} else if (pending && !success && !failed) {
|
||||||
this.paymentsTable.filter['status[eq]'] = 'pending'
|
paymentFilter['status[eq]'] = 'pending'
|
||||||
} else if (failed) {
|
} else if (failed && !success && !pending) {
|
||||||
this.paymentsTable.filter['status[eq]'] = 'failed'
|
paymentFilter['status[eq]'] = 'failed'
|
||||||
}
|
}
|
||||||
|
delete paymentFilter['amount[ge]']
|
||||||
delete this.paymentsTable.filter['amount[ge]']
|
delete paymentFilter['amount[le]']
|
||||||
delete this.paymentsTable.filter['amount[le]']
|
if ((incoming && outgoing) || (!incoming && !outgoing)) {
|
||||||
if (incoming && outgoing) {
|
|
||||||
// do nothing
|
// do nothing
|
||||||
} else if (incoming) {
|
} else if (incoming && !outgoing) {
|
||||||
this.paymentsTable.filter['amount[ge]'] = 0
|
paymentFilter['amount[ge]'] = 0
|
||||||
} else if (outgoing) {
|
} else if (outgoing && !incoming) {
|
||||||
this.paymentsTable.filter['amount[le]'] = 0
|
paymentFilter['amount[le]'] = 0
|
||||||
}
|
}
|
||||||
|
this.paymentFilter = paymentFilter
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
226
lnbits/static/js/components/lnbits-wallet-charts.js
Normal file
226
lnbits/static/js/components/lnbits-wallet-charts.js
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
window.app.component('lnbits-wallet-charts', {
|
||||||
|
template: '#lnbits-wallet-charts',
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
props: ['paymentFilter', 'chartConfig'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
debounceTimeoutValue: 1337,
|
||||||
|
debounceTimeout: null,
|
||||||
|
chartData: [],
|
||||||
|
chartDataPointCount: 0,
|
||||||
|
walletBalanceChart: null,
|
||||||
|
walletBalanceInOut: null,
|
||||||
|
walletPaymentInOut: null,
|
||||||
|
colorPrimary: Quasar.colors.changeAlpha(
|
||||||
|
Quasar.colors.getPaletteColor('primary'),
|
||||||
|
0.3
|
||||||
|
),
|
||||||
|
colorSecondary: Quasar.colors.changeAlpha(
|
||||||
|
Quasar.colors.getPaletteColor('secondary'),
|
||||||
|
0.3
|
||||||
|
),
|
||||||
|
barOptions: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
paymentFilter: {
|
||||||
|
deep: true,
|
||||||
|
handler() {
|
||||||
|
this.changeCharts()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chartConfig: {
|
||||||
|
deep: true,
|
||||||
|
handler(val) {
|
||||||
|
this.$q.localStorage.setItem('lnbits.wallets.chartConfig', val)
|
||||||
|
this.changeCharts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// Debounce chart drawing and data fetching, if its called multiple times in quick succession
|
||||||
|
// (e.g. when changing filters) chart.js will error because of a race condition trying to
|
||||||
|
// destroy and redraw charts at the same time
|
||||||
|
changeCharts() {
|
||||||
|
if (this.debounceTimeout) {
|
||||||
|
clearTimeout(this.debounceTimeout)
|
||||||
|
}
|
||||||
|
this.debounceTimeout = setTimeout(async () => {
|
||||||
|
await this.fetchChartData()
|
||||||
|
this.drawCharts()
|
||||||
|
}, this.debounceTimeoutValue)
|
||||||
|
},
|
||||||
|
filterChartData() {
|
||||||
|
const timeFrom = this.paymentFilter['time[ge]'] + 'T00:00:00'
|
||||||
|
const timeTo = this.paymentFilter['time[le]'] + 'T23:59:59'
|
||||||
|
let totalBalance = 0
|
||||||
|
let data = this.chartData.map(p => {
|
||||||
|
if (this.paymentFilter['amount[ge]'] !== undefined) {
|
||||||
|
totalBalance += p.balance_in
|
||||||
|
return {...p, balance: totalBalance, balance_out: 0, count_out: 0}
|
||||||
|
}
|
||||||
|
if (this.paymentFilter['amount[le]'] !== undefined) {
|
||||||
|
totalBalance -= p.balance_out
|
||||||
|
return {...p, balance: totalBalance, balance_in: 0, count_in: 0}
|
||||||
|
}
|
||||||
|
return {...p}
|
||||||
|
})
|
||||||
|
data = data.filter(p => {
|
||||||
|
if (this.paymentFilter['time[ge]'] && this.paymentFilter['time[le]']) {
|
||||||
|
return p.date >= timeFrom && p.date <= timeTo
|
||||||
|
}
|
||||||
|
if (this.paymentFilter['time[ge]']) {
|
||||||
|
return p.date >= timeFrom
|
||||||
|
}
|
||||||
|
if (this.paymentFilter['time[le]']) {
|
||||||
|
return p.date <= timeTo
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
const labels = data.map(s =>
|
||||||
|
new Date(s.date).toLocaleString('default', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this.chartDataPointCount = data.length
|
||||||
|
return {data, labels}
|
||||||
|
},
|
||||||
|
drawBalanceInOutChart(data, labels) {
|
||||||
|
if (this.walletBalanceInOut) {
|
||||||
|
this.walletBalanceInOut.destroy()
|
||||||
|
}
|
||||||
|
this.walletBalanceInOut = new Chart(
|
||||||
|
this.$refs.walletBalanceInOut.getContext('2d'),
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
options: this.barOptions,
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Balance In',
|
||||||
|
borderRadius: 5,
|
||||||
|
data: data.map(s => s.balance_in),
|
||||||
|
backgroundColor: this.colorPrimary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Balance Out',
|
||||||
|
borderRadius: 5,
|
||||||
|
data: data.map(s => s.balance_out),
|
||||||
|
backgroundColor: this.colorSecondary
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
drawPaymentInOut(data, labels) {
|
||||||
|
if (this.walletPaymentInOut) {
|
||||||
|
this.walletPaymentInOut.destroy()
|
||||||
|
}
|
||||||
|
this.walletPaymentInOut = new Chart(
|
||||||
|
this.$refs.walletPaymentInOut.getContext('2d'),
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
options: this.barOptions,
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Payments In',
|
||||||
|
data: data.map(s => s.count_in),
|
||||||
|
backgroundColor: this.colorPrimary
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Payments Out',
|
||||||
|
data: data.map(s => -s.count_out),
|
||||||
|
backgroundColor: this.colorSecondary
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
drawBalanceChart(data, labels) {
|
||||||
|
const ref = this.$refs.walletBalanceChart
|
||||||
|
if (this.walletBalanceChart) {
|
||||||
|
this.walletBalanceChart.destroy()
|
||||||
|
}
|
||||||
|
this.walletBalanceChart = new Chart(ref.getContext('2d'), {
|
||||||
|
type: 'line',
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'Balance',
|
||||||
|
data: data.map(s => s.balance),
|
||||||
|
pointStyle: false,
|
||||||
|
backgroundColor: this.colorPrimary,
|
||||||
|
borderColor: this.colorPrimary,
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.7,
|
||||||
|
fill: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Fees',
|
||||||
|
data: data.map(s => s.fee),
|
||||||
|
pointStyle: false,
|
||||||
|
backgroundColor: this.colorSecondary,
|
||||||
|
borderColor: this.colorSecondary,
|
||||||
|
borderWidth: 1,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.7,
|
||||||
|
fill: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
drawCharts() {
|
||||||
|
const {data, labels} = this.filterChartData()
|
||||||
|
if (this.chartConfig.showBalanceChart) {
|
||||||
|
this.drawBalanceChart(data, labels)
|
||||||
|
}
|
||||||
|
if (this.chartConfig.showBalanceInOutChart) {
|
||||||
|
this.drawBalanceInOutChart(data, labels)
|
||||||
|
}
|
||||||
|
if (this.chartConfig.showPaymentInOutChart) {
|
||||||
|
this.drawPaymentInOut(data, labels)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetchChartData() {
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'GET',
|
||||||
|
`/api/v1/payments/stats/daily?wallet_id=${this.g.wallet.id}`
|
||||||
|
)
|
||||||
|
this.chartData = data
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
await this.fetchChartData()
|
||||||
|
this.drawCharts()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -87,8 +87,8 @@ window._lnbitsUtils = {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
prepareFilterQuery(tableConfig, props) {
|
prepareFilterQuery(tableConfig, props, filter) {
|
||||||
tableConfig.filter = tableConfig.filter || {}
|
tableConfig.filter = filter || tableConfig.filter || {}
|
||||||
if (props) {
|
if (props) {
|
||||||
tableConfig.pagination = props.pagination
|
tableConfig.pagination = props.pagination
|
||||||
Object.assign(tableConfig.filter, props.filter)
|
Object.assign(tableConfig.filter, props.filter)
|
||||||
|
|
|
||||||
|
|
@ -109,23 +109,21 @@ window.WalletPageLogic = {
|
||||||
name: null,
|
name: null,
|
||||||
currency: null
|
currency: null
|
||||||
},
|
},
|
||||||
walletBalanceChart: null,
|
|
||||||
inkeyHidden: true,
|
|
||||||
adminkeyHidden: true,
|
|
||||||
walletIdHidden: true,
|
|
||||||
hasNfc: false,
|
hasNfc: false,
|
||||||
nfcReaderAbortController: null,
|
nfcReaderAbortController: null,
|
||||||
isFiatPriority: false,
|
isFiatPriority: false,
|
||||||
formattedFiatAmount: 0,
|
formattedFiatAmount: 0,
|
||||||
formattedExchange: null,
|
formattedExchange: null,
|
||||||
chartData: [],
|
paymentFilter: {
|
||||||
chartDataPointCount: 0,
|
'status[ne]': 'failed'
|
||||||
chartConfig: {
|
|
||||||
showBalance: true,
|
|
||||||
showBalanceInOut: true,
|
|
||||||
showPaymentCountInOut: true
|
|
||||||
},
|
},
|
||||||
paymentsFilter: {}
|
chartConfig: Quasar.LocalStorage.getItem(
|
||||||
|
'lnbits.wallets.chartConfig'
|
||||||
|
) || {
|
||||||
|
showPaymentInOutChart: true,
|
||||||
|
showBalanceChart: true,
|
||||||
|
showBalanceInOutChart: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -164,16 +162,6 @@ window.WalletPageLogic = {
|
||||||
},
|
},
|
||||||
formattedSatAmount() {
|
formattedSatAmount() {
|
||||||
return LNbits.utils.formatMsat(this.receive.amountMsat) + ' sat'
|
return LNbits.utils.formatMsat(this.receive.amountMsat) + ' sat'
|
||||||
},
|
|
||||||
wallet() {
|
|
||||||
return this.g.wallet
|
|
||||||
},
|
|
||||||
hasChartActive() {
|
|
||||||
return (
|
|
||||||
this.chartConfig.showBalance ||
|
|
||||||
this.chartConfig.showBalanceInOut ||
|
|
||||||
this.chartConfig.showPaymentCountInOut
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -820,256 +808,6 @@ window.WalletPageLogic = {
|
||||||
this.g.fiatTracking = false
|
this.g.fiatTracking = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
walletFormatBalance(amount) {
|
|
||||||
if (LNBITS_DENOMINATION != 'sats') {
|
|
||||||
return LNbits.utils.formatCurrency(amount / 100, LNBITS_DENOMINATION)
|
|
||||||
} else {
|
|
||||||
return LNbits.utils.formatSat(amount) + ' sats'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleFilterChange(value = {}) {
|
|
||||||
if (
|
|
||||||
this.paymentsFilter['time[ge]'] !== value['time[ge]'] ||
|
|
||||||
this.paymentsFilter['time[le]'] !== value['time[le]'] ||
|
|
||||||
this.paymentsFilter['amount[ge]'] !== value['amount[ge]'] ||
|
|
||||||
this.paymentsFilter['amount[le]'] !== value['amount[le]']
|
|
||||||
) {
|
|
||||||
this.refreshCharts()
|
|
||||||
}
|
|
||||||
this.paymentsFilter = value
|
|
||||||
},
|
|
||||||
async fetchChartData() {
|
|
||||||
if (this.g.mobileSimple) {
|
|
||||||
this.chartConfig = {}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.hasChartActive) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {data} = await LNbits.api.request(
|
|
||||||
'GET',
|
|
||||||
`/api/v1/payments/stats/daily?wallet_id=${this.g.wallet.id}`
|
|
||||||
)
|
|
||||||
this.chartData = data
|
|
||||||
this.refreshCharts()
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
LNbits.utils.notifyApiError(error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filterChartData(data) {
|
|
||||||
const timeFrom = this.paymentsFilter['time[ge]'] + 'T00:00:00'
|
|
||||||
const timeTo = this.paymentsFilter['time[le]'] + 'T23:59:59'
|
|
||||||
|
|
||||||
let totalBalance = 0
|
|
||||||
data = data.map(p => {
|
|
||||||
if (this.paymentsFilter['amount[ge]'] !== undefined) {
|
|
||||||
totalBalance += p.balance_in
|
|
||||||
return {...p, balance: totalBalance, balance_out: 0, count_out: 0}
|
|
||||||
}
|
|
||||||
if (this.paymentsFilter['amount[le]'] !== undefined) {
|
|
||||||
totalBalance -= p.balance_out
|
|
||||||
return {...p, balance: totalBalance, balance_in: 0, count_in: 0}
|
|
||||||
}
|
|
||||||
return {...p}
|
|
||||||
})
|
|
||||||
data = data.filter(p => {
|
|
||||||
if (
|
|
||||||
this.paymentsFilter['time[ge]'] &&
|
|
||||||
this.paymentsFilter['time[le]']
|
|
||||||
) {
|
|
||||||
return p.date >= timeFrom && p.date <= timeTo
|
|
||||||
}
|
|
||||||
if (this.paymentsFilter['time[ge]']) {
|
|
||||||
return p.date >= timeFrom
|
|
||||||
}
|
|
||||||
if (this.paymentsFilter['time[le]']) {
|
|
||||||
return p.date <= timeTo
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
const labels = data.map(s =>
|
|
||||||
new Date(s.date).toLocaleString('default', {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
this.chartDataPointCount = data.length
|
|
||||||
return {data, labels}
|
|
||||||
},
|
|
||||||
refreshCharts() {
|
|
||||||
const originalChartConfig = this.chartConfig || {}
|
|
||||||
this.chartConfig = {}
|
|
||||||
setTimeout(() => {
|
|
||||||
const chartConfig =
|
|
||||||
this.$q.localStorage.getItem('lnbits.wallets.chartConfig') ||
|
|
||||||
originalChartConfig
|
|
||||||
this.chartConfig = {...originalChartConfig, ...chartConfig}
|
|
||||||
}, 10)
|
|
||||||
setTimeout(() => {
|
|
||||||
this.drawCharts(this.chartData)
|
|
||||||
}, 100)
|
|
||||||
},
|
|
||||||
drawCharts(allData) {
|
|
||||||
try {
|
|
||||||
const {data, labels} = this.filterChartData(allData)
|
|
||||||
if (this.chartConfig.showBalance) {
|
|
||||||
if (this.walletBalanceChart) {
|
|
||||||
this.walletBalanceChart.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.walletBalanceChart = new Chart(
|
|
||||||
this.$refs.walletBalanceChart.getContext('2d'),
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Balance',
|
|
||||||
data: data.map(s => s.balance),
|
|
||||||
pointStyle: false,
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('primary'),
|
|
||||||
0.3
|
|
||||||
),
|
|
||||||
borderColor: Quasar.colors.getPaletteColor('primary'),
|
|
||||||
borderWidth: 2,
|
|
||||||
fill: true,
|
|
||||||
tension: 0.7,
|
|
||||||
fill: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Fees',
|
|
||||||
data: data.map(s => s.fee),
|
|
||||||
pointStyle: false,
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('secondary'),
|
|
||||||
0.3
|
|
||||||
),
|
|
||||||
borderColor: Quasar.colors.getPaletteColor('secondary'),
|
|
||||||
borderWidth: 1,
|
|
||||||
fill: true,
|
|
||||||
tension: 0.7,
|
|
||||||
fill: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.chartConfig.showBalanceInOut) {
|
|
||||||
if (this.walletBalanceInOut) {
|
|
||||||
this.walletBalanceInOut.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.walletBalanceInOut = new Chart(
|
|
||||||
this.$refs.walletBalanceInOut.getContext('2d'),
|
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
stacked: true
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
stacked: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Balance In',
|
|
||||||
borderRadius: 5,
|
|
||||||
data: data.map(s => s.balance_in),
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('primary'),
|
|
||||||
0.3
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Balance Out',
|
|
||||||
borderRadius: 5,
|
|
||||||
data: data.map(s => s.balance_out),
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('secondary'),
|
|
||||||
0.3
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.chartConfig.showPaymentCountInOut) {
|
|
||||||
if (this.walletPaymentsInOut) {
|
|
||||||
this.walletPaymentsInOut.destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.walletPaymentsInOut = new Chart(
|
|
||||||
this.$refs.walletPaymentsInOut.getContext('2d'),
|
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
stacked: true
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
stacked: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Payments In',
|
|
||||||
data: data.map(s => s.count_in),
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('primary'),
|
|
||||||
0.3
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Payments Out',
|
|
||||||
data: data.map(s => -s.count_out),
|
|
||||||
backgroundColor: Quasar.colors.changeAlpha(
|
|
||||||
Quasar.colors.getPaletteColor('secondary'),
|
|
||||||
0.3
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveChartsPreferences() {
|
|
||||||
this.$q.localStorage.set('lnbits.wallets.chartConfig', this.chartConfig)
|
|
||||||
this.refreshCharts()
|
|
||||||
},
|
|
||||||
updatePaylinks() {
|
updatePaylinks() {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request(
|
.request(
|
||||||
|
|
@ -1122,11 +860,6 @@ window.WalletPageLogic = {
|
||||||
this.parse.show = true
|
this.parse.show = true
|
||||||
}
|
}
|
||||||
this.createdTasks()
|
this.createdTasks()
|
||||||
try {
|
|
||||||
this.fetchChartData()
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Chart creation failed: ${error}`)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'g.updatePayments'(newVal, oldVal) {
|
'g.updatePayments'(newVal, oldVal) {
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@
|
||||||
"js/components/admin/lnbits-admin-site-customisation.js",
|
"js/components/admin/lnbits-admin-site-customisation.js",
|
||||||
"js/components/admin/lnbits-admin-assets-config.js",
|
"js/components/admin/lnbits-admin-assets-config.js",
|
||||||
"js/components/admin/lnbits-admin-audit.js",
|
"js/components/admin/lnbits-admin-audit.js",
|
||||||
|
"js/components/lnbits-wallet-charts.js",
|
||||||
"js/components/lnbits-wallet-api-docs.js",
|
"js/components/lnbits-wallet-api-docs.js",
|
||||||
"js/components/lnbits-wallet-new.js",
|
"js/components/lnbits-wallet-new.js",
|
||||||
"js/components/lnbits-wallet-share.js",
|
"js/components/lnbits-wallet-share.js",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ include('components/lnbits-language-dropdown.vue') %} {%
|
||||||
include('components/lnbits-payment-list.vue') %} {%
|
include('components/lnbits-payment-list.vue') %} {%
|
||||||
include('components/lnbits-wallet-new.vue') %} {%
|
include('components/lnbits-wallet-new.vue') %} {%
|
||||||
include('components/lnbits-wallet-api-docs.vue') %} {%
|
include('components/lnbits-wallet-api-docs.vue') %} {%
|
||||||
include('components/lnbits-wallet-share.vue') %}
|
include('components/lnbits-wallet-share.vue') %} {%
|
||||||
|
include('components/lnbits-wallet-charts.vue') %}
|
||||||
|
|
||||||
<template id="lnbits-manage">
|
<template id="lnbits-manage">
|
||||||
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">
|
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@
|
||||||
:row-key="paymentTableRowKey"
|
:row-key="paymentTableRowKey"
|
||||||
:columns="paymentsTable.columns"
|
:columns="paymentsTable.columns"
|
||||||
:no-data-label="$t('no_transactions')"
|
:no-data-label="$t('no_transactions')"
|
||||||
:filter="paymentsTable.filter"
|
:filter="paymentFilter"
|
||||||
:loading="paymentsTable.loading"
|
:loading="paymentsTable.loading"
|
||||||
:hide-header="g.mobileSimple"
|
:hide-header="g.mobileSimple"
|
||||||
:hide-bottom="g.mobileSimple"
|
:hide-bottom="g.mobileSimple"
|
||||||
|
|
|
||||||
28
lnbits/templates/components/lnbits-wallet-charts.vue
Normal file
28
lnbits/templates/components/lnbits-wallet-charts.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template id="lnbits-wallet-charts">
|
||||||
|
<div
|
||||||
|
:style="!chartDataPointCount ? 'display:none;' : ''"
|
||||||
|
class="col-12 col-md-5 q-gutter-y-md"
|
||||||
|
>
|
||||||
|
<q-card :style="chartConfig.showBalanceChart ? '' : 'display: none;'">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<div style="height: 200px" class="q-pa-sm">
|
||||||
|
<canvas ref="walletBalanceChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
<q-card :style="chartConfig.showBalanceInOutChart ? '' : 'display: none;'">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<div style="height: 200px" class="q-pa-sm">
|
||||||
|
<canvas ref="walletBalanceInOut"></canvas>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
<q-card :style="chartConfig.showPaymentInOutChart ? '' : 'display: none;'">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<div style="height: 200px" class="q-pa-sm">
|
||||||
|
<canvas ref="walletPaymentInOut"></canvas>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -119,6 +119,7 @@
|
||||||
"js/components/admin/lnbits-admin-site-customisation.js",
|
"js/components/admin/lnbits-admin-site-customisation.js",
|
||||||
"js/components/admin/lnbits-admin-assets-config.js",
|
"js/components/admin/lnbits-admin-assets-config.js",
|
||||||
"js/components/admin/lnbits-admin-audit.js",
|
"js/components/admin/lnbits-admin-audit.js",
|
||||||
|
"js/components/lnbits-wallet-charts.js",
|
||||||
"js/components/lnbits-wallet-api-docs.js",
|
"js/components/lnbits-wallet-api-docs.js",
|
||||||
"js/components/lnbits-wallet-new.js",
|
"js/components/lnbits-wallet-new.js",
|
||||||
"js/components/lnbits-wallet-share.js",
|
"js/components/lnbits-wallet-share.js",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue