`;
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();
});