Scientific Grant Proposal Generator

Scientific Grant Proposal Generator

This is your log of saved proposals. Click any cell (except 'Action') to edit the data.

Proposal Name Status Date Saved Action

Build your proposal below. Type directly, or add content from the 'Content Library' tab.

Click 'Add to Proposal' to append a block of text to the corresponding section in the 'Proposal Builder' tab.

Manage the reusable text blocks for the 'Content Library' tab.

Add New Text Block

Current Library

Name Section Content Action

${prompt.content.substring(0, 60)}...

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