let feedData = []; async function loadBudget() { const msg = document.getElementById('msg'); try { const [stats, purchases] = await Promise.all([ API.get('/api/stats/budget'), API.get('/api/feed'), ]); // All-time stats document.getElementById('b-cost-total').textContent = fmtMoney(stats.total_feed_cost); document.getElementById('b-eggs-total').textContent = stats.total_eggs_alltime; document.getElementById('b-cpe').textContent = fmtMoneyFull(stats.cost_per_egg); document.getElementById('b-cpd').textContent = fmtMoney(stats.cost_per_dozen); // Last 30 days document.getElementById('b-cost-30d').textContent = fmtMoney(stats.total_feed_cost_30d); document.getElementById('b-eggs-30d').textContent = stats.total_eggs_30d; document.getElementById('b-cpe-30d').textContent = fmtMoneyFull(stats.cost_per_egg_30d); document.getElementById('b-cpd-30d').textContent = fmtMoney(stats.cost_per_dozen_30d); feedData = purchases; renderTable(); } catch (err) { showMessage(msg, `Failed to load budget data: ${err.message}`, 'error'); } } function renderTable() { const tbody = document.getElementById('feed-body'); const tfoot = document.getElementById('feed-foot'); if (feedData.length === 0) { tbody.innerHTML = 'No feed purchases logged yet.'; tfoot.innerHTML = ''; return; } tbody.innerHTML = feedData.map(e => { const total = (parseFloat(e.bags) * parseFloat(e.price_per_bag)).toFixed(2); return ` ${fmtDate(e.date)} ${parseFloat(e.bags)} ${fmtMoney(e.price_per_bag)} ${fmtMoney(total)} ${e.notes || ''} `; }).join(''); // Total row const grandTotal = feedData.reduce((sum, e) => sum + parseFloat(e.bags) * parseFloat(e.price_per_bag), 0); tfoot.innerHTML = ` Total ${fmtMoney(grandTotal)} ${feedData.length} purchases `; } function startEdit(id) { const entry = feedData.find(e => e.id === id); const row = document.querySelector(`tr[data-id="${id}"]`); row.innerHTML = ` `; } async function saveEdit(id) { const msg = document.getElementById('msg'); const row = document.querySelector(`tr[data-id="${id}"]`); const inputs = row.querySelectorAll('input'); const [dateInput, bagsInput, priceInput, notesInput] = inputs; try { const updated = await API.put(`/api/feed/${id}`, { date: dateInput.value, bags: parseFloat(bagsInput.value), price_per_bag: parseFloat(priceInput.value), notes: notesInput.value.trim() || null, }); const idx = feedData.findIndex(e => e.id === id); feedData[idx] = updated; renderTable(); loadBudget(); showMessage(msg, 'Purchase updated.'); } catch (err) { showMessage(msg, `Error: ${err.message}`, 'error'); } } async function deleteEntry(id) { if (!confirm('Delete this purchase?')) return; const msg = document.getElementById('msg'); try { await API.del(`/api/feed/${id}`); feedData = feedData.filter(e => e.id !== id); renderTable(); loadBudget(); showMessage(msg, 'Purchase deleted.'); } catch (err) { showMessage(msg, `Error: ${err.message}`, 'error'); } } document.addEventListener('DOMContentLoaded', () => { const form = document.getElementById('feed-form'); const msg = document.getElementById('msg'); const bagsInput = document.getElementById('bags'); const priceInput = document.getElementById('price'); const totalDisplay = document.getElementById('total-display'); setToday(document.getElementById('date')); // Live total calculation function updateTotal() { const bags = parseFloat(bagsInput.value) || 0; const price = parseFloat(priceInput.value) || 0; totalDisplay.value = bags && price ? fmtMoney(bags * price) : ''; } bagsInput.addEventListener('input', updateTotal); priceInput.addEventListener('input', updateTotal); form.addEventListener('submit', async (e) => { e.preventDefault(); const data = { date: document.getElementById('date').value, bags: parseFloat(bagsInput.value), price_per_bag: parseFloat(priceInput.value), notes: document.getElementById('notes').value.trim() || null, }; try { await API.post('/api/feed', data); showMessage(msg, 'Purchase saved!'); form.reset(); totalDisplay.value = ''; setToday(document.getElementById('date')); loadBudget(); } catch (err) { showMessage(msg, `Error: ${err.message}`, 'error'); } }); loadBudget(); });