File: assets/js/Charts.js
(async function () {
console.log('Charts.js loaded');
const DATA_URL = '/health-data/walking_data.json';
const WEEK_CONTAINER_ID = 'weekly-chart';
const YEAR_CONTAINER_ID = 'yearly-chart';
try {
const response = await fetch(DATA_URL);
if (!response.ok) throw new Error(\`Fetch failed: \${response.status}\`);
const rawData = await response.json();
if (typeof rawData !== 'object' || Array.isArray(rawData)) throw new Error('Unexpected JSON structure');
const entries = Object.entries(rawData)
.filter(([date, km]) => typeof km === 'number' && km >= 0)
.sort(([a], [b]) => a.localeCompare(b));
if (entries.length === 0) throw new Error('No data points found');
// ========================
// Hilfsfunktionen
// ========================
function getWeekStart(date) {
const d = new Date(date);
const day = d.getDay();
d.setDate(d.getDate() - (day === 0 ? 6 : day - 1)); // Montag
return d;
}
function getWeekDates(startDate) {
const dates = [];
for (let i = 0; i < 7; i++) {
const d = new Date(startDate);
d.setDate(d.getDate() + i);
dates.push(d.toISOString().slice(0, 10));
}
return dates;
}
function getWeekData(startDate) {
return getWeekDates(startDate).map(d => rawData[d] || 0);
}
// ========================
// Wochen-Chart
// ========================
let latestDate = entries[entries.length - 1][0];
let currentWeekStart = getWeekStart(latestDate);
const weeklyContainer = document.getElementById(WEEK_CONTAINER_ID);
if (!weeklyContainer) throw new Error(\`#\${WEEK_CONTAINER_ID} not found\`);
weeklyContainer.innerHTML = ''; // clean container
// Navigation Pfeile
const navContainer = document.createElement('div');
navContainer.style.display = 'flex';
navContainer.style.justifyContent = 'space-between';
navContainer.style.alignItems = 'center';
navContainer.style.marginBottom = '2px';
const leftArrow = document.createElement('span');
leftArrow.textContent = '◀';
leftArrow.style.cursor = 'pointer';
leftArrow.style.fontSize = '14px';
leftArrow.title = 'Vorherige Woche';
const rightArrow = document.createElement('span');
rightArrow.textContent = '▶';
rightArrow.style.cursor = 'pointer';
rightArrow.style.fontSize = '14px';
rightArrow.title = 'Nächste Woche';
navContainer.appendChild(leftArrow);
navContainer.appendChild(rightArrow);
weeklyContainer.appendChild(navContainer);
const yearTitle = document.createElement('h4');
yearTitle.style.textAlign = 'center';
yearTitle.style.margin = '0 0 5px 0';
yearTitle.style.fontWeight = '400';
yearTitle.style.fontSize = '14px';
weeklyContainer.appendChild(yearTitle);
const canvasWeek = document.createElement('canvas');
canvasWeek.style.maxHeight = '180px';
canvasWeek.style.width = '100%';
weeklyContainer.appendChild(canvasWeek);
let weekChart;
function renderWeekChart(startDate) {
const weekDates = getWeekDates(startDate);
const weekData = getWeekData(startDate);
const weekLabels = weekDates.map(d => {
const dt = new Date(d);
const weekday = dt.toLocaleDateString(undefined, { weekday: 'short' });
const dayMonth = dt.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit' });
return \`\${weekday} \${dayMonth}\`;
});
yearTitle.textContent = new Date(weekDates[0]).getFullYear();
if (weekChart) {
weekChart.data.labels = weekLabels;
weekChart.data.datasets[0].data = weekData;
weekChart.update();
} else {
weekChart = new Chart(canvasWeek, {
type: 'bar',
data: {
labels: weekLabels,
datasets: [{
label: 'Km pro Tag',
data: weekData,
backgroundColor: 'rgba(0,123,255,0.7)',
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: { callbacks: { label: ctx => \`\${ctx.raw} km\` } }
},
scales: {
y: { beginAtZero: true, ticks: { font: { size: 10 }, callback: v => v + ' km' }, grid: { drawTicks: false, color: '#eee' } },
x: { ticks: { font: { size: 10 } }, grid: { drawTicks: false, color: '#eee' } }
}
}
});
}
}
renderWeekChart(currentWeekStart);
// Pfeil-Events
leftArrow.addEventListener('click', () => {
currentWeekStart.setDate(currentWeekStart.getDate() - 7);
renderWeekChart(currentWeekStart);
});
rightArrow.addEventListener('click', () => {
const nextWeek = new Date(currentWeekStart);
nextWeek.setDate(nextWeek.getDate() + 7);
if (nextWeek <= getWeekStart(latestDate)) {
currentWeekStart = nextWeek;
renderWeekChart(currentWeekStart);
}
});
// ========================
// Jahres-Chart
// ========================
const yearContainer = document.getElementById(YEAR_CONTAINER_ID);
if (!yearContainer) throw new Error(\`#\${YEAR_CONTAINER_ID} not found\`);
yearContainer.innerHTML = '';
const canvasYear = document.createElement('canvas');
canvasYear.style.maxHeight = '120px';
canvasYear.style.width = '100%';
yearContainer.appendChild(canvasYear);
const yearlySums = {};
for (const [date, km] of entries) {
const year = date.slice(0, 4);
yearlySums[year] = (yearlySums[year] || 0) + km;
}
const years = Object.keys(yearlySums).sort();
const yearValues = years.map(y => Math.round(yearlySums[y] * 100) / 100);
new Chart(canvasYear, {
type: 'bar',
data: { labels: years, datasets: [{ label: 'Km pro Jahr', data: yearValues, backgroundColor: 'rgba(40,167,69,0.7)', borderRadius: 4 }] },
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: { beginAtZero: true, ticks: { font: { size: 10 }, callback: v => v + ' km' }, grid: { drawTicks: false, color: '#eee' } },
x: { ticks: { font: { size: 10 } }, grid: { drawTicks: false, color: '#eee' } }
}
}
});
} catch (err) {
console.error('Health chart error:', err);
}
})();
Read 14 times, last 108 minutes ago
WE LOVE YOU