let eggChart = null; function buildChart(eggs) { const today = new Date(); const labels = []; const data = []; // Build a lookup map from date string → egg count const eggMap = {}; eggs.forEach(e => { eggMap[e.date] = e.eggs; }); // Generate the last 30 days in chronological order for (let i = 29; i >= 0; i--) { const d = new Date(today); d.setDate(d.getDate() - i); const y = d.getFullYear(); const mo = String(d.getMonth() + 1).padStart(2, '0'); const dy = String(d.getDate()).padStart(2, '0'); const dateStr = `${y}-${mo}-${dy}`; labels.push(`${mo}/${dy}`); data.push(eggMap[dateStr] ?? 0); } const ctx = document.getElementById('eggs-chart').getContext('2d'); const chartWrap = document.getElementById('chart-wrap'); const noDataMsg = document.getElementById('chart-no-data'); const hasData = data.some(v => v > 0); if (!hasData) { if (chartWrap) chartWrap.style.display = 'none'; if (noDataMsg) noDataMsg.style.display = 'block'; return; } if (chartWrap) chartWrap.style.display = 'block'; if (noDataMsg) noDataMsg.style.display = 'none'; if (eggChart) eggChart.destroy(); // prevent duplicate charts on re-render eggChart = new Chart(ctx, { type: 'line', data: { labels, datasets: [{ data, borderColor: '#3d6b4f', backgroundColor: 'rgba(61,107,79,0.08)', borderWidth: 2, pointRadius: 3, pointHoverRadius: 5, fill: true, tension: 0.3, }], }, options: { responsive: true, plugins: { legend: { display: false }, tooltip: { callbacks: { label: ctx => ` ${ctx.parsed.y} eggs`, }, }, }, scales: { y: { beginAtZero: true, suggestedMax: 5, ticks: { stepSize: 1, precision: 0 }, }, x: { ticks: { maxTicksLimit: 10 }, }, }, }, }); } async function loadDashboard() { const msg = document.getElementById('msg'); try { // Fetch stats and recent eggs in parallel const [stats, budget, eggs] = await Promise.all([ API.get('/api/stats/dashboard'), API.get('/api/stats/budget'), API.get('/api/eggs'), ]); // Populate stat cards document.getElementById('s-flock').textContent = stats.current_flock ?? '—'; document.getElementById('s-total').textContent = stats.total_eggs_alltime; document.getElementById('s-7d').textContent = stats.total_eggs_7d; document.getElementById('s-30d').textContent = stats.total_eggs_30d; document.getElementById('s-avg-day').textContent = stats.avg_eggs_per_day_30d ?? '—'; document.getElementById('s-avg-hen').textContent = stats.avg_eggs_per_hen_day_30d ?? '—'; document.getElementById('s-cpe').textContent = fmtMoney(budget.cost_per_egg); document.getElementById('s-cpe-30d').textContent = fmtMoney(budget.cost_per_egg_30d); document.getElementById('s-cpd').textContent = fmtMoney(budget.cost_per_dozen); document.getElementById('s-cpd-30d').textContent = fmtMoney(budget.cost_per_dozen_30d); // Trend chart — uses all fetched eggs, filtered to last 30 days inside buildChart buildChart(eggs); // Recent 10 collections const tbody = document.getElementById('recent-body'); const recent = eggs.slice(0, 10); if (recent.length === 0) { tbody.innerHTML = '