`;
libraryList.appendChild(div);
});
}
};
// --- EVENT HANDLERS (TAB 1 - DASHBOARD) ---
logBody.addEventListener('blur', (e) => {
if (e.target.isContentEditable) {
const id = parseInt(e.target.dataset.id, 10);
const prop = e.target.dataset.prop;
const newValue = e.target.textContent.trim();
const itemIndex = proposalLog.findIndex(item => item.id === id);
if (itemIndex > -1 && prop) {
proposalLog[itemIndex][prop] = newValue;
}
}
}, true); // Use capture phase
logBody.addEventListener('click', (e) => {
if (e.target.classList.contains('sgp-btn-delete')) {
const id = parseInt(e.target.dataset.id, 10);
if (confirm('Are you sure you want to delete this log entry?')) {
proposalLog = proposalLog.filter(item => item.id !== id);
renderLogTable();
}
}
});
// PDF for Tab 1 (Log)
downloadLogPdfBtn.addEventListener('click', () => {
if (typeof jspdf === 'undefined' || typeof jspdf.autoTable === 'undefined') {
alert('PDF libraries not loaded. Please check your connection.');
return;
}
const { jsPDF } = jspdf;
const doc = new jsPDF();
const head = [['Proposal Name', 'Status', 'Date Saved']];
const body = proposalLog.map(item => [item.name, item.status, item.date]);
doc.setFontSize(18);
doc.text("Saved Proposals Log", 14, 22);
jsPDF.autoTable.default(doc, { startY: 30, head: head, body: body, theme: 'grid' });
doc.save('proposal-log.pdf');
});
// --- EVENT HANDLERS (TAB 2 - BUILDER) ---
saveToLogBtn.addEventListener('click', () => {
const name = titleInput.value.trim();
if (!name) {
alert('Please enter a Project Title to save to the log.');
return;
}
const newId = (proposalLog.length > 0 ? Math.max(...proposalLog.map(i => i.id)) : 0) + 1;
proposalLog.push({
id: newId,
name: name,
status: "Draft",
date: new Date().toISOString().split('T')[0]
});
renderLogTable();
// Switch to dashboard (Spec II.B.4.o)
tabs[0].click();
});
// PDF for Tab 2 (Proposal)
downloadProposalPdfBtn.addEventListener('click', () => {
if (typeof jspdf === 'undefined') {
alert('PDF library not loaded. Please check your connection.');
return;
}
try {
const { jsPDF } = jspdf;
const doc = new jsPDF();
const margin = 15;
const maxWidth = doc.internal.pageSize.width - (margin * 2);
let y = 22; // Start y-position
// Helper to add a section with line wrapping
const addSection = (title, content, titleSize = 14, contentSize = 11) => {
if (!content) return; // Skip empty sections
// Check if we need a new page
const titleHeight = titleSize * 0.5;
const contentLines = doc.splitTextToSize(content, maxWidth);
const contentHeight = (contentLines.length * (contentSize * 0.5)) + 10;
if (y + titleHeight + contentHeight > doc.internal.pageSize.height - margin) {
doc.addPage();
y = 22;
}
doc.setFontSize(titleSize);
doc.setFont('helvetica', 'bold');
doc.setTextColor('#007bff'); // Blue title
doc.text(title, margin, y);
y += titleHeight + 2;
doc.setFontSize(contentSize);
doc.setFont('helvetica', 'normal');
doc.setTextColor('#333333');
doc.text(lines, margin, y);
y += contentHeight;
};
// --- Build the PDF ---
doc.setFontSize(20);
doc.setFont('helvetica', 'bold');
doc.setTextColor('#333333');
const titleLines = doc.splitTextToSize(titleInput.value || "Untitled Proposal", maxWidth);
doc.text(titleLines, margin, y);
y += (titleLines.length * 8) + 10;
// Add sections
addSection("ABSTRACT", abstractInput.value);
addSection("INTRODUCTION / BACKGROUND", introInput.value);
addSection("SPECIFIC AIMS", aimsInput.value);
addSection("METHODS / APPROACH", methodsInput.value);
addSection("SIGNIFICANCE & IMPACT", impactInput.value);
doc.save('grant-proposal.pdf');
} catch(e) {
console.error('SGP Tool: Error generating proposal PDF:', e);
alert('An error occurred while generating the PDF.');
}
});
// --- EVENT HANDLERS (TAB 3 - LIBRARY) ---
filterSection.addEventListener('change', renderLibrary);
libraryList.addEventListener('click', (e) => {
const btn = e.target.closest('.sgp-btn-add');
if (!btn) return;
const id = parseInt(btn.dataset.id, 10);
const prompt = promptDatabase.find(p => p.id === id);
if (!prompt) return;
let targetTextarea;
switch(prompt.section) {
case 'Abstract': targetTextarea = abstractInput; break;
case 'Introduction': targetTextarea = introInput; break;
case 'Aims': targetTextarea = aimsInput; break;
case 'Methods': targetTextarea = methodsInput; break;
case 'Impact': targetTextarea = impactInput; break;
default: return;
}
// Append content
targetTextarea.value += (targetTextarea.value ? '\n\n' : '') + prompt.content;
// Switch to builder (Tab 2)
tabs[1].click();
});
// --- EVENT HANDLERS (TAB 4 - CONFIG) ---
addBlockBtn.addEventListener('click', () => {
const name = newNameInput.value.trim();
const section = newSectionSelect.value;
const content = newContentInput.value.trim();
if (!name || !section || !content) {
alert('Please fill in all fields.');
return;
}
const newId = (promptDatabase.length > 0 ? Math.max(...promptDatabase.map(p => p.id)) : 0) + 1;
promptDatabase.push({ id: newId, name, section, content });
renderConfigTable();
configForm.reset();
});
configBody.addEventListener('click', (e) => {
if (e.target.classList.contains('sgp-btn-delete')) {
const id = parseInt(e.target.dataset.id, 10);
if (confirm('Are you sure you want to delete this prompt?')) {
promptDatabase = promptDatabase.filter(item => item.id !== id);
renderConfigTable();
}
}
});
// --- INITIALIZATION ---
const init = () => {
// Initial Renders
renderLogTable();
renderConfigTable();
// Set up tabs
updateNavButtons();
// Show the first tab on load
if (tabs.length > 0) {
sgpShowTab('sgp-dashboard-tab', tabs[0]);
}
};
init();
});
