Legal Budget Allocation Planner

Legal Budget Allocation Planner

Budget Overview

Allocation Summary

Total Budget: $0.00
Number of Categories: 0
Highest Allocation: N/A
Lowest Allocation: N/A

Category Breakdown

Category Amount ($)

No budget categories added yet. Click "+ Add Category" to start.

'; } else { budgetData.forEach((item, index) => { const row = document.createElement('div'); row.className = 'grid grid-cols-1 md:grid-cols-7 gap-4 items-center'; row.innerHTML = `
`; configRowsContainer.appendChild(row); }); } addConfigEventListeners(); }; /** * Renders the doughnut chart on the dashboard. */ const renderChart = () => { if (myChart) { myChart.destroy(); } const ctx = chartCanvas.getContext('2d'); const labels = budgetData.map(item => item.category); const data = budgetData.map(item => item.amount); const backgroundColors = [ '#4f46e5', '#7c3aed', '#db2777', '#f59e0b', '#10b981', '#3b82f6', '#ef4444', '#8b5cf6', '#ec4899', '#f97316' ]; myChart = new Chart(ctx, { type: 'doughnut', data: { labels: labels, datasets: [{ label: 'Budget Allocation', data: data, backgroundColor: backgroundColors.slice(0, data.length), borderColor: '#ffffff', borderWidth: 2, hoverOffset: 4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { font: { size: 12, family: 'Inter' }, color: '#4b5563', padding: 20 } }, tooltip: { callbacks: { label: function(context) { let label = context.label || ''; if (label) { label += ': '; } if (context.parsed !== null) { const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = total > 0 ? ((context.parsed / total) * 100).toFixed(2) : 0; label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed) + ` (${percentage}%)`; } return label; } } } } } }); }; /** * Updates the summary card on the dashboard. */ const updateSummary = () => { const total = budgetData.reduce((sum, item) => sum + (parseFloat(item.amount) || 0), 0); document.getElementById('summaryTotal').textContent = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(total); document.getElementById('summaryCategories').textContent = budgetData.length; const amounts = budgetData.map(item => parseFloat(item.amount) || 0); const highest = amounts.length > 0 ? Math.max(...amounts) : 0; const lowest = amounts.length > 0 ? Math.min(...amounts) : 0; document.getElementById('summaryHighest').textContent = amounts.length > 0 ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(highest) : 'N/A'; document.getElementById('summaryLowest').textContent = amounts.length > 0 ? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(lowest) : 'N/A'; const tableBody = document.getElementById('summaryTableBody'); tableBody.innerHTML = ''; const sortedData = [...budgetData].sort((a, b) => (parseFloat(b.amount) || 0) - (parseFloat(a.amount) || 0)); sortedData.forEach(item => { const row = document.createElement('tr'); row.innerHTML = ` ${item.category} ${new Intl.NumberFormat('en-US').format(item.amount)} `; tableBody.appendChild(row); }); }; /** * Adds a new empty category row to the configuration. */ window.addCategoryRow = () => { budgetData.push({ category: '', amount: '' }); updateAll(); }; /** * Removes a category row. * @param {number} index - The index of the row to remove. */ window.removeCategoryRow = (index) => { budgetData.splice(index, 1); updateAll(); }; /** * Adds event listeners to the input fields in the config tab. */ const addConfigEventListeners = () => { document.querySelectorAll('.category-input').forEach(input => { input.addEventListener('change', (e) => { const index = e.target.dataset.index; budgetData[index].category = e.target.value; updateAll(); }); }); document.querySelectorAll('.amount-input').forEach(input => { input.addEventListener('change', (e) => { const index = e.target.dataset.index; budgetData[index].amount = parseFloat(e.target.value) || 0; updateAll(); }); }); }; // --- UI Interaction --- /** * Switches the visible tab. * @param {string} tabId - The ID of the tab to switch to. */ window.switchTab = (tabId) => { currentTab = tabId; tabDashboard.classList.toggle('tab-active', tabId === 'dashboard'); tabConfig.classList.toggle('tab-active', tabId === 'config'); contentDashboard.classList.toggle('hidden', tabId !== 'dashboard'); contentConfig.classList.toggle('hidden', tabId !== 'config'); updateNavButtons(); }; /** * Updates the state of the Next/Previous navigation buttons. */ const updateNavButtons = () => { const currentIndex = tabs.indexOf(currentTab); prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex === tabs.length - 1; }; /** * Navigates between tabs using Next/Previous buttons. * @param {string} direction - 'next' or 'prev'. */ window.navigateTabs = (direction) => { const currentIndex = tabs.indexOf(currentTab); let newIndex; if (direction === 'next' && currentIndex < tabs.length - 1) { newIndex = currentIndex + 1; } else if (direction === 'prev' && currentIndex > 0) { newIndex = currentIndex - 1; } if (newIndex !== undefined) { switchTab(tabs[newIndex]); } }; // --- PDF Generation --- /** * Generates and downloads a PDF of the budget dashboard. */ window.downloadPDF = async () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-content'); // Temporarily make it visible if it's not, for rendering const wasHidden = pdfContent.offsetParent === null; if (wasHidden) { pdfContent.style.display = 'block'; } const canvas = await html2canvas(pdfContent, { scale: 2, // Higher scale for better quality backgroundColor: '#ffffff', useCORS: true }); if (wasHidden) { pdfContent.style.display = ''; // Revert style } const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'px', format: [canvas.width, canvas.height] }); pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height); pdf.save('Legal_Budget_Plan.pdf'); }; // --- Initial Load --- init(); updateNavButtons(); });
Scroll to Top