`;
resultsContainer.appendChild(card);
});
};
/**
* Filters the asset data based on the current filter values.
*/
const applyFilters = () => {
const criteria = {
assetClass: filters.assetClass.value,
strategy: filters.strategy.value,
geoFocus: filters.geoFocus.value,
fundSize: parseInt(filters.fundSize.value, 10),
targetIRR: parseInt(filters.targetIRR.value, 10)
};
const filteredAssets = assetData.filter(asset => {
const assetClassMatch = criteria.assetClass === 'all' || asset.assetClass === criteria.assetClass;
const strategyMatch = criteria.strategy === 'all' || asset.strategy === criteria.strategy;
const geoFocusMatch = criteria.geoFocus === 'all' || asset.geoFocus === criteria.geoFocus;
const fundSizeMatch = asset.fundSize <= criteria.fundSize;
const targetIRRMatch = asset.targetIRR >= criteria.targetIRR;
return assetClassMatch && strategyMatch && geoFocusMatch && fundSizeMatch && targetIRRMatch;
});
renderResults(filteredAssets);
};
/**
* Updates the display value for range sliders.
*/
const updateSliderDisplays = () => {
const fundSizeVal = parseInt(filters.fundSize.value, 10);
valueDisplays.fundSize.textContent = fundSizeVal >= 5000 ? `${formatCurrency(fundSizeVal)}+` : formatCurrency(fundSizeVal);
const targetIRRVal = filters.targetIRR.value;
valueDisplays.targetIRR.textContent = `${targetIRRVal}%+`;
};
/**
* Resets all filters to their default state and re-applies filtering.
*/
const resetAllFilters = () => {
filters.assetClass.value = 'all';
filters.strategy.value = 'all';
filters.geoFocus.value = 'all';
filters.fundSize.value = 5000;
filters.targetIRR.value = 8;
updateSliderDisplays();
applyFilters();
};
/**
* Generates and downloads a PDF report of the current results.
*/
const generatePdf = () => {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({
orientation: 'portrait',
unit: 'pt',
format: 'a4'
});
// Get current filtered data
const criteria = {
assetClass: filters.assetClass.value,
strategy: filters.strategy.value,
geoFocus: filters.geoFocus.value,
fundSize: parseInt(filters.fundSize.value, 10),
targetIRR: parseInt(filters.targetIRR.value, 10)
};
const filteredAssets = assetData.filter(asset =>
(criteria.assetClass === 'all' || asset.assetClass === criteria.assetClass) &&
(criteria.strategy === 'all' || asset.strategy === criteria.strategy) &&
(criteria.geoFocus === 'all' || asset.geoFocus === criteria.geoFocus) &&
(asset.fundSize <= criteria.fundSize) &&
(asset.targetIRR >= criteria.targetIRR)
);
if (filteredAssets.length === 0) {
alert("No data to export to PDF.");
return;
}
// --- PDF Design ---
const docWidth = doc.internal.pageSize.getWidth();
// Header
doc.setFillColor(30, 41, 59); // slate-800
doc.rect(0, 0, docWidth, 80, 'F');
doc.setFontSize(22);
doc.setTextColor(255, 255, 255);
doc.setFont('helvetica', 'bold');
doc.text("Private Market Asset Screener Report", docWidth / 2, 45, { align: 'center' });
// Sub-header with date
doc.setFontSize(10);
doc.setTextColor(220, 220, 220);
doc.setFont('helvetica', 'normal');
const reportDate = `Report Generated: ${new Date().toLocaleDateString('en-US')}`;
doc.text(reportDate, docWidth / 2, 65, { align: 'center' });
let yPos = 120;
// Applied Filters Section
doc.setFontSize(14);
doc.setTextColor(30, 41, 59);
doc.setFont('helvetica', 'bold');
doc.text("Applied Filter Criteria", 40, yPos);
yPos += 20;
doc.setFontSize(10);
doc.setFont('helvetica', 'normal');
const filtersText = `
- Asset Class: ${criteria.assetClass === 'all' ? 'Any' : criteria.assetClass}
- Investment Strategy: ${criteria.strategy === 'all' ? 'Any' : criteria.strategy}
- Geographic Focus: ${criteria.geoFocus === 'all' ? 'Any' : criteria.geoFocus}
- Maximum Fund Size: ${criteria.fundSize >= 5000 ? '$5.0B+' : formatCurrency(criteria.fundSize)}
- Minimum Target Net IRR: ${criteria.targetIRR}%+
`;
doc.setTextColor(71, 85, 105); // slate-600
doc.text(filtersText, 50, yPos);
yPos += 80;
// Prepare data for the table
const head = [['Fund Name', 'Asset Class', 'Strategy', 'Fund Size', 'Target IRR (%)']];
const body = filteredAssets.map(asset => [
asset.name,
asset.assetClass,
asset.strategy,
formatCurrency(asset.fundSize),
{ content: asset.targetIRR, styles: { halign: 'right' } }
]);
// Add the table
doc.autoTable({
startY: yPos,
head: head,
body: body,
theme: 'grid',
headStyles: {
fillColor: [45, 55, 72], // A darker blue-gray
textColor: [255, 255, 255],
fontStyle: 'bold',
halign: 'center'
},
styles: {
cellPadding: 6,
fontSize: 9,
valign: 'middle'
},
columnStyles: {
0: { cellWidth: 180 }, // Fund Name
4: { halign: 'right' } // Target IRR
},
didDrawPage: function(data) {
// Footer
const pageCount = doc.internal.getNumberOfPages();
doc.setFontSize(8);
doc.setTextColor(150, 150, 150);
doc.text(`Page ${doc.internal.pages.length - 1} of ${pageCount}`, docWidth - 50, doc.internal.pageSize.getHeight() - 20, { align: 'center' });
}
});
doc.save('Private_Market_Screener_Report.pdf');
};
// --- EVENT LISTENERS ---
Object.values(filters).forEach(filterElement => {
if (filterElement) {
filterElement.addEventListener('input', applyFilters);
filterElement.addEventListener('input', updateSliderDisplays);
} else {
console.error("A filter element was not found in the DOM.");
}
});
if (resetFiltersBtn) {
resetFiltersBtn.addEventListener('click', resetAllFilters);
}
if (downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', generatePdf);
} else {
console.error("PDF Download button not found in the DOM.");
}
// --- INITIALIZATION ---
if (resultsContainer && resultsCount && noResults) {
updateSliderDisplays();
applyFilters(); // Initial render on page load
} else {
console.error("One or more critical result elements are missing from the DOM.");
}
});