Update client API and frontend for DCA dashboard: Modify API endpoints to correctly reference wallet user, enhance Vue.js component with loading and error states, and implement data fetching for dashboard summary and transactions. Update HTML template to reflect new client extension structure and improve user experience with dynamic content rendering.

This commit is contained in:
padreug 2025-06-22 13:07:23 +02:00
parent cc12d68953
commit f779d000b4
3 changed files with 162 additions and 20 deletions

View file

@ -1,25 +1,25 @@
window.app = Vue.createApp({
el: '#vue',
mixins: [windowMixin],
delimiters: ['${', '}'],
data: function () {
data() {
return {
dashboardData: null,
transactions: [],
analytics: null,
loading: true,
error: null
}
},
///////////////////////////////////////////////////
////////////////METHODS FUNCTIONS//////////////////
///////////////////////////////////////////////////
methods: {
// Utility Methods
formatCurrency(amount) {
if (!amount) return 'Q 0.00';
// Convert centavos to quetzales
return new Intl.NumberFormat('es-GT', {
style: 'currency',
currency: 'GTQ',
}).format(amount);
}).format(amount / 100);
},
formatDate(dateString) {
@ -38,16 +38,54 @@ window.app = Vue.createApp({
return new Intl.NumberFormat('en-US').format(amount) + ' sats'
},
async loadDashboardData() {
try {
const {data} = await LNbits.api.request(
'GET',
'/satmachineclient/api/v1/dashboard/summary',
this.g.user.wallets[0].inkey
)
this.dashboardData = data
} catch (error) {
console.error('Error loading dashboard data:', error)
this.error = 'Failed to load dashboard data'
}
},
///////////////////////////////////////////////////
//////LIFECYCLE FUNCTIONS RUNNING ON PAGE LOAD/////
///////////////////////////////////////////////////
async loadTransactions() {
try {
const {data} = await LNbits.api.request(
'GET',
'/satmachineclient/api/v1/dashboard/transactions?limit=10',
this.g.user.wallets[0].inkey
)
this.transactions = data
} catch (error) {
console.error('Error loading transactions:', error)
}
}
},
async created() {
// Load DCA client data
try {
this.loading = true
await Promise.all([
this.loadDashboardData(),
this.loadTransactions()
])
} catch (error) {
console.error('Error initializing dashboard:', error)
this.error = 'Failed to initialize dashboard'
} finally {
this.loading = false
}
},
computed: {
hasData() {
return this.dashboardData && !this.loading
}
}
})
window.app.mount('#dcaClient')

View file

@ -1,5 +1,5 @@
<!--/////////////////////////////////////////////////-->
<!--//PAGE FOR THE DCA ADMIN EXTENSION IN LNBITS//////-->
<!--//PAGE FOR THE DCA CLIENT EXTENSION IN LNBITS/////-->
<!--/////////////////////////////////////////////////-->
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
@ -7,6 +7,110 @@
<script src="{{ static_url_for('satmachineclient/static', path='js/index.js') }}"></script>
{% endblock %} {% block page %}
<div class="row q-col-gutter-md" id="dcaClient">
<!-- Loading State -->
<div v-if="loading" class="col-12">
<q-card class="q-pa-md text-center">
<q-spinner size="2em" />
<div class="q-mt-md">Loading your DCA dashboard...</div>
</q-card>
</div>
<!-- Error State -->
<div v-if="error && !loading" class="col-12">
<q-banner class="bg-negative text-white">
<template v-slot:avatar>
<q-icon name="error" />
</template>
${error}
</q-banner>
</div>
<!-- Dashboard Content -->
<div v-if="hasData" class="col-12">
<!-- Summary Cards -->
<div class="row q-col-gutter-md q-mb-lg">
<div class="col-12 col-md-3">
<q-card class="q-pa-md text-center">
<div class="text-h6">${formatSats(dashboardData.total_sats_accumulated)}</div>
<div class="text-grey">Total Sats Accumulated</div>
</q-card>
</div>
<div class="col-12 col-md-3">
<q-card class="q-pa-md text-center">
<div class="text-h6">${formatCurrency(dashboardData.total_fiat_invested)}</div>
<div class="text-grey">Total Invested</div>
</q-card>
</div>
<div class="col-12 col-md-3">
<q-card class="q-pa-md text-center">
<div class="text-h6">${formatCurrency(dashboardData.current_fiat_balance)}</div>
<div class="text-grey">Available Balance</div>
</q-card>
</div>
<div class="col-12 col-md-3">
<q-card class="q-pa-md text-center">
<div class="text-h6">${dashboardData.total_transactions}</div>
<div class="text-grey">Total Transactions</div>
</q-card>
</div>
</div>
<!-- DCA Status -->
<div class="row q-col-gutter-md q-mb-lg">
<div class="col-12">
<q-card class="q-pa-md">
<div class="text-h6 q-mb-md">DCA Status</div>
<div class="row q-col-gutter-md">
<div class="col-6">
<div class="text-body2">Mode: <strong>${dashboardData.dca_mode}</strong></div>
</div>
<div class="col-6">
<div class="text-body2">Status:
<q-chip
:color="dashboardData.dca_status === 'active' ? 'positive' : 'warning'"
text-color="white"
size="sm"
>
${dashboardData.dca_status}
</q-chip>
</div>
</div>
</div>
<div v-if="dashboardData.average_cost_basis > 0" class="q-mt-md">
<div class="text-body2">Average Cost Basis: <strong>${Math.round(dashboardData.average_cost_basis)} sats/GTQ</strong></div>
</div>
</q-card>
</div>
</div>
<!-- Recent Transactions -->
<div class="row q-col-gutter-md">
<div class="col-12">
<q-card class="q-pa-md">
<div class="text-h6 q-mb-md">Recent Transactions</div>
<div v-if="transactions.length === 0" class="text-grey text-center q-pa-md">
No transactions yet
</div>
<q-list v-else>
<q-item v-for="tx in transactions" :key="tx.id">
<q-item-section>
<q-item-label>${formatSats(tx.amount_sats)}</q-item-label>
<q-item-label caption>${formatCurrency(tx.amount_fiat)} • ${formatDate(tx.created_at)}</q-item-label>
</q-item-section>
<q-item-section side>
<q-chip
:color="tx.status === 'confirmed' ? 'positive' : 'warning'"
text-color="white"
size="sm"
>
${tx.status}
</q-chip>
</q-item-section>
</q-item>
</q-list>
</q-card>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -35,7 +35,7 @@ async def api_get_dashboard_summary(
wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> ClientDashboardSummary:
"""Get client dashboard summary metrics"""
summary = await get_client_dashboard_summary(wallet.user)
summary = await get_client_dashboard_summary(wallet.wallet.user)
if not summary:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
@ -55,7 +55,7 @@ async def api_get_client_transactions(
) -> List[ClientTransaction]:
"""Get client's DCA transaction history with filtering"""
return await get_client_transactions(
wallet.user,
wallet.wallet.user,
limit=limit,
offset=offset,
transaction_type=transaction_type,
@ -70,7 +70,7 @@ async def api_get_client_analytics(
time_range: str = Query("30d", regex="^(7d|30d|90d|1y|all)$"),
) -> ClientAnalytics:
"""Get client performance analytics and cost basis data"""
analytics = await get_client_analytics(wallet.user, time_range)
analytics = await get_client_analytics(wallet.wallet.user, time_range)
if not analytics:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
@ -85,7 +85,7 @@ async def api_update_client_settings(
wallet: WalletTypeInfo = Depends(require_invoice_key),
) -> dict:
"""Update client DCA settings (mode, limits, status)"""
client = await get_client_by_user_id(wallet.user)
client = await get_client_by_user_id(wallet.wallet.user)
if not client:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND,
@ -111,7 +111,7 @@ async def api_export_transactions(
):
"""Export client transaction history"""
transactions = await get_client_transactions(
wallet.user,
wallet.wallet.user,
limit=10000, # Large limit for export
start_date=start_date,
end_date=end_date