182 lines
6.0 KiB
JavaScript
182 lines
6.0 KiB
JavaScript
/**
|
|
* Charts Module
|
|
* Handles data visualization and charts
|
|
*/
|
|
const Charts = (() => {
|
|
// Chart configuration
|
|
let weightChartInstance = null;
|
|
let dateFilter = {
|
|
startDate: null,
|
|
endDate: null
|
|
};
|
|
|
|
/**
|
|
* Initialize charts
|
|
*/
|
|
const init = () => {
|
|
// Load Chart.js from CDN if not available
|
|
if (typeof Chart === 'undefined') {
|
|
loadChartJS().then(() => {
|
|
renderWeightChart();
|
|
}).catch(error => {
|
|
console.error('Failed to load Chart.js:', error);
|
|
showChartError('Failed to load chart library');
|
|
});
|
|
} else {
|
|
renderWeightChart();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Load Chart.js and required adapters from CDN
|
|
* @returns {Promise} - Resolves when Chart.js is loaded
|
|
*/
|
|
const loadChartJS = () => {
|
|
return new Promise((resolve, reject) => {
|
|
// First load Chart.js core
|
|
const chartScript = document.createElement('script');
|
|
chartScript.src = 'https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js';
|
|
chartScript.crossOrigin = 'anonymous';
|
|
|
|
chartScript.onload = () => {
|
|
// Then load the date adapter
|
|
const adapterScript = document.createElement('script');
|
|
adapterScript.src = 'https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.min.js';
|
|
adapterScript.crossOrigin = 'anonymous';
|
|
|
|
adapterScript.onload = () => resolve();
|
|
adapterScript.onerror = () => reject(new Error('Failed to load Chart.js date adapter'));
|
|
|
|
document.head.appendChild(adapterScript);
|
|
};
|
|
|
|
chartScript.onerror = () => reject(new Error('Failed to load Chart.js'));
|
|
document.head.appendChild(chartScript);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Render weight chart
|
|
*/
|
|
const renderWeightChart = () => {
|
|
if (typeof Chart === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const chartContainer = document.getElementById('weight-chart');
|
|
|
|
// Get weight data with filters applied
|
|
const weights = DataManager.getWeights(dateFilter);
|
|
|
|
if (weights.length === 0) {
|
|
chartContainer.innerHTML = '<div class="placeholder-message">No weight data available for the selected period</div>';
|
|
return;
|
|
}
|
|
|
|
// Sort by date (oldest first for chart)
|
|
const sortedWeights = [...weights].sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
|
|
// Prepare data for the chart
|
|
const labels = sortedWeights.map(entry => entry.date);
|
|
const data = sortedWeights.map(entry => entry.weight);
|
|
|
|
// Clear existing chart
|
|
if (weightChartInstance) {
|
|
weightChartInstance.destroy();
|
|
}
|
|
|
|
// Create canvas element
|
|
chartContainer.innerHTML = '<canvas id="weight-chart-canvas"></canvas>';
|
|
const ctx = document.getElementById('weight-chart-canvas').getContext('2d');
|
|
|
|
// Create new chart
|
|
weightChartInstance = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: 'Weight (kg)',
|
|
data: data,
|
|
fill: false,
|
|
borderColor: '#4a6fa5',
|
|
tension: 0.1,
|
|
pointBackgroundColor: '#4a6fa5',
|
|
pointRadius: 5,
|
|
pointHoverRadius: 7
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'day',
|
|
displayFormats: {
|
|
day: 'MMM d'
|
|
}
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: 'Date'
|
|
}
|
|
},
|
|
y: {
|
|
title: {
|
|
display: true,
|
|
text: 'Weight (kg)'
|
|
},
|
|
beginAtZero: false
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'top'
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
title: function(tooltipItems) {
|
|
const date = new Date(tooltipItems[0].label);
|
|
return date.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Update the date filter and refresh the chart
|
|
* @param {string} startDate - Start date in YYYY-MM-DD format
|
|
* @param {string} endDate - End date in YYYY-MM-DD format
|
|
*/
|
|
const updateDateFilter = (startDate, endDate) => {
|
|
dateFilter.startDate = startDate || null;
|
|
dateFilter.endDate = endDate || null;
|
|
renderWeightChart();
|
|
};
|
|
|
|
/**
|
|
* Show error message in chart container
|
|
* @param {string} message - Error message
|
|
*/
|
|
const showChartError = (message) => {
|
|
const chartContainer = document.getElementById('weight-chart');
|
|
chartContainer.innerHTML = `<div class="placeholder-message error">${message}</div>`;
|
|
};
|
|
|
|
// Return public API
|
|
return {
|
|
init,
|
|
renderWeightChart,
|
|
updateDateFilter
|
|
};
|
|
})();
|