Refactor cumulative sats chart logic: Implement timeline data usage for cumulative calculations, enhancing accuracy and performance. Introduce fallback mechanism to group cost basis history by date, ensuring unique entries and improved chart rendering. Add detailed chart configuration for better user interaction and visual clarity.

This commit is contained in:
padreug 2025-06-22 17:48:18 +02:00
parent fee7ba13b2
commit d50033f834

View file

@ -258,161 +258,163 @@ 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 aggregated 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:', timelineData) // If we have timeline data, use it (already grouped by day)
console.log('Timeline data length:', timelineData.length) if (timelineData.length > 0) {
// Calculate running totals from daily data
let runningSats = 0
const labels = []
const cumulativeSats = []
if (timelineData.length === 0) { timelineData.forEach(point => {
console.log('No timeline data available, falling back to cost basis data') runningSats += point.sats
// Fallback to cost_basis_history if no timeline data
const costBasisData = this.analyticsData.cost_basis_history || []
if (costBasisData.length === 0) {
console.log('No chart data available')
// Create gradient for placeholder chart
const placeholderGradient = ctx.createLinearGradient(0, 0, 0, 300)
placeholderGradient.addColorStop(0, 'rgba(255, 149, 0, 0.3)')
placeholderGradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
// Show placeholder chart const date = new Date(point.date)
this.dcaChart = new Chart(ctx, { if (!isNaN(date.getTime())) {
type: 'line', labels.push(date.toLocaleDateString('en-US', {
data: { month: 'short',
labels: ['Start Your DCA Journey'], day: 'numeric'
datasets: [{ }))
label: 'Total Sats Accumulated', cumulativeSats.push(runningSats)
data: [0],
borderColor: '#FF9500',
backgroundColor: placeholderGradient,
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 8,
pointBackgroundColor: '#FFFFFF',
pointBorderColor: '#FF9500',
pointBorderWidth: 3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: '#FFFFFF',
bodyColor: '#FFFFFF',
borderColor: '#FF9500',
borderWidth: 2,
cornerRadius: 8
}
},
scales: {
x: {
grid: { display: false },
ticks: {
color: '#666666',
font: { size: 12, weight: '500' }
}
},
y: {
beginAtZero: true,
grid: {
color: 'rgba(255, 149, 0, 0.1)',
drawBorder: false
},
ticks: {
color: '#666666',
font: { size: 12, weight: '500' },
callback: function(value) {
return value.toLocaleString() + ' sats'
}
}
}
}
}
})
return
}
// Group cost basis data by date to avoid duplicates
const groupedData = new Map()
costBasisData.forEach(point => {
const dateStr = new Date(point.date).toDateString()
if (!groupedData.has(dateStr)) {
groupedData.set(dateStr, point)
} else {
// Use the latest cumulative values for the same date
const existing = groupedData.get(dateStr)
if (point.cumulative_sats > existing.cumulative_sats) {
groupedData.set(dateStr, point)
}
} }
}) })
const chartData = Array.from(groupedData.values()).sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
)
const labels = chartData.map(point => {
// Handle different date formats with enhanced timezone handling
let date;
if (point.date) {
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 (typeof point.date === 'string' && point.date.includes('T')) {
// ISO string - parse and convert to local date
date = new Date(point.date);
// For display purposes, use the date part only to avoid timezone shifts
const localDateStr = date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0');
date = new Date(localDateStr + 'T00:00:00'); // Force local midnight
} else {
date = new Date(point.date);
}
// Check if date is valid
if (isNaN(date.getTime())) {
date = new Date();
}
} else {
date = new Date();
}
console.log('Formatted date:', date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
})
})
const cumulativeSats = chartData.map(point => point.cumulative_sats)
this.createChart(labels, cumulativeSats) this.createChart(labels, cumulativeSats)
return return
} }
// Calculate running totals for timeline data // Fallback to cost_basis_history but group by date to avoid duplicates
let runningSats = 0 console.log('No timeline data, using cost_basis_history as fallback')
const labels = [] const chartData = this.analyticsData.cost_basis_history || []
const cumulativeSats = [] console.log('Chart data sample:', chartData.slice(0, 2)) // Debug first 2 records
timelineData.forEach(point => { // Handle empty data case
runningSats += point.sats if (chartData.length === 0) {
console.log('No chart data available')
// Create gradient for placeholder chart
const placeholderGradient = ctx.createLinearGradient(0, 0, 0, 300)
placeholderGradient.addColorStop(0, 'rgba(255, 149, 0, 0.3)')
placeholderGradient.addColorStop(1, 'rgba(255, 149, 0, 0.05)')
const date = new Date(point.date) // Show placeholder chart
if (!isNaN(date.getTime())) { this.dcaChart = new Chart(ctx, {
labels.push(date.toLocaleDateString('en-US', { type: 'line',
month: 'short', data: {
day: 'numeric' labels: ['Start Your DCA Journey'],
})) datasets: [{
cumulativeSats.push(runningSats) label: 'Total Sats Accumulated',
data: [0],
borderColor: '#FF9500',
backgroundColor: placeholderGradient,
borderWidth: 3,
fill: true,
tension: 0.4,
pointRadius: 8,
pointBackgroundColor: '#FFFFFF',
pointBorderColor: '#FF9500',
pointBorderWidth: 3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
titleColor: '#FFFFFF',
bodyColor: '#FFFFFF',
borderColor: '#FF9500',
borderWidth: 2,
cornerRadius: 8
}
},
scales: {
x: {
grid: { display: false },
ticks: {
color: '#666666',
font: { size: 12, weight: '500' }
}
},
y: {
beginAtZero: true,
grid: {
color: 'rgba(255, 149, 0, 0.1)',
drawBorder: false
},
ticks: {
color: '#666666',
font: { size: 12, weight: '500' },
callback: function(value) {
return value.toLocaleString() + ' sats'
}
}
}
}
}
})
return
}
// Group cost_basis_history by date to eliminate duplicates
const groupedData = new Map()
chartData.forEach(point => {
const dateStr = new Date(point.date).toDateString()
if (!groupedData.has(dateStr)) {
groupedData.set(dateStr, point)
} else {
// Use the latest cumulative values for the same date
const existing = groupedData.get(dateStr)
if (point.cumulative_sats > existing.cumulative_sats) {
groupedData.set(dateStr, point)
}
} }
}) })
const uniqueChartData = Array.from(groupedData.values()).sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
)
const labels = uniqueChartData.map(point => {
// Handle different date formats with enhanced timezone handling
let date;
if (point.date) {
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 (typeof point.date === 'string' && point.date.includes('T')) {
// ISO string - parse and convert to local date
date = new Date(point.date);
// For display purposes, use the date part only to avoid timezone shifts
const localDateStr = date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0');
date = new Date(localDateStr + 'T00:00:00'); // Force local midnight
} else {
date = new Date(point.date);
}
// Check if date is valid
if (isNaN(date.getTime())) {
date = new Date();
}
} else {
date = new Date();
}
console.log('Formatted date:', date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
})
})
const cumulativeSats = uniqueChartData.map(point => point.cumulative_sats)
this.createChart(labels, cumulativeSats) this.createChart(labels, cumulativeSats)
}, },