Shareholder Agreement Analyzer

Shareholder Agreement Analyzer

Equity Distribution

Key Metrics & Clauses

Total Shareholders: 0
Total Shares Issued: 0
Vesting Cliff: N/A
Vesting Period: N/A
Right of First Refusal (ROFR): Not Included
Tag-Along Rights: Not Included

Shareholder Details

Shareholder Name Shares Equity %

${s.name}

${s.shares.toLocaleString()} Shares

`; shareholderListContainer.insertAdjacentHTML('beforeend', item); }); }; const updateDashboardMetrics = () => { const totalShares = shareholders.reduce((sum, s) => sum + s.shares, 0); document.getElementById('metric-total-shareholders').textContent = shareholders.length; document.getElementById('metric-total-shares').textContent = totalShares.toLocaleString(); agreementDetails.vestingCliff = document.getElementById('vesting-cliff').value; agreementDetails.vestingPeriod = document.getElementById('vesting-period').value; agreementDetails.rofr = document.getElementById('rofr-clause').checked; agreementDetails.tagAlong = document.getElementById('tag-along-clause').checked; document.getElementById('metric-vesting-cliff').textContent = `${agreementDetails.vestingCliff} months`; document.getElementById('metric-vesting-period').textContent = `${agreementDetails.vestingPeriod} months`; const rofrMetric = document.getElementById('metric-rofr'); rofrMetric.textContent = agreementDetails.rofr ? 'Included' : 'Not Included'; rofrMetric.className = agreementDetails.rofr ? 'font-bold text-lg text-green-600' : 'font-bold text-lg text-red-600'; const tagAlongMetric = document.getElementById('metric-tag-along'); tagAlongMetric.textContent = agreementDetails.tagAlong ? 'Included' : 'Not Included'; tagAlongMetric.className = agreementDetails.tagAlong ? 'font-bold text-lg text-green-600' : 'font-bold text-lg text-red-600'; }; const renderEquityChart = () => { const ctx = document.getElementById('equity-chart').getContext('2d'); const labels = shareholders.map(s => s.name); const data = shareholders.map(s => s.shares); if (equityChart) { equityChart.destroy(); } if(shareholders.length === 0){ return; } equityChart = new Chart(ctx, { type: 'doughnut', data: { labels: labels, datasets: [{ label: 'Equity Distribution', data: data, backgroundColor: [ 'rgba(79, 70, 229, 0.8)', 'rgba(59, 130, 246, 0.8)', 'rgba(16, 185, 129, 0.8)', 'rgba(245, 158, 11, 0.8)', 'rgba(239, 68, 68, 0.8)', 'rgba(139, 92, 246, 0.8)' ], borderColor: '#fff', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: function(context) { let label = context.label || ''; if (label) { label += ': '; } if (context.parsed !== null) { const total = context.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); const percentage = total > 0 ? (context.parsed / total * 100).toFixed(2) : 0; label += `${context.raw.toLocaleString()} Shares (${percentage}%)`; } return label; } } } } } }); }; const refreshUI = () => { updateDashboardMetrics(); renderShareholderTable(); renderShareholderList(); renderEquityChart(); }; // --- CORE LOGIC & EVENT HANDLERS --- const handleFormSubmit = (e) => { e.preventDefault(); const id = document.getElementById('shareholder-id').value; const shareholderData = { name: document.getElementById('shareholder-name').value, shares: parseInt(document.getElementById('shareholder-shares').value, 10), }; if (id) { const index = shareholders.findIndex(s => s.id == id); if (index !== -1) shareholders[index] = { ...shareholders[index], ...shareholderData }; } else { shareholderData.id = shareholders.length > 0 ? Math.max(...shareholders.map(s => s.id)) + 1 : 1; shareholders.push(shareholderData); } resetForm(); refreshUI(); }; const resetForm = () => { shareholderForm.reset(); document.getElementById('shareholder-id').value = ''; shareholderFormTitle.textContent = 'Add Shareholder'; saveShareholderBtn.textContent = 'Save'; cancelEditBtn.classList.add('hidden'); }; // --- GLOBAL FUNCTIONS FOR INLINE ONCLICK --- window.globalFuncs = { editShareholder: (id) => { const s = shareholders.find(sh => sh.id === id); if (!s) return; document.getElementById('shareholder-id').value = s.id; document.getElementById('shareholder-name').value = s.name; document.getElementById('shareholder-shares').value = s.shares; shareholderFormTitle.textContent = 'Edit Shareholder'; saveShareholderBtn.textContent = 'Update'; cancelEditBtn.classList.remove('hidden'); shareholderFormTitle.scrollIntoView({ behavior: 'smooth' }); }, confirmDelete: (id) => { shareholderToDeleteId = id; deleteModal.classList.add('visible'); }, deleteShareholder: () => { if (shareholderToDeleteId !== null) { shareholders = shareholders.filter(s => s.id !== shareholderToDeleteId); shareholderToDeleteId = null; deleteModal.classList.remove('visible'); refreshUI(); } } }; // --- TAB NAVIGATION LOGIC --- window.switchTab = (tabName) => { currentTab = tabName; Object.keys(tabs).forEach(key => { tabs[key].classList.toggle('active', key === tabName); tabs[key].classList.toggle('inactive', key !== tabName); }); Object.keys(contents).forEach(key => { contents[key].classList.toggle('hidden', key !== tabName); }); updateNavButtons(); }; window.navigateTabs = (direction) => { const tabKeys = Object.keys(tabs); const currentIndex = tabKeys.indexOf(currentTab); let newIndex = (direction === 'next') ? currentIndex + 1 : currentIndex - 1; if (newIndex >= 0 && newIndex < tabKeys.length) { switchTab(tabKeys[newIndex]); } }; const updateNavButtons = () => { const tabKeys = Object.keys(tabs); const currentIndex = tabKeys.indexOf(currentTab); navButtons.prev.disabled = currentIndex === 0; navButtons.next.disabled = currentIndex === tabKeys.length - 1; }; // --- PDF GENERATION --- const generatePdf = () => { const { jsPDF } = window.jspdf; const doc = new jsPDF(); const totalShares = shareholders.reduce((sum, s) => sum + s.shares, 0); doc.setFontSize(18); doc.text('Shareholder Agreement Analysis Report', 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(`Report generated on: ${new Date().toLocaleDateString()}`, 14, 30); doc.setFontSize(14); doc.text('Key Terms & Clauses', 14, 45); doc.autoTable({ startY: 50, theme: 'plain', body: [ ['Total Shareholders', shareholders.length], ['Total Shares Issued', totalShares.toLocaleString()], ['Vesting Cliff', `${agreementDetails.vestingCliff} months`], ['Total Vesting Period', `${agreementDetails.vestingPeriod} months`], ['Right of First Refusal', agreementDetails.rofr ? 'Included' : 'Not Included'], ['Tag-Along Rights', agreementDetails.tagAlong ? 'Included' : 'Not Included'], ], }); doc.addPage(); doc.setFontSize(14); doc.text('Shareholder Equity Distribution', 14, 22); const tableColumn = ["Shareholder Name", "Shares", "Equity %"]; const tableRows = shareholders.map(s => [ s.name, s.shares.toLocaleString(), totalShares > 0 ? ((s.shares / totalShares) * 100).toFixed(2) + '%' : '0.00%' ]); doc.autoTable({ head: [tableColumn], body: tableRows, startY: 30, theme: 'grid', headStyles: { fillColor: [79, 70, 229] }, }); if (equityChart && shareholders.length > 0) { const chartImage = equityChart.toBase64Image(); doc.addPage(); doc.setFontSize(14); doc.text('Equity Distribution Chart', 14, 22); doc.addImage(chartImage, 'PNG', 14, 30, 180, 100); } doc.save('Shareholder_Agreement_Analysis.pdf'); }; // --- INITIALIZATION & EVENT LISTENERS --- shareholderForm.addEventListener('submit', handleFormSubmit); cancelEditBtn.addEventListener('click', resetForm); downloadPdfBtn.addEventListener('click', generatePdf); modalCancelBtn.addEventListener('click', () => deleteModal.classList.remove('visible')); modalConfirmBtn.addEventListener('click', window.globalFuncs.deleteShareholder); // Listen for changes on config inputs to update dashboard live document.getElementById('vesting-cliff').addEventListener('input', refreshUI); document.getElementById('vesting-period').addEventListener('input', refreshUI); document.getElementById('rofr-clause').addEventListener('change', refreshUI); document.getElementById('tag-along-clause').addEventListener('change', refreshUI); // Initial load shareholders = [...sampleShareholders]; refreshUI(); updateNavButtons(); });
Scroll to Top