Wallet polish (#2942)
This commit is contained in:
parent
4d490506f3
commit
2d41a1bed3
17 changed files with 565 additions and 311 deletions
|
|
@ -397,8 +397,8 @@ async def get_daily_stats(
|
||||||
SUM(apipayments.amount - ABS(apipayments.fee)) AS balance,
|
SUM(apipayments.amount - ABS(apipayments.fee)) AS balance,
|
||||||
ABS(SUM(apipayments.fee)) as fee,
|
ABS(SUM(apipayments.fee)) as fee,
|
||||||
COUNT(*) as payments_count
|
COUNT(*) as payments_count
|
||||||
FROM apipayments
|
FROM wallets
|
||||||
RIGHT JOIN wallets ON apipayments.wallet_id = wallets.id
|
LEFT JOIN apipayments ON apipayments.wallet_id = wallets.id
|
||||||
{clause}
|
{clause}
|
||||||
AND (wallets.deleted = false OR wallets.deleted is NULL)
|
AND (wallets.deleted = false OR wallets.deleted is NULL)
|
||||||
GROUP BY date
|
GROUP BY date
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ class Payment(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class PaymentFilters(FilterModel):
|
class PaymentFilters(FilterModel):
|
||||||
__search_fields__ = ["memo", "amount", "wallet_id", "tag"]
|
__search_fields__ = ["memo", "amount", "wallet_id", "tag", "status", "time"]
|
||||||
|
|
||||||
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
|
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,7 @@ async def calculate_fiat_amounts(
|
||||||
amount_sat = int(amount)
|
amount_sat = int(amount)
|
||||||
|
|
||||||
if wallet_currency:
|
if wallet_currency:
|
||||||
|
try:
|
||||||
if wallet_currency == currency:
|
if wallet_currency == currency:
|
||||||
fiat_amount = amount
|
fiat_amount = amount
|
||||||
else:
|
else:
|
||||||
|
|
@ -407,6 +408,8 @@ async def calculate_fiat_amounts(
|
||||||
fiat_amounts["wallet_fiat_amount"] = round(fiat_amount, ndigits=3)
|
fiat_amounts["wallet_fiat_amount"] = round(fiat_amount, ndigits=3)
|
||||||
fiat_amounts["wallet_fiat_rate"] = amount_sat / fiat_amount
|
fiat_amounts["wallet_fiat_rate"] = amount_sat / fiat_amount
|
||||||
fiat_amounts["wallet_btc_rate"] = (fiat_amount / amount_sat) * 100_000_000
|
fiat_amounts["wallet_btc_rate"] = (fiat_amount / amount_sat) * 100_000_000
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error calculating fiat amount for wallet '{wallet.id}': {e}")
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Calculated fiat amounts {wallet.id=} {amount=} {currency=}: {fiat_amounts=}"
|
f"Calculated fiat amounts {wallet.id=} {amount=} {currency=}: {fiat_amounts=}"
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@
|
||||||
v-model="formData.lnbits_default_bgimage"
|
v-model="formData.lnbits_default_bgimage"
|
||||||
label="Background Image"
|
label="Background Image"
|
||||||
@update:model-value="applyGlobalBgimage"
|
@update:model-value="applyGlobalBgimage"
|
||||||
|
hint="This must be a trusted source. It can change the content and it can log your IP address."
|
||||||
>
|
>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
'q-pt-sm': g.fiatTracking,
|
'q-pt-sm': g.fiatTracking,
|
||||||
'q-pt-lg': !g.fiatTracking
|
'q-pt-lg': !g.fiatTracking
|
||||||
}"
|
}"
|
||||||
v-if="!isPrioritySwapped || !g.fiatTracking"
|
v-if="!isFiatPriority || !g.fiatTracking"
|
||||||
style="height: 100px"
|
style="height: 100px"
|
||||||
>
|
>
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="column"
|
class="column"
|
||||||
v-if="isPrioritySwapped && g.fiatTracking"
|
v-if="isFiatPriority && g.fiatTracking"
|
||||||
:class="{
|
:class="{
|
||||||
'q-pt-sm': g.fiatTracking,
|
'q-pt-sm': g.fiatTracking,
|
||||||
'q-pt-lg': !g.fiatTracking
|
'q-pt-lg': !g.fiatTracking
|
||||||
|
|
@ -189,6 +189,7 @@
|
||||||
>
|
>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<payment-list
|
<payment-list
|
||||||
|
@filter-changed="handleFilterChange"
|
||||||
:update="updatePayments"
|
:update="updatePayments"
|
||||||
:mobile-simple="mobileSimple"
|
:mobile-simple="mobileSimple"
|
||||||
:expand-details="expandDetails"
|
:expand-details="expandDetails"
|
||||||
|
|
@ -256,7 +257,43 @@
|
||||||
|
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
group="extras"
|
group="extras"
|
||||||
icon="settings_cell"
|
icon="phone_android"
|
||||||
|
:label="$t('access_wallet_on_mobile')"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
You can connect to this wallet from a mobile app:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Download
|
||||||
|
<a class="text-secondary" href="https://zeusln.app"
|
||||||
|
>Zeus</a
|
||||||
|
>
|
||||||
|
or
|
||||||
|
<a
|
||||||
|
class="text-secondary"
|
||||||
|
href="https://bluewallet.io/"
|
||||||
|
>BlueWallet</a
|
||||||
|
>
|
||||||
|
from App Store or Google Play
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Enable the
|
||||||
|
<a class="text-secondary" href="/lndhub">LndHub </a>
|
||||||
|
extension for this account
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Scan the QR code in the
|
||||||
|
<a class="text-secondary" href="/lndhub">LndHub </a>
|
||||||
|
extensions with your mobile app
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
Or you can access the wallet directly from your mobile
|
||||||
|
browser using:
|
||||||
|
<q-expansion-item
|
||||||
|
icon="mobile_friendly"
|
||||||
:label="$t('export_to_phone')"
|
:label="$t('export_to_phone')"
|
||||||
>
|
>
|
||||||
<q-card>
|
<q-card>
|
||||||
|
|
@ -272,20 +309,24 @@
|
||||||
outline
|
outline
|
||||||
color="grey"
|
color="grey"
|
||||||
:label="$t('copy_wallet_url')"
|
:label="$t('copy_wallet_url')"
|
||||||
@click="copyText(`${baseUrl}/wallet?usr=${g.user.id}&wal=${g.wallet.id}`)"
|
@click="copyText(`${baseUrl}wallet?usr=${g.user.id}&wal=${g.wallet.id}`)"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
group="extras"
|
group="extras"
|
||||||
icon="edit"
|
icon="settings"
|
||||||
:label="$t('rename_wallet')"
|
:label="$t('wallet_config')"
|
||||||
>
|
>
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="" style="max-width: 320px">
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
v-model.trim="update.name"
|
v-model.trim="update.name"
|
||||||
|
|
@ -293,28 +334,23 @@
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-4 q-pl-sm">
|
||||||
<q-btn
|
<q-btn
|
||||||
:disable="!update.name.length"
|
:disable="!update.name.length"
|
||||||
unelevated
|
unelevated
|
||||||
class="q-mt-sm"
|
class="q-mt-xs full-width"
|
||||||
color="primary"
|
color="primary"
|
||||||
:label="$t('update_name')"
|
:label="$t('update_name')"
|
||||||
|
dense
|
||||||
@click="updateWallet({ name: update.name })"
|
@click="updateWallet({ name: update.name })"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="col-2"></div>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-expansion-item
|
|
||||||
group="extras"
|
|
||||||
icon="attach_money"
|
|
||||||
:label="$t('fiat_tracking')"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div style="max-width: 360px">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-6">
|
||||||
<q-select
|
<q-select
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
|
|
@ -324,46 +360,88 @@
|
||||||
:options="receive.units.filter((u) => u !== 'sat')"
|
:options="receive.units.filter((u) => u !== 'sat')"
|
||||||
></q-select>
|
></q-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-4 q-pl-sm">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
dense
|
||||||
color="primary"
|
color="primary"
|
||||||
|
class="q-mt-xs full-width"
|
||||||
@click="handleFiatTracking()"
|
@click="handleFiatTracking()"
|
||||||
:disable="update.currency == ''"
|
:disable="update.currency == ''"
|
||||||
:label="g.fiatTracking ? 'Remove' : 'Add'"
|
:label="g.fiatTracking ? 'Remove' : 'Add'"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-2">
|
||||||
</div>
|
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="g.user.admin"
|
v-if="g.user.admin"
|
||||||
class="absolute-top-right"
|
|
||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
icon="settings"
|
icon="settings"
|
||||||
|
class="float-right q-mb-lg"
|
||||||
to="/admin#exchange_providers"
|
to="/admin#exchange_providers"
|
||||||
><q-tooltip
|
><q-tooltip
|
||||||
v-text="$t('exchange_providers')"
|
v-text="$t('exchange_providers')"
|
||||||
></q-tooltip
|
></q-tooltip
|
||||||
></q-btn>
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
|
||||||
</q-expansion-item>
|
|
||||||
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-expansion-item
|
|
||||||
group="extras"
|
|
||||||
icon="remove_circle"
|
|
||||||
:label="$t('delete_wallet')"
|
|
||||||
>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
<p v-text="$t('delete_wallet_desc')"></p>
|
<p v-text="$t('delete_wallet_desc')"></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 q-pl-sm">
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
color="red-10"
|
color="red-10"
|
||||||
|
class="full-width"
|
||||||
@click="deleteWallet()"
|
@click="deleteWallet()"
|
||||||
:label="$t('delete_wallet')"
|
:label="$t('delete_wallet')"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<div class="col-2"></div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
<q-separator></q-separator>
|
||||||
|
<q-expansion-item
|
||||||
|
group="charts"
|
||||||
|
icon="insights"
|
||||||
|
:label="$t('wallet_charts')"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-sm-12">
|
||||||
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
@click="saveChartsPreferences"
|
||||||
|
v-model="chartConfig.showBalance"
|
||||||
|
:label="$t('payments_balance_chart')"
|
||||||
|
>
|
||||||
|
</q-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 col-sm-12">
|
||||||
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
@click="saveChartsPreferences"
|
||||||
|
v-model="chartConfig.showBalanceInOut"
|
||||||
|
:label="$t('payments_balance_in_out_chart')"
|
||||||
|
>
|
||||||
|
</q-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-sm-12">
|
||||||
|
<q-checkbox
|
||||||
|
dense
|
||||||
|
@click="saveChartsPreferences"
|
||||||
|
v-model="chartConfig.showPaymentCountInOut"
|
||||||
|
:label="$t('payments_count_in_out_chart')"
|
||||||
|
>
|
||||||
|
</q-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
@ -403,6 +481,27 @@
|
||||||
>{% endfor %}
|
>{% endfor %}
|
||||||
</q-card>
|
</q-card>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,13 @@ from lnbits.core.models import (
|
||||||
PaymentWalletStats,
|
PaymentWalletStats,
|
||||||
Wallet,
|
Wallet,
|
||||||
)
|
)
|
||||||
|
from lnbits.core.models.users import User
|
||||||
from lnbits.core.services.payments import get_payments_daily_stats
|
from lnbits.core.services.payments import get_payments_daily_stats
|
||||||
from lnbits.db import Filters, Page
|
from lnbits.db import Filters, Page
|
||||||
from lnbits.decorators import (
|
from lnbits.decorators import (
|
||||||
WalletTypeInfo,
|
WalletTypeInfo,
|
||||||
check_admin,
|
check_admin,
|
||||||
|
check_user_exists,
|
||||||
parse_filters,
|
parse_filters,
|
||||||
require_admin_key,
|
require_admin_key,
|
||||||
require_invoice_key,
|
require_invoice_key,
|
||||||
|
|
@ -135,13 +137,29 @@ async def api_payments_wallets_stats(
|
||||||
@payment_router.get(
|
@payment_router.get(
|
||||||
"/stats/daily",
|
"/stats/daily",
|
||||||
name="Get payments history per day",
|
name="Get payments history per day",
|
||||||
dependencies=[Depends(check_admin)],
|
|
||||||
response_model=List[PaymentDailyStats],
|
response_model=List[PaymentDailyStats],
|
||||||
openapi_extra=generate_filter_params_openapi(PaymentFilters),
|
openapi_extra=generate_filter_params_openapi(PaymentFilters),
|
||||||
)
|
)
|
||||||
async def api_payments_daily_stats(
|
async def api_payments_daily_stats(
|
||||||
|
user: User = Depends(check_user_exists),
|
||||||
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
||||||
):
|
):
|
||||||
|
|
||||||
|
if not user.admin:
|
||||||
|
exc = HTTPException(
|
||||||
|
status_code=HTTPStatus.UNAUTHORIZED,
|
||||||
|
detail="Missing wallet id.",
|
||||||
|
)
|
||||||
|
wallet_filter = next(
|
||||||
|
(f for f in filters.filters if f.field == "wallet_id"), None
|
||||||
|
)
|
||||||
|
if not wallet_filter:
|
||||||
|
raise exc
|
||||||
|
wallet_id = list((wallet_filter.values or {}).values())
|
||||||
|
if len(wallet_id) == 0:
|
||||||
|
raise exc
|
||||||
|
if not user.get_wallet(wallet_id[0]):
|
||||||
|
raise exc
|
||||||
return await get_payments_daily_stats(filters)
|
return await get_payments_daily_stats(filters)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -263,9 +263,9 @@ class ThemesSettings(LNbitsSettings):
|
||||||
lnbits_default_accounting_currency: Optional[str] = Field(default=None)
|
lnbits_default_accounting_currency: Optional[str] = Field(default=None)
|
||||||
lnbits_qr_logo: str = Field(default="/static/images/logos/lnbits.png")
|
lnbits_qr_logo: str = Field(default="/static/images/logos/lnbits.png")
|
||||||
lnbits_default_reaction: str = Field(default="confettiBothSides")
|
lnbits_default_reaction: str = Field(default="confettiBothSides")
|
||||||
lnbits_default_theme: str = Field(default="classic")
|
lnbits_default_theme: str = Field(default="salvador")
|
||||||
lnbits_default_border: str = Field(default="hard-border")
|
lnbits_default_border: str = Field(default="hard-border")
|
||||||
lnbits_default_gradient: bool = Field(default=False)
|
lnbits_default_gradient: bool = Field(default=True)
|
||||||
lnbits_default_bgimage: str = Field(default=None)
|
lnbits_default_bgimage: str = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -342,13 +342,6 @@ class ExchangeProvidersSettings(LNbitsSettings):
|
||||||
exclude_to=[],
|
exclude_to=[],
|
||||||
ticker_conversion=[],
|
ticker_conversion=[],
|
||||||
),
|
),
|
||||||
ExchangeRateProvider(
|
|
||||||
name="CoinMate",
|
|
||||||
api_url="https://coinmate.io/api/ticker?currencyPair=BTC_{TO}",
|
|
||||||
path="$.data.last",
|
|
||||||
exclude_to=[],
|
|
||||||
ticker_conversion=["USD:USDT"],
|
|
||||||
),
|
|
||||||
ExchangeRateProvider(
|
ExchangeRateProvider(
|
||||||
name="Kraken",
|
name="Kraken",
|
||||||
api_url="https://api.kraken.com/0/public/Ticker?pair=XBT{TO}",
|
api_url="https://api.kraken.com/0/public/Ticker?pair=XBT{TO}",
|
||||||
|
|
|
||||||
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
|
|
@ -46,6 +46,7 @@ window.localisation.en = {
|
||||||
export_to_phone: 'Export to Phone with QR Code',
|
export_to_phone: 'Export to Phone with QR Code',
|
||||||
export_to_phone_desc:
|
export_to_phone_desc:
|
||||||
'This QR code contains your wallet URL with full access. You can scan it from your phone to open your wallet from there.',
|
'This QR code contains your wallet URL with full access. You can scan it from your phone to open your wallet from there.',
|
||||||
|
access_wallet_on_mobile: 'Mobile Access',
|
||||||
wallet: 'Wallet: ',
|
wallet: 'Wallet: ',
|
||||||
wallets: 'Wallets',
|
wallets: 'Wallets',
|
||||||
add_wallet: 'Add a new wallet',
|
add_wallet: 'Add a new wallet',
|
||||||
|
|
@ -248,6 +249,8 @@ window.localisation.en = {
|
||||||
enter_ip: 'Enter IP and hit enter',
|
enter_ip: 'Enter IP and hit enter',
|
||||||
rate_limiter: 'Rate Limiter',
|
rate_limiter: 'Rate Limiter',
|
||||||
wallet_limiter: 'Wallet Limiter',
|
wallet_limiter: 'Wallet Limiter',
|
||||||
|
wallet_config: 'Wallet Config',
|
||||||
|
wallet_charts: 'Wallet Charts',
|
||||||
wallet_limit_max_withdraw_per_day:
|
wallet_limit_max_withdraw_per_day:
|
||||||
'Max daily wallet withdrawal in sats (0 for no limit, -1 to block withdrawal)',
|
'Max daily wallet withdrawal in sats (0 for no limit, -1 to block withdrawal)',
|
||||||
wallet_max_ballance: 'Wallet max balance in sats (0 to disable)',
|
wallet_max_ballance: 'Wallet max balance in sats (0 to disable)',
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
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: 'rgba(76, 175, 80, 0.5)' // green
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: data.spending,
|
|
||||||
type: 'bar',
|
|
||||||
label: 'out',
|
|
||||||
barPercentage: 0.75,
|
|
||||||
backgroundColor: 'rgba(233, 30, 99, 0.5)' // 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
window.app.component('payment-chart', {
|
|
||||||
template: '#payment-chart',
|
|
||||||
name: 'payment-chart',
|
|
||||||
props: ['wallet'],
|
|
||||||
mixins: [window.windowMixin],
|
|
||||||
data() {
|
|
||||||
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() {
|
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -38,6 +38,7 @@ window.app.component('payment-list', {
|
||||||
},
|
},
|
||||||
loading: false
|
loading: false
|
||||||
},
|
},
|
||||||
|
searchDate: {from: null, to: null},
|
||||||
exportTagName: '',
|
exportTagName: '',
|
||||||
exportPaymentTagList: [],
|
exportPaymentTagList: [],
|
||||||
paymentsCSV: {
|
paymentsCSV: {
|
||||||
|
|
@ -136,7 +137,31 @@ window.app.component('payment-list', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
searchByDate() {
|
||||||
|
if (typeof this.searchDate === 'string') {
|
||||||
|
this.searchDate = {
|
||||||
|
from: this.searchDate,
|
||||||
|
to: this.searchDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.searchDate.from) {
|
||||||
|
this.paymentsTable.filter['time[ge]'] =
|
||||||
|
this.searchDate.from + 'T00:00:00'
|
||||||
|
}
|
||||||
|
if (this.searchDate.to) {
|
||||||
|
this.paymentsTable.filter['time[le]'] = this.searchDate.to + 'T23:59:59'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fetchPayments()
|
||||||
|
},
|
||||||
|
clearDateSeach() {
|
||||||
|
this.searchDate = {from: null, to: null}
|
||||||
|
delete this.paymentsTable.filter['time[ge]']
|
||||||
|
delete this.paymentsTable.filter['time[le]']
|
||||||
|
this.fetchPayments()
|
||||||
|
},
|
||||||
fetchPayments(props) {
|
fetchPayments(props) {
|
||||||
|
this.$emit('filter-changed', {...this.paymentsTable.filter})
|
||||||
const params = LNbits.utils.prepareFilterQuery(this.paymentsTable, props)
|
const params = LNbits.utils.prepareFilterQuery(this.paymentsTable, props)
|
||||||
return LNbits.api
|
return LNbits.api
|
||||||
.getPayments(this.currentWallet, params)
|
.getPayments(this.currentWallet, params)
|
||||||
|
|
@ -223,13 +248,21 @@ window.app.component('payment-list', {
|
||||||
watch: {
|
watch: {
|
||||||
failedPaymentsToggle(newVal) {
|
failedPaymentsToggle(newVal) {
|
||||||
if (newVal === false) {
|
if (newVal === false) {
|
||||||
this.paymentsTable.filter = {
|
this.paymentsTable.filter['status[ne]'] = 'failed'
|
||||||
'status[ne]': 'failed'
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.paymentsTable.filter = null
|
delete this.paymentsTable.filter['status[ne]']
|
||||||
|
}
|
||||||
|
this.paymentsTable.pagination.page = 1
|
||||||
|
this.fetchPayments()
|
||||||
|
},
|
||||||
|
'paymentsTable.search': {
|
||||||
|
handler() {
|
||||||
|
const props = {}
|
||||||
|
if (this.paymentsTable.search) {
|
||||||
|
props['search'] = this.paymentsTable.search
|
||||||
}
|
}
|
||||||
this.fetchPayments()
|
this.fetchPayments()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
lazy(newVal) {
|
lazy(newVal) {
|
||||||
if (newVal === true) this.fetchPayments()
|
if (newVal === true) this.fetchPayments()
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ window.WalletPageLogic = {
|
||||||
show: false,
|
show: false,
|
||||||
location: window.location
|
location: window.location
|
||||||
},
|
},
|
||||||
|
mobileSimple: this.$q.screen.lt.md,
|
||||||
icon: {
|
icon: {
|
||||||
show: false,
|
show: false,
|
||||||
data: {},
|
data: {},
|
||||||
|
|
@ -104,14 +105,23 @@ window.WalletPageLogic = {
|
||||||
name: null,
|
name: null,
|
||||||
currency: null
|
currency: null
|
||||||
},
|
},
|
||||||
|
walletBalanceChart: null,
|
||||||
inkeyHidden: true,
|
inkeyHidden: true,
|
||||||
adminkeyHidden: true,
|
adminkeyHidden: true,
|
||||||
hasNfc: false,
|
hasNfc: false,
|
||||||
nfcReaderAbortController: null,
|
nfcReaderAbortController: null,
|
||||||
isPrioritySwapped: false,
|
isFiatPriority: false,
|
||||||
formattedFiatAmount: 0,
|
formattedFiatAmount: 0,
|
||||||
formattedExchange: null,
|
formattedExchange: null,
|
||||||
primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor')
|
primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor'),
|
||||||
|
secondaryColor: this.$q.localStorage.getItem('lnbits.secondaryColor'),
|
||||||
|
chartData: [],
|
||||||
|
chartConfig: {
|
||||||
|
showBalance: true,
|
||||||
|
showBalanceInOut: true,
|
||||||
|
showPaymentCountInOut: true
|
||||||
|
},
|
||||||
|
paymentsFilter: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -174,7 +184,9 @@ window.WalletPageLogic = {
|
||||||
this.receive.paymentHash = null
|
this.receive.paymentHash = null
|
||||||
this.receive.data.amount = null
|
this.receive.data.amount = null
|
||||||
this.receive.data.memo = null
|
this.receive.data.memo = null
|
||||||
this.receive.unit = 'sat'
|
this.receive.unit = this.isFiatPriority
|
||||||
|
? this.g.wallet.currency || 'sat'
|
||||||
|
: 'sat'
|
||||||
this.receive.minMax = [0, 2100000000000000]
|
this.receive.minMax = [0, 2100000000000000]
|
||||||
this.receive.lnurl = null
|
this.receive.lnurl = null
|
||||||
this.focusInput('setAmount')
|
this.focusInput('setAmount')
|
||||||
|
|
@ -770,17 +782,14 @@ window.WalletPageLogic = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
swapBalancePriority() {
|
swapBalancePriority() {
|
||||||
this.isPrioritySwapped = !this.isPrioritySwapped
|
this.isFiatPriority = !this.isFiatPriority
|
||||||
this.$q.localStorage.setItem(
|
this.$q.localStorage.setItem('lnbits.isFiatPriority', this.isFiatPriority)
|
||||||
'lnbits.isPrioritySwapped',
|
|
||||||
this.isPrioritySwapped
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
handleFiatTracking() {
|
handleFiatTracking() {
|
||||||
this.g.fiatTracking = !this.g.fiatTracking
|
this.g.fiatTracking = !this.g.fiatTracking
|
||||||
if (!this.g.fiatTracking) {
|
if (!this.g.fiatTracking) {
|
||||||
this.$q.localStorage.setItem('lnbits.isPrioritySwapped', false)
|
this.$q.localStorage.setItem('lnbits.isFiatPriority', false)
|
||||||
this.isPrioritySwapped = false
|
this.isFiatPriority = false
|
||||||
this.update.currency = ''
|
this.update.currency = ''
|
||||||
this.g.wallet.currency = ''
|
this.g.wallet.currency = ''
|
||||||
this.updateWallet({currency: ''})
|
this.updateWallet({currency: ''})
|
||||||
|
|
@ -800,6 +809,220 @@ window.WalletPageLogic = {
|
||||||
this.update.currency = ''
|
this.update.currency = ''
|
||||||
this.g.fiatTracking = false
|
this.g.fiatTracking = false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
handleFilterChange(value = {}) {
|
||||||
|
if (
|
||||||
|
this.paymentsFilter['time[ge]'] !== value['time[ge]'] ||
|
||||||
|
this.paymentsFilter['time[le]'] !== value['time[le]']
|
||||||
|
) {
|
||||||
|
this.refreshCharts()
|
||||||
|
}
|
||||||
|
this.paymentsFilter = value
|
||||||
|
},
|
||||||
|
async fetchChartData() {
|
||||||
|
if (this.mobileSimple) {
|
||||||
|
this.chartConfig = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!this.chartConfig.showBalance &&
|
||||||
|
!this.chartConfig.showBalanceInOut &&
|
||||||
|
!this.chartConfig.showPaymentCountInOut
|
||||||
|
) {
|
||||||
|
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'
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
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: LNbits.utils.hexAlpha(
|
||||||
|
this.primaryColor,
|
||||||
|
0.3
|
||||||
|
),
|
||||||
|
borderColor: this.primaryColor,
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.7,
|
||||||
|
fill: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Fees',
|
||||||
|
data: data.map(s => s.fee),
|
||||||
|
pointStyle: false,
|
||||||
|
backgroundColor: LNbits.utils.hexAlpha(
|
||||||
|
this.secondaryColor,
|
||||||
|
0.3
|
||||||
|
),
|
||||||
|
borderColor: this.secondaryColor,
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Balance Out',
|
||||||
|
borderRadius: 5,
|
||||||
|
data: data.map(s => s.balance_out)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Payments Out',
|
||||||
|
data: data.map(s => -s.count_out)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveChartsPreferences() {
|
||||||
|
this.$q.localStorage.set('lnbits.wallets.chartConfig', this.chartConfig)
|
||||||
|
this.refreshCharts()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -811,8 +1034,20 @@ 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.wallet.id'(newVal, oldVal) {
|
||||||
|
try {
|
||||||
|
this.fetchChartData()
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Chart creation failed: ${error}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
'g.updatePayments'(newVal, oldVal) {
|
'g.updatePayments'(newVal, oldVal) {
|
||||||
console.log('updatePayments changed:', {newVal, oldVal})
|
console.log('updatePayments changed:', {newVal, oldVal})
|
||||||
this.parse.show = false
|
this.parse.show = false
|
||||||
|
|
@ -846,19 +1081,17 @@ window.WalletPageLogic = {
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
if (!Quasar.LocalStorage.getItem('lnbits.disclaimerShown')) {
|
if (!Quasar.LocalStorage.getItem('lnbits.disclaimerShown')) {
|
||||||
this.disclaimerDialog.show = true
|
this.disclaimerDialog.show = true
|
||||||
Quasar.LocalStorage.setItem('lnbits.disclaimerShown', true)
|
Quasar.LocalStorage.setItem('lnbits.disclaimerShown', true)
|
||||||
Quasar.LocalStorage.setItem('lnbits.reactions', 'confettiTop')
|
Quasar.LocalStorage.setItem('lnbits.reactions', 'confettiTop')
|
||||||
}
|
}
|
||||||
if (Quasar.LocalStorage.getItem('lnbits.isPrioritySwapped')) {
|
if (Quasar.LocalStorage.getItem('lnbits.isFiatPriority')) {
|
||||||
this.isPrioritySwapped = Quasar.LocalStorage.getItem(
|
this.isFiatPriority = Quasar.LocalStorage.getItem('lnbits.isFiatPriority')
|
||||||
'lnbits.isPrioritySwapped'
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
this.isPrioritySwapped = false
|
this.isFiatPriority = false
|
||||||
Quasar.LocalStorage.setItem('lnbits.isPrioritySwapped', false)
|
Quasar.LocalStorage.setItem('lnbits.isFiatPriority', false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@
|
||||||
"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-list.js",
|
||||||
"js/components/payment-chart.js",
|
|
||||||
"js/components.js",
|
"js/components.js",
|
||||||
"js/init-app.js"
|
"js/init-app.js"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@
|
||||||
size="sm"
|
size="sm"
|
||||||
icon="add"
|
icon="add"
|
||||||
>
|
>
|
||||||
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
|
<q-popup-edit class="text-white" v-slot="scope" v-model="credit">
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
:label="$t('credit_label', {denomination: denomination})"
|
:label="$t('credit_label', {denomination: denomination})"
|
||||||
|
|
@ -562,7 +562,7 @@
|
||||||
class="float-right q-mt-sm"
|
class="float-right q-mt-sm"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<q-popup-edit class="bg-accent text-white" v-slot="scope" v-model="credit">
|
<q-popup-edit class="text-white" v-slot="scope" v-model="credit">
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
:label="$t('credit_label', {denomination: denomination})"
|
:label="$t('credit_label', {denomination: denomination})"
|
||||||
|
|
@ -649,6 +649,7 @@
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
outline
|
outline
|
||||||
persistent
|
persistent
|
||||||
|
dense
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
color="grey"
|
color="grey"
|
||||||
label="Export"
|
label="Export"
|
||||||
|
|
@ -700,7 +701,42 @@
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<payment-chart :wallet="wallet"></payment-chart>
|
<q-btn icon="event" outline flat color="grey">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-date v-model="searchDate" mask="YYYY-MM-DD" range />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<q-btn
|
||||||
|
label="Search"
|
||||||
|
@click="searchByDate()"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
class="float-left"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<q-btn
|
||||||
|
v-close-popup
|
||||||
|
@click="clearDateSeach()"
|
||||||
|
label="Clear"
|
||||||
|
class="float-right"
|
||||||
|
color="grey"
|
||||||
|
flat
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-popup-proxy>
|
||||||
|
<q-badge
|
||||||
|
v-if="searchDate?.to || searchDate?.from"
|
||||||
|
class="q-mt-lg q-mr-md"
|
||||||
|
color="primary"
|
||||||
|
rounded
|
||||||
|
floating
|
||||||
|
style="border-radius: 6px"
|
||||||
|
/>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
v-model="failedPaymentsToggle"
|
v-model="failedPaymentsToggle"
|
||||||
checked-icon="warning"
|
checked-icon="warning"
|
||||||
|
|
@ -709,7 +745,7 @@
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
<span v-text="`View failed payments`"></span>
|
<span v-text="`Include failed payments`"></span>
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-checkbox>
|
</q-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -722,7 +758,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.search"
|
:filter="paymentsTable.filter"
|
||||||
:loading="paymentsTable.loading"
|
:loading="paymentsTable.loading"
|
||||||
:hide-header="mobileSimple"
|
:hide-header="mobileSimple"
|
||||||
:hide-bottom="mobileSimple"
|
:hide-bottom="mobileSimple"
|
||||||
|
|
@ -742,7 +778,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body="props">
|
<template v-slot:body="props">
|
||||||
<q-tr :props="props">
|
<q-tr :props="props">
|
||||||
<q-td auto-width class="text-center">
|
<q-td auto-width class="text-center cursor-pointer">
|
||||||
<q-icon
|
<q-icon
|
||||||
v-if="props.row.isPaid"
|
v-if="props.row.isPaid"
|
||||||
size="14px"
|
size="14px"
|
||||||
|
|
@ -1025,36 +1061,6 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="payment-chart">
|
|
||||||
<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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template id="user-id-only">
|
<template id="user-id-only">
|
||||||
<div v-if="authAction === 'login' && authMethod === 'user-id-only'">
|
<div v-if="authAction === 'login' && authMethod === 'user-id-only'">
|
||||||
<q-card-section class="q-pb-none">
|
<q-card-section class="q-pb-none">
|
||||||
|
|
@ -1129,7 +1135,7 @@
|
||||||
<div class="text-body2 text-center q-mt-md">
|
<div class="text-body2 text-center q-mt-md">
|
||||||
<q-badge
|
<q-badge
|
||||||
@click="showLogin('user-id-only')"
|
@click="showLogin('user-id-only')"
|
||||||
color="accent"
|
color="primary"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
rounded
|
rounded
|
||||||
>
|
>
|
||||||
|
|
@ -1141,7 +1147,7 @@
|
||||||
<span v-text="$t('or')" class="q-mx-sm text-grey"></span>
|
<span v-text="$t('or')" class="q-mx-sm text-grey"></span>
|
||||||
<q-badge
|
<q-badge
|
||||||
@click="showRegister('user-id-only')"
|
@click="showRegister('user-id-only')"
|
||||||
color="accent"
|
color="primary"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
rounded
|
rounded
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ async def btc_rates(currency: str) -> list[tuple[str, float]]:
|
||||||
async def btc_price(currency: str) -> float:
|
async def btc_price(currency: str) -> float:
|
||||||
rates = await btc_rates(currency)
|
rates = await btc_rates(currency)
|
||||||
if not rates:
|
if not rates:
|
||||||
return 9999999999
|
raise ValueError("Could not fetch any Bitcoin price.")
|
||||||
elif len(rates) == 1:
|
elif len(rates) == 1:
|
||||||
logger.warning("Could only fetch one Bitcoin price.")
|
logger.warning("Could only fetch one Bitcoin price.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,6 @@
|
||||||
"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-list.js",
|
||||||
"js/components/payment-chart.js",
|
|
||||||
"js/components.js",
|
"js/components.js",
|
||||||
"js/init-app.js"
|
"js/init-app.js"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue