Personal Cost-Cutting Heatmap Tool
Log Your Expenses
Budget Period
Add Expense Item
Expense List:
Spending Heatmap & Cost-Cut Targets
Spending Analysis for Selected Period
Categories are color-coded by their share of total spending. Set target reductions for categories where you want to cut costs.
Please log expenses on Tab 1 first, then click "Generate/Refresh Heatmap".
Savings Summary & Action Plan
Financial Overview (Per Period)
Total Current Spending: $0.00
Total Potential Savings (from targets): $0.00
Projected New Total Spending: $0.00
Your Cost-Cutting Action Plan
Categories where you've targeted reductions:
No cost-cutting targets set yet. Go to Tab 2 to set targets.
General Cost-Cutting Tips (Informational)
- Track Spending: Regularly monitor where your money goes to identify patterns.
- Budgeting: Create a realistic budget and stick to it. Differentiate needs from wants.
- Reduce Subscriptions: Cancel unused or non-essential subscriptions and memberships.
- Dining Out: Cook more meals at home and pack lunches. Look for specials when eating out.
- Groceries: Plan meals, make a shopping list, buy in bulk (if practical), use coupons, and avoid impulse buys.
- Utilities: Conserve energy and water. Shop around for better rates if options exist.
- Entertainment: Look for free or low-cost activities, library resources, and community events.
Log expenses (Tab 1), set targets (Tab 2), then click "View / Refresh Full Summary".
Spent: ${pcch_formatCurrency(amountSpent)}
Share: ${percentage.toFixed(1)}% of total
Potential Saving: ${pcch_formatCurrency(pcch_categoryTargets[categoryName].potentialSaving)}
`; heatmapContainer.appendChild(itemDiv); } pcch_updateTotalPotentialSavingsTargets(); } catch(e) {console.error("Error in pcch_generateHeatmapAndTargets:", e.stack); alert("Error generating heatmap. Check console.");} } function pcch_updateCategorySaving(categoryName, reductionValueStr) { try { const reductionValue = parseFloat(reductionValueStr) || 0; if (pcch_categoryTargets[categoryName]) { const originalAmount = pcch_categoryTargets[categoryName].originalAmount; const actualReduction = Math.min(reductionValue, originalAmount); pcch_categoryTargets[categoryName].targetReduction = actualReduction; pcch_categoryTargets[categoryName].potentialSaving = actualReduction; const savingDisplayEl = document.getElementById(`pcch-saving-${categoryName.replace(/\s+/g, '-')}`); if(savingDisplayEl) savingDisplayEl.innerHTML = `Potential Saving: ${pcch_formatCurrency(actualReduction)}`; const inputEl = document.getElementById(`pcch-target-${categoryName.replace(/\s+/g, '-')}`); if(inputEl && reductionValue > originalAmount && originalAmount >= 0) { // only update if originalAmount is non-negative inputEl.value = originalAmount; } else if (inputEl && reductionValue < 0) { // Prevent negative reduction inputEl.value = 0; pcch_categoryTargets[categoryName].targetReduction = 0; pcch_categoryTargets[categoryName].potentialSaving = 0; savingDisplayEl.innerHTML = `Potential Saving: ${pcch_formatCurrency(0)}`; } } pcch_updateTotalPotentialSavingsTargets(); } catch(e) {console.error("Error in pcch_updateCategorySaving:", e.stack);} } function pcch_updateTotalPotentialSavingsTargets() { try { let totalSavings = 0; for (const category in pcch_categoryTargets) { totalSavings += pcch_categoryTargets[category].potentialSaving || 0; } const totalSavingsEl = document.getElementById('pcch-totalPotentialSavingsTargets'); if(totalSavingsEl) totalSavingsEl.textContent = pcch_formatCurrency(totalSavings); } catch(e) {console.error("Error in pcch_updateTotalPotentialSavingsTargets:", e.stack);} } function pcch_displayFullSummary() { console.log("pcch_displayFullSummary called"); try { pcch_generateHeatmapAndTargets(); let currentTotalSpending = 0; pcch_expenses.forEach(exp => currentTotalSpending += exp.amount); let totalPotentialSavings = 0; const actionPlanList = document.getElementById('pcch-actionPlanList'); const noActionMsg = document.getElementById('pcch-noActionItemsMessage'); if(actionPlanList) actionPlanList.innerHTML = ''; let actionItemsExist = false; for (const categoryName in pcch_categoryTargets) { const catData = pcch_categoryTargets[categoryName]; if (catData.potentialSaving > 0) { actionItemsExist = true; totalPotentialSavings += catData.potentialSaving; if (actionPlanList) { const listItem = document.createElement('li'); listItem.innerHTML = `Reduce ${categoryName} by ${pcch_formatCurrency(catData.potentialSaving)} (Original: ${pcch_formatCurrency(catData.originalAmount)}, Target Reduction: ${pcch_formatCurrency(catData.targetReduction)})`; actionPlanList.appendChild(listItem); } } } if(noActionMsg) noActionMsg.style.display = actionItemsExist ? 'none' : 'block'; const projectedNewTotal = currentTotalSpending - totalPotentialSavings; document.getElementById('pcch-summaryPeriodDisplay').textContent = document.getElementById('pcch-budgetPeriod').value; document.getElementById('pcch-summaryCurrentTotal').textContent = pcch_formatCurrency(currentTotalSpending); document.getElementById('pcch-summaryPotentialSavings').textContent = pcch_formatCurrency(totalPotentialSavings); document.getElementById('pcch-summaryNewTotal').textContent = pcch_formatCurrency(projectedNewTotal); document.getElementById('pcch-summaryOutputContainer').style.display = 'block'; document.getElementById('pcch-noFullSummaryMessage').style.display = 'none'; pcch_pdfDataStore = { period: document.getElementById('pcch-budgetPeriod').value, expenses: JSON.parse(JSON.stringify(pcch_expenses)), categoryTotalsData: JSON.parse(JSON.stringify(pcch_categoryTargets)), currentTotalSpending, totalPotentialSavings, projectedNewTotal }; } catch(e) {console.error("Error in pcch_displayFullSummary:", e.stack); pcch_pdfDataStore = null; alert("Error displaying summary. Check console.");} } function pcch_downloadPDF() { console.log("pcch_downloadPDF called"); try { if (!pcch_pdfDataStore) { pcch_displayFullSummary(); if (!pcch_pdfDataStore) { alert("Please log expenses and set targets, then view summary before downloading PDF."); 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 = pcch_pdfDataStore; const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--pcch-primary-color').trim(); const saveColor = getComputedStyle(document.documentElement).getPropertyValue('--pcch-accent-save-color').trim(); let yPos = 40; doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text("Personal Cost-Cutting Analysis", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 15; doc.setFontSize(10); doc.setTextColor(50); doc.text(`Budget Period: ${data.period}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 20; doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Financial Overview", 14, yPos); yPos += 15; const overviewBody = [ ["Total Current Spending:", pcch_formatCurrency(data.currentTotalSpending)], ["Total Potential Savings (from targets):", pcch_formatCurrency(data.totalPotentialSavings)], ["Projected New Total Spending:", pcch_formatCurrency(data.projectedNewTotal)] ]; doc.autoTable({startY: yPos, body: overviewBody, theme:'plain', styles:{fontSize:9, cellPadding:3}, columnStyles:{0:{fontStyle:'bold', cellWidth:200}}}); yPos = doc.lastAutoTable.finalY + 15; if (yPos > doc.internal.pageSize.getHeight() - 80 && Object.keys(data.categoryTotalsData).length > 0) { doc.addPage(); yPos = 40; } doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Spending Breakdown & Cost-Cut Targets", 14, yPos); yPos += 15; const heatmapHeaders = [["Category", "Amount Spent ($)", "% of Total", "Target Reduction ($)", "Potential Saving ($)"]]; const heatmapBody = []; for (const categoryName in data.categoryTotalsData) { const cat = data.categoryTotalsData[categoryName]; heatmapBody.push([ categoryName, (cat.originalAmount || 0).toFixed(2), `${(cat.percentage || 0).toFixed(1)}%`, (cat.targetReduction || 0).toFixed(2), (cat.potentialSaving || 0).toFixed(2) ]); } if (heatmapBody.length > 0) { doc.autoTable({ startY: yPos, head: heatmapHeaders, body: heatmapBody, theme:'grid', headStyles:{fillColor:primaryColor, textColor:[255,255,255]}, styles:{fontSize:8, cellPadding:2}, columnStyles:{1:{halign:'right'}, 2:{halign:'right'}, 3:{halign:'right'}, 4:{halign:'right'}} }); yPos = doc.lastAutoTable.finalY + 15; } else { doc.setFontSize(9); doc.text("No categorized spending data to display.", 14, yPos); yPos+=10; } if (data.expenses.length > 0) { if (yPos > doc.internal.pageSize.getHeight() - 80) { doc.addPage(); yPos = 40; } doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Detailed Expense Log", 14, yPos); yPos += 15; const expenseHeaders = [["Description", "Category", "Amount ($)"]]; const expenseBody = data.expenses.map(exp => [exp.description || "-", exp.category, exp.amount.toFixed(2)]); doc.autoTable({startY: yPos, head: expenseHeaders, body: expenseBody, theme:'striped', headStyles:{fillColor:primaryColor, textColor:[255,255,255]}, styles:{fontSize:8, cellPadding:2}, columnStyles:{2:{halign:'right'}}}); yPos = doc.lastAutoTable.finalY + 15; } 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() - 20, { align: 'center' }); doc.text(`Generated on: ${new Date().toLocaleDateString('en-US', {year:'numeric', month:'short', day:'numeric'})}`, 14, doc.internal.pageSize.getHeight() - 20); doc.text(`Cost-Cutting Heatmap Report`, doc.internal.pageSize.getWidth() - 14, doc.internal.pageSize.getHeight() - 20, { align: 'right' }); } doc.save("Cost_Cutting_Heatmap_Report.pdf"); } catch(e) { console.error("Error in pcch_downloadPDF:", e.stack); alert("Error generating PDF: " + e.message);} } document.addEventListener('DOMContentLoaded', () => { console.log("DOMContentLoaded event fired."); try { if (pcch_tabs && pcch_tabs.length > 0 && pcch_tabs[0] && pcch_tabs[0].id) { pcch_openTab(null, pcch_tabs[0].id); } else { console.error("Tabs or tabButtons not initialized correctly or first tab is missing ID."); // Fallback or error display if tabs are not set up const container = document.querySelector('.pcch-tool-container'); if (container) container.innerHTML = "Tool initialization error: Tab structure not found. Please check the HTML.
"; return; // Stop further execution if basic tab structure is missing } pcch_updateNavButtons(); pcch_renderExpenseList(); pcch_updatePeriodInExpenseLabels(); const primaryColorVal = getComputedStyle(document.documentElement).getPropertyValue('--pcch-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('--pcch-primary-color-rgb', `${r},${g},${b}`); } else { // Fallback document.documentElement.style.setProperty('--pcch-primary-color-rgb', `69,90,100`); } } else { // Fallback document.documentElement.style.setProperty('--pcch-primary-color-rgb', `69,90,100`); } console.log("DOMContentLoaded setup complete."); } 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). The tool might not be fully functional."); } }); console.log("Script loaded.");