Long-Term Travel Budget Planner
Trip Basics & Savings Goal
Estimated Trip Expenses
No expense items added yet.
Total Estimated Trip Cost: $0.00
Budget Analysis & Summary
Analysis will appear here once trip details and expenses are entered.
Full Plan & Report
A summary of your travel budget plan will appear here.
Status: Departure date issue.
`; } html += `The full PDF will contain the detailed expense list.
`; previewDiv.innerHTML = html; } function lttb_generatePdf() { const analysisData = lttb_calculateAndDisplayAnalysis(); // Ensure data is current if (!analysisData) { alert("Please ensure all trip details and expenses are correctly entered before generating PDF."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); let yPos = 20; const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const accentColor = getComputedStyle(document.documentElement).getPropertyValue('--accent-color').trim(); const dangerColor = getComputedStyle(document.documentElement).getPropertyValue('--danger-color').trim(); const textColor = getComputedStyle(document.documentElement).getPropertyValue('--text-color').trim(); const toCurrency = (num) => `$${num.toFixed(2)}`; doc.setFontSize(18); doc.setTextColor(primaryColor); doc.text(`Travel Budget Plan: ${analysisData.tripName}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 10; doc.setFontSize(10); doc.setTextColor(textColor); doc.text(`Target Departure: ${document.getElementById('lttbDepartureMonth').selectedOptions[0].text} ${document.getElementById('lttbDepartureYear').value} (${analysisData.monthsUntilDeparture} months from now)`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 10; // Savings Summary doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Savings Summary", 14, yPos); yPos += 7; doc.setFontSize(10); doc.setTextColor(textColor); doc.text(`Total Estimated Trip Cost: ${toCurrency(analysisData.totalEstimatedTripCost)}`, 14, yPos); yPos += 6; doc.text(`Amount Already Saved: ${toCurrency(analysisData.alreadySaved)}`, 14, yPos); yPos += 6; doc.text(`Remaining Amount to Save: ${toCurrency(analysisData.remainingToSave)}`, 14, yPos); yPos += 6; doc.text(`Target Monthly Savings: ${toCurrency(analysisData.targetMonthlySaving)}`, 14, yPos); yPos += 6; doc.text(`Projected Total Savings by Departure: ${toCurrency(analysisData.projectedSavingsByDeparture)}`, 14, yPos); yPos += 8; // Budget Analysis doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Budget Analysis", 14, yPos); yPos += 7; doc.setFontSize(10); doc.setTextColor(textColor); if (analysisData.monthsUntilDeparture <= 0) { doc.setTextColor(dangerColor); doc.text("Status: Departure date is in the past or too soon to effectively plan savings.", 14, yPos); yPos +=6; } else { const requiredMonthly = analysisData.remainingToSave > 0 ? analysisData.remainingToSave / analysisData.monthsUntilDeparture : 0; doc.text(`Required Monthly Savings to Meet Goal: ${toCurrency(requiredMonthly)}`, 14, yPos); yPos += 6; if (analysisData.projectedSavingsByDeparture >= analysisData.totalEstimatedTripCost) { doc.setTextColor(accentColor); doc.text(`Status: ON TRACK. Projected Surplus: ${toCurrency(analysisData.projectedSavingsByDeparture - analysisData.totalEstimatedTripCost)}`, 14, yPos); } else { doc.setTextColor(dangerColor); doc.text(`Status: SHORTFALL. Projected Shortfall: ${toCurrency(analysisData.totalEstimatedTripCost - analysisData.projectedSavingsByDeparture)}`, 14, yPos); } } doc.setTextColor(textColor); yPos += 10; // Detailed Expenses if (lttb_expenseItems.length > 0) { if (yPos > 250) { doc.addPage(); yPos = 20; } doc.setFontSize(12); doc.setTextColor(primaryColor); doc.text("Detailed Estimated Expenses", 14, yPos); yPos += 7; const tableBody = lttb_expenseItems.map(item => [item.category, item.description, toCurrency(item.cost)]); doc.autoTable({ startY: yPos, head: [['Category', 'Description', 'Estimated Cost ($)']], body: tableBody, theme: 'grid', headStyles: { fillColor: primaryColor, textColor: '#ffffff' }, styles: { fontSize: 9, cellPadding: 2 }, columnStyles: { 1: { cellWidth: 'auto'} }, // Description can wrap didDrawPage: (d) => { yPos = d.cursor.y; } }); } else { doc.setFontSize(10); doc.text("No detailed expenses listed.", 14, yPos); } doc.save(`${analysisData.tripName.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_travel_budget.pdf`); } // Initialize document.addEventListener('DOMContentLoaded', () => { lttb_populateDateSelects(); lttb_showTab(0); lttb_renderExpenseItemsList(); // For empty state lttb_updateTotalEstimatedCost(); // Demo Data lttb_expenseItems.push({id:'exp_001', category:'Flights', description:'Round-trip to Europe', cost:1200}); lttb_expenseItems.push({id:'exp_002', category:'Accommodation', description:'Hostels & Guesthouses (60 nights avg $40)', cost:2400}); lttb_expenseItems.push({id:'exp_003', category:'Food & Drinks', description:'Average $50/day for 60 days', cost:3000}); lttb_renderExpenseItemsList(); lttb_updateTotalEstimatedCost(); });