Project Approval Dashboard

Approval Pipeline Overview

Proposals in Funnel

Proposed Budget by Dept.

Pending Review Queue

ProjectDepartmentBudgetROIStatusActions

Submit New Project Proposal

Master Proposal Log

ProjectDepartmentBudgetStatusActions

Avg. Approval Time

${avgApprovalTime.toFixed(1)} days

`; } 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(pending) { const funnelData = STATUS_WORKFLOW.slice(0,3).map(stage => pending.filter(p => p.status === stage).length || proposals.filter(p => p.status === stage).length); renderChart('pa-funnel-chart', 'bar', { labels: STATUS_WORKFLOW.slice(0,3), datasets: [{ data: funnelData, backgroundColor: ['#6366f1', '#a78bfa', '#16a34a'] }] }, { responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins: { legend: {display:false} } }); const byDept = pending.reduce((acc, p) => { acc[p.dept] = (acc[p.dept] || 0) + p.budget; return acc; }, {}); renderChart('pa-dept-chart', 'doughnut', { labels: Object.keys(byDept), datasets: [{ data: Object.values(byDept) }] }, { responsive: true, maintainAspectRatio: false }); } function renderQueueTable(pending) { document.getElementById('pa-queue-tbody').innerHTML = pending.map(p => { const actions = { "Submitted": ``, "Under Review": `` }; return `${p.name}${p.dept}${formatCurrency(p.budget)}${p.roi}%${p.status}${actions[p.status] || ''}`; }).join(''); } // --- Actions --- function addProposal() { /* ... */ } function handleTableClick(e) { /* ... */ } // ... all other action and modal functions would be fully implemented here document.getElementById('pa-queue-tbody').addEventListener('click', (e) => { if(!e.target.matches('.action-btn')) return; const id = parseInt(e.target.dataset.id); const action = e.target.dataset.action; const proposal = proposals.find(p => p.id === id); if(proposal) { proposal.status = action === 'review' ? 'Under Review' : action; if(action === 'Approved' || action === 'Rejected') proposal.decisionDate = new Date(); render(); } }); initialize(); });
Scroll to Top