${savingText}
Eco-Benefit Notes (e.g., ${ecoItem.ecoBenefitPlaceholder || 'reduced plastic, lower emissions'})
I plan to make this change / explore this further.
`;
container.appendChild(itemDiv);
});
} catch(e) { console.error("Error in sbel_renderEcoAlternatives:", e.stack); }
}
function sbel_updateEcoChoice(itemId, field, value) {
try {
if (!sbel_ecoChoices[itemId]) {
sbel_ecoChoices[itemId] = { currentSpend: 0, alternativeSpend: 0, ecoBenefitNotes: '', considerChange: false };
}
if (field === 'currentSpend' || field === 'alternativeSpend') {
sbel_ecoChoices[itemId][field] = parseFloat(value) || 0;
} else if (field === 'considerChange'){
sbel_ecoChoices[itemId][field] = value; // boolean
} else {
sbel_ecoChoices[itemId][field] = value; // notes (string)
}
const itemData = sbel_ecoChoices[itemId];
const monthlySaving = itemData.currentSpend - itemData.alternativeSpend;
const annualSaving = monthlySaving * 12;
let savingText = "No change in cost.";
let savingClass = "";
if (monthlySaving > 0) {
savingText = `Monthly Saving:
${sbel_formatCurrency(monthlySaving)} | Annual Saving:
${sbel_formatCurrency(annualSaving)} `;
savingClass = "sbel-saving";
} else if (monthlySaving < 0) {
savingText = `Monthly Extra Cost:
${sbel_formatCurrency(Math.abs(monthlySaving))} | Annual Extra Cost:
${sbel_formatCurrency(Math.abs(annualSaving))} `;
savingClass = "sbel-extra-cost";
}
const savingTextEl = document.getElementById(`eco-${itemId}-savingText`);
if(savingTextEl){
savingTextEl.innerHTML = savingText;
savingTextEl.className = `sbel-cost-comparison ${savingClass}`;
}
} catch(e) { console.error("Error in sbel_updateEcoChoice:", e.stack); }
}
function sbel_calculateSummary() {
try {
let baselineTotal = 0;
for (const catId in sbel_currentSpendings) {
baselineTotal += sbel_currentSpendings[catId];
}
const baselineTotalEl = document.getElementById('sbel-summaryBaselineTotal');
if(baselineTotalEl) baselineTotalEl.textContent = sbel_formatCurrency(baselineTotal);
let totalMonthlySavingsFromChanges = 0;
const actionPlanList = document.getElementById('sbel-actionPlanList');
if(actionPlanList) actionPlanList.innerHTML = '';
let actionItemsExist = false;
sbel_eco_alternatives_config.forEach(ecoConfig => {
const choice = sbel_ecoChoices[ecoConfig.id];
if (choice && choice.considerChange) {
actionItemsExist = true;
const diff = choice.currentSpend - choice.alternativeSpend;
totalMonthlySavingsFromChanges += diff;
if (actionPlanList) {
const listItem = document.createElement('div');
listItem.className = 'sbel-action-plan-item';
let savingText = "";
if (diff > 0) savingText = `(Saves ${sbel_formatCurrency(diff)}/month)`;
else if (diff < 0) savingText = `(Costs ${sbel_formatCurrency(Math.abs(diff))}/month extra)`;
listItem.innerHTML = `
${ecoConfig.areaName}
Benefit: ${choice.ecoBenefitNotes || "N/A"}
`;
actionPlanList.appendChild(listItem);
}
}
});
const projectedEcoTotal = baselineTotal - totalMonthlySavingsFromChanges;
const summaryEcoTotalEl = document.getElementById('sbel-summaryEcoTotal');
if(summaryEcoTotalEl) summaryEcoTotalEl.textContent = sbel_formatCurrency(projectedEcoTotal);
const savingElement = document.getElementById('sbel-totalPotentialSavingMonthly');
const annualSavingElement = document.getElementById('sbel-annualPotentialSaving');
if (savingElement) savingElement.innerHTML = `Total Potential Monthly Savings: `;
if (annualSavingElement) annualSavingElement.innerHTML = `Total Potential Annual Savings: `;
const noActionEl = document.getElementById('sbel-noActionItems');
if(noActionEl) noActionEl.style.display = actionItemsExist ? 'none' : 'block';
const summaryContainerEl = document.getElementById('sbel-summaryOutputContainer');
if(summaryContainerEl) summaryContainerEl.style.display = 'block';
const noSummaryMsgEl = document.getElementById('sbel-noSummaryMessage');
if(noSummaryMsgEl) noSummaryMsgEl.style.display = 'none';
sbel_pdfDataStore = {
baselineTotal,
projectedEcoTotal,
totalMonthlySavingsFromChanges,
ecoChoices: JSON.parse(JSON.stringify(sbel_ecoChoices)),
ecoAlternativesConfig: sbel_eco_alternatives_config,
currentSpendingsRaw: JSON.parse(JSON.stringify(sbel_currentSpendings))
};
} catch(error) {
console.error("Error calculating summary:", error.stack);
alert("Error calculating summary. Check console for details.");
sbel_pdfDataStore = null;
}
}
function sbel_downloadPDF() {
try {
if (!sbel_pdfDataStore) {
sbel_calculateSummary(); // Ensure data is calculated before PDF generation
if (!sbel_pdfDataStore) { // Check again after calculation
alert("Please input data and calculate the summary first (Tab 3 -> View/Refresh Summary)."); return;
}
}
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
alert('PDF library (jsPDF) not loaded.'); return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'pt');
if (typeof doc.autoTable !== 'function') {
alert('PDF Table plugin (jsPDF-AutoTable) not loaded.'); return;
}
const data = sbel_pdfDataStore;
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--sbel-primary-color').trim();
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--sbel-text-color').trim();
let yPos = 40;
doc.setFontSize(18); doc.setTextColor(primaryColor);
doc.text("Sustainable Budget & Eco-Living Plan", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 25;
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Budget Overview (Monthly)", 14, yPos); yPos += 15;
const summaryData = [
["Baseline Monthly Spending:", sbel_formatCurrency(data.baselineTotal)],
["Projected Monthly Spending with Eco-Changes:", sbel_formatCurrency(data.projectedEcoTotal)],
[{content: "Total Potential Monthly Savings:", styles:{fontStyle:'bold'}},
{content: sbel_formatCurrency(data.totalMonthlySavingsFromChanges), styles:{fontStyle:'bold', textColor: data.totalMonthlySavingsFromChanges >= 0 ? primaryColor : getComputedStyle(document.documentElement).getPropertyValue('--sbel-accent-color').trim()}}],
["Total Potential Annual Savings:", sbel_formatCurrency(data.totalMonthlySavingsFromChanges * 12)]
];
doc.autoTable({ startY: yPos, body: summaryData, theme: 'plain', styles: { fontSize: 9, cellPadding:3 }, columnStyles: {0:{fontStyle:'bold', cellWidth:250}}});
yPos = doc.lastAutoTable.finalY + 20;
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Eco-Action Plan (Changes Considered)", 14, yPos); yPos += 15;
const actionPlanHeaders = [["Area of Change", "Current Spend ($/mo)", "Alternative Spend ($/mo)", "Monthly Saving ($)", "Eco-Benefit Notes"]];
const actionPlanBody = [];
data.ecoAlternativesConfig.forEach(config => {
const choice = data.ecoChoices[config.id];
if (choice && choice.considerChange) {
const saving = choice.currentSpend - choice.alternativeSpend;
actionPlanBody.push([
config.areaName,
choice.currentSpend.toFixed(2),
choice.alternativeSpend.toFixed(2),
saving.toFixed(2),
choice.ecoBenefitNotes || "N/A"
]);
}
});
if (actionPlanBody.length > 0) {
doc.autoTable({
startY: yPos, head: actionPlanHeaders, body: actionPlanBody, theme: 'grid',
headStyles: { fillColor: primaryColor, textColor: [255,255,255], fontStyle: 'bold' },
styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak' },
columnStyles: { 1:{halign:'right'}, 2:{halign:'right'}, 3:{halign:'right'}, 4:{cellWidth:150} }
});
yPos = doc.lastAutoTable.finalY + 15;
} else {
doc.setFontSize(9); doc.setTextColor(textColor); doc.text("No specific eco-actions were selected for the plan.", 14, yPos); yPos+=10;
}
if (yPos > doc.internal.pageSize.getHeight() - 80 && Object.keys(data.currentSpendingsRaw).length > 0) { doc.addPage(); yPos = 40; }
doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Baseline Monthly Spending Details (Summary)", 14, yPos); yPos += 15;
const baselineHeaders = [["Category", "Current Monthly Spend ($)"]];
const baselineBody = [];
sbel_budget_categories.forEach(catConfig => {
const spend = data.currentSpendingsRaw[catConfig.id] || 0;
if(spend > 0) {
baselineBody.push([catConfig.name, spend.toFixed(2)]);
}
});
if(baselineBody.length > 0) {
doc.autoTable({
startY: yPos, head: baselineHeaders, body: baselineBody, theme: 'striped',
headStyles: { fillColor: primaryColor, textColor: [255,255,255], fontStyle: 'bold' },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 1:{halign:'right'} }
});
} else {
doc.setFontSize(9); doc.setTextColor(textColor); doc.text("No baseline spending details were entered.", 14, yPos);
}
const pageCount = doc.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(8); doc.setTextColor(150);
doc.text(`Page ${i} of ${pageCount}`, doc.internal.pageSize.getWidth() / 2, doc.internal.pageSize.getHeight() - 10, { align: 'center' });
doc.text(`Generated on: ${new Date().toLocaleDateString('en-US', {year:'numeric', month:'short', day:'numeric'})}`, 14, doc.internal.pageSize.getHeight() - 10);
doc.text(`Sustainable Budget Plan`, doc.internal.pageSize.getWidth() - 14, doc.internal.pageSize.getHeight() - 10, { align: 'right' });
}
doc.save("Sustainable_Budget_Plan.pdf");
} catch(e) { console.error("Error in sbel_downloadPDF:", e.stack); alert("Error generating PDF.");}
}
document.addEventListener('DOMContentLoaded', () => {
try {
if (sbel_tabs.length > 0 && sbel_tabs[0]) sbel_openTab(null, sbel_tabs[0].id);
sbel_updateNavButtons();
sbel_renderCurrentSpendingInputs();
sbel_renderEcoAlternatives();
sbel_calculateSummary();
const primaryColorVal = getComputedStyle(document.documentElement).getPropertyValue('--sbel-primary-color').trim();
if (primaryColorVal.startsWith('#')) {
const r = parseInt(primaryColorVal.slice(1, 3), 16);
const g = parseInt(primaryColorVal.slice(3, 5), 16);
const b = parseInt(primaryColorVal.slice(5, 7), 16);
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
document.documentElement.style.setProperty('--sbel-primary-color-rgb', `${r},${g},${b}`);
} else { // Fallback if parsing fails
document.documentElement.style.setProperty('--sbel-primary-color-rgb', `76,175,80`);
}
} else { // Fallback if not hex (e.g. named color, or rgb string)
document.documentElement.style.setProperty('--sbel-primary-color-rgb', `76,175,80`);
}
} catch (e) {
console.error("Error during DOMContentLoaded setup:", e.stack);
alert("An error occurred initializing the tool. Please check the console (Right-click -> Inspect -> Console).");
}
});