Family Group Sheet Creator

Family Group Sheet Creator

Family Head (Husband)

Spouse (Wife)

Marriage Details

Children (Dashboard)

# Full Name Birth Date & Place Spouse (Optional) Actions

General Notes

Source Citations

List the records and documents verifying this data (one source per line).

Review the finalized Family Group Sheet document.

Click "Next" or "Previous" to refresh this preview.

No general notes provided.

'}

Source Citations

${formatMultiLine(data.sources) || '

No source citations provided.

'} `; }; const downloadTxt = () => { const data = getSheetData(); let content = `FAMILY GROUP SHEET: ${data.husband.name} & ${data.wife.name}\n`; content += "========================================================\n\n"; content += "I. FAMILY HEAD (HUSBAND)\n"; content += "--------------------------\n"; content += `Name: ${data.husband.name}\n`; content += `Birth: ${data.husband.birth}\n`; content += `Death: ${data.husband.death}\n\n`; content += "II. SPOUSE (WIFE)\n"; content += "--------------------------\n"; content += `Name: ${data.wife.name}\n`; content += `Birth: ${data.wife.birth}\n`; content += `Death: ${data.wife.death}\n\n`; content += "III. MARRIAGE DETAILS\n"; content += "--------------------------\n"; content += `Date: ${data.marriage.date}\n`; content += `Place: ${data.marriage.place}\n\n`; content += "IV. CHILDREN\n"; content += "--------------------------\n"; content += " # | Name\t\t\t| Birth Data\t\t| Spouse\n"; content += "---|-----------------------|-----------------------|-----------------------\n"; data.children.forEach((child, index) => { content += `${(index + 1).toString().padEnd(2)} | ${child.name.padEnd(21).substring(0, 21)} | ${child.birth.padEnd(21).substring(0, 21)} | ${child.spouse}\n`; }); content += "\n"; content += "V. GENERAL NOTES\n"; content += "--------------------------\n"; content += `${data.notes}\n\n`; content += "VI. SOURCE CITATIONS\n"; content += "--------------------------\n"; content += `${data.sources}\n`; const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `family_group_sheet_${data.husband.name.replace(/ /g, '_')}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = () => { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const data = getSheetData(); const margin = 15; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - margin * 2; let yPos = 20; const addWrappedText = (text, size, style, color = [52, 73, 94], lineSpacing = 6, indent = 0) => { doc.setFontSize(size); doc.setFont(undefined, style); doc.setTextColor(color[0], color[1], color[2]); const splitText = doc.splitTextToSize(text, usableWidth - indent); if (yPos + (splitText.length * lineSpacing) > 280) { doc.addPage(); yPos = 20; } doc.text(splitText, margin + indent, yPos); yPos += (splitText.length * lineSpacing); }; const addSectionTitle = (title) => { yPos += 5; addWrappedText(title, 14, 'bold', [0, 128, 128]); // Teal doc.setDrawColor(224, 224, 224); doc.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; }; // 1. Title doc.setFontSize(18); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(`Family Group Sheet: ${data.husband.name} & ${data.wife.name}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. Parents addSectionTitle("I. FAMILY HEAD (HUSBAND)"); addWrappedText(`Name: ${data.husband.name}`, 10, 'bold', [52, 73, 94]); addWrappedText(`Birth: ${data.husband.birth} | Death: ${data.husband.death}`, 10, 'normal', [52, 73, 94], 5, 5); yPos += 5; addSectionTitle("II. SPOUSE (WIFE)"); addWrappedText(`Name: ${data.wife.name}`, 10, 'bold', [52, 73, 94]); addWrappedText(`Birth: ${data.wife.birth} | Death: ${data.wife.death}`, 10, 'normal', [52, 73, 94], 5, 5); yPos += 5; // 3. Marriage addSectionTitle("III. MARRIAGE DETAILS"); addWrappedText(`Date: ${data.marriage.date} | Place: ${data.marriage.place}`, 10, 'normal', [52, 73, 94], 5, 5); yPos += 5; // 4. Children Table addSectionTitle(`IV. CHILDREN (${data.children.length} Total)`); const childHead = [['#', 'Full Name', 'Birth Date & Place', 'Spouse (Optional)']]; const childBody = data.children.map((child, index) => [ index + 1, child.name, child.birth, child.spouse ]); // FIX: Only call autoTable if there is data, or handle yPos update gracefully if (childBody.length > 0) { doc.autoTable({ startY: yPos, head: childHead, body: childBody, theme: 'grid', styles: { fontSize: 9, cellPadding: 2, textColor: [52, 73, 94], valign: 'top' }, headStyles: { fillColor: [200, 200, 200], textColor: [44, 62, 80] }, columnStyles: { 0: { cellWidth: 8, halign: 'center' }, 1: { cellWidth: 45, fontStyle: 'bold' } }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; } else { addWrappedText("No children listed for this family unit.", 10, 'italic', [108, 117, 125], 5, 5); yPos += 5; } // 5. Notes & Sources addSectionTitle("V. GENERAL NOTES"); addWrappedText(data.notes || 'No general notes provided.', 10, 'normal', [52, 73, 94]); yPos += 5; addSectionTitle("VI. SOURCE CITATIONS"); addWrappedText(data.sources || 'No source citations provided.', 10, 'normal', [52, 73, 94]); doc.save(`family_group_sheet_${data.husband.name.replace(/ /g, '_')}.pdf`); }; // --- Event Listeners --- // Tab Buttons tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); // Next/Prev Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 2 Actions (Children) addChildBtn.addEventListener('click', addChild); childrenTbody.addEventListener('click', (e) => { if (e.target.dataset.removeId) { removeChild(parseInt(e.target.dataset.removeId)); } }); childrenTbody.addEventListener('blur', (e) => { if (e.target.tagName === 'TD' && e.target.isContentEditable) { const id = parseInt(e.target.dataset.id); const field = e.target.dataset.field; updateChild(id, field, e.target.textContent); } }, true); // Tab 4 Actions refreshBtn.addEventListener('click', generatePreview); downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- showTab(1); // Set initial state });
Scroll to Top