`;
summaryOutput.innerHTML = summaryHTML;
const pdfButton = document.getElementById('tbDownloadPdfButton');
if(pdfButton) pdfButton.style.display = 'block';
if (switchToSummaryTab) {
const summaryTabButton = Array.from(document.getElementsByClassName("tb-tab-button")).find(btn => btn.textContent.includes("Budget Summary"));
if (summaryTabButton && !summaryTabButton.classList.contains('active')) {
openTbTab({currentTarget: summaryTabButton}, 'tbSummaryTab');
}
}
}
function downloadTravelBudgetPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const destination = getTbTextValue('tbDestination', 'N/A');
const numDays = getTbNumValue('tbNumDays', 1);
const numTravelers = getTbNumValue('tbNumTravelers', 1);
const currencySymbol = getTbTextValue('tbCurrencySymbol', '$');
const contingencyType = getTbTextValue('tbContingencyType');
const contingencyValueInput = getTbNumValue('tbContingencyValue');
let overallTotalCost = 0;
const categoryDetailsForPdf = [];
const allItemsForPdf = [];
tbCategories.forEach(category => {
let categorySubtotal = 0;
let itemsInSection = [];
category.items.forEach(item => {
let itemCalculatedCost = item.cost || 0;
let costTypeString = item.type;
switch (item.type) {
case 'perPerson': itemCalculatedCost *= numTravelers; costTypeString = 'Per Person'; break;
case 'perDay': itemCalculatedCost *= numDays; costTypeString = 'Per Day'; break;
case 'perPersonPerDay': itemCalculatedCost *= numTravelers * numDays; costTypeString = 'Per Person/Day'; break;
default: costTypeString = 'Total'; break;
}
categorySubtotal += itemCalculatedCost;
itemsInSection.push([item.description || 'N/A', `${currencySymbol}${item.cost.toFixed(2)} (${costTypeString})`, `${currencySymbol}${itemCalculatedCost.toFixed(2)}`]);
});
overallTotalCost += categorySubtotal;
categoryDetailsForPdf.push({ name: category.name, total: categorySubtotal, items: itemsInSection });
});
let contingencyAmount = 0;
let contingencyDisplay = "";
if (contingencyType === 'percentage') {
contingencyAmount = overallTotalCost * (contingencyValueInput / 100);
contingencyDisplay = `${contingencyValueInput}% of Subtotal`;
} else {
contingencyAmount = contingencyValueInput;
contingencyDisplay = `${currencySymbol}${contingencyValueInput.toFixed(2)} (Fixed)`;
}
const grandTotalWithContingency = overallTotalCost + contingencyAmount;
const costPerTraveler = numTravelers > 0 ? grandTotalWithContingency / numTravelers : grandTotalWithContingency;
const costPerDay = numDays > 0 ? grandTotalWithContingency / numDays : grandTotalWithContingency;
const costPerTravelerPerDay = (numTravelers > 0 && numDays > 0) ? grandTotalWithContingency / (numTravelers * numDays) : grandTotalWithContingency;
let yPos = 20;
doc.setFontSize(20); doc.setTextColor(TbPrimaryColor);
doc.text("Travel Budget Plan", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += 12;
doc.setFontSize(12); doc.setTextColor(TbTextColor);
doc.text(`Destination: ${destination}`, 14, yPos); yPos += 7;
doc.text(`Duration: ${numDays} days`, 14, yPos); yPos += 7;
doc.text(`Number of Travelers: ${numTravelers}`, 14, yPos); yPos += 7;
doc.text(`Planning Currency: ${currencySymbol}`, 14, yPos); yPos += 10;
doc.setFontSize(14); doc.setTextColor(TbPrimaryColor);
doc.text("Expense Breakdown:", 14, yPos); yPos += 7;
categoryDetailsForPdf.forEach(catDetail => {
if (catDetail.items.length > 0 || catDetail.total > 0) { // Only show categories with items or a total
doc.setFontSize(12);
doc.setTextColor(TbPrimaryColor);
doc.text(catDetail.name, 14, yPos);
yPos += 1; // Small gap
doc.autoTable({
startY: yPos,
head: [['Description', 'Original Cost', `Calculated Cost (${currencySymbol})`]],
body: catDetail.items,
theme: 'striped',
headStyles: { fillColor: TbPrimaryColor, textColor: '#FFFFFF', fontSize: 10 },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: { 2: { halign: 'right' } },
didDrawPage: function(data) { // Handle page breaks
if (data.pageNumber > 1) { yPos = data.cursor.y; } // Reset yPos for new page content
}
});
yPos = doc.autoTable.previous.finalY + 5;
doc.setFontSize(10);
doc.setFont(undefined, 'bold');
doc.text(`Subtotal for ${catDetail.name}:`, 14, yPos);
doc.text(`${currencySymbol}${catDetail.total.toFixed(2)}`, doc.internal.pageSize.getWidth() - 14, yPos, {align: 'right'});
doc.setFont(undefined, 'normal');
yPos += 8;
}
});
if (yPos > doc.internal.pageSize.getHeight() - 60) { // Check if space for totals is enough
doc.addPage(); yPos = 20;
}
doc.setLineWidth(0.5);
doc.line(14, yPos, doc.internal.pageSize.getWidth() - 14, yPos); // Separator line
yPos += 8;
doc.setFontSize(12);
doc.setFont(undefined, 'bold');
doc.text("Summary Totals", 14, yPos); yPos += 8;
doc.setFont(undefined, 'normal');
doc.setFontSize(10);
const summaryData = [
["Subtotal (All Expenses):", `${currencySymbol}${overallTotalCost.toFixed(2)}`],
[`Contingency (${contingencyDisplay}):`, `${currencySymbol}${contingencyAmount.toFixed(2)}`],
[{content: "Grand Total Estimated Cost:", styles: {fontStyle: 'bold', fontSize: 11, textColor: TbPrimaryColor}},
{content: `${currencySymbol}${grandTotalWithContingency.toFixed(2)}`, styles: {fontStyle: 'bold', fontSize: 11, textColor: TbPrimaryColor, halign: 'right'}}],
["", ""], // Spacer
["Cost per Traveler:", `${currencySymbol}${costPerTraveler.toFixed(2)}`],
["Cost per Day:", `${currencySymbol}${costPerDay.toFixed(2)}`],
["Cost per Traveler per Day:", `${currencySymbol}${costPerTravelerPerDay.toFixed(2)}`]
];
doc.autoTable({
startY: yPos,
body: summaryData,
theme: 'plain',
styles: { fontSize: 10, cellPadding: 1.5 },
columnStyles: { 1: { halign: 'right' } }
});
yPos = doc.autoTable.previous.finalY + 10;
doc.setFontSize(8); doc.setTextColor('#777777');
doc.text(`Report Generated: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`, 14, doc.internal.pageSize.getHeight() - 10);
doc.text("This is an estimate for planning purposes.", doc.internal.pageSize.getWidth() / 2, doc.internal.pageSize.getHeight() - 10, { align: 'center' });
doc.save(`Travel_Budget_${destination.replace(/[^a-zA-Z0-9]/g, '_') || 'Plan'}.pdf`);
}
