`;
};
const renderDataTable = () => {
dataTableBody.innerHTML = '';
const sortedPeople = [...state.people].sort((a, b) => a.birthYear - b.birthYear);
sortedPeople.forEach(p => {
const parent1 = state.people.find(parent => parent.id === p.parent1Id);
const parent2 = state.people.find(parent => parent.id === p.parent2Id);
const parentsText = [parent1, parent2].filter(Boolean).map(p => p.name).join(' & ');
const row = `
${p.name}
${p.birthYear || 'N/A'}
${p.deathYear || 'Living'}
${parentsText || 'N/A'}
`;
dataTableBody.innerHTML += row;
});
};
const buildTreeHTML = (personId) => {
const person = state.people.find(p => p.id === personId);
if (!person) return '';
const children = state.people.filter(p => p.parent1Id === person.id || p.parent2Id === person.id);
const spouseRel = state.relationships.find(r => r.person1Id === person.id || r.person2Id === person.id);
const spouse = spouseRel ? state.people.find(p => p.id === (spouseRel.person1Id === person.id ? spouseRel.person2Id : spouseRel.person1Id)) : null;
let html = ``;
html += `${person.name}
${person.birthYear || '?'} - ${person.deathYear || 'Present'}`; if (spouse) { html += `${spouse.name}
${spouse.birthYear || '?'} - ${spouse.deathYear || 'Present'}`; } if (children.length > 0) { html += ` `;
return html;
};
const renderFamilyTree = () => {
// Find root nodes (people without parents in the dataset)
const rootPeople = state.people.filter(p => !p.parent1Id && !p.parent2Id);
if (rootPeople.length > 0) {
familyTreeContainer.innerHTML = `
`.replaceAll('class="config-input"', 'class="config-input w-full border-gray-300 rounded-md shadow-sm"');
});
// Set selected options for parents after rendering all rows
sortedPeople.forEach(p => {
if(p.parent1Id) document.querySelector(`select[data-id="${p.id}"][data-field="parent1Id"]`).value = p.parent1Id;
if(p.parent2Id) document.querySelector(`select[data-id="${p.id}"][data-field="parent2Id"]`).value = p.parent2Id;
});
addConfigEventListeners();
};
// --- EVENT HANDLERS ---
const handleConfigChange = (e) => {
const id = parseInt(e.target.dataset.id);
const field = e.target.dataset.field;
const value = e.target.value;
const person = state.people.find(p => p.id === id);
if (person) {
person[field] = value ? parseInt(value, 10) : null;
if (field === 'name') person[field] = value;
}
renderAll();
};
const handleAddPerson = () => {
const newId = state.people.length > 0 ? Math.max(...state.people.map(p => p.id)) + 1 : 1;
state.people.push({ id: newId, name: "New Person", birthYear: null, deathYear: null, parent1Id: null, parent2Id: null });
renderAll();
};
const handleRemovePerson = (e) => {
const id = parseInt(e.target.dataset.id);
state.people = state.people.filter(p => p.id !== id);
// Also remove from parent fields of other people
state.people.forEach(p => {
if (p.parent1Id === id) p.parent1Id = null;
if (p.parent2Id === id) p.parent2Id = null;
});
renderAll();
};
const addConfigEventListeners = () => {
document.querySelectorAll('.config-input').forEach(input => input.addEventListener('change', handleConfigChange));
document.querySelectorAll('.remove-btn').forEach(button => button.addEventListener('click', handleRemovePerson));
};
const handleDownloadPdf = () => {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-content');
document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'hidden');
html2canvas(pdfContent, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => {
document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'visible');
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgWidth = pdfWidth - 20;
const imgHeight = canvas.height * imgWidth / canvas.width;
pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight);
pdf.save('Genealogy-Dashboard.pdf');
});
};
// --- TABBING LOGIC ---
let currentTabIndex = 0;
const updateTabButtons = () => {
prevTabBtn.disabled = currentTabIndex === 0;
nextTabBtn.disabled = currentTabIndex === tabContents.length - 1;
};
const switchTab = (index) => {
tabButtons.forEach(btn => btn.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
tabButtons[index].classList.add('active');
tabContents[index].classList.add('active');
currentTabIndex = index;
updateTabButtons();
};
tabButtons.forEach((button, index) => button.addEventListener('click', () => switchTab(index)));
prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); });
nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) switchTab(currentTabIndex + 1); });
// --- INITIALIZATION ---
if (kpiContainer && addPersonBtn && downloadPdfBtn) {
addPersonBtn.addEventListener('click', handleAddPerson);
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
renderAll();
updateTabButtons();
} else {
console.error("Essential dashboard elements could not be found.");
}
});
${person.birthYear || '?'} - ${person.deathYear || 'Present'}`; if (spouse) { html += `${spouse.name}
${spouse.birthYear || '?'} - ${spouse.deathYear || 'Present'}`; } if (children.length > 0) { html += `
- `;
children.forEach(child => {
html += buildTreeHTML(child.id);
});
html += `
- ${buildTreeHTML(rootPeople[0].id)}
- ${buildTreeHTML(state.people[0].id)}
No data to display tree.
`; } }; const renderConfigRows = () => { configRowsContainer.innerHTML = ''; const sortedPeople = [...state.people].sort((a,b) => a.id - b.id); const peopleOptions = state.people.map(p => ``).join(''); sortedPeople.forEach(p => { configRowsContainer.innerHTML += `