Refactor DCA client registration form: Update wallet selection to use walletOptions computed property for better data handling. Change element ID from 'dcaClient' to 'vue' for consistency. Improve error handling and data validation in chart loading and registration processes.
This commit is contained in:
parent
306549a656
commit
03179647ec
2 changed files with 85 additions and 89 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
window.app = Vue.createApp({
|
window.app = Vue.createApp({
|
||||||
el: '#dcaClient',
|
el: '#vue',
|
||||||
mixins: [windowMixin],
|
mixins: [windowMixin],
|
||||||
delimiters: ['${', '}'],
|
delimiters: ['${', '}'],
|
||||||
data: function () {
|
data: function () {
|
||||||
|
|
@ -14,7 +14,7 @@ window.app = Vue.createApp({
|
||||||
fixed_mode_daily_limit: null,
|
fixed_mode_daily_limit: null,
|
||||||
username: ''
|
username: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
// Dashboard state
|
// Dashboard state
|
||||||
dashboardData: null,
|
dashboardData: null,
|
||||||
transactions: [],
|
transactions: [],
|
||||||
|
|
@ -80,17 +80,17 @@ window.app = Vue.createApp({
|
||||||
'/satmachineclient/api/v1/registration-status',
|
'/satmachineclient/api/v1/registration-status',
|
||||||
this.g.user.wallets[0].adminkey
|
this.g.user.wallets[0].adminkey
|
||||||
)
|
)
|
||||||
|
|
||||||
this.isRegistered = data.is_registered
|
this.isRegistered = data.is_registered
|
||||||
this.registrationChecked = true
|
this.registrationChecked = true
|
||||||
|
|
||||||
if (!this.isRegistered) {
|
if (!this.isRegistered) {
|
||||||
this.showRegistrationDialog = true
|
this.showRegistrationDialog = true
|
||||||
// Pre-fill username and default wallet if available
|
// Pre-fill username and default wallet if available
|
||||||
this.registrationForm.username = this.g.user.username || ''
|
this.registrationForm.username = this.g.user.username || ''
|
||||||
this.registrationForm.selectedWallet = this.g.user.wallets[0] || null
|
this.registrationForm.selectedWallet = this.g.user.wallets[0]?.id || null
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking registration status:', error)
|
console.error('Error checking registration status:', error)
|
||||||
|
|
@ -108,26 +108,32 @@ window.app = Vue.createApp({
|
||||||
username: this.registrationForm.username || this.g.user.username || `user_${this.g.user.id.substring(0, 8)}`
|
username: this.registrationForm.username || this.g.user.username || `user_${this.g.user.id.substring(0, 8)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the selected wallet object to get the adminkey
|
||||||
|
const selectedWallet = this.g.user.wallets.find(w => w.id === this.registrationForm.selectedWallet)
|
||||||
|
if (!selectedWallet) {
|
||||||
|
throw new Error('Selected wallet not found')
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await LNbits.api.request(
|
const { data } = await LNbits.api.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/satmachineclient/api/v1/register',
|
'/satmachineclient/api/v1/register',
|
||||||
this.registrationForm.selectedWallet.adminkey,
|
selectedWallet.adminkey,
|
||||||
registrationData
|
registrationData
|
||||||
)
|
)
|
||||||
|
|
||||||
this.isRegistered = true
|
this.isRegistered = true
|
||||||
this.showRegistrationDialog = false
|
this.showRegistrationDialog = false
|
||||||
|
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: data.message || 'Successfully registered for DCA!',
|
message: data.message || 'Successfully registered for DCA!',
|
||||||
icon: 'check_circle',
|
icon: 'check_circle',
|
||||||
position: 'top'
|
position: 'top'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Load dashboard data after successful registration
|
// Load dashboard data after successful registration
|
||||||
await this.loadDashboardData()
|
await this.loadDashboardData()
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error registering client:', error)
|
console.error('Error registering client:', error)
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
|
|
@ -297,18 +303,18 @@ window.app = Vue.createApp({
|
||||||
console.log('Chart already loading, ignoring request')
|
console.log('Chart already loading, ignoring request')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.chartLoading = true
|
this.chartLoading = true
|
||||||
|
|
||||||
// Destroy existing chart immediately to prevent conflicts
|
// Destroy existing chart immediately to prevent conflicts
|
||||||
if (this.dcaChart) {
|
if (this.dcaChart) {
|
||||||
console.log('Destroying existing chart before loading new data')
|
console.log('Destroying existing chart before loading new data')
|
||||||
this.dcaChart.destroy()
|
this.dcaChart.destroy()
|
||||||
this.dcaChart = null
|
this.dcaChart = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const {data} = await LNbits.api.request(
|
const { data } = await LNbits.api.request(
|
||||||
'GET',
|
'GET',
|
||||||
`/satmachineclient/api/v1/dashboard/analytics?time_range=${this.chartTimeRange}`,
|
`/satmachineclient/api/v1/dashboard/analytics?time_range=${this.chartTimeRange}`,
|
||||||
this.g.user.wallets[0].adminkey
|
this.g.user.wallets[0].adminkey
|
||||||
|
|
@ -321,10 +327,10 @@ window.app = Vue.createApp({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.analyticsData = data
|
this.analyticsData = data
|
||||||
|
|
||||||
// Wait for DOM update and ensure we're still in loading state
|
// Wait for DOM update and ensure we're still in loading state
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
|
|
||||||
// Double-check we're still the active loading request
|
// Double-check we're still the active loading request
|
||||||
if (this.chartLoading) {
|
if (this.chartLoading) {
|
||||||
this.initDCAChart()
|
this.initDCAChart()
|
||||||
|
|
@ -343,13 +349,13 @@ window.app = Vue.createApp({
|
||||||
console.log('analyticsData:', this.analyticsData)
|
console.log('analyticsData:', this.analyticsData)
|
||||||
console.log('dcaChart ref:', this.$refs.dcaChart)
|
console.log('dcaChart ref:', this.$refs.dcaChart)
|
||||||
console.log('chartLoading state:', this.chartLoading)
|
console.log('chartLoading state:', this.chartLoading)
|
||||||
|
|
||||||
// Skip if we're not in a loading state (indicates this is a stale call)
|
// Skip if we're not in a loading state (indicates this is a stale call)
|
||||||
if (!this.chartLoading && this.dcaChart) {
|
if (!this.chartLoading && this.dcaChart) {
|
||||||
console.log('Chart already exists and not loading, skipping initialization')
|
console.log('Chart already exists and not loading, skipping initialization')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.analyticsData) {
|
if (!this.analyticsData) {
|
||||||
console.log('No analytics data available')
|
console.log('No analytics data available')
|
||||||
return
|
return
|
||||||
|
|
@ -371,7 +377,7 @@ window.app = Vue.createApp({
|
||||||
console.error('Chart.js is not loaded')
|
console.error('Chart.js is not loaded')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Chart.js version:', Chart.version || 'unknown')
|
console.log('Chart.js version:', Chart.version || 'unknown')
|
||||||
console.log('Chart.js available:', typeof Chart)
|
console.log('Chart.js available:', typeof Chart)
|
||||||
|
|
||||||
|
|
@ -383,45 +389,45 @@ window.app = Vue.createApp({
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctx = this.$refs.dcaChart.getContext('2d')
|
const ctx = this.$refs.dcaChart.getContext('2d')
|
||||||
|
|
||||||
// Use accumulation_timeline data which is already grouped by day
|
// Use accumulation_timeline data which is already grouped by day
|
||||||
const timelineData = this.analyticsData.accumulation_timeline || []
|
const timelineData = this.analyticsData.accumulation_timeline || []
|
||||||
console.log('Timeline data sample:', timelineData.slice(0, 2)) // Debug first 2 records
|
console.log('Timeline data sample:', timelineData.slice(0, 2)) // Debug first 2 records
|
||||||
|
|
||||||
// If we have timeline data, use it (already grouped by day)
|
// If we have timeline data, use it (already grouped by day)
|
||||||
if (timelineData.length > 0) {
|
if (timelineData.length > 0) {
|
||||||
// Calculate running totals from daily data
|
// Calculate running totals from daily data
|
||||||
let runningSats = 0
|
let runningSats = 0
|
||||||
const labels = []
|
const labels = []
|
||||||
const cumulativeSats = []
|
const cumulativeSats = []
|
||||||
|
|
||||||
timelineData.forEach(point => {
|
timelineData.forEach(point => {
|
||||||
// Ensure sats is a valid number
|
// Ensure sats is a valid number
|
||||||
const sats = point.sats || 0
|
const sats = point.sats || 0
|
||||||
const validSats = typeof sats === 'number' ? sats : parseFloat(sats) || 0
|
const validSats = typeof sats === 'number' ? sats : parseFloat(sats) || 0
|
||||||
runningSats += validSats
|
runningSats += validSats
|
||||||
|
|
||||||
const date = new Date(point.date)
|
const date = new Date(point.date)
|
||||||
if (!isNaN(date.getTime())) {
|
if (!isNaN(date.getTime())) {
|
||||||
labels.push(date.toLocaleDateString('en-US', {
|
labels.push(date.toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric'
|
||||||
}))
|
}))
|
||||||
cumulativeSats.push(runningSats)
|
cumulativeSats.push(runningSats)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Timeline chart data:', { labels, cumulativeSats })
|
console.log('Timeline chart data:', { labels, cumulativeSats })
|
||||||
|
|
||||||
this.createChart(labels, cumulativeSats)
|
this.createChart(labels, cumulativeSats)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to cost_basis_history but group by date to avoid duplicates
|
// Fallback to cost_basis_history but group by date to avoid duplicates
|
||||||
console.log('No timeline data, using cost_basis_history as fallback')
|
console.log('No timeline data, using cost_basis_history as fallback')
|
||||||
const chartData = this.analyticsData.cost_basis_history || []
|
const chartData = this.analyticsData.cost_basis_history || []
|
||||||
console.log('Chart data sample:', chartData.slice(0, 2)) // Debug first 2 records
|
console.log('Chart data sample:', chartData.slice(0, 2)) // Debug first 2 records
|
||||||
|
|
||||||
// Handle empty data case
|
// Handle empty data case
|
||||||
if (chartData.length === 0) {
|
if (chartData.length === 0) {
|
||||||
console.log('No chart data available')
|
console.log('No chart data available')
|
||||||
|
|
@ -429,7 +435,7 @@ window.app = Vue.createApp({
|
||||||
const placeholderGradient = ctx.createLinearGradient(0, 0, 0, 300)
|
const placeholderGradient = ctx.createLinearGradient(0, 0, 0, 300)
|
||||||
placeholderGradient.addColorStop(0, 'rgba(255, 149, 0, 0.3)')
|
placeholderGradient.addColorStop(0, 'rgba(255, 149, 0, 0.3)')
|
||||||
placeholderGradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
|
placeholderGradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
|
||||||
|
|
||||||
// Show placeholder chart with enhanced styling
|
// Show placeholder chart with enhanced styling
|
||||||
this.dcaChart = new Chart(ctx, {
|
this.dcaChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|
@ -463,7 +469,7 @@ window.app = Vue.createApp({
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
cornerRadius: 8,
|
cornerRadius: 8,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function (context) {
|
||||||
return `${context.parsed.y.toLocaleString()} sats`
|
return `${context.parsed.y.toLocaleString()} sats`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -486,7 +492,7 @@ window.app = Vue.createApp({
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#666666',
|
color: '#666666',
|
||||||
font: { size: 12, weight: '500' },
|
font: { size: 12, weight: '500' },
|
||||||
callback: function(value) {
|
callback: function (value) {
|
||||||
return value.toLocaleString() + ' sats'
|
return value.toLocaleString() + ' sats'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -498,7 +504,7 @@ window.app = Vue.createApp({
|
||||||
this.chartLoading = false
|
this.chartLoading = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group cost_basis_history by date to eliminate duplicates
|
// Group cost_basis_history by date to eliminate duplicates
|
||||||
const groupedData = new Map()
|
const groupedData = new Map()
|
||||||
chartData.forEach(point => {
|
chartData.forEach(point => {
|
||||||
|
|
@ -513,30 +519,30 @@ window.app = Vue.createApp({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const uniqueChartData = Array.from(groupedData.values()).sort((a, b) =>
|
const uniqueChartData = Array.from(groupedData.values()).sort((a, b) =>
|
||||||
new Date(a.date).getTime() - new Date(b.date).getTime()
|
new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||||
)
|
)
|
||||||
|
|
||||||
const labels = uniqueChartData.map(point => {
|
const labels = uniqueChartData.map(point => {
|
||||||
// Handle different date formats with enhanced timezone handling
|
// Handle different date formats with enhanced timezone handling
|
||||||
let date;
|
let date;
|
||||||
if (point.date) {
|
if (point.date) {
|
||||||
console.log('Raw date from API:', point.date); // Debug the actual date string
|
console.log('Raw date from API:', point.date); // Debug the actual date string
|
||||||
|
|
||||||
// If it's an ISO string with timezone info, parse it correctly
|
// If it's an ISO string with timezone info, parse it correctly
|
||||||
if (typeof point.date === 'string' && point.date.includes('T')) {
|
if (typeof point.date === 'string' && point.date.includes('T')) {
|
||||||
// ISO string - parse and convert to local date
|
// ISO string - parse and convert to local date
|
||||||
date = new Date(point.date);
|
date = new Date(point.date);
|
||||||
// For display purposes, use the date part only to avoid timezone shifts
|
// For display purposes, use the date part only to avoid timezone shifts
|
||||||
const localDateStr = date.getFullYear() + '-' +
|
const localDateStr = date.getFullYear() + '-' +
|
||||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||||
String(date.getDate()).padStart(2, '0');
|
String(date.getDate()).padStart(2, '0');
|
||||||
date = new Date(localDateStr + 'T00:00:00'); // Force local midnight
|
date = new Date(localDateStr + 'T00:00:00'); // Force local midnight
|
||||||
} else {
|
} else {
|
||||||
date = new Date(point.date);
|
date = new Date(point.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if date is valid
|
// Check if date is valid
|
||||||
if (isNaN(date.getTime())) {
|
if (isNaN(date.getTime())) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
|
|
@ -544,9 +550,9 @@ window.app = Vue.createApp({
|
||||||
} else {
|
} else {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Formatted date:', date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
|
console.log('Formatted date:', date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
|
||||||
|
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric'
|
day: 'numeric'
|
||||||
|
|
@ -557,22 +563,22 @@ window.app = Vue.createApp({
|
||||||
const sats = point.cumulative_sats || 0
|
const sats = point.cumulative_sats || 0
|
||||||
return typeof sats === 'number' ? sats : parseFloat(sats) || 0
|
return typeof sats === 'number' ? sats : parseFloat(sats) || 0
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('Final chart data:', { labels, cumulativeSats })
|
console.log('Final chart data:', { labels, cumulativeSats })
|
||||||
console.log('Labels array:', labels)
|
console.log('Labels array:', labels)
|
||||||
console.log('CumulativeSats array:', cumulativeSats)
|
console.log('CumulativeSats array:', cumulativeSats)
|
||||||
|
|
||||||
// Validate data before creating chart
|
// Validate data before creating chart
|
||||||
if (labels.length === 0 || cumulativeSats.length === 0) {
|
if (labels.length === 0 || cumulativeSats.length === 0) {
|
||||||
console.warn('No valid data for chart, skipping creation')
|
console.warn('No valid data for chart, skipping creation')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labels.length !== cumulativeSats.length) {
|
if (labels.length !== cumulativeSats.length) {
|
||||||
console.warn('Mismatched data arrays:', { labelsLength: labels.length, dataLength: cumulativeSats.length })
|
console.warn('Mismatched data arrays:', { labelsLength: labels.length, dataLength: cumulativeSats.length })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for any invalid values in cumulativeSats
|
// Check for any invalid values in cumulativeSats
|
||||||
const hasInvalidValues = cumulativeSats.some(val => val === null || val === undefined || isNaN(val))
|
const hasInvalidValues = cumulativeSats.some(val => val === null || val === undefined || isNaN(val))
|
||||||
if (hasInvalidValues) {
|
if (hasInvalidValues) {
|
||||||
|
|
@ -585,34 +591,34 @@ window.app = Vue.createApp({
|
||||||
|
|
||||||
createChart(labels, cumulativeSats) {
|
createChart(labels, cumulativeSats) {
|
||||||
console.log('createChart called with loading state:', this.chartLoading)
|
console.log('createChart called with loading state:', this.chartLoading)
|
||||||
|
|
||||||
if (!this.$refs.dcaChart) {
|
if (!this.$refs.dcaChart) {
|
||||||
console.log('Chart ref not available for createChart')
|
console.log('Chart ref not available for createChart')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if we're not in a loading state (indicates this is a stale call)
|
// Skip if we're not in a loading state (indicates this is a stale call)
|
||||||
if (!this.chartLoading) {
|
if (!this.chartLoading) {
|
||||||
console.log('Not in loading state, skipping createChart')
|
console.log('Not in loading state, skipping createChart')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy existing chart
|
// Destroy existing chart
|
||||||
if (this.dcaChart) {
|
if (this.dcaChart) {
|
||||||
console.log('Destroying existing chart in createChart')
|
console.log('Destroying existing chart in createChart')
|
||||||
this.dcaChart.destroy()
|
this.dcaChart.destroy()
|
||||||
this.dcaChart = null
|
this.dcaChart = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctx = this.$refs.dcaChart.getContext('2d')
|
const ctx = this.$refs.dcaChart.getContext('2d')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create gradient for the area fill
|
// Create gradient for the area fill
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, 300)
|
const gradient = ctx.createLinearGradient(0, 0, 0, 300)
|
||||||
gradient.addColorStop(0, 'rgba(255, 149, 0, 0.4)')
|
gradient.addColorStop(0, 'rgba(255, 149, 0, 0.4)')
|
||||||
gradient.addColorStop(0.5, 'rgba(255, 149, 0, 0.2)')
|
gradient.addColorStop(0.5, 'rgba(255, 149, 0, 0.2)')
|
||||||
gradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
|
gradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
|
||||||
|
|
||||||
// Small delay to ensure Chart.js is fully initialized
|
// Small delay to ensure Chart.js is fully initialized
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -621,7 +627,7 @@ window.app = Vue.createApp({
|
||||||
console.log('Loading state changed during timeout, aborting chart creation')
|
console.log('Loading state changed during timeout, aborting chart creation')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dcaChart = new Chart(ctx, {
|
this.dcaChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -662,7 +668,7 @@ window.app = Vue.createApp({
|
||||||
cornerRadius: 8,
|
cornerRadius: 8,
|
||||||
displayColors: false,
|
displayColors: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
title: function(context) {
|
title: function (context) {
|
||||||
return `📅 ${context[0].label}`
|
return `📅 ${context[0].label}`
|
||||||
},
|
},
|
||||||
label: function (context) {
|
label: function (context) {
|
||||||
|
|
@ -741,10 +747,10 @@ window.app = Vue.createApp({
|
||||||
async created() {
|
async created() {
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
// Check registration status first
|
// Check registration status first
|
||||||
await this.checkRegistrationStatus()
|
await this.checkRegistrationStatus()
|
||||||
|
|
||||||
// Only load dashboard data if registered
|
// Only load dashboard data if registered
|
||||||
if (this.isRegistered) {
|
if (this.isRegistered) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|
@ -768,7 +774,7 @@ window.app = Vue.createApp({
|
||||||
console.log('Loading state:', this.loading)
|
console.log('Loading state:', this.loading)
|
||||||
console.log('Chart ref available:', !!this.$refs.dcaChart)
|
console.log('Chart ref available:', !!this.$refs.dcaChart)
|
||||||
console.log('Analytics data available:', !!this.analyticsData)
|
console.log('Analytics data available:', !!this.analyticsData)
|
||||||
|
|
||||||
if (this.analyticsData && this.$refs.dcaChart) {
|
if (this.analyticsData && this.$refs.dcaChart) {
|
||||||
console.log('Initializing chart from mounted hook')
|
console.log('Initializing chart from mounted hook')
|
||||||
this.initDCAChart()
|
this.initDCAChart()
|
||||||
|
|
@ -781,6 +787,14 @@ window.app = Vue.createApp({
|
||||||
computed: {
|
computed: {
|
||||||
hasData() {
|
hasData() {
|
||||||
return this.dashboardData && !this.loading && this.isRegistered
|
return this.dashboardData && !this.loading && this.isRegistered
|
||||||
|
},
|
||||||
|
|
||||||
|
walletOptions() {
|
||||||
|
if (!this.g.user?.wallets) return []
|
||||||
|
return this.g.user.wallets.map(wallet => ({
|
||||||
|
label: `${wallet.name} (${Math.round(wallet.balance_msat / 1000)} sats)`,
|
||||||
|
value: wallet.id
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
<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>
|
<script src="{{ static_url_for('satmachineclient/static', path='js/index.js') }}"></script>
|
||||||
{% endblock %} {% block page %}
|
{% endblock %} {% block page %}
|
||||||
<div class="row q-col-gutter-md" id="dcaClient">
|
<div class="row q-col-gutter-md" id="vue">
|
||||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||||
|
|
||||||
<!-- Loading State -->
|
<!-- Loading State -->
|
||||||
|
|
@ -542,49 +542,32 @@
|
||||||
label="Username (Optional)"
|
label="Username (Optional)"
|
||||||
placeholder="Enter a friendly name"
|
placeholder="Enter a friendly name"
|
||||||
hint="How you'd like to be identified in the system"
|
hint="How you'd like to be identified in the system"
|
||||||
/>
|
></q-input>
|
||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
|
emit-value
|
||||||
v-model="registrationForm.selectedWallet"
|
v-model="registrationForm.selectedWallet"
|
||||||
:options="g.user.wallets"
|
:options="walletOptions"
|
||||||
option-label="name"
|
|
||||||
label="DCA Wallet *"
|
label="DCA Wallet *"
|
||||||
hint="Choose which wallet will receive your Bitcoin DCA purchases"
|
hint="Choose which wallet will receive your Bitcoin DCA purchases"
|
||||||
>
|
></q-select>
|
||||||
<template v-slot:option="scope">
|
|
||||||
<q-item v-bind="scope.itemProps">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>
|
|
||||||
${scope.opt.name} (ID: ${scope.opt.id.substring(0, 8)}... • ${Math.round(scope.opt.balance_msat / 1000)} sats)
|
|
||||||
</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<q-select
|
<q-select
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
|
emit-value
|
||||||
v-model="registrationForm.dca_mode"
|
v-model="registrationForm.dca_mode"
|
||||||
:options="[
|
:options="[
|
||||||
{ label: 'Flow Mode (Recommended)', value: 'flow', description: 'Proportional distribution based on your balance' },
|
{ label: 'Flow Mode (Recommended)', value: 'flow' },
|
||||||
{ label: 'Fixed Mode', value: 'fixed', description: 'Set daily purchase limits' }
|
{ label: 'Fixed Mode', value: 'fixed' }
|
||||||
]"
|
]"
|
||||||
option-label="label"
|
option-label="label"
|
||||||
option-value="value"
|
option-value="value"
|
||||||
label="DCA Strategy *"
|
label="DCA Strategy *"
|
||||||
hint="Choose how your Bitcoin purchases will be distributed"
|
hint="Choose how your Bitcoin purchases will be distributed"
|
||||||
>
|
></q-select>
|
||||||
<template v-slot:option="scope">
|
|
||||||
<q-item v-bind="scope.itemProps">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>${scope.opt.label} - ${scope.opt.description}</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</template>
|
|
||||||
</q-select>
|
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
v-if="registrationForm.dca_mode === 'fixed'"
|
v-if="registrationForm.dca_mode === 'fixed'"
|
||||||
|
|
@ -598,7 +581,7 @@
|
||||||
:rules="[
|
:rules="[
|
||||||
val => registrationForm.dca_mode !== 'fixed' || (val && val > 0) || 'Daily limit is required for fixed mode'
|
val => registrationForm.dca_mode !== 'fixed' || (val && val > 0) || 'Daily limit is required for fixed mode'
|
||||||
]"
|
]"
|
||||||
/>
|
></q-input>
|
||||||
|
|
||||||
<q-banner class="bg-blue-1 text-blue-9 q-mt-md">
|
<q-banner class="bg-blue-1 text-blue-9 q-mt-md">
|
||||||
<template v-slot:avatar>
|
<template v-slot:avatar>
|
||||||
|
|
@ -619,8 +602,7 @@
|
||||||
class="full-width"
|
class="full-width"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
<q-icon name="rocket_launch" class="q-mr-sm" />
|
🚀 Start My DCA Journey
|
||||||
Start My DCA Journey
|
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue