Online Automated Business Proposal Generator

Online Automated Business Proposal Generator

Proposal Details

Client Information
Project Scope
Pricing

Live Preview

Company: ${data.companyName || ''}

Date: ${new Date().toLocaleDateString()}

Introduction

${introText.replace(/\n/g, '
')}

Project Scope

${(data.scope || '').replace(/\n/g, '
')}

Pricing

${pricingTableHTML}

About Us

${config.about.replace(/\n/g, '
')}

Terms & Conditions

${config.terms.replace(/\n/g, '
')}

`; }; const renderConfigForm = () => { templateIntro.value = config.intro; templateAbout.value = config.about; templateTerms.value = config.terms; }; const renderSavedProposals = () => { savedProposalsBody.innerHTML = ''; if (savedProposals.length === 0) { noSavedMsg.style.display = 'block'; savedProposalsBody.style.display = 'none'; } else { noSavedMsg.style.display = 'none'; savedProposalsBody.style.display = ''; savedProposals.forEach(p => { const row = document.createElement('tr'); row.innerHTML = ` ${new Date(p.date).toLocaleDateString()} ${p.projectTitle} ${p.clientName} `; savedProposalsBody.appendChild(row); }); } }; // --- CORE LOGIC --- const getCurrentProposalData = () => { const pricing = []; document.querySelectorAll('.pricing-item').forEach(item => { pricing.push({ description: item.querySelector('.item-desc').value, price: item.querySelector('.item-price').value }); }); return { clientName: document.getElementById('client-name').value, companyName: document.getElementById('company-name').value, projectTitle: document.getElementById('project-title').value, scope: document.getElementById('project-scope').value, pricing: pricing }; }; // --- EVENT HANDLERS --- Object.keys(tabButtons).forEach(tabId => { tabButtons[tabId].addEventListener('click', () => switchTab(tabId)); }); nextBtn.addEventListener('click', () => { const currentIndex = tabs.indexOf(currentTab); if (currentIndex < tabs.length - 1) switchTab(tabs[currentIndex + 1]); }); prevBtn.addEventListener('click', () => { const currentIndex = tabs.indexOf(currentTab); if (currentIndex > 0) switchTab(tabs[currentIndex - 1]); }); addItemBtn.addEventListener('click', () => addPricingItem()); pricingItemsContainer.addEventListener('click', (e) => { if (e.target.classList.contains('remove-item-btn')) { e.target.closest('.pricing-item').remove(); updatePreview(); } }); proposalForm.addEventListener('input', updatePreview); configForm.addEventListener('submit', (e) => { e.preventDefault(); config.intro = templateIntro.value; config.about = templateAbout.value; config.terms = templateTerms.value; saveState(); updatePreview(); // Update preview in case user is on dashboard alert('Template saved successfully!'); }); saveProposalBtn.addEventListener('click', () => { const data = getCurrentProposalData(); if (!data.clientName || !data.projectTitle) { alert('Please fill in at least the Client Name and Project Title.'); return; } const newProposal = { ...data, id: proposalIdCounter++, date: new Date().toISOString() }; savedProposals.push(newProposal); saveState(); renderSavedProposals(); alert('Proposal saved!'); }); downloadPdfBtn.addEventListener('click', () => { const { jsPDF } = window.jspdf; const doc = new jsPDF(); const data = getCurrentProposalData(); const margin = 15; let yPos = 20; doc.setFontSize(22); doc.text(`Proposal: ${data.projectTitle}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 15; doc.setFontSize(12); doc.text(`Client: ${data.clientName}`, margin, yPos); doc.text(`Date: ${new Date().toLocaleDateString()}`, doc.internal.pageSize.getWidth() - margin, yPos, { align: 'right' }); yPos += 10; const addSection = (title, text) => { if (yPos > 260) { doc.addPage(); yPos = 20; } doc.setFontSize(16); doc.text(title, margin, yPos); yPos += 8; doc.setFontSize(11); const lines = doc.splitTextToSize(text.replace(/\n/g, '\n'), 180); doc.text(lines, margin, yPos); yPos += lines.length * 5 + 10; }; const introText = config.intro.replace('{clientName}', data.clientName).replace('{projectTitle}', data.projectTitle); addSection('Introduction', introText); addSection('Project Scope', data.scope); if (yPos > 240) { doc.addPage(); yPos = 20; } doc.setFontSize(16); doc.text('Pricing', margin, yPos); yPos += 8; const tableColumn = ["Item Description", "Price"]; const tableRows = []; let total = 0; data.pricing.forEach(item => { const price = parseFloat(item.price) || 0; total += price; tableRows.push([item.description, `$${price.toFixed(2)}`]); }); tableRows.push([{content: 'Total', styles: {fontStyle: 'bold'}}, {content: `$${total.toFixed(2)}`, styles: {fontStyle: 'bold'}}]); doc.autoTable({ head: [tableColumn], body: tableRows, startY: yPos, theme: 'grid', headStyles: { fillColor: [59, 130, 246] }, }); yPos = doc.autoTable.previous.finalY + 15; addSection('About Us', config.about); addSection('Terms & Conditions', config.terms); doc.save(`${data.projectTitle}_Proposal.pdf`); }); // --- GLOBAL FUNCTIONS --- window.loadProposal = (id) => { const proposal = savedProposals.find(p => p.id === id); if (!proposal) return; document.getElementById('client-name').value = proposal.clientName; document.getElementById('company-name').value = proposal.companyName; document.getElementById('project-title').value = proposal.projectTitle; document.getElementById('project-scope').value = proposal.scope; pricingItemsContainer.innerHTML = ''; proposal.pricing.forEach(item => addPricingItem(item.description, item.price)); updatePreview(); switchTab('dashboard'); }; window.deleteProposal = (id) => { if (confirm('Are you sure you want to delete this proposal?')) { savedProposals = savedProposals.filter(p => p.id !== id); saveState(); renderSavedProposals(); } }; // --- START THE APP --- initialize(); });
Scroll to Top