Enhance client dashboard summary: Add current fiat value of Bitcoin holdings and wallet currency to the summary metrics. Update model and frontend to display current value and unrealized gains/losses, improving financial insights for users.

This commit is contained in:
padreug 2025-06-22 16:02:43 +02:00
parent e05404fcab
commit 9821be0406
4 changed files with 62 additions and 1 deletions

22
crud.py
View file

@ -4,6 +4,8 @@ from typing import List, Optional
from datetime import datetime, timedelta
from lnbits.db import Database
from lnbits.utils.exchange_rates import satoshis_amount_as_fiat
from lnbits.core.crud.wallets import get_wallet
from .models import (
ClientDashboardSummary,
@ -32,6 +34,13 @@ async def get_client_dashboard_summary(user_id: str) -> Optional[ClientDashboard
if not client:
return None
# Get wallet to determine currency
wallet = await get_wallet(client["wallet_id"])
# TODO: Get currency from wallet; bit more difficult to do in a different
# currency than deposit cause of cross exchange rates
# currency = wallet.currency or "GTQ" # Default to GTQ if no currency set
currency = "GTQ" # Default to GTQ if no currency set
# Get total sats accumulated from DCA transactions
sats_result = await db.fetchone(
"""
@ -95,17 +104,28 @@ async def get_client_dashboard_summary(user_id: str) -> Optional[ClientDashboard
remaining_balance = confirmed_deposits - dca_spent # Remaining = deposits - DCA spending
avg_cost_basis = total_sats / dca_spent if dca_spent > 0 else 0 # Cost basis = sats / fiat spent
# Calculate current fiat value of total sats
current_sats_fiat_value = 0.0
if total_sats > 0:
try:
current_sats_fiat_value = await satoshis_amount_as_fiat(total_sats, currency)
except Exception as e:
print(f"Warning: Could not fetch exchange rate for {currency}: {e}")
current_sats_fiat_value = 0.0
return ClientDashboardSummary(
user_id=user_id,
total_sats_accumulated=total_sats,
total_fiat_invested=total_invested, # Sum of confirmed deposits
pending_fiat_deposits=pending_deposits, # Sum of pending deposits
current_sats_fiat_value=current_sats_fiat_value, # Current fiat value of sats
average_cost_basis=avg_cost_basis,
current_fiat_balance=remaining_balance, # Confirmed deposits - DCA spent
total_transactions=tx_stats["tx_count"] if tx_stats else 0,
dca_mode=client["dca_mode"],
dca_status=client["status"],
last_transaction_date=tx_stats["last_tx_date"] if tx_stats else None
last_transaction_date=tx_stats["last_tx_date"] if tx_stats else None,
currency=currency # Wallet's currency
)

View file

@ -13,6 +13,7 @@ class ClientDashboardSummary(BaseModel):
total_sats_accumulated: int
total_fiat_invested: int # Confirmed deposits
pending_fiat_deposits: int # Pending deposits awaiting confirmation
current_sats_fiat_value: float # Current fiat value of total sats
average_cost_basis: float # Average sats per fiat unit
current_fiat_balance: int # Available balance for DCA
total_transactions: int

View file

@ -21,6 +21,20 @@ window.app = Vue.createApp({
}).format(amount);
},
formatCurrencyWithCode(amount, currencyCode) {
if (!amount) return `${currencyCode} 0.00`;
// Format with the provided currency code
try {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
}).format(amount);
} catch (error) {
// Fallback if currency code is not supported
return `${currencyCode} ${amount.toFixed(2)}`;
}
},
formatDate(dateString) {
if (!dateString) return ''
return new Date(dateString).toLocaleDateString()

View file

@ -57,6 +57,32 @@
</div>
</div>
</div>
<!-- Current Value Row -->
<div class="row q-col-gutter-md q-mt-sm">
<div class="col-12 col-md-6">
<q-card flat class="bg-green-1">
<q-card-section class="text-center">
<div class="text-h5 text-green-8">${formatCurrencyWithCode(dashboardData.current_sats_fiat_value, dashboardData.currency)}</div>
<div class="text-caption text-green-7">Current Value of Bitcoin Holdings</div>
<div class="text-caption text-grey">at today's ${dashboardData.currency} exchange rate</div>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-6">
<q-card flat class="bg-blue-1">
<q-card-section class="text-center">
<div class="text-h6 text-blue-8">
${dashboardData.current_sats_fiat_value > (dashboardData.total_fiat_invested / 100) ? '+' : ''}
${formatCurrencyWithCode(dashboardData.current_sats_fiat_value - (dashboardData.total_fiat_invested / 100), dashboardData.currency)}
</div>
<div class="text-caption text-blue-7">
${dashboardData.current_sats_fiat_value > (dashboardData.total_fiat_invested / 100) ? 'Unrealized Gain' : 'Unrealized Loss'}
</div>
<div class="text-caption text-grey">vs total invested</div>
</q-card-section>
</q-card>
</div>
</div>
<!-- Pending Deposits Row -->
<div v-if="dashboardData.pending_fiat_deposits > 0" class="row q-col-gutter-md q-mt-sm">
<div class="col-12">