website/hub/services/static/js/roi-calculator/export-manager.js

235 lines
No EOL
10 KiB
JavaScript

/**
* Export Management Module
* Handles PDF and CSV export functionality
*/
class ExportManager {
constructor(calculator, uiManager) {
this.calculator = calculator;
this.uiManager = uiManager;
}
exportToPDF() {
// Check if jsPDF is available
if (typeof window.jspdf === 'undefined') {
alert('PDF export library is loading. Please try again in a moment.');
return;
}
try {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Add header
doc.setFontSize(20);
doc.setTextColor(0, 123, 255); // Bootstrap primary blue
doc.text('CSP ROI Calculator Report', 20, 25);
// Add generation date
doc.setFontSize(10);
doc.setTextColor(100, 100, 100);
const currentDate = new Date().toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
doc.text(`Generated on: ${currentDate}`, 20, 35);
// Reset text color
doc.setTextColor(0, 0, 0);
// Add input parameters section
doc.setFontSize(16);
doc.text('Investment Parameters', 20, 50);
const inputs = this.calculator.getInputValues();
let yPos = 60;
doc.setFontSize(11);
const params = [
['Investment Amount:', this.uiManager.formatCurrencyDetailed(inputs.investmentAmount)],
['Investment Timeframe:', `${inputs.timeframe} years`],
['Investment Model:', inputs.investmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'],
...(inputs.investmentModel === 'loan' ? [['Loan Interest Rate:', `${(inputs.loanInterestRate * 100).toFixed(1)}%`]] : []),
['Revenue per Instance:', this.uiManager.formatCurrencyDetailed(inputs.revenuePerInstance)],
...(inputs.investmentModel === 'direct' ? [
['Servala Revenue Share:', `${(inputs.servalaShare * 100).toFixed(0)}%`],
['Grace Period:', `${inputs.gracePeriod} months`]
] : [])
];
params.forEach(([label, value]) => {
doc.text(label, 25, yPos);
doc.text(value, 80, yPos);
yPos += 8;
});
// Add scenario results section
yPos += 10;
doc.setFontSize(16);
doc.text('Scenario Results', 20, yPos);
yPos += 10;
doc.setFontSize(11);
Object.values(this.calculator.results).forEach(result => {
if (yPos > 250) {
doc.addPage();
yPos = 20;
}
// Scenario header
doc.setFontSize(14);
doc.setTextColor(0, 123, 255);
doc.text(`${result.scenario} Scenario`, 25, yPos);
yPos += 10;
doc.setFontSize(11);
doc.setTextColor(0, 0, 0);
const resultData = [
['Final Instances:', result.finalInstances.toLocaleString()],
['Total Revenue:', this.uiManager.formatCurrencyDetailed(result.totalRevenue)],
['CSP Revenue:', this.uiManager.formatCurrencyDetailed(result.cspRevenue)],
['Servala Revenue:', this.uiManager.formatCurrencyDetailed(result.servalaRevenue)],
['ROI:', this.uiManager.formatPercentage(result.roi)],
['Break-even:', result.breakEvenMonth ? `${result.breakEvenMonth} months` : 'Not achieved']
];
resultData.forEach(([label, value]) => {
doc.text(label, 30, yPos);
doc.text(value, 90, yPos);
yPos += 7;
});
yPos += 8;
});
// Add summary section
if (yPos > 220) {
doc.addPage();
yPos = 20;
}
yPos += 10;
doc.setFontSize(16);
doc.text('Executive Summary', 20, yPos);
yPos += 10;
doc.setFontSize(11);
const enabledResults = Object.values(this.calculator.results);
if (enabledResults.length > 0) {
const avgROI = enabledResults.reduce((sum, r) => sum + r.roi, 0) / enabledResults.length;
const avgBreakeven = enabledResults.filter(r => r.breakEvenMonth).reduce((sum, r) => sum + r.breakEvenMonth, 0) / enabledResults.filter(r => r.breakEvenMonth).length;
doc.text(`This analysis evaluates ${enabledResults.length} growth scenario(s) over a ${inputs.timeframe}-year period.`, 25, yPos);
yPos += 8;
doc.text(`Average projected ROI: ${this.uiManager.formatPercentage(avgROI)}`, 25, yPos);
yPos += 8;
if (!isNaN(avgBreakeven)) {
doc.text(`Average break-even timeline: ${Math.round(avgBreakeven)} months`, 25, yPos);
yPos += 8;
}
yPos += 5;
doc.text('Key assumptions:', 25, yPos);
yPos += 8;
doc.text('• Growth rates based on market analysis and industry benchmarks', 30, yPos);
yPos += 6;
doc.text('• Churn rates reflect typical SaaS industry standards', 30, yPos);
yPos += 6;
doc.text('• Revenue calculations include grace period provisions', 30, yPos);
}
// Add footer
const pageCount = doc.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(8);
doc.setTextColor(150, 150, 150);
doc.text(`Page ${i} of ${pageCount}`, doc.internal.pageSize.getWidth() - 30, doc.internal.pageSize.getHeight() - 10);
doc.text('Generated by Servala CSP ROI Calculator', 20, doc.internal.pageSize.getHeight() - 10);
}
// Save the PDF
const filename = `servala-csp-roi-report-${new Date().toISOString().split('T')[0]}.pdf`;
doc.save(filename);
} catch (error) {
console.error('PDF Export Error:', error);
alert('An error occurred while generating the PDF. Please try again or export as CSV instead.');
}
}
exportToCSV() {
try {
// Create comprehensive CSV with summary and detailed data
let csvContent = 'CSP ROI Calculator Export\n';
csvContent += `Generated on: ${new Date().toLocaleDateString()}\n\n`;
// Add input parameters
csvContent += 'INPUT PARAMETERS\n';
const inputs = this.calculator.getInputValues();
csvContent += `Investment Amount,${inputs.investmentAmount}\n`;
csvContent += `Timeframe (years),${inputs.timeframe}\n`;
csvContent += `Investment Model,${inputs.investmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'}\n`;
if (inputs.investmentModel === 'loan') {
csvContent += `Loan Interest Rate (%),${(inputs.loanInterestRate * 100).toFixed(1)}\n`;
}
csvContent += `Revenue per Instance,${inputs.revenuePerInstance}\n`;
if (inputs.investmentModel === 'direct') {
csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`;
csvContent += `Grace Period (months),${inputs.gracePeriod}\n`;
}
csvContent += '\n';
// Add scenario summary
csvContent += 'SCENARIO SUMMARY\n';
csvContent += 'Scenario,Investment Model,Final Instances,Total Revenue,CSP Revenue,Servala Revenue,ROI (%),Break-even (months)\n';
Object.values(this.calculator.results).forEach(result => {
const modelText = result.investmentModel === 'loan' ? 'Loan' : 'Direct';
csvContent += `${result.scenario},${modelText},${result.finalInstances},${result.totalRevenue.toFixed(2)},${result.cspRevenue.toFixed(2)},${result.servalaRevenue.toFixed(2)},${result.roi.toFixed(2)},${result.breakEvenMonth || 'N/A'}\n`;
});
csvContent += '\n';
// Add detailed monthly data
csvContent += 'MONTHLY BREAKDOWN\n';
csvContent += 'Month,Scenario,New Instances,Churned Instances,Total Instances,Monthly Revenue,CSP Revenue,Servala Revenue,Cumulative CSP Revenue,Cumulative Servala Revenue\n';
// Combine all monthly data
const allData = [];
Object.keys(this.calculator.monthlyData).forEach(scenario => {
this.calculator.monthlyData[scenario].forEach(monthData => {
allData.push(monthData);
});
});
allData.sort((a, b) => a.month - b.month || a.scenario.localeCompare(b.scenario));
allData.forEach(data => {
csvContent += `${data.month},${data.scenario},${data.newInstances},${data.churnedInstances},${data.totalInstances},${data.monthlyRevenue.toFixed(2)},${data.cspRevenue.toFixed(2)},${data.servalaRevenue.toFixed(2)},${data.cumulativeCSPRevenue.toFixed(2)},${data.cumulativeServalaRevenue.toFixed(2)}\n`;
});
// Create and download file
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
const filename = `servala-csp-roi-data-${new Date().toISOString().split('T')[0]}.csv`;
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Clean up
URL.revokeObjectURL(url);
} catch (error) {
console.error('CSV Export Error:', error);
alert('An error occurred while generating the CSV file. Please try again.');
}
}
}