satmachineclient/templates/satmachineclient/index.html

509 lines
20 KiB
HTML

<!--/////////////////////////////////////////////////-->
<!--//PAGE FOR THE DCA CLIENT EXTENSION IN LNBITS/////-->
<!--/////////////////////////////////////////////////-->
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block scripts %} {{ window_vars(user) }}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script src="{{ static_url_for('satmachineclient/static', path='js/index.js') }}"></script>
{% endblock %} {% block page %}
<div class="row q-col-gutter-md" id="dcaClient">
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
<!-- Loading State -->
<q-card v-if="loading">
<q-card-section class="text-center">
<q-spinner size="2em" />
<div class="q-mt-md">Loading your DCA dashboard...</div>
</q-card-section>
</q-card>
<!-- Error State -->
<q-card v-if="error && !loading" class="bg-negative text-white">
<q-card-section>
<q-icon name="error" class="q-mr-sm" />
${error}
</q-card-section>
</q-card>
<!-- Dashboard Content -->
<div v-if="hasData">
<!-- Hero Card - Bitcoin Stack Progress -->
<q-card class="q-mb-md bg-gradient-to-r from-orange-1 to-orange-2" style="background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);">
<q-card-section class="text-center">
<div class="row items-center">
<div class="col-12 col-md-8">
<div class="text-h4 text-orange-8 q-mb-sm">
⚡ ${formatSats(dashboardData.total_sats_accumulated)}
<q-spinner-gears
v-if="dashboardData.current_fiat_balance > 0 && dashboardData.dca_status === 'active'"
color="orange-7"
size="1.2em"
class="q-ml-sm"
/>
</div>
<div class="text-h6 text-grey-7 q-mb-xs">
stacked and growing!
<span v-if="dashboardData.current_fiat_balance > 0 && dashboardData.dca_status === 'active'" class="text-orange-7 q-ml-xs">
• actively stacking
</span>
</div>
<div class="text-caption text-grey-6">
${dashboardData.total_transactions} DCA ${dashboardData.total_transactions === 1 ? 'purchase' : 'purchases'} • <span class="text-weight-bold text-blue-8">${dashboardData.dca_mode}</span> mode
</div>
</div>
<div class="col-12 col-md-4 q-mt-md">
<q-circular-progress
show-value
font-size="12px"
:value="getMilestoneProgress()"
size="80px"
:thickness="0.25"
color="orange-7"
track-color="orange-1"
class="q-ma-md text-orange-8"
>
${(getMilestoneProgress() || 0).toFixed(2)}%
</q-circular-progress>
<div class="text-body2 text-weight-medium text-grey-7">to ${getNextMilestone().name}</div>
</div>
</div>
</q-card-section>
</q-card>
<!-- Key Metrics Cards -->
<div class="row q-col-gutter-md q-mb-md">
<div class="col-6 col-md-3">
<q-card class="text-center bg-orange-1" style="min-height: 100px;">
<q-card-section class="q-pa-md">
<div class="text-h6 text-orange-8">${formatCurrency(dashboardData.total_fiat_invested)}</div>
<div class="text-caption text-orange-7 text-weight-medium">Total Invested</div>
</q-card-section>
</q-card>
</div>
<div class="col-6 col-md-3">
<q-card class="text-center bg-blue-1" style="min-height: 100px;">
<q-card-section class="q-pa-md">
<div class="text-h6 text-blue-8">${formatCurrency(dashboardData.current_fiat_balance)}</div>
<div class="text-caption text-blue-7 text-weight-medium">Available Balance</div>
</q-card-section>
</q-card>
</div>
<div class="col-6 col-md-3">
<q-card class="text-center bg-green-1" style="min-height: 100px;">
<q-card-section class="q-pa-md">
<div class="text-h5 text-weight-bold text-green-8">${dashboardData.total_transactions}</div>
<div class="text-caption text-green-7 text-weight-medium">DCA Purchases</div>
</q-card-section>
</q-card>
</div>
<div class="col-6 col-md-3">
<q-card class="text-center bg-purple-1" style="min-height: 100px;">
<q-card-section class="q-pa-md">
<div class="text-h6 text-purple-8" v-if="dashboardData.average_cost_basis > 0">
${Math.round(dashboardData.average_cost_basis)}
</div>
<div class="text-h6 text-purple-8" v-else>-</div>
<div class="text-caption text-purple-7 text-weight-medium">Avg Cost (sats/GTQ)</div>
</q-card-section>
</q-card>
</div>
</div>
<!-- Bitcoin Performance Card -->
<q-card class="q-mb-md">
<q-card-section>
<div class="row items-center q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">
Bitcoin Performance
</h6>
</div>
<div class="col-auto">
<q-btn
flat
dense
:color="showFiatValues ? 'orange' : 'grey'"
:icon="showFiatValues ? 'visibility_off' : 'attach_money'"
@click="showFiatValues = !showFiatValues"
size="sm"
>
${showFiatValues ? 'Hide' : 'Show'} Fiat Values
</q-btn>
</div>
</div>
<!-- Performance Cards (Conditional) -->
<div v-if="showFiatValues" class="row q-col-gutter-md">
<div class="col-12 col-md-6">
<q-card flat class="bg-orange-1">
<q-card-section class="text-center q-pa-md">
<div class="text-h5 text-orange-8">${formatCurrencyWithCode(dashboardData.current_sats_fiat_value, dashboardData.currency)}</div>
<div class="text-caption text-orange-7 q-mb-xs">Current Bitcoin Value</div>
<div class="text-caption text-grey">at today's ${dashboardData.currency} rate</div>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-6">
<q-card flat :class="(dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) > dashboardData.total_fiat_invested ? 'bg-green-1' : 'bg-red-1'">
<q-card-section class="text-center q-pa-md">
<div class="text-h5" :class="(dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) > dashboardData.total_fiat_invested ? 'text-green-8' : 'text-red-8'">
${(dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) > dashboardData.total_fiat_invested ? '📈 +' : '📉 '}
${formatCurrencyWithCode((dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) - dashboardData.total_fiat_invested, dashboardData.currency)}
</div>
<div class="text-caption" :class="(dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) > dashboardData.total_fiat_invested ? 'text-green-7' : 'text-red-7'">
${(dashboardData.current_sats_fiat_value + dashboardData.current_fiat_balance) > dashboardData.total_fiat_invested ? 'Portfolio Growth' : 'Portfolio Change'}
</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="q-mt-md">
<q-banner rounded class="bg-orange-1 text-orange-9">
<template v-slot:avatar>
<q-icon name="schedule" color="orange" size="md" />
</template>
<div class="text-subtitle2">
<strong>${formatCurrency(dashboardData.pending_fiat_deposits)}</strong> ready to DCA
</div>
<div class="text-caption">
Cash waiting to be inserted into ATM for automatic Bitcoin purchases
</div>
</q-banner>
</div>
</q-card-section>
</q-card>
<!-- DCA Status & Strategy -->
<q-card class="q-mb-md">
<q-card-section>
<div class="row items-center q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">
DCA Strategy
</h6>
</div>
<div class="col-auto">
<q-chip
:color="dashboardData.dca_status === 'active' ? 'positive' : 'warning'"
text-color="white"
icon="circle"
size="sm"
>
${dashboardData.dca_status}
</q-chip>
</div>
</div>
<div class="row q-col-gutter-md">
<div class="col-12 col-md-6">
<q-card flat class="bg-blue-1">
<q-card-section class="q-pa-md">
<div class="text-h6 text-blue-8">
<strong>${dashboardData.dca_mode}</strong> Mode
</div>
<div class="text-caption text-blue-7 q-mt-xs">
Automatic Bitcoin accumulation strategy
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-6" v-if="dashboardData.average_cost_basis > 0">
<q-card flat class="bg-purple-1">
<q-card-section class="q-pa-md">
<div class="text-body2 text-purple-8">
<span class="text-h6"><strong>${Math.round(dashboardData.average_cost_basis)}</strong></span> sats/GTQ
</div>
<div class="text-caption text-purple-7 q-mt-xs">
Average cost basis over time
</div>
</q-card-section>
</q-card>
</div>
</div>
</q-card-section>
</q-card>
</div>
<!-- DCA Performance Chart - Always render to ensure canvas is available -->
<q-card class="q-mb-md">
<q-card-section>
<h6 class="text-subtitle2 q-my-none q-mb-md">Bitcoin Accumulation Progress</h6>
<div class="chart-container" style="position: relative; height: 300px;">
<canvas ref="dcaChart" style="max-height: 300px;"></canvas>
</div>
<div class="row q-mt-sm">
<div class="col">
<q-btn-toggle
v-model="chartTimeRange"
@update:model-value="loadChartData"
toggle-color="orange"
:options="[
{label: '7D', value: '7d'},
{label: '30D', value: '30d'},
{label: '90D', value: '90d'},
{label: 'ALL', value: 'all'}
]"
size="sm"
flat
:disable="chartLoading"
/>
</div>
</div>
</q-card-section>
</q-card>
<!-- Dashboard Content -->
<div v-if="hasData">
<!-- Transaction History -->
<q-card>
<q-card-section>
<div class="row items-center q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">
DCA Transaction History
</h6>
<div class="text-caption text-grey-6">
Your Bitcoin accumulation journey
</div>
</div>
<div class="col-auto">
<q-btn
flat
dense
icon="refresh"
@click="refreshAllData"
:loading="loading"
size="sm"
color="orange"
>
Refresh
</q-btn>
</div>
</div>
<q-table
:rows="transactions"
:columns="transactionColumns"
row-key="id"
:pagination="transactionPagination"
:loading="loading"
flat
bordered
:no-data-label="'🚀 No transactions yet - start your DCA journey!'"
class="q-mt-md"
>
<template v-slot:body-cell-amount_sats="props">
<q-td :props="props" class="text-center">
<div class="text-weight-bold text-orange-8">
${formatSats(props.row.amount_sats)}
</div>
</q-td>
</template>
<template v-slot:body-cell-amount_fiat="props">
<q-td :props="props" class="text-center">
<div class="text-weight-medium">
${formatCurrency(props.row.amount_fiat)}
</div>
</q-td>
</template>
<template v-slot:body-cell-date="props">
<q-td :props="props">
<div class="text-weight-medium">${formatDate(props.value)}</div>
<div class="text-caption text-grey">
${formatTime(props.value)}
</div>
</q-td>
</template>
<template v-slot:body-cell-status="props">
<q-td :props="props" class="text-center">
<q-chip
:color="props.row.status === 'confirmed' ? 'positive' : props.row.status === 'failed' ? 'negative' : 'warning'"
text-color="white"
size="sm"
:icon="props.row.status === 'confirmed' ? 'check_circle' : props.row.status === 'failed' ? 'error' : 'schedule'"
>
${props.row.status}
</q-chip>
</q-td>
</template>
<template v-slot:body-cell-type="props">
<q-td :props="props" class="text-center">
<q-badge
:color="props.row.transaction_type === 'flow' ? 'blue' : 'purple'"
:label="props.row.transaction_type.toUpperCase()"
/>
</q-td>
</template>
<template v-slot:no-data="{ message }">
<div class="full-width row flex-center q-pa-lg">
<div class="text-center">
<q-icon name="rocket_launch" size="3em" class="text-orange-5 q-mb-md" />
<div class="text-h6 text-grey-7 q-mb-sm">${message}</div>
<div class="text-caption text-grey-5">
Visit your nearest Lamassu ATM to begin stacking sats automatically
</div>
</div>
</div>
</template>
</q-table>
</q-card-section>
</q-card>
</div>
</div>
<!-- Sidebar -->
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<!-- Welcome Card -->
<q-card class="bg-gradient-to-br" style="background: linear-gradient(135deg, #F3E5F5 0%, #E1F5FE 100%);">
<q-card-section>
<div class="text-center">
<h6 class="text-subtitle1 q-my-none text-orange-8">
Bitcoin DCA Dashboard
</h6>
<p class="text-body2 text-grey-7 q-mb-none">
Your automated sat stacking journey with {{SITE_TITLE}}
</p>
</div>
</q-card-section>
</q-card>
<!-- Quick Actions -->
<q-card>
<q-card-section>
<h6 class="text-subtitle2 q-my-none q-mb-md">
Quick Actions
</h6>
<div class="q-gutter-sm">
<q-btn
outline
color="orange"
icon="refresh"
label="Refresh Data"
@click="refreshAllData()"
:loading="loading"
class="full-width"
size="md"
/>
<q-btn
outline
color="blue"
icon="download"
label="Export History"
class="full-width"
size="md"
disable
>
<q-tooltip>Export functionality coming soon!</q-tooltip>
</q-btn>
</div>
</q-card-section>
</q-card>
<!-- Stacking Milestones -->
<q-card v-if="dashboardData">
<q-card-section>
<h6 class="text-subtitle2 q-my-none q-mb-md">
🏆 Stacking Milestones
</h6>
<q-list dense>
<q-item>
<q-item-section avatar>
<div v-if="dashboardData.total_sats_accumulated >= 10000" class="text-positive text-h6"></div>
<div v-else class="text-grey-5 text-h6"></div>
</q-item-section>
<q-item-section>
<q-item-label class="text-body2">10,000 sats</q-item-label>
<q-item-label caption>First milestone 🎯</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<div v-if="dashboardData.total_sats_accumulated >= 100000" class="text-positive text-h6"></div>
<div v-else class="text-grey-5 text-h6"></div>
</q-item-section>
<q-item-section>
<q-item-label class="text-body2">100,000 sats</q-item-label>
<q-item-label caption>Getting serious 🚀</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<div v-if="dashboardData.total_sats_accumulated >= 500000" class="text-positive text-h6"></div>
<div v-else class="text-grey-5 text-h6"></div>
</q-item-section>
<q-item-section>
<q-item-label class="text-body2">500,000 sats</q-item-label>
<q-item-label caption>Half a million! 🔥</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<div v-if="dashboardData.total_sats_accumulated >= 1000000" class="text-positive text-h6"></div>
<div v-else class="text-grey-5 text-h6"></div>
</q-item-section>
<q-item-section>
<q-item-label class="text-body2">1,000,000 sats</q-item-label>
<q-item-label caption>True HODLer 💎</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
<!-- DCA Tips -->
<q-card>
<q-card-section>
<h6 class="text-subtitle2 q-my-none q-mb-md">
💡 DCA Tips
</h6>
<q-list dense>
<q-item>
<q-item-section avatar>
<div class="text-orange text-weight-bold">📈</div>
</q-item-section>
<q-item-section>
<q-item-label class="text-caption">
Consistency beats timing - small, regular purchases smooth out volatility
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<div class="text-blue text-weight-bold"></div>
</q-item-section>
<q-item-section>
<q-item-label class="text-caption">
Time in the market beats timing the market
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<div class="text-green text-weight-bold">🔐</div>
</q-item-section>
<q-item-section>
<q-item-label class="text-caption">
Each sat purchased is sovereignty gained
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</div>
</div>
{% endblock %}