${g.description}
`).join('')}
Action Plan
${data.actions.map(a => `
${a.title}
Owner: ${a.owner || 'N/A'}
Timeline: ${a.timeline || 'N/A'}
`).join('')}
Measures of Success
Key Performance Indicators (KPIs)
${listToHtml(data.kpis)}
Budget Overview
${data.budget}
`;
}
const addBlock = (containerId, countText, content) => {
const container = document.getElementById(containerId);
if (!container) return;
const blockCount = container.children.length + 1;
const newBlock = document.createElement('div');
newBlock.className = 'p-4 border border-gray-200 rounded-lg';
newBlock.innerHTML = `
${countText} ${blockCount}
${content}`;
container.appendChild(newBlock);
};
const addNewGoalBlock = () => addBlock('goals-container', 'Goal', `
`);
const addNewActionBlock = () => addBlock('actions-container', 'Action Item', `
`);
function showMessage() {
messageBox.classList.remove('opacity-0', 'translate-y-10');
messageBox.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
messageBox.classList.remove('opacity-100', 'translate-y-0');
messageBox.classList.add('opacity-0', 'translate-y-10');
}, 2000);
}
// --- PDF Generation ---
function generateStrategyPdf() {
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const data = getFormData();
const pageW = pdf.internal.pageSize.getWidth();
const pageH = pdf.internal.pageSize.getHeight();
const margin = 20;
const contentWidth = pageW - (margin * 2);
let y = 0;
const lineH = 6;
const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
const addHeaderFooter = () => {
const pageCount = pdf.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
pdf.setPage(i);
if (i > 1) {
pdf.setFontSize(9);
pdf.setTextColor('#6b7280');
pdf.text('Strategic Plan', margin, 12);
pdf.text(`${data.companyName} | Page ${i}`, pageW - margin, 12, { align: 'right' });
pdf.setDrawColor('#d1d5db');
pdf.setLineWidth(0.2);
pdf.line(margin, 15, pageW - margin, 15);
}
}
};
const checkBreak = (needed = 20) => {
if (y + needed > pageH - margin) {
pdf.addPage();
y = margin + 10;
}
};
const addText = (text, options = {}) => {
const { size=10, style='normal', color='#374151', indent=0, spacing=1 } = options;
if (!text) return;
checkBreak();
pdf.setFontSize(size);
pdf.setFont(undefined, style);
pdf.setTextColor(color);
const splitText = pdf.splitTextToSize(text, contentWidth - indent);
pdf.text(splitText, margin + indent, y);
y += (splitText.length * lineH * 0.8) + (lineH * spacing);
};
const addSection = (title, content, renderFn) => {
if (!content || (Array.isArray(content) && content.length === 0)) return;
y += lineH * 1.5;
checkBreak(30);
addText(title, { size: 16, style: 'bold', color: '#1e3a8a' }); // blue-800
y += lineH * 0.5;
renderFn(content);
};
// --- Build PDF Document ---
// Page 1: Title Page
pdf.setFillColor('#1e40af'); // blue-800
pdf.rect(0, 0, pageW, pageH, 'F');
pdf.setFontSize(32);
pdf.setFont(undefined, 'bold');
pdf.setTextColor('#FFFFFF');
pdf.text('Strategic Plan', pageW / 2, 110, { align: 'center' });
pdf.setFontSize(18);
pdf.text(data.companyName, pageW / 2, 130, { align: 'center' });
pdf.setFontSize(12);
pdf.text(today, pageW / 2, 150, { align: 'center' });
pdf.addPage();
y = margin + 10;
// Content Pages
addSection('Vision & Mission Statements', data, () => {
addText('Vision:', {style: 'bold'});
addText(data.vision, {style: 'italic'});
y += lineH;
addText('Mission:', {style: 'bold'});
addText(data.mission, {style: 'italic'});
});
addSection('SWOT Analysis', data, () => {
const swotYStart = y;
const halfW = contentWidth / 2;
const boxPad = 5;
// Strengths
addText('Strengths', {style: 'bold', color: '#166534'});
data.strengths.forEach(s => addText(`• ${s}`, {indent: 2}));
const strengthsYEnd = y;
y = swotYStart;
// Weaknesses
addText('Weaknesses', {style: 'bold', color: '#991b1b', indent: halfW + boxPad});
data.weaknesses.forEach(w => addText(`• ${w}`, {indent: halfW + boxPad + 2}));
const weaknessesYEnd = y;
y = Math.max(strengthsYEnd, weaknessesYEnd) + lineH * 2;
const swotYMid = y;
// Opportunities
addText('Opportunities', {style: 'bold', color: '#1d4ed8'});
data.opportunities.forEach(o => addText(`• ${o}`, {indent: 2}));
const oppsYEnd = y;
y = swotYMid;
// Threats
addText('Threats', {style: 'bold', color: '#92400e', indent: halfW + boxPad});
data.threats.forEach(t => addText(`• ${t}`, {indent: halfW + boxPad + 2}));
const threatsYEnd = y;
y = Math.max(oppsYEnd, threatsYEnd);
});
addSection('Strategic Goals', data.goals, (goals) => {
goals.forEach(g => {
y += lineH;
addText(g.title, {style: 'bold', size: 11, color: '#1f2937'});
addText(g.description, {indent: 5});
});
});
addSection('Action Plan', data.actions, (actions) => {
actions.forEach(a => {
y += lineH;
addText(a.title, {style: 'bold', size: 11, color: '#1f2937'});
addText(`Owner: ${a.owner || 'N/A'} | Timeline: ${a.timeline || 'N/A'}`, {indent: 5, style: 'italic'});
});
});
addSection('Measures of Success', data, () => {
addText('Key Performance Indicators (KPIs):', {style: 'bold'});
data.kpis.forEach(kpi => addText(`• ${kpi}`, {indent: 5}));
y += lineH;
addText('Budget Overview:', {style: 'bold'});
addText(data.budget);
});
addHeaderFooter();
pdf.save(`Strategic_Plan_${data.companyName.replace(/\s+/g, '_')}.pdf`);
}
// --- Event Listeners ---
tabs.forEach(tab => tab.addEventListener('click', () => goToTab(parseInt(tab.dataset.tab))));
prevButton.addEventListener('click', () => goToTab(currentTab - 1));
nextButton.addEventListener('click', () => goToTab(currentTab + 1));
addGoalButton.addEventListener('click', addNewGoalBlock);
addActionButton.addEventListener('click', addNewActionBlock);
copyButton.addEventListener('click', () => {
const preview = document.getElementById('plan-preview-container');
if (preview) {
navigator.clipboard.writeText(preview.innerText).then(() => {
showMessage();
}).catch(err => console.error('Copy failed: ', err));
}
});
pdfDownloadButton.addEventListener('click', generateStrategyPdf);
// Initialize
updateTabUI();
});