Free vs. Paid Services: Cost-Benefit Analyzer
1. Define Services & Costs
2. Feature & Benefit Valuation
3. Comparative Analysis
4. Summary & PDF
Step 1: Define Comparison Sets and Your Time Value
+ Add Comparison Set
Previous
Next
Step 2: Feature & Benefit Valuation (Paid Service)
-- Select a Comparison Set --
Previous
Next
Step 3: Comparative Analysis
-- Select a Comparison Set --
Previous
Next
Step 4: Overall Summary
Define and analyze comparison sets to see the summary here.
Download Full Analysis as PDF
Previous
Next
Select a set to see analysis.
';
}
});
}
// --- Tab 2: Feature & Benefit Valuation ---
function loadSetForFeatureBenefit(setId) {
collectAllSetInputs(); // Ensure latest data, esp. set names
const set = comparisonSets.find(s => s.id === parseInt(setId));
const form = document.getElementById('featureBenefitForm');
const featureRowsContainer = document.getElementById('featureRowsContainer');
if (!set) {
form.style.display = 'none';
return;
}
form.style.display = 'block';
document.querySelector('#featureBenefitForm .current-set-name-fb').textContent = set.setName;
const fv = set.paidService.featuresValue || {};
document.getElementById('fbEfficiencyGain').value = fv.efficiencyGainValue || 0;
document.getElementById('fbPremiumFeatures').value = fv.premiumFeaturesValue || 0;
document.getElementById('fbSupportValue').value = fv.supportValue || 0;
document.getElementById('fbAdFreeValue').value = fv.adFreeValue || 0;
document.getElementById('fbOtherBenefits').value = fv.otherBenefitsValue || 0;
// Load feature comparison rows
featureRowsContainer.innerHTML = ''; // Clear existing
if (set.featureComparisonRows && set.featureComparisonRows.length > 0) {
set.featureComparisonRows.forEach(featRow => addFeatureComparisonRow(setId, featRow.id, featRow.featureName, featRow.freeValue, featRow.paidValue));
} else {
// Add one blank if none exist for this set
addFeatureComparisonRow(setId);
}
}
function addFeatureComparisonRow(setId, existingRowId = null, name = '', freeV = '', paidV = '') {
const container = document.getElementById('featureRowsContainer');
featureRowIdCounter = existingRowId !== null ? Math.max(featureRowIdCounter, existingRowId) +1 : featureRowIdCounter + 1;
const rowId = existingRowId !== null ? existingRowId : featureRowIdCounter;
const newRow = document.createElement('div');
newRow.classList.add('feature-row');
newRow.setAttribute('data-feature-row-id', rowId);
newRow.innerHTML = `
Feature:
Free Version:
Paid Version:
X
`;
container.appendChild(newRow);
}
function saveFeatureBenefitData(showAlert = true) {
const setId = document.getElementById('comparisonSetSelectFB').value;
if (!setId) { if(showAlert) alert("Please select a comparison set."); return; }
const set = comparisonSets.find(s => s.id === parseInt(setId));
if (!set) return;
set.paidService.featuresValue = {
efficiencyGainValue: parseFloat(document.getElementById('fbEfficiencyGain').value) || 0,
premiumFeaturesValue: parseFloat(document.getElementById('fbPremiumFeatures').value) || 0,
supportValue: parseFloat(document.getElementById('fbSupportValue').value) || 0,
adFreeValue: parseFloat(document.getElementById('fbAdFreeValue').value) || 0,
otherBenefitsValue: parseFloat(document.getElementById('fbOtherBenefits').value) || 0,
};
set.paidService.featuresValue.totalValue = Object.values(set.paidService.featuresValue).reduce((sum, val) => sum + val, 0);
// Save feature comparison rows
set.featureComparisonRows = [];
document.querySelectorAll('#featureRowsContainer .feature-row').forEach(rowEl => {
const rowId = rowEl.getAttribute('data-feature-row-id');
set.featureComparisonRows.push({
id: rowId,
featureName: document.getElementById(`featureName_${setId}_${rowId}`).value,
freeValue: document.getElementById(`featureFree_${setId}_${rowId}`).value,
paidValue: document.getElementById(`featurePaid_${setId}_${rowId}`).value,
});
});
if(showAlert) alert(`Benefit valuation saved for ${set.setName}. View analysis on Tab 3.`);
displayAnalysis(setId); // Update analysis tab if it's already loaded with this set
populateComparisonSetSelects(); // Refresh other selects just in case
}
// --- Tab 3: Comparative Analysis ---
function displayAnalysis(setId) {
collectAllSetInputs(); // Ensure basic costs are up-to-date
const set = comparisonSets.find(s => s.id === parseInt(setId));
const container = document.getElementById('analysisOutputContainer');
if (!set) {
container.innerHTML = "
Select a comparison set to see the analysis.
";
return;
}
if (!set.paidService.featuresValue || set.paidService.featuresValue.totalValue === undefined) {
container.innerHTML = `
Please complete Step 2 (Feature & Benefit Valuation) for ${set.setName} first.
`;
return;
}
// Calculate Net Effective Cost for Paid Service
set.paidService.netEffectiveCost = set.paidService.effectiveMonthlySubscriptionCost - set.paidService.featuresValue.totalValue;
let analysisHTML = `
Analysis for: ${set.setName} `;
analysisHTML += `
Free Service: ${set.freeService.name}
Direct Monetary Cost: $${set.freeService.monetaryCostMonthly.toFixed(2)}/month
Monetized Time Costs (Ads/Workarounds): $${((set.freeService.timeCostAdsHours + set.freeService.timeCostWorkaroundsHours) * set.valuePerHour).toFixed(2)}/month
Cost of Add-ons: $${set.freeService.addOnsCostMonthly.toFixed(2)}/month
Total Effective Monthly Cost (Free): $${set.freeService.totalEffectiveMonthlyCost.toFixed(2)}
`;
analysisHTML += `
Paid Service: ${set.paidService.name}
Subscription Cost: $${set.paidService.effectiveMonthlySubscriptionCost.toFixed(2)}/month
Total Quantified Value of Benefits: $${set.paidService.featuresValue.totalValue.toFixed(2)}/month
Net Effective Value/Cost (Paid): $${Math.abs(set.paidService.netEffectiveCost).toFixed(2)}
${set.paidService.netEffectiveCost < 0 ? ' (Net Benefit)' : (set.paidService.netEffectiveCost > 0 ? ' (Net Cost)' : ' (Breakeven)')}
`;
analysisHTML += `
`;
if (set.paidService.netEffectiveCost < set.freeService.totalEffectiveMonthlyCost) {
analysisHTML += `
The Paid Service (${set.paidService.name}) appears to offer better overall value for this set, considering your inputs.
`;
set.analysis = { recommendedOption: "Paid Service" };
} else if (set.paidService.netEffectiveCost > set.freeService.totalEffectiveMonthlyCost) {
analysisHTML += `
The Free Service (${set.freeService.name}) appears to be more cost-effective for this set, considering your inputs.
`;
set.analysis = { recommendedOption: "Free Service" };
} else {
analysisHTML += `
Both services have a similar effective value/cost for this set. Consider qualitative factors.
`;
set.analysis = { recommendedOption: "Depends/Equivalent" };
}
analysisHTML += `
`;
container.innerHTML = analysisHTML;
}
// --- Tab 4: Overall Summary ---
function generateOverallComparisonSummary() {
collectAllSetInputs(); // Ensure all data is current
const container = document.getElementById('overallComparisonSummaryTable');
const analyzedSets = comparisonSets.filter(s => s.analysis && s.paidService.featuresValue && s.paidService.featuresValue.totalValue !== undefined);
if (analyzedSets.length === 0) {
container.innerHTML = "
No comparison sets have been fully analyzed. Please complete steps 1-3 for each set.
";
document.getElementById('downloadComparerPdfButton').disabled = true;
return;
}
let tableHTML = `
Comparison Set
Free Service (Effective Cost)
Paid Service (Net Value/Cost)
Recommendation
`;
analyzedSets.forEach(set => {
const freeEffCost = set.freeService.totalEffectiveMonthlyCost;
const paidNetEffValue = set.paidService.netEffectiveCost; // Negative is good (benefit)
tableHTML += `
${set.setName}
${set.freeService.name}: $${freeEffCost.toFixed(2)}
${set.paidService.name}: $${Math.abs(paidNetEffValue).toFixed(2)} ${paidNetEffValue < 0 ? '(Net Benefit)' : paidNetEffValue > 0 ? '(Net Cost)' : '(Breakeven)'}
${set.analysis.recommendedOption}
`;
});
tableHTML += `
`;
container.innerHTML = tableHTML;
document.getElementById('downloadComparerPdfButton').disabled = false;
}
// --- PDF Generation ---
function downloadComparerPdf() {
const analyzedSets = comparisonSets.filter(s => s.analysis && s.paidService.featuresValue && s.paidService.featuresValue.totalValue !== undefined);
if (analyzedSets.length === 0) {
alert("Please fully analyze at least one comparison set to generate a PDF."); return;
}
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert('PDF generation library (jsPDF) is not loaded.'); return;
}
const jsPDFConstructor = window.jspdf.jsPDF;
const doc = new jsPDFConstructor();
if (typeof doc.autoTable !== 'function') {
alert('jsPDF AutoTable plugin not loaded.'); return;
}
const primaryColor = '#007bff', textColor = '#212529', tableHeaderColor = '#e9ecef';
let yPos = 22;
doc.setFontSize(18); doc.setTextColor(primaryColor);
doc.text("Free vs. Paid Services: Cost-Benefit Analysis", 14, yPos); yPos += 8;
doc.setFontSize(10); doc.setTextColor(textColor);
doc.text(`Report Date: ${new Date().toLocaleDateString()}`, 14, yPos); yPos += 7;
doc.text(`Your Value of Time: $${(parseFloat(document.getElementById('valuePerHourGlobal').value) || 0).toFixed(2)}/hour`, 14, yPos); yPos += 10;
analyzedSets.forEach((set, index) => {
if (yPos > 240 && index > 0) { doc.addPage(); yPos = 20; } // New page if needed
doc.setFontSize(14); doc.setTextColor(primaryColor);
doc.text(`Comparison Set: ${set.setName}`, 14, yPos); yPos += 7;
// Free Service Details
doc.setFontSize(11); doc.setTextColor(textColor); doc.setFont(undefined, 'bold');
doc.text(`Free Service: ${set.freeService.name}`, 14, yPos); yPos += 5; doc.setFont(undefined, 'normal');
let freeBody = [
['Direct Monetary Cost/month:', `$${set.freeService.monetaryCostMonthly.toFixed(2)}`],
['Time Cost (Ads/Interrupts):', `${set.freeService.timeCostAdsHours} hrs/month`],
['Time Cost (Workarounds):', `${set.freeService.timeCostWorkaroundsHours} hrs/month`],
['Monetized Time Cost:', `$${((set.freeService.timeCostAdsHours + set.freeService.timeCostWorkaroundsHours) * set.valuePerHour).toFixed(2)}/month`],
['Cost of Add-ons/month:', `$${set.freeService.addOnsCostMonthly.toFixed(2)}`],
[{content: 'Total Effective Monthly Cost (Free):', styles:{fontStyle:'bold'}}, {content: `$${set.freeService.totalEffectiveMonthlyCost.toFixed(2)}`, styles:{fontStyle:'bold'}}]
];
doc.autoTable({ startY: yPos, body: freeBody, theme: 'plain', styles: {fontSize:9, cellPadding:1}, columnStyles: {0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 7;
if (yPos > 260) { doc.addPage(); yPos = 20; }
// Paid Service Details
doc.setFontSize(11); doc.setTextColor(textColor); doc.setFont(undefined, 'bold');
doc.text(`Paid Service: ${set.paidService.name}`, 14, yPos); yPos += 5; doc.setFont(undefined, 'normal');
let paidBody = [
['Subscription Cost:', `$${set.paidService.subscriptionCost.toFixed(2)} (${set.paidService.billingCycle})`],
['Effective Monthly Subscription Cost:', `$${set.paidService.effectiveMonthlySubscriptionCost.toFixed(2)}`],
['--- Valued Benefits of Paid Service ---', ''],
['Increased Efficiency/Productivity:', `$${set.paidService.featuresValue.efficiencyGainValue.toFixed(2)}/month`],
['Value of Premium Features:', `$${set.paidService.featuresValue.premiumFeaturesValue.toFixed(2)}/month`],
['Value of Better Support/Reliability:', `$${set.paidService.featuresValue.supportValue.toFixed(2)}/month`],
['Value of Ad-Free/Better UX:', `$${set.paidService.featuresValue.adFreeValue.toFixed(2)}/month`],
['Other Quantifiable Benefits:', `$${set.paidService.featuresValue.otherBenefitsValue.toFixed(2)}/month`],
[{content: 'Total Quantified Value of Benefits:', styles:{fontStyle:'bold'}}, {content: `$${set.paidService.featuresValue.totalValue.toFixed(2)}/month`, styles:{fontStyle:'bold'}}],
[{content: 'Net Effective Value/Cost (Paid):', styles:{fontStyle:'bold'}},
{content: `$${Math.abs(set.paidService.netEffectiveCost).toFixed(2)} ${set.paidService.netEffectiveCost < 0 ? '(Net Benefit)' : set.paidService.netEffectiveCost > 0 ? '(Net Cost)' : '(Breakeven)'}`, styles:{fontStyle:'bold'}}]
];
doc.autoTable({ startY: yPos, body: paidBody, theme: 'plain', styles: {fontSize:9, cellPadding:1}, columnStyles: {0:{fontStyle:'bold'}}});
yPos = doc.lastAutoTable.finalY + 7;
if (yPos > 260) { doc.addPage(); yPos = 20; }
// Feature Comparison Rows (if any)
if(set.featureComparisonRows && set.featureComparisonRows.filter(fr => fr.featureName.trim() !== '').length > 0){
doc.setFontSize(10); doc.setTextColor(textColor); doc.setFont(undefined, 'bold');
doc.text("Key Feature Differences:", 14, yPos); yPos+=5; doc.setFont(undefined, 'normal');
const featureTableBody = set.featureComparisonRows
.filter(fr => fr.featureName.trim() !== '')
.map(fr => [fr.featureName, fr.freeValue, fr.paidValue]);
doc.autoTable({
startY: yPos,
head: [['Feature', 'Free Version Offers', 'Paid Version Offers']],
body: featureTableBody,
theme: 'grid',
headStyles: {fillColor: tableHeaderColor, textColor: textColor, fontSize:8.5, fontStyle:'bold'},
styles: {fontSize:8, cellPadding:1.5}
});
yPos = doc.lastAutoTable.finalY + 7;
}
// Recommendation for this set
doc.setFontSize(10); doc.setFont(undefined, 'bold');
doc.text(`Recommendation for ${set.setName}: ${set.analysis.recommendedOption}`, 14, yPos); yPos += 10;
});
// Overall Summary Table (if multiple sets analyzed)
if (analyzedSets.length > 1) {
if (yPos > 230) { doc.addPage(); yPos = 20; }
doc.setFontSize(14); doc.setTextColor(primaryColor);
doc.text("Overall Comparison Summary", 14, yPos); yPos += 7;
const summaryPdfBody = analyzedSets.map(set => [
set.setName,
`$${set.freeService.totalEffectiveMonthlyCost.toFixed(2)}`,
`$${Math.abs(set.paidService.netEffectiveCost).toFixed(2)} ${set.paidService.netEffectiveCost < 0 ? '(Net Benefit)' : set.paidService.netEffectiveCost > 0 ? '(Net Cost)' : '(Breakeven)'}`,
set.analysis.recommendedOption
]);
doc.autoTable({
startY: yPos,
head: [['Comparison Set', 'Free Service (Effective Cost)', 'Paid Service (Net Value/Cost)', 'Recommendation']],
body: summaryPdfBody,
theme: 'grid',
headStyles: { fillColor: tableHeaderColor, textColor: textColor, fontStyle: 'bold', fontSize:9 },
styles: { fontSize: 8, cellPadding: 1.5 },
columnStyles: {1:{halign:'right'}, 2:{halign:'right'}}
});
}
doc.save("Free_vs_Paid_Services_Analysis.pdf");
}
// Initialize
updateComparerNavButtons();
populateComparisonSetSelects();