Film Production Budget Dashboard

Top Sheet Summary

Budget vs. Actual by Category

Budget vs. Actual by Department

Detailed Budget Breakdown

CategoryDepartmentItemBudgetedActualVariance

Add New Budget Line Item

Master Budget List

CategoryDepartmentDescriptionBudgetedActualAction

Overall Variance

${formatCurrency(variance)}

`; } const renderChart = (id, type, data, options) => { const ctx = document.getElementById(id)?.getContext('2d'); if (!ctx) return; if (charts[id]) charts[id].destroy(); charts[id] = new Chart(ctx, { type, data, options }); }; function renderCharts() { const aggregate = (key) => budgetItems.reduce((acc, item) => { if (!acc[item[key]]) acc[item[key]] = { budget: 0, actual: 0 }; acc[item[key]].budget += item.budget; acc[item[key]].actual += item.actual; return acc; }, {}); // By Category (ATL/BTL) const byCategory = aggregate('cat'); renderChart('film-cat-chart', 'bar', { labels: Object.keys(byCategory), datasets: [ { label: 'Budgeted', data: Object.values(byCategory).map(v => v.budget), backgroundColor: 'rgba(71, 85, 105, 0.7)' }, { label: 'Actual', data: Object.values(byCategory).map(v => v.actual), backgroundColor: 'rgba(124, 58, 237, 0.7)' } ]}, { responsive: true, maintainAspectRatio: false, indexAxis: 'y' } ); // By Department const byDept = aggregate('dept'); renderChart('film-dept-chart', 'bar', { labels: Object.keys(byDept), datasets: [ { label: 'Budgeted', data: Object.values(byDept).map(v => v.budget), backgroundColor: 'rgba(71, 85, 105, 0.7)' }, { label: 'Actual', data: Object.values(byDept).map(v => v.actual), backgroundColor: 'rgba(124, 58, 237, 0.7)' } ]}, { responsive: true, maintainAspectRatio: false } ); } function renderDetailTable() { document.getElementById('film-detail-tbody').innerHTML = budgetItems.map(item => { const variance = item.budget - item.actual; return `${item.cat}${item.dept}${item.desc}${formatCurrency(item.budget)}${formatCurrency(item.actual)}${formatCurrency(variance)}`; }).join(''); } // --- RENDER MANAGEMENT UI --- function renderManagementTable() { document.getElementById('film-master-tbody').innerHTML = budgetItems.map(item => ` ${item.cat}${item.dept}${item.desc}${formatCurrency(item.budget)}${formatCurrency(item.actual)} `).join(''); } // --- ACTIONS --- window.filmAddBudgetItem = function() { budgetItems.push({ id: Date.now(), cat: document.getElementById('add-category').value, dept: document.getElementById('add-dept').value, desc: document.getElementById('add-desc').value, budget: parseFloat(document.getElementById('add-budgeted').value) || 0, actual: parseFloat(document.getElementById('add-actual').value) || 0, }); renderAll(); } // --- GLOBAL & UTILITY --- window.filmShowTab = id => { document.querySelectorAll('.film-main-tab-content, .film-main-tab-button').forEach(el => el.classList.remove('active')); document.getElementById(`film-tab-${id}`)?.classList.add('active'); document.querySelector(`.film-main-tab-button[onclick="filmShowTab('${id}')"]`)?.classList.add('active'); }; window.filmDownloadPDF = () => { html2canvas(document.getElementById('film-pdf-content'), { scale: 2 }).then(canvas => { const pdf = new jspdf.jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 40, 40, pdf.internal.pageSize.getWidth() - 80, 0); pdf.save('Film_Budget_Summary.pdf'); }); }; initialize(); });
Scroll to Top