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:
parent
e05404fcab
commit
9821be0406
4 changed files with 62 additions and 1 deletions
22
crud.py
22
crud.py
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue