Sales Pipeline Stage Definer

Sales Pipeline Stage Definer

Stage ${stage.order}: ${escapeHTML(stage.name)} (${stage.prob}%)

Entry Gate: ${escapeHTML(stage.entry)}

Exit Gate: ${escapeHTML(stage.exit)}

`).join('')}
`; }; // --- Data Extraction & Persistence --- const updateStageData = (index) => { const stageElement = contentArea.querySelector(`[data-stage-index="${index}"]`); if (!stageElement) return; // Ensure the data structure exists for this index while (pipelineData.stages.length <= index) { pipelineData.stages.push({ id: stageIdCounter++, name: `Stage ${pipelineData.stages.length + 1}`, prob: 0, entry: '', exit: '' }); } const stage = pipelineData.stages[index]; stage.name = stageElement.querySelector(`#spd-stage-name-${index}`).value; stage.prob = parseFloat(stageElement.querySelector(`#spd-stage-prob-${index}`).value) || 0; stage.entry = stageElement.querySelector(`#spd-stage-entry-${index}`).value; stage.exit = stageElement.querySelector(`#spd-stage-exit-${index}`).value; }; const getReportData = () => { // Recalculate stages just in case user edited fields without clicking next/prev if (currentTab > 1 && currentTab <= numStages + 1) { updateStageData(currentTab - 2); } const rawDate = new Date().toISOString().substring(0, 10); return { name: pipelineData.name, version: pipelineData.version, date: rawDate, stages: pipelineData.stages.slice(0, numStages).map((s, index) => ({ order: index + 1, name: s.name, prob: s.prob, entry: s.entry, exit: s.exit })) }; }; // --- Event Listener Attachments --- const attachSetupListeners = () => { const nameInput = toolRoot.querySelector('#spd-pipeline-name'); const versionInput = toolRoot.querySelector('#spd-pipeline-version'); const numStagesSelect = toolRoot.querySelector('#spd-num-stages'); if (nameInput) nameInput.addEventListener('input', (e) => { pipelineData.name = e.target.value; renderTabs(); }); if (versionInput) versionInput.addEventListener('input', (e) => { pipelineData.version = e.target.value; }); if (numStagesSelect) numStagesSelect.addEventListener('change', (e) => { numStages = parseInt(e.target.value, 10); // Ensure state array length matches new stage count before rendering tabs while (pipelineData.stages.length < numStages) { pipelineData.stages.push({ id: stageIdCounter++, name: `Stage ${pipelineData.stages.length + 1}`, prob: 0, entry: 'TBD', exit: 'TBD' }); } renderTabs(); }); }; const attachStageListeners = (index) => { const stageElement = contentArea.querySelector(`[data-stage-index="${index}"]`); if (!stageElement) return; // Use 'blur' for textareas to minimize listener calls while preserving typing flow stageElement.querySelectorAll('input, textarea').forEach(input => { input.addEventListener('blur', () => updateStageData(index)); if (input.id.includes('name')) { input.addEventListener('input', () => { updateStageData(index); renderTabs(); }); } }); }; const attachSummaryListeners = () => { const data = getReportData(); downloadPdfBtn.onclick = () => downloadPDF(data); downloadTxtBtn.onclick = () => downloadTxt(data); }; // --- Download Functions --- const downloadTxt = (data) => { let content = `SALES PIPELINE PROTOCOL: ${data.name.toUpperCase()}\n`; content += `Version: ${data.version} | Date: ${data.date}\n`; content += "========================================\n\n"; data.stages.forEach(stage => { content += `STAGE ${stage.order}: ${stage.name.toUpperCase()} (${stage.prob}% PROBABILITY)\n`; content += "----------------------------------------\n"; content += `ENTRY GATE: ${stage.entry}\n`; content += `EXIT GATE: ${stage.exit}\n\n`; }); const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `pipeline_protocol_${data.name.replace(/ /g, '_')}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = (data) => { 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 margin = 15; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - margin * 2; let yPos = 20; // --- Helper for wrapping and section titles const addText = (text, size = 11, style = 'normal', color = [52, 73, 94]) => { doc.setFontSize(size); doc.setFont(undefined, style); doc.setTextColor(color[0], color[1], color[2]); const lines = doc.splitTextToSize(text, usableWidth); if (lines.length * 6 + yPos > 280) { doc.addPage(); yPos = 20; } doc.text(lines, margin, yPos); yPos += (lines.length * 6) + 4; }; const addSectionTitle = (text) => { yPos += 5; addText(text, 14, 'bold', [26, 115, 232]); // Blue doc.setDrawColor(224, 224, 224); doc.line(margin, yPos - 3, pageWidth - margin, yPos - 3); yPos += 2; }; // 1. Header doc.setFontSize(18); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(`Sales Pipeline Protocol: ${data.name}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 8; doc.setFontSize(10); doc.setTextColor(108, 117, 125); doc.text(`Version: ${data.version} | Date: ${data.date}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. Stages Overview Table addSectionTitle("1. Pipeline Stages Overview"); const tableHead = [['#', 'Stage Name', 'Win Prob. (%)']]; const tableBody = data.stages.map(s => [s.order, s.name, `${s.prob}%`]); doc.autoTable({ startY: yPos, head: tableHead, body: tableBody, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: [52, 73, 94] }, headStyles: { fillColor: [26, 115, 232], textColor: [255, 255, 255] }, columnStyles: { 0: { cellWidth: 10 }, 2: { cellWidth: 25, fontStyle: 'bold', halign: 'center' } }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; // 3. Detailed Stage Gates addSectionTitle("2. Detailed Stage Gates"); data.stages.forEach(stage => { addText(`Stage ${stage.order}: ${stage.name} (${stage.prob}%)`, 12, 'bold'); addText("ENTRY GATE:", 10, 'bold', 5); addText(stage.entry, 10, 'normal', 5); addText("EXIT GATE:", 10, 'bold', 5); addText(stage.exit, 10, 'normal', 5); yPos += 5; // Extra space between stages }); doc.save(`pipeline_protocol_${data.name.replace(/ /g, '_')}.pdf`); }; // --- Initialization --- // Initial setup for the state array while (pipelineData.stages.length < numStages) { pipelineData.stages.push({ id: stageIdCounter++, name: `Stage ${pipelineData.stages.length + 1}`, prob: 0, entry: 'TBD', exit: 'TBD' }); } // Event Listeners for Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Initial render showTab(1); });
Scroll to Top