feat: add payments table to user manager (#2491)
* feat: add payments table to user manager refactor payments table and payment chart into components and add them to usermanager * bundle
This commit is contained in:
parent
9933484558
commit
a5623ef7c3
11 changed files with 592 additions and 546 deletions
|
|
@ -102,226 +102,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<payment-list :wallet="this.g.wallet" :mobileSimple="mobileSimple" />
|
||||||
<q-card
|
|
||||||
:style="$q.screen.lt.md ? {
|
|
||||||
background: $q.screen.lt.md ? 'none !important': ''
|
|
||||||
, boxShadow: $q.screen.lt.md ? 'none !important': ''
|
|
||||||
, marginTop: $q.screen.lt.md ? '0px !important': ''
|
|
||||||
} : ''"
|
|
||||||
>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row items-center no-wrap q-mb-sm">
|
|
||||||
<div class="col">
|
|
||||||
<h5
|
|
||||||
class="text-subtitle1 q-my-none"
|
|
||||||
:v-text="$t('transactions')"
|
|
||||||
></h5>
|
|
||||||
</div>
|
|
||||||
<div class="gt-sm col-auto">
|
|
||||||
<q-btn
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
@click="exportCSV"
|
|
||||||
:label="$t('export_csv')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
icon="show_chart"
|
|
||||||
color="grey"
|
|
||||||
@click="showChart"
|
|
||||||
>
|
|
||||||
<q-tooltip>
|
|
||||||
<span v-text="$t('chart_tooltip')"></span
|
|
||||||
></q-tooltip>
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-input
|
|
||||||
:style="$q.screen.lt.md ? {
|
|
||||||
display: mobileSimple ? 'none !important': ''
|
|
||||||
} : ''"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
clearable
|
|
||||||
v-model="paymentsTable.search"
|
|
||||||
debounce="300"
|
|
||||||
:placeholder="$t('search_by_tag_memo_amount')"
|
|
||||||
class="q-mb-md"
|
|
||||||
>
|
|
||||||
</q-input>
|
|
||||||
<q-table
|
|
||||||
dense
|
|
||||||
flat
|
|
||||||
:data="paymentsOmitter"
|
|
||||||
:row-key="paymentTableRowKey"
|
|
||||||
:columns="paymentsTable.columns"
|
|
||||||
:pagination.sync="paymentsTable.pagination"
|
|
||||||
:no-data-label="$t('no_transactions')"
|
|
||||||
:filter="paymentsTable.search"
|
|
||||||
:loading="paymentsTable.loading"
|
|
||||||
:hide-header="mobileSimple"
|
|
||||||
:hide-bottom="mobileSimple"
|
|
||||||
@request="fetchPayments"
|
|
||||||
>
|
|
||||||
<template v-slot:header="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-th auto-width></q-th>
|
|
||||||
<q-th
|
|
||||||
v-for="col in props.cols"
|
|
||||||
:key="col.name"
|
|
||||||
:props="props"
|
|
||||||
v-text="col.label"
|
|
||||||
></q-th>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-td auto-width class="text-center">
|
|
||||||
<q-icon
|
|
||||||
v-if="props.row.isPaid"
|
|
||||||
size="14px"
|
|
||||||
:name="props.row.isOut ? 'call_made' : 'call_received'"
|
|
||||||
:color="props.row.isOut ? 'pink' : 'green'"
|
|
||||||
@click="props.expand = !props.expand"
|
|
||||||
></q-icon>
|
|
||||||
<q-icon
|
|
||||||
v-else
|
|
||||||
name="settings_ethernet"
|
|
||||||
color="grey"
|
|
||||||
@click="props.expand = !props.expand"
|
|
||||||
>
|
|
||||||
<q-tooltip
|
|
||||||
><span v-text="$t('pending')"></span
|
|
||||||
></q-tooltip>
|
|
||||||
</q-icon>
|
|
||||||
</q-td>
|
|
||||||
<q-td
|
|
||||||
key="time"
|
|
||||||
:props="props"
|
|
||||||
style="white-space: normal; word-break: break-all"
|
|
||||||
>
|
|
||||||
<q-badge
|
|
||||||
v-if="props.row.tag"
|
|
||||||
color="yellow"
|
|
||||||
text-color="black"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-text="'#'+props.row.tag"
|
|
||||||
class="inherit"
|
|
||||||
:href="['/', props.row.tag].join('')"
|
|
||||||
></a>
|
|
||||||
</q-badge>
|
|
||||||
<span v-text="props.row.memo"></span>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<i>
|
|
||||||
<span v-text="props.row.dateFrom"></span>
|
|
||||||
<q-tooltip
|
|
||||||
><span v-text="props.row.date"></span
|
|
||||||
></q-tooltip>
|
|
||||||
</i>
|
|
||||||
</q-td>
|
|
||||||
<q-td
|
|
||||||
auto-width
|
|
||||||
key="amount"
|
|
||||||
v-if="'{{LNBITS_DENOMINATION}}' != 'sats'"
|
|
||||||
:props="props"
|
|
||||||
v-text="parseFloat(String(props.row.fsat).replaceAll(',', '')) / 100"
|
|
||||||
>
|
|
||||||
</q-td>
|
|
||||||
<q-td auto-width key="amount" v-else :props="props">
|
|
||||||
<span v-text="props.row.fsat"></span>
|
|
||||||
<br />
|
|
||||||
<i v-if="props.row.extra.wallet_fiat_currency">
|
|
||||||
<span
|
|
||||||
v-text="formatFiat(props.row.extra.wallet_fiat_currency, props.row.extra.wallet_fiat_amount)"
|
|
||||||
></span>
|
|
||||||
<br />
|
|
||||||
</i>
|
|
||||||
<i v-if="props.row.extra.fiat_currency">
|
|
||||||
<span
|
|
||||||
v-text="formatFiat(props.row.extra.fiat_currency, props.row.extra.fiat_amount)"
|
|
||||||
></span>
|
|
||||||
</i>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
|
|
||||||
<q-dialog v-model="props.expand" :props="props" position="top">
|
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
||||||
<div class="text-center q-mb-lg">
|
|
||||||
<div v-if="props.row.isIn && props.row.pending">
|
|
||||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
|
||||||
<span v-text="$t('invoice_waiting')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
<div
|
|
||||||
v-if="props.row.bolt11"
|
|
||||||
class="text-center q-mb-lg"
|
|
||||||
>
|
|
||||||
<a :href="'lightning:' + props.row.bolt11">
|
|
||||||
<q-responsive :ratio="1" class="q-mx-xl">
|
|
||||||
<lnbits-qrcode
|
|
||||||
:value="'lightning:' + props.row.bolt11.toUpperCase()"
|
|
||||||
></lnbits-qrcode>
|
|
||||||
</q-responsive>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="row q-mt-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="copyText(props.row.bolt11)"
|
|
||||||
:label="$t('copy_invoice')"
|
|
||||||
></q-btn>
|
|
||||||
<q-btn
|
|
||||||
v-close-popup
|
|
||||||
flat
|
|
||||||
color="grey"
|
|
||||||
class="q-ml-auto"
|
|
||||||
:label="$t('close')"
|
|
||||||
></q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isPaid && props.row.isIn">
|
|
||||||
<q-icon
|
|
||||||
size="18px"
|
|
||||||
:name="'call_received'"
|
|
||||||
:color="'green'"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('payment_received')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isPaid && props.row.isOut">
|
|
||||||
<q-icon
|
|
||||||
size="18px"
|
|
||||||
:name="'call_made'"
|
|
||||||
:color="'pink'"
|
|
||||||
></q-icon>
|
|
||||||
<span v-text="$t('payment_sent')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="props.row.isOut && props.row.pending">
|
|
||||||
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
|
||||||
<span v-text="$t('outgoing_payment_pending')"></span>
|
|
||||||
<lnbits-payment-details
|
|
||||||
:payment="props.row"
|
|
||||||
></lnbits-payment-details>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
</div>
|
||||||
{% if HIDE_API %}
|
{% if HIDE_API %}
|
||||||
<div class="col-12 col-md-4 q-gutter-y-md">
|
<div class="col-12 col-md-4 q-gutter-y-md">
|
||||||
|
|
@ -877,27 +658,6 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="paymentsChart.show" position="top">
|
|
||||||
<q-card class="q-pa-sm" style="width: 800px; max-width: unset">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row q-gutter-sm justify-between">
|
|
||||||
<div class="text-h6">Payments Chart</div>
|
|
||||||
<q-select
|
|
||||||
label="Group"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model="paymentsChart.group"
|
|
||||||
style="min-width: 120px"
|
|
||||||
:options="paymentsChart.groupOptions"
|
|
||||||
>
|
|
||||||
</q-select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<canvas ref="canvas" width="600" height="400"></canvas>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
|
|
||||||
<q-tabs
|
<q-tabs
|
||||||
class="lt-md fixed-bottom left-0 right-0 bg-primary text-white shadow-2 z-top"
|
class="lt-md fixed-bottom left-0 right-0 bg-primary text-white shadow-2 z-top"
|
||||||
active-class="px-0"
|
active-class="px-0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
<q-dialog v-model="walletDialog.show">
|
<q-dialog v-model="walletDialog.show">
|
||||||
<q-card class="q-pa-lg" style="width: 700px; max-width: 80vw">
|
<q-card class="q-pa-lg" style="width: 700px; max-width: 80vw">
|
||||||
<h2 class="text-h6 q-mb-md">Wallets</h2>
|
<h2 class="text-h6 q-mb-md">Wallets</h2>
|
||||||
|
<q-dialog v-model="paymentDialog.show">
|
||||||
|
<q-card class="q-pa-lg" style="width: 700px; max-width: 80vw">
|
||||||
|
<payment-list :wallet="activeWallet" />
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
<q-table :data="wallets" :columns="walletTable.columns">
|
<q-table :data="wallets" :columns="walletTable.columns">
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
|
|
@ -17,6 +22,15 @@
|
||||||
<template v-slot:body="props">
|
<template v-slot:body="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
<q-td auto-width>
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
icon="menu"
|
||||||
|
size="sm"
|
||||||
|
color="secondary"
|
||||||
|
@click="showPayments(props.row.id)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Show Payments</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="!props.row.deleted"
|
v-if="!props.row.deleted"
|
||||||
round
|
round
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ include "users/_createWalletDialog.html" %}
|
||||||
<q-td>
|
<q-td>
|
||||||
<q-btn
|
<q-btn
|
||||||
round
|
round
|
||||||
icon="menu"
|
icon="list"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@click="fetchWallets(props.row.id)"
|
@click="fetchWallets(props.row.id)"
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,28 @@ from lnbits.core.crud import (
|
||||||
get_wallets,
|
get_wallets,
|
||||||
update_admin_settings,
|
update_admin_settings,
|
||||||
)
|
)
|
||||||
from lnbits.core.models import Account, AccountFilters, CreateTopup, User, Wallet
|
from lnbits.core.models import (
|
||||||
|
Account,
|
||||||
|
AccountFilters,
|
||||||
|
CreateTopup,
|
||||||
|
User,
|
||||||
|
Wallet,
|
||||||
|
)
|
||||||
from lnbits.core.services import update_wallet_balance
|
from lnbits.core.services import update_wallet_balance
|
||||||
from lnbits.db import Filters, Page
|
from lnbits.db import Filters, Page
|
||||||
from lnbits.decorators import check_admin, check_super_user, parse_filters
|
from lnbits.decorators import check_admin, check_super_user, parse_filters
|
||||||
|
from lnbits.helpers import generate_filter_params_openapi
|
||||||
from lnbits.settings import EditableSettings, settings
|
from lnbits.settings import EditableSettings, settings
|
||||||
|
|
||||||
users_router = APIRouter(prefix="/users/api/v1", dependencies=[Depends(check_admin)])
|
users_router = APIRouter(prefix="/users/api/v1", dependencies=[Depends(check_admin)])
|
||||||
|
|
||||||
|
|
||||||
@users_router.get("/user")
|
@users_router.get(
|
||||||
|
"/user",
|
||||||
|
name="get accounts",
|
||||||
|
summary="Get paginated list of accounts",
|
||||||
|
openapi_extra=generate_filter_params_openapi(AccountFilters),
|
||||||
|
)
|
||||||
async def api_get_users(
|
async def api_get_users(
|
||||||
filters: Filters = Depends(parse_filters(AccountFilters)),
|
filters: Filters = Depends(parse_filters(AccountFilters)),
|
||||||
) -> Page[Account]:
|
) -> Page[Account]:
|
||||||
|
|
|
||||||
20
lnbits/static/bundle.min.js
vendored
20
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
156
lnbits/static/js/components/payment-chart.js
Normal file
156
lnbits/static/js/components/payment-chart.js
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
function generateChart(canvas, rawData) {
|
||||||
|
const data = rawData.reduce(
|
||||||
|
(previous, current) => {
|
||||||
|
previous.labels.push(current.date)
|
||||||
|
previous.income.push(current.income)
|
||||||
|
previous.spending.push(current.spending)
|
||||||
|
previous.cumulative.push(current.balance)
|
||||||
|
return previous
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labels: [],
|
||||||
|
income: [],
|
||||||
|
spending: [],
|
||||||
|
cumulative: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return new Chart(canvas.getContext('2d'), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: data.labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: data.cumulative,
|
||||||
|
type: 'line',
|
||||||
|
label: 'balance',
|
||||||
|
backgroundColor: '#673ab7', // deep-purple
|
||||||
|
borderColor: '#673ab7',
|
||||||
|
borderWidth: 4,
|
||||||
|
pointRadius: 3,
|
||||||
|
fill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: data.income,
|
||||||
|
type: 'bar',
|
||||||
|
label: 'in',
|
||||||
|
barPercentage: 0.75,
|
||||||
|
backgroundColor: window.Color('rgb(76,175,80)').alpha(0.5).rgbString() // green
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: data.spending,
|
||||||
|
type: 'bar',
|
||||||
|
label: 'out',
|
||||||
|
barPercentage: 0.75,
|
||||||
|
backgroundColor: window.Color('rgb(233,30,99)').alpha(0.5).rgbString() // pink
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
text: 'Chart.js Combo Time Scale'
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
type: 'time',
|
||||||
|
display: true,
|
||||||
|
//offset: true,
|
||||||
|
time: {
|
||||||
|
minUnit: 'hour',
|
||||||
|
stepSize: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// performance tweaks
|
||||||
|
animation: {
|
||||||
|
duration: 0
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
line: {
|
||||||
|
tension: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component('payment-chart', {
|
||||||
|
name: 'payment-chart',
|
||||||
|
props: ['wallet'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
paymentsChart: {
|
||||||
|
show: false,
|
||||||
|
group: {
|
||||||
|
value: 'hour',
|
||||||
|
label: 'Hour'
|
||||||
|
},
|
||||||
|
groupOptions: [
|
||||||
|
{value: 'hour', label: 'Hour'},
|
||||||
|
{value: 'day', label: 'Day'},
|
||||||
|
{value: 'week', label: 'Week'},
|
||||||
|
{value: 'month', label: 'Month'},
|
||||||
|
{value: 'year', label: 'Year'}
|
||||||
|
],
|
||||||
|
instance: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showChart: function () {
|
||||||
|
this.paymentsChart.show = true
|
||||||
|
LNbits.api
|
||||||
|
.request(
|
||||||
|
'GET',
|
||||||
|
'/api/v1/payments/history?group=' + this.paymentsChart.group.value,
|
||||||
|
this.wallet.adminkey
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.paymentsChart.instance) {
|
||||||
|
this.paymentsChart.instance.destroy()
|
||||||
|
}
|
||||||
|
this.paymentsChart.instance = generateChart(
|
||||||
|
this.$refs.canvas,
|
||||||
|
response.data
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
LNbits.utils.notifyApiError(err)
|
||||||
|
this.paymentsChart.show = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<span id="payment-chart">
|
||||||
|
<q-btn dense flat round icon="show_chart" color="grey" @click="showChart" >
|
||||||
|
<q-tooltip>
|
||||||
|
<span v-text="$t('chart_tooltip')"></span>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-dialog v-model="paymentsChart.show" position="top">
|
||||||
|
<q-card class="q-pa-sm" style="width: 800px; max-width: unset">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row q-gutter-sm justify-between">
|
||||||
|
<div class="text-h6">Payments Chart</div>
|
||||||
|
<q-select label="Group" filled dense v-model="paymentsChart.group"
|
||||||
|
style="min-width: 120px"
|
||||||
|
:options="paymentsChart.groupOptions"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
</div>
|
||||||
|
<canvas ref="canvas" width="600" height="400"></canvas>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</span>
|
||||||
|
`
|
||||||
|
})
|
||||||
383
lnbits/static/js/components/payment-list.js
Normal file
383
lnbits/static/js/components/payment-list.js
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
Vue.component('payment-list', {
|
||||||
|
name: 'payment-list',
|
||||||
|
props: ['wallet', 'mobileSimple', 'lazy'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
denomination: LNBITS_DENOMINATION,
|
||||||
|
payments: [],
|
||||||
|
paymentsTable: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'time',
|
||||||
|
align: 'left',
|
||||||
|
label: this.$t('memo') + '/' + this.$t('date'),
|
||||||
|
field: 'date',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amount',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
||||||
|
field: 'sat',
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
rowsPerPage: 10,
|
||||||
|
page: 1,
|
||||||
|
sortBy: 'time',
|
||||||
|
descending: true,
|
||||||
|
rowsNumber: 10
|
||||||
|
},
|
||||||
|
search: null,
|
||||||
|
loading: false
|
||||||
|
},
|
||||||
|
paymentsCSV: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'pending',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Pending',
|
||||||
|
field: 'pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'memo',
|
||||||
|
align: 'left',
|
||||||
|
label: this.$t('memo'),
|
||||||
|
field: 'memo'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'time',
|
||||||
|
align: 'left',
|
||||||
|
label: this.$t('date'),
|
||||||
|
field: 'date',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amount',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
||||||
|
field: 'sat',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fee',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('fee') + ' (m' + LNBITS_DENOMINATION + ')',
|
||||||
|
field: 'fee'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tag',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('tag'),
|
||||||
|
field: 'tag'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'payment_hash',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('payment_hash'),
|
||||||
|
field: 'payment_hash'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'payment_proof',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('payment_proof'),
|
||||||
|
field: 'payment_proof'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webhook',
|
||||||
|
align: 'right',
|
||||||
|
label: this.$t('webhook'),
|
||||||
|
field: 'webhook'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fiat_currency',
|
||||||
|
align: 'right',
|
||||||
|
label: 'Fiat Currency',
|
||||||
|
field: row => row.extra.wallet_fiat_currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fiat_amount',
|
||||||
|
align: 'right',
|
||||||
|
label: 'Fiat Amount',
|
||||||
|
field: row => row.extra.wallet_fiat_amount
|
||||||
|
}
|
||||||
|
],
|
||||||
|
filter: null,
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filteredPayments: function () {
|
||||||
|
var q = this.paymentsTable.search
|
||||||
|
if (!q || q === '') return this.payments
|
||||||
|
|
||||||
|
return LNbits.utils.search(this.payments, q)
|
||||||
|
},
|
||||||
|
paymentsOmitter() {
|
||||||
|
if (this.$q.screen.lt.md && this.mobileSimple) {
|
||||||
|
return this.payments.length > 0 ? [this.payments[0]] : []
|
||||||
|
}
|
||||||
|
return this.payments
|
||||||
|
},
|
||||||
|
pendingPaymentsExist: function () {
|
||||||
|
return this.payments.findIndex(payment => payment.pending) !== -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchPayments: function (props) {
|
||||||
|
const params = LNbits.utils.prepareFilterQuery(this.paymentsTable, props)
|
||||||
|
return LNbits.api
|
||||||
|
.getPayments(this.wallet, params)
|
||||||
|
.then(response => {
|
||||||
|
this.paymentsTable.loading = false
|
||||||
|
this.paymentsTable.pagination.rowsNumber = response.data.total
|
||||||
|
this.payments = response.data.data.map(obj => {
|
||||||
|
return LNbits.map.payment(obj)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.paymentsTable.loading = false
|
||||||
|
LNbits.utils.notifyApiError(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
paymentTableRowKey: function (row) {
|
||||||
|
return row.payment_hash + row.amount
|
||||||
|
},
|
||||||
|
exportCSV: function () {
|
||||||
|
// status is important for export but it is not in paymentsTable
|
||||||
|
// because it is manually added with payment detail link and icons
|
||||||
|
// and would cause duplication in the list
|
||||||
|
const pagination = this.paymentsTable.pagination
|
||||||
|
const query = {
|
||||||
|
sortby: pagination.sortBy ?? 'time',
|
||||||
|
direction: pagination.descending ? 'desc' : 'asc'
|
||||||
|
}
|
||||||
|
const params = new URLSearchParams(query)
|
||||||
|
LNbits.api.getPayments(this.wallet, params).then(response => {
|
||||||
|
const payments = response.data.data.map(LNbits.map.payment)
|
||||||
|
LNbits.utils.exportCSV(
|
||||||
|
this.paymentsCSV.columns,
|
||||||
|
payments,
|
||||||
|
this.wallet.name + '-payments'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
lazy: function (newVal) {
|
||||||
|
debugger
|
||||||
|
if (newVal === true) this.fetchPayments()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
if (this.lazy === undefined) this.fetchPayments()
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<q-card
|
||||||
|
:style="$q.screen.lt.md ? {
|
||||||
|
background: $q.screen.lt.md ? 'none !important': ''
|
||||||
|
, boxShadow: $q.screen.lt.md ? 'none !important': ''
|
||||||
|
, marginTop: $q.screen.lt.md ? '0px !important': ''
|
||||||
|
} : ''"
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row items-center no-wrap q-mb-sm">
|
||||||
|
<div class="col">
|
||||||
|
<h5
|
||||||
|
class="text-subtitle1 q-my-none"
|
||||||
|
:v-text="$t('transactions')"
|
||||||
|
></h5>
|
||||||
|
</div>
|
||||||
|
<div class="gt-sm col-auto">
|
||||||
|
<q-btn flat color="grey" @click="exportCSV" :label="$t('export_csv')" ></q-btn>
|
||||||
|
<payment-chart :wallet="wallet" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-input
|
||||||
|
:style="$q.screen.lt.md ? {
|
||||||
|
display: mobileSimple ? 'none !important': ''
|
||||||
|
} : ''"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
v-model="paymentsTable.search"
|
||||||
|
debounce="300"
|
||||||
|
:placeholder="$t('search_by_tag_memo_amount')"
|
||||||
|
class="q-mb-md"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
<q-table
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
:data="paymentsOmitter"
|
||||||
|
:row-key="paymentTableRowKey"
|
||||||
|
:columns="paymentsTable.columns"
|
||||||
|
:pagination.sync="paymentsTable.pagination"
|
||||||
|
:no-data-label="$t('no_transactions')"
|
||||||
|
:filter="paymentsTable.search"
|
||||||
|
:loading="paymentsTable.loading"
|
||||||
|
:hide-header="mobileSimple"
|
||||||
|
:hide-bottom="mobileSimple"
|
||||||
|
@request="fetchPayments"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-th auto-width></q-th>
|
||||||
|
<q-th
|
||||||
|
v-for="col in props.cols"
|
||||||
|
:key="col.name"
|
||||||
|
:props="props"
|
||||||
|
v-text="col.label"
|
||||||
|
></q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-td auto-width class="text-center">
|
||||||
|
<q-icon
|
||||||
|
v-if="props.row.isPaid"
|
||||||
|
size="14px"
|
||||||
|
:name="props.row.isOut ? 'call_made' : 'call_received'"
|
||||||
|
:color="props.row.isOut ? 'pink' : 'green'"
|
||||||
|
@click="props.expand = !props.expand"
|
||||||
|
></q-icon>
|
||||||
|
<q-icon
|
||||||
|
v-else
|
||||||
|
name="settings_ethernet"
|
||||||
|
color="grey"
|
||||||
|
@click="props.expand = !props.expand"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
><span v-text="$t('pending')"></span
|
||||||
|
></q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
<q-td
|
||||||
|
key="time"
|
||||||
|
:props="props"
|
||||||
|
style="white-space: normal; word-break: break-all"
|
||||||
|
>
|
||||||
|
<q-badge
|
||||||
|
v-if="props.row.tag"
|
||||||
|
color="yellow"
|
||||||
|
text-color="black"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
v-text="'#'+props.row.tag"
|
||||||
|
class="inherit"
|
||||||
|
:href="['/', props.row.tag].join('')"
|
||||||
|
></a>
|
||||||
|
</q-badge>
|
||||||
|
<span v-text="props.row.memo"></span>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<i>
|
||||||
|
<span v-text="props.row.dateFrom"></span>
|
||||||
|
<q-tooltip
|
||||||
|
><span v-text="props.row.date"></span
|
||||||
|
></q-tooltip>
|
||||||
|
</i>
|
||||||
|
</q-td>
|
||||||
|
<q-td
|
||||||
|
auto-width
|
||||||
|
key="amount"
|
||||||
|
v-if="denomination != 'sats'"
|
||||||
|
:props="props"
|
||||||
|
class="lol1"
|
||||||
|
v-text="parseFloat(String(props.row.fsat).replaceAll(',', '')) / 100"
|
||||||
|
>
|
||||||
|
</q-td>
|
||||||
|
<q-td class="lol2" auto-width key="amount" v-else :props="props">
|
||||||
|
<span v-text="props.row.fsat"></span>
|
||||||
|
<br />
|
||||||
|
<i v-if="props.row.extra.wallet_fiat_currency">
|
||||||
|
<span
|
||||||
|
v-text="LNbits.utils.formatCurrency(props.row.extra.wallet_fiat_currency, props.row.extra.wallet_fiat_amount)"
|
||||||
|
></span>
|
||||||
|
<br />
|
||||||
|
</i>
|
||||||
|
<i v-if="props.row.extra.fiat_currency">
|
||||||
|
<span
|
||||||
|
v-text="LNbits.utils.formatCurrency(props.row.extra.fiat_currency, props.row.extra.fiat_amount)"
|
||||||
|
></span>
|
||||||
|
</i>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-dialog v-model="props.expand" :props="props" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
|
<div class="text-center q-mb-lg">
|
||||||
|
<div v-if="props.row.isIn && props.row.pending">
|
||||||
|
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||||
|
<span v-text="$t('invoice_waiting')"></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
<div
|
||||||
|
v-if="props.row.bolt11"
|
||||||
|
class="text-center q-mb-lg"
|
||||||
|
>
|
||||||
|
<a :href="'lightning:' + props.row.bolt11">
|
||||||
|
<q-responsive :ratio="1" class="q-mx-xl">
|
||||||
|
<lnbits-qrcode
|
||||||
|
:value="'lightning:' + props.row.bolt11.toUpperCase()"
|
||||||
|
></lnbits-qrcode>
|
||||||
|
</q-responsive>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="grey"
|
||||||
|
@click="copyText(props.row.bolt11)"
|
||||||
|
:label="$t('copy_invoice')"
|
||||||
|
></q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
flat
|
||||||
|
color="grey"
|
||||||
|
class="q-ml-auto"
|
||||||
|
:label="$t('close')"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="props.row.isPaid && props.row.isIn">
|
||||||
|
<q-icon
|
||||||
|
size="18px"
|
||||||
|
:name="'call_received'"
|
||||||
|
:color="'green'"
|
||||||
|
></q-icon>
|
||||||
|
<span v-text="$t('payment_received')"></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="props.row.isPaid && props.row.isOut">
|
||||||
|
<q-icon
|
||||||
|
size="18px"
|
||||||
|
:name="'call_made'"
|
||||||
|
:color="'pink'"
|
||||||
|
></q-icon>
|
||||||
|
<span v-text="$t('payment_sent')"></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="props.row.isOut && props.row.pending">
|
||||||
|
<q-icon name="settings_ethernet" color="grey"></q-icon>
|
||||||
|
<span v-text="$t('outgoing_payment_pending')"></span>
|
||||||
|
<lnbits-payment-details
|
||||||
|
:payment="props.row"
|
||||||
|
></lnbits-payment-details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
@ -4,10 +4,14 @@ new Vue({
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
isSuperUser: false,
|
isSuperUser: false,
|
||||||
|
activeWallet: {},
|
||||||
wallet: {},
|
wallet: {},
|
||||||
cancel: {},
|
cancel: {},
|
||||||
users: [],
|
users: [],
|
||||||
wallets: [],
|
wallets: [],
|
||||||
|
paymentDialog: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
walletDialog: {
|
walletDialog: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
|
|
@ -324,6 +328,10 @@ new Vue({
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
showPayments(wallet_id) {
|
||||||
|
this.activeWallet = this.wallets.find(wallet => wallet.id === wallet_id)
|
||||||
|
this.paymentDialog.show = true
|
||||||
|
},
|
||||||
toggleAdmin(user_id) {
|
toggleAdmin(user_id) {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', `/users/api/v1/user/${user_id}/admin`)
|
.request('GET', `/users/api/v1/user/${user_id}/admin`)
|
||||||
|
|
|
||||||
|
|
@ -1,90 +1,8 @@
|
||||||
/* globals windowMixin, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, Chart, decryptLnurlPayAES */
|
/* globals windowMixin, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, decryptLnurlPayAES */
|
||||||
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
Vue.use(VueQrcodeReader)
|
Vue.use(VueQrcodeReader)
|
||||||
|
|
||||||
function generateChart(canvas, rawData) {
|
|
||||||
const data = rawData.reduce(
|
|
||||||
(previous, current) => {
|
|
||||||
previous.labels.push(current.date)
|
|
||||||
previous.income.push(current.income)
|
|
||||||
previous.spending.push(current.spending)
|
|
||||||
previous.cumulative.push(current.balance)
|
|
||||||
return previous
|
|
||||||
},
|
|
||||||
{
|
|
||||||
labels: [],
|
|
||||||
income: [],
|
|
||||||
spending: [],
|
|
||||||
cumulative: []
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return new Chart(canvas.getContext('2d'), {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: data.labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: data.cumulative,
|
|
||||||
type: 'line',
|
|
||||||
label: 'balance',
|
|
||||||
backgroundColor: '#673ab7', // deep-purple
|
|
||||||
borderColor: '#673ab7',
|
|
||||||
borderWidth: 4,
|
|
||||||
pointRadius: 3,
|
|
||||||
fill: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: data.income,
|
|
||||||
type: 'bar',
|
|
||||||
label: 'in',
|
|
||||||
barPercentage: 0.75,
|
|
||||||
backgroundColor: window.Color('rgb(76,175,80)').alpha(0.5).rgbString() // green
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: data.spending,
|
|
||||||
type: 'bar',
|
|
||||||
label: 'out',
|
|
||||||
barPercentage: 0.75,
|
|
||||||
backgroundColor: window.Color('rgb(233,30,99)').alpha(0.5).rgbString() // pink
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
title: {
|
|
||||||
text: 'Chart.js Combo Time Scale'
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
xAxes: [
|
|
||||||
{
|
|
||||||
type: 'time',
|
|
||||||
display: true,
|
|
||||||
//offset: true,
|
|
||||||
time: {
|
|
||||||
minUnit: 'hour',
|
|
||||||
stepSize: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// performance tweaks
|
|
||||||
animation: {
|
|
||||||
duration: 0
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
line: {
|
|
||||||
tension: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#vue',
|
el: '#vue',
|
||||||
mixins: [windowMixin],
|
mixins: [windowMixin],
|
||||||
|
|
@ -126,118 +44,6 @@ new Vue({
|
||||||
camera: 'auto'
|
camera: 'auto'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
payments: [],
|
|
||||||
paymentsTable: {
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
name: 'time',
|
|
||||||
align: 'left',
|
|
||||||
label: this.$t('memo') + '/' + this.$t('date'),
|
|
||||||
field: 'date',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'amount',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
|
||||||
field: 'sat',
|
|
||||||
sortable: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
pagination: {
|
|
||||||
rowsPerPage: 10,
|
|
||||||
page: 1,
|
|
||||||
sortBy: 'time',
|
|
||||||
descending: true,
|
|
||||||
rowsNumber: 10
|
|
||||||
},
|
|
||||||
search: null,
|
|
||||||
loading: false
|
|
||||||
},
|
|
||||||
paymentsCSV: {
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
name: 'pending',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Pending',
|
|
||||||
field: 'pending'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'memo',
|
|
||||||
align: 'left',
|
|
||||||
label: this.$t('memo'),
|
|
||||||
field: 'memo'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'time',
|
|
||||||
align: 'left',
|
|
||||||
label: this.$t('date'),
|
|
||||||
field: 'date',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'amount',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('amount') + ' (' + LNBITS_DENOMINATION + ')',
|
|
||||||
field: 'sat',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fee',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('fee') + ' (m' + LNBITS_DENOMINATION + ')',
|
|
||||||
field: 'fee'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'tag',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('tag'),
|
|
||||||
field: 'tag'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'payment_hash',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('payment_hash'),
|
|
||||||
field: 'payment_hash'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'payment_proof',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('payment_proof'),
|
|
||||||
field: 'payment_proof'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'webhook',
|
|
||||||
align: 'right',
|
|
||||||
label: this.$t('webhook'),
|
|
||||||
field: 'webhook'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fiat_currency',
|
|
||||||
align: 'right',
|
|
||||||
label: 'Fiat Currency',
|
|
||||||
field: row => row.extra.wallet_fiat_currency
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fiat_amount',
|
|
||||||
align: 'right',
|
|
||||||
label: 'Fiat Amount',
|
|
||||||
field: row => row.extra.wallet_fiat_amount
|
|
||||||
}
|
|
||||||
],
|
|
||||||
filter: null,
|
|
||||||
loading: false
|
|
||||||
},
|
|
||||||
paymentsChart: {
|
|
||||||
show: false,
|
|
||||||
group: {value: 'hour', label: 'Hour'},
|
|
||||||
groupOptions: [
|
|
||||||
{value: 'month', label: 'Month'},
|
|
||||||
{value: 'day', label: 'Day'},
|
|
||||||
{value: 'hour', label: 'Hour'}
|
|
||||||
],
|
|
||||||
instance: null
|
|
||||||
},
|
|
||||||
disclaimerDialog: {
|
disclaimerDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
location: window.location
|
location: window.location
|
||||||
|
|
@ -268,63 +74,21 @@ new Vue({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
filteredPayments: function () {
|
|
||||||
var q = this.paymentsTable.search
|
|
||||||
if (!q || q === '') return this.payments
|
|
||||||
|
|
||||||
return LNbits.utils.search(this.payments, q)
|
|
||||||
},
|
|
||||||
paymentsOmitter() {
|
|
||||||
if (this.$q.screen.lt.md && this.mobileSimple) {
|
|
||||||
return this.payments.length > 0 ? [this.payments[0]] : []
|
|
||||||
}
|
|
||||||
return this.payments
|
|
||||||
},
|
|
||||||
canPay: function () {
|
canPay: function () {
|
||||||
if (!this.parse.invoice) return false
|
if (!this.parse.invoice) return false
|
||||||
return this.parse.invoice.sat <= this.balance
|
return this.parse.invoice.sat <= this.balance
|
||||||
},
|
|
||||||
pendingPaymentsExist: function () {
|
|
||||||
return this.payments.findIndex(payment => payment.pending) !== -1
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
msatoshiFormat: function (value) {
|
msatoshiFormat: function (value) {
|
||||||
return LNbits.utils.formatSat(value / 1000)
|
return LNbits.utils.formatSat(value / 1000)
|
||||||
},
|
},
|
||||||
paymentTableRowKey: function (row) {
|
|
||||||
return row.payment_hash + row.amount
|
|
||||||
},
|
|
||||||
closeCamera: function () {
|
closeCamera: function () {
|
||||||
this.parse.camera.show = false
|
this.parse.camera.show = false
|
||||||
},
|
},
|
||||||
showCamera: function () {
|
showCamera: function () {
|
||||||
this.parse.camera.show = true
|
this.parse.camera.show = true
|
||||||
},
|
},
|
||||||
showChart: function () {
|
|
||||||
this.paymentsChart.show = true
|
|
||||||
LNbits.api
|
|
||||||
.request(
|
|
||||||
'GET',
|
|
||||||
'/api/v1/payments/history?group=' + this.paymentsChart.group.value,
|
|
||||||
this.g.wallet.adminkey
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.paymentsChart.instance) {
|
|
||||||
this.paymentsChart.instance.destroy()
|
|
||||||
}
|
|
||||||
this.paymentsChart.instance = generateChart(
|
|
||||||
this.$refs.canvas,
|
|
||||||
response.data
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
this.paymentsChart.show = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
focusInput(el) {
|
focusInput(el) {
|
||||||
this.$nextTick(() => this.$refs[el].focus())
|
this.$nextTick(() => this.$refs[el].focus())
|
||||||
},
|
},
|
||||||
|
|
@ -359,8 +123,6 @@ new Vue({
|
||||||
}, 10000)
|
}, 10000)
|
||||||
},
|
},
|
||||||
onPaymentReceived: function (paymentHash) {
|
onPaymentReceived: function (paymentHash) {
|
||||||
this.fetchPayments()
|
|
||||||
|
|
||||||
if (this.receive.paymentHash === paymentHash) {
|
if (this.receive.paymentHash === paymentHash) {
|
||||||
this.receive.show = false
|
this.receive.show = false
|
||||||
this.receive.paymentHash = null
|
this.receive.paymentHash = null
|
||||||
|
|
@ -407,8 +169,6 @@ new Vue({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchPayments()
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
|
|
@ -587,7 +347,6 @@ new Vue({
|
||||||
this.parse.show = false
|
this.parse.show = false
|
||||||
clearInterval(this.parse.paymentChecker)
|
clearInterval(this.parse.paymentChecker)
|
||||||
dismissPaymentMsg()
|
dismissPaymentMsg()
|
||||||
this.fetchPayments()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 2000)
|
}, 2000)
|
||||||
|
|
@ -627,7 +386,6 @@ new Vue({
|
||||||
if (res.data.paid) {
|
if (res.data.paid) {
|
||||||
dismissPaymentMsg()
|
dismissPaymentMsg()
|
||||||
clearInterval(this.parse.paymentChecker)
|
clearInterval(this.parse.paymentChecker)
|
||||||
this.fetchPayments()
|
|
||||||
// show lnurlpay success action
|
// show lnurlpay success action
|
||||||
if (response.data.success_action) {
|
if (response.data.success_action) {
|
||||||
switch (response.data.success_action.tag) {
|
switch (response.data.success_action.tag) {
|
||||||
|
|
@ -740,27 +498,10 @@ new Vue({
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.paymentsTable.loading = false
|
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchPayments: function (props) {
|
|
||||||
const params = LNbits.utils.prepareFilterQuery(this.paymentsTable, props)
|
|
||||||
return LNbits.api
|
|
||||||
.getPayments(this.g.wallet, params)
|
|
||||||
.then(response => {
|
|
||||||
this.paymentsTable.loading = false
|
|
||||||
this.paymentsTable.pagination.rowsNumber = response.data.total
|
|
||||||
this.payments = response.data.data.map(obj => {
|
|
||||||
return LNbits.map.payment(obj)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.paymentsTable.loading = false
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchBalance: function () {
|
fetchBalance: function () {
|
||||||
LNbits.api.getWallet(this.g.wallet).then(response => {
|
LNbits.api.getWallet(this.g.wallet).then(response => {
|
||||||
this.balance = Math.floor(response.data.balance / 1000)
|
this.balance = Math.floor(response.data.balance / 1000)
|
||||||
|
|
@ -785,31 +526,9 @@ new Vue({
|
||||||
})
|
})
|
||||||
.catch(e => console.error(e))
|
.catch(e => console.error(e))
|
||||||
},
|
},
|
||||||
formatFiat(currency, amount) {
|
|
||||||
return LNbits.utils.formatCurrency(amount, currency)
|
|
||||||
},
|
|
||||||
updateBalanceCallback: function (res) {
|
updateBalanceCallback: function (res) {
|
||||||
this.balance += res.value
|
this.balance += res.value
|
||||||
},
|
},
|
||||||
exportCSV: function () {
|
|
||||||
// status is important for export but it is not in paymentsTable
|
|
||||||
// because it is manually added with payment detail link and icons
|
|
||||||
// and would cause duplication in the list
|
|
||||||
const pagination = this.paymentsTable.pagination
|
|
||||||
const query = {
|
|
||||||
sortby: pagination.sortBy ?? 'time',
|
|
||||||
direction: pagination.descending ? 'desc' : 'asc'
|
|
||||||
}
|
|
||||||
const params = new URLSearchParams(query)
|
|
||||||
LNbits.api.getPayments(this.g.wallet, params).then(response => {
|
|
||||||
const payments = response.data.data.map(LNbits.map.payment)
|
|
||||||
LNbits.utils.exportCSV(
|
|
||||||
this.paymentsCSV.columns,
|
|
||||||
payments,
|
|
||||||
this.g.wallet.name + '-payments'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
pasteToTextArea: function () {
|
pasteToTextArea: function () {
|
||||||
this.$refs.textArea.focus() // Set cursor to textarea
|
this.$refs.textArea.focus() // Set cursor to textarea
|
||||||
navigator.clipboard.readText().then(text => {
|
navigator.clipboard.readText().then(text => {
|
||||||
|
|
@ -817,14 +536,6 @@ new Vue({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
payments: function () {
|
|
||||||
this.fetchBalance()
|
|
||||||
},
|
|
||||||
'paymentsChart.group': function () {
|
|
||||||
this.showChart()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created: function () {
|
created: function () {
|
||||||
let urlParams = new URLSearchParams(window.location.search)
|
let urlParams = new URLSearchParams(window.location.search)
|
||||||
if (urlParams.has('lightning') || urlParams.has('lnurl')) {
|
if (urlParams.has('lightning') || urlParams.has('lnurl')) {
|
||||||
|
|
@ -836,8 +547,6 @@ new Vue({
|
||||||
if (this.$q.screen.lt.md) {
|
if (this.$q.screen.lt.md) {
|
||||||
this.mobileSimple = true
|
this.mobileSimple = true
|
||||||
}
|
}
|
||||||
this.fetchPayments()
|
|
||||||
|
|
||||||
this.update.name = this.g.wallet.name
|
this.update.name = this.g.wallet.name
|
||||||
this.update.currency = this.g.wallet.currency
|
this.update.currency = this.g.wallet.currency
|
||||||
this.receive.units = ['sat', ...window.currencies]
|
this.receive.units = ['sat', ...window.currencies]
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@
|
||||||
"js/components.js",
|
"js/components.js",
|
||||||
"js/components/lnbits-funding-sources.js",
|
"js/components/lnbits-funding-sources.js",
|
||||||
"js/components/extension-settings.js",
|
"js/components/extension-settings.js",
|
||||||
|
"js/components/payment-list.js",
|
||||||
|
"js/components/payment-chart.js",
|
||||||
"js/event-reactions.js",
|
"js/event-reactions.js",
|
||||||
"js/bolt11-decoder.js"
|
"js/bolt11-decoder.js"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@
|
||||||
"js/components.js",
|
"js/components.js",
|
||||||
"js/components/lnbits-funding-sources.js",
|
"js/components/lnbits-funding-sources.js",
|
||||||
"js/components/extension-settings.js",
|
"js/components/extension-settings.js",
|
||||||
|
"js/components/payment-list.js",
|
||||||
|
"js/components/payment-chart.js",
|
||||||
"js/event-reactions.js",
|
"js/event-reactions.js",
|
||||||
"js/bolt11-decoder.js"
|
"js/bolt11-decoder.js"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue